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 modulebotan.rng.hmac_rng;
12 13 importbotan.mac.mac;
14 importbotan.rng.rng;
15 importbotan.utils.types;
16 importbotan.libstate.libstate;
17 importbotan.utils.get_byte;
18 importbotan.entropy.entropy_src;
19 importbotan.utils.xor_buf;
20 importstd.algorithm;
21 importstd.datetime;
22 importbotan.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 finalclassHMAC_RNG : RandomNumberGenerator34 {
35 public:
36 /*
37 * Generate a buffer of random bytes
38 */39 overridevoidrandomize(ubyte* output, size_tlength)
40 {
41 if (!isSeeded())
42 {
43 reseed(256);
44 if (!isSeeded())
45 thrownewPRNGUnseeded(name);
46 }
47 48 constsize_tmax_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 constsize_tcopied = 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 overrideboolisSeeded() const71 {
72 return (m_collected_entropy_estimate >= 256);
73 }
74 75 /*
76 * Clear memory of sensitive data
77 */78 overridevoidclear()
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 @propertystringname() const91 {
92 return"HMAC_RNG(" ~ m_extractor.name ~ "," ~ m_prf.name ~ ")";
93 }
94 95 /*
96 * Poll for entropy and reset the internal keys
97 */98 overridevoidreseed(size_tpoll_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 doublebits_collected = 0;
109 110 EntropyAccumulatoraccum = EntropyAccumulator(
111 (const(ubyte)* input, size_tin_len, doubleentropy_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 output134 135 hmacPrf(*m_prf, m_K, m_counter, "reseed");
136 m_extractor.update(m_K); // K is the CTXinfo=reseed PRF output137 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 salt143 hmacPrf(*m_prf, m_K, m_counter, "xts");
144 m_extractor.setKey(m_K);
145 146 // Reset state147 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 overridevoidaddEntropy(const(ubyte)* input, size_tlength)
160 {
161 m_extractor.update(input, length);
162 reseed(BOTAN_RNG_RESEED_POLL_BITS);
163 }
164 165 overrideSecureVector!ubyterandomVec(size_tbytes) { returnsuper.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(MessageAuthenticationCodeextractor, MessageAuthenticationCodeprf)
173 {
174 m_extractor = extractor; // swap175 m_prf = prf; // swap176 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 thrownewInvalidArgument("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 2190 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!ubyteprf_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!MessageAuthenticationCodem_extractor;
221 Unique!MessageAuthenticationCodem_prf;
222 223 size_tm_collected_entropy_estimate = 0;
224 size_tm_output_since_reseed = 0;
225 226 SecureVector!ubytem_K;
227 uintm_counter = 0;
228 }
229 230 231 232 private:
233 234 voidhmacPrf()(MessageAuthenticationCodeprf,
235 refSecureVector!ubyteK,
236 refuintcounter,
237 instringlabel)
238 {
239 240 autotimestamp = 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 }