diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | include/cobs.h | 23 | ||||
-rw-r--r-- | libusbhost_stm32f4.ld | 6 | ||||
-rw-r--r-- | src/CMakeLists.txt | 9 | ||||
-rw-r--r-- | src/cobs.c | 292 | ||||
-rw-r--r-- | src/crypto/CMakeLists.txt | 93 | ||||
-rw-r--r-- | src/demo.c | 172 | ||||
-rw-r--r-- | src/rand_stm32.c | 134 | ||||
-rw-r--r-- | src/rand_stm32.h | 9 |
10 files changed, 745 insertions, 5 deletions
@@ -8,3 +8,8 @@ *.project *.a doc +Makefile +CMakeCache.txt +cmake_install.cmake +CMakeFiles +src/demo diff --git a/CMakeLists.txt b/CMakeLists.txt index 5abb56c..53724b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/include/cobs.h b/include/cobs.h new file mode 100644 index 0000000..6c70c02 --- /dev/null +++ b/include/cobs.h @@ -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__ diff --git a/libusbhost_stm32f4.ld b/libusbhost_stm32f4.ld index 4f676ec..d1801d8 100644 --- a/libusbhost_stm32f4.ld +++ b/libusbhost_stm32f4.ld @@ -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));
+
+
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 <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 + 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" +) + @@ -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);
+}
+
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 <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 diff --git a/src/rand_stm32.h b/src/rand_stm32.h new file mode 100644 index 0000000..6f9ba3d --- /dev/null +++ b/src/rand_stm32.h @@ -0,0 +1,9 @@ +#ifndef __RAND_STM32_H__ +#define __RAND_STM32_H__ + +#include <stdint.h> +#include <unistd.h> + +void rand_init(void); + +#endif |