diff options
-rw-r--r-- | hal_internal.h | 14 | ||||
-rw-r--r-- | ks_flash.c | 251 |
2 files changed, 249 insertions, 16 deletions
diff --git a/hal_internal.h b/hal_internal.h index 9896ac0..0c38c00 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -250,7 +250,7 @@ extern const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch, hal_rpc_remote * one for RSA keys, one for EC keys, because the RSA keys are so much * larger than the EC keys. This led to unnecessarily complex and * duplicated code, so for now we treat all keys the same, and waste - * the unneded space in the case of EC keys. + * the unneeded space in the case of EC keys. * * Sizes for ASN.1-encoded keys, this may not be exact due to ASN.1 * INTEGER encoding rules but should be good enough for buffer sizing: @@ -277,15 +277,19 @@ extern const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch, hal_rpc_remote #define HAL_STATIC_PKEY_STATE_BLOCKS 0 #endif +/* This struct is ordered such that all metadata appears before the + * big buffers, in order for all metadata to be loaded with a single + * page read from e.g. the ks_flash module. + */ typedef struct { hal_key_type_t type; hal_curve_name_t curve; hal_key_flags_t flags; - uint8_t name[HAL_RPC_PKEY_NAME_MAX]; + uint8_t in_use; size_t name_len; - uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len; - uint8_t in_use; + uint8_t name[HAL_RPC_PKEY_NAME_MAX]; + uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; } hal_ks_key_t; #ifndef HAL_PIN_SALT_LENGTH @@ -302,6 +306,8 @@ typedef struct { #if HAL_STATIC_PKEY_STATE_BLOCKS > 0 hal_ks_key_t keys[HAL_STATIC_PKEY_STATE_BLOCKS]; +#else + #warning No keys in keydb #endif hal_ks_pin_t wheel_pin; @@ -3,8 +3,8 @@ * ---------- * Keystore implementation in flash memory. * - * Authors: Rob Austein - * Copyright (c) 2015, NORDUnet A/S All rights reserved. + * Authors: Rob Austein, Fredrik Thulin + * Copyright (c) 2015-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 @@ -33,41 +33,256 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#define HAL_OK LIBHAL_OK #include "hal.h" #include "hal_internal.h" +#undef HAL_OK -static hal_ks_keydb_t *db; +#define HAL_OK CMIS_HAL_OK +#include "stm-keystore.h" +#undef HAL_OK + +#include <string.h> + + +#define PAGE_SIZE_MASK (KEYSTORE_PAGE_SIZE - 1) + +/* + * Use a one-element array here so that references can be pointer-based + * as in the other implementations, to ease re-merge at some later date. + */ + +static hal_ks_keydb_t db[1]; + +#define FLASH_SECTOR_1_OFFSET (0 * KEYSTORE_SECTOR_SIZE) +#define FLASH_SECTOR_2_OFFSET (1 * KEYSTORE_SECTOR_SIZE) + +uint32_t _active_sector_offset() +{ + /* XXX Load status bytes from both sectors and decide which is current. */ + #warning Have not implemented two flash sectors yet + return FLASH_SECTOR_1_OFFSET; +} + +uint32_t _get_key_offset(uint32_t num) +{ + /* Reserve first two pages for flash sector state, PINs and future additions. + * The three PINs alone currently occupy 3 * (64 + 16 + 4) bytes (252). + */ + uint32_t offset = KEYSTORE_PAGE_SIZE * 2; + uint32_t key_size = sizeof(*db->keys); + uint32_t bytes_per_key = KEYSTORE_PAGE_SIZE * ((key_size / KEYSTORE_PAGE_SIZE) + 1); + offset += num * bytes_per_key; + return offset; +} const hal_ks_keydb_t *hal_ks_get_keydb(void) { + uint32_t offset, i, idx = 0, active_sector_offset; + hal_ks_key_t *key; + uint8_t page_buf[KEYSTORE_PAGE_SIZE]; + + memset(db, 0, sizeof(*db)); + + if (keystore_check_id() != 1) return NULL; + + active_sector_offset = _active_sector_offset(); + + /* The PINs are in the second page of the sector. */ + offset = active_sector_offset + KEYSTORE_PAGE_SIZE; + if (keystore_read_data(offset, page_buf, sizeof(page_buf)) != 1) return NULL; + offset = 0; + memcpy(&db->wheel_pin, page_buf + offset, sizeof(db->wheel_pin)); + offset += sizeof(db->wheel_pin); + memcpy(&db->so_pin, page_buf + offset, sizeof(db->so_pin)); + offset += sizeof(db->so_pin); + memcpy(&db->user_pin, page_buf + offset, sizeof(db->user_pin)); + + for (i = 0; i < sizeof(db->keys) / sizeof(*db->keys); i++) { + offset = _get_key_offset(i); + if (offset > KEYSTORE_SECTOR_SIZE) { + idx++; + continue; + } + + offset += active_sector_offset; + + if (keystore_read_data(offset, page_buf, sizeof(page_buf)) != 1) return NULL; + + key = (hal_ks_key_t *) page_buf; + if (key->in_use == 0xff) { + /* unprogrammed data */ + idx++; + continue; + } + + if (key->in_use == 1) { + uint8_t *dst = (uint8_t *) &db->keys[idx]; + uint32_t to_read = sizeof(*db->keys); + + /* We already have the first page in page_buf. Put it into place. */ + memcpy(dst, page_buf, sizeof(page_buf)); + to_read -= sizeof(page_buf); + dst += sizeof(page_buf); + + /* Read as many more full pages as possible */ + if (keystore_read_data (offset + KEYSTORE_PAGE_SIZE, dst, to_read & ~PAGE_SIZE_MASK) != 1) return NULL; + dst += to_read & ~PAGE_SIZE_MASK; + to_read &= PAGE_SIZE_MASK; + + if (to_read) { + /* Partial last page. We can only read full pages so load it into page_buf. */ + if (keystore_read_data(offset + sizeof(*db->keys) - to_read, page_buf, sizeof(page_buf)) != 1) return NULL; + memcpy(dst, page_buf, to_read); + } + } + idx++; + } + + return db; +} + +hal_error_t _write_data_to_flash(const uint32_t offset, const uint8_t *data, const size_t len) +{ + uint8_t page_buf[KEYSTORE_PAGE_SIZE]; + uint32_t to_write = len; -#error Not sure what goes here yet + if (keystore_write_data(offset, data, to_write & ~PAGE_SIZE_MASK) != 1) { + return HAL_ERROR_KEYSTORE_ACCESS; + } + to_write &= PAGE_SIZE_MASK; + if (to_write) { + /* Use page_buf to write the remaining bytes, since we must write a full page each time. */ + memset(page_buf, 0xff, sizeof(page_buf)); + memcpy(page_buf, data + len - to_write, to_write); + if (keystore_write_data((offset + len) & ~PAGE_SIZE_MASK, page_buf, sizeof(page_buf)) != 1) { + return HAL_ERROR_KEYSTORE_ACCESS; + } + } + return LIBHAL_OK; +} + +/* + * Write the full DB to flash, PINs and all. + */ +hal_error_t _write_db_to_flash(const uint32_t sector_offset) +{ + hal_error_t status; + uint8_t page_buf[KEYSTORE_PAGE_SIZE]; + uint32_t i, offset; + + if (sizeof(db->wheel_pin) + sizeof(db->so_pin) + sizeof(db->user_pin) > sizeof(page_buf)) { + return HAL_ERROR_BAD_ARGUMENTS; + } + + /* Put the three PINs into page_buf */ + offset = 0; + memcpy(page_buf + offset, &db->wheel_pin, sizeof(db->wheel_pin)); + offset += sizeof(db->wheel_pin); + memcpy(page_buf + offset, &db->so_pin, sizeof(db->so_pin)); + offset += sizeof(db->so_pin); + memcpy(page_buf + offset, &db->user_pin, sizeof(db->user_pin)); + + /* Write PINs into the second of the two reserved pages at the start of the sector. */ + offset = sector_offset + KEYSTORE_PAGE_SIZE; + if ((status = _write_data_to_flash(offset, page_buf, sizeof(page_buf))) != LIBHAL_OK) { + return status; + } + + for (i = 0; i < sizeof(db->keys) / sizeof(*db->keys); i++) { + offset = _get_key_offset(i); + if (offset > KEYSTORE_SECTOR_SIZE) { + return HAL_ERROR_BAD_ARGUMENTS; + } + + offset += sector_offset; + + if ((status =_write_data_to_flash(offset, (uint8_t *) &db->keys[i], sizeof(*db->keys))) != LIBHAL_OK) { + return status; + } + } + + return LIBHAL_OK; } hal_error_t hal_ks_set_keydb(const hal_ks_key_t * const key, const int loc, const int updating) { - if (key == NULL || loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys) || (!key->in_use != !updating)) - return HAL_ERROR_BAD_ARGUMENTS; + hal_error_t status; + uint32_t offset, active_sector_offset; + hal_ks_key_t *tmp_key; + uint8_t page_buf[KEYSTORE_PAGE_SIZE]; + + if (key == NULL || loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys) || (!key->in_use != !updating)) + return HAL_ERROR_BAD_ARGUMENTS; -#error Not sure what goes here yet either + offset = _get_key_offset(loc); + if (offset > KEYSTORE_SECTOR_SIZE) return HAL_ERROR_BAD_ARGUMENTS; + active_sector_offset = _active_sector_offset(); + + offset += active_sector_offset; + + if (keystore_check_id() != 1) return HAL_ERROR_KEYSTORE_ACCESS; + + /* Check if there is a key occupying this slot in the flash already. + * Don't trust the in-memory representation since it would mean data + * corruption in flash if it had been altered. + */ + if (keystore_read_data(offset, page_buf, sizeof(page_buf)) != 1) { + return HAL_ERROR_KEYSTORE_ACCESS; + } + tmp_key = (hal_ks_key_t *) page_buf; + + db->keys[loc] = *key; + db->keys[loc].in_use = 1; + + if (tmp_key->in_use == 0xff) { + /* Key slot was unused in flash. Write the new key there. */ + if ((status = _write_data_to_flash(offset, (uint8_t *) key, sizeof(*db->keys))) != LIBHAL_OK) { + return status; + } + } else { + /* TODO: Erase and write the database to the inactive sector, and then toggle active sector. */ + if (keystore_erase_sectors(active_sector_offset / KEYSTORE_SECTOR_SIZE, + active_sector_offset / KEYSTORE_SECTOR_SIZE) != 1) { + return HAL_ERROR_KEYSTORE_ACCESS; + } + if ((status =_write_db_to_flash(active_sector_offset)) != LIBHAL_OK) { + return status; + } + } + + return LIBHAL_OK; } hal_error_t hal_ks_del_keydb(const int loc) { + uint32_t offset; + if (loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys)) return HAL_ERROR_BAD_ARGUMENTS; -#error Or what goes here + offset = _get_key_offset(loc); + if (offset > KEYSTORE_SECTOR_SIZE) { + return HAL_ERROR_BAD_ARGUMENTS; + } + + offset += _active_sector_offset(); + memset(&db->keys[loc], 0, sizeof(*db->keys)); + + /* Setting bits to 0 never requires erasing flash. Just write it. */ + return _write_data_to_flash(offset, (uint8_t *) &db->keys[loc], sizeof(*db->keys)); } hal_error_t hal_ks_set_pin(const hal_user_t user, const hal_ks_pin_t * const pin) { + uint32_t active_sector_offset; + if (pin == NULL) return HAL_ERROR_BAD_ARGUMENTS; @@ -80,9 +295,20 @@ hal_error_t hal_ks_set_pin(const hal_user_t user, default: return HAL_ERROR_BAD_ARGUMENTS; } -#error Or what goes here + memcpy(p, pin, sizeof(*p)); - return HAL_OK; + active_sector_offset = _active_sector_offset(); + + /* TODO: Could check if the PIN is currently all 0xff, in which case we wouldn't have to + * erase and re-write the whole DB. + */ + + /* TODO: Erase and write the database to the inactive sector, and then toggle active sector. */ + if (keystore_erase_sectors(active_sector_offset / KEYSTORE_SECTOR_SIZE, + active_sector_offset / KEYSTORE_SECTOR_SIZE) != 1) { + return HAL_ERROR_KEYSTORE_ACCESS; + } + return _write_db_to_flash(active_sector_offset); } @@ -97,9 +323,10 @@ hal_error_t hal_ks_get_kek(uint8_t *kek, (kek_max < bitsToBytes(256)) ? bitsToBytes(192) : bitsToBytes(256)); -#error Or what goes here + #warning Faking the Key Encryption Key + memset(kek, 4, len); - return HAL_OK; + return LIBHAL_OK; } |