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 }