1 module dcrypt.crypto.generators.pkcs5s2; 2 3 public import dcrypt.crypto.generators.pbe; 4 import dcrypt.crypto.macs.hmac; 5 import dcrypt.crypto.digest; 6 import dcrypt.crypto.params.keyparameter; 7 import dcrypt.exceptions; 8 import std.datetime; 9 10 /** 11 * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 2. 12 * This generator uses a SHA-1 HMac as the calculation function. 13 * 14 * The document this implementation is based on can be found at 15 * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html> 16 * RSA's PKCS5 Page</a> 17 */ 18 @safe 19 public class PKCS5S2ParametersGenerator(D) : PBEParametersGenerator 20 if(isDigest!D) 21 { 22 23 // unittest { 24 // 25 // // test vectors from http://tools.ietf.org/html/rfc6070 26 // 27 // import dcrypt.crypto.digests.sha1; 28 // import dcrypt.crypto.params.keyparameter; 29 // import dcrypt.util.encoders.hex; 30 // import std.datetime: StopWatch; 31 // 32 // PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA1Digest()); 33 // 34 // ubyte[] pass = PKCS5PasswordToBytes("password"); 35 // ubyte[] salt = PKCS5PasswordToBytes("salt"); 36 // 37 // gen.init(pass, salt, 2); 38 // KeyParameter key = gen.generateDerivedParameters(20*8); 39 // assert(key.getKey() == hexDecode("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"), "PKCS5S2 PBKDF2 failed!"); 40 // 41 // uint iterTime = 10; // milli seconds 42 // gen.init(pass, salt, 0, iterTime); 43 // 44 // StopWatch sw; 45 // sw.start(); 46 // key = gen.generateDerivedParameters(20*8); 47 // sw.stop(); 48 // 49 // assert(sw.peek().msecs() >= iterTime, "PBKDF2 terminated too fast"); 50 // assert(gen.getIterationCount() > 0, "failed to do any iterations in given time"); 51 // } 52 53 54 55 56 public override { 57 /** 58 * Generate a key parameter derived from the password, salt, and iteration 59 * count we are currently initialised with. 60 * 61 * Params: 62 * keySize = the size of the key we want (in bits) 63 * Returns: 64 * a KeyParameter object. 65 */ 66 KeyParameter generateDerivedParameters(uint keySize) 67 { 68 keySize = keySize / 8; 69 70 ubyte[] dKey = generateDerivedKey(keySize); 71 72 return new KeyParameter(dKey); 73 } 74 75 /** 76 * Generate a key with initialization vector parameter derived from 77 * the password, salt, and iteration count we are currently initialized 78 * with. 79 * 80 * Params: 81 * keySize = the size of the key we want (in bits) 82 * ivSize = the size of the iv we want (in bits) 83 * Returns: a ParametersWithIV object. 84 */ 85 ParametersWithIV generateDerivedParameters(uint keySize, uint ivSize) 86 { 87 keySize = keySize / 8; 88 ivSize = ivSize / 8; 89 90 ubyte[] dKey = generateDerivedKey(keySize + ivSize); 91 92 return new ParametersWithIV(dKey[0..keySize], dKey[keySize..keySize+ivSize]); 93 } 94 95 /** 96 * Generate a key parameter for use with a MAC derived from the password, 97 * salt, and iteration count we are currently initialized with. 98 * 99 * Params: keySize = the size of the key we want (in bits) 100 * Returns: a KeyParameter object. 101 */ 102 KeyParameter generateDerivedMacParameters(uint keySize) 103 { 104 return generateDerivedParameters(keySize); 105 } 106 107 string getAlgorithmName() { 108 return "PBKDF2/"~hMac.name; 109 } 110 } 111 112 private: 113 HMac!D hMac; 114 ubyte[D.digestLength] state; 115 116 117 void F(in ubyte[] S, uint iterCount, in ubyte[] iBuf, ubyte[] output) 118 in { 119 assert(output.length == state.length, "length of output buffer should be equal to state.length"); 120 } 121 body { 122 if (S != null) 123 { 124 hMac.put(S); 125 } 126 127 hMac.put(iBuf); 128 hMac.doFinal(state); 129 130 output[] = state[]; 131 132 if(iterCount > 0) { 133 foreach (count; 1..iterCount) 134 { 135 hMac.put(state); 136 hMac.doFinal(state); 137 138 assert(output.length >= state.length); 139 output[] ^= state[]; 140 } 141 } else { 142 // run for given amount of time 143 StopWatch sw; 144 sw.start(); 145 uint count = 0; 146 do { 147 hMac.put(state); 148 hMac.doFinal(state); 149 150 assert(output.length >= state.length); 151 output[] ^= state[]; 152 153 ++count; 154 } while(count < iterTime || (sw.peek().msecs() < iterTime)); 155 156 iterationCount = count; 157 } 158 } 159 160 ubyte[] generateDerivedKey(uint dkLen) 161 { 162 enum hLen = hMac.macSize; 163 size_t l = (dkLen + hLen - 1) / hLen; 164 ubyte[4] iBuf; 165 ubyte[] outBytes = new ubyte[l * hLen]; 166 uint outPos = 0; 167 168 hMac.start(password); 169 170 foreach (i; 1..l+1) 171 { 172 // Increment the value in 'iBuf' 173 uint pos = 3; 174 while (++iBuf[pos] == 0) 175 { 176 --pos; 177 } 178 179 F(salt, iterationCount, iBuf, outBytes[outPos..outPos+hLen]); 180 outPos += hLen; 181 } 182 return outBytes; 183 } 184 }