diff options
Diffstat (limited to 'projects/hsm/mgmt-misc.c')
-rw-r--r-- | projects/hsm/mgmt-misc.c | 158 |
1 files changed, 129 insertions, 29 deletions
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 <string.h> +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#include "cryptech_signing_key.h" +#undef HAL_OK +#include <string.h> -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) |