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 }