From 050d49a56bc54c78c3fc1f4f125b09634d6c5b83 Mon Sep 17 00:00:00 2001 From: jaseg Date: Thu, 8 Nov 2018 15:41:46 +0900 Subject: Noise integration compiles --- src/CMakeLists.txt | 9 ++ src/cobs.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++ src/crypto/CMakeLists.txt | 93 +++++++++++++++ src/demo.c | 172 ++++++++++++++++++++++++++- src/rand_stm32.c | 134 +++++++++++++++++++++ src/rand_stm32.h | 9 ++ 6 files changed, 706 insertions(+), 3 deletions(-) create mode 100644 src/cobs.c create mode 100644 src/crypto/CMakeLists.txt create mode 100644 src/rand_stm32.c create mode 100644 src/rand_stm32.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25d8683..cb50069 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,16 +24,25 @@ add_library (usbhost cobs.c ) +add_subdirectory (crypto) + +add_definitions ( + -DBLAKE2S_USE_VECTOR_MATH=0 +) + target_link_libraries (usbhost + noise ${LIBOPENCM3_LIB} ) add_executable (demo + rand_stm32.c demo.c ) target_link_libraries (demo usbhost + noise ) add_custom_command (TARGET demo diff --git a/src/cobs.c b/src/cobs.c new file mode 100644 index 0000000..84991f3 --- /dev/null +++ b/src/cobs.c @@ -0,0 +1,292 @@ + +#include + +/*@ requires \valid(dst + (0..dstlen-1)); + @ requires \valid_read(src + (0..srclen-1)); + @ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1)); + @ + @ behavior valid: + @ assumes 0 <= srclen <= 254; + @ assumes 0 <= dstlen <= 65535; + @ assumes dstlen >= srclen+2; + @ assigns dst[0..srclen+1]; + @ ensures \forall integer i; (0 <= i < srclen && \old(src[i]) != 0) ==> dst[i+1] == src[i]; + @ ensures \result == srclen+2; + @ ensures \forall integer i; 0 <= i <= srclen ==> dst[i] != 0; + @ ensures dst[srclen+1] == 0; + @ + @ behavior invalid: + @ assumes srclen < 0 || srclen > 254 + @ || dstlen < 0 || dstlen > 65535 + @ || dstlen < srclen+2; + @ assigns \nothing; + @ ensures \result == -1; + @ + @ complete behaviors; + @ disjoint behaviors; + @*/ +ssize_t cobs_encode(char *dst, size_t dstlen, char *src, size_t srclen) { + if (dstlen > 65535 || srclen > 254) + return -1; + //@ assert 0 <= dstlen <= 65535 && 0 <= srclen <= 254; + + if (dstlen < srclen+2) + return -1; + //@ assert 0 <= srclen < srclen+2 <= dstlen; + + size_t p = 0; + /*@ loop invariant 0 <= p <= srclen+1; + @ loop invariant \forall integer i; 0 <= i < p ==> dst[i] != 0; + @ loop invariant \forall integer i; 0 < i < p ==> (src[i-1] != 0 ==> dst[i] == src[i-1]); + @ loop assigns p, dst[0..srclen+1]; + @ loop variant srclen-p+1; + @*/ + while (p <= srclen) { + + char val; + if (p != 0 && src[p-1] != 0) { + val = src[p-1]; + + } else { + size_t q = p; + /*@ loop invariant 0 <= p <= q <= srclen; + @ loop invariant \forall integer i; p <= i < q ==> src[i] != 0; + @ loop assigns q; + @ loop variant srclen-q; + @*/ + while (q < srclen && src[q] != 0) + q++; + //@ assert q == srclen || src[q] == 0; + //@ assert q <= srclen <= 254; + val = (char)q-p+1; + //@ assert val != 0; + } + + dst[p] = val; + p++; + } + + dst[p] = 0; + //@ assert p == srclen+1; + + return srclen+2; +} + +int cobs_encode_incremental(void *f, int (*output)(void *f, char c), char *src, size_t srclen) { + if (srclen > 254) + return -1; + //@ assert 0 <= srclen <= 254; + + size_t p = 0; + /*@ loop invariant 0 <= p <= srclen+1; + @ loop assigns p; + @ loop variant srclen-p+1; + @*/ + while (p <= srclen) { + + char val; + if (p != 0 && src[p-1] != 0) { + val = src[p-1]; + + } else { + size_t q = p; + /*@ loop invariant 0 <= p <= q <= srclen; + @ loop invariant \forall integer i; p <= i < q ==> src[i] != 0; + @ loop assigns q; + @ loop variant srclen-q; + @*/ + while (q < srclen && src[q] != 0) + q++; + //@ assert q == srclen || src[q] == 0; + //@ assert q <= srclen <= 254; + val = (char)q-p+1; + //@ assert val != 0; + } + + int rv = output(f, val); + if (rv) + return rv; + p++; + } + + int rv = output(f, 0); + if (rv) + return rv; + //@ assert p == srclen+1; + + return 0; +} + +/*@ requires \valid(dst + (0..dstlen-1)); + @ requires \valid_read(src + (0..srclen-1)); + @ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1)); + @ + @ behavior maybe_valid_frame: + @ assumes 1 <= srclen <= dstlen <= 65535; + @ assumes \exists integer j; j > 0 && \forall integer i; 0 <= i < j ==> src[i] != 0; + @ assumes \exists integer i; 0 <= i < srclen && src[i] == 0; + @ assigns dst[0..dstlen-1]; + @ ensures \result >= 0 || \result == -3; + @ ensures \result >= 0 ==> src[\result+1] == 0; + @ ensures \result >= 0 ==> (\forall integer i; 0 <= i < \result ==> src[i] != 0); + @ + @ behavior invalid_frame: + @ assumes 1 <= srclen <= dstlen <= 65535; + @ assumes src[0] == 0 || \forall integer i; 0 <= i < srclen ==> src[i] != 0; + @ assigns dst[0..dstlen-1]; + @ ensures \result == -2; + @ + @ behavior invalid_buffers: + @ assumes dstlen < 0 || dstlen > 65535 + @ || srclen < 1 || srclen > 65535 + @ || dstlen < srclen; + @ assigns \nothing; + @ ensures \result == -1; + @ + @ complete behaviors; + @ disjoint behaviors; + @*/ +ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen) { + if (dstlen > 65535 || srclen > 65535) + return -1; + + if (srclen < 1) + return -1; + + if (dstlen < srclen) + return -1; + + size_t p = 1; + size_t c = (unsigned char)src[0]; + //@ assert 0 <= c < 256; + //@ assert 0 <= c; + //@ assert c < 256; + if (c == 0) + return -2; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */ + //@ assert c >= 0; + //@ assert c != 0; + //@ assert c <= 257; + //@ assert c > 0; + //@ assert c >= 0 && c != 0 ==> c > 0; + + /*@ //loop invariant \forall integer i; 0 <= i <= p ==> (i == srclen || src[i] != 0); + @ loop invariant \forall integer i; 1 <= i < p ==> src[i] != 0; + @ loop invariant c > 0; + @ loop invariant 1 <= p <= srclen <= dstlen <= 65535; + @ loop invariant \separated(dst + (0..dstlen-1), src + (0..srclen-1)); + @ loop invariant \valid_read(src + (0..srclen-1)); + @ loop invariant \forall integer i; 1 <= i <= srclen ==> \valid(dst + i - 1); + @ loop assigns dst[0..dstlen-1], p, c; + @ loop variant srclen-p; + @*/ + while (p < srclen && src[p]) { + char val; + c--; + + //@ assert src[p] != 0; + if (c == 0) { + c = (unsigned char)src[p]; + val = 0; + } else { + val = src[p]; + } + + //@ assert 0 <= p-1 <= dstlen-1; + dst[p-1] = val; + p++; + } + + if (p == srclen) + return -2; /* Invalid framing. The terminating null byte should always be present in the input buffer. */ + + if (c != 1) + return -3; /* Invalid framing. The skip counter does not hit the end of the frame. */ + + //@ assert 0 < p <= srclen <= 65535; + //@ assert src[p] == 0; + //@ assert \forall integer i; 1 <= i < p ==> src[i] != 0; + return p-1; +} + +void cobs_decode_incremental_initialize(struct cobs_decode_state *state) { + state->p = 0; + state->c = 0; +} + +int cobs_decode_incremental(struct cobs_decode_state *state, char *dst, size_t dstlen, char src) { + if (state->p == 0) { + if (src == 0) + goto errout; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */ + state->c = (unsigned char)src; + state->p++; + return 0; + } + + if (!src) { + if (state->c != 1) + goto errout; /* Invalid framing. The skip counter does not hit the end of the frame. */ + int rv = state->p-1; + cobs_decode_incremental_initialize(state); + return rv; + } + + char val; + state->c--; + + if (state->c == 0) { + state->c = (unsigned char)src; + val = 0; + } else { + val = src; + } + + size_t pos = state->p-1; + if (pos >= dstlen) + return -2; /* output buffer too small */ + dst[pos] = val; + state->p++; + return 0; + +errout: + cobs_decode_incremental_initialize(state); + return -1; +} + +#ifdef VALIDATION +/*@ + @ requires 0 <= d < 256; + @ assigns \nothing; + @*/ +size_t test(char foo, unsigned int d) { + unsigned int c = (unsigned char)foo; + if (c != 0) { + //@ assert c < 256; + //@ assert c >= 0; + //@ assert c != 0; + //@ assert c > 0; + } + if (d != 0) { + //@ assert d >= 0; + //@ assert d != 0; + //@ assert d > 0; + } + return c + d; +} + +#include <__fc_builtin.h> + +void main(void) { + char inbuf[254]; + char cobsbuf[256]; + char outbuf[256]; + + size_t range = Frama_C_interval(0, sizeof(inbuf)); + Frama_C_make_unknown((char *)inbuf, range); + + cobs_encode(cobsbuf, sizeof(cobsbuf), inbuf, sizeof(inbuf)); + cobs_decode(outbuf, sizeof(outbuf), cobsbuf, sizeof(cobsbuf)); + + //@ assert \forall integer i; 0 <= i < sizeof(inbuf) ==> outbuf[i] == inbuf[i]; +} +#endif//VALIDATION + diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt new file mode 100644 index 0000000..b21dc2b --- /dev/null +++ b/src/crypto/CMakeLists.txt @@ -0,0 +1,93 @@ + +include_directories ( + noise-c/include + noise-c/include/noise/keys + noise-c/src + noise-c/src/crypto/goldilocks/include + noise-c/src/crypto/goldilocks/src/include + noise-c/src/crypto/goldilocks/src/p448/arch_arm_32 + noise-c/src/crypto/goldilocks/src/p448 + noise-c/src/protocol +) + +add_library (noise + noise-c/src/protocol/util.c + noise-c/src/protocol/patterns.c + noise-c/src/protocol/signstate.c + noise-c/src/protocol/randstate.c + noise-c/src/protocol/symmetricstate.c + noise-c/src/protocol/internal.c + noise-c/src/protocol/names.c + noise-c/src/protocol/hashstate.c + noise-c/src/protocol/errors.c + noise-c/src/protocol/cipherstate.c + noise-c/src/protocol/handshakestate.c + noise-c/src/protocol/dhstate.c + noise-c/src/keys/certificate.c + noise-c/src/keys/loader.c + noise-c/src/crypto/sha2/sha256.c + noise-c/src/crypto/sha2/sha512.c + noise-c/src/crypto/ghash/ghash.c + noise-c/src/crypto/ed25519/ed25519.c + noise-c/src/crypto/blake2/blake2s.c + noise-c/src/crypto/blake2/blake2b.c + noise-c/src/crypto/chacha/chacha.c + noise-c/src/crypto/goldilocks/src/ec_point.c + noise-c/src/crypto/goldilocks/src/sha512.c + noise-c/src/crypto/goldilocks/src/p448/arch_32/p448.c + noise-c/src/crypto/goldilocks/src/p448/f_arithmetic.c + noise-c/src/crypto/goldilocks/src/p448/arch_arm_32/p448.c + noise-c/src/crypto/goldilocks/src/p448/magic.c + noise-c/src/crypto/goldilocks/src/barrett_field.c + noise-c/src/crypto/goldilocks/src/goldilocks.c + noise-c/src/crypto/goldilocks/src/arithmetic.c + noise-c/src/crypto/goldilocks/src/crandom.c + noise-c/src/crypto/goldilocks/src/scalarmul.c + noise-c/src/crypto/newhope/poly.c + noise-c/src/crypto/newhope/randombytes.c + noise-c/src/crypto/newhope/reduce.c + noise-c/src/crypto/newhope/ntt.c + noise-c/src/crypto/newhope/crypto_stream_chacha20.c + noise-c/src/crypto/newhope/error_correction.c + noise-c/src/crypto/newhope/batcher.c + noise-c/src/crypto/newhope/fips202.c + noise-c/src/crypto/newhope/newhope.c + noise-c/src/crypto/newhope/precomp.c + noise-c/src/crypto/aes/rijndael-alg-fst.c + noise-c/src/crypto/curve448/curve448.c + noise-c/src/crypto/donna/poly1305-donna.c + noise-c/src/crypto/donna/curve25519-donna.c + noise-c/src/protobufs/protobufs.c + noise-c/src/backend/ref/sign-ed25519.c + noise-c/src/backend/ref/hash-blake2b.c + noise-c/src/backend/ref/hash-sha512.c + noise-c/src/backend/ref/hash-sha256.c + noise-c/src/backend/ref/cipher-aesgcm.c + noise-c/src/backend/ref/cipher-chachapoly.c + noise-c/src/backend/ref/dh-curve25519.c + noise-c/src/backend/ref/dh-newhope.c + noise-c/src/backend/ref/dh-curve448.c + noise-c/src/backend/ref/hash-blake2s.c +) + +add_definitions ( + -DUSE_LIBSODIUM=0 + -DUSE_SODIUM=0 + -DHAVE_PTHREAD=0 + -DUSE_OPENSSL=0 + -D__WORDSIZE=32 + -D__BIG_ENDIAN=4321 + -D__LITTLE_ENDIAN=1234 + -D__BYTE_ORDER=__LITTLE_ENDIAN + -DED25519_CUSTOMRANDOM=1 + -DED25519_CUSTOMHASH=1 + -DED25519_REFHASH=1 + -DBLAKE2S_USE_VECTOR_MATH=0 + -DEXPERIMENT_CRANDOM_CUTOFF_BYTES=0 + -D__clang__=0 +) + +set (CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} -Wno-implicit-fallthrough -Wno-shadow -Wno-unused-parameter" +) + diff --git a/src/demo.c b/src/demo.c index fdd61e7..a4debe5 100644 --- a/src/demo.c +++ b/src/demo.c @@ -25,6 +25,8 @@ #include "usbh_lld_stm32f4.h" /// provides low level usb host driver for stm32f4 platform #include "usbh_driver_hid.h" /// provides generic usb device driver for Human Interface Device (HID) #include "usbh_driver_hub.h" /// provides usb full speed hub driver (Low speed devices on hub are not supported) +#include "cobs.h" +#include "rand_stm32.h" // STM32f407 compatible #include @@ -41,6 +43,11 @@ #include #include +#include + +void _fini(void); +int generate_identity_key(void); + static inline void delay_ms_busy_loop(uint32_t ms) { for (volatile uint32_t i = 0; i < 14903*ms; i++); } @@ -59,6 +66,8 @@ static void clock_setup(void) { rcc_periph_clock_enable(RCC_TIM6); rcc_periph_clock_enable(RCC_DMA2); rcc_periph_clock_enable(RCC_DMA1); + + rcc_periph_clock_enable(RCC_RNG); } @@ -182,6 +191,96 @@ void dma1_stream6_isr(void) { schedule_dma(usart2_out); } +static struct cobs_decode_state host_cobs_state; +#define CURVE25519_KEY_LEN 32 +#define MAX_HOST_PACKET_SIZE 256 +static volatile uint8_t host_packet_buf[MAX_HOST_PACKET_SIZE]; +static volatile uint8_t host_packet_length = 0; + +void usart2_isr(void) { + if (USART2_SR & USART_SR_ORE) { /* Overrun handling */ + LOG_PRINTF("USART2 data register overrun\n"); + /* Clear interrupt flag */ + (void)USART2_DR; /* FIXME make sure this read is not optimized out */ + return; + } + + uint8_t data = USART2_DR; /* This automatically acknowledges the IRQ */ + + if (host_packet_length) { + LOG_PRINTF("USART2 COBS buffer overrun\n"); + return; + } + + ssize_t rv = cobs_decode_incremental(&host_cobs_state, (char *)host_packet_buf, sizeof(host_packet_buf), data); + if (rv == -2) { + LOG_PRINTF("Host interface COBS packet too large\n"); + } else if (rv < 0) { + LOG_PRINTF("Host interface COBS framing error\n"); + } else if (rv > 0) { + host_packet_length = rv; + } /* else just return and wait for next byte */ +} + +static uint8_t local_key[CURVE25519_KEY_LEN]; +NoiseCipherState *tx_cipher, *rx_cipher; + +#define HANDLE_NOISE_ERROR(x, msg) do { \ + err = x; \ + if (err != NOISE_ERROR_NONE) { \ + char errbuf[256]; \ + noise_strerror(err, errbuf, sizeof(errbuf)); \ + LOG_PRINTF("Error " msg ": %s\n", errbuf); \ + goto errout; \ + } \ + } while(0); + +static NoiseHandshakeState *start_protocol_handshake(void) { + /* TODO Noise-C is nice for prototyping, but we should really get rid of it for mostly two reasons: + * * We don't need cipher/protocol agility, and by baking the final protocol into the firmware we can save a lot + * of flash space by not including all the primitives we don't need as well as noise's dynamic protocol + * abstraction layer. + * * Noise-c is not very embedded-friendly, in particular it uses malloc and free. We should be able to run + * everything with statically allocated buffers instead. + */ + NoiseHandshakeState *handshake; + int err; + + HANDLE_NOISE_ERROR(noise_init(), "initializing noise"); + + HANDLE_NOISE_ERROR(noise_handshakestate_new_by_name(&handshake, "Noise_XX_25519_ChaChaPoly_BLAKE2s", NOISE_ROLE_RESPONDER), "instantiating handshake pattern"); + + NoiseDHState *dh = noise_handshakestate_get_local_keypair_dh(handshake); + HANDLE_NOISE_ERROR(noise_dhstate_set_keypair_private(dh, local_key, sizeof(local_key)), "loading local private keys"); + + HANDLE_NOISE_ERROR(noise_handshakestate_start(handshake), "starting handshake"); + + return handshake; + +errout: + noise_handshakestate_free(handshake); + return 0; +} + +int generate_identity_key(void) { + NoiseDHState *dh; + int err; + + HANDLE_NOISE_ERROR(noise_dhstate_new_by_name(&dh, "25519"), "creating dhstate for key generation"); + HANDLE_NOISE_ERROR(noise_dhstate_generate_keypair(dh), "generating key pair"); + + uint8_t unused[CURVE25519_KEY_LEN]; /* the noise api is a bit bad here. */ + memset(local_key, 0, sizeof(local_key)); + + HANDLE_NOISE_ERROR(noise_dhstate_get_keypair(dh, local_key, sizeof(local_key), unused, sizeof(unused)), "saving key pair"); + + return 0; + +errout: + if (dh) + noise_dhstate_free(dh); + return -1; +} int main(void) { @@ -194,12 +293,17 @@ int main(void) #ifdef USART_DEBUG usart_dma_init(debug_out); #endif + usart_dma_init(usart2_out); + cobs_decode_incremental_initialize(&host_cobs_state); + usart_enable_rx_interrupt(USART2); + nvic_enable_irq(NVIC_USART2_IRQ); LOG_PRINTF("\n==================================\n"); LOG_PRINTF("SecureHID device side initializing\n"); LOG_PRINTF("==================================\n"); + LOG_PRINTF("Initializing USB...\n"); hid_driver_init(&hid_config); hub_driver_init(); @@ -212,7 +316,18 @@ int main(void) */ usbh_init(lld_drivers, device_drivers); - LOG_PRINTF("USB init complete\n"); + LOG_PRINTF("Initializing RNG...\n"); + rand_init(); + + /* FIXME only run this on first boot and persist key in backup sram. Allow reset via jumper-triggered factory reset function. */ + LOG_PRINTF("Generating identity key...\n"); + if (generate_identity_key()) + LOG_PRINTF("Error generating identiy key\n"); + + LOG_PRINTF("Starting noise protocol handshake...\n"); + NoiseHandshakeState *handshake = start_protocol_handshake(); + if (!handshake) + LOG_PRINTF("Error starting protocol handshake.\n"); int i = 0, j = 0; while (23) { @@ -220,10 +335,61 @@ int main(void) delay_ms_busy_loop(1); /* approx 1ms interval between usbh_poll() */ if (i++ == 1000) { i = 0; - const char *s = "foobarfoobarfoobarfoobarfoobar"; - send_packet(usart2_out, (uint8_t *)s, strlen(s)); LOG_PRINTF("Loop iteration %d\n", 1000*(j++)); } + + if (handshake) { +#define MAX_MESSAGE_LEN 256 + uint8_t message[MAX_MESSAGE_LEN]; + NoiseBuffer noise_msg; + /* Run the protocol handshake */ + switch (noise_handshakestate_get_action(handshake)) { + case NOISE_ACTION_WRITE_MESSAGE: + /* Write the next handshake message with a zero-length payload */ + noise_buffer_set_output(noise_msg, message, sizeof(message)); + if (noise_handshakestate_write_message(handshake, &noise_msg, NULL) != NOISE_ERROR_NONE) { + LOG_PRINTF("Error writing handshake message\n"); + noise_handshakestate_free(handshake); + handshake = NULL; + } + send_packet(usart2_out, message, noise_msg.size); + break; + + case NOISE_ACTION_READ_MESSAGE: + if (host_packet_length > 0) { + /* Read the next handshake message and discard the payload */ + noise_buffer_set_input(noise_msg, (uint8_t *)host_packet_buf, host_packet_length); + if (noise_handshakestate_read_message(handshake, &noise_msg, NULL) != NOISE_ERROR_NONE) { + LOG_PRINTF("Error reading handshake message\n"); + noise_handshakestate_free(handshake); + handshake = NULL; + } + } + break; + + case NOISE_ACTION_SPLIT: + if (noise_handshakestate_split(handshake, &tx_cipher, &rx_cipher) != NOISE_ERROR_NONE) { + LOG_PRINTF("Error splitting handshake state\n"); + } else { + LOG_PRINTF("Noise protocol handshake completed successfully\n"); + } + + noise_handshakestate_free(handshake); + handshake = NULL; + break; + + default: + LOG_PRINTF("Noise protocol handshake failed\n"); + noise_handshakestate_free(handshake); + handshake = 0; + break; + } + } } return 0; } + +void _fini() { + while (1); +} + diff --git a/src/rand_stm32.c b/src/rand_stm32.c new file mode 100644 index 0000000..862587c --- /dev/null +++ b/src/rand_stm32.c @@ -0,0 +1,134 @@ +/* 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 "crypto/noise-c/src/protocol/internal.h" +#include "crypto/noise-c/src/crypto/blake2/blake2s.h" + +#define BLAKE2S_HASH_SIZE 32 + +/* 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) { + 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 +#include + +void rand_init(void); + +#endif -- cgit