/* Jitter RNG: Noise Sources * * Copyright (C) 2021 - 2022, Stephan Mueller * * License: see LICENSE 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 "jitterentropy-noise.h" #include "jitterentropy-health.h" #include "jitterentropy-timer.h" #include "jitterentropy-sha3.h" #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) /*************************************************************************** * Noise sources ***************************************************************************/ /** * Update of the loop count used for the next round of * an entropy collection. * * @ec [in] entropy collector struct * @bits [in] is the number of low bits of the timer to consider * @min [in] is the number of bits we shift the timer value to the right at * the end to make sure we have a guaranteed minimum value * * @return Newly calculated loop counter */ static uint64_t jent_loop_shuffle(struct rand_data *ec, unsigned int bits, unsigned int min) { #ifdef JENT_CONF_DISABLE_LOOP_SHUFFLE (void)ec; (void)bits; return (UINT64_C(1)< i; i++) { shuffle ^= time & mask; time = time >> bits; } /* * We add a lower boundary value to ensure we have a minimum * RNG loop count. */ return (shuffle + (UINT64_C(1)< 63); uint64_t hash_loop_cnt = jent_loop_shuffle(ec, MAX_HASH_LOOP, MIN_HASH_LOOP); /* Use the memset to shut up valgrind */ memset(intermediary, 0, sizeof(intermediary)); sha3_256_init(&ctx); /* * testing purposes -- allow test app to set the counter, not * needed during runtime */ if (loop_cnt) hash_loop_cnt = loop_cnt; /* * This loop fills a buffer which is injected into the entropy pool. * The main reason for this loop is to execute something over which we * can perform a timing measurement. The injection of the resulting * data into the pool is performed to ensure the result is used and * the compiler cannot optimize the loop away in case the result is not * used at all. Yet that data is considered "additional information" * considering the terminology from SP800-90A without any entropy. * * Note, it does not matter which or how much data you inject, we are * interested in one Keccack1600 compression operation performed with * the sha3_final. */ for (j = 0; j < hash_loop_cnt; j++) { sha3_update(&ctx, intermediary, sizeof(intermediary)); sha3_update(&ctx, (uint8_t *)&ec->rct_count, sizeof(ec->rct_count)); sha3_update(&ctx, (uint8_t *)&ec->apt_cutoff, sizeof(ec->apt_cutoff)); sha3_update(&ctx, (uint8_t *)&ec->apt_observations, sizeof(ec->apt_observations)); sha3_update(&ctx, (uint8_t *)&ec->apt_count, sizeof(ec->apt_count)); sha3_update(&ctx,(uint8_t *) &ec->apt_base, sizeof(ec->apt_base)); sha3_update(&ctx, (uint8_t *)&j, sizeof(uint64_t)); sha3_final(&ctx, intermediary); } /* * Inject the data from the previous loop into the pool. This data is * not considered to contain any entropy, but it stirs the pool a bit. */ sha3_update(ec->hash_state, intermediary, sizeof(intermediary)); /* * Insert the time stamp into the hash context representing the pool. * * If the time stamp is stuck, do not finally insert the value into the * entropy pool. Although this operation should not do any harm even * when the time stamp has no entropy, SP800-90B requires that any * conditioning operation to have an identical amount of input data * according to section 3.1.5. */ if (!stuck) sha3_update(ec->hash_state, (uint8_t *)&time, sizeof(uint64_t)); jent_memset_secure(&ctx, SHA_MAX_CTX_SIZE); jent_memset_secure(intermediary, sizeof(intermediary)); } #define MAX_ACC_LOOP_BIT 7 #define MIN_ACC_LOOP_BIT 0 #ifdef JENT_RANDOM_MEMACCESS static inline uint32_t uint32rotl(const uint32_t x, int k) { return (x << k) | (x >> (32 - k)); } static inline uint32_t xoshiro128starstar(uint32_t *s) { const uint32_t result = uint32rotl(s[1] * 5, 7) * 9; const uint32_t t = s[1] << 9; s[2] ^= s[0]; s[3] ^= s[1]; s[1] ^= s[2]; s[0] ^= s[3]; s[2] ^= t; s[3] = uint32rotl(s[3], 11); return result; } static void jent_memaccess(struct rand_data *ec, uint64_t loop_cnt) { uint64_t i = 0, time = 0; union { uint32_t u[4]; uint8_t b[sizeof(uint32_t) * 4]; } prngState = { .u = {0x8e93eec0, 0xce65608a, 0xa8d46b46, 0xe83cef69} }; uint32_t addressMask; /* Ensure that macros cannot overflow jent_loop_shuffle() */ BUILD_BUG_ON((MAX_ACC_LOOP_BIT + MIN_ACC_LOOP_BIT) > 63); uint64_t acc_loop_cnt = jent_loop_shuffle(ec, MAX_ACC_LOOP_BIT, MIN_ACC_LOOP_BIT); if (NULL == ec || NULL == ec->mem) return; addressMask = ec->memmask; /* * Mix the current data into prngState * * Any time you see a PRNG in a noise source, you should be concerned. * * The PRNG doesn’t directly produce the raw noise, it just adjusts the * location being updated. The timing of the update is part of the raw * sample. The main thing this process gets you isn’t better * “per-update” timing, it gets you mostly independent “per-update” * timing, so we can now benefit from the Central Limit Theorem! */ for (i = 0; i < sizeof(prngState); i++) { jent_get_nstime_internal(ec, &time); prngState.b[i] ^= (uint8_t)(time & 0xff); } /* * testing purposes -- allow test app to set the counter, not * needed during runtime */ if (loop_cnt) acc_loop_cnt = loop_cnt; for (i = 0; i < (ec->memaccessloops + acc_loop_cnt); i++) { /* Take PRNG output to find the memory location to update. */ unsigned char *tmpval = ec->mem + (xoshiro128starstar(prngState.u) & addressMask); /* * memory access: just add 1 to one byte, * wrap at 255 -- memory access implies read * from and write to memory location */ *tmpval = (unsigned char)((*tmpval + 1) & 0xff); } } #else /* JENT_RANDOM_MEMACCESS */ /** * Memory Access noise source -- this is a noise source based on variations in * memory access times * * This function performs memory accesses which will add to the timing * variations due to an unknown amount of CPU wait states that need to be * added when accessing memory. The memory size should be larger than the L1 * caches as outlined in the documentation and the associated testing. * * The L1 cache has a very high bandwidth, albeit its access rate is usually * slower than accessing CPU registers. Therefore, L1 accesses only add minimal * variations as the CPU has hardly to wait. Starting with L2, significant * variations are added because L2 typically does not belong to the CPU any more * and therefore a wider range of CPU wait states is necessary for accesses. * L3 and real memory accesses have even a wider range of wait states. However, * to reliably access either L3 or memory, the ec->mem memory must be quite * large which is usually not desirable. * * @ec [in] Reference to the entropy collector with the memory access data -- if * the reference to the memory block to be accessed is NULL, this noise * source is disabled * @loop_cnt [in] if a value not equal to 0 is set, use the given value as * number of loops to perform the hash operation */ static void jent_memaccess(struct rand_data *ec, uint64_t loop_cnt) { unsigned int wrap = 0; uint64_t i = 0; /* Ensure that macros cannot overflow jent_loop_shuffle() */ BUILD_BUG_ON((MAX_ACC_LOOP_BIT + MIN_ACC_LOOP_BIT) > 63); uint64_t acc_loop_cnt = jent_loop_shuffle(ec, MAX_ACC_LOOP_BIT, MIN_ACC_LOOP_BIT); if (NULL == ec || NULL == ec->mem) return; wrap = ec->memblocksize * ec->memblocks; /* * testing purposes -- allow test app to set the counter, not * needed during runtime */ if (loop_cnt) acc_loop_cnt = loop_cnt; for (i = 0; i < (ec->memaccessloops + acc_loop_cnt); i++) { unsigned char *tmpval = ec->mem + ec->memlocation; /* * memory access: just add 1 to one byte, * wrap at 255 -- memory access implies read * from and write to memory location */ *tmpval = (unsigned char)((*tmpval + 1) & 0xff); /* * Addition of memblocksize - 1 to pointer * with wrap around logic to ensure that every * memory location is hit evenly */ ec->memlocation = ec->memlocation + ec->memblocksize - 1; ec->memlocation = ec->memlocation % wrap; } } #endif /* JENT_RANDOM_MEMACCESS */ /*************************************************************************** * Start of entropy processing logic ***************************************************************************/ /** * This is the heart of the entropy generation: calculate time deltas and * use the CPU jitter in the time deltas. The jitter is injected into the * entropy pool. * * WARNING: ensure that ->prev_time is primed before using the output * of this function! This can be done by calling this function * and not using its result. * * @ec [in] Reference to entropy collector * @loop_cnt [in] see jent_hash_time * @ret_current_delta [out] Test interface: return time delta - may be NULL * * @return: result of stuck test */ unsigned int jent_measure_jitter(struct rand_data *ec, uint64_t loop_cnt, uint64_t *ret_current_delta) { uint64_t time = 0; uint64_t current_delta = 0; unsigned int stuck; /* Invoke one noise source before time measurement to add variations */ jent_memaccess(ec, loop_cnt); /* * Get time stamp and calculate time delta to previous * invocation to measure the timing variations */ jent_get_nstime_internal(ec, &time); current_delta = jent_delta(ec->prev_time, time) / ec->jent_common_timer_gcd; ec->prev_time = time; /* Check whether we have a stuck measurement. */ stuck = jent_stuck(ec, current_delta); /* Now call the next noise sources which also injects the data */ jent_hash_time(ec, current_delta, loop_cnt, stuck); /* return the raw entropy value */ if (ret_current_delta) *ret_current_delta = current_delta; return stuck; } /** * Generator of one 256 bit random number * Function fills rand_data->hash_state * * @ec [in] Reference to entropy collector */ void jent_random_data(struct rand_data *ec) { unsigned int k = 0, safety_factor = 0; if (ec->fips_enabled) safety_factor = ENTROPY_SAFETY_FACTOR; /* priming of the ->prev_time value */ jent_measure_jitter(ec, 0, NULL); while (!jent_health_failure(ec)) { /* If a stuck measurement is received, repeat measurement */ if (jent_measure_jitter(ec, 0, NULL)) continue; /* * We multiply the loop value with ->osr to obtain the * oversampling rate requested by the caller */ if (++k >= ((DATA_SIZE_BITS + safety_factor) * ec->osr)) break; } } void jent_read_random_block(struct rand_data *ec, char *dst, size_t dst_len) { uint8_t jent_block[SHA3_256_SIZE_DIGEST]; BUILD_BUG_ON(SHA3_256_SIZE_DIGEST != (DATA_SIZE_BITS / 8)); /* The final operation automatically re-initializes the ->hash_state */ sha3_final(ec->hash_state, jent_block); if (dst_len) memcpy(dst, jent_block, dst_len); /* * Stir the new state with the data from the old state - the digest * of the old data is not considered to have entropy. */ sha3_update(ec->hash_state, jent_block, sizeof(jent_block)); jent_memset_secure(jent_block, sizeof(jent_block)); }