Noise-C
 All Data Structures Files Functions Variables Typedefs Macros Groups Pages
randstate.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "internal.h"
24 #if USE_LIBSODIUM
25 #include <sodium.h>
26 #else
27 #include "crypto/chacha/chacha.h"
28 #endif
29 #include <string.h>
30 
59 /* The random number generator here is inspired by the
60  * ChaCha20 version of arc4random() from OpenBSD. */
61 
67 struct NoiseRandState_s
68 {
70  size_t size;
71 
73  size_t left;
74 
76 #if USE_LIBSODIUM
77  uint8_t chacha_k[crypto_stream_chacha20_KEYBYTES];
78  uint8_t chacha_n[crypto_stream_chacha20_IETF_NONCEBYTES];
79 #else
80  chacha_ctx chacha;
81 #endif
82 };
83 
85 #define NOISE_RAND_RESEED_COUNT 1600000
86 
88 #define NOISE_RAND_REKEY_COUNT 16
89 
90 /* Starting key for the random state before the first reseed.
91  This is the SHA256 initialization vector, to introduce a
92  little chaos into the starting state. */
93 static uint8_t const starting_key[32] = {
94  0x6A, 0x09, 0xE6, 0x67, 0xBB, 0x67, 0xAE, 0x85,
95  0x3C, 0x6E, 0xF3, 0x72, 0xA5, 0x4F, 0xF5, 0x3A,
96  0x51, 0x0E, 0x52, 0x7F, 0x9B, 0x05, 0x68, 0x8C,
97  0x1F, 0x83, 0xD9, 0xAB, 0x5B, 0xE0, 0xCD, 0x19
98 };
99 
116 {
117  /* Validate the parameter */
118  if (!state)
120 
121  /* Create the random number generator state */
122  *state = noise_new(NoiseRandState);
123  if (!(*state))
124  return NOISE_ERROR_NO_MEMORY;
125 
126  /* Initialize the random number generator */
127 #if USE_LIBSODIUM
128  memcpy((*state)->chacha_k, starting_key, crypto_stream_chacha20_KEYBYTES);
129  memset((*state)->chacha_n, 0, crypto_stream_chacha20_IETF_NONCEBYTES);
130 #else
131  chacha_keysetup(&((*state)->chacha), starting_key, 256);
132 #endif
133  noise_randstate_reseed((*state));
134  return NOISE_ERROR_NONE;
135 }
136 
148 {
149  /* Validate the parameter */
150  if (!state)
152 
153  /* Clean and free the memory */
154  noise_free(state, state->size);
155  return NOISE_ERROR_NONE;
156 }
157 
178 {
179 #if USE_LIBSODIUM
180  uint8_t data[crypto_stream_chacha20_KEYBYTES + crypto_stream_chacha20_IETF_NONCEBYTES];
181 #else
182  uint8_t data[40];
183 #endif
184 
185  /* Validate the parameter */
186  if (!state)
188 
189  /* Get new random data from the operating system, encrypt it
190  with the previous key/IV, and then replace the key/IV */
191  noise_rand_bytes(data, sizeof(data));
192 #if USE_LIBSODIUM
193  crypto_stream_chacha20_ietf_xor(data, data, sizeof(data), state->chacha_n, state->chacha_k);
194  memcpy(state->chacha_k, data, crypto_stream_chacha20_KEYBYTES);
195  memcpy(state->chacha_n, data + crypto_stream_chacha20_KEYBYTES, crypto_stream_chacha20_IETF_NONCEBYTES);
196 #else
197  chacha_encrypt_bytes(&(state->chacha), data, data, sizeof(data));
198  chacha_keysetup(&(state->chacha), data, 256);
199  chacha_ivsetup(&(state->chacha), data + 32, 0);
200 #endif
201  state->left = NOISE_RAND_RESEED_COUNT;
202 
203  /* And force a rekey as well for good measure */
204  memset(data, 0, sizeof(data));
205 #if USE_LIBSODIUM
206  crypto_stream_chacha20_ietf_xor(data, data, sizeof(data), state->chacha_n, state->chacha_k);
207  memcpy(state->chacha_k, data, crypto_stream_chacha20_KEYBYTES);
208  memcpy(state->chacha_n, data + crypto_stream_chacha20_KEYBYTES, crypto_stream_chacha20_IETF_NONCEBYTES);
209 #else
210  chacha_encrypt_bytes(&(state->chacha), data, data, sizeof(data));
211  chacha_keysetup(&(state->chacha), data, 256);
212  chacha_ivsetup(&(state->chacha), data + 32, 0);
213 #endif
214  noise_clean(data, sizeof(data));
215 
216  /* Ready to go */
217  return NOISE_ERROR_NONE;
218 }
219 
225 static void noise_randstate_rekey(NoiseRandState *state)
226 {
227 #if USE_LIBSODIUM
228  uint8_t data[crypto_stream_chacha20_KEYBYTES + crypto_stream_chacha20_IETF_NONCEBYTES];
229 #else
230  uint8_t data[40];
231 #endif
232  memset(data, 0, sizeof(data));
233 #if USE_LIBSODIUM
234  crypto_stream_chacha20_ietf_xor(data, data, sizeof(data), state->chacha_n, state->chacha_k);
235  memcpy(state->chacha_k, data, crypto_stream_chacha20_KEYBYTES);
236  memcpy(state->chacha_n, data + crypto_stream_chacha20_KEYBYTES, crypto_stream_chacha20_IETF_NONCEBYTES);
237 #else
238  chacha_encrypt_bytes(&(state->chacha), data, data, sizeof(data));
239  chacha_keysetup(&(state->chacha), data, 256);
240  chacha_ivsetup(&(state->chacha), data + 32, 0);
241 #endif
242  noise_clean(data, sizeof(data));
243 }
244 
264  (NoiseRandState *state, uint8_t *buffer, size_t len)
265 {
266  size_t blocks;
267  size_t temp_len;
268 
269  /* Validate the parameters. We make sure to set the contents of
270  the buffer to zero before proceeding just in case this function
271  bails out before it can actually generate data. We don't want
272  to accidentally leak the previous contents in the buffer. */
273  if (!buffer)
275  memset(buffer, 0, len);
276  if (!state)
278 
279  /* Force a reseed if necessary */
280  if (state->left < len)
281  noise_randstate_reseed(state);
282 
283  /* Generate the random data in ChaCha20 block-sized chunks */
284  blocks = 0;
285  while (len > 0) {
286  temp_len = len;
287  if (temp_len > 64)
288  temp_len = 64;
289  if (state->left >= 64) {
290  /* One less block before we need to force a reseed */
291  state->left -= 64;
292  } else {
293  /* Too much random data generated. Force a reseed now */
294  noise_randstate_reseed(state);
295  blocks = 0;
296  }
297  if (blocks++ >= NOISE_RAND_REKEY_COUNT) {
298  /* Too many blocks in the current request. Force a rekey now */
299  noise_randstate_rekey(state);
300  blocks = 0;
301  }
302 #if USE_LIBSODIUM
303  crypto_stream_chacha20_ietf_xor_ic(buffer, buffer, temp_len, state->chacha_n, blocks + 1, state->chacha_k);
304 #else
305  chacha_encrypt_bytes(&(state->chacha), buffer, buffer, temp_len);
306 #endif
307  buffer += temp_len;
308  len -= temp_len;
309  }
310 
311  /* Force a rekey after every request to destroy the input that
312  was used to generate the random data for this request.
313  This prevents the state from being rolled backwards. */
314  noise_randstate_rekey(state);
315  return NOISE_ERROR_NONE;
316 }
317 
349  (NoiseRandState *state, uint8_t *payload, size_t orig_len,
350  size_t padded_len, int padding_mode)
351 {
352  /* Validate the parameters */
353  if (!payload)
355  if (!state) {
356  /* Zero the padding region just in case so that we don't accidentally
357  leak the previous contents of the payload's padding region. */
358  if (padded_len > orig_len)
359  memset(payload + orig_len, 0, padded_len - orig_len);
361  }
362 
363  /* Nothing to do if the new length is shorter than the original */
364  if (padded_len <= orig_len)
365  return NOISE_ERROR_NONE;
366 
367  /* Pad the payload as requested */
368  if (padding_mode == NOISE_PADDING_ZERO) {
369  memset(payload + orig_len, 0, padded_len - orig_len);
370  return NOISE_ERROR_NONE;
371  } else {
373  (state, payload + orig_len, padded_len - orig_len);
374  }
375 }
376 
391 int noise_randstate_generate_simple(uint8_t *buffer, size_t len)
392 {
393  NoiseRandState state;
394 
395  /* Validate the parameters */
396  if (!buffer)
398 
399  /* Initialize the random number generator on the stack */
400  memset(&state, 0, sizeof(state));
401 #if USE_LIBSODIUM
402  memcpy(state.chacha_k, starting_key, crypto_stream_chacha20_KEYBYTES);
403  memset(state.chacha_n, 0, crypto_stream_chacha20_IETF_NONCEBYTES);
404 #else
405  chacha_keysetup(&(state.chacha), starting_key, 256);
406 #endif
407  noise_randstate_reseed(&state);
408 
409  /* Generate the required data */
410  noise_randstate_generate(&state, buffer, len);
411 
412  /* Clean up the random number generator on the stack */
413  noise_clean(&state, sizeof(state));
414  return NOISE_ERROR_NONE;
415 }
416 
#define NOISE_PADDING_ZERO
Pads messages with zero bytes.
void noise_clean(void *data, size_t size)
Cleans a block of memory to destroy its contents.
Definition: util.c:170
#define NOISE_ERROR_INVALID_PARAM
Invalid parameter to function; e.g. a NULL value.
#define NOISE_ERROR_NONE
Success, no error.
int noise_randstate_pad(NoiseRandState *state, uint8_t *payload, size_t orig_len, size_t padded_len, int padding_mode)
Adds padding bytes to the end of a message payload.
Definition: randstate.c:349
#define NOISE_ERROR_NO_MEMORY
Insufficient memory to complete the operation.
int noise_randstate_generate(NoiseRandState *state, uint8_t *buffer, size_t len)
Generates random bytes for use by the application.
Definition: randstate.c:264
int noise_randstate_new(NoiseRandState **state)
Creates a new random number generator.
Definition: randstate.c:115
struct NoiseRandState_s NoiseRandState
Opaque object that represents a random number generator.
Definition: randstate.h:33
int noise_randstate_generate_simple(uint8_t *buffer, size_t len)
Generates random data without first creating a RandState object.
Definition: randstate.c:391
Internal definitions for the library.
void noise_free(void *ptr, size_t size)
Destroys the contents of a block of memory and free it.
Definition: util.c:152
int noise_randstate_reseed(NoiseRandState *state)
Reseeds the random number generator from operating system entropy.
Definition: randstate.c:177
#define noise_new(type)
Allocates an object from the system and initializes it.
Definition: util.h:35
int noise_randstate_free(NoiseRandState *state)
Frees a RandState object after destroying all sensitive material.
Definition: randstate.c:147
void noise_rand_bytes(void *bytes, size_t size)
Gets cryptographically-strong random bytes from the operating system.
Definition: rand_os.c:68