diff options
Diffstat (limited to 'ks.h')
-rw-r--r-- | ks.h | 421 |
1 files changed, 421 insertions, 0 deletions
@@ -0,0 +1,421 @@ +/* + * ks.h + * ---- + * Keystore, generic parts anyway. This is internal within libhal. + * + * Copyright (c) 2015-2017, 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. + */ + +#ifndef _KS_H_ +#define _KS_H_ + +#include "hal.h" +#include "hal_internal.h" + +/* + * Size of a keystore "block". + * + * This must be an integer multiple of the flash subsector size, among + * other reasons because that's the minimum erasable unit. + */ + +#ifndef HAL_KS_BLOCK_SIZE +#define HAL_KS_BLOCK_SIZE (4096) +#endif + +/* + * PIN block gets the all-zeros UUID, which will never be returned by + * the UUID generation code (by definition -- it's not a version 4 UUID). + */ + +const hal_uuid_t hal_ks_pin_uuid; + +/* + * Known block states. + * + * C does not guarantee any particular representation for enums, so + * including enums directly in the block header isn't safe. Instead, + * we use an access method which casts when reading from the header. + * Writing to the header isn't a problem, because C does guarantee + * that enum is compatible with *some* integer type, it just doesn't + * specify which one. + */ + +typedef enum { + HAL_KS_BLOCK_TYPE_ERASED = 0xFF, /* Pristine erased block (candidate for reuse) */ + HAL_KS_BLOCK_TYPE_ZEROED = 0x00, /* Zeroed block (recently used) */ + HAL_KS_BLOCK_TYPE_KEY = 0x55, /* Block contains key material */ + HAL_KS_BLOCK_TYPE_PIN = 0xAA, /* Block contains PINs */ + HAL_KS_BLOCK_TYPE_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ +} hal_ks_block_type_t; + +/* + * Block status. + */ + +typedef enum { + HAL_KS_BLOCK_STATUS_LIVE = 0x66, /* This is a live block */ + HAL_KS_BLOCK_STATUS_TOMBSTONE = 0x44, /* This is a tombstone left behind during an update */ + HAL_KS_BLOCK_STATUS_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ +} hal_ks_block_status_t; + +/* + * Common header for all keystore block types. + * A few of these fields are deliberately omitted from the CRC. + */ + +typedef struct { + uint8_t block_type; + uint8_t block_status; + hal_crc32_t crc; +} hal_ks_block_header_t; + +/* + * Key block. Tail end of "der" field (after der_len) used for attributes. + */ + +typedef struct { + hal_ks_block_header_t header; + hal_uuid_t name; + hal_key_type_t type; + hal_curve_name_t curve; + hal_key_flags_t flags; + size_t der_len; + unsigned attributes_len; + uint8_t der[]; /* Must be last field -- C99 "flexible array member" */ +} hal_ks_key_block_t; + +#define SIZEOF_KS_KEY_BLOCK_DER \ + (HAL_KS_BLOCK_SIZE - offsetof(hal_ks_key_block_t, der)) + +/* + * PIN block. Also includes space for backing up the KEK when + * HAL_MKM_FLASH_BACKUP_KLUDGE is enabled. + */ + +typedef struct { + hal_ks_block_header_t header; + hal_ks_pin_t wheel_pin; + hal_ks_pin_t so_pin; + hal_ks_pin_t user_pin; +#if HAL_MKM_FLASH_BACKUP_KLUDGE + uint32_t kek_set; + uint8_t kek[KEK_LENGTH]; +#endif +} hal_ks_pin_block_t; + +#define FLASH_KEK_SET 0x33333333 + +/* + * One keystore block. + */ + +typedef union { + uint8_t bytes[HAL_KS_BLOCK_SIZE]; + hal_ks_block_header_t header; + hal_ks_key_block_t key; + hal_ks_pin_block_t pin; +} hal_ks_block_t; + +/* + * In-memory cache. + */ + +typedef struct { + unsigned blockno; + unsigned lru; + hal_ks_block_t block; +} hal_ks_cache_block_t; + +/* + * Keystore object. hal_internal.h typedefs this to hal_ks_t. + * + * We expect this to be a static variable, but we expect the arrays in + * it to be allocated at runtime using hal_allocate_static_memory() + * because they can get kind of large. + * + * Driver-specific stuff is handled by a form of subclassing: the + * driver embeds the hal_ks_t structure at the head of whatever else + * it needs, and performs (controlled, type-safe) casts as needed. + * + * Core of this is the keystore index. This is intended to be usable + * by both memory-based and flash-based keystores. Some of the + * features aren't necessary for memory-based keystores, but should be + * harmless, and let us keep the drivers simple. + * + * General approach is multiple arrays, all but one of which are + * indexed by "block" numbers, where a block number might be a slot in + * yet another static array, the number of a flash sub-sector, or + * whatever is the appropriate unit for holding one keystore record. + * + * The index array only contains block numbers. This is a small data + * structure so that moving data within it is relatively cheap. + * + * The index array is divided into two portions: the index proper, and + * the free queue. The index proper is ordered according to the names + * (UUIDs) of the corresponding blocks; the free queue is a FIFO, to + * support a simplistic form of wear leveling in flash-based keystores. + * + * Key names are kept in a separate array, indexed by block number. + * + * The all-zeros UUID, which (by definition) cannot be a valid key + * UUID, is reserved for the (non-key) block used to stash PINs and + * other small data which aren't really part of the keystore proper + * but are kept with it because the keystore is the flash we have. + * + * Note that this API deliberately says nothing about how the keys + * themselves are stored, that's up to the keystore driver. + */ + +typedef struct hal_ks_driver hal_ks_driver_t; + +struct hal_ks { + const hal_ks_driver_t *driver;/* Must be first */ + unsigned size; /* Blocks in keystore */ + unsigned used; /* How many blocks are in use */ + uint16_t *index; /* Index/freelist array */ + hal_uuid_t *names; /* Keyname array */ + unsigned cache_lru; /* Cache LRU counter */ + unsigned cache_size; /* Size (how many blocks) in cache */ + hal_ks_cache_block_t *cache; /* Cache */ +}; + +/* + * Keystore driver. + */ + +struct hal_ks_driver { + hal_error_t (*init) (hal_ks_t *ks, const int alloc); + hal_error_t (*read) (hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block); + hal_error_t (*write) (hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block); + hal_error_t (*deprecate) (hal_ks_t *ks, const unsigned blockno); + hal_error_t (*zero) (hal_ks_t *ks, const unsigned blockno); + hal_error_t (*erase) (hal_ks_t *ks, const unsigned blockno); + hal_error_t (*erase_maybe) (hal_ks_t *ks, const unsigned blockno); + hal_error_t (*set_owner) (hal_ks_t *ks, const unsigned blockno, + const hal_client_handle_t client, const hal_session_handle_t session); + hal_error_t (*test_owner) (hal_ks_t *ks, const unsigned blockno, + const hal_client_handle_t client, const hal_session_handle_t session); + hal_error_t (*copy_owner) (hal_ks_t *ks, const unsigned source, const unsigned target); + hal_error_t (*logout) (hal_ks_t *ks, const hal_client_handle_t client); +}; + +/* + * Wrappers around keystore driver methods. + * + * hal_ks_init() and hal_ks_logout() are missing here because we + * expose them to the rest of libhal. + */ + +static inline hal_error_t hal_ks_block_read(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->read == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->read(ks, blockno, block); +} + +static inline hal_error_t hal_ks_block_write(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->write == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->write(ks, blockno, block); +} + +static inline hal_error_t hal_ks_block_deprecate(hal_ks_t *ks, const unsigned blockno) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->deprecate == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->deprecate(ks, blockno); +} + +static inline hal_error_t hal_ks_block_zero(hal_ks_t *ks, const unsigned blockno) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->zero == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->zero(ks, blockno); +} + +static inline hal_error_t hal_ks_block_erase(hal_ks_t *ks, const unsigned blockno) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->erase == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->erase(ks, blockno); +} + +static inline hal_error_t hal_ks_block_erase_maybe(hal_ks_t *ks, const unsigned blockno) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->erase_maybe == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->erase_maybe(ks, blockno); +} + +static inline hal_error_t hal_ks_block_set_owner(hal_ks_t *ks, + const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->set_owner == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->set_owner(ks, blockno, client, session); +} + +static inline hal_error_t hal_ks_block_test_owner(hal_ks_t *ks, + const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->test_owner == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->test_owner(ks, blockno, client, session); +} + +static inline hal_error_t hal_ks_block_copy_owner(hal_ks_t *ks, + const unsigned source, + const unsigned target) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->copy_owner == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->copy_owner(ks, source, target); +} + +/* + * Type safe casts. + */ + +static inline hal_ks_block_type_t hal_ks_block_get_type(const hal_ks_block_t * const block) +{ + return block == NULL ? HAL_KS_BLOCK_TYPE_UNKNOWN : + (hal_ks_block_type_t) block->header.block_type; +} + +static inline hal_ks_block_status_t hal_ks_block_get_status(const hal_ks_block_t * const block) +{ + return block == NULL ? HAL_KS_BLOCK_STATUS_UNKNOWN : + (hal_ks_block_status_t) block->header.block_status; +} + +/* + * Keystore utilities. Some or all of these may end up static within ks.c. + */ + +extern hal_error_t hal_ks_alloc_common(hal_ks_t *ks, + const unsigned ks_blocks, + const unsigned cache_blocks, + void **extra, + const size_t extra_len); + +extern hal_error_t hal_ks_init_common(hal_ks_t *ks); + +extern hal_crc32_t hal_ks_block_calculate_crc(const hal_ks_block_t * const block); + +extern hal_error_t hal_ks_index_heapsort(hal_ks_t *ks); + +extern hal_error_t hal_ks_index_find(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint); + +extern hal_error_t hal_ks_index_add(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint); + +extern hal_error_t hal_ks_index_delete(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint); + +extern hal_error_t hal_ks_index_replace(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint); + +extern hal_error_t hal_ks_index_fsck(hal_ks_t *ks); + +extern const size_t hal_ks_attribute_header_size; + +extern hal_error_t hal_ks_attribute_scan(const uint8_t * const bytes, + const size_t bytes_len, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + size_t *total_len); + +extern hal_error_t hal_ks_attribute_delete(uint8_t *bytes, + const size_t bytes_len, + hal_pkey_attribute_t *attributes, + unsigned *attributes_len, + size_t *total_len, + const uint32_t type); + +extern hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_len, + hal_pkey_attribute_t *attributes, + unsigned *attributes_len, + size_t *total_len, + const uint32_t type, + const uint8_t * const value, + const size_t value_len); + +extern hal_ks_block_t *hal_ks_cache_pick_lru(hal_ks_t *ks); + +extern hal_ks_block_t *hal_ks_cache_find_block(const hal_ks_t * const ks, + const unsigned blockno); + +extern void hal_ks_cache_mark_used(hal_ks_t *ks, + const hal_ks_block_t * const block, + const unsigned blockno); + +extern void hal_ks_cache_release(hal_ks_t *ks, + const hal_ks_block_t * const block); + +extern hal_error_t hal_ks_block_read_cached(hal_ks_t *ks, + const unsigned blockno, + hal_ks_block_t **block); + +extern hal_error_t hal_ks_block_update(hal_ks_t *ks, + const unsigned b1, + hal_ks_block_t *block, + const hal_uuid_t * const uuid, + int *hint); + +#endif /* _KS_H_ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ |