summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--CMakeLists.txt7
-rw-r--r--include/cobs.h23
-rw-r--r--libusbhost_stm32f4.ld6
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/cobs.c292
-rw-r--r--src/crypto/CMakeLists.txt93
-rw-r--r--src/demo.c172
-rw-r--r--src/rand_stm32.c134
-rw-r--r--src/rand_stm32.h9
10 files changed, 745 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore
index 3e83207..9c68981 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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"
+)
+
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 <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