1 /** 2 * PBKDF2 3 * 4 * Copyright: 5 * (C) 1999-2007,2012 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.pbkdf.pbkdf2; 12 13 import botan.constants; 14 static if (BOTAN_HAS_PBKDF2): 15 16 import botan.pbkdf.pbkdf; 17 import botan.mac.mac; 18 import botan.utils.get_byte; 19 import botan.utils.xor_buf; 20 import botan.utils.rounding; 21 import std.datetime; 22 import std.conv : to; 23 import std.algorithm : min; 24 25 /** 26 * PKCS #5 PBKDF2 27 */ 28 final class PKCS5_PBKDF2 : PBKDF 29 { 30 public: 31 override @property string name() const 32 { 33 return "PBKDF2(" ~ m_mac.name ~ ")"; 34 } 35 36 override PBKDF clone() const 37 { 38 return new PKCS5_PBKDF2(m_mac.clone()); 39 } 40 41 /* 42 * Return a PKCS #5 PBKDF2 derived key 43 */ 44 override 45 Pair!(size_t, OctetString) 46 keyDerivation(size_t key_len, 47 in string passphrase, 48 const(ubyte)* salt, size_t salt_len, 49 size_t iterations, 50 Duration loop_for) const 51 { 52 if (key_len == 0) 53 return makePair(iterations, OctetString()); 54 Unique!MessageAuthenticationCode mac = m_mac.clone(); 55 try 56 { 57 mac.setKey(cast(const(ubyte)*)(passphrase.ptr), passphrase.length); 58 } 59 catch(InvalidKeyLength) 60 { 61 throw new Exception(name ~ " cannot accept passphrases of length " ~ to!string(passphrase.length)); 62 } 63 64 SecureVector!ubyte key = SecureVector!ubyte(key_len); 65 66 ubyte* T = key.ptr; 67 68 SecureVector!ubyte U = SecureVector!ubyte(mac.outputLength); 69 70 const size_t blocks_needed = roundUp(key_len, mac.outputLength) / mac.outputLength; 71 72 Duration dur_per_block = loop_for / blocks_needed; 73 74 uint counter = 1; 75 while (key_len) 76 { 77 size_t T_size = min(mac.outputLength, key_len); 78 79 mac.update(salt, salt_len); 80 mac.updateBigEndian(counter); 81 mac.flushInto(U.ptr); 82 83 xorBuf(T, U.ptr, T_size); 84 85 if (iterations == 0) 86 { 87 /* 88 If no iterations set, run the first block to calibrate based 89 on how long hashing takes on whatever machine we're running on. 90 */ 91 92 const auto start = Clock.currTime(UTC()); 93 94 iterations = 1; // the first iteration we did above 95 96 while (true) 97 { 98 mac.update(U); 99 mac.flushInto(U.ptr); 100 xorBuf(T, U.ptr, T_size); 101 iterations++; 102 103 /* 104 Only break on relatively 'even' iterations. For one it 105 avoids confusion, and likely some broken implementations 106 break on getting completely randomly distributed values 107 */ 108 if (iterations % 10000 == 0) 109 { 110 auto time_taken = Clock.currTime(UTC()) - start; 111 if (time_taken > dur_per_block) 112 break; 113 } 114 } 115 } 116 else 117 { 118 foreach (size_t i; 1 .. iterations) 119 { 120 mac.update(U); 121 mac.flushInto(U.ptr); 122 xorBuf(T, U.ptr, T_size); 123 } 124 } 125 126 key_len -= T_size; 127 T += T_size; 128 ++counter; 129 } 130 131 return makePair(iterations, OctetString(key)); 132 } 133 134 /** 135 * Create a PKCS #5 instance using the specified message auth code 136 * Params: 137 * mac_fn = the MAC object to use as PRF 138 */ 139 this(MessageAuthenticationCode mac_fn) 140 { 141 m_mac = mac_fn; 142 } 143 private: 144 Unique!MessageAuthenticationCode m_mac; 145 }