/* Quick-and-dirty cryptographic RNG based on BLAKE2s * * This system uses a 32-byte BLAKE2s hash as internal state seeded by somewhat random post-powerup SRAM content, the * unique device ID and the program flash contents. This seed state is mixed with values from the hardware RNG for each * 32-byte block of output data. * * The RNG's chaining looks like the following, with H(...) being the BLAKE2s hash function, | being binary * concatenation and hw_rng(...) being the hardware RNG. c and e are the fixed extraction and chain string constants * defined below. * * Seed: state = H(SRAM | FLASH | hw_rng(64 byte)) * * Extract: state = H(state | c | hw_rng(64 byte)) block[0] = H(state | e) * state = H(state | c | hw_rng(64 byte)) block[1] = H(state | e) * [...] * state = H(state | c | hw_rng(64 byte)) block[n] = H(state | e) * state = H(state | c | hw_rng(64 byte)) * * * Graphically, with C = H( state | c | rng ) being the chaining function * and X = H( state | e ) being the extraction function * this becomes: * * rng rng rng rng * | | | | * v v v v * state ---> [C] ---> [C] -- . . . --> [C] ---> [C] ---> new state * | | | * v v v * [X] [X] [X] * | | | * v v v * out[0] out[1] . . . out[n] * */ #include #include #include #include #include "usart_helpers.h" #include "rand_stm32.h" #include "tracing.h" #include "crypto/noise-c/src/protocol/internal.h" #include "crypto/noise-c/src/crypto/blake2/blake2s.h" /* FIXME persist state in backup sram */ extern unsigned _ram_start, _ram_end, _rom_start, _rom_end; static uint8_t global_stm_rand_state[BLAKE2S_HASH_SIZE]; static uint32_t stm32_read_rng_raw(void) { if ((RNG_SR & (RNG_SR_SEIS | RNG_SR_CEIS)) || !(RNG_CR & RNG_CR_RNGEN)) { LOG_PRINTF("RNG error detected, bailing out.\n"); exit(1); } while (!(RNG_SR & RNG_SR_DRDY)) ; return RNG_DR; } static void rng_seed_blake(BLAKE2s_context_t *bc) { /* This pulls out 64 bytes. Even though the resulting BLAKE2s hash only is 32 bytes large, the internal state of * BLAKE2s is larger. Also I don't quite trust the STM32F4's hardware RNG. */ for (int i=0; i<16; i++) { uint32_t val = stm32_read_rng_raw(); BLAKE2s_update(bc, &val, sizeof(val)); } } void rand_init() { RNG_CR |= RNG_CR_RNGEN; BLAKE2s_context_t bc; BLAKE2s_reset(&bc); /* Seed with entire SRAM area */ BLAKE2s_update(&bc, &_ram_start, &_ram_end - &_ram_start); /* Seed with entire flash area. This includes the device unique ID if it has not been overwritten. */ BLAKE2s_update(&bc, &_rom_start, &_rom_end - &_rom_start); /* Seed with 64 bytes of handware RNG input */ rng_seed_blake(&bc); /* FIXME use ADC to seeed */ BLAKE2s_finish(&bc, global_stm_rand_state); /* FIXME make sure this is not optimized out */ memset(&bc, 0, sizeof(bc)); } const char *extraction_constant = "Blake2 RNG extraction constant"; const char *chain_constant = "Blake2 RNG chaining constant"; void noise_rand_bytes(void *bytes, size_t size) { TRACING_SET(TR_RNG); BLAKE2s_context_t out_ctx, chain_ctx; uint8_t *out = (uint8_t *)bytes; uint8_t hash_buf[BLAKE2S_HASH_SIZE]; for (size_t wr_pos = 0; wr_pos