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