From 26a343971eb6f0e6e055441df353e60e81cf3595 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Sun, 10 Jul 2016 22:51:47 -0400 Subject: Sign/verifiy installable images Receive the image into sdram, verify the signature before copying to flash. It would be great if worked... --- projects/hsm/Makefile | 1 + projects/hsm/cryptech_upload | 51 +++++++++---- projects/hsm/hsm.c | 22 ------ projects/hsm/mgmt-bootloader.c | 31 ++++---- projects/hsm/mgmt-fpga.c | 32 +++++---- projects/hsm/mgmt-misc.c | 158 +++++++++++++++++++++++++++++++++-------- projects/hsm/mgmt-misc.h | 7 +- sdram-malloc.c | 88 +++++++++++++++++++++++ sdram-malloc.h | 36 ++++++++++ 9 files changed, 326 insertions(+), 100 deletions(-) create mode 100644 sdram-malloc.c create mode 100644 sdram-malloc.h diff --git a/projects/hsm/Makefile b/projects/hsm/Makefile index acb9962..5153a64 100644 --- a/projects/hsm/Makefile +++ b/projects/hsm/Makefile @@ -22,6 +22,7 @@ BOARD_OBJS = \ $(TOPLEVEL)/stm-keystore.o \ $(TOPLEVEL)/stm-sdram.o \ $(TOPLEVEL)/stm-flash.o \ + $(TOPLEVEL)/sdram-malloc.o \ $(BOARD_DIR)/TOOLCHAIN_GCC_ARM/startup_stm32f429xx_rtos.o \ $(BOARD_DIR)/system_stm32f4xx.o \ $(BOARD_DIR)/stm32f4xx_hal_msp.o \ diff --git a/projects/hsm/cryptech_upload b/projects/hsm/cryptech_upload index 7590b38..9f401b8 100755 --- a/projects/hsm/cryptech_upload +++ b/projects/hsm/cryptech_upload @@ -79,16 +79,17 @@ def parse_args(): # positional argument(s) parser.add_argument('filename') + parser.add_argument('signature') return parser.parse_args() def _write(dst, data): dst.write(data) - #if len(data) == 4: - # print("Wrote 0x{!s}".format(data.encode('hex'))) - #else: - # print("Wrote {!r}".format(data)) + if len(data) == 4: + print("Wrote 0x{!s}".format(data.encode('hex'))) + else: + print("Wrote {!r}".format(data)) def _read(dst): @@ -99,7 +100,7 @@ def _read(dst): while x: res += x x = dst.read(1) - #print ("Read {!r}".format(res)) + print ("Read {!r}".format(res)) return res pin = None @@ -123,9 +124,16 @@ def _execute(dst, cmd): response = _read(dst) return response -def send_file(filename, args, dst): - s = os.stat(filename) - size = s.st_size +def send_file(filename, signature, args, dst): + def fsize(fn): + try: + s = os.stat(fn) + except OSError as e: + print e + exit(1) + return s.st_size + + size = fsize(filename) src = open(filename, 'rb') if args.fpga: chunk_size = FPGA_CHUNK_SIZE @@ -178,14 +186,29 @@ def send_file(filename, args, dst): crc = crc32(data, crc) & 0xffffffff - _read(dst) + src.close() - # 3. Write CRC-32 (4 bytes) - _write(dst, struct.pack(' &__end_sdram1) - return NULL; - - sdram_heap += size; - return p; -} - /* The main thread. This does all the setup, and the worker threads handle * the rest. */ diff --git a/projects/hsm/mgmt-bootloader.c b/projects/hsm/mgmt-bootloader.c index d2d7ffe..68a0b38 100644 --- a/projects/hsm/mgmt-bootloader.c +++ b/projects/hsm/mgmt-bootloader.c @@ -38,6 +38,7 @@ #include "stm-uart.h" #include "stm-flash.h" #include "stm-fpgacfg.h" +#include "sdram-malloc.h" #include "mgmt-cli.h" #include "mgmt-misc.h" @@ -50,15 +51,6 @@ extern hal_user_t user; -static uint32_t dfu_offset; - -static int _flash_write_callback(uint8_t *buf, size_t len) -{ - stm_flash_write32(dfu_offset, (uint32_t *)buf, sizeof(buf)/4); - dfu_offset += DFU_UPLOAD_CHUNK_SIZE; - return 1; -} - static int cmd_bootloader_upload(struct cli_def *cli, const char *command, char *argv[], int argc) { if (user < HAL_USER_SO) { @@ -78,13 +70,24 @@ static int cmd_bootloader_upload(struct cli_def *cli, const char *command, char return CLI_ERROR; } - uint8_t buf[DFU_UPLOAD_CHUNK_SIZE]; - dfu_offset = DFU_BOOTLOADER_ADDR; + uint8_t *filebuf; + size_t file_len; - cli_receive_data(cli, buf, sizeof(buf), _flash_write_callback); + if (cli_receive_data(cli, &filebuf, &file_len, DFU_UPLOAD_CHUNK_SIZE) == CLI_ERROR) + return CLI_ERROR; - cli_print(cli, "DFU offset now: %li (%li chunks)", dfu_offset, dfu_offset / DFU_UPLOAD_CHUNK_SIZE); - return CLI_OK; + cli_print(cli, "Writing flash"); + int res = stm_flash_write32(DFU_BOOTLOADER_ADDR, (uint32_t *)filebuf, file_len/4) == 1; + sdram_free(filebuf); + + if (res) { + cli_print(cli, "SUCCESS"); + return CLI_OK; + } + else { + cli_print(cli, "FAIL"); + return CLI_ERROR; + } } void configure_cli_bootloader(struct cli_def *cli) diff --git a/projects/hsm/mgmt-fpga.c b/projects/hsm/mgmt-fpga.c index 45bd33c..78978b6 100644 --- a/projects/hsm/mgmt-fpga.c +++ b/projects/hsm/mgmt-fpga.c @@ -37,6 +37,7 @@ #include "stm-init.h" #include "stm-uart.h" #include "stm-fpgacfg.h" +#include "sdram-malloc.h" #include "mgmt-cli.h" #include "mgmt-fpga.h" @@ -52,15 +53,6 @@ extern hal_user_t user; -static volatile uint32_t dfu_offset = 0; - - -static int _flash_write_callback(uint8_t *buf, size_t len) { - int res = fpgacfg_write_data(dfu_offset, buf, BITSTREAM_UPLOAD_CHUNK_SIZE) == 1; - dfu_offset += BITSTREAM_UPLOAD_CHUNK_SIZE; - return res; -} - static int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, char *argv[], int argc) { if (user < HAL_USER_SO) { @@ -68,9 +60,8 @@ static int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, c return CLI_ERROR; } - uint8_t buf[BITSTREAM_UPLOAD_CHUNK_SIZE]; - - dfu_offset = 0; + uint8_t *filebuf; + size_t file_len; fpgacfg_access_control(ALLOW_ARM); @@ -80,12 +71,23 @@ static int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, c return CLI_ERROR; } - cli_receive_data(cli, &buf[0], sizeof(buf), _flash_write_callback); + if (cli_receive_data(cli, &filebuf, &file_len, BITSTREAM_UPLOAD_CHUNK_SIZE) == CLI_ERROR) + return CLI_ERROR; + + cli_print(cli, "Writing flash"); + int res = fpgacfg_write_data(0, filebuf, file_len) == 1; fpgacfg_access_control(ALLOW_FPGA); + sdram_free(filebuf); - cli_print(cli, "DFU offset now: %li (%li chunks)", dfu_offset, dfu_offset / BITSTREAM_UPLOAD_CHUNK_SIZE); - return CLI_OK; + if (res) { + cli_print(cli, "SUCCESS"); + return CLI_OK; + } + else { + cli_print(cli, "FAIL"); + return CLI_ERROR; + } } static int cmd_fpga_bitstream_erase(struct cli_def *cli, const char *command, char *argv[], int argc) diff --git a/projects/hsm/mgmt-misc.c b/projects/hsm/mgmt-misc.c index 15b2d13..5eecdb3 100644 --- a/projects/hsm/mgmt-misc.c +++ b/projects/hsm/mgmt-misc.c @@ -32,74 +32,174 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK #include "stm-init.h" #include "stm-uart.h" +#include "sdram-malloc.h" #include "mgmt-cli.h" #include "mgmt-misc.h" -#include +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#include "cryptech_signing_key.h" +#undef HAL_OK +#include -extern uint32_t update_crc(uint32_t crc, uint8_t *buf, int len); +#define pad(n, sz) (((n) + (sz-1)) & ~(sz-1)) +static hal_error_t verify_signature(const uint8_t * const data, + const size_t data_len, + const uint8_t * const signature, + const size_t signature_len) +{ + hal_error_t err; + const hal_core_t *core = NULL; + const hal_hash_descriptor_t * const descriptor = hal_hash_sha256; + hal_hash_state_t *state = NULL; + uint8_t statebuf[descriptor->hash_state_length]; + uint8_t digest[signature_len]; + +#if 1 + /* HACK - find the second sha256 core, to avoid interfering with rpc. + * If there isn't a second one, this will set core to NULL, and + * hal_hash_initialize will find the first one. + */ + core = hal_core_find(descriptor->core_name, core); + core = hal_core_find(descriptor->core_name, core); +#endif + + if ((err = hal_hash_initialize(core, descriptor, &state, statebuf, sizeof(statebuf))) != LIBHAL_OK || + (err = hal_hash_update(state, data, data_len)) != LIBHAL_OK || + (err = hal_hash_finalize(state, digest, sizeof(digest))) != LIBHAL_OK) + return err; + + const uint8_t name[] = "Cryptech signing key"; + const size_t name_len = sizeof(name); + uint8_t der[65]; /* ?? */ + int ks_hint; + + if ((err = hal_ks_fetch(HAL_KEY_TYPE_EC_PUBLIC, name, name_len, NULL, NULL, der, NULL, sizeof(der), &ks_hint)) != LIBHAL_OK) + /* get the built-in signing key */ + memcpy(der, cryptech_signing_key, sizeof(der)); + + uint8_t keybuf[hal_ecdsa_key_t_size]; + hal_ecdsa_key_t *key = NULL; + + if ((err = hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, sizeof(der))) != LIBHAL_OK || + (err = hal_ecdsa_verify(NULL, key, digest, sizeof(digest), signature, signature_len)) != LIBHAL_OK) + return err; + + return LIBHAL_OK; +} -int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback) +int cli_receive_data(struct cli_def *cli, uint8_t **filebuf, size_t *file_len, size_t chunksize) { - uint32_t filesize = 0, crc = 0, my_crc = 0, counter = 0; - size_t n = len; + uint32_t filesize = 0, counter = 0; + uint8_t *writeptr; + size_t n = chunksize; if (! control_mgmt_uart_dma_rx(DMA_RX_STOP)) { cli_print(cli, "Failed stopping DMA"); return CLI_OK; } - cli_print(cli, "OK, write size (4 bytes), data in %li byte chunks, CRC-32 (4 bytes)", (uint32_t) n); + cli_print(cli, "OK, write size (4 bytes), data in %li byte chunks", (uint32_t) n); - if (uart_receive_bytes(STM_UART_MGMT, (void *) &filesize, 4, 1000) != HAL_OK) { + if (uart_receive_bytes(STM_UART_MGMT, (void *) &filesize, 4, 1000) != CMSIS_HAL_OK) { cli_print(cli, "Receive timed out"); return CLI_ERROR; } + *file_len = (size_t)(pad(filesize, chunksize)); + if ((*filebuf = sdram_malloc(*file_len)) == NULL) { + cli_print(cli, "File buffer allocation failed"); + return CLI_ERROR; + } + + /* By initializing buf to the same value that erased flash has (0xff), we don't + * have to try and be smart when writing the last page of data to a flash memory. + */ + memset(*filebuf, 0xff, *file_len); + writeptr = *filebuf; + cli_print(cli, "Send %li bytes of data", filesize); while (filesize) { - /* By initializing buf to the same value that erased flash has (0xff), we don't - * have to try and be smart when writing the last page of data to a flash memory. - */ - memset(buf, 0xff, len); if (filesize < n) n = filesize; - if (uart_receive_bytes(STM_UART_MGMT, (void *) buf, n, 1000) != HAL_OK) { + if (uart_receive_bytes(STM_UART_MGMT, (void *) writeptr, n, 1000) != CMSIS_HAL_OK) { cli_print(cli, "Receive timed out"); - return CLI_ERROR; + goto errout; } + writeptr += n; filesize -= n; - my_crc = update_crc(my_crc, buf, n); - - /* After reception of a chunk but before ACKing we have "all" the time in the world to - * calculate CRC and invoke the data_callback. - */ - if (data_callback != NULL && ! data_callback(buf, (size_t) n)) { - cli_print(cli, "Data processing failed"); - return CLI_OK; - } counter++; uart_send_bytes(STM_UART_MGMT, (void *) &counter, 4); } - cli_print(cli, "Send CRC-32"); - uart_receive_bytes(STM_UART_MGMT, (void *) &crc, 4, 1000); - cli_print(cli, "CRC-32 0x%x, calculated CRC 0x%x", (unsigned int) crc, (unsigned int) my_crc); - if (crc == my_crc) { - cli_print(cli, "CRC checksum MATCHED"); - } else { - cli_print(cli, "CRC checksum did NOT match"); + uint32_t sigsize; + cli_print(cli, "Send signature size (4 bytes)"); + if (uart_receive_bytes(STM_UART_MGMT, (void *) &sigsize, 4, 1000) != CMSIS_HAL_OK) { + cli_print(cli, "Receive timed out"); + goto errout; } + if (sigsize < 70 || sigsize > 72) { + cli_print(cli, "Unexpected signature size %d, expected 70-72", (int)sigsize); + goto errout; + } + uint8_t sigbuf[72]; + cli_print(cli, "Send %li bytes of data", sigsize); + if (uart_receive_bytes(STM_UART_MGMT, (void *) sigbuf, sigsize, 1000) != CMSIS_HAL_OK) { + cli_print(cli, "Receive timed out"); + goto errout; + } + /* Signature is in DER format: sequence of 2 integers. Decode into + * what ecdsa expects: an octet string consisting of concatenated + * values for r and s, each of which occupies half of the octet string. + */ + /* XXX should use the asn1 decoder, but just going to hack this for now */ + int i = 0; + #define ASN1_SEQUENCE 0x30 + #define ASN1_INTEGER 0x02 + if (sigbuf[i++] != ASN1_SEQUENCE) { + cli_print(cli, "Error parsing signature"); + goto errout; + } + i++; + if (sigbuf[i++] != ASN1_INTEGER) { + cli_print(cli, "Error parsing signature"); + goto errout; + } + if (sigbuf[i++] == 0x21) i++; + memmove(&sigbuf[0], &sigbuf[i], 32); + i += 32; + if (sigbuf[i++] != ASN1_INTEGER) { + cli_print(cli, "Error parsing signature"); + goto errout; + } + if (sigbuf[i++] == 0x21) i++; + memmove(&sigbuf[32], &sigbuf[i], 32); + + if (verify_signature(*filebuf, *file_len, sigbuf, 64) != LIBHAL_OK) { + cli_print(cli, "Signature verification FAILED"); + goto errout; + } + cli_print(cli, "Signature verification SUCCEEDED"); return CLI_OK; + +errout: + sdram_free(*filebuf); + *filebuf = NULL; + *file_len = 0; + return CLI_ERROR; } static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], int argc) diff --git a/projects/hsm/mgmt-misc.h b/projects/hsm/mgmt-misc.h index b7eb4f4..619b94e 100644 --- a/projects/hsm/mgmt-misc.h +++ b/projects/hsm/mgmt-misc.h @@ -38,12 +38,7 @@ #include "stm-init.h" #include - -#define FILETRANSFER_UPLOAD_CHUNK_SIZE 256 - -typedef int (*cli_data_callback)(uint8_t *, size_t); - extern void configure_cli_misc(struct cli_def *cli); -extern int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback); +extern int cli_receive_data(struct cli_def *cli, uint8_t **filebuf, size_t *file_len, size_t chunksize); #endif /* __STM32_CLI_MGMT_MISC_H */ diff --git a/sdram-malloc.c b/sdram-malloc.c new file mode 100644 index 0000000..3c2eabe --- /dev/null +++ b/sdram-malloc.c @@ -0,0 +1,88 @@ +/* + * sdram_malloc.c + * -------------- + * Use SDRAM for a very limited sort of heap. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Allocate memory from SDRAM1. This is not a general allocator, and + * should only be used with the greatest of caution and care. + * + * This is intended for allocating memory that will either never be freed + * (e.g. thread stack buffers), or will be freed as soon as the requesting + * function is done with it (e.g. file upload buffers). + * + * Memory should be freed in reverse order of allocation (most + * recent first). + * + * There are no memory block control fields, so no protection to ensure + * that memory that is freed is actually the memory that was allocated. + */ + +#include +#include + +/* end of variables declared with __attribute__((section(".sdram1"))) */ +extern uint8_t _esdram1 __asm ("_esdram1"); +static uint8_t * const sdram_heap_base = &_esdram1; + +/* end of sdram1 section */ +extern uint8_t __end_sdram1 __asm ("__end_sdram1"); +static uint8_t * const sdram_heap_end = &__end_sdram1; + +/* current heap top */ +static uint8_t *sdram_heap = &_esdram1; + +/* Allocate some memory. Allocations are not padded, because file upload will + * call repeatedly to write blocks of data to a "file". OTOH, it's a good + * idea to allocate padding after the last write, so the next allocation + * will be aligned. + */ +uint8_t *sdram_malloc(size_t size) +{ + uint8_t *p = sdram_heap; + + if (p + size > sdram_heap_end) + return NULL; + + sdram_heap += size; + return p; +} + +/* Free some memory allocated from sdram. This does only the most basic of + * sanity checks. + */ +void sdram_free(uint8_t *s) +{ + if (s < sdram_heap_base || s >= sdram_heap_end) + return; + if (s < sdram_heap) + sdram_heap = s; +} diff --git a/sdram-malloc.h b/sdram-malloc.h new file mode 100644 index 0000000..5fc80fc --- /dev/null +++ b/sdram-malloc.h @@ -0,0 +1,36 @@ +/* + * sdram_malloc.h + * -------------- + * Use SDRAM for a very limited sort of heap. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +extern uint8_t *sdram_malloc(size_t size); +extern void sdram_free(uint8_t *s); -- cgit v1.2.3