Noise integration compiles

This commit is contained in:
jaseg 2018-11-08 15:41:46 +09:00
parent 8ee5f4ce6a
commit 050d49a56b
10 changed files with 745 additions and 5 deletions

5
.gitignore vendored
View file

@ -8,3 +8,8 @@
*.project
*.a
doc
Makefile
CMakeCache.txt
cmake_install.cmake
CMakeFiles
src/demo

View file

@ -32,7 +32,7 @@ set (ARCH_FLAGS
"-mthumb -mcpu=cortex-m4 ${FP_FLAGS}"
)
set (COMMON_FLAGS
"-O2 -g -Wextra -Wshadow -Wredundant-decls -fno-common -ffunction-sections -fdata-sections"
"-O2 -g -Wextra -Wshadow -Wredundant-decls -fno-common -ffunction-sections -fdata-sections --specs=nosys.specs"
)
set (CMAKE_C_FLAGS
@ -57,7 +57,10 @@ set (CMAKE_EXE_LINKER_FLAGS
"--static -nostartfiles -T${CMAKE_SOURCE_DIR}/libusbhost_stm32f4.ld -Wl,-Map=FIXME_ONE.map -Wl,--gc-sections -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group"
)
include_directories (${CMAKE_SOURCE_DIR}/include)
include_directories (
${CMAKE_SOURCE_DIR}/include
src/crypto/noise-c/include
)
function (init_libopencm3)
include_directories (${CMAKE_SOURCE_DIR}/libopencm3/include)

23
include/cobs.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef __COBS_H__
#define __COBS_H__
#include <stdint.h>
#include <unistd.h>
#include <string.h>
struct cobs_decode_state {
size_t p;
size_t c;
};
ssize_t cobs_encode(char *dst, size_t dstlen, char *src, size_t srclen);
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen);
int cobs_encode_incremental(void *f, int (*output)(void *, char), char *src, size_t srclen);
void cobs_decode_incremental_initialize(struct cobs_decode_state *state);
int cobs_decode_incremental(struct cobs_decode_state *state, char *dst, size_t dstlen, char src);
#endif//__COBS_H__

View file

@ -33,3 +33,9 @@ MEMORY
/* Include the common ld script. */
INCLUDE libopencm3_stm32f4.ld
PROVIDE(_ram_start = ORIGIN(ram));
PROVIDE(_ram_end = ORIGIN(ram) + LENGTH(ram));
PROVIDE(_rom_start = ORIGIN(rom));
PROVIDE(_rom_end = ORIGIN(rom) + LENGTH(rom));

View file

@ -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

292
src/cobs.c Normal file
View file

@ -0,0 +1,292 @@
#include <cobs.h>
/*@ 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

93
src/crypto/CMakeLists.txt Normal file
View file

@ -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"
)

View file

@ -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 <libopencm3/stm32/rcc.h>
@ -41,6 +43,11 @@
#include <string.h>
#include <stdlib.h>
#include <noise/protocol.h>
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);
}

134
src/rand_stm32.c Normal file
View file

@ -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 <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libopencm3/stm32/f4/rng.h>
#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<size; wr_pos += BLAKE2S_HASH_SIZE) {
BLAKE2s_reset(&chain_ctx);
BLAKE2s_update(&chain_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&chain_ctx, chain_constant, strlen(chain_constant));
rng_seed_blake(&chain_ctx);
BLAKE2s_finish(&chain_ctx, global_stm_rand_state);
BLAKE2s_reset(&out_ctx);
BLAKE2s_update(&out_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&out_ctx, extraction_constant, strlen(extraction_constant));
BLAKE2s_finish(&out_ctx, hash_buf);
size_t rem = size-wr_pos;
memcpy(&out[wr_pos], hash_buf, rem < BLAKE2S_HASH_SIZE ? rem : BLAKE2S_HASH_SIZE);
}
BLAKE2s_reset(&chain_ctx);
BLAKE2s_update(&chain_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&chain_ctx, chain_constant, strlen(chain_constant));
rng_seed_blake(&chain_ctx);
BLAKE2s_finish(&chain_ctx, global_stm_rand_state);
/* FIXME make sure this is not optimized out */
memset(&out_ctx, 0, sizeof(out_ctx));
memset(&chain_ctx, 0, sizeof(chain_ctx));
memset(hash_buf, 0, sizeof(hash_buf));
}
#ifdef ED25519_CUSTOMRANDOM /* We are building against ed25519-donna, which needs a random function */
void ed25519_randombytes_unsafe(void *p, size_t len) {
noise_rand_bytes(p, len);
}
#endif

9
src/rand_stm32.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef __RAND_STM32_H__
#define __RAND_STM32_H__
#include <stdint.h>
#include <unistd.h>
void rand_init(void);
#endif