Botan  1.11.4
randpool.cpp
Go to the documentation of this file.
1 /*
2 * Randpool
3 * (C) 1999-2009 Jack Lloyd
4 *
5 * Distributed under the terms of the Botan license
6 */
7 
8 #include <botan/randpool.h>
9 #include <botan/get_byte.h>
10 #include <botan/internal/xor_buf.h>
11 #include <algorithm>
12 #include <chrono>
13 
14 namespace Botan {
15 
16 namespace {
17 
18 /*
19 * PRF based on a MAC
20 */
21 enum RANDPOOL_PRF_TAG {
22  CIPHER_KEY = 0,
23  MAC_KEY = 1,
24  GEN_OUTPUT = 2
25 };
26 
27 }
28 
29 /*
30 * Generate a buffer of random bytes
31 */
32 void Randpool::randomize(byte out[], size_t length)
33  {
34  if(!is_seeded())
35  throw PRNG_Unseeded(name());
36 
37  update_buffer();
38  while(length)
39  {
40  const size_t copied = std::min<size_t>(length, buffer.size());
41  copy_mem(out, &buffer[0], copied);
42  out += copied;
43  length -= copied;
44  update_buffer();
45  }
46  }
47 
48 /*
49 * Refill the output buffer
50 */
51 void Randpool::update_buffer()
52  {
53  for(size_t i = 0; i != counter.size(); ++i)
54  if(++counter[i])
55  break;
56 
57  mac->update(static_cast<byte>(GEN_OUTPUT));
58  mac->update(counter);
59  secure_vector<byte> mac_val = mac->final();
60 
61  for(size_t i = 0; i != mac_val.size(); ++i)
62  buffer[i % buffer.size()] ^= mac_val[i];
63  cipher->encrypt(buffer);
64 
65  if(counter[0] % ITERATIONS_BEFORE_RESEED == 0)
66  mix_pool();
67  }
68 
69 /*
70 * Mix the entropy pool
71 */
72 void Randpool::mix_pool()
73  {
74  const size_t BLOCK_SIZE = cipher->block_size();
75 
76  mac->update(static_cast<byte>(MAC_KEY));
77  mac->update(pool);
78  mac->set_key(mac->final());
79 
80  mac->update(static_cast<byte>(CIPHER_KEY));
81  mac->update(pool);
82  cipher->set_key(mac->final());
83 
84  xor_buf(pool, buffer, BLOCK_SIZE);
85  cipher->encrypt(pool);
86  for(size_t i = 1; i != POOL_BLOCKS; ++i)
87  {
88  const byte* previous_block = &pool[BLOCK_SIZE*(i-1)];
89  byte* this_block = &pool[BLOCK_SIZE*i];
90  xor_buf(this_block, previous_block, BLOCK_SIZE);
91  cipher->encrypt(this_block);
92  }
93 
94  update_buffer();
95  }
96 
97 /*
98 * Reseed the internal state
99 */
100 void Randpool::reseed(size_t poll_bits)
101  {
102  Entropy_Accumulator_BufferedComputation accum(*mac, poll_bits);
103 
104  if(!entropy_sources.empty())
105  {
106  size_t poll_attempt = 0;
107 
108  while(!accum.polling_goal_achieved() && poll_attempt < poll_bits)
109  {
110  entropy_sources[poll_attempt % entropy_sources.size()]->poll(accum);
111  ++poll_attempt;
112  }
113  }
114 
115  secure_vector<byte> mac_val = mac->final();
116 
117  xor_buf(pool, mac_val, mac_val.size());
118  mix_pool();
119 
120  if(accum.bits_collected() >= poll_bits)
121  seeded = true;
122  }
123 
124 /*
125 * Add user-supplied entropy
126 */
127 void Randpool::add_entropy(const byte input[], size_t length)
128  {
129  secure_vector<byte> mac_val = mac->process(input, length);
130  xor_buf(pool, mac_val, mac_val.size());
131  mix_pool();
132 
133  if(length)
134  seeded = true;
135  }
136 
137 /*
138 * Add another entropy source to the list
139 */
141  {
142  entropy_sources.push_back(src);
143  }
144 
145 /*
146 * Clear memory of sensitive data
147 */
149  {
150  cipher->clear();
151  mac->clear();
152  zeroise(pool);
153  zeroise(buffer);
154  zeroise(counter);
155  seeded = false;
156  }
157 
158 /*
159 * Return the name of this type
160 */
161 std::string Randpool::name() const
162  {
163  return "Randpool(" + cipher->name() + "," + mac->name() + ")";
164  }
165 
166 /*
167 * Randpool Constructor
168 */
171  size_t pool_blocks,
172  size_t iter_before_reseed) :
173  ITERATIONS_BEFORE_RESEED(iter_before_reseed),
174  POOL_BLOCKS(pool_blocks),
175  cipher(cipher_in),
176  mac(mac_in)
177  {
178  const size_t BLOCK_SIZE = cipher->block_size();
179  const size_t OUTPUT_LENGTH = mac->output_length();
180 
181  if(OUTPUT_LENGTH < BLOCK_SIZE ||
182  !cipher->valid_keylength(OUTPUT_LENGTH) ||
183  !mac->valid_keylength(OUTPUT_LENGTH))
184  {
185  delete cipher;
186  delete mac;
187  throw Internal_Error("Randpool: Invalid algorithm combination " +
188  cipher->name() + "/" + mac->name());
189  }
190 
191  buffer.resize(BLOCK_SIZE);
192  pool.resize(POOL_BLOCKS * BLOCK_SIZE);
193  counter.resize(12);
194  seeded = false;
195  }
196 
197 /*
198 * Randpool Destructor
199 */
201  {
202  delete cipher;
203  delete mac;
204 
205  for(auto i = entropy_sources.begin(); i != entropy_sources.end(); ++i)
206  delete *i;
207  }
208 
209 }