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 }