1 module dcrypt.crypto.generators.pkcs5s2;
2 
3 public import dcrypt.crypto.generators.pbe;
4 import dcrypt.crypto.macs.hmac;
5 import dcrypt.crypto.digest;
6 import dcrypt.crypto.params.keyparameter;
7 import dcrypt.exceptions;
8 import std.datetime;
9 
10 /**
11  * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 2.
12  * This generator uses a SHA-1 HMac as the calculation function.
13  * 
14  * The document this implementation is based on can be found at
15  * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html>
16  * RSA's PKCS5 Page</a>
17  */
18 @safe
19 public class PKCS5S2ParametersGenerator(D) : PBEParametersGenerator
20 	if(isDigest!D)
21 {
22 
23 	//	unittest {
24 	//		
25 	//		// test vectors from http://tools.ietf.org/html/rfc6070
26 	//		
27 	//		import dcrypt.crypto.digests.sha1;
28 	//		import dcrypt.crypto.params.keyparameter;
29 	//		import dcrypt.util.encoders.hex;
30 	//		import std.datetime: StopWatch;
31 	//		
32 	//		PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA1Digest());
33 	//		
34 	//		ubyte[] pass = PKCS5PasswordToBytes("password");
35 	//		ubyte[] salt = PKCS5PasswordToBytes("salt");
36 	//		
37 	//		gen.init(pass, salt, 2);
38 	//		KeyParameter key = gen.generateDerivedParameters(20*8);
39 	//		assert(key.getKey() == hexDecode("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"), "PKCS5S2 PBKDF2 failed!");
40 	//		
41 	//		uint iterTime = 10; // milli seconds
42 	//		gen.init(pass, salt, 0, iterTime);
43 	//		
44 	//		StopWatch sw;
45 	//		sw.start();
46 	//		key = gen.generateDerivedParameters(20*8);
47 	//		sw.stop();
48 	//		
49 	//		assert(sw.peek().msecs() >= iterTime, "PBKDF2 terminated too fast");
50 	//		assert(gen.getIterationCount() > 0, "failed to do any iterations in given time");
51 	//	}
52 
53 	
54 
55 	
56 	public override {
57 		/**
58 		 * Generate a key parameter derived from the password, salt, and iteration
59 		 * count we are currently initialised with.
60 		 *
61 		 * Params:
62 		 * keySize = the size of the key we want (in bits)
63 		 * Returns:
64 		 * 	a KeyParameter object.
65 		 */
66 		KeyParameter generateDerivedParameters(uint keySize)
67 		{
68 			keySize = keySize / 8;
69 			
70 			ubyte[]  dKey = generateDerivedKey(keySize);
71 			
72 			return new KeyParameter(dKey);
73 		}
74 		
75 		/**
76 		 * Generate a key with initialization vector parameter derived from
77 		 * the password, salt, and iteration count we are currently initialized
78 		 * with.
79 		 *
80 		 * Params:
81 		 * keySize	=	the size of the key we want (in bits)
82 		 * ivSize	=	the size of the iv we want (in bits)
83 		 * Returns: a ParametersWithIV object.
84 		 */
85 		ParametersWithIV generateDerivedParameters(uint keySize, uint ivSize)
86 		{
87 			keySize = keySize / 8;
88 			ivSize = ivSize / 8;
89 			
90 			ubyte[]  dKey = generateDerivedKey(keySize + ivSize);
91 			
92 			return new ParametersWithIV(dKey[0..keySize], dKey[keySize..keySize+ivSize]);
93 		}
94 		
95 		/**
96 		 * Generate a key parameter for use with a MAC derived from the password,
97 		 * salt, and iteration count we are currently initialized with.
98 		 *
99 		 * Params: keySize = the size of the key we want (in bits)
100 		 * Returns: a KeyParameter object.
101 		 */
102 		KeyParameter generateDerivedMacParameters(uint keySize)
103 		{
104 			return generateDerivedParameters(keySize);
105 		}
106 		
107 		string getAlgorithmName() {
108 			return "PBKDF2/"~hMac.name;
109 		}
110 	}
111 
112 private:
113 	HMac!D hMac;
114 	ubyte[D.digestLength] state;
115 
116 	
117 	void F(in ubyte[]  S, uint iterCount, in ubyte[]  iBuf, ubyte[]  output) 
118 	in {
119 		assert(output.length == state.length, "length of output buffer should be equal to state.length");
120 	}
121 	body {
122 		if (S != null)
123 		{
124 			hMac.put(S);
125 		}
126 
127 		hMac.put(iBuf);
128 		hMac.doFinal(state);
129 
130 		output[] = state[];
131 		
132 		if(iterCount > 0) { 
133 			foreach (count; 1..iterCount)
134 			{
135 				hMac.put(state);
136 				hMac.doFinal(state);
137 
138 				assert(output.length >= state.length);
139 				output[] ^= state[];
140 			}
141 		} else {
142 			// run for given amount of time
143 			StopWatch sw;
144 			sw.start();
145 			uint count = 0;
146 			do {
147 				hMac.put(state);
148 				hMac.doFinal(state);
149 
150 				assert(output.length >= state.length);
151 				output[] ^= state[];
152 				
153 				++count;
154 			} while(count < iterTime || (sw.peek().msecs() < iterTime));
155 			
156 			iterationCount = count;
157 		}
158 	}
159 
160 	ubyte[] generateDerivedKey(uint dkLen)
161 	{
162 		enum     hLen = hMac.macSize;
163 		size_t     l = (dkLen + hLen - 1) / hLen;
164 		ubyte[4]  iBuf;
165 		ubyte[]  outBytes = new ubyte[l * hLen];
166 		uint     outPos = 0;
167 
168 		hMac.start(password);
169 
170 		foreach (i; 1..l+1)
171 		{
172 			// Increment the value in 'iBuf'
173 			uint pos = 3;
174 			while (++iBuf[pos] == 0)
175 			{
176 				--pos;
177 			}
178 
179 			F(salt, iterationCount, iBuf, outBytes[outPos..outPos+hLen]);
180 			outPos += hLen;
181 		}
182 		return outBytes;
183 	}
184 }