1 /** 2 * HMAC RNG 3 * 4 * Copyright: 5 * (C) 2008,2013 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.rng.hmac_rng; 12 13 import botan.mac.mac; 14 import botan.rng.rng; 15 import botan.utils.types; 16 import botan.libstate.libstate; 17 import botan.utils.get_byte; 18 import botan.entropy.entropy_src; 19 import botan.utils.xor_buf; 20 import std.algorithm; 21 import std.datetime; 22 import botan.constants; 23 /** 24 * HMAC_RNG - based on the design described in "On Extract-then-Expand 25 * Key Derivation Functions and an HMAC-based KDF" by Hugo Krawczyk 26 * (henceforce, 'E-t-E') 27 * 28 * However it actually can be parameterized with any two MAC functions, 29 * not restricted to HMAC (this variation is also described in 30 * Krawczyk's paper), for instance one could use HMAC(SHA-512) as the 31 * extractor and CMAC(AES-256) as the PRF. 32 */ 33 final class HMAC_RNG : RandomNumberGenerator 34 { 35 public: 36 /* 37 * Generate a buffer of random bytes 38 */ 39 override void randomize(ubyte* output, size_t length) 40 { 41 if (!isSeeded()) 42 { 43 reseed(256); 44 if (!isSeeded()) 45 throw new PRNGUnseeded(name); 46 } 47 48 const size_t max_per_prf_iter = m_prf.outputLength / 2; 49 50 /* 51 HMAC KDF as described in E-t-E, using a CTXinfo of "rng" 52 */ 53 while (length) 54 { 55 hmacPrf(*m_prf, m_K, m_counter, "rng"); 56 57 const size_t copied = std.algorithm.min(length, max_per_prf_iter); 58 59 copyMem(output, m_K.ptr, copied); 60 output += copied; 61 length -= copied; 62 63 m_output_since_reseed += copied; 64 65 if (m_output_since_reseed >= BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED) 66 reseed(BOTAN_RNG_RESEED_POLL_BITS); 67 } 68 } 69 70 override bool isSeeded() const 71 { 72 return (m_collected_entropy_estimate >= 256); 73 } 74 75 /* 76 * Clear memory of sensitive data 77 */ 78 override void clear() 79 { 80 m_collected_entropy_estimate = 0; 81 m_extractor.clear(); 82 m_prf.clear(); 83 zeroise(m_K); 84 m_counter = 0; 85 } 86 87 /* 88 * Return the name of this type 89 */ 90 override @property string name() const 91 { 92 return "HMAC_RNG(" ~ m_extractor.name ~ "," ~ m_prf.name ~ ")"; 93 } 94 95 /* 96 * Poll for entropy and reset the internal keys 97 */ 98 override void reseed(size_t poll_bits) 99 { 100 /* 101 Using the terminology of E-t-E, XTR is the MAC function (normally 102 HMAC) seeded with XTS (below) and we form SKM, the key material, by 103 polling as many sources as we think needed to reach our polling 104 goal. We then also include feedback of the current PRK so that 105 a bad poll doesn't wipe us out. 106 */ 107 108 double bits_collected = 0; 109 110 EntropyAccumulator accum = EntropyAccumulator( 111 (const(ubyte)* input, size_t in_len, double entropy_estimate) 112 { 113 m_extractor.update(input, in_len); 114 bits_collected += entropy_estimate; 115 return (bits_collected >= poll_bits); 116 } 117 ); 118 119 globalState().pollAvailableSources(accum); 120 121 /* 122 * It is necessary to feed forward poll data. Otherwise, a good poll 123 * (collecting a large amount of conditional entropy) followed by a 124 * bad one (collecting little) would be unsafe. Do this by 125 * generating new PRF outputs using the previous key and feeding 126 * them into the extractor function. 127 * 128 * Cycle the RNG once (CTXinfo="rng"), then generate a new PRF 129 * output using the CTXinfo "reseed". Provide these values as input 130 * to the extractor function. 131 */ 132 hmacPrf(*m_prf, m_K, m_counter, "rng"); 133 m_extractor.update(m_K); // K is the CTXinfo=rng PRF output 134 135 hmacPrf(*m_prf, m_K, m_counter, "reseed"); 136 m_extractor.update(m_K); // K is the CTXinfo=reseed PRF output 137 138 /* Now derive the new PRK using everything that has been fed into 139 the extractor, and set the PRF key to that */ 140 m_prf.setKey(m_extractor.finished()); 141 142 // Now generate a new PRF output to use as the XTS extractor salt 143 hmacPrf(*m_prf, m_K, m_counter, "xts"); 144 m_extractor.setKey(m_K); 145 146 // Reset state 147 zeroise(m_K); 148 m_counter = 0; 149 150 m_collected_entropy_estimate = cast(size_t)std.algorithm.min(m_collected_entropy_estimate + bits_collected, 151 m_extractor.outputLength * 8); 152 153 m_output_since_reseed = 0; 154 } 155 156 /* 157 * Add user-supplied entropy to the extractor input 158 */ 159 override void addEntropy(const(ubyte)* input, size_t length) 160 { 161 m_extractor.update(input, length); 162 reseed(BOTAN_RNG_RESEED_POLL_BITS); 163 } 164 165 override SecureVector!ubyte randomVec(size_t bytes) { return super.randomVec(bytes); } 166 167 /** 168 * Params: 169 * extractor = a MAC used for extracting the entropy 170 * prf = a MAC used as a PRF using HKDF construction 171 */ 172 this(MessageAuthenticationCode extractor, MessageAuthenticationCode prf) 173 { 174 m_extractor = extractor; // swap 175 m_prf = prf; // swap 176 this(); 177 } 178 179 private: 180 this() { 181 assert(!m_prf.isEmpty && !m_extractor.isEmpty); 182 183 if (!m_prf.validKeylength(m_extractor.outputLength) || 184 !m_extractor.validKeylength(m_prf.outputLength)) 185 throw new InvalidArgument("HMAC_RNG: Bad algo combination " ~ 186 m_extractor.name ~ " and " ~ 187 m_prf.name); 188 189 // First PRF inputs are all zero, as specified in section 2 190 m_K.resize(m_prf.outputLength); 191 192 /* 193 Normally we want to feedback PRF outputs to the extractor function 194 to ensure a single bad poll does not reduce entropy. Thus in reseed 195 we'll want to invoke the PRF before we reset the PRF key, but until 196 the first reseed the PRF is unkeyed. Rather than trying to keep 197 track of this, just set the initial PRF key to constant zero. 198 Since all PRF inputs in the first reseed are constants, this 199 amounts to suffixing the seed in the first poll with a fixed 200 constant string. 201 202 The PRF key will not be used to generate outputs until after reseed 203 sets m_seeded to true. 204 */ 205 SecureVector!ubyte prf_key = SecureVector!ubyte(m_extractor.outputLength); 206 m_prf.setKey(prf_key); 207 /* 208 Use PRF("Botan HMAC_RNG XTS") as the intitial XTS key. 209 210 This will be used during the first extraction sequence; XTS values 211 after this one are generated using the PRF. 212 213 If I understand the E-t-E paper correctly (specifically Section 4), 214 using this fixed extractor key is safe to do. 215 */ 216 m_extractor.setKey(m_prf.process("Botan HMAC_RNG XTS")); 217 218 } 219 220 Unique!MessageAuthenticationCode m_extractor; 221 Unique!MessageAuthenticationCode m_prf; 222 223 size_t m_collected_entropy_estimate = 0; 224 size_t m_output_since_reseed = 0; 225 226 SecureVector!ubyte m_K; 227 uint m_counter = 0; 228 } 229 230 231 232 private: 233 234 void hmacPrf()(MessageAuthenticationCode prf, 235 ref SecureVector!ubyte K, 236 ref uint counter, 237 in string label) 238 { 239 240 auto timestamp = Clock.currStdTime(); 241 242 prf.update(K); 243 prf.update(label); 244 prf.updateBigEndian(timestamp); 245 prf.updateBigEndian(counter); 246 prf.flushInto(K.ptr); 247 248 ++counter; 249 }