1 module dcrypt.keyderivation.pbkdf2;
2 
3 import dcrypt.macs.hmac;
4 import dcrypt.digest;
5 import dcrypt.exceptions;
6 import dcrypt.bitmanip;
7 import std.datetime;
8 import std.algorithm: min;
9 
10 
11 
12 unittest {
13 	
14 	// test vectors from http://tools.ietf.org/html/rfc6070
15 	
16 	import dcrypt.digests.sha1;
17 	import std.datetime: StopWatch;
18 
19 	const ubyte[] pass = cast(const ubyte[]) "password";
20 	const ubyte[] salt = cast(const ubyte[]) "salt";
21 
22 	ubyte[] key = new ubyte[20];
23 
24 	pbkdf2!SHA1(key, pass, salt, 1);
25 	assert(key == x"0c60c80f961f0e71f3a9b524af6012062fe037a6", "PKCS5S2 PBKDF2 failed!");
26 
27 	pbkdf2!SHA1(key, pass, salt, 2);
28 	assert(key == x"ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957", "PKCS5S2 PBKDF2 failed!");
29 
30 	pbkdf2!SHA1(key, pass, salt, 4096);
31 	assert(key == x"4b007901b765489abead49d926f721d065a429c1", "PKCS5S2 PBKDF2 failed!");
32 
33 	key.length = 25;
34 	pbkdf2!SHA1(key, cast(const ubyte[])"passwordPASSWORDpassword", cast(const ubyte[])"saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096);
35 	assert(key == x"3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038 ", "PKCS5S2 PBKDF2 failed!");
36 
37 	//	uint iterTime = 10; // milli seconds
38 	//	gen.start(pass, salt, 0, iterTime);
39 	//	
40 	//	StopWatch sw;
41 	//	sw.start();
42 	//	gen.nextBytes(key);
43 	//	sw.stop();
44 	//	
45 	//	assert(sw.peek().msecs() >= iterTime, "PBKDF2 terminated too fast");
46 	//assert(gen.getIterationCount() > 0, "failed to do any iterations in given time");
47 }
48 
49 /// Derive a key from a password and a salt using multiple iterations.
50 /// 
51 /// Params:
52 /// derivedKey = Output buffer for derived key. Slice will be filled.
53 /// password = Password.
54 /// salt = Salt.
55 /// iterationCount = Number of iterations.
56 @safe
57 public void pbkdf2(D)(ubyte[] derivedKey, in ubyte[] password, in ubyte[] salt, uint iterationCount) if(isDigest!D) {
58 
59 	HMac!D hMac;
60 	ubyte[hMac.macSize] state;
61 	uint counter = 0;
62 
63 	hMac.start(password);
64 
65 	// fill the output buffer
66 	while(derivedKey.length > 0) {
67 		++counter;
68 		state = genBlock(hMac, salt, counter, iterationCount);
69 
70 		size_t len = min(state.length, derivedKey.length);
71 		derivedKey[0..len] = state[0..len];
72 		derivedKey = derivedKey[len..$];
73 	}
74 }
75 
76 
77 private ubyte[M.macSize] genBlock(M)(ref M hMac, in ubyte[] salt, in uint counter, ulong iterCount)
78 	if(isMAC!M)
79 	in {
80 		assert(iterCount > 0, "iterCount can't be 0.");
81 }
82 body {
83 	ubyte[M.macSize] state, output;
84 	
85 	if (salt != null)
86 	{
87 		hMac.put(salt);
88 	}
89 	
90 	hMac.put(toBigEndian(counter));
91 	hMac.finish(state);
92 	
93 	output[] = state[];
94 	
95 	
96 	foreach (count; 1..iterCount)
97 	{
98 		hMac.put(state);
99 		hMac.finish(state);
100 		output[] ^= state[];
101 	}
102 
103 	return output;
104 }