/* * Copyright (C) 2010-2012 Free Software Foundation, Inc. * Copyright (C) 2000, 2001, 2008 Niels Möller * * Author: Nikos Mavrogiannopoulos * * This file is part of GNUTLS. * * The GNUTLS library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ /* Here is the random generator layer. This code was based on the LSH * random generator (the trivia and device source functions for POSIX) * and modified to fit gnutls' needs. Relicenced with permission. * Original author Niels Möller. */ #include #include #include #include #include #include #include #include #include #include #define NONCE_KEY_SIZE SALSA20_KEY_SIZE #define SOURCES 2 #define RND_LOCK(ctx) if (gnutls_mutex_lock(&((ctx)->mutex))!=0) abort() #define RND_UNLOCK(ctx) if (gnutls_mutex_unlock(&((ctx)->mutex))!=0) abort() enum { RANDOM_SOURCE_TRIVIA = 0, RANDOM_SOURCE_DEVICE, }; struct nonce_ctx_st { struct salsa20_ctx ctx; unsigned int counter; void *mutex; unsigned int dfork; }; struct rnd_ctx_st { struct yarrow256_ctx yctx; struct yarrow_source ysources[SOURCES]; struct timespec device_last_read; time_t trivia_previous_time; time_t trivia_time_count; void *mutex; unsigned dfork; /* detect fork() */ }; static struct rnd_ctx_st rnd_ctx; /* after this number of bytes salsa20 will rekey */ #define NONCE_RESEED_BYTES (1048576) static struct nonce_ctx_st nonce_ctx; inline static unsigned int timespec_sub_sec(struct timespec *a, struct timespec *b) { return (a->tv_sec - b->tv_sec); } #define DEVICE_READ_INTERVAL (21600) /* universal functions */ static int do_trivia_source(struct rnd_ctx_st *ctx, int init, struct event_st *event) { unsigned entropy = 0; if (init) { ctx->trivia_time_count = 0; } else { ctx->trivia_time_count++; if (event->now.tv_sec != ctx->trivia_previous_time) { /* Count one bit of entropy if we either have more than two * invocations in one second, or more than two seconds * between invocations. */ if ((ctx->trivia_time_count > 2) || ((event->now.tv_sec - ctx->trivia_previous_time) > 2)) entropy++; ctx->trivia_time_count = 0; } } ctx->trivia_previous_time = event->now.tv_sec; return yarrow256_update(&ctx->yctx, RANDOM_SOURCE_TRIVIA, entropy, sizeof(*event), (void *) event); } #define DEVICE_READ_SIZE 16 #define DEVICE_READ_SIZE_MAX 32 static int do_device_source(struct rnd_ctx_st *ctx, int init, struct event_st *event) { unsigned int read_size = DEVICE_READ_SIZE; int ret; if (init) { memcpy(&ctx->device_last_read, &event->now, sizeof(ctx->device_last_read)); read_size = DEVICE_READ_SIZE_MAX; /* initially read more data */ } if ((init || (timespec_sub_sec(&event->now, &ctx->device_last_read) > DEVICE_READ_INTERVAL))) { /* More than 20 minutes since we last read the device */ uint8_t buf[DEVICE_READ_SIZE_MAX]; ret = _rnd_get_system_entropy(buf, read_size); if (ret < 0) return gnutls_assert_val(ret); memcpy(&ctx->device_last_read, &event->now, sizeof(ctx->device_last_read)); return yarrow256_update(&ctx->yctx, RANDOM_SOURCE_DEVICE, read_size * 8 / 2 /* we trust the RNG */ , read_size, buf); } return 0; } static void wrap_nettle_rnd_deinit(void *ctx) { _rnd_system_entropy_deinit(); gnutls_mutex_deinit(&nonce_ctx.mutex); nonce_ctx.mutex = NULL; gnutls_mutex_deinit(&rnd_ctx.mutex); rnd_ctx.mutex = NULL; } /* Initializes the nonce level random generator. * * the @nonce_key must be provided. * * @init must be non zero on first initialization, and * zero on any subsequent reinitializations. */ static int nonce_rng_init(struct nonce_ctx_st *ctx, uint8_t nonce_key[NONCE_KEY_SIZE], unsigned nonce_key_size, unsigned init) { uint8_t iv[8]; int ret; if (init == 0) { /* use the previous key to generate IV as well */ memset(iv, 0, sizeof(iv)); /* to prevent valgrind from whinning */ salsa20r12_crypt(&ctx->ctx, sizeof(iv), iv, iv); /* Add key continuity by XORing the new key with data generated * from the old key */ salsa20r12_crypt(&ctx->ctx, nonce_key_size, nonce_key, nonce_key); } else { _gnutls_fork_set_val(&ctx->dfork); /* when initializing read the IV from the system randomness source */ ret = _rnd_get_system_entropy(iv, sizeof(iv)); if (ret < 0) return gnutls_assert_val(ret); } salsa20_set_key(&ctx->ctx, nonce_key_size, nonce_key); salsa20_set_iv(&ctx->ctx, iv); zeroize_key(nonce_key, nonce_key_size); ctx->counter = 0; return 0; } /* API functions */ static int wrap_nettle_rnd_init(void **ctx) { int ret; struct event_st event; uint8_t nonce_key[NONCE_KEY_SIZE]; memset(&rnd_ctx, 0, sizeof(rnd_ctx)); ret = gnutls_mutex_init(&nonce_ctx.mutex); if (ret < 0) { gnutls_assert(); return ret; } ret = gnutls_mutex_init(&rnd_ctx.mutex); if (ret < 0) { gnutls_assert(); return ret; } ret = _rnd_system_entropy_init(); if (ret < 0) { gnutls_assert(); return ret; } /* initialize the main RNG */ yarrow256_init(&rnd_ctx.yctx, SOURCES, rnd_ctx.ysources); _rnd_get_event(&event); _gnutls_fork_set_val(&rnd_ctx.dfork); ret = do_device_source(&rnd_ctx, 1, &event); if (ret < 0) { gnutls_assert(); return ret; } ret = do_trivia_source(&rnd_ctx, 1, &event); if (ret < 0) { gnutls_assert(); return ret; } yarrow256_slow_reseed(&rnd_ctx.yctx); /* initialize the nonce RNG */ ret = _rnd_get_system_entropy(nonce_key, sizeof(nonce_key)); if (ret < 0) return gnutls_assert_val(ret); ret = nonce_rng_init(&nonce_ctx, nonce_key, sizeof(nonce_key), 1); if (ret < 0) return gnutls_assert_val(ret); return 0; } /* This is called when gnutls_global_init() is called for second time. * It must check whether any resources are still available. * The particular problem it solves is to verify that the urandom fd is still * open (for applications that for some reason closed all fds */ static int wrap_nettle_rnd_check(void **ctx) { return _rnd_system_entropy_check(); } static int wrap_nettle_rnd_nonce(void *_ctx, void *data, size_t datasize) { int ret, reseed = 0; uint8_t nonce_key[NONCE_KEY_SIZE]; /* we don't really need memset here, but otherwise we * get filled with valgrind warnings */ memset(data, 0, datasize); RND_LOCK(&nonce_ctx); if (_gnutls_fork_detected(&nonce_ctx.dfork)) { reseed = 1; } if (reseed != 0 || nonce_ctx.counter > NONCE_RESEED_BYTES) { /* reseed nonce */ ret = _rnd_get_system_entropy(nonce_key, sizeof(nonce_key)); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = nonce_rng_init(&nonce_ctx, nonce_key, sizeof(nonce_key), 0); if (ret < 0) { gnutls_assert(); goto cleanup; } } salsa20r12_crypt(&nonce_ctx.ctx, datasize, data, data); nonce_ctx.counter += datasize; ret = 0; cleanup: RND_UNLOCK(&nonce_ctx); return ret; } static int wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize) { int ret, reseed = 0; struct event_st event; if (level == GNUTLS_RND_NONCE) return wrap_nettle_rnd_nonce(_ctx, data, datasize); _rnd_get_event(&event); RND_LOCK(&rnd_ctx); if (_gnutls_fork_detected(&rnd_ctx.dfork)) { /* fork() detected */ memset(&rnd_ctx.device_last_read, 0, sizeof(rnd_ctx.device_last_read)); reseed = 1; } /* reseed main */ ret = do_trivia_source(&rnd_ctx, 0, &event); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = do_device_source(&rnd_ctx, 0, &event); if (ret < 0) { gnutls_assert(); goto cleanup; } if (reseed != 0) yarrow256_slow_reseed(&rnd_ctx.yctx); yarrow256_random(&rnd_ctx.yctx, datasize, data); ret = 0; cleanup: RND_UNLOCK(&rnd_ctx); return ret; } static void wrap_nettle_rnd_refresh(void *_ctx) { uint8_t nonce_key[NONCE_KEY_SIZE]; /* this call refreshes the random context */ wrap_nettle_rnd(&rnd_ctx, GNUTLS_RND_RANDOM, nonce_key, sizeof(nonce_key)); RND_LOCK(&nonce_ctx); nonce_rng_init(&nonce_ctx, nonce_key, sizeof(nonce_key), 0); RND_UNLOCK(&nonce_ctx); return; } int crypto_rnd_prio = INT_MAX; gnutls_crypto_rnd_st _gnutls_rnd_ops = { .init = wrap_nettle_rnd_init, .check = wrap_nettle_rnd_check, .deinit = wrap_nettle_rnd_deinit, .rnd = wrap_nettle_rnd, .rnd_refresh = wrap_nettle_rnd_refresh, .self_test = NULL, };