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 }