diff options
Diffstat (limited to 'src/chacha20_drng.c')
-rw-r--r-- | src/chacha20_drng.c | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/src/chacha20_drng.c b/src/chacha20_drng.c new file mode 100644 index 0000000..9180557 --- /dev/null +++ b/src/chacha20_drng.c @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2016 - 2017, Stephan Mueller <smueller@chronox.de> + * + * License: see COPYING file in root directory + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <sys/mman.h> +#endif + + +#include "chacha20_drng.h" +#include "randombytes.h" + +#define MAJVERSION 1 /* API / ABI incompatible changes, + * functional changes that require consumer + * to be updated (as long as this number is + * zero, the API is not considered stable + * and can change without a bump of the + * major version). */ +#define MINVERSION 3 /* API compatible, ABI may change, + * functional enhancements only, consumer + * can be left unchanged if enhancements are + * not considered. */ +#define PATCHLEVEL 2 /* API / ABI compatible, no functional + * changes, no enhancements, bug fixes + * only. */ + +#define CHACHA20_DRNG_ALIGNMENT 8 /* allow u8 to u32 conversions */ + +#if __GNUC__ >= 4 +# define DSO_PUBLIC __attribute__ ((visibility ("default"))) +#else +# define DSO_PUBLIC +#endif + +/*********************************** Helper ***********************************/ + +#ifndef _WIN32 +#define min(x, y) ((x < y) ? x : y) +#endif +#define __aligned(x) __attribute__((aligned(x))) + +static inline void memset_secure(void *s, int c, uint32_t n) +{ + memset(s, c, n); + __asm__ __volatile__("" : : "r" (s) : "memory"); +} + +static inline void get_time(time_t *sec, uint32_t *nsec) +{ +#ifdef _WIN32 + SYSTEMTIME SystemTime; + GetSystemTime(&SystemTime); + if(sec) + *sec = SystemTime.wSecond; + if(nsec) + *nsec = SystemTime.wMilliseconds; +#else + struct timespec time; + if (clock_gettime(CLOCK_REALTIME, &time) == 0) { + if (sec) + *sec = time.tv_sec; + if (nsec) + *nsec = time.tv_nsec; + } +#endif + +} + +static inline uint32_t rol32(uint32_t x, int n) +{ + return ( (x << (n&(32-1))) | (x >> ((32-n)&(32-1))) ); +} + + +/* Endian dependent byte swap operations. */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +/* Byte swap for 32-bit and 64-bit integers. */ +static inline uint32_t ror32(uint32_t x, int n) +{ + return ( (x >> (n&(32-1))) | (x << ((32-n)&(32-1))) ); +} +static inline uint32_t _bswap32(uint32_t x) +{ + return ((rol32(x, 8) & 0x00ff00ffL) | (ror32(x, 8) & 0xff00ff00L)); +} +# define le_bswap32(x) _bswap32(x) +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define le_bswap32(x) ((uint32_t)(x)) +#else +#error "Endianess not defined" +#endif + +/******************************* ChaCha20 Block *******************************/ + +#define CHACHA20_KEY_SIZE 32 +#define CHACHA20_KEY_SIZE_WORDS (CHACHA20_KEY_SIZE / sizeof(uint32_t)) + +/* State according to RFC 7539 section 2.3 */ +struct chacha20_state { + uint32_t constants[4]; + union { + uint32_t u[CHACHA20_KEY_SIZE_WORDS]; + uint8_t b[CHACHA20_KEY_SIZE]; + } key; + uint32_t counter; + uint32_t nonce[3]; +}; + +#define CHACHA20_BLOCK_SIZE sizeof(struct chacha20_state) +#define CHACHA20_BLOCK_SIZE_WORDS (CHACHA20_BLOCK_SIZE / sizeof(uint32_t)) + +/* ChaCha20 block function according to RFC 7539 section 2.3 */ +static void chacha20_block(uint32_t *state, uint32_t *stream) +{ + uint32_t i, ws[CHACHA20_BLOCK_SIZE_WORDS], *out = stream; + + for (i = 0; i < CHACHA20_BLOCK_SIZE_WORDS; i++) + ws[i] = state[i]; + + for (i = 0; i < 10; i++) { + /* Quarterround 1 */ + ws[0] += ws[4]; ws[12] = rol32(ws[12] ^ ws[0], 16); + ws[8] += ws[12]; ws[4] = rol32(ws[4] ^ ws[8], 12); + ws[0] += ws[4]; ws[12] = rol32(ws[12] ^ ws[0], 8); + ws[8] += ws[12]; ws[4] = rol32(ws[4] ^ ws[8], 7); + + /* Quarterround 2 */ + ws[1] += ws[5]; ws[13] = rol32(ws[13] ^ ws[1], 16); + ws[9] += ws[13]; ws[5] = rol32(ws[5] ^ ws[9], 12); + ws[1] += ws[5]; ws[13] = rol32(ws[13] ^ ws[1], 8); + ws[9] += ws[13]; ws[5] = rol32(ws[5] ^ ws[9], 7); + + /* Quarterround 3 */ + ws[2] += ws[6]; ws[14] = rol32(ws[14] ^ ws[2], 16); + ws[10] += ws[14]; ws[6] = rol32(ws[6] ^ ws[10], 12); + ws[2] += ws[6]; ws[14] = rol32(ws[14] ^ ws[2], 8); + ws[10] += ws[14]; ws[6] = rol32(ws[6] ^ ws[10], 7); + + /* Quarterround 4 */ + ws[3] += ws[7]; ws[15] = rol32(ws[15] ^ ws[3], 16); + ws[11] += ws[15]; ws[7] = rol32(ws[7] ^ ws[11], 12); + ws[3] += ws[7]; ws[15] = rol32(ws[15] ^ ws[3], 8); + ws[11] += ws[15]; ws[7] = rol32(ws[7] ^ ws[11], 7); + + /* Quarterround 5 */ + ws[0] += ws[5]; ws[15] = rol32(ws[15] ^ ws[0], 16); + ws[10] += ws[15]; ws[5] = rol32(ws[5] ^ ws[10], 12); + ws[0] += ws[5]; ws[15] = rol32(ws[15] ^ ws[0], 8); + ws[10] += ws[15]; ws[5] = rol32(ws[5] ^ ws[10], 7); + + /* Quarterround 6 */ + ws[1] += ws[6]; ws[12] = rol32(ws[12] ^ ws[1], 16); + ws[11] += ws[12]; ws[6] = rol32(ws[6] ^ ws[11], 12); + ws[1] += ws[6]; ws[12] = rol32(ws[12] ^ ws[1], 8); + ws[11] += ws[12]; ws[6] = rol32(ws[6] ^ ws[11], 7); + + /* Quarterround 7 */ + ws[2] += ws[7]; ws[13] = rol32(ws[13] ^ ws[2], 16); + ws[8] += ws[13]; ws[7] = rol32(ws[7] ^ ws[8], 12); + ws[2] += ws[7]; ws[13] = rol32(ws[13] ^ ws[2], 8); + ws[8] += ws[13]; ws[7] = rol32(ws[7] ^ ws[8], 7); + + /* Quarterround 8 */ + ws[3] += ws[4]; ws[14] = rol32(ws[14] ^ ws[3], 16); + ws[9] += ws[14]; ws[4] = rol32(ws[4] ^ ws[9], 12); + ws[3] += ws[4]; ws[14] = rol32(ws[14] ^ ws[3], 8); + ws[9] += ws[14]; ws[4] = rol32(ws[4] ^ ws[9], 7); + } + + for (i = 0; i < CHACHA20_BLOCK_SIZE_WORDS; i++) + out[i] = le_bswap32(ws[i] + state[i]); + + state[12]++; +} + +static inline int drng_chacha20_selftest_one(struct chacha20_state *state, + uint32_t *expected) +{ + uint32_t result[CHACHA20_BLOCK_SIZE_WORDS]; + + chacha20_block(&state->constants[0], result); + return memcmp(expected, result, CHACHA20_BLOCK_SIZE); +} + +static int drng_chacha20_selftest(void) +{ + struct chacha20_state chacha20; + uint32_t expected[CHACHA20_BLOCK_SIZE_WORDS]; + + /* Test vector according to RFC 7539 section 2.3.2 */ + chacha20.constants[0] = 0x61707865; chacha20.constants[1] = 0x3320646e; + chacha20.constants[2] = 0x79622d32; chacha20.constants[3] = 0x6b206574; + chacha20.key.u[0] = 0x03020100; chacha20.key.u[1] = 0x07060504; + chacha20.key.u[2] = 0x0b0a0908; chacha20.key.u[3] = 0x0f0e0d0c; + chacha20.key.u[4] = 0x13121110; chacha20.key.u[5] = 0x17161514; + chacha20.key.u[6] = 0x1b1a1918; chacha20.key.u[7] = 0x1f1e1d1c; + chacha20.counter = 0x00000001; chacha20.nonce[0] = 0x09000000; + chacha20.nonce[1] = 0x4a000000; chacha20.nonce[2] = 0x00000000; + + expected[0] = 0xe4e7f110; expected[1] = 0x15593bd1; + expected[2] = 0x1fdd0f50; expected[3] = 0xc47120a3; + expected[4] = 0xc7f4d1c7; expected[5] = 0x0368c033; + expected[6] = 0x9aaa2204; expected[7] = 0x4e6cd4c3; + expected[8] = 0x466482d2; expected[9] = 0x09aa9f07; + expected[10] = 0x05d7c214; expected[11] = 0xa2028bd9; + expected[12] = 0xd19c12b5; expected[13] = 0xb94e16de; + expected[14] = 0xe883d0cb; expected[15] = 0x4e3c50a2; + + return drng_chacha20_selftest_one(&chacha20, &expected[0]); +} + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <limits.h> + +static int drng_random_get(uint8_t *buf, uint32_t buflen) +{ + int ret = randombytes(buf, buflen); + if( ret != 0) { + return 0; + } else { + return buflen; + } +} + +/******************************* ChaCha20 DRNG *******************************/ + +struct chacha20_drng { + struct chacha20_state chacha20; + time_t last_seeded; + uint64_t generated_bytes; +}; + +/** + * Update of the ChaCha20 state by generating one ChaCha20 block which is + * equal to the state of the ChaCha20. The generated block is XORed into + * the key part of the state. This shall ensure backtracking resistance as well + * as a proper mix of the ChaCha20 state once the key is injected. + */ +static inline void drng_chacha20_update(struct chacha20_state *chacha20, + uint32_t *buf, uint32_t used_words) +{ + uint32_t i, tmp[CHACHA20_BLOCK_SIZE_WORDS]; + + if (used_words > CHACHA20_KEY_SIZE_WORDS) { + chacha20_block(&chacha20->constants[0], tmp); + for (i = 0; i < CHACHA20_KEY_SIZE_WORDS; i++) + chacha20->key.u[i] ^= tmp[i]; + memset_secure(tmp, 0, sizeof(tmp)); + } else { + for (i = 0; i < CHACHA20_KEY_SIZE_WORDS; i++) + chacha20->key.u[i] ^= buf[i + used_words]; + } + + /* Deterministic increment of nonce as required in RFC 7539 chapter 4 */ + chacha20->nonce[0]++; + if (chacha20->nonce[0] == 0) + chacha20->nonce[1]++; + if (chacha20->nonce[1] == 0) + chacha20->nonce[2]++; + + /* Leave counter untouched as it is start value is undefined in RFC */ +} + +/** + * Seed the ChaCha20 DRNG by injecting the input data into the key part of + * the ChaCha20 state. If the input data is longer than the ChaCha20 key size, + * perform a ChaCha20 operation after processing of key size input data. + * This operation shall spread out the entropy into the ChaCha20 state before + * new entropy is injected into the key part. + * + * The approach taken here is logically similar to a CBC-MAC: The input data + * is processed chunk-wise. Each chunk is encrypted, the output is XORed with + * the next chunk of the input and then encrypted again. I.e. the + * ChaCha20 CBC-MAC of the seed data is injected into the DRNG state. + */ +static int drng_chacha20_seed(struct chacha20_state *chacha20, + const uint8_t *inbuf, uint32_t inbuflen) +{ + while (inbuflen) { + uint32_t i, todo = min(inbuflen, CHACHA20_KEY_SIZE); + + for (i = 0; i < todo; i++) + chacha20->key.b[i] ^= inbuf[i]; + + /* Break potential dependencies between the inbuf key blocks */ + drng_chacha20_update(chacha20, NULL, CHACHA20_BLOCK_SIZE_WORDS); + inbuf += todo; + inbuflen -= todo; + } + + return 0; +} + +/** + * Chacha20 DRNG generation of random numbers: the stream output of ChaCha20 + * is the random number. After the completion of the generation of the + * stream, the entire ChaCha20 state is updated. + * + * Note, as the ChaCha20 implements a 32 bit counter, we must ensure + * that this function is only invoked for at most 2^32 - 1 ChaCha20 blocks + * before a reseed or an update happens. This is ensured by the variable + * outbuflen which is a 32 bit integer defining the number of bytes to be + * generated by the ChaCha20 DRNG. At the end of this function, an update + * operation is invoked which implies that the 32 bit counter will never be + * overflown in this implementation. + */ +static int drng_chacha20_generate(struct chacha20_state *chacha20, + uint8_t *outbuf, uint32_t outbuflen) +{ + uint32_t aligned_buf[(CHACHA20_BLOCK_SIZE / sizeof(uint32_t))]; + uint32_t used = CHACHA20_BLOCK_SIZE_WORDS; + int zeroize_buf = 0; + + while (outbuflen >= CHACHA20_BLOCK_SIZE) { + if ((uintptr_t)outbuf & (sizeof(aligned_buf[0]) - 1)) { + chacha20_block(&chacha20->constants[0], aligned_buf); + memcpy(outbuf, aligned_buf, CHACHA20_BLOCK_SIZE); + zeroize_buf = 1; + } else { + chacha20_block(&chacha20->constants[0], + (uint32_t *)outbuf); + } + + outbuf += CHACHA20_BLOCK_SIZE; + outbuflen -= CHACHA20_BLOCK_SIZE; + } + + if (outbuflen) { + chacha20_block(&chacha20->constants[0], aligned_buf); + memcpy(outbuf, aligned_buf, outbuflen); + used = ((outbuflen + sizeof(aligned_buf[0]) - 1) / + sizeof(aligned_buf[0])); + zeroize_buf = 1; + } + + drng_chacha20_update(chacha20, aligned_buf, used); + + if (zeroize_buf) + memset_secure(aligned_buf, 0, sizeof(aligned_buf)); + + return 0; +} + +static int drng_chacha20_rng_selftest(struct chacha20_drng *drng) +{ + int ret; + uint8_t outbuf[CHACHA20_KEY_SIZE * 2] __aligned(sizeof(uint32_t)); + uint8_t seed[CHACHA20_KEY_SIZE * 2] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }; + + /* + * Expected result when ChaCha20 DRNG state is zero: + * * constants are set to "expand 32-byte k" + * * remaining state is 0 + * and pulling one ChaCha20 DRNG block. + */ + uint8_t expected_block[CHACHA20_KEY_SIZE] = { + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, + 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, + 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, + 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7 }; + + /* + * Expected result when ChaCha20 DRNG state is zero: + * * constants are set to "expand 32-byte k" + * * remaining state is 0 + * followed by a reseed with + * 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + * 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + * 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + * 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + * 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + * 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + * 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + * 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f + * and pulling two ChaCha20 DRNG blocks. + */ + uint8_t expected_twoblocks[CHACHA20_KEY_SIZE * 2] = { + 0xf5, 0xb4, 0xb6, 0x5a, 0xec, 0xcd, 0x5a, 0x65, + 0x87, 0x56, 0xe3, 0x86, 0x51, 0x54, 0xfc, 0x90, + 0x56, 0xff, 0x5e, 0xae, 0x58, 0xf2, 0x01, 0x88, + 0xb1, 0x7e, 0xb8, 0x2e, 0x17, 0x9a, 0x27, 0xe6, + 0x86, 0xb3, 0xed, 0x33, 0xf7, 0xb9, 0x06, 0x05, + 0x8a, 0x2d, 0x1a, 0x93, 0xc9, 0x0b, 0x80, 0x04, + 0x03, 0xaa, 0x60, 0xaf, 0xd5, 0x36, 0x40, 0x11, + 0x67, 0x89, 0xb1, 0x66, 0xd5, 0x88, 0x62, 0x6d }; + + /* + * Expected result when ChaCha20 DRNG state is zero: + * * constants are set to "expand 32-byte k" + * * remaining state is 0 + * followed by a reseed with + * 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + * 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + * 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + * 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + * 0x20 + * and pulling one ChaCha20 DRNG block plus one byte. + */ + uint8_t expected_block_and_byte[CHACHA20_KEY_SIZE + 1] = { + 0x3d, 0x13, 0x47, 0x1e, 0x7f, 0x7c, 0x99, 0x33, + 0xfc, 0x44, 0xa4, 0xdd, 0xf9, 0x3d, 0xe1, 0x9a, + 0xd4, 0xe8, 0x7a, 0x7d, 0x42, 0xac, 0xd1, 0xcd, + 0x10, 0x69, 0xe7, 0xbf, 0xd4, 0xfd, 0x69, 0x4b, + 0xa7 }; + + /* Generate with zero state */ + ret = drng_chacha20_generate(&drng->chacha20, outbuf, + sizeof(expected_block)); + if (ret) + return ret; + if (memcmp(outbuf, expected_block, sizeof(expected_block))) + return -EFAULT; + + /* Clear state of DRNG */ + memset(&drng->chacha20.key.u[0], 0, 48); + + /* Reseed with 2 blocks */ + ret = drng_chacha20_seed(&drng->chacha20, seed, + sizeof(expected_twoblocks)); + if (ret) + return ret; + ret = drng_chacha20_generate(&drng->chacha20, outbuf, + sizeof(expected_twoblocks)); + if (ret) + return ret; + if (memcmp(outbuf, expected_twoblocks, sizeof(expected_twoblocks))) + return -EFAULT; + + /* Clear state of DRNG */ + memset(&drng->chacha20.key.u[0], 0, 48); + + /* Reseed with 1 block and one byte */ + ret = drng_chacha20_seed(&drng->chacha20, seed, + sizeof(expected_block_and_byte)); + if (ret) + return ret; + ret = drng_chacha20_generate(&drng->chacha20, outbuf, + sizeof(expected_block_and_byte)); + if (ret) + return ret; + if (memcmp(outbuf, expected_block_and_byte, + sizeof(expected_block_and_byte))) + return -EFAULT; + + return 0; +} + +static void drng_chacha20_dealloc(struct chacha20_drng *drng) +{ + memset_secure(drng, 0, sizeof(*drng)); +#ifdef _WIN32 + _aligned_free(drng); +#else + free(drng); +#endif +} + +/** + * Allocation of the DRBG state + */ +static int drng_chacha20_alloc(struct chacha20_drng **out) +{ + struct chacha20_drng *drng; + uint32_t i, v = 0; + int ret = 0; + + if (drng_chacha20_selftest()) { + return -EFAULT; + } + +#ifdef _WIN32 + drng = _aligned_malloc(sizeof(*drng), CHACHA20_DRNG_ALIGNMENT); +#endif + +#ifndef aligned_alloc + drng = malloc(sizeof(*drng)); +#else + drng = aligned_alloc(CHACHA20_DRNG_ALIGNMENT, sizeof(*drng)); +#endif + if (drng == NULL) { + return -1; + } + +#ifndef _WIN32 + /* prevent paging out of the memory state to swap space */ + ret = mlock(drng, sizeof(*drng)); + if (ret && errno != EPERM && errno != EAGAIN) { + ret = -errno; + goto err; + } +#endif + + memset(drng, 0, sizeof(*drng)); + + memcpy(&drng->chacha20.constants[0], "expand 32-byte k", 16); + + ret = drng_chacha20_rng_selftest(drng); + if (ret) + goto err; + + /* Update the state left by the self test */ + for (i = 0; i < CHACHA20_KEY_SIZE_WORDS; i++) { + get_time(NULL, &v); + drng->chacha20.key.u[i] ^= v; + } + + for (i = 0; i < 3; i++) { + get_time(NULL, &v); + drng->chacha20.nonce[i] ^= v; + } + + *out = drng; + + return 0; + +err: + drng_chacha20_dealloc(drng); + return ret; +} + +/***************************** ChaCha20 DRNG API *****************************/ + +DSO_PUBLIC +int drng_chacha20_reseed(struct chacha20_drng *drng, const uint8_t *inbuf, + uint32_t inbuflen) +{ + uint8_t seed[CHACHA20_KEY_SIZE * 2]; + int ret; + uint32_t collected = 0; + + /* Entropy assumption: 1 data bit delivers one bit of entropy */ + ret = drng_random_get(seed, CHACHA20_KEY_SIZE); + if (ret < 0) + return ret; + + if (ret) { + collected += ret; + + ret = drng_chacha20_seed(&drng->chacha20, seed, + CHACHA20_KEY_SIZE); + if (ret) + return ret; + } + + memset_secure(seed, 0, sizeof(seed)); + + /* Internal noise sources must have delivered sufficient information */ + if (collected < CHACHA20_KEY_SIZE) + return -EFAULT; + + if (inbuf && inbuflen) + ret = drng_chacha20_seed(&drng->chacha20, inbuf, inbuflen); + + get_time(&drng->last_seeded, NULL); + drng->generated_bytes = 0; + + return ret; +} + +DSO_PUBLIC +void drng_chacha20_destroy(struct chacha20_drng *drng) +{ + drng_chacha20_dealloc(drng); +} + +DSO_PUBLIC +int drng_chacha20_init(struct chacha20_drng **drng) +{ + int ret = drng_chacha20_alloc(drng); + + if (ret) + return ret; + + ret = drng_chacha20_reseed(*drng, NULL, 0); + if (ret) { + drng_chacha20_destroy(*drng); + return ret; + } + + return 0; +} + +DSO_PUBLIC +int drng_chacha20_get(struct chacha20_drng *drng, uint8_t *outbuf, + uint32_t outbuflen) +{ + time_t now = 0; + uint32_t nsec; + int ret; + + get_time(&now, &nsec); + + /* + * Reseed if: + * * last seeding was more than 600 seconds ago + * * more than 1<<30 bytes were generated since last reseed + */ + if (((now - drng->last_seeded) > 600) || + (drng->generated_bytes > (1<<30))) { + ret = drng_chacha20_reseed(drng, (uint8_t *)&nsec, + sizeof(nsec)); + + if (ret) + return ret; + drng->last_seeded = now; + drng->generated_bytes = 0; + } else { + ret = drng_chacha20_seed(&drng->chacha20, (uint8_t *)&nsec, + sizeof(nsec)); + if (ret) + return ret; + } + + ret = drng_chacha20_generate(&drng->chacha20, outbuf, outbuflen); + if (ret) + return ret; + + drng->generated_bytes += outbuflen; + + return 0; +} + +DSO_PUBLIC +int drng_chacha20_versionstring(char *buf, size_t buflen) +{ + return snprintf(buf, buflen, "ChaCha20 DRNG %d.%d.%d", + MAJVERSION, MINVERSION, PATCHLEVEL); +} + +DSO_PUBLIC +uint32_t drng_chacha20_version(void) +{ + uint32_t version = 0; + + version = MAJVERSION * 1000000; + version += MINVERSION * 10000; + version += PATCHLEVEL * 100; + + return version; +} |