diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 53 | ||||
-rw-r--r-- | core.c | 9 | ||||
-rw-r--r-- | crc32.c | 143 | ||||
-rw-r--r-- | daemon.c | 4 | ||||
-rw-r--r-- | hal.h | 136 | ||||
-rw-r--r-- | hal_internal.h | 617 | ||||
-rw-r--r-- | hash.c | 14 | ||||
-rw-r--r-- | ks.c | 411 | ||||
-rw-r--r-- | ks_attribute.c | 178 | ||||
-rw-r--r-- | ks_flash.c | 2042 | ||||
-rw-r--r-- | ks_index.c | 457 | ||||
-rw-r--r-- | ks_mmap.c | 6 | ||||
-rw-r--r-- | ks_volatile.c | 520 | ||||
-rw-r--r-- | libhal.py | 662 | ||||
-rw-r--r-- | masterkey.c | 248 | ||||
-rw-r--r-- | mkm.c | 230 | ||||
-rw-r--r-- | rpc_api.c | 111 | ||||
-rw-r--r-- | rpc_client.c | 489 | ||||
-rw-r--r-- | rpc_hash.c | 24 | ||||
-rw-r--r-- | rpc_misc.c | 20 | ||||
-rw-r--r-- | rpc_pkcs1.c (renamed from masterkey.h) | 58 | ||||
-rw-r--r-- | rpc_pkey.c | 537 | ||||
-rw-r--r-- | rpc_server.c | 409 | ||||
-rw-r--r-- | slip.c | 2 | ||||
-rw-r--r-- | tests/test-bus.c | 3 | ||||
-rw-r--r-- | tests/test-mkmif.c | 10 | ||||
-rw-r--r-- | tests/test-rpc_bighash.c | 2 | ||||
-rw-r--r-- | tests/test-rpc_hash.c | 92 | ||||
-rw-r--r-- | tests/test-rpc_pkey.c | 519 | ||||
-rw-r--r-- | tests/test-trng.c | 4 | ||||
-rw-r--r-- | unit-tests.py | 1035 | ||||
-rw-r--r-- | uuid.c | 111 |
33 files changed, 7195 insertions, 1962 deletions
@@ -1,4 +1,5 @@ *.[ao] +*.pyc *~ Makefile TAGS @@ -33,17 +33,18 @@ STATIC_CORE_STATE_BLOCKS = 32 STATIC_HASH_STATE_BLOCKS = 10 STATIC_HMAC_STATE_BLOCKS = 4 -STATIC_PKEY_STATE_BLOCKS = 6 +STATIC_PKEY_STATE_BLOCKS = 32 +STATIC_KS_VOLATILE_SLOTS = 128 INC = hal.h hal_internal.h LIB = libhal.a # Error checking on known control options, some of which allow the user entirely too much rope. -USAGE := "usage: ${MAKE} [IO_BUS=eim|i2c|fmc] [RPC_MODE=none|server|client-simple|client-mixed] [KS=volatile|mmap|flash] [RPC_TRANSPORT=none|loopback|serial|daemon] [MODEXP_CORE=no|yes]" +USAGE := "usage: ${MAKE} [IO_BUS=eim|i2c|fmc] [RPC_MODE=none|server|client-simple|client-mixed] [KS=mmap|flash] [RPC_TRANSPORT=none|loopback|serial|daemon] [MODEXP_CORE=no|yes]" IO_BUS ?= none -KS ?= volatile +KS ?= flash RPC_MODE ?= none RPC_TRANSPORT ?= none MODEXP_CORE ?= no @@ -51,7 +52,7 @@ MODEXP_CORE ?= no ifeq (,$(and \ $(filter none eim i2c fmc ,${IO_BUS}),\ $(filter none server client-simple client-mixed ,${RPC_MODE}),\ - $(filter volatile mmap flash ,${KS}),\ + $(filter mmap flash ,${KS}),\ $(filter none loopback serial daemon ,${RPC_TRANSPORT}),\ $(filter no yes ,${MODEXP_CORE}))) $(error ${USAGE}) @@ -73,8 +74,8 @@ endif # makefile, so the working definition of "always want" is sometimes # just "building this is harmless even if we don't use it." -OBJ += errorstrings.o hash.o asn1.o ecdsa.o rsa.o ${KS_OBJ} xdr.o slip.o -OBJ += rpc_api.o rpc_hash.o rpc_misc.o rpc_pkey.o rpc_client.o rpc_server.o +OBJ += errorstrings.o hash.o asn1.o ecdsa.o rsa.o xdr.o slip.o +OBJ += rpc_api.o rpc_hash.o uuid.o rpc_pkcs1.o crc32.o # Object files to build when we're on a platform with direct access # to our hardware (Verilog) cores. @@ -105,22 +106,25 @@ ifneq "${IO_BUS}" "fmc" CFLAGS += -fPIC endif -# The mmap and flash keystore implementations are both server code. +# The keystore code has mutated a bit with the new API, and the Makefile, +# probably needs more extensive changes to track that. # -# The volatile keystore (conventional memory) is client code, to -# support using the same API for things like PKCS #11 "session" objects. +# In the old world, the volatile keystore was for the client side, +# while the flash and mmap keystores were for the server side (on the +# Alpha and the Novena, respectively). # -# Default at the moment is mmap, since that should work on the Novena -# and we haven't yet written the flash code for the bridge board. +# In the new world, all keystores are on the server side, and the +# volatile keystore is always present, to support things like PKCS #11 +# "session" objects. +# +# The mmap keystore hasn't been rewritten for the new API yet. -KS_OBJ = ks.o +KS_OBJ = ks_index.o ks_attribute.o ks_volatile.o ifeq "${KS}" "mmap" KS_OBJ += ks_mmap.o -else ifeq "${KS}" "volatile" - KS_OBJ += ks_volatile.o else ifeq "${KS}" "flash" - KS_OBJ += ks_flash.o masterkey.o + KS_OBJ += ks_flash.o mkm.o endif # RPC_MODE = none | server | client-simple | client-mixed @@ -144,18 +148,22 @@ ifneq "${RPC_MODE}" "server" OBJ += rpc_serial.o endif +RPC_CLIENT_OBJ = rpc_client.o + ifeq "${RPC_TRANSPORT}" "loopback" - RPC_CLIENT_OBJ = rpc_client_loopback.o + RPC_CLIENT_OBJ += rpc_client_loopback.o else ifeq "${RPC_TRANSPORT}" "serial" - RPC_CLIENT_OBJ = rpc_client_serial.o + RPC_CLIENT_OBJ += rpc_client_serial.o else ifeq "${RPC_TRANSPORT}" "daemon" - RPC_CLIENT_OBJ = rpc_client_daemon.o + RPC_CLIENT_OBJ += rpc_client_daemon.o endif +RPC_SERVER_OBJ = ${KS_OBJ} rpc_misc.o rpc_pkey.o rpc_server.o + ifeq "${RPC_TRANSPORT}" "loopback" - RPC_SERVER_OBJ = rpc_server_loopback.o + RPC_SERVER_OBJ += rpc_server_loopback.o else ifeq "${RPC_TRANSPORT}" "serial" - RPC_SERVER_OBJ = rpc_server_serial.o + RPC_SERVER_OBJ += rpc_server_serial.o endif ifeq "${RPC_MODE}" "none" @@ -169,8 +177,7 @@ else ifeq "${RPC_MODE}" "client-simple" CFLAGS += -DRPC_CLIENT=RPC_CLIENT_REMOTE -DHAL_RSA_USE_MODEXP=0 else ifeq "${RPC_MODE}" "client-mixed" OBJ += ${RPC_CLIENT_OBJ} - CFLAGS += -DRPC_CLIENT=RPC_CLIENT_MIXED -DHAL_RSA_USE_MODEXP=0 -DHAL_ONLY_USE_SOFTWARE_HASH_CORES=1 - KS = volatile + CFLAGS += -DRPC_CLIENT=RPC_CLIENT_MIXED -DHAL_RSA_USE_MODEXP=0 endif ifndef CRYPTECH_ROOT @@ -234,7 +241,7 @@ asn1.o rsa.o ecdsa.o: asn1_internal.h ecdsa.o: ecdsa_curves.h novena-eim.o hal_io_eim.o: novena-eim.h slip.o rpc_client_serial.o rpc_server_serial.o: slip_internal.h -ks.o: last_gasp_pin_internal.h +ks_flash.o: last_gasp_pin_internal.h last_gasp_pin_internal.h: ./utils/last_gasp_default_pin >$@ @@ -42,6 +42,13 @@ #include "hal_internal.h" /* + * POSIX function whose declaration gets lost somewhere in the twisty + * corridors of glibc's "Feature Test Macro" system. + */ + +extern size_t strnlen(const char *, size_t); + +/* * Structure of our internal database is private, in case we want to * change representation (array, tree, list of lists, whatever) at * some later date without having to change the public API. @@ -208,7 +215,7 @@ hal_error_t hal_core_alloc(const char *name, hal_core_t **pcore) { hal_core_t *core; hal_error_t err = HAL_ERROR_CORE_NOT_FOUND; - + if (name == NULL && (pcore == NULL || *pcore == NULL)) return HAL_ERROR_BAD_ARGUMENTS; @@ -0,0 +1,143 @@ +/* + * crc32.c + * ------- + * CRC-32 implementation. This is internal within libhal. + * + * 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. + */ + +/* + * This code was generated by the nifty pycrc package. pycrc itself + * (well, the version used for this) is under the MIT license, but the + * author explictly disclaims ownership of the generated code, so + * we're free to do whatever we want with it. For simplicity, we've + * placed it under the same copyright we use for everything else. + * + * This file contains two different implementations of the same API, + * corresponding to different speed/size tradeoffs. These were + * generated with the commands: + * + * pycrc.py --model crc-32 --algorithm table-driven --std c99 --symbol-prefix hal_crc32_ --generate h + * pycrc.py --model crc-32 --algorithm table-driven --std c99 --symbol-prefix hal_crc32_ --generate c --table-idx-width 4 + * pycrc.py --model crc-32 --algorithm table-driven --std c99 --symbol-prefix hal_crc32_ --generate c --table-idx-width 8 + */ + +/* + * Generated on Mon Sep 12 15:36:31 2016, by pycrc v0.9, https://pycrc.org using the configuration: + * Width = 32 + * Poly = 0x04c11db7 + * Xor_In = 0xffffffff + * ReflectIn = True + * Xor_Out = 0xffffffff + * ReflectOut = True + * Algorithm = table-driven + */ + +#include "hal.h" +#include "hal_internal.h" + +#ifndef HAL_CRC32_TINY +#define HAL_CRC32_TINY 0 +#endif + +#if HAL_CRC32_TINY + +static const hal_crc32_t crc_table[16] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c +}; + +hal_crc32_t hal_crc32_update(hal_crc32_t crc, const void *data, size_t data_len) +{ + const unsigned char *d = (const unsigned char *)data; + unsigned int tbl_idx; + + while (data_len--) { + tbl_idx = crc ^ (*d >> (0 * 4)); + crc = crc_table[tbl_idx & 0x0f] ^ (crc >> 4); + tbl_idx = crc ^ (*d >> (1 * 4)); + crc = crc_table[tbl_idx & 0x0f] ^ (crc >> 4); + + d++; + } + return crc & 0xffffffff; +} + +#else /* HAL_CRC32_TINY */ + +static const hal_crc32_t crc_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +hal_crc32_t hal_crc32_update(hal_crc32_t crc, const void *data, size_t data_len) +{ + const unsigned char *d = (const unsigned char *)data; + unsigned int tbl_idx; + + while (data_len--) { + tbl_idx = (crc ^ *d) & 0xff; + crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff; + + d++; + } + return crc & 0xffffffff; +} + +#endif /* HAL_CRC32_TINY */ @@ -74,7 +74,7 @@ static void poll_add(int fd) { /* add 4 entries at a time to avoid having to realloc too often */ #define NNEW 4 - + /* expand the array if necessary */ if (nfds == npollfds) { npollfds = nfds + NNEW; @@ -247,7 +247,7 @@ int main(int argc, char *argv[]) perror("poll"); exit(EXIT_FAILURE); } - + for (nfds_t i = 0; i < nfds; ++i) { if (pollfds[i].revents != 0) { /* XXX POLLERR|POLLHUP|POLLNVAL */ @@ -39,6 +39,7 @@ #include <stdint.h> #include <sys/types.h> #include <stdlib.h> +#include <string.h> /* * A handy macro from cryptlib. @@ -143,6 +144,20 @@ DEFINE_HAL_ERROR(HAL_ERROR_MASTERKEY_NOT_SET, "Master key (Key Encryption Key) not set") \ DEFINE_HAL_ERROR(HAL_ERROR_MASTERKEY_FAIL, "Master key generic failure") \ DEFINE_HAL_ERROR(HAL_ERROR_MASTERKEY_BAD_LENGTH, "Master key of unacceptable length") \ + DEFINE_HAL_ERROR(HAL_ERROR_KS_DRIVER_NOT_FOUND, "Keystore driver not found") \ + DEFINE_HAL_ERROR(HAL_ERROR_KEYSTORE_BAD_CRC, "Bad CRC in keystore") \ + DEFINE_HAL_ERROR(HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE, "Unsupported keystore block type") \ + DEFINE_HAL_ERROR(HAL_ERROR_KEYSTORE_LOST_DATA, "Keystore appears to have lost data") \ + DEFINE_HAL_ERROR(HAL_ERROR_BAD_ATTRIBUTE_LENGTH, "Bad attribute length") \ + DEFINE_HAL_ERROR(HAL_ERROR_ATTRIBUTE_NOT_FOUND, "Attribute not found") \ + DEFINE_HAL_ERROR(HAL_ERROR_NO_KEY_INDEX_SLOTS, "No key index slots available") \ + DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_UUID_MISORDERED, "Key index UUID misordered") \ + DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_CHUNK_ORPHANED, "Key index chunk orphaned") \ + DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_CHUNK_MISSING, "Key index chunk missing") \ + DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS, "Key index chunk overlaps") \ + DEFINE_HAL_ERROR(HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE, "Wrong block type in keystore") \ + DEFINE_HAL_ERROR(HAL_ERROR_RPC_PROTOCOL_ERROR, "RPC protocol error") \ + DEFINE_HAL_ERROR(HAL_ERROR_NOT_IMPLEMENTED, "Not implemented") \ END_OF_HAL_ERROR_LIST /* Marker to forestall silly line continuation errors */ @@ -246,14 +261,14 @@ typedef struct hal_hash_driver hal_hash_driver_t; */ typedef enum { - hal_digest_algorithm_none, - hal_digest_algorithm_sha1, - hal_digest_algorithm_sha224, - hal_digest_algorithm_sha256, - hal_digest_algorithm_sha512_224, - hal_digest_algorithm_sha512_256, - hal_digest_algorithm_sha384, - hal_digest_algorithm_sha512 + HAL_DIGEST_ALGORITHM_NONE, + HAL_DIGEST_ALGORITHM_SHA1, + HAL_DIGEST_ALGORITHM_SHA224, + HAL_DIGEST_ALGORITHM_SHA256, + HAL_DIGEST_ALGORITHM_SHA512_224, + HAL_DIGEST_ALGORITHM_SHA512_256, + HAL_DIGEST_ALGORITHM_SHA384, + HAL_DIGEST_ALGORITHM_SHA512 } hal_digest_algorithm_t; typedef struct { @@ -553,6 +568,26 @@ extern hal_error_t hal_ecdsa_verify(const hal_core_t *core, const uint8_t * const signature, const size_t signature_len); /* + * UUID stuff. All UUIDs we use (or are likely to use) are type 4 "random" UUIDs + */ + +typedef struct { uint8_t uuid[16]; } hal_uuid_t; + +#define HAL_UUID_TEXT_SIZE (sizeof("00112233-4455-6677-8899-aabbccddeeff")) + +static inline int hal_uuid_cmp(const hal_uuid_t * const a, const hal_uuid_t * const b) +{ + return memcmp(a, b, sizeof(hal_uuid_t)); +} + +extern hal_error_t hal_uuid_gen(hal_uuid_t *uuid); + +extern hal_error_t hal_uuid_parse(hal_uuid_t *uuid, const char * const string); + +extern hal_error_t hal_uuid_format(const hal_uuid_t * const uuid, + char *buffer, const size_t buffer_len); + +/* * Higher level RPC-based mechanism for working with HSM at arm's * length, using handles instead of direct access to the cores. * @@ -675,8 +710,6 @@ extern hal_error_t hal_rpc_hash_finalize(const hal_hash_handle_t hash, * a session handle and which ones don't...). */ -#define HAL_RPC_PKEY_NAME_MAX 128 - typedef struct { uint32_t handle; } hal_pkey_handle_t; typedef uint32_t hal_key_flags_t; @@ -684,28 +717,43 @@ typedef uint32_t hal_key_flags_t; #define HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE (1 << 0) #define HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT (1 << 1) #define HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT (1 << 2) -#define HAL_KEY_FLAG_PROXIMATE (1 << 3) +#define HAL_KEY_FLAG_TOKEN (1 << 3) +#define HAL_KEY_FLAG_PUBLIC (1 << 4) + +/* + * hal_pkey_attribute_t.length would be size_t, except that we also + * need it to transport HAL_PKEY_ATTRIBUTE_NIL safely, which we can + * only do with a known-width type. The RPC code conveys size_t as a + * uint32_t in any case, so we just use that here and have done. + */ + +typedef struct { + uint32_t type; + uint32_t length; + const void *value; +} hal_pkey_attribute_t; + +#define HAL_PKEY_ATTRIBUTE_NIL (0xFFFFFFFF) extern hal_error_t hal_rpc_pkey_load(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, const hal_key_type_t type, const hal_curve_name_t curve, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const uint8_t * const der, const size_t der_len, const hal_key_flags_t flags); -extern hal_error_t hal_rpc_pkey_find(const hal_client_handle_t client, +extern hal_error_t hal_rpc_pkey_open(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, + const hal_uuid_t * const name, const hal_key_flags_t flags); extern hal_error_t hal_rpc_pkey_generate_rsa(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const unsigned key_length, const uint8_t * const public_exponent, const size_t public_exponent_len, const hal_key_flags_t flags); @@ -713,7 +761,7 @@ extern hal_error_t hal_rpc_pkey_generate_rsa(const hal_client_handle_t client, extern hal_error_t hal_rpc_pkey_generate_ec(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const hal_curve_name_t curve, const hal_key_flags_t flags); @@ -721,12 +769,12 @@ extern hal_error_t hal_rpc_pkey_close(const hal_pkey_handle_t pkey); extern hal_error_t hal_rpc_pkey_delete(const hal_pkey_handle_t pkey); -extern hal_error_t hal_rpc_pkey_rename(const hal_pkey_handle_t pkey, - const uint8_t * const name, const size_t name_len); - extern hal_error_t hal_rpc_pkey_get_key_type(const hal_pkey_handle_t pkey, hal_key_type_t *type); +extern hal_error_t hal_rpc_pkey_get_key_curve(const hal_pkey_handle_t pkey, + hal_curve_name_t *curve); + extern hal_error_t hal_rpc_pkey_get_key_flags(const hal_pkey_handle_t pkey, hal_key_flags_t *flags); @@ -735,37 +783,49 @@ extern size_t hal_rpc_pkey_get_public_key_len(const hal_pkey_handle_t pkey); extern hal_error_t hal_rpc_pkey_get_public_key(const hal_pkey_handle_t pkey, uint8_t *der, size_t *der_len, const size_t der_max); -extern hal_error_t hal_rpc_pkey_sign(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +extern hal_error_t hal_rpc_pkey_sign(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max); -extern hal_error_t hal_rpc_pkey_verify(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +extern hal_error_t hal_rpc_pkey_verify(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, const uint8_t * const signature, const size_t signature_len); -typedef struct { - hal_key_type_t type; - hal_curve_name_t curve; - hal_key_flags_t flags; - char name[HAL_RPC_PKEY_NAME_MAX]; - size_t name_len; - /* ... */ -} hal_pkey_info_t; - -extern hal_error_t hal_rpc_pkey_list(hal_pkey_info_t *result, - unsigned *result_len, - const unsigned result_max, - hal_key_flags_t flags); +extern hal_error_t hal_rpc_pkey_match(const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, + unsigned *result_len, + const unsigned result_max, + const hal_uuid_t * const previous_uuid); + +extern hal_error_t hal_rpc_pkey_set_attributes(const hal_pkey_handle_t pkey, + const hal_pkey_attribute_t *const attributes, + const unsigned attributes_len); + +extern hal_error_t hal_rpc_pkey_get_attributes(const hal_pkey_handle_t pkey, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len); extern hal_error_t hal_rpc_client_init(void); + extern hal_error_t hal_rpc_client_close(void); + extern hal_error_t hal_rpc_server_init(void); + extern hal_error_t hal_rpc_server_close(void); -extern hal_error_t hal_rpc_server_dispatch(const uint8_t * const ibuf, const size_t ilen, uint8_t * const obuf, size_t * const olen); + +extern hal_error_t hal_rpc_server_dispatch(const uint8_t * const ibuf, const size_t ilen, + uint8_t * const obuf, size_t * const olen); + extern void hal_rpc_server_main(void); #endif /* _HAL_H_ */ diff --git a/hal_internal.h b/hal_internal.h index 5e08c4e..9aa360b 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -36,6 +36,8 @@ #ifndef _HAL_INTERNAL_H_ #define _HAL_INTERNAL_H_ +#include <string.h> + #include "hal.h" #include "verilog_constants.h" @@ -67,6 +69,20 @@ inline uint32_t htonl(uint32_t w) #endif /* + * Static memory allocation on start-up. Don't use this except where + * really necessary. By design, there's no way to free this, we don't + * want to have to manage a heap. Intent is just to allow allocation + * things like the large-ish ks_index arrays used by ks_flash.c from a + * memory source external to the executable image file (eg, from the + * secondary SDRAM chip on the Cryptech Alpha board). + * + * We shouldn't need this except on the HSM, so for now we don't bother + * with implementing a version of this based on malloc() or sbrk(). + */ + +extern void *hal_allocate_static_memory(const size_t size); + +/* * Longest hash block and digest we support at the moment. */ @@ -169,21 +185,20 @@ typedef struct { hal_pkey_handle_t *pkey, const hal_key_type_t type, const hal_curve_name_t curve, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const uint8_t * const der, const size_t der_len, const hal_key_flags_t flags); - hal_error_t (*find)(const hal_client_handle_t client, + hal_error_t (*open)(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, + const hal_uuid_t * const name, const hal_key_flags_t flags); hal_error_t (*generate_rsa)(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const unsigned key_length, const uint8_t * const public_exponent, const size_t public_exponent_len, const hal_key_flags_t flags); @@ -191,7 +206,7 @@ typedef struct { hal_error_t (*generate_ec)(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const hal_curve_name_t curve, const hal_key_flags_t flags); @@ -199,11 +214,11 @@ typedef struct { hal_error_t (*delete)(const hal_pkey_handle_t pkey); - hal_error_t (*rename)(const hal_pkey_handle_t pkey, - const uint8_t * const name, const size_t name_len); - hal_error_t (*get_key_type)(const hal_pkey_handle_t pkey, - hal_key_type_t *key_type); + hal_key_type_t *type); + + hal_error_t (*get_key_curve)(const hal_pkey_handle_t pkey, + hal_curve_name_t *curve); hal_error_t (*get_key_flags)(const hal_pkey_handle_t pkey, hal_key_flags_t *flags); @@ -213,22 +228,37 @@ typedef struct { hal_error_t (*get_public_key)(const hal_pkey_handle_t pkey, uint8_t *der, size_t *der_len, const size_t der_max); - hal_error_t (*sign)(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, + hal_error_t (*sign)(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max); - hal_error_t (*verify)(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, + hal_error_t (*verify)(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, const uint8_t * const signature, const size_t signature_len); - hal_error_t (*list)(hal_pkey_info_t *result, + hal_error_t (*match)(const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, unsigned *result_len, const unsigned result_max, - hal_key_flags_t flags); + const hal_uuid_t * const previous_uuid); + + hal_error_t (*set_attributes)(const hal_pkey_handle_t pkey, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len); + + hal_error_t (*get_attributes)(const hal_pkey_handle_t pkey, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len); } hal_rpc_pkey_dispatch_t; @@ -241,7 +271,7 @@ extern const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch, hal_rpc_remote * See code in rpc_pkey.c for how this flag fits into the pkey handle. */ -#define HAL_PKEY_HANDLE_PROXIMATE_FLAG (1 << 31) +#define HAL_PKEY_HANDLE_TOKEN_FLAG (1 << 31) /* * Mostly used by the local_pkey code, but the mixed_pkey code needs @@ -251,19 +281,33 @@ extern const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch, hal_rpc_remote * and just pass the plain hash for everything else. */ -extern hal_error_t hal_rpc_pkey_pkcs1_construct_digestinfo(const hal_hash_handle_t handle, - uint8_t *digest_info, size_t *digest_info_len, - const size_t digest_info_max); +extern hal_error_t hal_rpc_pkcs1_construct_digestinfo(const hal_hash_handle_t handle, + uint8_t *digest_info, size_t *digest_info_len, + const size_t digest_info_max); /* - * Keystore API. - * - * The original design for this subsystem used two separate tables, - * 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 unneeded space in the case of EC keys. + * CRC-32 stuff (for flash keystore, etc). Dunno if we want a Verilog + * implementation of this, or if it would even be faster than doing it + * the main CPU taking I/O overhead and so forth into account. * + * These prototypes were generated by pycrc.py, see notes in crc32.c. + */ + +typedef uint32_t hal_crc32_t; + +static inline hal_crc32_t hal_crc32_init(void) +{ + return 0xffffffff; +} + +extern hal_crc32_t hal_crc32_update(hal_crc32_t crc, const void *data, size_t data_len); + +static inline hal_crc32_t hal_crc32_finalize(hal_crc32_t crc) +{ + return crc ^ 0xffffffff; +} + +/* * 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,32 +321,21 @@ extern hal_error_t hal_rpc_pkey_pkcs1_construct_digestinfo(const hal_hash_handle * Plus we need a bit of AES-keywrap overhead, since we're storing the * wrapped form (see hal_aes_keywrap_cyphertext_length()). * - * We also need to store PINs somewhere, so they go into the keystore - * data structure even though they're not keys. Like keys, they're - * stored in a relatively safe form (PBKDF2), so while we would prefer - * to keep them private, they don't require tamper-protected RAM. + * A buffer big enough for a 8192-bit RSA key would overflow one + * sub-sector on the flash chip we're using on the Alpha. We could + * invent some more complex scheme where key blocks are allowed to + * span multiple sub-sectors, but since an 8192-bit RSA key would also + * be unusably slow with the current RSA implementation, for the + * moment we take the easy way out and cap this at 4096-bit RSA. */ -#define HAL_KS_WRAPPED_KEYSIZE ((4655 + 15) & ~7) +#define HAL_KS_WRAPPED_KEYSIZE ((2351 + 15) & ~7) -#ifndef HAL_STATIC_PKEY_STATE_BLOCKS -#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. +/* + * PINs. + * + * The functions here might want renaming, eg, to hal_pin_*(). */ -typedef struct { - hal_key_type_t type; - hal_curve_name_t curve; - hal_key_flags_t flags; - uint8_t in_use; - size_t name_len; - size_t der_len; - uint8_t name[HAL_RPC_PKEY_NAME_MAX]; - uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; -} hal_ks_key_t; #ifndef HAL_PIN_SALT_LENGTH #define HAL_PIN_SALT_LENGTH 16 @@ -314,40 +347,47 @@ typedef struct { uint8_t salt[HAL_PIN_SALT_LENGTH]; } hal_ks_pin_t; -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; - hal_ks_pin_t so_pin; - hal_ks_pin_t user_pin; - -} hal_ks_keydb_t; - extern hal_error_t hal_set_pin_default_iterations(const hal_client_handle_t client, const uint32_t iterations); +extern hal_error_t hal_get_pin(const hal_user_t user, + const hal_ks_pin_t **pin); + +extern hal_error_t hal_set_pin(const hal_user_t user, + const hal_ks_pin_t * const pin); + /* - * Internal functions within the keystore implementation. Think of - * these as concrete methods for the keystore API subclassed onto - * various storage technologies. + * Master key memory (MKM) and key-encryption-key (KEK). + * + * Providing a mechanism for storing the KEK in flash is a horrible + * kludge which defeats the entire purpose of having the MKM. We + * support it for now because the Alpha hardware does not yet have + * a working battery backup for the MKM, but it should go away RSN. */ -extern const hal_ks_keydb_t *hal_ks_get_keydb(void); +#ifndef HAL_MKM_FLASH_BACKUP_KLUDGE +#define HAL_MKM_FLASH_BACKUP_KLUDGE 1 +#endif + +#ifndef KEK_LENGTH +#define KEK_LENGTH (bitsToBytes(256)) +#endif + +extern hal_error_t hal_mkm_get_kek(uint8_t *kek, size_t *kek_len, const size_t kek_max); + +extern hal_error_t hal_mkm_volatile_read(uint8_t *buf, const size_t len); +extern hal_error_t hal_mkm_volatile_write(const uint8_t * const buf, const size_t len); +extern hal_error_t hal_mkm_volatile_erase(const size_t len); -extern hal_error_t hal_ks_set_keydb(const hal_ks_key_t * const key, - const int loc, - const int updating); +#if HAL_MKM_FLASH_BACKUP_KLUDGE -extern hal_error_t hal_ks_del_keydb(const int loc); +/* #warning MKM flash backup kludge enabled. Do NOT use this in production! */ -extern hal_error_t hal_ks_get_kek(uint8_t *kek, - size_t *kek_len, - const size_t kek_max); +extern hal_error_t hal_mkm_flash_read(uint8_t *buf, const size_t len); +extern hal_error_t hal_mkm_flash_write(const uint8_t * const buf, const size_t len); +extern hal_error_t hal_mkm_flash_erase(const size_t len); + +#endif /* * Keystore API for use by the pkey implementation. @@ -359,44 +399,405 @@ extern hal_error_t hal_ks_get_kek(uint8_t *kek, * Unclear whether these should also call the ASN.1 encode/decode * functions. For the moment, the answer is no, but we may need to * revisit this as the underlying Verilog API evolves. + * + * hal_pkey_slot_t is defined here too, so that keystore drivers can + * piggyback on the pkey database for storage related to keys on which + * the user currently has an active pkey handle. Nothing outside the + * pkey and keystore code should touch this. */ -extern hal_error_t hal_ks_store(const hal_key_type_t type, - const hal_curve_name_t curve, - const hal_key_flags_t flags, - const uint8_t * const name, const size_t name_len, - const uint8_t * const der, const size_t der_len, - int *hint); +typedef struct { + hal_client_handle_t client_handle; + hal_session_handle_t session_handle; + hal_pkey_handle_t pkey_handle; + hal_key_type_t type; + hal_curve_name_t curve; + hal_key_flags_t flags; + hal_uuid_t name; + int hint; + + /* + * This might be where we'd stash a (hal_core_t *) pointing to a + * core which has already been loaded with the key, if we were + * trying to be clever about using multiple signing cores. Moot + * point (ie, no way we could possibly test such a thing) as long as + * the FPGA is too small to hold more than one modexp core and ECDSA + * is entirely software, so skip it for now, but the implied + * semantics are interesting: a pkey handle starts to resemble an + * initialized signing core, and once all the cores are in use, one + * can't load another key without closing an existing pkey handle. + */ +} hal_pkey_slot_t; + +typedef struct hal_ks_driver hal_ks_driver_t; + +typedef struct hal_ks hal_ks_t; + +struct hal_ks_driver { + + hal_error_t (*init)(const hal_ks_driver_t * const driver, + const int alloc); -extern hal_error_t hal_ks_exists(const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, - int *hint); + hal_error_t (*shutdown)(const hal_ks_driver_t * const driver); -extern hal_error_t hal_ks_fetch(const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, - hal_curve_name_t *curve, - hal_key_flags_t *flags, - uint8_t *der, size_t *der_len, const size_t der_max, - int *hint); + hal_error_t (*open)(const hal_ks_driver_t * const driver, + hal_ks_t **ks); -extern hal_error_t hal_ks_delete(const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, - int *hint); + hal_error_t (*close)(hal_ks_t *ks); -extern hal_error_t hal_ks_rename(const hal_key_type_t type, - const uint8_t * const old_name, const size_t old_name_len, - const uint8_t * const new_name, const size_t new_name_len, - int *hint); + hal_error_t (*store)(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const uint8_t * const der, const size_t der_len); -extern hal_error_t hal_ks_list(hal_pkey_info_t *result, - unsigned *result_len, - const unsigned result_max); + hal_error_t (*fetch)(hal_ks_t *ks, + hal_pkey_slot_t *slot, + uint8_t *der, size_t *der_len, const size_t der_max); -extern hal_error_t hal_ks_get_pin(const hal_user_t user, - const hal_ks_pin_t **pin); + hal_error_t (*delete)(hal_ks_t *ks, + hal_pkey_slot_t *slot); + + hal_error_t (*match)(hal_ks_t *ks, + const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, + unsigned *result_len, + const unsigned result_max, + const hal_uuid_t * const previous_uuid); + + hal_error_t (*set_attributes)(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len); + + hal_error_t (*get_attributes)(hal_ks_t *ks, + hal_pkey_slot_t *slot, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len); + +}; + + +struct hal_ks { + const hal_ks_driver_t *driver; + /* + * Any other common portions of hal_ks_t go here. + */ + + /* + * Driver-specific stuff is handled by a form of subclassing: + * driver module embeds this structure at the head of whatever + * else it needs, and performs casts as needed. + */ +}; + +extern const hal_ks_driver_t + hal_ks_volatile_driver[1], + hal_ks_token_driver[1]; + +static inline hal_error_t hal_ks_init(const hal_ks_driver_t * const driver, + const int alloc) +{ + if (driver == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (driver->init == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return driver->init(driver, alloc); +} + +static inline hal_error_t hal_ks_shutdown(const hal_ks_driver_t * const driver) +{ + if (driver == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (driver->shutdown == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return driver->shutdown(driver); +} + +static inline hal_error_t hal_ks_open(const hal_ks_driver_t * const driver, + hal_ks_t **ks) +{ + if (driver == NULL || ks == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (driver->open == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return driver->open(driver, ks); +} + +static inline hal_error_t hal_ks_close(hal_ks_t *ks) +{ + if (ks == NULL || ks->driver == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->close == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return ks->driver->close(ks); +} + +static inline hal_error_t hal_ks_store(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const uint8_t * const der, const size_t der_len) +{ + if (ks == NULL || ks->driver == NULL || slot == NULL || der == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->store == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return ks->driver->store(ks, slot, der, der_len); +} + +static inline hal_error_t hal_ks_fetch(hal_ks_t *ks, + hal_pkey_slot_t *slot, + uint8_t *der, size_t *der_len, const size_t der_max) +{ + if (ks == NULL || ks->driver == NULL || slot == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->fetch == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return ks->driver->fetch(ks, slot, der, der_len, der_max); +} + +static inline hal_error_t hal_ks_delete(hal_ks_t *ks, + hal_pkey_slot_t *slot) +{ + if (ks == NULL || ks->driver == NULL || slot == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->delete == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return ks->driver->delete(ks, slot); +} + +static inline hal_error_t hal_ks_match(hal_ks_t *ks, + const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, + unsigned *result_len, + const unsigned result_max, + const hal_uuid_t * const previous_uuid) +{ + if (ks == NULL || ks->driver == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->match == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return ks->driver->match(ks, client, session, type, curve, flags, attributes, attributes_len, + result, result_len, result_max, previous_uuid); +} + +static inline hal_error_t hal_ks_set_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len) +{ + if (ks == NULL || ks->driver == NULL || slot == NULL || + attributes == NULL || attributes_len == 0) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->set_attributes == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return ks->driver->set_attributes(ks, slot, attributes, attributes_len); +} + +static inline hal_error_t hal_ks_get_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len) +{ + if (ks == NULL || ks->driver == NULL || slot == NULL || + attributes == NULL || attributes_len == 0) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->get_attributes == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + return ks->driver->get_attributes(ks, slot, attributes, attributes_len, + attributes_buffer, attributes_buffer_len); +} + +/* + * Keystore index. This is intended to be usable by both memory-based + * (in-memory, mmap(), ...) keystores and keystores based on raw flash. + * Some of the features aren't really necessary for memory-based keystores, + * but should be harmless. + * + * 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 contains nothing but flags and block numbers, and + * is deliberately a small data structure so that moving data around + * 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. + * Key names here are a composite of the key's UUID and a "chunk" + * number; the latter allows storage of keys whose total size exceeds + * one block (whatever a block is). For the moment we keep the UUID + * and the chunk number in a single array, which may provide (very) + * slightly better performance due to reference locality in SDRAM, but + * this may change if we need to reclaim the space wasted by structure + * size rounding. + * + * 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. This + * portion of the API is only concerned with allocation and naming. + */ + +typedef struct { + hal_uuid_t name; /* Key name */ + uint8_t chunk; /* Key chunk number */ +} hal_ks_name_t; + +typedef struct { + unsigned size; /* Array length */ + unsigned used; /* How many blocks are in use */ + uint16_t *index; /* Index/freelist array */ + hal_ks_name_t *names; /* Keyname array */ +} hal_ks_index_t; + +/* + * Finish setting up key index. Caller must populate index, free + * list, and name array. + * + * This function checks a few things then sorts the index proper. + * + * If driver cares about wear leveling, driver must supply the free + * list in the desired order (FIFO); figuring out what that order is a + * problem for the keystore driver. + */ +extern hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi); + +/* + * Find a key block, return its block number. + */ +extern hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned chunk, + unsigned *blockno, + int *hint); + +/* + * Find all the blocks in a key, return the block numbers. + */ +extern hal_error_t hal_ks_index_find_range(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned max_blocks, + unsigned *n_blocks, + unsigned *blocknos, + int *hint, + const int strict); + +/* + * Add a key block, return its block number. + */ +extern hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned chunk, + unsigned *blockno, + int *hint); + +/* + * Delete a key block, returns its block number (driver may need it). + */ +extern hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned chunk, + unsigned *blockno, + int *hint); + +/* + * Delete all of blocks in a key, returning the block numbers. + */ + +extern hal_error_t hal_ks_index_delete_range(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned max_blocks, + unsigned *n_blocks, + unsigned *blocknos, + int *hint); + +/* + * Replace a key block with a new one, return new block number. + * Name of block does not change. This is an optimization of + * a delete immediately followed by an add for the same name. + */ + +extern hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned chunk, + unsigned *blockno, + int *hint); + +/* + * Check the index for errors. At least for the moment, this just + * reports errors, it doesn't attempt to fix them. + */ + +extern hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi); + +/* + * Keystore attribute utilities, for use by keystore drivers. + */ -extern hal_error_t hal_ks_set_pin(const hal_user_t user, - const hal_ks_pin_t * const pin); +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); /* * RPC lowest-level send and receive routines. These are blocking, and @@ -421,7 +822,7 @@ extern hal_error_t hal_rpc_server_transport_close(void); */ typedef enum { - RPC_FUNC_GET_VERSION = 0, + RPC_FUNC_GET_VERSION, RPC_FUNC_GET_RANDOM, RPC_FUNC_SET_PIN, RPC_FUNC_LOGIN, @@ -435,7 +836,7 @@ typedef enum { RPC_FUNC_HASH_UPDATE, RPC_FUNC_HASH_FINALIZE, RPC_FUNC_PKEY_LOAD, - RPC_FUNC_PKEY_FIND, + RPC_FUNC_PKEY_OPEN, RPC_FUNC_PKEY_GENERATE_RSA, RPC_FUNC_PKEY_GENERATE_EC, RPC_FUNC_PKEY_CLOSE, @@ -446,8 +847,10 @@ typedef enum { RPC_FUNC_PKEY_GET_PUBLIC_KEY, RPC_FUNC_PKEY_SIGN, RPC_FUNC_PKEY_VERIFY, - RPC_FUNC_PKEY_LIST, - RPC_FUNC_PKEY_RENAME, + RPC_FUNC_PKEY_MATCH, + RPC_FUNC_PKEY_GET_KEY_CURVE, + RPC_FUNC_PKEY_SET_ATTRIBUTES, + RPC_FUNC_PKEY_GET_ATTRIBUTES, } rpc_func_num_t; #define RPC_VERSION 0x01010000 /* 1.1.0.0 */ @@ -200,7 +200,7 @@ static const uint8_t */ const hal_hash_descriptor_t hal_hash_sha1[1] = {{ - hal_digest_algorithm_sha1, + HAL_DIGEST_ALGORITHM_SHA1, SHA1_BLOCK_LEN, SHA1_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha1, sizeof(dalgid_sha1), @@ -208,7 +208,7 @@ const hal_hash_descriptor_t hal_hash_sha1[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha224[1] = {{ - hal_digest_algorithm_sha256, + HAL_DIGEST_ALGORITHM_SHA256, SHA256_BLOCK_LEN, SHA224_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha224, sizeof(dalgid_sha224), @@ -216,7 +216,7 @@ const hal_hash_descriptor_t hal_hash_sha224[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha256[1] = {{ - hal_digest_algorithm_sha256, + HAL_DIGEST_ALGORITHM_SHA256, SHA256_BLOCK_LEN, SHA256_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha256, sizeof(dalgid_sha256), @@ -224,7 +224,7 @@ const hal_hash_descriptor_t hal_hash_sha256[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha512_224[1] = {{ - hal_digest_algorithm_sha512_224, + HAL_DIGEST_ALGORITHM_SHA512_224, SHA512_BLOCK_LEN, SHA512_224_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha512_224, sizeof(dalgid_sha512_224), @@ -232,7 +232,7 @@ const hal_hash_descriptor_t hal_hash_sha512_224[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha512_256[1] = {{ - hal_digest_algorithm_sha512_256, + HAL_DIGEST_ALGORITHM_SHA512_256, SHA512_BLOCK_LEN, SHA512_256_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha512_256, sizeof(dalgid_sha512_256), @@ -240,7 +240,7 @@ const hal_hash_descriptor_t hal_hash_sha512_256[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha384[1] = {{ - hal_digest_algorithm_sha384, + HAL_DIGEST_ALGORITHM_SHA384, SHA512_BLOCK_LEN, SHA384_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha384, sizeof(dalgid_sha384), @@ -248,7 +248,7 @@ const hal_hash_descriptor_t hal_hash_sha384[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha512[1] = {{ - hal_digest_algorithm_sha512, + HAL_DIGEST_ALGORITHM_SHA512, SHA512_BLOCK_LEN, SHA512_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha512, sizeof(dalgid_sha512), @@ -1,411 +0,0 @@ -/* - * ks.c - * ---- - * Keystore API. This is internal within libhal. - * - * Authors: Rob Austein - * Copyright (c) 2015, 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. - */ - -#include <string.h> -#include <assert.h> - -#include "hal.h" -#include "hal_internal.h" -#include "last_gasp_pin_internal.h" - -#define KEK_LENGTH (bitsToBytes(256)) - -/* - * In "remote" and "mixed" RPC modes we're a software only RPC client - * without (direct) access to secure hardware, thus there is no real - * point in encrypting keys. As precautions, we (a) warn about this - * when configured in one of these modes, and (b) refuse to store any - * sort of private keys. - */ - -#define USE_KEK (RPC_CLIENT != RPC_CLIENT_REMOTE && RPC_CLIENT != RPC_CLIENT_MIXED) - -#if !USE_KEK -#warning ks.c compiled without KEK support and will only accept public keys -- this is normal for the host-side build of libhsm -#endif - -static inline int acceptable_key_type(const hal_key_type_t type) -{ - switch (type) { -#if USE_KEK - case HAL_KEY_TYPE_RSA_PRIVATE: - case HAL_KEY_TYPE_EC_PRIVATE: -#endif - case HAL_KEY_TYPE_RSA_PUBLIC: - case HAL_KEY_TYPE_EC_PUBLIC: - return 1; - default: - return 0; - } -} - -hal_error_t hal_ks_store(const hal_key_type_t type, - const hal_curve_name_t curve, - const hal_key_flags_t flags, - const uint8_t * const name, const size_t name_len, - const uint8_t * const der, const size_t der_len, - int *hint) -{ - if (name == NULL || der == NULL || der_len == 0 || !acceptable_key_type(type)) - return HAL_ERROR_BAD_ARGUMENTS; - - if (name_len > HAL_RPC_PKEY_NAME_MAX) - return HAL_ERROR_KEY_NAME_TOO_LONG; - - const hal_ks_keydb_t * const db = hal_ks_get_keydb(); - hal_error_t err; - int hint_; - - if (db == NULL) - return HAL_ERROR_KEYSTORE_ACCESS; - - if (hint == NULL) - hint = &hint_; - - *hint = -1; - - for (int i = 0; i < sizeof(db->keys)/sizeof(*db->keys); i++) { - if (!db->keys[i].in_use && *hint < 0) - *hint = i; - if (db->keys[i].in_use && - db->keys[i].type == type && - db->keys[i].name_len == name_len && memcmp(db->keys[i].name, name, name_len) == 0) - return HAL_ERROR_KEY_NAME_IN_USE; - } - - if (*hint < 0) - return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; - - hal_ks_key_t k; - memset(&k, 0, sizeof(k)); - k.der_len = sizeof(k.der); - -#if USE_KEK - - uint8_t kek[KEK_LENGTH]; - size_t kek_len; - - if ((err = hal_ks_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK) - err = hal_aes_keywrap(NULL, kek, kek_len, der, der_len, k.der, &k.der_len); - - memset(kek, 0, sizeof(kek)); - - if (err != HAL_OK) - return err; - -#else /* USE_KEK */ - - if (der_len > k.der_len) - return HAL_ERROR_RESULT_TOO_LONG; - - k.der_len = der_len; - memcpy(k.der, der, der_len); - -#endif /* USE_KEK */ - - assert(name_len <= sizeof(k.name)); - memcpy(k.name, name, name_len); - k.name_len = name_len; - k.type = type; - k.curve = curve; - k.flags = flags; - - if ((err = hal_ks_set_keydb(&k, *hint, 0)) != HAL_OK) - return err; - - return HAL_OK; -} - -static int find(const hal_ks_keydb_t * const db, - const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, - int *hint) -{ - assert(db != NULL && name != NULL && acceptable_key_type(type)); - - if (hint != NULL && *hint >= 0 && *hint < sizeof(db->keys)/sizeof(*db->keys) && - db->keys[*hint].in_use && - db->keys[*hint].type == type && - db->keys[*hint].name_len == name_len && memcmp(db->keys[*hint].name, name, name_len) == 0) - return 1; - - for (int i = 0; i < sizeof(db->keys)/sizeof(*db->keys); i++) { - if (!db->keys[i].in_use || - (hint != NULL && i == *hint) || - db->keys[i].type != type || - db->keys[i].name_len != name_len || memcmp(db->keys[i].name, name, name_len) != 0) - continue; - if (hint != NULL) - *hint = i; - return 1; - } - - return 0; -} - -hal_error_t hal_ks_exists(const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, - int *hint) -{ - if (name == NULL || !acceptable_key_type(type)) - return HAL_ERROR_BAD_ARGUMENTS; - - const hal_ks_keydb_t * const db = hal_ks_get_keydb(); - - if (db == NULL) - return HAL_ERROR_KEYSTORE_ACCESS; - - if (find(db, type, name, name_len, hint)) - return HAL_OK; - else - return HAL_ERROR_KEY_NOT_FOUND; -} - -hal_error_t hal_ks_fetch(const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, - hal_curve_name_t *curve, - hal_key_flags_t *flags, - uint8_t *der, size_t *der_len, const size_t der_max, - int *hint) -{ - if (name == NULL || !acceptable_key_type(type)) - return HAL_ERROR_BAD_ARGUMENTS; - - const hal_ks_keydb_t * const db = hal_ks_get_keydb(); - int hint_ = -1; - - if (db == NULL) - return HAL_ERROR_KEYSTORE_ACCESS; - - if (hint == NULL) - hint = &hint_; - - if (!find(db, type, name, name_len, hint)) - return HAL_ERROR_KEY_NOT_FOUND; - - const hal_ks_key_t * const k = &db->keys[*hint]; - - if (curve != NULL) - *curve = k->curve; - - if (flags != NULL) - *flags = k->flags; - - if (der == NULL && der_len != NULL) - *der_len = k->der_len; - - if (der != NULL) { - -#if USE_KEK - - uint8_t kek[KEK_LENGTH]; - size_t kek_len, der_len_; - hal_error_t err; - - if (der_len == NULL) - der_len = &der_len_; - - *der_len = der_max; - - if ((err = hal_ks_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK) - err = hal_aes_keyunwrap(NULL, kek, kek_len, k->der, k->der_len, der, der_len); - - memset(kek, 0, sizeof(kek)); - - if (err != HAL_OK) - return err; - -#else /* USE_KEK */ - - if (k->der_len > der_max) - return HAL_ERROR_RESULT_TOO_LONG; - - if (der_len != NULL) - *der_len = k->der_len; - - memcpy(der, k->der, k->der_len); - -#endif /* USE_KEK */ - } - - return HAL_OK; -} - -hal_error_t hal_ks_delete(const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, - int *hint) -{ - if (name == NULL || !acceptable_key_type(type)) - return HAL_ERROR_BAD_ARGUMENTS; - - const hal_ks_keydb_t * const db = hal_ks_get_keydb(); - int hint_ = -1; - - if (db == NULL) - return HAL_ERROR_KEYSTORE_ACCESS; - - if (hint == NULL) - hint = &hint_; - - if (!find(db, type, name, name_len, hint)) - return HAL_ERROR_KEY_NOT_FOUND; - - return hal_ks_del_keydb(*hint); -} - -hal_error_t hal_ks_rename(const hal_key_type_t type, - const uint8_t * const old_name, const size_t old_name_len, - const uint8_t * const new_name, const size_t new_name_len, - int *hint) -{ - if (old_name == NULL || new_name == NULL || !acceptable_key_type(type)) - return HAL_ERROR_BAD_ARGUMENTS; - - if (new_name_len > HAL_RPC_PKEY_NAME_MAX) - return HAL_ERROR_KEY_NAME_TOO_LONG; - - const hal_ks_keydb_t * const db = hal_ks_get_keydb(); - int hint_ = -1; - - if (db == NULL) - return HAL_ERROR_KEYSTORE_ACCESS; - - if (find(db, type, new_name, new_name_len, NULL)) - return HAL_ERROR_KEY_NAME_IN_USE; - - if (hint == NULL) - hint = &hint_; - - if (!find(db, type, old_name, old_name_len, hint)) - return HAL_ERROR_KEY_NOT_FOUND; - - hal_ks_key_t k = db->keys[*hint]; - - assert(new_name_len <= sizeof(k.name)); - memcpy(k.name, new_name, new_name_len); - k.name_len = new_name_len; - - return hal_ks_set_keydb(&k, *hint, 1); -} - -hal_error_t hal_ks_list(hal_pkey_info_t *result, - unsigned *result_len, - const unsigned result_max) -{ - if (result == NULL || result_len == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - const hal_ks_keydb_t * const db = hal_ks_get_keydb(); - - if (db == NULL) - return HAL_ERROR_KEYSTORE_ACCESS; - - *result_len = 0; - - for (int i = 0; i < sizeof(db->keys)/sizeof(*db->keys); i++) { - - if (!db->keys[i].in_use) - continue; - - if (*result_len == result_max) - return HAL_ERROR_RESULT_TOO_LONG; - - result[*result_len].type = db->keys[i].type; - result[*result_len].curve = db->keys[i].curve; - result[*result_len].flags = db->keys[i].flags; - result[*result_len].name_len = db->keys[i].name_len; - memcpy(result[*result_len].name, db->keys[i].name, db->keys[i].name_len); - ++ *result_len; - } - - return HAL_OK; -} - -hal_error_t hal_ks_get_pin(const hal_user_t user, - const hal_ks_pin_t **pin) -{ - if (pin == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - const hal_ks_keydb_t * const db = hal_ks_get_keydb(); - - if (db == NULL) - return HAL_ERROR_KEYSTORE_ACCESS; - - switch (user) { - case HAL_USER_WHEEL: *pin = &db->wheel_pin; break; - case HAL_USER_SO: *pin = &db->so_pin; break; - case HAL_USER_NORMAL: *pin = &db->user_pin; break; - default: return HAL_ERROR_BAD_ARGUMENTS; - } - -#warning Need better "Have we been initialized yet?" test - /* - * If we were looking for the WHEEL PIN and it appears to be - * completely unset, return the compiled-in last-gasp PIN. This is - * a terrible answer, but we need some kind of bootstrapping - * mechanism. Feel free to suggest something better. - * - * We probably need some more general "have we been initialized?" - * state somewhere, and might want to refuse to do things like - * storing keys until we've been initialized and the appropriate - * PINs have been set. - * - * Just to make things more fun, some drivers return all zeros for - * "this has never been set", some return all ones to indicate the - * same thing. REALLY need a flag somewhere. - */ - - uint8_t u00 = 0x00, uFF = 0xFF; - for (int i = 0; i < sizeof((*pin)->pin); i++) { - u00 |= (*pin)->pin[i]; - uFF &= (*pin)->pin[i]; - } - for (int i = 0; i < sizeof((*pin)->salt); i++) { - u00 |= (*pin)->salt[i]; - uFF &= (*pin)->salt[i]; - } - if (user == HAL_USER_WHEEL && ((u00 == 0x00 && (*pin)->iterations == 0x00000000) || - (uFF == 0xFF && (*pin)->iterations == 0xFFFFFFFF))) - *pin = &hal_last_gasp_pin; - - return HAL_OK; -} - -/* - * Local variables: - * indent-tabs-mode: nil - * End: - */ diff --git a/ks_attribute.c b/ks_attribute.c new file mode 100644 index 0000000..92e450d --- /dev/null +++ b/ks_attribute.c @@ -0,0 +1,178 @@ +/* + * ks_attribute.c + * -------------- + * Keystore attribute API. This is internal within libhal. + * + * 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. + */ + +#include <string.h> +#include <assert.h> + +#include "hal.h" +#include "hal_internal.h" + +/* + * Read and write attribute headers (type and length). We could do + * this with a structure type and casts, but that has portability + * issues, and doing it this way just isn't expensive enough to worry about. + */ + +const size_t hal_ks_attribute_header_size = 6; + +static inline hal_error_t read_header(const uint8_t * const bytes, const size_t bytes_len, + uint32_t *attribute_type, size_t *attribute_len) +{ + if (bytes == NULL || bytes_len < hal_ks_attribute_header_size || + attribute_type == NULL || attribute_len == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + *attribute_type = ((bytes[0] << 24) | + (bytes[1] << 16) | + (bytes[2] << 8) | + (bytes[3] << 0)); + *attribute_len = ((bytes[4] << 8) | + (bytes[5] << 0)); + + return HAL_OK; +} + +static inline hal_error_t write_header(uint8_t *bytes, const size_t bytes_len, + const uint32_t attribute_type, const size_t attribute_len) +{ + if (bytes == NULL || bytes_len < hal_ks_attribute_header_size) + return HAL_ERROR_BAD_ARGUMENTS; + + bytes[0] = (attribute_type >> 24) & 0xFF; + bytes[1] = (attribute_type >> 16) & 0xFF; + bytes[2] = (attribute_type >> 8) & 0xFF; + bytes[3] = (attribute_type >> 0) & 0xFF; + bytes[4] = (attribute_len >> 8) & 0xFF; + bytes[5] = (attribute_len >> 0) & 0xFF; + + return HAL_OK; +} + +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) +{ + if (bytes == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + const uint8_t *b = bytes; + const uint8_t * const end = bytes + bytes_len; + + for (int i = 0; i < attributes_len; i++) { + uint32_t type; + size_t length; + hal_error_t err = read_header(b, end - b, &type, &length); + if (err != HAL_OK) + return err; + if (b + hal_ks_attribute_header_size + length > end) + return HAL_ERROR_BAD_ATTRIBUTE_LENGTH; + b += hal_ks_attribute_header_size; + if (attributes != NULL) { + attributes[i].type = type; + attributes[i].length = length; + attributes[i].value = b; + } + b += length; + } + + if (total_len != NULL) + *total_len = b - bytes; + + return HAL_OK; +} + +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) +{ + if (bytes == NULL || attributes == NULL || attributes_len == NULL || total_len == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + int i = 0; + + while (i < *attributes_len && attributes[i].type != type) + i++; + + if (i == *attributes_len) + return HAL_OK; + + const size_t delete_length = hal_ks_attribute_header_size + attributes[i].length; + const size_t delete_offset = (uint8_t*) attributes[i].value - hal_ks_attribute_header_size - bytes; + + if (delete_offset + delete_length > *total_len) + return HAL_ERROR_IMPOSSIBLE; + + memmove(bytes + delete_offset, + bytes + delete_offset + delete_length, + *total_len - delete_length - delete_offset); + + return hal_ks_attribute_scan(bytes, bytes_len, attributes, --*attributes_len, total_len); +} + +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) + +{ + if (bytes == NULL || attributes == NULL || attributes_len == NULL || + total_len == NULL || value == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_error_t err + = hal_ks_attribute_delete(bytes, bytes_len, attributes, attributes_len, total_len, type); + + if (err != HAL_OK) + return err; + + if (*total_len + hal_ks_attribute_header_size + value_len > bytes_len) + return HAL_ERROR_RESULT_TOO_LONG; + + uint8_t *b = bytes + *total_len; + + if ((err = write_header(b, bytes_len - *total_len, type, value_len)) != HAL_OK) + return err; + + memcpy(b + hal_ks_attribute_header_size, value, value_len); + + return hal_ks_attribute_scan(bytes, bytes_len, attributes, ++*attributes_len, total_len); +} + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ @@ -33,315 +33,1945 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define HAL_OK LIBHAL_OK +#include <stddef.h> +#include <string.h> +#include <assert.h> + #include "hal.h" #include "hal_internal.h" -#undef HAL_OK + +#include "last_gasp_pin_internal.h" #define HAL_OK CMIS_HAL_OK #include "stm-keystore.h" -#include "masterkey.h" #undef HAL_OK -#include <string.h> +/* + * 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 { + BLOCK_TYPE_ERASED = 0xFF, /* Pristine erased block (candidate for reuse) */ + BLOCK_TYPE_ZEROED = 0x00, /* Zeroed block (recently used) */ + BLOCK_TYPE_KEY = 0x55, /* Block contains key material */ + BLOCK_TYPE_ATTR = 0x66, /* Block contains key attributes (overflow from key block) */ + BLOCK_TYPE_PIN = 0xAA, /* Block contains PINs */ + BLOCK_TYPE_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ +} flash_block_type_t; + +/* + * Block status. + */ + +typedef enum { + BLOCK_STATUS_LIVE = 0x66, /* This is a live flash block */ + BLOCK_STATUS_TOMBSTONE = 0x44, /* This is a tombstone left behind during an update */ + BLOCK_STATUS_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ +} flash_block_status_t; + +/* + * Common header for all flash block types. + * A few of these fields are deliberately omitted from the CRC. + */ + +typedef struct { + uint8_t block_type; + uint8_t block_status; + uint8_t total_chunks; + uint8_t this_chunk; + hal_crc32_t crc; +} flash_block_header_t; + +/* + * Key block. Tail end of "der" field (after der_len) used for attributes. + */ + +typedef struct { + flash_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" */ +} flash_key_block_t; + +#define SIZEOF_FLASH_KEY_BLOCK_DER \ + (KEYSTORE_SUBSECTOR_SIZE - offsetof(flash_key_block_t, der)) + +/* + * Key attribute overflow block (attributes which don't fit in der field of key block). + */ + +typedef struct { + flash_block_header_t header; + hal_uuid_t name; + unsigned attributes_len; + uint8_t attributes[]; /* Must be last field -- C99 "flexible array member" */ +} flash_attributes_block_t; + +#define SIZEOF_FLASH_ATTRIBUTE_BLOCK_ATTRIBUTES \ + (KEYSTORE_SUBSECTOR_SIZE - offsetof(flash_attributes_block_t, attributes)) + +/* + * PIN block. Also includes space for backing up the KEK when + * HAL_MKM_FLASH_BACKUP_KLUDGE is enabled. + */ + +typedef struct { + flash_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 +} flash_pin_block_t; + +#define FLASH_KEK_SET 0x33333333 + +/* + * One flash block. + */ +typedef union { + uint8_t bytes[KEYSTORE_SUBSECTOR_SIZE]; + flash_block_header_t header; + flash_key_block_t key; + flash_attributes_block_t attr; + flash_pin_block_t pin; +} flash_block_t; -#define PAGE_SIZE_MASK (KEYSTORE_PAGE_SIZE - 1) +/* + * In-memory cache. + */ + +typedef struct { + unsigned blockno; + uint32_t lru; + flash_block_t block; +} cache_block_t; + +/* + * In-memory database. + * + * The top-level structure is a static variable; the arrays are allocated at runtime + * using hal_allocate_static_memory() because they can get kind of large. + */ + +#ifndef KS_FLASH_CACHE_SIZE +#define KS_FLASH_CACHE_SIZE 4 +#endif + +#define NUM_FLASH_BLOCKS KEYSTORE_NUM_SUBSECTORS + +typedef struct { + hal_ks_t ks; /* Must be first (C "subclassing") */ + hal_ks_index_t ksi; + hal_ks_pin_t wheel_pin; + hal_ks_pin_t so_pin; + hal_ks_pin_t user_pin; + uint32_t cache_lru; + cache_block_t *cache; +} db_t; /* - * 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. + * 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). */ -static hal_ks_keydb_t db[1]; +const static hal_uuid_t pin_uuid = {{0}}; -#define FLASH_SECTOR_1_OFFSET (0 * KEYSTORE_SECTOR_SIZE) -#define FLASH_SECTOR_2_OFFSET (1 * KEYSTORE_SECTOR_SIZE) +/* + * The in-memory database structure itself is small, but the arrays it + * points to are large enough that they come from SDRAM allocated at + * startup. + */ -uint32_t _active_sector_offset() +static db_t db; + +/* + * Type safe casts. + */ + +static inline flash_block_type_t block_get_type(const flash_block_t * const block) { - /* 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; + assert(block != NULL); + return (flash_block_type_t) block->header.block_type; } -uint32_t _get_key_offset(uint32_t num) +static inline flash_block_status_t block_get_status(const flash_block_t * const block) { - /* 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; + assert(block != NULL); + return (flash_block_status_t) block->header.block_status; } -const hal_ks_keydb_t *hal_ks_get_keydb(void) +/* + * Pick unused or least-recently-used slot in our in-memory cache. + * + * Updating lru values is caller's problem: if caller is using a cache + * slot as a temporary buffer and there's no point in caching the + * result, leave the lru values alone and the right thing will happen. + */ + +static inline flash_block_t *cache_pick_lru(void) { - uint32_t offset, i, idx = 0, active_sector_offset; - hal_ks_key_t *key; - uint8_t page_buf[KEYSTORE_PAGE_SIZE]; + uint32_t best_delta = 0; + int best_index = 0; - memset(db, 0, sizeof(*db)); + for (int i = 0; i < KS_FLASH_CACHE_SIZE; i++) { - if (keystore_check_id() != 1) return NULL; + if (db.cache[i].blockno == ~0) + return &db.cache[i].block; - active_sector_offset = _active_sector_offset(); + const uint32_t delta = db.cache_lru - db.cache[i].lru; + if (delta > best_delta) { + best_delta = delta; + best_index = i; + } - /* 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; - } + db.cache[best_index].blockno = ~0; + return &db.cache[best_index].block; +} - offset += active_sector_offset; +/* + * Find a block in our in-memory cache; return block or NULL if not present. + */ - if (keystore_read_data(offset, page_buf, sizeof(page_buf)) != 1) return NULL; +static inline flash_block_t *cache_find_block(const unsigned blockno) +{ + for (int i = 0; i < KS_FLASH_CACHE_SIZE; i++) + if (db.cache[i].blockno == blockno) + return &db.cache[i].block; + return NULL; +} - key = (hal_ks_key_t *) page_buf; - if (key->in_use == 0xff) { - /* unprogrammed data */ - idx++; - continue; - } +/* + * Mark a block in our in-memory cache as being in current use. + */ - 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++; +static inline void cache_mark_used(const flash_block_t * const block, const unsigned blockno) +{ + for (int i = 0; i < KS_FLASH_CACHE_SIZE; i++) { + if (&db.cache[i].block == block) { + db.cache[i].blockno = blockno; + db.cache[i].lru = ++db.cache_lru; + return; } + } +} - return db; +/* + * Release a block from the in-memory cache. + */ + +static inline void cache_release(const flash_block_t * const block) +{ + if (block != NULL) + cache_mark_used(block, ~0); } -hal_error_t _write_data_to_flash(const uint32_t offset, const uint8_t *data, const size_t len) +/* + * Generate CRC-32 for a block. + * + * This function needs to understand the structure of + * flash_block_header_t, so that it can skip over fields that + * shouldn't be included in the CRC. + */ + +static hal_crc32_t calculate_block_crc(const flash_block_t * const block) { - uint8_t page_buf[KEYSTORE_PAGE_SIZE]; - uint32_t to_write = len; + assert(block != NULL); - 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; - } - } + hal_crc32_t crc = hal_crc32_init(); + + crc = hal_crc32_update(crc, &block->header.block_type, + sizeof(block->header.block_type)); + + crc = hal_crc32_update(crc, &block->header.total_chunks, + sizeof(block->header.total_chunks)); + + crc = hal_crc32_update(crc, &block->header.this_chunk, + sizeof(block->header.this_chunk)); + + crc = hal_crc32_update(crc, block->bytes + sizeof(flash_block_header_t), + sizeof(*block) - sizeof(flash_block_header_t)); + + return hal_crc32_finalize(crc); +} + +/* + * Calculate block offset. + */ + +static inline uint32_t block_offset(const unsigned blockno) +{ + return blockno * KEYSTORE_SUBSECTOR_SIZE; +} + +/* + * Read a flash block. + * + * Flash read on the Alpha is slow enough that it pays to check the + * first page before reading the rest of the block. + */ + +static hal_error_t block_read(const unsigned blockno, flash_block_t *block) +{ + if (block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) + return HAL_ERROR_IMPOSSIBLE; + + /* Sigh, magic numeric return codes */ + if (keystore_read_data(block_offset(blockno), + block->bytes, + KEYSTORE_PAGE_SIZE) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + switch (block_get_type(block)) { + case BLOCK_TYPE_ERASED: + case BLOCK_TYPE_ZEROED: + return HAL_OK; + case BLOCK_TYPE_KEY: + case BLOCK_TYPE_PIN: + case BLOCK_TYPE_ATTR: + break; + default: + return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE; + } + + switch (block_get_status(block)) { + case BLOCK_STATUS_LIVE: + case BLOCK_STATUS_TOMBSTONE: + break; + default: + return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE; + } + + /* Sigh, magic numeric return codes */ + if (keystore_read_data(block_offset(blockno) + KEYSTORE_PAGE_SIZE, + block->bytes + KEYSTORE_PAGE_SIZE, + sizeof(*block) - KEYSTORE_PAGE_SIZE) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + if (calculate_block_crc(block) != block->header.crc) + return HAL_ERROR_KEYSTORE_BAD_CRC; + + return HAL_OK; +} + +/* + * Read a block using the cache. Marking the block as used is left + * for the caller, so we can avoid blowing out the cache when we + * perform a ks_match() operation. + */ + +static hal_error_t block_read_cached(const unsigned blockno, flash_block_t **block) +{ + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if ((*block = cache_find_block(blockno)) != NULL) + return HAL_OK; + + if ((*block = cache_pick_lru()) == NULL) + return HAL_ERROR_IMPOSSIBLE; + + return block_read(blockno, *block); +} + +/* + * Convert a live block into a tombstone. Caller is responsible for + * making sure that the block being converted is valid; since we don't + * need to update the CRC for this, we just modify the first page. + */ + +static hal_error_t block_deprecate(const unsigned blockno) +{ + if (blockno >= NUM_FLASH_BLOCKS) + return HAL_ERROR_IMPOSSIBLE; + + uint8_t page[KEYSTORE_PAGE_SIZE]; + flash_block_header_t *header = (void *) page; + uint32_t offset = block_offset(blockno); + + /* Sigh, magic numeric return codes */ + if (keystore_read_data(offset, page, sizeof(page)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + header->block_status = BLOCK_STATUS_TOMBSTONE; + + /* Sigh, magic numeric return codes */ + if (keystore_write_data(offset, page, sizeof(page)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + return HAL_OK; +} + +/* + * Zero (not erase) a flash block. Just need to zero the first page. + */ + +static hal_error_t block_zero(const unsigned blockno) +{ + if (blockno >= NUM_FLASH_BLOCKS) + return HAL_ERROR_IMPOSSIBLE; + + uint8_t page[KEYSTORE_PAGE_SIZE] = {0}; + + /* Sigh, magic numeric return codes */ + if (keystore_write_data(block_offset(blockno), page, sizeof(page)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + return HAL_OK; +} + +/* + * Erase a flash block. Also see block_erase_maybe(), below. + */ + +static hal_error_t block_erase(const unsigned blockno) +{ + if (blockno >= NUM_FLASH_BLOCKS) + return HAL_ERROR_IMPOSSIBLE; + + /* Sigh, magic numeric return codes */ + if (keystore_erase_subsectors(blockno, blockno) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + return HAL_OK; +} + +/* + * Erase a flash block if it hasn't already been erased. + * May not be necessary, trying to avoid unnecessary wear. + * + * Unclear whether there's any sane reason why this needs to be + * constant time, given how slow erasure is. But side channel attacks + * can be tricky things, and it's theoretically possible that we could + * leak information about, eg, key length, so we do constant time. + */ + +static hal_error_t block_erase_maybe(const unsigned blockno) +{ + if (blockno >= NUM_FLASH_BLOCKS) + return HAL_ERROR_IMPOSSIBLE; + + uint8_t mask = 0xFF; + + for (uint32_t a = block_offset(blockno); a < block_offset(blockno + 1); a += KEYSTORE_PAGE_SIZE) { + uint8_t page[KEYSTORE_PAGE_SIZE]; + if (keystore_read_data(a, page, sizeof(page)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + for (int i = 0; i < KEYSTORE_PAGE_SIZE; i++) + mask &= page[i]; + } + + return mask == 0xFF ? HAL_OK : block_erase(blockno); +} + +/* + * Write a flash block, calculating CRC when appropriate. + */ + +static hal_error_t block_write(const unsigned blockno, flash_block_t *block) +{ + if (block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) + return HAL_ERROR_IMPOSSIBLE; + + hal_error_t err = block_erase_maybe(blockno); + + if (err != HAL_OK) + return err; + + switch (block_get_type(block)) { + case BLOCK_TYPE_KEY: + case BLOCK_TYPE_PIN: + case BLOCK_TYPE_ATTR: + block->header.crc = calculate_block_crc(block); + break; + default: + break; + } + + /* Sigh, magic numeric return codes */ + if (keystore_write_data(block_offset(blockno), block->bytes, sizeof(*block)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + return HAL_OK; +} + +/* + * Update one flash block, including zombie jamboree. + */ + +static hal_error_t block_update(const unsigned b1, flash_block_t *block, + const hal_uuid_t * const uuid, const unsigned chunk, int *hint) +{ + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if (db.ksi.used == db.ksi.size) + return HAL_ERROR_NO_KEY_INDEX_SLOTS; + + cache_release(block); - return LIBHAL_OK; + hal_error_t err; + unsigned b2; + + if ((err = block_deprecate(b1)) != HAL_OK || + (err = hal_ks_index_replace(&db.ksi, uuid, chunk, &b2, hint)) != HAL_OK || + (err = block_write(b2, block)) != HAL_OK || + (err = block_zero(b1)) != HAL_OK) + return err; + + cache_mark_used(block, b2); + + return block_erase_maybe(db.ksi.index[db.ksi.used]); } /* - * Write the full DB to flash, PINs and all. + * Forward reference. */ -hal_error_t _write_db_to_flash(const uint32_t sector_offset) + +static hal_error_t fetch_pin_block(unsigned *b, flash_block_t **block); + +/* + * Initialize keystore. This includes various tricky bits, some of + * which attempt to preserve the free list ordering across reboots, to + * improve our simplistic attempt at wear leveling, others attempt to + * recover from unclean shutdown. + */ + +static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size) +{ + if (mem == NULL || *mem == NULL || len == NULL || size > *len) + return NULL; + void *ret = *mem; + *mem += size; + *len -= size; + return ret; +} + +static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc) { - hal_error_t status; - uint8_t page_buf[KEYSTORE_PAGE_SIZE]; - uint32_t i, offset; + /* + * Initialize the in-memory database. + */ + + if (alloc) { + + size_t len = (sizeof(*db.ksi.index) * NUM_FLASH_BLOCKS + + sizeof(*db.ksi.names) * NUM_FLASH_BLOCKS + + sizeof(*db.cache) * KS_FLASH_CACHE_SIZE); + + uint8_t *mem = hal_allocate_static_memory(len); + + if (mem == NULL) + return HAL_ERROR_ALLOCATION_FAILURE; + + memset(&db, 0, sizeof(db)); + memset(mem, 0, len); + + db.ksi.index = gnaw(&mem, &len, sizeof(*db.ksi.index) * NUM_FLASH_BLOCKS); + db.ksi.names = gnaw(&mem, &len, sizeof(*db.ksi.names) * NUM_FLASH_BLOCKS); + db.cache = gnaw(&mem, &len, sizeof(*db.cache) * KS_FLASH_CACHE_SIZE); + db.ksi.size = NUM_FLASH_BLOCKS; + } + + else { + memset(&db.wheel_pin, 0, sizeof(db.wheel_pin)); + memset(&db.so_pin, 0, sizeof(db.so_pin)); + memset(&db.user_pin, 0, sizeof(db.user_pin)); + } + + db.ksi.used = 0; + + if (db.ksi.index == NULL || db.ksi.names == NULL || db.cache == NULL) + return HAL_ERROR_IMPOSSIBLE; + + for (int i = 0; i < KS_FLASH_CACHE_SIZE; i++) + db.cache[i].blockno = ~0; + + /* + * Scan existing content of flash to figure out what we've got. + * This gets a bit involved due to the need to recover from things + * like power failures at inconvenient times. + */ + + flash_block_type_t block_types[NUM_FLASH_BLOCKS]; + flash_block_status_t block_status[NUM_FLASH_BLOCKS]; + flash_block_t *block = cache_pick_lru(); + int first_erased = -1; + hal_error_t err; + uint16_t n = 0; + + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; + + for (int i = 0; i < NUM_FLASH_BLOCKS; i++) { + + /* + * Read one block. If the CRC is bad or the block type is + * unknown, it's old data we don't understand, something we were + * writing when we crashed, or bad flash; in any of these cases, + * we want the block to ends up near the end of the free list. + */ + + err = block_read(i, block); + + if (err == HAL_ERROR_KEYSTORE_BAD_CRC || err == HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE) + block_types[i] = BLOCK_TYPE_UNKNOWN; + + else if (err == HAL_OK) + block_types[i] = block_get_type(block); + + else + return err; - if (sizeof(db->wheel_pin) + sizeof(db->so_pin) + sizeof(db->user_pin) > sizeof(page_buf)) { - return HAL_ERROR_BAD_ARGUMENTS; + switch (block_types[i]) { + case BLOCK_TYPE_KEY: + case BLOCK_TYPE_PIN: + case BLOCK_TYPE_ATTR: + block_status[i] = block_get_status(block); + break; + default: + block_status[i] = BLOCK_STATUS_UNKNOWN; } - /* 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; + /* + * First erased block we see is head of the free list. + */ + + if (block_types[i] == BLOCK_TYPE_ERASED && first_erased < 0) + first_erased = i; + + /* + * If it's a valid data block, include it in the index. We remove + * tombstones (if any) below, for now it's easiest to include them + * in the index, so we can look them up by name if we must. + */ + + const hal_uuid_t *uuid = NULL; + + switch (block_types[i]) { + case BLOCK_TYPE_KEY: uuid = &block->key.name; break; + case BLOCK_TYPE_ATTR: uuid = &block->attr.name; break; + case BLOCK_TYPE_PIN: uuid = &pin_uuid; break; + default: /* Keep GCC happy */ break; } - 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; - } + if (uuid != NULL) { + db.ksi.names[i].name = *uuid; + db.ksi.names[i].chunk = block->header.this_chunk; + db.ksi.index[n++] = i; + } + } + + db.ksi.used = n; + + assert(db.ksi.used <= db.ksi.size); + + /* + * At this point we've built the (unsorted) index from all the valid + * blocks. Now we need to insert free and unrecognized blocks into + * the free list in our preferred order. It's possible that there's + * a better way to do this than linear scan, but this is just + * integer comparisons in a fairly small data set, so it's probably + * not worth trying to optimize. + */ + + if (n < db.ksi.size) + for (int i = 0; i < NUM_FLASH_BLOCKS; i++) + if (block_types[i] == BLOCK_TYPE_ERASED) + db.ksi.index[n++] = i; + + if (n < db.ksi.size) + for (int i = first_erased; i < NUM_FLASH_BLOCKS; i++) + if (block_types[i] == BLOCK_TYPE_ZEROED) + db.ksi.index[n++] = i; + + if (n < db.ksi.size) + for (int i = 0; i < first_erased; i++) + if (block_types[i] == BLOCK_TYPE_ZEROED) + db.ksi.index[n++] = i; + + if (n < db.ksi.size) + for (int i = 0; i < NUM_FLASH_BLOCKS; i++) + if (block_types[i] == BLOCK_TYPE_UNKNOWN) + db.ksi.index[n++] = i; + + assert(n == db.ksi.size); + + /* + * Initialize the index. + */ + + if ((err = hal_ks_index_setup(&db.ksi)) != HAL_OK) + return err; + + /* + * We might want to call hal_ks_index_fsck() here, if we can figure + * out some safe set of recovery actions we can take. + */ + + /* + * Deal with tombstones. These are blocks left behind when + * something bad (like a power failure) happened while we updating. + * The sequence of operations while updating is designed so that, + * barring a bug or a hardware failure, we should never lose data. + * + * For any tombstone we find, we start by looking for all the blocks + * with a matching UUID, then see what valid sequences we can + * construct from what we found. + * + * If we can construct a valid sequence of live blocks, the complete + * update was written out, and we just need to zero the tombstones. + * + * Otherwise, if we can construct a complete sequence of tombstone + * blocks, the update failed before it was completely written, so we + * have to zero the incomplete sequence of live blocks then restore + * from the tombstones. + * + * Otherwise, if the live and tombstone blocks taken together form a + * valid sequence, the update failed while deprecating the old live + * blocks, and the update itself was not written, so we need to + * restore the tombstones and leave the live blocks alone. + * + * If none of the above applies, we don't understand what happened, + * which is a symptom of either a bug or a hardware failure more + * serious than simple loss of power or reboot at an inconvenient + * time, so we error out to avoid accidental loss of data. + */ + + for (int i = 0; i < NUM_FLASH_BLOCKS; i++) { + + if (block_status[i] != BLOCK_STATUS_TOMBSTONE) + continue; + + hal_uuid_t name = db.ksi.names[i].name; + unsigned n_blocks; + int where = -1; + + if ((err = hal_ks_index_find_range(&db.ksi, &name, 0, &n_blocks, NULL, &where, 0)) != HAL_OK) + return err; + + while (where > 0 && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where - 1]].name)) { + where--; + n_blocks++; + } + + int live_ok = 1, tomb_ok = 1, join_ok = 1; + unsigned n_live = 0, n_tomb = 0; + unsigned i_live = 0, i_tomb = 0; + + for (int j = 0; j < n_blocks; j++) { + unsigned b = db.ksi.index[where + j]; + switch (block_status[b]) { + case BLOCK_STATUS_LIVE: n_live++; break; + case BLOCK_STATUS_TOMBSTONE: n_tomb++; break; + default: return HAL_ERROR_IMPOSSIBLE; + } + } + + uint16_t live_blocks[n_live], tomb_blocks[n_tomb]; + + for (int j = 0; j < n_blocks; j++) { + unsigned b = db.ksi.index[where + j]; - offset += sector_offset; + if ((err = block_read(b, block)) != HAL_OK) + return err; - if ((status =_write_data_to_flash(offset, (uint8_t *) &db->keys[i], sizeof(*db->keys))) != LIBHAL_OK) { - return status; + join_ok &= block->header.this_chunk == j && block->header.total_chunks == n_blocks; + + switch (block_status[b]) { + case BLOCK_STATUS_LIVE: + live_blocks[i_live] = b; + live_ok &= block->header.this_chunk == i_live++ && block->header.total_chunks == n_live; + break; + case BLOCK_STATUS_TOMBSTONE: + tomb_blocks[i_tomb] = b; + tomb_ok &= block->header.this_chunk == i_tomb++ && block->header.total_chunks == n_tomb; + break; + default: + return HAL_ERROR_IMPOSSIBLE; + } + } + + if (!live_ok && !tomb_ok && !join_ok) + return HAL_ERROR_KEYSTORE_LOST_DATA; + + if (live_ok) { + for (int j = 0; j < n_tomb; j++) { + const unsigned b = tomb_blocks[j]; + if ((err = block_zero(b)) != HAL_OK) + return err; + block_types[b] = BLOCK_TYPE_ZEROED; + block_status[b] = BLOCK_STATUS_UNKNOWN; + } + } + + else if (tomb_ok) { + for (int j = 0; j < n_live; j++) { + const unsigned b = live_blocks[j]; + if ((err = block_zero(b)) != HAL_OK) + return err; + block_types[b] = BLOCK_TYPE_ZEROED; + block_status[b] = BLOCK_STATUS_UNKNOWN; + } + } + + if (live_ok) { + memcpy(&db.ksi.index[where], live_blocks, n_live * sizeof(*db.ksi.index)); + memmove(&db.ksi.index[where + n_live], &db.ksi.index[where + n_blocks], + (db.ksi.size - where - n_blocks) * sizeof(*db.ksi.index)); + memcpy(&db.ksi.index[db.ksi.size - n_tomb], tomb_blocks, n_tomb * sizeof(*db.ksi.index)); + db.ksi.used -= n_tomb; + n_blocks = n_live; + } + + else if (tomb_ok) { + memcpy(&db.ksi.index[where], tomb_blocks, n_tomb * sizeof(*db.ksi.index)); + memmove(&db.ksi.index[where + n_tomb], &db.ksi.index[where + n_blocks], + (db.ksi.size - where - n_blocks) * sizeof(*db.ksi.index)); + memcpy(&db.ksi.index[db.ksi.size - n_live], live_blocks, n_live * sizeof(*db.ksi.index)); + db.ksi.used -= n_live; + n_blocks = n_tomb; + } + + for (int j = 0; j < n_blocks; j++) { + int hint = where + j; + unsigned b1 = db.ksi.index[hint], b2; + if (block_status[b1] != BLOCK_STATUS_TOMBSTONE) + continue; + if ((err = block_read(b1, block)) != HAL_OK) + return err; + block->header.block_status = BLOCK_STATUS_LIVE; + if ((err = hal_ks_index_replace(&db.ksi, &name, j, &b2, &hint)) != HAL_OK || + (err = block_write(b2, block)) != HAL_OK) + return err; + block_types[b1] = BLOCK_TYPE_ZEROED; + block_status[b1] = BLOCK_STATUS_UNKNOWN; + block_status[b2] = BLOCK_STATUS_LIVE; + } + } + + err = fetch_pin_block(NULL, &block); + + if (err == HAL_OK) { + db.wheel_pin = block->pin.wheel_pin; + db.so_pin = block->pin.so_pin; + db.user_pin = block->pin.user_pin; + } + + else if (err != HAL_ERROR_KEY_NOT_FOUND) + return err; + + else { + /* + * We found no PIN block, so create one, with the user and so PINs + * cleared and the wheel PIN set to the last-gasp value. The + * last-gasp WHEEL PIN is a terrible answer, but we need some kind + * of bootstrapping mechanism when all else fails. If you have a + * better suggestion, we'd love to hear it. + */ + + unsigned b; + + memset(block, 0xFF, sizeof(*block)); + + block->header.block_type = BLOCK_TYPE_PIN; + block->header.block_status = BLOCK_STATUS_LIVE; + block->header.total_chunks = 1; + block->header.this_chunk = 0; + + block->pin.wheel_pin = db.wheel_pin = hal_last_gasp_pin; + block->pin.so_pin = db.so_pin; + block->pin.user_pin = db.user_pin; + + if ((err = hal_ks_index_add(&db.ksi, &pin_uuid, 0, &b, NULL)) != HAL_OK) + return err; + + cache_mark_used(block, b); + + err = block_write(b, block); + + cache_release(block); + + if (err != HAL_OK) + return err; + } + + /* + * Erase first block on free list if it's not already erased. + */ + + if (db.ksi.used < db.ksi.size && + (err = block_erase_maybe(db.ksi.index[db.ksi.used])) != HAL_OK) + return err; + + /* + * And we're finally done. + */ + + db.ks.driver = driver; + + return HAL_OK; +} + +static hal_error_t ks_shutdown(const hal_ks_driver_t * const driver) +{ + if (db.ks.driver != driver) + return HAL_ERROR_KEYSTORE_ACCESS; + return HAL_OK; +} + +static hal_error_t ks_open(const hal_ks_driver_t * const driver, + hal_ks_t **ks) +{ + if (driver != hal_ks_token_driver || ks == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + *ks = &db.ks; + return HAL_OK; +} + +static hal_error_t ks_close(hal_ks_t *ks) +{ + if (ks != NULL && ks != &db.ks) + return HAL_ERROR_BAD_ARGUMENTS; + + return HAL_OK; +} + +static inline int acceptable_key_type(const hal_key_type_t type) +{ + switch (type) { + case HAL_KEY_TYPE_RSA_PRIVATE: + case HAL_KEY_TYPE_EC_PRIVATE: + case HAL_KEY_TYPE_RSA_PUBLIC: + case HAL_KEY_TYPE_EC_PUBLIC: + return 1; + default: + return 0; + } +} + +static hal_error_t ks_store(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const uint8_t * const der, const size_t der_len) +{ + if (ks != &db.ks || slot == NULL || der == NULL || der_len == 0 || !acceptable_key_type(slot->type)) + return HAL_ERROR_BAD_ARGUMENTS; + + flash_block_t *block = cache_pick_lru(); + flash_key_block_t *k = &block->key; + uint8_t kek[KEK_LENGTH]; + size_t kek_len; + hal_error_t err; + unsigned b; + + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if ((err = hal_ks_index_add(&db.ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + return err; + + cache_mark_used(block, b); + + memset(block, 0xFF, sizeof(*block)); + + block->header.block_type = BLOCK_TYPE_KEY; + block->header.block_status = BLOCK_STATUS_LIVE; + block->header.total_chunks = 1; + block->header.this_chunk = 0; + + k->name = slot->name; + k->type = slot->type; + k->curve = slot->curve; + k->flags = slot->flags; + k->der_len = SIZEOF_FLASH_KEY_BLOCK_DER; + k->attributes_len = 0; + + if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK) + err = hal_aes_keywrap(NULL, kek, kek_len, der, der_len, k->der, &k->der_len); + + memset(kek, 0, sizeof(kek)); + + if (err == HAL_OK && + (err = block_write(b, block)) == HAL_OK) + return HAL_OK; + + memset(block, 0, sizeof(*block)); + cache_release(block); + (void) hal_ks_index_delete(&db.ksi, &slot->name, 0, NULL, &slot->hint); + return err; +} + +static hal_error_t ks_fetch(hal_ks_t *ks, + hal_pkey_slot_t *slot, + uint8_t *der, size_t *der_len, const size_t der_max) +{ + if (ks != &db.ks || slot == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + flash_block_t *block; + hal_error_t err; + unsigned b; + + if ((err = hal_ks_index_find(&db.ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK || + (err = block_read_cached(b, &block)) != HAL_OK) + return err; + + if (block_get_type(block) != BLOCK_TYPE_KEY) + return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */ + + cache_mark_used(block, b); + + flash_key_block_t *k = &block->key; + + slot->type = k->type; + slot->curve = k->curve; + slot->flags = k->flags; + + if (der == NULL && der_len != NULL) + *der_len = k->der_len; + + if (der != NULL) { + + uint8_t kek[KEK_LENGTH]; + size_t kek_len, der_len_; + hal_error_t err; + + if (der_len == NULL) + der_len = &der_len_; + + *der_len = der_max; + + if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK) + err = hal_aes_keyunwrap(NULL, kek, kek_len, k->der, k->der_len, der, der_len); + + memset(kek, 0, sizeof(kek)); + + if (err != HAL_OK) + return err; + } + + return HAL_OK; +} + +static hal_error_t ks_delete(hal_ks_t *ks, + hal_pkey_slot_t *slot) +{ + if (ks != &db.ks || slot == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_error_t err; + unsigned n; + + if ((err = hal_ks_index_delete_range(&db.ksi, &slot->name, 0, &n, NULL, &slot->hint)) != HAL_OK) + return err; + + unsigned b[n]; + + if ((err = hal_ks_index_delete_range(&db.ksi, &slot->name, n, NULL, b, &slot->hint)) != HAL_OK) + return err; + + for (int i = 0; i < n; i++) + cache_release(cache_find_block(b[i])); + + for (int i = 0; i < n; i++) + if ((err = block_zero(b[i])) != HAL_OK) + return err; + + return block_erase_maybe(db.ksi.index[db.ksi.used]); +} + +static inline hal_error_t locate_attributes(flash_block_t *block, const unsigned chunk, + uint8_t **bytes, size_t *bytes_len, + unsigned **attrs_len) +{ + if (block == NULL || bytes == NULL || bytes_len == NULL || attrs_len == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if (chunk == 0) { + if (block_get_type(block) != BLOCK_TYPE_KEY) + return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */ + *attrs_len = &block->key.attributes_len; + *bytes = block->key.der + block->key.der_len; + *bytes_len = SIZEOF_FLASH_KEY_BLOCK_DER - block->key.der_len; + } + + else { + if (block_get_type(block) != BLOCK_TYPE_ATTR) + return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */ + *attrs_len = &block->attr.attributes_len; + *bytes = block->attr.attributes; + *bytes_len = SIZEOF_FLASH_ATTRIBUTE_BLOCK_ATTRIBUTES; + } + + return HAL_OK; +} + +static hal_error_t ks_match(hal_ks_t *ks, + const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, + unsigned *result_len, + const unsigned result_max, + const hal_uuid_t * const previous_uuid) +{ + if (ks == NULL || (attributes == NULL && attributes_len > 0) || + result == NULL || result_len == NULL || previous_uuid == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + uint8_t need_attr[attributes_len > 0 ? attributes_len : 1]; + flash_block_t *block; + int possible = 0; + hal_error_t err; + int i = -1; + + *result_len = 0; + + err = hal_ks_index_find(&db.ksi, previous_uuid, 0, NULL, &i); + + if (err == HAL_ERROR_KEY_NOT_FOUND) + i--; + else if (err != HAL_OK) + return err; + + while (*result_len < result_max && ++i < db.ksi.used) { + + unsigned b = db.ksi.index[i]; + + if (db.ksi.names[b].chunk == 0) + possible = 1; + + if (!possible) + continue; + + if ((err = block_read_cached(b, &block)) != HAL_OK) + return err; + + if (db.ksi.names[b].chunk == 0) { + memset(need_attr, 1, sizeof(need_attr)); + possible = ((type == HAL_KEY_TYPE_NONE || type == block->key.type) && + (curve == HAL_CURVE_NONE || curve == block->key.curve)); + } + + if (!possible) + continue; + + if (attributes_len > 0) { + uint8_t *bytes = NULL; + size_t bytes_len = 0; + unsigned *attrs_len; + + if ((err = locate_attributes(block, db.ksi.names[b].chunk, + &bytes, &bytes_len, &attrs_len)) != HAL_OK) + return err; + + if (*attrs_len > 0) { + hal_pkey_attribute_t attrs[*attrs_len]; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK) + return err; + + for (int j = 0; possible && j < attributes_len; j++) { + + if (!need_attr[j]) + continue; + + for (hal_pkey_attribute_t *a = attrs; a < attrs + *attrs_len; a++) { + if (a->type != attributes[j].type) + continue; + need_attr[j] = 0; + possible = (a->length == attributes[j].length && + !memcmp(a->value, attributes[j].value, a->length)); + break; + } } + } } - return LIBHAL_OK; + if (!possible) + continue; + + if (attributes_len > 0 && memchr(need_attr, 1, sizeof(need_attr)) != NULL) + continue; + + result[*result_len] = db.ksi.names[b].name; + ++*result_len; + possible = 0; + } + + return HAL_OK; } -hal_error_t hal_ks_set_keydb(const hal_ks_key_t * const key, - const int loc, - const int updating) +/* + * This controls whether we include a separate code path for the + * common case where we can handle attribute setting via a single + * block update. It's a lot simpler when it works, but it's yet + * another code path, and enabling it leaves the slower full-blown + * algorithm less tested. + */ + +#ifndef KS_SET_ATTRIBUTES_SINGLE_BLOCK_UPDATE_FAST_PATH +#define KS_SET_ATTRIBUTES_SINGLE_BLOCK_UPDATE_FAST_PATH 0 +#endif + +/* + * ks_set_attributes() is much too long. Probably needs to be broken + * up into a collection of inline functions, even if most of them end + * up being called exactly once. + */ + +static hal_error_t ks_set_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len) { - hal_error_t status; - uint32_t offset, active_sector_offset; - hal_ks_key_t *tmp_key; - uint8_t page_buf[KEYSTORE_PAGE_SIZE]; + if (ks != &db.ks || slot == NULL || attributes == NULL || attributes_len == 0) + return HAL_ERROR_BAD_ARGUMENTS; - if (key == NULL || loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys) || (!key->in_use != !updating)) - return HAL_ERROR_BAD_ARGUMENTS; + /* + * Perform initial scan of the object to figure out the total + * attribute count and a few other parameters. + * + * If enabled, try to do everything as as a single-block update. If + * single block update fails, we MUST clear the modified block from + * the cache before doing anything else. + */ - offset = _get_key_offset(loc); - if (offset > KEYSTORE_SECTOR_SIZE) return HAL_ERROR_BAD_ARGUMENTS; + unsigned updated_attributes_len = attributes_len; + flash_block_t *block; + unsigned chunk = 0; + hal_error_t err; + unsigned b; + + do { + int hint = slot->hint + chunk; + + if ((err = hal_ks_index_find(&db.ksi, &slot->name, chunk, &b, &hint)) != HAL_OK || + (err = block_read_cached(b, &block)) != HAL_OK) + return err; + + if (block->header.this_chunk != chunk) + return HAL_ERROR_IMPOSSIBLE; + + cache_mark_used(block, b); + + if (chunk == 0) + slot->hint = hint; + + uint8_t *bytes = NULL; + size_t bytes_len = 0; + unsigned *attrs_len; + + if ((err = locate_attributes(block, chunk, &bytes, &bytes_len, &attrs_len)) != HAL_OK) + return err; + + updated_attributes_len += *attrs_len; + +#if KS_SET_ATTRIBUTES_SINGLE_BLOCK_UPDATE_FAST_PATH + + hal_pkey_attribute_t attrs[*attrs_len + attributes_len]; + size_t total; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK) + return err; + + for (int i = 0; err == HAL_OK && i < attributes_len; i++) + if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) + err = hal_ks_attribute_delete(bytes, bytes_len, attrs, attrs_len, &total, + attributes[i].type); + else + err = hal_ks_attribute_insert(bytes, bytes_len, attrs, attrs_len, &total, + attributes[i].type, + attributes[i].value, + attributes[i].length); + + if (err != HAL_OK) + cache_release(block); + + if (err == HAL_ERROR_RESULT_TOO_LONG) + continue; + + if (err != HAL_OK) + return err; + + return block_update(b, block, &slot->name, chunk, &hint); + +#endif /* KS_SET_ATTRIBUTES_SINGLE_BLOCK_UPDATE_FAST_PATH */ + + } while (++chunk < block->header.total_chunks); + + /* + * If we get here, we're on the slow path, which requires rewriting + * all the chunks in this object but which can also add or remove + * chunks from this object. We need to keep track of all the old + * chunks so we can zero them at the end, and because we can't zero + * them until we've written out the new chunks, we need enough free + * blocks to hold all the new chunks. + * + * Calculating all of this is extremely tedious, but flash writes + * are so much more expensive than anything else we do here that + * it's almost certainly worth it. + * + * We don't need the attribute values to compute the sizes, just the + * attribute sizes, so we scan all the existing blocks, build up a + * structure with the current attribute types and sizes, modify that + * according to our arguments, and compute the needed size. Once we + * have that, we can start rewriting existing blocks. We put all + * the new stuff at the end, which simplifies this slightly. + * + * In theory, this process never requires us to have more than two + * blocks in memory at the same time (source and destination when + * copying across chunk boundaries), but having enough cache buffers + * to keep the whole set in memory will almost certainly make this + * run faster. + */ + + hal_pkey_attribute_t updated_attributes[updated_attributes_len]; + const unsigned total_chunks_old = block->header.total_chunks; + size_t bytes_available = 0; - active_sector_offset = _active_sector_offset(); + updated_attributes_len = 0; - offset += active_sector_offset; + /* + * Phase 0.1: Walk the old chunks to populate updated_attributes[]. + * This also initializes bytes_available, since we can only get that + * by reading old chunk zero. + */ - if (keystore_check_id() != 1) return HAL_ERROR_KEYSTORE_ACCESS; + for (chunk = 0; chunk < total_chunks_old; chunk++) { + int hint = slot->hint + chunk; - /* 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; + if ((err = hal_ks_index_find(&db.ksi, &slot->name, chunk, &b, &hint)) != HAL_OK || + (err = block_read_cached(b, &block)) != HAL_OK) + return err; + + if (block->header.this_chunk != chunk) + return HAL_ERROR_IMPOSSIBLE; + + cache_mark_used(block, b); + + uint8_t *bytes = NULL; + size_t bytes_len = 0; + unsigned *attrs_len; + + if ((err = locate_attributes(block, chunk, &bytes, &bytes_len, &attrs_len)) != HAL_OK) + return err; + + hal_pkey_attribute_t attrs[*attrs_len]; + size_t total; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK) + return err; + + if (chunk == 0) + bytes_available = bytes_len; + + for (int i = 0; i < *attrs_len; i++) { + + if (updated_attributes_len >= sizeof(updated_attributes)/sizeof(*updated_attributes)) + return HAL_ERROR_IMPOSSIBLE; + + updated_attributes[updated_attributes_len].type = attrs[i].type; + updated_attributes[updated_attributes_len].length = attrs[i].length; + updated_attributes[updated_attributes_len].value = NULL; + updated_attributes_len++; } - tmp_key = (hal_ks_key_t *) page_buf; + } + + /* + * Phase 0.2: Merge new attributes into updated_attributes[]. + */ + + for (int i = 0; i < attributes_len; i++) { + + for (int j = 0; j < updated_attributes_len; j++) + if (updated_attributes[j].type == attributes[i].type) + updated_attributes[j].length = HAL_PKEY_ATTRIBUTE_NIL; + + if (updated_attributes_len >= sizeof(updated_attributes)/sizeof(*updated_attributes)) + return HAL_ERROR_IMPOSSIBLE; + + updated_attributes[updated_attributes_len].type = attributes[i].type; + updated_attributes[updated_attributes_len].length = attributes[i].length; + updated_attributes[updated_attributes_len].value = attributes[i].value; + updated_attributes_len++; + } + + /* + * Phase 0.3: Prune trailing deletion actions: we don't need them to + * maintain synchronization with existing attributes, and doing so + * simplifies logic for updating the final new chunk. + */ + + while (updated_attributes_len > 0 && + updated_attributes[updated_attributes_len - 1].length == HAL_PKEY_ATTRIBUTE_NIL) + --updated_attributes_len; + + /* + * Phase 0.4: Figure out how many chunks all this will occupy. + */ + + chunk = 0; + + for (int i = 0; i < updated_attributes_len; i++) { - db->keys[loc] = *key; - db->keys[loc].in_use = 1; + if (updated_attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) + continue; - 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; + const size_t needed = hal_ks_attribute_header_size + updated_attributes[i].length; + + if (needed > bytes_available) { + bytes_available = SIZEOF_FLASH_ATTRIBUTE_BLOCK_ATTRIBUTES; + chunk++; + } + + if (needed > bytes_available) + return HAL_ERROR_RESULT_TOO_LONG; + + bytes_available -= needed; + } + + const unsigned total_chunks_new = chunk + 1; + + /* + * If there aren't enough free blocks, give up now, before changing anything. + */ + + if (db.ksi.used + total_chunks_new > db.ksi.size) + return HAL_ERROR_NO_KEY_INDEX_SLOTS; + + /* + * Phase 1: Deprecate all the old chunks, remember where they were. + */ + + unsigned old_blocks[total_chunks_old]; + + for (chunk = 0; chunk < total_chunks_old; chunk++) { + int hint = slot->hint + chunk; + if ((err = hal_ks_index_find(&db.ksi, &slot->name, chunk, &b, &hint)) != HAL_OK || + (err = block_deprecate(b)) != HAL_OK) + return err; + old_blocks[chunk] = b; + } + + /* + * Phase 2: Write new chunks, copying attributes from old chunks or + * from attributes[], as needed. + */ + + { + hal_pkey_attribute_t old_attrs[updated_attributes_len], new_attrs[updated_attributes_len]; + unsigned *old_attrs_len = NULL, *new_attrs_len = NULL; + flash_block_t *old_block = NULL, *new_block = NULL; + uint8_t *old_bytes = NULL, *new_bytes = NULL; + size_t old_bytes_len = 0, new_bytes_len = 0; + unsigned old_chunk = 0, new_chunk = 0; + size_t old_total = 0, new_total = 0; + + int updated_attributes_i = 0, old_attrs_i = 0; + + uint32_t new_attr_type; + size_t new_attr_length; + const uint8_t *new_attr_value; + + while (updated_attributes_i < updated_attributes_len) { + + if (old_chunk >= total_chunks_old || new_chunk >= total_chunks_new) + return HAL_ERROR_IMPOSSIBLE; + + /* + * If we've gotten as far as new data that comes from + * attributes[], we have it in hand and can just copy it. + */ + + if (updated_attributes_len - updated_attributes_i <= attributes_len) { + new_attr_type = updated_attributes[updated_attributes_i].type; + new_attr_length = updated_attributes[updated_attributes_i].length; + new_attr_value = updated_attributes[updated_attributes_i].value; + } + + /* + * Otherwise, we have to read it from an old block, which may in + * turn require reading in the next old block. + */ + + else { + + if (old_block == NULL) { + + if ((err = block_read_cached(old_blocks[old_chunk], &old_block)) != HAL_OK) + return err; + + if (old_block->header.this_chunk != old_chunk) + return HAL_ERROR_IMPOSSIBLE; + + if ((err = locate_attributes(old_block, old_chunk, + &old_bytes, &old_bytes_len, &old_attrs_len)) != HAL_OK || + (err = hal_ks_attribute_scan(old_bytes, old_bytes_len, + old_attrs, *old_attrs_len, &old_total)) != HAL_OK) + return err; + + old_attrs_i = 0; + } + + if (old_attrs_i >= *old_attrs_len) { + old_chunk++; + old_block = NULL; + continue; } - } 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; + + new_attr_type = old_attrs[old_attrs_i].type; + new_attr_length = old_attrs[old_attrs_i].length; + new_attr_value = old_attrs[old_attrs_i].value; + + if (new_attr_type != updated_attributes[updated_attributes_i].type) + return HAL_ERROR_IMPOSSIBLE; + + old_attrs_i++; + } + + /* + * Unless this is a deletion, we should have something to write. + */ + + if (new_attr_length != HAL_PKEY_ATTRIBUTE_NIL && new_attr_value == NULL) + return HAL_ERROR_IMPOSSIBLE; + + /* + * Initialize the new block if necessary. If it's the new chunk + * zero, we need to copy all the non-attribute data from the old + * chunk zero; otherwise, it's a new empty attribute block. + */ + + if (new_block == NULL) { + + new_block = cache_pick_lru(); + memset(new_block, 0xFF, sizeof(*new_block)); + + if (new_chunk == 0) { + flash_block_t *tmp_block; + if ((err = block_read_cached(old_blocks[0], &tmp_block)) != HAL_OK) + return err; + if (tmp_block->header.this_chunk != 0) + return HAL_ERROR_IMPOSSIBLE; + new_block->header.block_type = BLOCK_TYPE_KEY; + new_block->key.name = slot->name; + new_block->key.type = tmp_block->key.type; + new_block->key.curve = tmp_block->key.curve; + new_block->key.flags = tmp_block->key.flags; + new_block->key.der_len = tmp_block->key.der_len; + new_block->key.attributes_len = 0; + memcpy(new_block->key.der, tmp_block->key.der, tmp_block->key.der_len); } - if ((status =_write_db_to_flash(active_sector_offset)) != LIBHAL_OK) { - return status; + else { + new_block->header.block_type = BLOCK_TYPE_ATTR; + new_block->attr.name = slot->name; + new_block->attr.attributes_len = 0; } + + new_block->header.block_status = BLOCK_STATUS_LIVE; + new_block->header.total_chunks = total_chunks_new; + new_block->header.this_chunk = new_chunk; + + if ((err = locate_attributes(new_block, new_chunk, + &new_bytes, &new_bytes_len, &new_attrs_len)) != HAL_OK) + return err; + + new_total = 0; + } + + /* + * After all that setup, we finally get to write the frelling attribute. + */ + + if (new_attr_length != HAL_PKEY_ATTRIBUTE_NIL) + err = hal_ks_attribute_insert(new_bytes, new_bytes_len, new_attrs, new_attrs_len, &new_total, + new_attr_type, new_attr_value, new_attr_length); + + /* + * Figure out what to do next: immediately loop for next + * attribute, write current block, or bail out. + */ + + switch (err) { + case HAL_OK: + if (++updated_attributes_i < updated_attributes_len) + continue; + break; + case HAL_ERROR_RESULT_TOO_LONG: + if (new_chunk > 0 && new_attrs_len == 0) + return err; + break; + default: + return err; + } + + /* + * If we get here, either the current new block is full or we + * finished the last block, so we need to write it out. + */ + + int hint = slot->hint + new_chunk; + + if (new_chunk < total_chunks_old) + err = hal_ks_index_replace(&db.ksi, &slot->name, new_chunk, &b, &hint); + else + err = hal_ks_index_add( &db.ksi, &slot->name, new_chunk, &b, &hint); + + if (err != HAL_OK || (err = block_write(b, new_block)) != HAL_OK) + return err; + + cache_mark_used(new_block, b); + + new_block = NULL; + new_chunk++; } - return LIBHAL_OK; + /* + * If number of blocks shrank, we need to clear trailing entries from the index. + */ + + for (old_chunk = total_chunks_new; old_chunk < total_chunks_old; old_chunk++) { + int hint = slot->hint + old_chunk; + + err = hal_ks_index_delete(&db.ksi, &slot->name, old_chunk, NULL, &hint); + + if (err != HAL_OK) + return err; + } + + } + + /* + * Phase 3: Zero the old chunks we deprecated in phase 1. + */ + + for (chunk = 0; chunk < total_chunks_old; chunk++) + if ((err = block_zero(old_blocks[chunk])) != HAL_OK) + return err; + + return HAL_OK; + +#warning What happens if something goes wrong partway through this awful mess? + // We're left in a state with all the old blocks deprecated and + // (maybe) some of the new blocks current, need to clean that up. + // Would be nice if we could just reuse the keystore initialization + // code, but don't quite see how (yet?). } -hal_error_t hal_ks_del_keydb(const int loc) +static hal_error_t ks_get_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len) { - uint32_t offset; - - if (loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys)) + if (ks != &db.ks || slot == NULL || attributes == NULL || attributes_len == 0 || + attributes_buffer == NULL) return HAL_ERROR_BAD_ARGUMENTS; - offset = _get_key_offset(loc); - if (offset > KEYSTORE_SECTOR_SIZE) { - return HAL_ERROR_BAD_ARGUMENTS; + for (int i = 0; i < attributes_len; i++) { + attributes[i].length = 0; + attributes[i].value = NULL; } - offset += _active_sector_offset(); + uint8_t *abuf = attributes_buffer; + flash_block_t *block = NULL; + unsigned chunk = 0; + unsigned found = 0; + hal_error_t err; + unsigned b; + + do { + int hint = slot->hint + chunk; + + if ((err = hal_ks_index_find(&db.ksi, &slot->name, chunk, &b, &hint)) != HAL_OK || + (err = block_read_cached(b, &block)) != HAL_OK) + return err; + + if (block->header.this_chunk != chunk) + return HAL_ERROR_IMPOSSIBLE; + + if (chunk == 0) + slot->hint = hint; + + cache_mark_used(block, b); - memset(&db->keys[loc], 0, sizeof(*db->keys)); + uint8_t *bytes = NULL; + size_t bytes_len = 0; + unsigned *attrs_len; - /* 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)); + if ((err = locate_attributes(block, chunk, &bytes, &bytes_len, &attrs_len)) != HAL_OK) + return err; + + if (*attrs_len == 0) + continue; + + hal_pkey_attribute_t attrs[*attrs_len]; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK) + return err; + + for (int i = 0; i < attributes_len; i++) { + + if (attributes[i].length > 0) + continue; + + int j = 0; + while (j < *attrs_len && attrs[j].type != attributes[i].type) + j++; + if (j >= *attrs_len) + continue; + found++; + + attributes[i].length = attrs[j].length; + + if (attributes_buffer_len == 0) + continue; + + if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf) + return HAL_ERROR_RESULT_TOO_LONG; + + memcpy(abuf, attrs[j].value, attrs[j].length); + attributes[i].value = abuf; + abuf += attrs[j].length; + } + + } while (found < attributes_len && ++chunk < block->header.total_chunks); + + if (found < attributes_len && attributes_buffer_len > 0) + return HAL_ERROR_ATTRIBUTE_NOT_FOUND; + + return HAL_OK; } -hal_error_t hal_ks_set_pin(const hal_user_t user, - const hal_ks_pin_t * const pin) -{ - uint32_t active_sector_offset; +const hal_ks_driver_t hal_ks_token_driver[1] = {{ + .init = ks_init, + .shutdown = ks_shutdown, + .open = ks_open, + .close = ks_close, + .store = ks_store, + .fetch = ks_fetch, + .delete = ks_delete, + .match = ks_match, + .set_attributes = ks_set_attributes, + .get_attributes = ks_get_attributes +}}; + +/* + * The remaining functions aren't really part of the keystore API per se, + * but they all involve non-key data which we keep in the keystore + * because it's the flash we've got. + */ +/* + * Fetch PIN. This is always cached, so just returned cached value. + */ + +hal_error_t hal_get_pin(const hal_user_t user, + const hal_ks_pin_t **pin) +{ if (pin == NULL) return HAL_ERROR_BAD_ARGUMENTS; - hal_ks_pin_t *p = NULL; - switch (user) { - case HAL_USER_WHEEL: p = &db->wheel_pin; break; - case HAL_USER_SO: p = &db->so_pin; break; - case HAL_USER_NORMAL: p = &db->user_pin; break; - default: return HAL_ERROR_BAD_ARGUMENTS; + case HAL_USER_WHEEL: *pin = &db.wheel_pin; break; + case HAL_USER_SO: *pin = &db.so_pin; break; + case HAL_USER_NORMAL: *pin = &db.user_pin; break; + default: return HAL_ERROR_BAD_ARGUMENTS; } - memcpy(p, pin, sizeof(*p)); + return HAL_OK; +} + +/* + * Fetch PIN block. hint = 0 because we know that the all-zeros UUID + * should always sort to first slot in the index. + */ + +static hal_error_t fetch_pin_block(unsigned *b, flash_block_t **block) +{ + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; - active_sector_offset = _active_sector_offset(); + hal_error_t err; + int hint = 0; + unsigned b_; - /* 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. - */ + if (b == NULL) + b = &b_; - /* 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); + if ((err = hal_ks_index_find(&db.ksi, &pin_uuid, 0, b, &hint)) != HAL_OK || + (err = block_read_cached(*b, block)) != HAL_OK) + return err; + + cache_mark_used(*block, *b); + + if (block_get_type(*block) != BLOCK_TYPE_PIN) + return HAL_ERROR_IMPOSSIBLE; + + return HAL_OK; } +/* + * Update the PIN block. This block should always be present, but we + * have to do the zombie jamboree to make sure we write the new PIN + * block before destroying the old one. hint = 0 because we know that + * the all-zeros UUID should always sort to first slot in the index. + */ -hal_error_t hal_ks_get_kek(uint8_t *kek, - size_t *kek_len, - const size_t kek_max) +static hal_error_t update_pin_block(const unsigned b, + flash_block_t *block, + const flash_pin_block_t * const new_data) { - if (kek == NULL || kek_len == NULL || kek_max < bitsToBytes(128)) + if (block == NULL || new_data == NULL || block_get_type(block) != BLOCK_TYPE_PIN) + return HAL_ERROR_IMPOSSIBLE; + + int hint = 0; + + block->pin = *new_data; + + return block_update(b, block, &pin_uuid, 0, &hint); +} + +/* + * Change a PIN. + */ + +hal_error_t hal_set_pin(const hal_user_t user, + const hal_ks_pin_t * const pin) +{ + if (pin == NULL) return HAL_ERROR_BAD_ARGUMENTS; - const size_t len = ((kek_max < bitsToBytes(192)) ? bitsToBytes(128) : - (kek_max < bitsToBytes(256)) ? bitsToBytes(192) : - bitsToBytes(256)); + flash_block_t *block; + hal_error_t err; + unsigned b; - hal_error_t err = masterkey_volatile_read(kek, len); - if (err == LIBHAL_OK) { - *kek_len = len; - return LIBHAL_OK; - } - if (masterkey_flash_read(kek, len) == LIBHAL_OK) { - *kek_len = len; - return LIBHAL_OK; + if ((err = fetch_pin_block(&b, &block)) != HAL_OK) + return err; + + flash_pin_block_t new_data = block->pin; + hal_ks_pin_t *dp, *bp; + + switch (user) { + case HAL_USER_WHEEL: bp = &new_data.wheel_pin; dp = &db.wheel_pin; break; + case HAL_USER_SO: bp = &new_data.so_pin; dp = &db.so_pin; break; + case HAL_USER_NORMAL: bp = &new_data.user_pin; dp = &db.user_pin; break; + default: return HAL_ERROR_BAD_ARGUMENTS; } - /* Both keystores returned an error, probably HAL_ERROR_MASTERKEY_NOT_SET. - * I could try to be clever and compare the errors, but really the volatile - * keystore is the important one (you shouldn't store the master key in - * flash), so return that error. - */ + const hal_ks_pin_t old_pin = *dp; + *dp = *bp = *pin; + + if ((err = update_pin_block(b, block, &new_data)) != HAL_OK) + *dp = old_pin; + return err; } +#if HAL_MKM_FLASH_BACKUP_KLUDGE + +/* + * Horrible insecure kludge in lieu of a battery for the MKM. + * + * API here is a little strange: all calls pass a length parameter, + * but any length other than the compiled in constant just returns an + * immediate error, there's no notion of buffer max length vs buffer + * used length, querying for the size of buffer really needed, or + * anything like that. + * + * We might want to rewrite this some day, if we don't replace it with + * a battery first. For now we just preserve the API as we found it + * while re-implementing it on top of the new keystore. + */ + +hal_error_t hal_mkm_flash_read(uint8_t *buf, const size_t len) +{ + if (buf != NULL && len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + flash_block_t *block; + hal_error_t err; + unsigned b; + + if ((err = fetch_pin_block(&b, &block)) != HAL_OK) + return err; + + if (block->pin.kek_set != FLASH_KEK_SET) + return HAL_ERROR_MASTERKEY_NOT_SET; + + if (buf != NULL) + memcpy(buf, block->pin.kek, len); + + return HAL_OK; +} + +hal_error_t hal_mkm_flash_write(const uint8_t * const buf, const size_t len) +{ + if (buf == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + flash_block_t *block; + hal_error_t err; + unsigned b; + + if ((err = fetch_pin_block(&b, &block)) != HAL_OK) + return err; + + flash_pin_block_t new_data = block->pin; + + new_data.kek_set = FLASH_KEK_SET; + memcpy(new_data.kek, buf, len); + + return update_pin_block(b, block, &new_data); +} + +hal_error_t hal_mkm_flash_erase(const size_t len) +{ + if (len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + flash_block_t *block; + hal_error_t err; + unsigned b; + + if ((err = fetch_pin_block(&b, &block)) != HAL_OK) + return err; + + flash_pin_block_t new_data = block->pin; + + new_data.kek_set = FLASH_KEK_SET; + memset(new_data.kek, 0, len); + + return update_pin_block(b, block, &new_data); +} + +#endif /* HAL_MKM_FLASH_BACKUP_KLUDGE */ /* diff --git a/ks_index.c b/ks_index.c new file mode 100644 index 0000000..0c12fcc --- /dev/null +++ b/ks_index.c @@ -0,0 +1,457 @@ +/* + * ks_index.c + * ---------- + * Keystore index API. This is internal within libhal. + * + * 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. + */ + +#include <string.h> +#include <assert.h> + +#include "hal.h" +#include "hal_internal.h" + +/* + * Compare two hal_ks_name_t objects. + */ + +static inline int ks_name_cmp(const hal_ks_name_t * const name1, const hal_ks_name_t * const name2) +{ + assert(name1 != NULL && name2 != NULL); + + int cmp = hal_uuid_cmp(&name1->name, &name2->name); + + if (cmp == 0) + cmp = ((int) name1->chunk) - ((int) name2->chunk); + + return cmp; +} + +/* + * Return value indicates whether the name is present in the index. + * "where" indicates the name's position whether present or not. + * + * NB: This does NOT return a block number, it returns an index into + * ksi->index[]. + */ + +static int ks_find(const hal_ks_index_t * const ksi, + const hal_uuid_t * const uuid, + const uint8_t chunk, + const int * const hint, + int *where) +{ + assert(ksi != NULL && ksi->index != NULL && ksi->names != NULL && uuid != NULL && where != NULL); + + const hal_ks_name_t name = { *uuid, chunk }; + + if (hint != NULL && *hint >= 0 && *hint < ksi->used && + ks_name_cmp(&name, &ksi->names[ksi->index[*hint]]) == 0) { + *where = *hint; + return 1; + } + + int lo = -1; + int hi = ksi->used; + + for (;;) { + int m = (lo + hi) / 2; + if (hi == 0 || m == lo) { + *where = hi; + return 0; + } + const int cmp = ks_name_cmp(&name, &ksi->names[ksi->index[m]]); + if (cmp < 0) + hi = m; + else if (cmp > 0) + lo = m; + else { + *where = m; + return 1; + } + } +} + +/* + * Heapsort the index. We only need to do this on setup, for other + * operations we're just inserting or deleting a single entry in an + * already-ordered array, which is just a search problem. If we were + * really crunched for space, we could use an insertion sort here, but + * heapsort is easy and works well with data already in place. + */ + +static inline void ks_heapsift(hal_ks_index_t *ksi, int parent, const int end) +{ + assert(ksi != NULL && ksi->index != NULL && ksi->names != NULL && + parent >= 0 && end >= parent); + for (;;) { + const int left_child = parent * 2 + 1; + const int right_child = parent * 2 + 2; + int biggest = parent; + if (left_child <= end && ks_name_cmp(&ksi->names[ksi->index[biggest]], + &ksi->names[ksi->index[left_child]]) < 0) + biggest = left_child; + if (right_child <= end && ks_name_cmp(&ksi->names[ksi->index[biggest]], + &ksi->names[ksi->index[right_child]]) < 0) + biggest = right_child; + if (biggest == parent) + return; + const uint16_t tmp = ksi->index[biggest]; + ksi->index[biggest] = ksi->index[parent]; + ksi->index[parent] = tmp; + parent = biggest; + } +} + +static inline void ks_heapsort(hal_ks_index_t *ksi) +{ + assert(ksi != NULL && ksi->index != NULL && ksi->names != NULL); + if (ksi->used < 2) + return; + for (int i = (ksi->used - 2) / 2; i >= 0; i--) + ks_heapsift(ksi, i, ksi->used - 1); + for (int i = ksi->used - 1; i > 0; i--) { + const uint16_t tmp = ksi->index[i]; + ksi->index[i] = ksi->index[0]; + ksi->index[0] = tmp; + ks_heapsift(ksi, 0, i - 1); + } +} + +#define fsck(_ksi) \ + do { hal_error_t _err = hal_ks_index_fsck(_ksi); if (_err != HAL_OK) return _err; } while (0) + + +hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi) +{ + if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || + ksi->size == 0 || ksi->used > ksi->size) + return HAL_ERROR_BAD_ARGUMENTS; + + for (int i = 0; i < ksi->used; i++) { + + const int cmp = i == 0 ? -1 : hal_uuid_cmp(&ksi->names[ksi->index[i - 1]].name, + &ksi->names[ksi->index[i ]].name); + + const uint8_t prev_chunk = i == 0 ? 0 : ksi->names[ksi->index[i - 1]].chunk; + const uint8_t cur_chunk = ksi->names[ksi->index[i ]].chunk; + + if (cmp > 0) + return HAL_ERROR_KSI_INDEX_UUID_MISORDERED; + + if (cur_chunk > 0 && cmp != 0) + return HAL_ERROR_KSI_INDEX_CHUNK_ORPHANED; + + if (cur_chunk > 0 && prev_chunk + 1 < cur_chunk) + return HAL_ERROR_KSI_INDEX_CHUNK_MISSING; + + if (cur_chunk > 0 && prev_chunk + 1 > cur_chunk) + return HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS; + } + + return HAL_OK; +} + +hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi) +{ + if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || + ksi->size == 0 || ksi->used > ksi->size) + return HAL_ERROR_BAD_ARGUMENTS; + + /* + * Only setup task we have at the moment is sorting the index. + */ + + ks_heapsort(ksi); + + /* + * One might think we should fsck here, but errors in the index + * at this point probably relate to errors in the supplied data, + * which only the driver knows how to clean up. + */ + + return HAL_OK; +} + +hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned chunk, + unsigned *blockno, + int *hint) +{ + if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || + ksi->size == 0 || ksi->used > ksi->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + int where; + + fsck(ksi); + + int ok = ks_find(ksi, name, chunk, hint, &where); + + if (blockno != NULL) + *blockno = ksi->index[where]; + + if (hint != NULL) + *hint = where; + + return ok ? HAL_OK : HAL_ERROR_KEY_NOT_FOUND; +} + +hal_error_t hal_ks_index_find_range(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned max_blocks, + unsigned *n_blocks, + unsigned *blocknos, + int *hint, + const int strict) +{ + if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || + ksi->size == 0 || ksi->used > ksi->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + int where; + + fsck(ksi); + + if (!ks_find(ksi, name, 0, hint, &where)) + return HAL_ERROR_KEY_NOT_FOUND; + + int n = 0; + + for (int i = where; i < ksi->used && !hal_uuid_cmp(name, &ksi->names[ksi->index[i]].name); i++) { + if (strict && n != ksi->names[ksi->index[i]].chunk) + return HAL_ERROR_IMPOSSIBLE; + if (blocknos != NULL && n < max_blocks) + blocknos[n] = ksi->index[i]; + n++; + } + + if (n_blocks != NULL) + *n_blocks = n; + + if (hint != NULL) + *hint = where; + + if (blocknos != NULL && n > max_blocks) + return HAL_ERROR_RESULT_TOO_LONG; + + return HAL_OK; +} + +hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned chunk, + unsigned *blockno, + int *hint) +{ + if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || + ksi->size == 0 || ksi->used > ksi->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ksi->used == ksi->size) + return HAL_ERROR_NO_KEY_INDEX_SLOTS; + + int where; + + fsck(ksi); + + if (ks_find(ksi, name, chunk, hint, &where)) + return HAL_ERROR_KEY_NAME_IN_USE; + + /* + * Grab first block on free list, which makes room to slide the + * index up by one slot so we can insert the new block number. + */ + + const size_t len = (ksi->used - where) * sizeof(*ksi->index); + const uint16_t b = ksi->index[ksi->used++]; + memmove(&ksi->index[where + 1], &ksi->index[where], len); + ksi->index[where] = b; + ksi->names[b].name = *name; + ksi->names[b].chunk = chunk; + + if (blockno != NULL) + *blockno = b; + + if (hint != NULL) + *hint = where; + + fsck(ksi); + + return HAL_OK; +} + +hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned chunk, + unsigned *blockno, + int *hint) +{ + if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || + ksi->size == 0 || ksi->used > ksi->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + int where; + + fsck(ksi); + + if (ksi->used == 0 || !ks_find(ksi, name, chunk, hint, &where)) + return HAL_ERROR_KEY_NOT_FOUND; + + /* + * Free the block and stuff it at the end of the free list. + */ + + const size_t len = (ksi->size - where - 1) * sizeof(*ksi->index); + const uint16_t b = ksi->index[where]; + memmove(&ksi->index[where], &ksi->index[where + 1], len); + ksi->index[ksi->size - 1] = b; + ksi->used--; + memset(&ksi->names[b], 0, sizeof(ksi->names[b])); + + if (blockno != NULL) + *blockno = b; + + if (hint != NULL) + *hint = where; + + fsck(ksi); + + return HAL_OK; +} + +hal_error_t hal_ks_index_delete_range(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned max_blocks, + unsigned *n_blocks, + unsigned *blocknos, + int *hint) +{ + if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || + ksi->size == 0 || ksi->used > ksi->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + int where; + + fsck(ksi); + + if (ksi->used == 0 || !ks_find(ksi, name, 0, hint, &where)) + return HAL_ERROR_KEY_NOT_FOUND; + + int n = 0; + + for (int i = where; i < ksi->used && !hal_uuid_cmp(name, &ksi->names[ksi->index[i]].name); i++) { + if (n != ksi->names[ksi->index[i]].chunk) + return HAL_ERROR_IMPOSSIBLE; + if (blocknos != NULL && n < max_blocks) + blocknos[n] = ksi->index[i]; + n++; + } + + if (n_blocks != NULL) + *n_blocks = n; + + /* + * Free the blocks and stuff them at the end of the free list. + */ + + if (blocknos != NULL) { + if (n > max_blocks) + return HAL_ERROR_RESULT_TOO_LONG; + const size_t len = (ksi->size - where - n) * sizeof(*ksi->index); + memmove(&ksi->index[where], &ksi->index[where + n], len); + ksi->used -= n; + for (int i = 0; i < n; i++) { + ksi->index[ksi->size - n + i] = blocknos[i]; + memset(&ksi->names[blocknos[i]], 0, sizeof(ksi->names[blocknos[i]])); + } + where = -1; + } + + if (hint != NULL) + *hint = where; + + fsck(ksi); + + return HAL_OK; +} + +hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, + const hal_uuid_t * const name, + const unsigned chunk, + unsigned *blockno, + int *hint) +{ + if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || + ksi->size == 0 || ksi->used > ksi->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ksi->used == ksi->size) + return HAL_ERROR_NO_KEY_INDEX_SLOTS; + + int where; + + fsck(ksi); + + if (ksi->used == 0 || !ks_find(ksi, name, chunk, hint, &where)) + return HAL_ERROR_KEY_NOT_FOUND; + + /* + * Grab first block from free list, slide free list down, put old + * block at end of free list and replace old block with new block. + */ + + const size_t len = (ksi->size - ksi->used - 1) * sizeof(*ksi->index); + const uint16_t b1 = ksi->index[where]; + const uint16_t b2 = ksi->index[ksi->used]; + memmove(&ksi->index[ksi->used], &ksi->index[ksi->used + 1], len); + ksi->index[ksi->size - 1] = b1; + ksi->index[where] = b2; + ksi->names[b2].name = *name; + ksi->names[b2].chunk = chunk; + memset(&ksi->names[b1], 0, sizeof(ksi->names[b1])); + + if (blockno != NULL) + *blockno = b2; + + if (hint != NULL) + *hint = where; + + fsck(ksi); + + return HAL_OK; +} + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ @@ -125,8 +125,8 @@ hal_error_t hal_ks_del_keydb(const int loc) return HAL_OK; } -hal_error_t hal_ks_set_pin(const hal_user_t user, - const hal_ks_pin_t * const pin) +hal_error_t hal_set_pin(const hal_user_t user, + const hal_ks_pin_t * const pin) { if (pin == NULL) return HAL_ERROR_BAD_ARGUMENTS; @@ -144,7 +144,7 @@ hal_error_t hal_ks_set_pin(const hal_user_t user, return HAL_OK; } -hal_error_t hal_ks_get_kek(uint8_t *kek, +hal_error_t hal_mkm_get_kek(uint8_t *kek, size_t *kek_len, const size_t kek_max) { diff --git a/ks_volatile.c b/ks_volatile.c index 00f656a..99ad68c 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -7,7 +7,7 @@ * to survive library exit, eg, for storing PKCS #11 session keys. * * Authors: Rob Austein - * Copyright (c) 2015, NORDUnet A/S All rights reserved. + * 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 @@ -37,106 +37,530 @@ */ #include <string.h> +#include <assert.h> #include "hal.h" #include "hal_internal.h" +#define KEK_LENGTH (bitsToBytes(256)) + +#ifndef STATIC_KS_VOLATILE_SLOTS +#define STATIC_KS_VOLATILE_SLOTS HAL_STATIC_PKEY_STATE_BLOCKS +#endif + +#ifndef STATIC_KS_VOLATILE_ATTRIBUTE_SPACE +#define STATIC_KS_VOLATILE_ATTRIBUTE_SPACE 4096 +#endif + /* - * Splitting the different keystore backends out into separate files - * seemed like a good idea at the time, but the code is getting - * somewhat repetitive. Might want to re-merge and conditionalize in - * some other way. Deferred until we sort out ks_flash.c. + * In-memory keystore database. This should also be usable for + * mmap(), if and when we get around to rewriting that driver (and in + * which case this driver probably ought to be renamed ks_memory). */ +typedef struct { + hal_key_type_t type; + hal_curve_name_t curve; + hal_key_flags_t flags; + hal_client_handle_t client; + hal_session_handle_t session; + size_t der_len; + unsigned attributes_len; + uint8_t der[HAL_KS_WRAPPED_KEYSIZE + STATIC_KS_VOLATILE_ATTRIBUTE_SPACE]; +} ks_key_t; + +typedef struct { + hal_ks_index_t ksi; + ks_key_t *keys; +} db_t; + +/* + * "Subclass" (well, what one can do in C) of hal_ks_t. This is + * separate from db_t primarily to simplify things like rewriting the + * old ks_mmap driver to piggy-back on the ks_volatile driver: we + * wouldn't want the hal_ks_t into the mmap()ed file. + */ + +typedef struct { + hal_ks_t ks; /* Must be first */ + db_t *db; /* Which memory-based keystore database */ + int per_session; /* Whether objects are per-session */ +} ks_t; + /* - * 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. + * If we also supported mmap, there would be a separate definition for + * HAL_KS_MMAP_SLOTS above, and the bulk of the code would be under a + * conditional testing whether either HAL_KS_*_SLOTS were nonzero. */ -static hal_ks_keydb_t db[1]; +#if STATIC_KS_VOLATILE_SLOTS > 0 + +static ks_t volatile_ks; + +static inline ks_t *ks_to_ksv(hal_ks_t *ks) +{ + return (ks_t *) ks; +} /* - * There's no good place to store the master key (KEK) in this volatile memory implementation. - * We might be able to add a bit of protection doing things like using locked physical memory, - * as gpg does, or obfuscating the KEK a bit to make it harder to pull out of a crash dump, - * but, really, there's not a lot we can do against a determined opponant in this case. + * Check whether the current session can see a particular key. One + * might expect this to be based on whether the session matches, and + * indeed it would be in a sane world, but in the world of PKCS #11, + * keys belong to sessions, are visible to other sessions, and may + * even be modifiable by other sessions, but softly and silently + * vanish away when the original creating session is destroyed. * - * For now, we just go through the motions. + * In our terms, this means that visibility of session objects is + * determined only by the client handle, so taking the session handle + * as an argument here isn't really necessary, but we've flipflopped + * on that enough times that at least for now I'd prefer to leave the + * session handle here and not have to revise all the RPC calls again. + * Remove it at some later date and redo the RPC calls if we manage to + * avoid revising this yet again. */ -static uint8_t kekbuf[bitsToBytes(256)]; +static inline int key_visible_to_session(const ks_t * const ksv, + const hal_client_handle_t client, + const hal_session_handle_t session, + const ks_key_t * const k) +{ + return !ksv->per_session || client.handle == HAL_HANDLE_NONE || k->client.handle == client.handle; +} + +static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size) +{ + if (mem == NULL || *mem == NULL || len == NULL || size > *len) + return NULL; + void *ret = *mem; + *mem += size; + *len -= size; + return ret; +} + +static hal_error_t ks_init(const hal_ks_driver_t * const driver, + const int per_session, + ks_t *ksv, + uint8_t *mem, + size_t len) +{ + if (ksv == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if (mem != NULL) { + memset(ksv, 0, sizeof(*ksv)); + memset(mem, 0, len); + + ksv->db = gnaw(&mem, &len, sizeof(*ksv->db)); + ksv->db->ksi.index = gnaw(&mem, &len, sizeof(*ksv->db->ksi.index) * STATIC_KS_VOLATILE_SLOTS); + ksv->db->ksi.names = gnaw(&mem, &len, sizeof(*ksv->db->ksi.names) * STATIC_KS_VOLATILE_SLOTS); + ksv->db->keys = gnaw(&mem, &len, sizeof(*ksv->db->keys) * STATIC_KS_VOLATILE_SLOTS); + ksv->db->ksi.size = STATIC_KS_VOLATILE_SLOTS; + } + + if (ksv->db == NULL || + ksv->db->ksi.index == NULL || + ksv->db->ksi.names == NULL || + ksv->db->keys == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if (mem == NULL) { + memset(ksv->db->ksi.index, 0, sizeof(*ksv->db->ksi.index) * STATIC_KS_VOLATILE_SLOTS); + memset(ksv->db->ksi.names, 0, sizeof(*ksv->db->ksi.names) * STATIC_KS_VOLATILE_SLOTS); + memset(ksv->db->keys, 0, sizeof(*ksv->db->keys) * STATIC_KS_VOLATILE_SLOTS); + } + + ksv->ks.driver = driver; + ksv->per_session = per_session; + ksv->db->ksi.used = 0; -const hal_ks_keydb_t *hal_ks_get_keydb(void) + /* + * Set up keystore with empty index and full free list. + * Since this driver doesn't care about wear leveling, + * just populate the free list in block numerical order. + */ + + for (int i = 0; i < STATIC_KS_VOLATILE_SLOTS; i++) + ksv->db->ksi.index[i] = i; + + return hal_ks_index_setup(&ksv->db->ksi); +} + +static hal_error_t ks_volatile_init(const hal_ks_driver_t * const driver, const int alloc) { - return db; + const size_t len = (sizeof(*volatile_ks.db) + + sizeof(*volatile_ks.db->ksi.index) * STATIC_KS_VOLATILE_SLOTS + + sizeof(*volatile_ks.db->ksi.names) * STATIC_KS_VOLATILE_SLOTS + + sizeof(*volatile_ks.db->keys) * STATIC_KS_VOLATILE_SLOTS); + + uint8_t *mem = NULL; + + if (alloc && (mem = hal_allocate_static_memory(len)) == NULL) + return HAL_ERROR_ALLOCATION_FAILURE; + + return ks_init(driver, 1, &volatile_ks, mem, len); +} + +static hal_error_t ks_volatile_shutdown(const hal_ks_driver_t * const driver) +{ + if (volatile_ks.ks.driver != driver) + return HAL_ERROR_KEYSTORE_ACCESS; + return HAL_OK; +} + +static hal_error_t ks_volatile_open(const hal_ks_driver_t * const driver, + hal_ks_t **ks) +{ + assert(driver != NULL && ks != NULL); + *ks = &volatile_ks.ks; + return HAL_OK; +} + +static hal_error_t ks_volatile_close(hal_ks_t *ks) +{ + return HAL_OK; +} + +static inline int acceptable_key_type(const hal_key_type_t type) +{ + switch (type) { + case HAL_KEY_TYPE_RSA_PRIVATE: + case HAL_KEY_TYPE_EC_PRIVATE: + case HAL_KEY_TYPE_RSA_PUBLIC: + case HAL_KEY_TYPE_EC_PUBLIC: + return 1; + default: + return 0; + } +} + +static hal_error_t ks_store(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const uint8_t * const der, const size_t der_len) +{ + if (ks == NULL || slot == NULL || der == NULL || der_len == 0 || !acceptable_key_type(slot->type)) + return HAL_ERROR_BAD_ARGUMENTS; + + ks_t *ksv = ks_to_ksv(ks); + hal_error_t err; + unsigned b; + + if (ksv->db == NULL) + return HAL_ERROR_KEYSTORE_ACCESS; + + if ((err = hal_ks_index_add(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + return err; + + uint8_t kek[KEK_LENGTH]; + size_t kek_len; + ks_key_t k; + + memset(&k, 0, sizeof(k)); + k.der_len = sizeof(k.der); + k.type = slot->type; + k.curve = slot->curve; + k.flags = slot->flags; + k.client = slot->client_handle; + k.session = slot->session_handle; + + if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK) + err = hal_aes_keywrap(NULL, kek, kek_len, der, der_len, k.der, &k.der_len); + + memset(kek, 0, sizeof(kek)); + + if (err == HAL_OK) + ksv->db->keys[b] = k; + else + (void) hal_ks_index_delete(&ksv->db->ksi, &slot->name, 0, NULL, &slot->hint); + + return err; +} + +static hal_error_t ks_fetch(hal_ks_t *ks, + hal_pkey_slot_t *slot, + uint8_t *der, size_t *der_len, const size_t der_max) +{ + if (ks == NULL || slot == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + ks_t *ksv = ks_to_ksv(ks); + hal_error_t err; + unsigned b; + + if (ksv->db == NULL) + return HAL_ERROR_KEYSTORE_ACCESS; + + if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + return err; + + const ks_key_t * const k = &ksv->db->keys[b]; + + if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k)) + return HAL_ERROR_KEY_NOT_FOUND; + + slot->type = k->type; + slot->curve = k->curve; + slot->flags = k->flags; + + if (der == NULL && der_len != NULL) + *der_len = k->der_len; + + if (der != NULL) { + + uint8_t kek[KEK_LENGTH]; + size_t kek_len, der_len_; + hal_error_t err; + + if (der_len == NULL) + der_len = &der_len_; + + *der_len = der_max; + + if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK) + err = hal_aes_keyunwrap(NULL, kek, kek_len, k->der, k->der_len, der, der_len); + + memset(kek, 0, sizeof(kek)); + + if (err != HAL_OK) + return err; + } + + return HAL_OK; } -hal_error_t hal_ks_set_keydb(const hal_ks_key_t * const key, - const int loc, - const int updating) +static hal_error_t ks_delete(hal_ks_t *ks, + hal_pkey_slot_t *slot) { - if (key == NULL || loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys) || (!key->in_use != !updating)) + if (ks == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; - db->keys[loc] = *key; - db->keys[loc].in_use = 1; + ks_t *ksv = ks_to_ksv(ks); + hal_error_t err; + unsigned b; + + if (ksv->db == NULL) + return HAL_ERROR_KEYSTORE_ACCESS; + + if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + return err; + + if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, &ksv->db->keys[b])) + return HAL_ERROR_KEY_NOT_FOUND; + + if ((err = hal_ks_index_delete(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + return err; + + memset(&ksv->db->keys[b], 0, sizeof(ksv->db->keys[b])); + return HAL_OK; } -hal_error_t hal_ks_del_keydb(const int loc) +static hal_error_t ks_match(hal_ks_t *ks, + hal_client_handle_t client, + hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, + unsigned *result_len, + const unsigned result_max, + const hal_uuid_t * const previous_uuid) { - if (loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys)) + if (ks == NULL || (attributes == NULL && attributes_len > 0) || + result == NULL || result_len == NULL || previous_uuid == NULL) return HAL_ERROR_BAD_ARGUMENTS; - memset(&db->keys[loc], 0, sizeof(db->keys[loc])); + ks_t *ksv = ks_to_ksv(ks); + + if (ksv->db == NULL) + return HAL_ERROR_KEYSTORE_ACCESS; + + hal_error_t err; + int i = -1; + + *result_len = 0; + + err = hal_ks_index_find(&ksv->db->ksi, previous_uuid, 0, NULL, &i); + + if (err == HAL_ERROR_KEY_NOT_FOUND) + i--; + else if (err != HAL_OK) + return err; + + while (*result_len < result_max && ++i < ksv->db->ksi.used) { + + unsigned b = ksv->db->ksi.index[i]; + + if (ksv->db->ksi.names[b].chunk > 0) + continue; + + if (type != HAL_KEY_TYPE_NONE && type != ksv->db->keys[b].type) + continue; + + if (curve != HAL_CURVE_NONE && curve != ksv->db->keys[b].curve) + continue; + + if (!key_visible_to_session(ksv, client, session, &ksv->db->keys[b])) + continue; + + if (attributes_len > 0) { + const ks_key_t * const k = &ksv->db->keys[b]; + int ok = 1; + + if (k->attributes_len == 0) + continue; + + hal_pkey_attribute_t key_attrs[k->attributes_len]; + + if ((err = hal_ks_attribute_scan(k->der + k->der_len, sizeof(k->der) - k->der_len, + key_attrs, k->attributes_len, NULL)) != HAL_OK) + return err; + + for (const hal_pkey_attribute_t *required = attributes; + ok && required < attributes + attributes_len; required++) { + + hal_pkey_attribute_t *present = key_attrs; + while (ok && present->type != required->type) + ok = ++present < key_attrs + k->attributes_len; + + if (ok) + ok = (present->length == required->length && + !memcmp(present->value, required->value, present->length)); + } + + if (!ok) + continue; + } + + result[*result_len] = ksv->db->ksi.names[b].name; + ++*result_len; + } + return HAL_OK; } -hal_error_t hal_ks_set_pin(const hal_user_t user, - const hal_ks_pin_t * const pin) +static hal_error_t ks_set_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len) { - if (pin == NULL) + if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0) return HAL_ERROR_BAD_ARGUMENTS; - hal_ks_pin_t *p = NULL; + ks_t *ksv = ks_to_ksv(ks); + hal_error_t err; + unsigned b; + + if (ksv->db == NULL) + return HAL_ERROR_KEYSTORE_ACCESS; + + if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + return err; - switch (user) { - case HAL_USER_WHEEL: p = &db->wheel_pin; break; - case HAL_USER_SO: p = &db->so_pin; break; - case HAL_USER_NORMAL: p = &db->user_pin; break; - default: return HAL_ERROR_BAD_ARGUMENTS; + ks_key_t * const k = &ksv->db->keys[b]; + + if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k)) + return HAL_ERROR_KEY_NOT_FOUND; + + hal_pkey_attribute_t attrs[k->attributes_len + attributes_len]; + uint8_t *bytes = k->der + k->der_len; + size_t bytes_len = sizeof(k->der) - k->der_len; + size_t total_len; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, k->attributes_len, &total_len)) != HAL_OK) + return err; + + for (const hal_pkey_attribute_t *a = attributes; a < attributes + attributes_len; a++) { + if (a->length == HAL_PKEY_ATTRIBUTE_NIL) + err = hal_ks_attribute_delete(bytes, bytes_len, attrs, &k->attributes_len, &total_len, + a->type); + else + err = hal_ks_attribute_insert(bytes, bytes_len, attrs, &k->attributes_len, &total_len, + a->type, a->value, a->length); + if (err != HAL_OK) + return err; } - *p = *pin; return HAL_OK; } -hal_error_t hal_ks_get_kek(uint8_t *kek, - size_t *kek_len, - const size_t kek_max) +static hal_error_t ks_get_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len) { - if (kek == NULL || kek_len == NULL || kek_max < bitsToBytes(128)) + if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0 || + attributes_buffer == NULL) return HAL_ERROR_BAD_ARGUMENTS; + ks_t *ksv = ks_to_ksv(ks); hal_error_t err; + unsigned b; - const size_t len = ((kek_max < bitsToBytes(192)) ? bitsToBytes(128) : - (kek_max < bitsToBytes(256)) ? bitsToBytes(192) : - bitsToBytes(256)); + if (ksv->db == NULL) + return HAL_ERROR_KEYSTORE_ACCESS; - uint8_t t = 0; + if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + return err; + + const ks_key_t * const k = &ksv->db->keys[b]; - for (int i = 0; i < sizeof(kekbuf); i++) - t |= kekbuf[i]; + if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k)) + return HAL_ERROR_KEY_NOT_FOUND; - if (t == 0 && (err = hal_rpc_get_random(kekbuf, sizeof(kekbuf))) != HAL_OK) + hal_pkey_attribute_t attrs[k->attributes_len > 0 ? k->attributes_len : 1]; + + if ((err = hal_ks_attribute_scan(k->der + k->der_len, sizeof(k->der) - k->der_len, + attrs, k->attributes_len, NULL)) != HAL_OK) return err; - memcpy(kek, kekbuf, len); - *kek_len = len; + uint8_t *abuf = attributes_buffer; + + for (int i = 0; i < attributes_len; i++) { + int j = 0; + while (j < k->attributes_len && attrs[j].type != attributes[i].type) + j++; + const int found = j < k->attributes_len; + + if (attributes_buffer_len == 0) { + attributes[i].value = NULL; + attributes[i].length = found ? attrs[j].length : 0; + continue; + } + + if (!found) + return HAL_ERROR_ATTRIBUTE_NOT_FOUND; + + if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf) + return HAL_ERROR_RESULT_TOO_LONG; + + memcpy(abuf, attrs[j].value, attrs[j].length); + attributes[i].value = abuf; + attributes[i].length = attrs[j].length; + abuf += attrs[j].length; + } + return HAL_OK; } +const hal_ks_driver_t hal_ks_volatile_driver[1] = {{ + .init = ks_volatile_init, + .shutdown = ks_volatile_shutdown, + .open = ks_volatile_open, + .close = ks_volatile_close, + .store = ks_store, + .fetch = ks_fetch, + .delete = ks_delete, + .match = ks_match, + .set_attributes = ks_set_attributes, + .get_attributes = ks_get_attributes +}}; + +#endif /* STATIC_KS_VOLATILE_SLOTS > 0 */ + /* * Local variables: * indent-tabs-mode: nil diff --git a/libhal.py b/libhal.py new file mode 100644 index 0000000..369e5e1 --- /dev/null +++ b/libhal.py @@ -0,0 +1,662 @@ +# 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. + +""" +A Python interface to the Cryptech libhal RPC API. +""" + +# A lot of this is hand-generated XDR data structure encoding. If and +# when we ever convert the C library to use data structures processed +# by rpcgen, we may want to rewrite this code to use the output of +# something like https://github.com/floodlight/xdr.git -- in either +# case the generated code would just be for the data structures, we're +# not likely to want to use the full ONC RPC mechanism. + +import os +import sys +import time +import uuid +import xdrlib +import serial +import contextlib + +SLIP_END = chr(0300) # indicates end of packet +SLIP_ESC = chr(0333) # indicates byte stuffing +SLIP_ESC_END = chr(0334) # ESC ESC_END means END data byte +SLIP_ESC_ESC = chr(0335) # ESC ESC_ESC means ESC data byte + +HAL_OK = 0 + +class HALError(Exception): + "LibHAL error" + + table = [None] + + @classmethod + def define(cls, **kw): + assert len(kw) == 1 + name, text = kw.items()[0] + e = type(name, (cls,), dict(__doc__ = text)) + cls.table.append(e) + globals()[name] = e + +HALError.define(HAL_ERROR_BAD_ARGUMENTS = "Bad arguments given") +HALError.define(HAL_ERROR_UNSUPPORTED_KEY = "Unsupported key type or key length") +HALError.define(HAL_ERROR_IO_SETUP_FAILED = "Could not set up I/O with FPGA") +HALError.define(HAL_ERROR_IO_TIMEOUT = "I/O with FPGA timed out") +HALError.define(HAL_ERROR_IO_UNEXPECTED = "Unexpected response from FPGA") +HALError.define(HAL_ERROR_IO_OS_ERROR = "Operating system error talking to FPGA") +HALError.define(HAL_ERROR_IO_BAD_COUNT = "Bad byte count") +HALError.define(HAL_ERROR_CSPRNG_BROKEN = "CSPRNG is returning nonsense") +HALError.define(HAL_ERROR_KEYWRAP_BAD_MAGIC = "Bad magic number while unwrapping key") +HALError.define(HAL_ERROR_KEYWRAP_BAD_LENGTH = "Length out of range while unwrapping key") +HALError.define(HAL_ERROR_KEYWRAP_BAD_PADDING = "Non-zero padding detected unwrapping key") +HALError.define(HAL_ERROR_IMPOSSIBLE = "\"Impossible\" error") +HALError.define(HAL_ERROR_ALLOCATION_FAILURE = "Memory allocation failed") +HALError.define(HAL_ERROR_RESULT_TOO_LONG = "Result too long for buffer") +HALError.define(HAL_ERROR_ASN1_PARSE_FAILED = "ASN.1 parse failed") +HALError.define(HAL_ERROR_KEY_NOT_ON_CURVE = "EC key is not on its purported curve") +HALError.define(HAL_ERROR_INVALID_SIGNATURE = "Invalid signature") +HALError.define(HAL_ERROR_CORE_NOT_FOUND = "Requested core not found") +HALError.define(HAL_ERROR_CORE_BUSY = "Requested core busy") +HALError.define(HAL_ERROR_KEYSTORE_ACCESS = "Could not access keystore") +HALError.define(HAL_ERROR_KEY_NOT_FOUND = "Key not found") +HALError.define(HAL_ERROR_KEY_NAME_IN_USE = "Key name in use") +HALError.define(HAL_ERROR_NO_KEY_SLOTS_AVAILABLE = "No key slots available") +HALError.define(HAL_ERROR_PIN_INCORRECT = "PIN incorrect") +HALError.define(HAL_ERROR_NO_CLIENT_SLOTS_AVAILABLE = "No client slots available") +HALError.define(HAL_ERROR_FORBIDDEN = "Forbidden") +HALError.define(HAL_ERROR_XDR_BUFFER_OVERFLOW = "XDR buffer overflow") +HALError.define(HAL_ERROR_RPC_TRANSPORT = "RPC transport error") +HALError.define(HAL_ERROR_RPC_PACKET_OVERFLOW = "RPC packet overflow") +HALError.define(HAL_ERROR_RPC_BAD_FUNCTION = "Bad RPC function number") +HALError.define(HAL_ERROR_KEY_NAME_TOO_LONG = "Key name too long") +HALError.define(HAL_ERROR_MASTERKEY_NOT_SET = "Master key (Key Encryption Key) not set") +HALError.define(HAL_ERROR_MASTERKEY_FAIL = "Master key generic failure") +HALError.define(HAL_ERROR_MASTERKEY_BAD_LENGTH = "Master key of unacceptable length") +HALError.define(HAL_ERROR_KS_DRIVER_NOT_FOUND = "Keystore driver not found") +HALError.define(HAL_ERROR_KEYSTORE_BAD_CRC = "Bad CRC in keystore") +HALError.define(HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE = "Unsupported keystore block type") +HALError.define(HAL_ERROR_KEYSTORE_LOST_DATA = "Keystore appears to have lost data") +HALError.define(HAL_ERROR_BAD_ATTRIBUTE_LENGTH = "Bad attribute length") +HALError.define(HAL_ERROR_ATTRIBUTE_NOT_FOUND = "Attribute not found") +HALError.define(HAL_ERROR_NO_KEY_INDEX_SLOTS = "No key index slots available") +HALError.define(HAL_ERROR_KSI_INDEX_UUID_MISORDERED = "Key index UUID misordered") +HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_ORPHANED = "Key index chunk orphaned") +HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_MISSING = "Key index chunk missing") +HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS = "Key index chunk overlaps") +HALError.define(HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE = "Wrong block type in keystore") +HALError.define(HAL_ERROR_RPC_PROTOCOL_ERROR = "RPC protocol error") +HALError.define(HAL_ERROR_NOT_IMPLEMENTED = "Not implemented") + + +class Enum(int): + + def __new__(cls, name, value): + self = int.__new__(cls, value) + self._name = name + setattr(self.__class__, name, self) + return self + + def __str__(self): + return self._name + + def __repr__(self): + return "<Enum:{0.__class__.__name__} {0._name}:{0:d}>".format(self) + + _counter = 0 + + @classmethod + def define(cls, names): + symbols = [] + for name in names.translate(None, "{}").split(","): + if "=" in name: + name, sep, expr = name.partition("=") + cls._counter = eval(expr.strip()) + if not isinstance(cls._counter, int): + raise TypeError + symbols.append(cls(name.strip(), cls._counter)) + cls._counter += 1 + cls.index = dict((int(symbol), symbol) for symbol in symbols) + globals().update((symbol._name, symbol) for symbol in symbols) + + def xdr_packer(self, packer): + packer.pack_uint(self) + + +class RPCFunc(Enum): pass + +RPCFunc.define(''' + RPC_FUNC_GET_VERSION, + RPC_FUNC_GET_RANDOM, + RPC_FUNC_SET_PIN, + RPC_FUNC_LOGIN, + RPC_FUNC_LOGOUT, + RPC_FUNC_LOGOUT_ALL, + RPC_FUNC_IS_LOGGED_IN, + RPC_FUNC_HASH_GET_DIGEST_LEN, + RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID, + RPC_FUNC_HASH_GET_ALGORITHM, + RPC_FUNC_HASH_INITIALIZE, + RPC_FUNC_HASH_UPDATE, + RPC_FUNC_HASH_FINALIZE, + RPC_FUNC_PKEY_LOAD, + RPC_FUNC_PKEY_OPEN, + RPC_FUNC_PKEY_GENERATE_RSA, + RPC_FUNC_PKEY_GENERATE_EC, + RPC_FUNC_PKEY_CLOSE, + RPC_FUNC_PKEY_DELETE, + RPC_FUNC_PKEY_GET_KEY_TYPE, + RPC_FUNC_PKEY_GET_KEY_FLAGS, + RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN, + RPC_FUNC_PKEY_GET_PUBLIC_KEY, + RPC_FUNC_PKEY_SIGN, + RPC_FUNC_PKEY_VERIFY, + RPC_FUNC_PKEY_MATCH, + RPC_FUNC_PKEY_GET_KEY_CURVE, + RPC_FUNC_PKEY_SET_ATTRIBUTES, + RPC_FUNC_PKEY_GET_ATTRIBUTES, +''') + +class HALDigestAlgorithm(Enum): pass + +HALDigestAlgorithm.define(''' + HAL_DIGEST_ALGORITHM_NONE, + HAL_DIGEST_ALGORITHM_SHA1, + HAL_DIGEST_ALGORITHM_SHA224, + HAL_DIGEST_ALGORITHM_SHA256, + HAL_DIGEST_ALGORITHM_SHA512_224, + HAL_DIGEST_ALGORITHM_SHA512_256, + HAL_DIGEST_ALGORITHM_SHA384, + HAL_DIGEST_ALGORITHM_SHA512 +''') + +class HALKeyType(Enum): pass + +HALKeyType.define(''' + HAL_KEY_TYPE_NONE, + HAL_KEY_TYPE_RSA_PRIVATE, + HAL_KEY_TYPE_RSA_PUBLIC, + HAL_KEY_TYPE_EC_PRIVATE, + HAL_KEY_TYPE_EC_PUBLIC +''') + +class HALCurve(Enum): pass + +HALCurve.define(''' + HAL_CURVE_NONE, + HAL_CURVE_P256, + HAL_CURVE_P384, + HAL_CURVE_P521 +''') + +class HALUser(Enum): pass + +HALUser.define(''' + HAL_USER_NONE, + HAL_USER_NORMAL, + HAL_USER_SO, + HAL_USER_WHEEL +''') + +HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE = (1 << 0) +HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT = (1 << 1) +HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT = (1 << 2) +HAL_KEY_FLAG_TOKEN = (1 << 3) +HAL_KEY_FLAG_PUBLIC = (1 << 4) + +HAL_PKEY_ATTRIBUTE_NIL = (0xFFFFFFFF) + + +class UUID(uuid.UUID): + + def xdr_packer(self, packer): + packer.pack_bytes(self.bytes) + + +def cached_property(func): + + attr_name = "_" + func.__name__ + + def wrapped(self): + try: + value = getattr(self, attr_name) + except AttributeError: + value = func(self) + setattr(self, attr_name, value) + return value + + wrapped.__name__ = func.__name__ + + return property(wrapped) + + +class Handle(object): + + def __int__(self): + return self.handle + + def __cmp__(self, other): + return cmp(self.handle, int(other)) + + def xdr_packer(self, packer): + packer.pack_uint(self.handle) + + +class Digest(Handle): + + def __init__(self, hsm, handle, algorithm): + self.hsm = hsm + self.handle = handle + self.algorithm = algorithm + + def update(self, data): + self.hsm.hash_update(self, data) + + def finalize(self, length = None): + return self.hsm.hash_finalize(self, length or self.digest_length) + + @cached_property + def algorithm_id(self): + return self.hsm.hash_get_digest_algorithm_id(self.algorithm) + + @cached_property + def digest_length(self): + return self.hsm.hash_get_digest_length(self.algorithm) + + +class LocalDigest(object): + """ + Implements same interface as Digest class, but using PyCrypto, to + support mixed-mode PKey operations. This only supports algorithms + that PyCrypto supports, so no SHA512/224 or SHA512/256, sorry. + """ + + def __init__(self, hsm, handle, algorithm, key): + from Crypto.Hash import HMAC, SHA, SHA224, SHA256, SHA384, SHA512 + self.hsm = hsm + self.handle = handle + self.algorithm = algorithm + try: + h = self._algorithms[algorithm] + except AttributeError: + self._algorithms = { + HAL_DIGEST_ALGORITHM_SHA1 : SHA.SHA1Hash, + HAL_DIGEST_ALGORITHM_SHA224 : SHA224.SHA224Hash, + HAL_DIGEST_ALGORITHM_SHA256 : SHA256.SHA256Hash, + HAL_DIGEST_ALGORITHM_SHA384 : SHA384.SHA384Hash, + HAL_DIGEST_ALGORITHM_SHA512 : SHA512.SHA512Hash + } + h = self._algorithms[algorithm] + self.digest_length = h.digest_size + self.algorithm_id = chr(0x30) + chr(2 + len(h.oid)) + h.oid + self._context = HMAC.HMAC(key = key, digestmod = h) if key else h() + + def update(self, data): + self._context.update(data) + + def finalize(self, length = None): + return self._context.digest() + + def finalize_padded(self, pkey): + if pkey.key_type not in (HAL_KEY_TYPE_RSA_PRIVATE, HAL_KEY_TYPE_RSA_PUBLIC): + return self.finalize() + # PKCS #1.5 requires the digest to be wrapped up in an ASN.1 DigestInfo object. + from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString + return DerSequence([DerSequence([self._context.oid, DerNull().encode()]).encode(), + DerOctetString(self.finalize()).encode()]).encode() + + +class PKey(Handle): + + def __init__(self, hsm, handle, uuid): + self.hsm = hsm + self.handle = handle + self.uuid = uuid + self.deleted = False + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if not self.deleted: + self.close() + + def close(self): + self.hsm.pkey_close(self) + + def delete(self): + self.hsm.pkey_delete(self) + self.deleted = True + + @cached_property + def key_type(self): + return self.hsm.pkey_get_key_type(self) + + @cached_property + def key_curve(self): + return self.hsm.pkey_get_key_curve(self) + + @cached_property + def key_flags(self): + return self.hsm.pkey_get_key_flags(self) + + @cached_property + def public_key_len(self): + return self.hsm.pkey_get_public_key_len(self) + + @cached_property + def public_key(self): + return self.hsm.pkey_get_public_key(self, self.public_key_len) + + def sign(self, hash = 0, data = "", length = 1024): + return self.hsm.pkey_sign(self, hash = hash, data = data, length = length) + + def verify(self, hash = 0, data = "", signature = None): + self.hsm.pkey_verify(self, hash = hash, data = data, signature = signature) + + def set_attributes(self, attributes): + self.hsm.pkey_set_attributes(self, attributes) + + def get_attributes(self, attributes): + attrs = self.hsm.pkey_get_attributes(self, attributes, 0) + attrs = dict((k, v) for k, v in attrs.iteritems() if v != HAL_PKEY_ATTRIBUTE_NIL) + result = dict((a, None) for a in attributes) + result.update(self.hsm.pkey_get_attributes(self, attrs.iterkeys(), sum(attrs.itervalues()))) + return result + + +class HSM(object): + + debug = False + mixed_mode = False + + _send_delay = 0 # 0.1 + + def _raise_if_error(self, status): + if status != 0: + raise HALError.table[status]() + + def __init__(self, device = os.getenv("CRYPTECH_RPC_CLIENT_SERIAL_DEVICE", "/dev/ttyUSB0")): + while True: + try: + self.tty = serial.Serial(device, 921600, timeout = 0.1) + break + except serial.SerialException: + time.sleep(0.2) + + def _write(self, c): + if self.debug: + sys.stdout.write("{:02x}".format(ord(c))) + self.tty.write(c) + if self._send_delay > 0: + time.sleep(self._send_delay) + + def _send(self, msg): # Expects an xdrlib.Packer + if self.debug: + sys.stdout.write("+send: ") + self._write(SLIP_END) + for c in msg.get_buffer(): + if c == SLIP_END: + self._write(SLIP_ESC) + self._write(SLIP_ESC_END) + elif c == SLIP_ESC: + self._write(SLIP_ESC) + self._write(SLIP_ESC_ESC) + else: + self._write(c) + self._write(SLIP_END) + if self.debug: + sys.stdout.write("\n") + + def _recv(self, code): # Returns an xdrlib.Unpacker + if self.debug: + sys.stdout.write("+recv: ") + msg = [] + esc = False + while True: + c = self.tty.read(1) + if self.debug and c: + sys.stdout.write("{:02x}".format(ord(c))) + if not c: + time.sleep(0.1) + elif c == SLIP_END and not msg: + continue + elif c == SLIP_END: + if self.debug: + sys.stdout.write("\n") + msg = xdrlib.Unpacker("".join(msg)) + if msg.unpack_uint() == code: + return msg + msg = [] + if self.debug: + sys.stdout.write("+recv: ") + elif c == SLIP_ESC: + esc = True + elif esc and c == SLIP_ESC_END: + esc = False + msg.append(SLIP_END) + elif esc and c == SLIP_ESC_ESC: + esc = False + msg.append(SLIP_ESC) + else: + msg.append(c) + + _pack_builtin = (((int, long), "_pack_uint"), + (str, "_pack_bytes"), + ((list, tuple, set), "_pack_array"), + (dict, "_pack_items")) + + def _pack_arg(self, packer, arg): + if hasattr(arg, "xdr_packer"): + return arg.xdr_packer(packer) + for cls, method in self._pack_builtin: + if isinstance(arg, cls): + return getattr(self, method)(packer, arg) + raise RuntimeError("Don't know how to pack {!r} ({!r})".format(arg, type(arg))) + + def _pack_args(self, packer, args): + for arg in args: + self._pack_arg(packer, arg) + + def _pack_uint(self, packer, arg): + packer.pack_uint(arg) + + def _pack_bytes(self, packer, arg): + packer.pack_bytes(arg) + + def _pack_array(self, packer, arg): + packer.pack_uint(len(arg)) + self._pack_args(packer, arg) + + def _pack_items(self, packer, arg): + packer.pack_uint(len(arg)) + for name, value in arg.iteritems(): + self._pack_arg(packer, name) + self._pack_arg(packer, HAL_PKEY_ATTRIBUTE_NIL if value is None else value) + + @contextlib.contextmanager + def rpc(self, code, *args, **kwargs): + client = kwargs.get("client", 0) + packer = xdrlib.Packer() + packer.pack_uint(code) + packer.pack_uint(client) + self._pack_args(packer, args) + self._send(packer) + unpacker = self._recv(code) + client = unpacker.unpack_uint() + self._raise_if_error(unpacker.unpack_uint()) + yield unpacker + unpacker.done() + + def get_version(self): + with self.rpc(RPC_FUNC_GET_VERSION) as r: + return r.unpack_uint() + + def get_random(self, n): + with self.rpc(RPC_FUNC_GET_RANDOM, n) as r: + return r.unpack_bytes() + + def set_pin(self, user, pin, client = 0): + with self.rpc(RPC_FUNC_SET_PIN, user, pin, client = client): + return + + def login(self, user, pin, client = 0): + with self.rpc(RPC_FUNC_LOGIN, user, pin, client = client): + return + + def logout(self, client = 0): + with self.rpc(RPC_FUNC_LOGOUT, client = client): + return + + def logout_all(self): + with self.rpc(RPC_FUNC_LOGOUT_ALL): + return + + def is_logged_in(self, user, client = 0): + with self.rpc(RPC_FUNC_IS_LOGGED_IN, user, client = client): + return + + def hash_get_digest_length(self, alg): + with self.rpc(RPC_FUNC_HASH_GET_DIGEST_LEN, alg) as r: + return r.unpack_uint() + + def hash_get_digest_algorithm_id(self, alg, max_len = 256): + with self.rpc(RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID, alg, max_len) as r: + return r.unpack_bytes() + + def hash_get_algorithm(self, handle): + with self.rpc(RPC_FUNC_HASH_GET_ALGORITHM, handle) as r: + return HALDigestAlgorithm.index[r.unpack_uint()] + + def hash_initialize(self, alg, key = "", client = 0, session = 0, mixed_mode = None): + if mixed_mode is None: + mixed_mode = self.mixed_mode + if mixed_mode: + return LocalDigest(self, 0, alg, key) + else: + with self.rpc(RPC_FUNC_HASH_INITIALIZE, session, alg, key, client = client) as r: + return Digest(self, r.unpack_uint(), alg) + + def hash_update(self, handle, data): + with self.rpc(RPC_FUNC_HASH_UPDATE, handle, data): + return + + def hash_finalize(self, handle, length = None): + if length is None: + length = self.hash_get_digest_length(self.hash_get_algorithm(handle)) + with self.rpc(RPC_FUNC_HASH_FINALIZE, handle, length) as r: + return r.unpack_bytes() + + def pkey_load(self, type, curve, der, flags = 0, client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_LOAD, session, type, curve, der, flags, client = client) as r: + return PKey(self, r.unpack_uint(), UUID(bytes = r.unpack_bytes())) + + def pkey_open(self, uuid, flags = 0, client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_OPEN, session, uuid, flags, client = client) as r: + return PKey(self, r.unpack_uint(), uuid) + + def pkey_generate_rsa(self, keylen, exponent = "\x01\x00\x01", flags = 0, client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_GENERATE_RSA, session, keylen, exponent, flags, client = client) as r: + return PKey(self, r.unpack_uint(), UUID(bytes = r.unpack_bytes())) + + def pkey_generate_ec(self, curve, flags = 0, client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_GENERATE_EC, session, curve, flags, client = client) as r: + return PKey(self, r.unpack_uint(), UUID(bytes = r.unpack_bytes())) + + def pkey_close(self, pkey): + with self.rpc(RPC_FUNC_PKEY_CLOSE, pkey): + return + + def pkey_delete(self, pkey): + with self.rpc(RPC_FUNC_PKEY_DELETE, pkey): + return + + def pkey_get_key_type(self, pkey): + with self.rpc(RPC_FUNC_PKEY_GET_KEY_TYPE, pkey) as r: + return HALKeyType.index[r.unpack_uint()] + + def pkey_get_key_curve(self, pkey): + with self.rpc(RPC_FUNC_PKEY_GET_KEY_CURVE, pkey) as r: + return HALCurve.index[r.unpack_uint()] + + def pkey_get_key_flags(self, pkey): + with self.rpc(RPC_FUNC_PKEY_GET_KEY_FLAGS, pkey) as r: + return r.unpack_uint() + + def pkey_get_public_key_len(self, pkey): + with self.rpc(RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN, pkey) as r: + return r.unpack_uint() + + def pkey_get_public_key(self, pkey, length = None): + if length is None: + length = self.pkey_get_public_key_len(pkey) + with self.rpc(RPC_FUNC_PKEY_GET_PUBLIC_KEY, pkey, length) as r: + return r.unpack_bytes() + + def pkey_sign(self, pkey, hash = 0, data = "", length = 1024): + assert not hash or not data + if isinstance(hash, LocalDigest): + hash, data = 0, hash.finalize_padded(pkey) + with self.rpc(RPC_FUNC_PKEY_SIGN, pkey, hash, data, length) as r: + return r.unpack_bytes() + + def pkey_verify(self, pkey, hash = 0, data = "", signature = None): + assert not hash or not data + if isinstance(hash, LocalDigest): + hash, data = 0, hash.finalize_padded(pkey) + with self.rpc(RPC_FUNC_PKEY_VERIFY, pkey, hash, data, signature): + return + + def pkey_match(self, type = 0, curve = 0, flags = 0, attributes = {}, + length = 64, client = 0, session = 0): + u = UUID(int = 0) + n = length + while n == length: + with self.rpc(RPC_FUNC_PKEY_MATCH, session, type, curve, flags, + attributes, length, u, client = client) as r: + n = r.unpack_uint() + for i in xrange(n): + u = UUID(bytes = r.unpack_bytes()) + yield u + + def pkey_set_attributes(self, pkey, attributes): + with self.rpc(RPC_FUNC_PKEY_SET_ATTRIBUTES, pkey, attributes): + return + + def pkey_get_attributes(self, pkey, attributes, attributes_buffer_len = 2048): + attributes = tuple(attributes) + with self.rpc(RPC_FUNC_PKEY_GET_ATTRIBUTES, pkey, attributes, attributes_buffer_len) as r: + n = r.unpack_uint() + if n != len(attributes): + raise HAL_ERROR_RPC_PROTOCOL_ERROR + if attributes_buffer_len > 0: + return dict((r.unpack_uint(), r.unpack_bytes()) for i in xrange(n)) + else: + return dict((r.unpack_uint(), r.unpack_uint()) for i in xrange(n)) diff --git a/masterkey.c b/masterkey.c deleted file mode 100644 index 3c4f0d3..0000000 --- a/masterkey.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * masterkey.c - * ----------- - * Masterkey set/get functions. - * - * 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. - */ - -/* - * Code to load the Master key (Key Encryption Key) from either the volatile MKM - * (by asking the FPGA to provide it, using the mkmif) or from the last sector in - * the keystore flash. - * - * Storing the master key in flash is a pretty Bad Idea, but since the Alpha board - * doesn't have a battery mounted (only pin headers for attaching one), it might - * help in non-production use where one doesn't have tamper protection anyways. - * - * For production use on the Alpha, one option is to have the Master Key on paper - * and enter it into volatile RAM after each power on. - * - * In both volatile memory and flash, the data is stored as a 32 bit status to - * know if the memory is initialized or not, followed by 32 bytes (256 bits) of - * Master Key. - */ - -#define HAL_OK CMIS_HAL_OK -#include "stm-init.h" -#include "stm-keystore.h" -#undef HAL_OK - -#define HAL_OK LIBHAL_OK -#include "hal.h" -#include "masterkey.h" -#undef HAL_OK - -#include <string.h> - - -static int volatile_init = 0, flash_init = 0; -static hal_core_t *core = NULL; - -#define MKM_VOLATILE_STATUS_ADDRESS 0 -#define MKM_VOLATILE_SCLK_DIV 0x20 -#define MKM_FLASH_STATUS_ADDRESS (KEYSTORE_SECTOR_SIZE * (KEYSTORE_NUM_SECTORS - 1)) -#define KEK_LENGTH (256 / 8) - -/* Match uninitialized flash for the "not set" value. - * Leave some bits at 1 for the "set" value to allow - * for adding more values later, if needed. - */ -#define MKM_STATUS_NOT_SET 0xffffffff -#define MKM_STATUS_SET 0x0000ffff -#define MKM_STATUS_ERASED 0x00000000 - - -hal_error_t masterkey_volatile_init() -{ - hal_error_t err; - uint32_t status; - - if (! volatile_init) { - if ((core = hal_core_find(MKMIF_NAME, NULL)) == NULL) { - return HAL_ERROR_CORE_NOT_FOUND; - } - - if ((err = hal_mkmif_set_clockspeed(core, MKM_VOLATILE_SCLK_DIV)) != LIBHAL_OK || - (err = hal_mkmif_init(core)) != LIBHAL_OK || - (err = hal_mkmif_read_word(core, MKM_VOLATILE_STATUS_ADDRESS, &status)) != LIBHAL_OK) - return err; - - if (status != MKM_STATUS_SET && status != MKM_STATUS_NOT_SET) { - /* XXX Something is a bit fishy here. If we just write the status word, it reads back wrong sometimes, - * while if we write the full buf too it is consistently right afterwards. - */ - uint8_t buf[KEK_LENGTH] = {0}; - if ((err = hal_mkmif_write(core, MKM_VOLATILE_STATUS_ADDRESS + 4, buf, sizeof(buf))) != LIBHAL_OK || - (err = hal_mkmif_write_word(core, MKM_VOLATILE_STATUS_ADDRESS, MKM_STATUS_NOT_SET)) != LIBHAL_OK) - return err; - } - - volatile_init = 1; - } - return LIBHAL_OK; -} - -hal_error_t masterkey_volatile_read(uint8_t *buf, size_t len) -{ - hal_error_t err; - uint32_t status; - - if (len && len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - - if ((err = masterkey_volatile_init()) != LIBHAL_OK || - (err = hal_mkmif_read_word(core, MKM_VOLATILE_STATUS_ADDRESS, &status)) != LIBHAL_OK) - return err; - - if (buf != NULL && len) { - /* Don't return the random bytes in the RAM memory in case it isn't initialized. - * Or maybe we should fill the buffer with proper random data in that case... hmm. - */ - if (status == MKM_STATUS_SET) { - if ((err = hal_mkmif_read(core, MKM_VOLATILE_STATUS_ADDRESS + 4, buf, len)) != LIBHAL_OK) { - return err; - } - } else { - memset(buf, 0x0, len); - } - } - - if (status == MKM_STATUS_SET) return LIBHAL_OK; - if (status == MKM_STATUS_NOT_SET) return HAL_ERROR_MASTERKEY_NOT_SET; - - return HAL_ERROR_MASTERKEY_FAIL; -} - -hal_error_t masterkey_volatile_write(uint8_t *buf, size_t len) -{ - hal_error_t err; - - if (len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - if (! buf) return HAL_ERROR_MASTERKEY_FAIL; - - if ((err = masterkey_volatile_init()) != LIBHAL_OK || - (err = hal_mkmif_write(core, MKM_VOLATILE_STATUS_ADDRESS + 4, buf, len)) != LIBHAL_OK || - (err = hal_mkmif_write_word(core, MKM_VOLATILE_STATUS_ADDRESS, MKM_STATUS_SET)) != LIBHAL_OK) - return err; - - return LIBHAL_OK; -} - -hal_error_t masterkey_volatile_erase(size_t len) -{ - uint8_t buf[KEK_LENGTH] = {0}; - hal_error_t err; - - if (len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - - if ((err = masterkey_volatile_init()) != LIBHAL_OK || - (err = hal_mkmif_write(core, MKM_VOLATILE_STATUS_ADDRESS + 4, buf, sizeof(buf))) != LIBHAL_OK || - (err = hal_mkmif_write_word(core, MKM_VOLATILE_STATUS_ADDRESS, MKM_STATUS_NOT_SET)) != LIBHAL_OK) - return err; - - return LIBHAL_OK; -} - -hal_error_t masterkey_flash_init() -{ - if (! flash_init) { - if (! keystore_check_id()) return HAL_ERROR_IO_UNEXPECTED; - flash_init = 1; - } - return LIBHAL_OK; -} - -hal_error_t masterkey_flash_read(uint8_t *buf, size_t len) -{ - uint8_t page[KEYSTORE_PAGE_SIZE]; - uint32_t *status = (uint32_t *) page; - hal_error_t err; - - if (len && len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - - if ((err = masterkey_flash_init()) != LIBHAL_OK) return err; - - if (! keystore_read_data(MKM_FLASH_STATUS_ADDRESS, page, sizeof(page))) { - memset(page, 0, sizeof(page)); - return HAL_ERROR_MASTERKEY_FAIL; - } - - if (buf != NULL && len) { - /* Don't return what's in the flash memory in case it isn't initialized. - * Or maybe we should fill the buffer with proper random data in that case... hmm. - */ - if (*status == MKM_STATUS_SET) { - memcpy(buf, page + 4, len); - } else { - memset(buf, 0x0, len); - } - } - - memset(page + 4, 0, sizeof(page) - 4); - - if (*status == MKM_STATUS_SET) return LIBHAL_OK; - if (*status == MKM_STATUS_ERASED || *status == MKM_STATUS_NOT_SET) return HAL_ERROR_MASTERKEY_NOT_SET; - - return HAL_ERROR_MASTERKEY_FAIL; -} - -hal_error_t masterkey_flash_write(uint8_t *buf, size_t len) -{ - uint8_t page[KEYSTORE_PAGE_SIZE] = {0xff}; - uint32_t *status = (uint32_t *) page; - int res; - - if (len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - if (buf == NULL) return HAL_ERROR_MASTERKEY_FAIL; - - if (masterkey_flash_init() != LIBHAL_OK) return HAL_ERROR_MASTERKEY_FAIL; - - *status = MKM_STATUS_SET; - memcpy(page + 4, buf, len); - - res = keystore_write_data(MKM_FLASH_STATUS_ADDRESS, page, sizeof(page)); - memset(page, 0, sizeof(page)); - if (res != 1) { - return HAL_ERROR_MASTERKEY_FAIL; - } - - return LIBHAL_OK; -} - -hal_error_t masterkey_flash_erase(size_t len) -{ - if (len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - - if (keystore_erase_sectors(MKM_FLASH_STATUS_ADDRESS / KEYSTORE_SECTOR_SIZE, - MKM_FLASH_STATUS_ADDRESS / KEYSTORE_SECTOR_SIZE) != 1) { - return HAL_ERROR_MASTERKEY_FAIL; - } - - return LIBHAL_OK; -} @@ -0,0 +1,230 @@ +/* + * mkm.c + * ----- + * Master Key Memory functions. + * + * 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. + */ + +/* + * Code to load the Master key (Key Encryption Key) from either the volatile MKM + * (by asking the FPGA to provide it, using the mkmif) or from the last sector in + * the keystore flash. + * + * Storing the master key in flash is a pretty Bad Idea, but since the Alpha board + * doesn't have a battery mounted (only pin headers for attaching one), it might + * help in non-production use where one doesn't have tamper protection anyways. + * + * For production use on the Alpha, one option is to have the Master Key on paper + * and enter it into volatile RAM after each power on. + * + * In both volatile memory and flash, the data is stored as a 32 bit status to + * know if the memory is initialized or not, followed by 32 bytes (256 bits) of + * Master Key. + */ + +#define HAL_OK CMIS_HAL_OK +#include "stm-init.h" +#include "stm-keystore.h" +#undef HAL_OK + +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#undef HAL_OK + +#include <string.h> + + +static int volatile_init = 0; +static hal_core_t *core = NULL; + +#define MKM_VOLATILE_STATUS_ADDRESS 0 +#define MKM_VOLATILE_SCLK_DIV 0x20 +#define MKM_FLASH_STATUS_ADDRESS (KEYSTORE_SECTOR_SIZE * (KEYSTORE_NUM_SECTORS - 1)) + +/* + * Match uninitialized flash for the "not set" value. + * Leave some bits at 1 for the "set" value to allow + * for adding more values later, if needed. + */ +#define MKM_STATUS_NOT_SET 0xffffffff +#define MKM_STATUS_SET 0x0000ffff +#define MKM_STATUS_ERASED 0x00000000 + + +static hal_error_t hal_mkm_volatile_init(void) +{ + if (volatile_init) + return LIBHAL_OK; + + hal_error_t err; + uint32_t status; + + if ((core = hal_core_find(MKMIF_NAME, NULL)) == NULL) + return HAL_ERROR_CORE_NOT_FOUND; + + if ((err = hal_mkmif_set_clockspeed(core, MKM_VOLATILE_SCLK_DIV)) != LIBHAL_OK || + (err = hal_mkmif_init(core)) != LIBHAL_OK || + (err = hal_mkmif_read_word(core, MKM_VOLATILE_STATUS_ADDRESS, &status)) != LIBHAL_OK) + return err; + + if (status != MKM_STATUS_SET && status != MKM_STATUS_NOT_SET) { + /* + * XXX Something is a bit fishy here. If we just write the status word, it reads back wrong sometimes, + * while if we write the full buf too it is consistently right afterwards. + */ + uint8_t buf[KEK_LENGTH] = {0}; + if ((err = hal_mkmif_write(core, MKM_VOLATILE_STATUS_ADDRESS + 4, buf, sizeof(buf))) != LIBHAL_OK || + (err = hal_mkmif_write_word(core, MKM_VOLATILE_STATUS_ADDRESS, MKM_STATUS_NOT_SET)) != LIBHAL_OK) + return err; + } + + volatile_init = 1; + return LIBHAL_OK; +} + +hal_error_t hal_mkm_volatile_read(uint8_t *buf, const size_t len) +{ + hal_error_t err; + uint32_t status; + + if (len && len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + if ((err = hal_mkm_volatile_init()) != LIBHAL_OK || + (err = hal_mkmif_read_word(core, MKM_VOLATILE_STATUS_ADDRESS, &status)) != LIBHAL_OK) + return err; + + if (buf != NULL && len) { + /* + * Don't return the random bytes in the RAM memory in case it isn't initialized. + * Or maybe we should fill the buffer with proper random data in that case... hmm. + */ + if (status != MKM_STATUS_SET) + memset(buf, 0x0, len); + else if ((err = hal_mkmif_read(core, MKM_VOLATILE_STATUS_ADDRESS + 4, buf, len)) != LIBHAL_OK) + return err; + } + + if (status == MKM_STATUS_SET) + return LIBHAL_OK; + + if (status == MKM_STATUS_NOT_SET) + return HAL_ERROR_MASTERKEY_NOT_SET; + + return HAL_ERROR_MASTERKEY_FAIL; +} + +hal_error_t hal_mkm_volatile_write(const uint8_t * const buf, const size_t len) +{ + hal_error_t err; + + if (len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + if (buf == NULL) + return HAL_ERROR_MASTERKEY_FAIL; + + if ((err = hal_mkm_volatile_init()) != LIBHAL_OK || + (err = hal_mkmif_write(core, MKM_VOLATILE_STATUS_ADDRESS + 4, buf, len)) != LIBHAL_OK || + (err = hal_mkmif_write_word(core, MKM_VOLATILE_STATUS_ADDRESS, MKM_STATUS_SET)) != LIBHAL_OK) + return err; + + return LIBHAL_OK; +} + +hal_error_t hal_mkm_volatile_erase(const size_t len) +{ + uint8_t buf[KEK_LENGTH] = {0}; + hal_error_t err; + + if (len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + if ((err = hal_mkm_volatile_init()) != LIBHAL_OK || + (err = hal_mkmif_write(core, MKM_VOLATILE_STATUS_ADDRESS + 4, buf, sizeof(buf))) != LIBHAL_OK || + (err = hal_mkmif_write_word(core, MKM_VOLATILE_STATUS_ADDRESS, MKM_STATUS_NOT_SET)) != LIBHAL_OK) + return err; + + return LIBHAL_OK; +} + +/* + * hal_mkm_flash_*() functions moved to ks_flash.c, to keep all the code that + * knows intimate details of the keystore flash layout in one place. + */ + +hal_error_t hal_mkm_get_kek(uint8_t *kek, + size_t *kek_len, + const size_t kek_max) +{ + if (kek == NULL || kek_len == NULL || kek_max < bitsToBytes(128)) + return HAL_ERROR_BAD_ARGUMENTS; + + const size_t len = ((kek_max < bitsToBytes(192)) ? bitsToBytes(128) : + (kek_max < bitsToBytes(256)) ? bitsToBytes(192) : + bitsToBytes(256)); + + hal_error_t err = hal_mkm_volatile_read(kek, len); + + if (err == LIBHAL_OK) { + *kek_len = len; + return LIBHAL_OK; + } + +#if HAL_MKM_FLASH_BACKUP_KLUDGE + + if (hal_mkm_flash_read(kek, len) == LIBHAL_OK) { + *kek_len = len; + return LIBHAL_OK; + } + +#endif + + /* + * Both keystores returned an error, probably HAL_ERROR_MASTERKEY_NOT_SET. + * I could try to be clever and compare the errors, but really the volatile + * keystore is the important one (you shouldn't store the master key in + * flash), so return that error. + */ + return err; +} + +/* + * "Any programmer who fails to comply with the standard naming, formatting, + * or commenting conventions should be shot. If it so happens that it is + * inconvenient to shoot him, then he is to be politely requested to recode + * his program in adherence to the above standard." + * -- Michael Spier, Digital Equipment Corporation + * + * Local variables: + * indent-tabs-mode: nil + * End: + */ @@ -75,7 +75,7 @@ static inline int check_pkey_flags(const hal_key_flags_t flags) return (flags &~ (HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT | HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT | - HAL_KEY_FLAG_PROXIMATE)) == 0; + HAL_KEY_FLAG_TOKEN)) == 0; } static inline int check_pkey_type_curve_flags(const hal_key_type_t type, @@ -218,36 +218,30 @@ hal_error_t hal_rpc_pkey_load(const hal_client_handle_t client, hal_pkey_handle_t *pkey, const hal_key_type_t type, const hal_curve_name_t curve, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const uint8_t * const der, const size_t der_len, const hal_key_flags_t flags) { - if (pkey == NULL || name == NULL || der == NULL || der_len == 0 || - !check_pkey_type_curve_flags(type, curve, flags)) + if (pkey == NULL || name == NULL || der == NULL || der_len == 0 || !check_pkey_type_curve_flags(type, curve, flags)) return HAL_ERROR_BAD_ARGUMENTS; - if (name_len > HAL_RPC_PKEY_NAME_MAX) - return HAL_ERROR_KEY_NAME_TOO_LONG; - return hal_rpc_pkey_dispatch->load(client, session, pkey, type, curve, name, name_len, der, der_len, flags); + return hal_rpc_pkey_dispatch->load(client, session, pkey, type, curve, name, der, der_len, flags); } -hal_error_t hal_rpc_pkey_find(const hal_client_handle_t client, +hal_error_t hal_rpc_pkey_open(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, + const hal_uuid_t * const name, const hal_key_flags_t flags) { - if (pkey == NULL || name == NULL || !check_pkey_type(type)) + if (pkey == NULL || name == NULL) return HAL_ERROR_BAD_ARGUMENTS; - if (name_len > HAL_RPC_PKEY_NAME_MAX) - return HAL_ERROR_KEY_NAME_TOO_LONG; - return hal_rpc_pkey_dispatch->find(client, session, pkey, type, name, name_len, flags); + return hal_rpc_pkey_dispatch->open(client, session, pkey, name, flags); } hal_error_t hal_rpc_pkey_generate_rsa(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const unsigned key_len, const uint8_t * const exp, const size_t exp_len, const hal_key_flags_t flags) @@ -255,24 +249,20 @@ hal_error_t hal_rpc_pkey_generate_rsa(const hal_client_handle_t client, if (pkey == NULL || name == NULL || key_len == 0 || (key_len & 7) != 0 || exp == NULL || exp_len == 0 || !check_pkey_flags(flags)) return HAL_ERROR_BAD_ARGUMENTS; - if (name_len > HAL_RPC_PKEY_NAME_MAX) - return HAL_ERROR_KEY_NAME_TOO_LONG; - return hal_rpc_pkey_dispatch->generate_rsa(client, session, pkey, name, name_len, key_len, exp, exp_len, flags); + return hal_rpc_pkey_dispatch->generate_rsa(client, session, pkey, name, key_len, exp, exp_len, flags); } hal_error_t hal_rpc_pkey_generate_ec(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const hal_curve_name_t curve, const hal_key_flags_t flags) { if (pkey == NULL || name == NULL || !check_pkey_type_curve_flags(HAL_KEY_TYPE_EC_PRIVATE, curve, flags)) return HAL_ERROR_BAD_ARGUMENTS; - if (name_len > HAL_RPC_PKEY_NAME_MAX) - return HAL_ERROR_KEY_NAME_TOO_LONG; - return hal_rpc_pkey_dispatch->generate_ec(client, session, pkey, name, name_len, curve, flags); + return hal_rpc_pkey_dispatch->generate_ec(client, session, pkey, name, curve, flags); } hal_error_t hal_rpc_pkey_close(const hal_pkey_handle_t pkey) @@ -285,16 +275,6 @@ hal_error_t hal_rpc_pkey_delete(const hal_pkey_handle_t pkey) return hal_rpc_pkey_dispatch->delete(pkey); } -hal_error_t hal_rpc_pkey_rename(const hal_pkey_handle_t pkey, - const uint8_t * const name, const size_t name_len) -{ - if (name == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - if (name_len > HAL_RPC_PKEY_NAME_MAX) - return HAL_ERROR_KEY_NAME_TOO_LONG; - return hal_rpc_pkey_dispatch->rename(pkey, name, name_len); -} - hal_error_t hal_rpc_pkey_get_key_type(const hal_pkey_handle_t pkey, hal_key_type_t *type) { @@ -303,6 +283,14 @@ hal_error_t hal_rpc_pkey_get_key_type(const hal_pkey_handle_t pkey, return hal_rpc_pkey_dispatch->get_key_type(pkey, type); } +hal_error_t hal_rpc_pkey_get_key_curve(const hal_pkey_handle_t pkey, + hal_curve_name_t *curve) +{ + if (curve == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + return hal_rpc_pkey_dispatch->get_key_curve(pkey, curve); +} + hal_error_t hal_rpc_pkey_get_key_flags(const hal_pkey_handle_t pkey, hal_key_flags_t *flags) { @@ -324,8 +312,7 @@ hal_error_t hal_rpc_pkey_get_public_key(const hal_pkey_handle_t pkey, return hal_rpc_pkey_dispatch->get_public_key(pkey, der, der_len, der_max); } -hal_error_t hal_rpc_pkey_sign(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +hal_error_t hal_rpc_pkey_sign(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max) @@ -333,11 +320,10 @@ hal_error_t hal_rpc_pkey_sign(const hal_session_handle_t session, if (signature == NULL || signature_len == NULL || signature_max == 0 || (hash.handle == HAL_HANDLE_NONE) == (input == NULL || input_len == 0)) return HAL_ERROR_BAD_ARGUMENTS; - return hal_rpc_pkey_dispatch->sign(session, pkey, hash, input, input_len, signature, signature_len, signature_max); + return hal_rpc_pkey_dispatch->sign(pkey, hash, input, input_len, signature, signature_len, signature_max); } -hal_error_t hal_rpc_pkey_verify(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +hal_error_t hal_rpc_pkey_verify(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, const uint8_t * const signature, const size_t signature_len) @@ -345,17 +331,54 @@ hal_error_t hal_rpc_pkey_verify(const hal_session_handle_t session, if (signature == NULL || signature_len == 0 || (hash.handle == HAL_HANDLE_NONE) == (input == NULL || input_len == 0)) return HAL_ERROR_BAD_ARGUMENTS; - return hal_rpc_pkey_dispatch->verify(session, pkey, hash, input, input_len, signature, signature_len); + return hal_rpc_pkey_dispatch->verify(pkey, hash, input, input_len, signature, signature_len); +} + +hal_error_t hal_rpc_pkey_match(const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, + unsigned *result_len, + const unsigned result_max, + const hal_uuid_t * const previous_uuid) +{ + if ((attributes == NULL && attributes_len > 0) || previous_uuid == NULL || + result == NULL || result_len == NULL || result_max == 0) + return HAL_ERROR_BAD_ARGUMENTS; + + if (attributes != NULL) + for (int i = 0; i < attributes_len; i++) + if (attributes[i].value == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + return hal_rpc_pkey_dispatch->match(client, session, type, curve, flags, + attributes, attributes_len, + result, result_len, result_max, previous_uuid); +} + +hal_error_t hal_rpc_pkey_set_attributes(const hal_pkey_handle_t pkey, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len) +{ + if (attributes == NULL || attributes_len == 0) + return HAL_ERROR_BAD_ARGUMENTS; + return hal_rpc_pkey_dispatch->set_attributes(pkey, attributes, attributes_len); } -hal_error_t hal_rpc_pkey_list(hal_pkey_info_t *result, - unsigned *result_len, - const unsigned result_max, - hal_key_flags_t flags) +hal_error_t hal_rpc_pkey_get_attributes(const hal_pkey_handle_t pkey, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len) { - if (result == NULL || result_len == NULL || result_max == 0) + if (attributes == NULL || attributes_len == 0) return HAL_ERROR_BAD_ARGUMENTS; - return hal_rpc_pkey_dispatch->list(result, result_len, result_max, flags); + return hal_rpc_pkey_dispatch->get_attributes(pkey, attributes, attributes_len, + attributes_buffer, attributes_buffer_len); } /* diff --git a/rpc_client.c b/rpc_client.c index c4ceedd..4adf247 100644 --- a/rpc_client.c +++ b/rpc_client.c @@ -101,6 +101,16 @@ static hal_error_t read_matching_packet(const rpc_func_num_t expected_func, /* * RPC calls. + * + * In reading these, it helps to know that every call takes a minimum + * of two arguments (function code and client handle, even if the + * latter is just a dummy), and that every call returns a minimum of + * three values (function code, client handle, and return status). + * This may seem a bit redundant, but There Are Reasons: + * read_matching_packet() wants to make sure the result we're getting + * is from the function we thought we called, and having the client + * handle always present in a known place vastly simplifies the task + * of the client-side MUX daemon. */ static hal_error_t get_version(uint32_t *version) @@ -292,13 +302,14 @@ static hal_error_t hash_get_digest_algorithm_id(const hal_digest_algorithm_t alg hal_client_handle_t dummy_client = {0}; hal_error_t rpc_ret; - check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_HASH_GET_DIGEST_LEN)); + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID)); check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle)); check(hal_xdr_encode_int(&optr, olimit, alg)); check(hal_xdr_encode_int(&optr, olimit, len_max)); check(hal_rpc_send(outbuf, optr - outbuf)); - check(read_matching_packet(RPC_FUNC_HASH_GET_DIGEST_LEN, inbuf, sizeof(inbuf), &iptr, &ilimit)); + check(read_matching_packet(RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID, + inbuf, sizeof(inbuf), &iptr, &ilimit)); check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); if (rpc_ret == HAL_OK) { @@ -411,13 +422,14 @@ static hal_error_t pkey_remote_load(const hal_client_handle_t client, hal_pkey_handle_t *pkey, const hal_key_type_t type, const hal_curve_name_t curve, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const uint8_t * const der, const size_t der_len, const hal_key_flags_t flags) { - uint8_t outbuf[nargs(8) + pad(name_len) + pad(der_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); - uint8_t inbuf[nargs(4)]; + uint8_t outbuf[nargs(7) + pad(der_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(5) + pad(sizeof(name->uuid))]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); + uint32_t name_len = sizeof(name->uuid); hal_error_t rpc_ret; check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_LOAD)); @@ -425,7 +437,6 @@ static hal_error_t pkey_remote_load(const hal_client_handle_t client, check(hal_xdr_encode_int(&optr, olimit, session.handle)); check(hal_xdr_encode_int(&optr, olimit, type)); check(hal_xdr_encode_int(&optr, olimit, curve)); - check(hal_xdr_encode_buffer(&optr, olimit, name, name_len)); check(hal_xdr_encode_buffer(&optr, olimit, der, der_len)); check(hal_xdr_encode_int(&optr, olimit, flags)); check(hal_rpc_send(outbuf, optr - outbuf)); @@ -433,35 +444,39 @@ static hal_error_t pkey_remote_load(const hal_client_handle_t client, check(read_matching_packet(RPC_FUNC_PKEY_LOAD, inbuf, sizeof(inbuf), &iptr, &ilimit)); check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); - if (rpc_ret == HAL_OK) + + if (rpc_ret == HAL_OK) { check(hal_xdr_decode_int(&iptr, ilimit, &pkey->handle)); + check(hal_xdr_decode_buffer(&iptr, ilimit, name->uuid, &name_len)); + if (name_len != sizeof(name->uuid)) + return HAL_ERROR_KEY_NAME_TOO_LONG; + } return rpc_ret; } -static hal_error_t pkey_remote_find(const hal_client_handle_t client, +static hal_error_t pkey_remote_open(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, + const hal_uuid_t * const name, const hal_key_flags_t flags) { - uint8_t outbuf[nargs(6) + pad(name_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t outbuf[nargs(5) + pad(sizeof(name->uuid))], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); uint8_t inbuf[nargs(4)]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); hal_error_t rpc_ret; - check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_FIND)); + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_OPEN)); check(hal_xdr_encode_int(&optr, olimit, client.handle)); check(hal_xdr_encode_int(&optr, olimit, session.handle)); - check(hal_xdr_encode_int(&optr, olimit, type)); - check(hal_xdr_encode_buffer(&optr, olimit, name, name_len)); + check(hal_xdr_encode_buffer(&optr, olimit, name->uuid, sizeof(name->uuid))); check(hal_xdr_encode_int(&optr, olimit, flags)); check(hal_rpc_send(outbuf, optr - outbuf)); - check(read_matching_packet(RPC_FUNC_PKEY_FIND, inbuf, sizeof(inbuf), &iptr, &ilimit)); + check(read_matching_packet(RPC_FUNC_PKEY_OPEN, inbuf, sizeof(inbuf), &iptr, &ilimit)); check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); + if (rpc_ret == HAL_OK) check(hal_xdr_decode_int(&iptr, ilimit, &pkey->handle)); @@ -471,20 +486,20 @@ static hal_error_t pkey_remote_find(const hal_client_handle_t client, static hal_error_t pkey_remote_generate_rsa(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const unsigned key_len, const uint8_t * const exp, const size_t exp_len, const hal_key_flags_t flags) { - uint8_t outbuf[nargs(7) + pad(name_len) + pad(exp_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); - uint8_t inbuf[nargs(4)]; + uint8_t outbuf[nargs(6) + pad(exp_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(5) + pad(sizeof(name->uuid))]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); + uint32_t name_len = sizeof(name->uuid); hal_error_t rpc_ret; check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_GENERATE_RSA)); check(hal_xdr_encode_int(&optr, olimit, client.handle)); check(hal_xdr_encode_int(&optr, olimit, session.handle)); - check(hal_xdr_encode_buffer(&optr, olimit, name, name_len)); check(hal_xdr_encode_int(&optr, olimit, key_len)); check(hal_xdr_encode_buffer(&optr, olimit, exp, exp_len)); check(hal_xdr_encode_int(&optr, olimit, flags)); @@ -493,8 +508,13 @@ static hal_error_t pkey_remote_generate_rsa(const hal_client_handle_t client, check(read_matching_packet(RPC_FUNC_PKEY_GENERATE_RSA, inbuf, sizeof(inbuf), &iptr, &ilimit)); check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); - if (rpc_ret == HAL_OK) + + if (rpc_ret == HAL_OK) { check(hal_xdr_decode_int(&iptr, ilimit, &pkey->handle)); + check(hal_xdr_decode_buffer(&iptr, ilimit, name->uuid, &name_len)); + if (name_len != sizeof(name->uuid)) + return HAL_ERROR_KEY_NAME_TOO_LONG; + } return rpc_ret; } @@ -502,19 +522,19 @@ static hal_error_t pkey_remote_generate_rsa(const hal_client_handle_t client, static hal_error_t pkey_remote_generate_ec(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const hal_curve_name_t curve, const hal_key_flags_t flags) { - uint8_t outbuf[nargs(6) + pad(name_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); - uint8_t inbuf[nargs(4)]; + uint8_t outbuf[nargs(5)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(5) + pad(sizeof(name->uuid))]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); + uint32_t name_len = sizeof(name->uuid); hal_error_t rpc_ret; check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_GENERATE_EC)); check(hal_xdr_encode_int(&optr, olimit, client.handle)); check(hal_xdr_encode_int(&optr, olimit, session.handle)); - check(hal_xdr_encode_buffer(&optr, olimit, name, name_len)); check(hal_xdr_encode_int(&optr, olimit, curve)); check(hal_xdr_encode_int(&optr, olimit, flags)); check(hal_rpc_send(outbuf, optr - outbuf)); @@ -522,8 +542,13 @@ static hal_error_t pkey_remote_generate_ec(const hal_client_handle_t client, check(read_matching_packet(RPC_FUNC_PKEY_GENERATE_EC, inbuf, sizeof(inbuf), &iptr, &ilimit)); check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); - if (rpc_ret == HAL_OK) + + if (rpc_ret == HAL_OK) { check(hal_xdr_decode_int(&iptr, ilimit, &pkey->handle)); + check(hal_xdr_decode_buffer(&iptr, ilimit, name->uuid, &name_len)); + if (name_len != sizeof(name->uuid)) + return HAL_ERROR_KEY_NAME_TOO_LONG; + } return rpc_ret; } @@ -566,49 +591,52 @@ static hal_error_t pkey_remote_delete(const hal_pkey_handle_t pkey) return rpc_ret; } -static hal_error_t pkey_remote_rename(const hal_pkey_handle_t pkey, - const uint8_t * const name, const size_t name_len) +static hal_error_t pkey_remote_get_key_type(const hal_pkey_handle_t pkey, + hal_key_type_t *type) { - uint8_t outbuf[nargs(4) + pad(name_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); - uint8_t inbuf[nargs(3)]; + uint8_t outbuf[nargs(3)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(4)]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); + uint32_t type32; hal_client_handle_t dummy_client = {0}; hal_error_t rpc_ret; - check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_RENAME)); + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_KEY_TYPE)); check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle)); check(hal_xdr_encode_int(&optr, olimit, pkey.handle)); - check(hal_xdr_encode_buffer(&optr, olimit, name, name_len)); check(hal_rpc_send(outbuf, optr - outbuf)); - check(read_matching_packet(RPC_FUNC_PKEY_RENAME, inbuf, sizeof(inbuf), &iptr, &ilimit)); + check(read_matching_packet(RPC_FUNC_PKEY_GET_KEY_TYPE, inbuf, sizeof(inbuf), &iptr, &ilimit)); check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); + if (rpc_ret == HAL_OK) { + check(hal_xdr_decode_int(&iptr, ilimit, &type32)); + *type = (hal_key_type_t)type32; + } return rpc_ret; } - -static hal_error_t pkey_remote_get_key_type(const hal_pkey_handle_t pkey, - hal_key_type_t *type) +static hal_error_t pkey_remote_get_key_curve(const hal_pkey_handle_t pkey, + hal_curve_name_t *curve) { uint8_t outbuf[nargs(3)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); uint8_t inbuf[nargs(4)]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); - uint32_t type32; + uint32_t curve32; hal_client_handle_t dummy_client = {0}; hal_error_t rpc_ret; - check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_KEY_TYPE)); + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_KEY_CURVE)); check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle)); check(hal_xdr_encode_int(&optr, olimit, pkey.handle)); check(hal_rpc_send(outbuf, optr - outbuf)); - check(read_matching_packet(RPC_FUNC_PKEY_GET_KEY_TYPE, inbuf, sizeof(inbuf), &iptr, &ilimit)); + check(read_matching_packet(RPC_FUNC_PKEY_GET_KEY_CURVE, inbuf, sizeof(inbuf), &iptr, &ilimit)); check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); if (rpc_ret == HAL_OK) { - check(hal_xdr_decode_int(&iptr, ilimit, &type32)); - *type = (hal_key_type_t)type32; + check(hal_xdr_decode_int(&iptr, ilimit, &curve32)); + *curve = (hal_curve_name_t)curve32; } return rpc_ret; } @@ -689,13 +717,12 @@ static hal_error_t pkey_remote_get_public_key(const hal_pkey_handle_t pkey, return rpc_ret; } -static hal_error_t pkey_remote_sign(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +static hal_error_t pkey_remote_sign(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, - const uint8_t * const input, const size_t input_len, + const uint8_t * const input, const size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max) { - uint8_t outbuf[nargs(7) + pad(input_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t outbuf[nargs(6) + pad(input_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); uint8_t inbuf[nargs(4) + pad(signature_max)]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); uint32_t slen32 = signature_max; @@ -704,7 +731,6 @@ static hal_error_t pkey_remote_sign(const hal_session_handle_t session, check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_SIGN)); check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle)); - check(hal_xdr_encode_int(&optr, olimit, session.handle)); check(hal_xdr_encode_int(&optr, olimit, pkey.handle)); check(hal_xdr_encode_int(&optr, olimit, hash.handle)); check(hal_xdr_encode_buffer(&optr, olimit, input, input_len)); @@ -721,13 +747,12 @@ static hal_error_t pkey_remote_sign(const hal_session_handle_t session, return rpc_ret; } -static hal_error_t pkey_remote_verify(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +static hal_error_t pkey_remote_verify(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, const uint8_t * const signature, const size_t signature_len) { - uint8_t outbuf[nargs(7) + pad(input_len) + pad(signature_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t outbuf[nargs(6) + pad(input_len) + pad(signature_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); uint8_t inbuf[nargs(3)]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); hal_client_handle_t dummy_client = {0}; @@ -735,7 +760,6 @@ static hal_error_t pkey_remote_verify(const hal_session_handle_t session, check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_VERIFY)); check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle)); - check(hal_xdr_encode_int(&optr, olimit, session.handle)); check(hal_xdr_encode_int(&optr, olimit, pkey.handle)); check(hal_xdr_encode_int(&optr, olimit, hash.handle)); check(hal_xdr_encode_buffer(&optr, olimit, input, input_len)); @@ -748,46 +772,143 @@ static hal_error_t pkey_remote_verify(const hal_session_handle_t session, return rpc_ret; } -static hal_error_t hal_xdr_decode_pkey_info(const uint8_t **iptr, const uint8_t * const ilimit, hal_pkey_info_t *info) +static hal_error_t pkey_remote_match(const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, + unsigned *result_len, + const unsigned result_max, + const hal_uuid_t * const previous_uuid) { - uint32_t i32; + size_t attributes_buffer_len = 0; + if (attributes != NULL) + for (int i = 0; i < attributes_len; i++) + attributes_buffer_len += pad(attributes[i].length); + + uint8_t outbuf[nargs(9 + attributes_len * 2) + attributes_buffer_len + pad(sizeof(hal_uuid_t))]; + uint8_t *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(4) + pad(result_max * sizeof(hal_uuid_t))]; + const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); + hal_error_t rpc_ret; - check(hal_xdr_decode_int(iptr, ilimit, &i32)); info->type = i32; - check(hal_xdr_decode_int(iptr, ilimit, &i32)); info->curve = i32; - check(hal_xdr_decode_int(iptr, ilimit, &i32)); info->flags = i32; - check(hal_xdr_decode_buffer(iptr, ilimit, (uint8_t *)&info->name[0], &i32)); info->name_len = i32; - return HAL_OK; + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_MATCH)); + check(hal_xdr_encode_int(&optr, olimit, client.handle)); + check(hal_xdr_encode_int(&optr, olimit, session.handle)); + check(hal_xdr_encode_int(&optr, olimit, type)); + check(hal_xdr_encode_int(&optr, olimit, curve)); + check(hal_xdr_encode_int(&optr, olimit, flags)); + check(hal_xdr_encode_int(&optr, olimit, attributes_len)); + if (attributes != NULL) { + for (int i = 0; i < attributes_len; i++) { + check(hal_xdr_encode_int(&optr, olimit, attributes[i].type)); + check(hal_xdr_encode_buffer(&optr, olimit, attributes[i].value, attributes[i].length)); + } + } + check(hal_xdr_encode_int(&optr, olimit, result_max)); + check(hal_xdr_encode_buffer(&optr, olimit, previous_uuid->uuid, sizeof(previous_uuid->uuid))); + check(hal_rpc_send(outbuf, optr - outbuf)); + + check(read_matching_packet(RPC_FUNC_PKEY_MATCH, inbuf, sizeof(inbuf), &iptr, &ilimit)); + + check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); + if (rpc_ret == HAL_OK) { + uint32_t array_len; + *result_len = 0; + check(hal_xdr_decode_int(&iptr, ilimit, &array_len)); + for (int i = 0; i < array_len; ++i) { + uint32_t uuid_len = sizeof(result[i].uuid); + check(hal_xdr_decode_buffer(&iptr, ilimit, result[i].uuid, &uuid_len)); + if (uuid_len != sizeof(result[i].uuid)) + return HAL_ERROR_KEY_NAME_TOO_LONG; + } + *result_len = array_len; + } + return rpc_ret; } -static hal_error_t pkey_remote_list(hal_pkey_info_t *result, - unsigned *result_len, - const unsigned result_max, - hal_key_flags_t flags) +static hal_error_t pkey_remote_set_attributes(const hal_pkey_handle_t pkey, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len) { - uint8_t outbuf[nargs(4)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); - uint8_t inbuf[nargs(4) + pad(result_max * sizeof(hal_pkey_info_t))]; + size_t outbuf_len = nargs(4 + 2 * attributes_len); + for (int i = 0; i < attributes_len; i++) + outbuf_len += pad(attributes[i].length); + + uint8_t outbuf[outbuf_len], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(3)]; const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); - uint32_t len; hal_client_handle_t dummy_client = {0}; - hal_error_t ret, rpc_ret; + hal_error_t rpc_ret; - check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_LIST)); + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_SET_ATTRIBUTES)); check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle)); - check(hal_xdr_encode_int(&optr, olimit, result_max)); - check(hal_xdr_encode_int(&optr, olimit, flags)); + check(hal_xdr_encode_int(&optr, olimit, pkey.handle)); + check(hal_xdr_encode_int(&optr, olimit, attributes_len)); + for (int i = 0; i < attributes_len; i++) { + check(hal_xdr_encode_int(&optr, olimit, attributes[i].type)); + if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) + check(hal_xdr_encode_int(&optr, olimit, HAL_PKEY_ATTRIBUTE_NIL)); + else + check(hal_xdr_encode_buffer(&optr, olimit, attributes[i].value, attributes[i].length)); + } check(hal_rpc_send(outbuf, optr - outbuf)); - check(read_matching_packet(RPC_FUNC_PKEY_LIST, inbuf, sizeof(inbuf), &iptr, &ilimit)); + check(read_matching_packet(RPC_FUNC_PKEY_SET_ATTRIBUTES, inbuf, sizeof(inbuf), &iptr, &ilimit)); + + check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); + return rpc_ret; +} + +static hal_error_t pkey_remote_get_attributes(const hal_pkey_handle_t pkey, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len) +{ + /* inbuf[] here includes one extra word per attribute for padding */ + uint8_t outbuf[nargs(5 + attributes_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(3 + 3 * attributes_len) + attributes_buffer_len]; + const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); + hal_client_handle_t dummy_client = {0}; + hal_error_t rpc_ret; + + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_ATTRIBUTES)); + check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle)); + check(hal_xdr_encode_int(&optr, olimit, pkey.handle)); + check(hal_xdr_encode_int(&optr, olimit, attributes_len)); + for (int i = 0; i < attributes_len; i++) + check(hal_xdr_encode_int(&optr, olimit, attributes[i].type)); + check(hal_xdr_encode_int(&optr, olimit, attributes_buffer_len)); + check(hal_rpc_send(outbuf, optr - outbuf)); + + check(read_matching_packet(RPC_FUNC_PKEY_GET_ATTRIBUTES, inbuf, sizeof(inbuf), &iptr, &ilimit)); check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); if (rpc_ret == HAL_OK) { - int i; - check(hal_xdr_decode_int(&iptr, ilimit, &len)); - *result_len = len; - for (i = 0; i < len; ++i) { - if ((ret = hal_xdr_decode_pkey_info(&iptr, ilimit, &result[i])) != HAL_OK) { - *result_len = 0; - return ret; + uint8_t *abuf = attributes_buffer; + uint32_t u32; + check(hal_xdr_decode_int(&iptr, ilimit, &u32)); + if (u32 != attributes_len) + return HAL_ERROR_RPC_PROTOCOL_ERROR; + for (int i = 0; i < attributes_len; i++) { + check(hal_xdr_decode_int(&iptr, ilimit, &u32)); + if (u32 != attributes[i].type) + return HAL_ERROR_RPC_PROTOCOL_ERROR; + if (attributes_buffer_len == 0) { + check(hal_xdr_decode_int(&iptr, ilimit, &u32)); + attributes[i].value = NULL; + attributes[i].length = u32; + } + else { + u32 = attributes_buffer + attributes_buffer_len - abuf; + check(hal_xdr_decode_buffer(&iptr, ilimit, abuf, &u32)); + attributes[i].value = abuf; + attributes[i].length = u32; + abuf += u32; } } } @@ -795,41 +916,21 @@ static hal_error_t pkey_remote_list(hal_pkey_info_t *result, } #if RPC_CLIENT == RPC_CLIENT_MIXED + /* * "Mixed" mode pkey operations, where the public key operation itself * takes place on the HSM but the hashing takes place locally. If * we're given a hash context in this case, it's local, so we have to * pull the digest from the hash context and send that to the HSM. - * - * These methods are also responsible for dispatching pkey operations - * to the local or remote key store based on the PROXIMATE flags. - * These flags are only meaningful when operating in mixed mode. */ -static inline const hal_rpc_pkey_dispatch_t *mixed_flags_dispatch(const hal_key_flags_t flags) -{ - if ((flags & HAL_KEY_FLAG_PROXIMATE) == 0) - return &hal_rpc_remote_pkey_dispatch; - else - return &hal_rpc_local_pkey_dispatch; -} - -static inline const hal_rpc_pkey_dispatch_t *mixed_handle_dispatch(const hal_pkey_handle_t pkey) -{ - if ((pkey.handle & HAL_PKEY_HANDLE_PROXIMATE_FLAG) == 0) - return &hal_rpc_remote_pkey_dispatch; - else - return &hal_rpc_local_pkey_dispatch; -} - -static hal_error_t pkey_mixed_sign(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +static hal_error_t pkey_mixed_sign(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max) { if (input != NULL) - return mixed_handle_dispatch(pkey)->sign(session, pkey, hash, input, input_len, + return hal_rpc_remote_pkey_dispatch.sign(pkey, hash, input, input_len, signature, signature_len, signature_max); hal_digest_algorithm_t alg; @@ -848,7 +949,7 @@ static hal_error_t pkey_mixed_sign(const hal_session_handle_t session, case HAL_KEY_TYPE_RSA_PRIVATE: case HAL_KEY_TYPE_RSA_PUBLIC: - if ((err = hal_rpc_pkey_pkcs1_construct_digestinfo(hash, digest, &digest_len, sizeof(digest))) != HAL_OK) + if ((err = hal_rpc_pkcs1_construct_digestinfo(hash, digest, &digest_len, sizeof(digest))) != HAL_OK) return err; break; @@ -858,18 +959,17 @@ static hal_error_t pkey_mixed_sign(const hal_session_handle_t session, } - return mixed_handle_dispatch(pkey)->sign(session, pkey, hal_hash_handle_none, digest, digest_len, + return hal_rpc_remote_pkey_dispatch.sign(pkey, hal_hash_handle_none, digest, digest_len, signature, signature_len, signature_max); } -static hal_error_t pkey_mixed_verify(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +static hal_error_t pkey_mixed_verify(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, const uint8_t * const signature, const size_t signature_len) { if (input != NULL) - return mixed_handle_dispatch(pkey)->verify(session, pkey, hash, input, input_len, + return hal_rpc_remote_pkey_dispatch.verify(pkey, hash, input, input_len, signature, signature_len); hal_digest_algorithm_t alg; @@ -888,7 +988,7 @@ static hal_error_t pkey_mixed_verify(const hal_session_handle_t session, case HAL_KEY_TYPE_RSA_PRIVATE: case HAL_KEY_TYPE_RSA_PUBLIC: - if ((err = hal_rpc_pkey_pkcs1_construct_digestinfo(hash, digest, &digest_len, sizeof(digest))) != HAL_OK) + if ((err = hal_rpc_pkcs1_construct_digestinfo(hash, digest, &digest_len, sizeof(digest))) != HAL_OK) return err; break; @@ -898,103 +998,10 @@ static hal_error_t pkey_mixed_verify(const hal_session_handle_t session, } - return mixed_handle_dispatch(pkey)->verify(session, pkey, hal_hash_handle_none, + return hal_rpc_remote_pkey_dispatch.verify(pkey, hal_hash_handle_none, digest, digest_len, signature, signature_len); } -static hal_error_t pkey_mixed_load(const hal_client_handle_t client, - const hal_session_handle_t session, - hal_pkey_handle_t *pkey, - const hal_key_type_t type, - const hal_curve_name_t curve, - const uint8_t * const name, const size_t name_len, - const uint8_t * const der, const size_t der_len, - const hal_key_flags_t flags) -{ - return mixed_flags_dispatch(flags)->load(client, session, pkey, type, curve, - name, name_len, der, der_len, flags); -} - -static hal_error_t pkey_mixed_find(const hal_client_handle_t client, - const hal_session_handle_t session, - hal_pkey_handle_t *pkey, - const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, - const hal_key_flags_t flags) -{ - return mixed_flags_dispatch(flags)->find(client, session, pkey, type, - name, name_len, flags); -} - -static hal_error_t pkey_mixed_generate_rsa(const hal_client_handle_t client, - const hal_session_handle_t session, - hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, - const unsigned key_length, - const uint8_t * const public_exponent, const size_t public_exponent_len, - const hal_key_flags_t flags) -{ - return mixed_flags_dispatch(flags)->generate_rsa(client, session, pkey, - name, name_len, key_length, - public_exponent, public_exponent_len, flags); -} - -static hal_error_t pkey_mixed_generate_ec(const hal_client_handle_t client, - const hal_session_handle_t session, - hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, - const hal_curve_name_t curve, - const hal_key_flags_t flags) -{ - return mixed_flags_dispatch(flags)->generate_ec(client, session, pkey, name, name_len, curve, flags); -} - -static hal_error_t pkey_mixed_close(const hal_pkey_handle_t pkey) -{ - return mixed_handle_dispatch(pkey)->close(pkey); -} - -static hal_error_t pkey_mixed_delete(const hal_pkey_handle_t pkey) -{ - return mixed_handle_dispatch(pkey)->delete(pkey); -} - -static hal_error_t pkey_mixed_rename(const hal_pkey_handle_t pkey, - const uint8_t * const name, const size_t name_len) -{ - return mixed_handle_dispatch(pkey)->rename(pkey, name, name_len); -} - -static hal_error_t pkey_mixed_get_key_type(const hal_pkey_handle_t pkey, - hal_key_type_t *key_type) -{ - return mixed_handle_dispatch(pkey)->get_key_type(pkey, key_type); -} - -static hal_error_t pkey_mixed_get_key_flags(const hal_pkey_handle_t pkey, - hal_key_flags_t *flags) -{ - return mixed_handle_dispatch(pkey)->get_key_flags(pkey, flags); -} - -static size_t pkey_mixed_get_public_key_len(const hal_pkey_handle_t pkey) -{ - return mixed_handle_dispatch(pkey)->get_public_key_len(pkey); -} - -static hal_error_t pkey_mixed_get_public_key(const hal_pkey_handle_t pkey, - uint8_t *der, size_t *der_len, const size_t der_max) -{ - return mixed_handle_dispatch(pkey)->get_public_key(pkey, der, der_len, der_max); -} - -static hal_error_t pkey_mixed_list(hal_pkey_info_t *result, - unsigned *result_len, - const unsigned result_max, - hal_key_flags_t flags) -{ - return mixed_flags_dispatch(flags)->list(result, result_len, result_max, flags); -} #endif /* RPC_CLIENT == RPC_CLIENT_MIXED */ /* @@ -1002,57 +1009,61 @@ static hal_error_t pkey_mixed_list(hal_pkey_info_t *result, */ const hal_rpc_misc_dispatch_t hal_rpc_remote_misc_dispatch = { - set_pin, - login, - logout, - logout_all, - is_logged_in, - get_random, - get_version + .set_pin = set_pin, + .login = login, + .logout = logout, + .logout_all = logout_all, + .is_logged_in = is_logged_in, + .get_random = get_random, + .get_version = get_version }; const hal_rpc_hash_dispatch_t hal_rpc_remote_hash_dispatch = { - hash_get_digest_len, - hash_get_digest_algorithm_id, - hash_get_algorithm, - hash_initialize, - hash_update, - hash_finalize + .get_digest_length = hash_get_digest_len, + .get_digest_algorithm_id = hash_get_digest_algorithm_id, + .get_algorithm = hash_get_algorithm, + .initialize = hash_initialize, + .update = hash_update, + .finalize = hash_finalize }; const hal_rpc_pkey_dispatch_t hal_rpc_remote_pkey_dispatch = { - pkey_remote_load, - pkey_remote_find, - pkey_remote_generate_rsa, - pkey_remote_generate_ec, - pkey_remote_close, - pkey_remote_delete, - pkey_remote_rename, - pkey_remote_get_key_type, - pkey_remote_get_key_flags, - pkey_remote_get_public_key_len, - pkey_remote_get_public_key, - pkey_remote_sign, - pkey_remote_verify, - pkey_remote_list + .load = pkey_remote_load, + .open = pkey_remote_open, + .generate_rsa = pkey_remote_generate_rsa, + .generate_ec = pkey_remote_generate_ec, + .close = pkey_remote_close, + .delete = pkey_remote_delete, + .get_key_type = pkey_remote_get_key_type, + .get_key_curve = pkey_remote_get_key_curve, + .get_key_flags = pkey_remote_get_key_flags, + .get_public_key_len = pkey_remote_get_public_key_len, + .get_public_key = pkey_remote_get_public_key, + .sign = pkey_remote_sign, + .verify = pkey_remote_verify, + .match = pkey_remote_match, + .set_attributes = pkey_remote_set_attributes, + .get_attributes = pkey_remote_get_attributes }; #if RPC_CLIENT == RPC_CLIENT_MIXED const hal_rpc_pkey_dispatch_t hal_rpc_mixed_pkey_dispatch = { - pkey_mixed_load, - pkey_mixed_find, - pkey_mixed_generate_rsa, - pkey_mixed_generate_ec, - pkey_mixed_close, - pkey_mixed_delete, - pkey_mixed_rename, - pkey_mixed_get_key_type, - pkey_mixed_get_key_flags, - pkey_mixed_get_public_key_len, - pkey_mixed_get_public_key, - pkey_mixed_sign, - pkey_mixed_verify, - pkey_mixed_list + .load = pkey_remote_load, + .open = pkey_remote_open, + .generate_rsa = pkey_remote_generate_rsa, + .generate_ec = pkey_remote_generate_ec, + .close = pkey_remote_close, + .delete = pkey_remote_delete, + .get_key_type = pkey_remote_get_key_type, + .get_key_curve = pkey_remote_get_key_curve, + .get_key_flags = pkey_remote_get_key_flags, + .get_public_key_len = pkey_remote_get_public_key_len, + .get_public_key = pkey_remote_get_public_key, + .sign = pkey_mixed_sign, + .verify = pkey_mixed_verify, + .match = pkey_remote_match, + .set_attributes = pkey_remote_set_attributes, + .get_attributes = pkey_remote_get_attributes }; #endif /* RPC_CLIENT == RPC_CLIENT_MIXED */ @@ -157,12 +157,12 @@ static inline void free_handle(handle_slot_t *slot) static inline const hal_hash_descriptor_t *alg_to_descriptor(const hal_digest_algorithm_t alg) { switch (alg) { - case hal_digest_algorithm_sha1: return hal_hash_sha1; - case hal_digest_algorithm_sha256: return hal_hash_sha256; - case hal_digest_algorithm_sha512_224: return hal_hash_sha512_224; - case hal_digest_algorithm_sha512_256: return hal_hash_sha512_256; - case hal_digest_algorithm_sha384: return hal_hash_sha384; - case hal_digest_algorithm_sha512: return hal_hash_sha512; + case HAL_DIGEST_ALGORITHM_SHA1: return hal_hash_sha1; + case HAL_DIGEST_ALGORITHM_SHA256: return hal_hash_sha256; + case HAL_DIGEST_ALGORITHM_SHA512_224: return hal_hash_sha512_224; + case HAL_DIGEST_ALGORITHM_SHA512_256: return hal_hash_sha512_256; + case HAL_DIGEST_ALGORITHM_SHA384: return hal_hash_sha384; + case HAL_DIGEST_ALGORITHM_SHA512: return hal_hash_sha512; default: return NULL; } } @@ -303,12 +303,12 @@ static hal_error_t finalize(const hal_hash_handle_t handle, } const hal_rpc_hash_dispatch_t hal_rpc_local_hash_dispatch = { - get_digest_length, - get_digest_algorithm_id, - get_algorithm, - initialize, - update, - finalize + .get_digest_length = get_digest_length, + .get_digest_algorithm_id = get_digest_algorithm_id, + .get_algorithm = get_algorithm, + .initialize = initialize, + .update = update, + .finalize = finalize }; /* @@ -133,7 +133,7 @@ static hal_error_t login(const hal_client_handle_t client, const hal_ks_pin_t *p; hal_error_t err; - if ((err = hal_ks_get_pin(user, &p)) != HAL_OK) + if ((err = hal_get_pin(user, &p)) != HAL_OK) return err; uint8_t buf[sizeof(p->pin)]; @@ -207,7 +207,7 @@ static hal_error_t set_pin(const hal_client_handle_t client, const hal_ks_pin_t *pp; hal_error_t err; - if ((err = hal_ks_get_pin(user, &pp)) != HAL_OK) + if ((err = hal_get_pin(user, &pp)) != HAL_OK) return err; hal_ks_pin_t p = *pp; @@ -219,7 +219,7 @@ static hal_error_t set_pin(const hal_client_handle_t client, (const uint8_t *) newpin, newpin_len, p.salt, sizeof(p.salt), p.pin, sizeof(p.pin), p.iterations)) != HAL_OK || - (err = hal_ks_set_pin(user, &p)) != HAL_OK) + (err = hal_set_pin(user, &p)) != HAL_OK) return err; return HAL_OK; @@ -238,13 +238,13 @@ hal_error_t hal_set_pin_default_iterations(const hal_client_handle_t client, } const hal_rpc_misc_dispatch_t hal_rpc_local_misc_dispatch = { - set_pin, - login, - logout, - logout_all, - is_logged_in, - get_random, - get_version + .set_pin = set_pin, + .login = login, + .logout = logout, + .logout_all = logout_all, + .is_logged_in = is_logged_in, + .get_random = get_random, + .get_version = get_version }; /* diff --git a/masterkey.h b/rpc_pkcs1.c index 1d5e7a5..2dcf9dd 100644 --- a/masterkey.h +++ b/rpc_pkcs1.c @@ -1,7 +1,7 @@ /* - * masterkey.h + * rpc_pkcs1.c * ----------- - * Code to handle the Master Key that wraps all the keys in the keystore. + * PKCS #1 (RSA) support code layered on top of RPC hash API. * * Copyright (c) 2016, NORDUnet A/S All rights reserved. * @@ -32,15 +32,51 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __STM32_HSM_MASTERKEY_H -#define __STM32_HSM_MASTERKEY_H +#include <assert.h> -extern hal_error_t masterkey_volatile_read(uint8_t *buf, size_t len); -extern hal_error_t masterkey_volatile_write(uint8_t *buf, size_t len); -extern hal_error_t masterkey_volatile_erase(size_t len); +#include "hal.h" +#include "hal_internal.h" -extern hal_error_t masterkey_flash_read(uint8_t *buf, size_t len); -extern hal_error_t masterkey_flash_write(uint8_t *buf, size_t len); -extern hal_error_t masterkey_flash_erase(size_t len); +/* + * Construct a PKCS #1 DigestInfo object. This requires some (very + * basic) ASN.1 encoding, which we perform inline. + */ + +hal_error_t hal_rpc_pkcs1_construct_digestinfo(const hal_hash_handle_t handle, + uint8_t *digest_info, size_t *digest_info_len, + const size_t digest_info_max) +{ + assert(digest_info != NULL && digest_info_len != NULL); + + hal_digest_algorithm_t alg; + size_t len, alg_len; + hal_error_t err; + + if ((err = hal_rpc_hash_get_algorithm(handle, &alg)) != HAL_OK || + (err = hal_rpc_hash_get_digest_length(alg, &len)) != HAL_OK || + (err = hal_rpc_hash_get_digest_algorithm_id(alg, NULL, &alg_len, 0)) != HAL_OK) + return err; + + *digest_info_len = len + alg_len + 4; + + if (*digest_info_len >= digest_info_max) + return HAL_ERROR_RESULT_TOO_LONG; + + assert(*digest_info_len < 130); + + uint8_t *d = digest_info; + + *d++ = 0x30; /* SEQUENCE */ + *d++ = (uint8_t) (*digest_info_len - 2); + + if ((err = hal_rpc_hash_get_digest_algorithm_id(alg, d, NULL, alg_len)) != HAL_OK) + return err; + d += alg_len; + + *d++ = 0x04; /* OCTET STRING */ + *d++ = (uint8_t) len; + + assert(digest_info + *digest_info_len == d + len); -#endif /* __STM32_HSM_MASTERKEY_H */ + return hal_rpc_hash_finalize(handle, d, len); +} @@ -39,35 +39,12 @@ #include "hal.h" #include "hal_internal.h" -typedef struct { - hal_client_handle_t client_handle; - hal_session_handle_t session_handle; - hal_pkey_handle_t pkey_handle; - hal_key_type_t type; - hal_curve_name_t curve; - hal_key_flags_t flags; - uint8_t name[HAL_RPC_PKEY_NAME_MAX]; - size_t name_len; - int ks_hint; - /* - * This might be where we'd stash a (hal_core_t *) pointing to a - * core which has already been loaded with the key, if we were - * trying to be clever about using multiple signing cores. Moot - * point (ie, no way we could possibly test such a thing) as long as - * the FPGA is too small to hold more than one modexp core and ECDSA - * is entirely software, so skip it for now, but the implied - * semantics are interesting: a pkey handle starts to resemble an - * initialized signing core, and once all the cores are in use, one - * can't load another key without closing an existing pkey handle. - */ -} pkey_slot_t; - #ifndef HAL_STATIC_PKEY_STATE_BLOCKS #define HAL_STATIC_PKEY_STATE_BLOCKS 0 #endif #if HAL_STATIC_PKEY_STATE_BLOCKS > 0 -static pkey_slot_t pkey_handle[HAL_STATIC_PKEY_STATE_BLOCKS]; +static hal_pkey_slot_t pkey_handle[HAL_STATIC_PKEY_STATE_BLOCKS]; #endif /* @@ -78,27 +55,28 @@ static pkey_slot_t pkey_handle[HAL_STATIC_PKEY_STATE_BLOCKS]; * soon, to help identify use-after-free bugs in calling code. * * The high order bit of the pkey handle is left free for - * HAL_PKEY_HANDLE_PROXIMATE_FLAG, which is used by the mixed-mode + * HAL_PKEY_HANDLE_TOKEN_FLAG, which is used by the mixed-mode * handlers to route calls to the appropriate destination. */ -static inline pkey_slot_t *alloc_slot(const hal_key_flags_t flags) +static inline hal_pkey_slot_t *alloc_slot(const hal_key_flags_t flags) { #if HAL_STATIC_PKEY_STATE_BLOCKS > 0 static uint16_t next_glop = 0; uint32_t glop = ++next_glop << 16; next_glop %= 0x7FFF; - assert((glop & HAL_PKEY_HANDLE_PROXIMATE_FLAG) == 0); + assert((glop & HAL_PKEY_HANDLE_TOKEN_FLAG) == 0); - if ((flags & HAL_KEY_FLAG_PROXIMATE) != 0) - glop |= HAL_PKEY_HANDLE_PROXIMATE_FLAG; + if ((flags & HAL_KEY_FLAG_TOKEN) != 0) + glop |= HAL_PKEY_HANDLE_TOKEN_FLAG; for (int i = 0; i < sizeof(pkey_handle)/sizeof(*pkey_handle); i++) { if (pkey_handle[i].type != HAL_KEY_TYPE_NONE) continue; + memset(&pkey_handle[i], 0, sizeof(pkey_handle[i])); pkey_handle[i].pkey_handle.handle = i | glop; - pkey_handle[i].ks_hint = -1; + pkey_handle[i].hint = -1; return &pkey_handle[i]; } #endif @@ -111,7 +89,7 @@ static inline pkey_slot_t *alloc_slot(const hal_key_flags_t flags) * the right glop. Returns slot pointer on success, NULL otherwise. */ -static inline pkey_slot_t *find_handle(const hal_pkey_handle_t handle) +static inline hal_pkey_slot_t *find_handle(const hal_pkey_handle_t handle) { #if HAL_STATIC_PKEY_STATE_BLOCKS > 0 const int i = (int) (handle.handle & 0xFFFF); @@ -123,61 +101,67 @@ static inline pkey_slot_t *find_handle(const hal_pkey_handle_t handle) return NULL; } -#warning Still need access control on pkey objects based on current login state /* - * This would be simple, except for PKCS #11 non-token objects (CKA_TOKEN = CK_FALSE). - * Need to check detailed PKCS #11 rules, but, from memory, we may be supposed to allow - * access to non-token objects even when not logged in. Maybe. Rules are complex. + * Access rules are a bit complicated, mostly due to PKCS #11. * - * I think the libhal translation of this resolves around what we've - * been calling the PROXIMATE flags (which probably ought to be - * renamed to *_NONTOKEN_*, slightly less confusing name). For token - * objects, we insist on being logged in properly; for non-token - * objects, we do whatever silly thing PKCS #11 wants us to do, - * probably defaulting to requiring login if PKCS #11 gives us a choice. - */ - -/* - * Construct a PKCS #1 DigestInfo object. This requires some (very - * basic) ASN.1 encoding, which we perform inline. + * The simple, obvious rule would be that one must be logged in as + * HAL_USER_NORMAL to create, see, use, or delete a key, full stop. + * + * That's almost the rule that PKCS #11 follows for so-called + * "private" objects (CKA_PRIVATE = CK_TRUE), but PKCS #11 has a more + * model which not only allows wider visibility to "public" objects + * (CKA_PRIVATE = CK_FALSE) but also allows write access to "public + * session" (CKA_PRIVATE = CK_FALSE, CKA_TOKEN = CK_FALSE) objects + * regardless of login state. + * + * PKCS #11 also has a concept of read-only sessions, which we don't + * bother to implement at all on the HSM, since the PIN is required to + * be the same as for the corresponding read-write session, so this + * would just be additional compexity without adding any security on + * the HSM; the PKCS #11 library still has to support read-only + * sessions, but that's not our problem here. + * + * In general, non-PKCS #11 users of this API should probably never + * set HAL_KEY_FLAG_PUBLIC, in which case they'll get the simple rule. + * + * Note that keystore drivers may need to implement additional + * additional checks, eg, ks_volatile needs to enforce the rule that + * session objects are only visible to the client which created them + * (not the session, that would be too simple, thanks PKCS #11). In + * practice, this should not be a serious problem, since such checks + * will likely only apply to existing objects. The thing we really + * want to avoid is doing all the work to create a large key only to + * have the keystore driver reject access at the end, but since, by + * definition, that only occurs when creating new objects, the access + * decision doesn't depend on preexisting data, so the rules here + * should suffice. That's the theory, anyway, if this is wrong we may + * need to refactor. */ -hal_error_t hal_rpc_pkey_pkcs1_construct_digestinfo(const hal_hash_handle_t handle, - uint8_t *digest_info, size_t *digest_info_len, const size_t digest_info_max) +static inline hal_error_t check_normal_or_wheel(const hal_client_handle_t client) { - assert(digest_info != NULL && digest_info_len != NULL); - - hal_digest_algorithm_t alg; - size_t len, alg_len; - hal_error_t err; - - if ((err = hal_rpc_hash_get_algorithm(handle, &alg)) != HAL_OK || - (err = hal_rpc_hash_get_digest_length(alg, &len)) != HAL_OK || - (err = hal_rpc_hash_get_digest_algorithm_id(alg, NULL, &alg_len, 0)) != HAL_OK) - return err; - - *digest_info_len = len + alg_len + 4; - - if (*digest_info_len >= digest_info_max) - return HAL_ERROR_RESULT_TOO_LONG; - - assert(*digest_info_len < 130); - - uint8_t *d = digest_info; - - *d++ = 0x30; /* SEQUENCE */ - *d++ = (uint8_t) (*digest_info_len - 2); + const hal_error_t err = hal_rpc_is_logged_in(client, HAL_USER_NORMAL); + return (err == HAL_ERROR_FORBIDDEN + ? hal_rpc_is_logged_in(client, HAL_USER_WHEEL) + : err); +} - if ((err = hal_rpc_hash_get_digest_algorithm_id(alg, d, NULL, alg_len)) != HAL_OK) - return err; - d += alg_len; +static inline hal_error_t check_readable(const hal_client_handle_t client, + const hal_key_flags_t flags) +{ + if ((flags & HAL_KEY_FLAG_PUBLIC) != 0) + return HAL_OK; - *d++ = 0x04; /* OCTET STRING */ - *d++ = (uint8_t) len; + return check_normal_or_wheel(client); +} - assert(digest_info + *digest_info_len == d + len); +static inline hal_error_t check_writable(const hal_client_handle_t client, + const hal_key_flags_t flags) +{ + if ((flags & (HAL_KEY_FLAG_TOKEN | HAL_KEY_FLAG_PUBLIC)) == HAL_KEY_FLAG_PUBLIC) + return HAL_OK; - return hal_rpc_hash_finalize(handle, d, len); + return check_normal_or_wheel(client); } /* @@ -223,6 +207,18 @@ static hal_error_t pkcs1_5_pad(const uint8_t * const data, const size_t data_len } /* + * Given key flags, open appropriate keystore driver. + */ + +static inline hal_error_t ks_open_from_flags(hal_ks_t **ks, const hal_key_flags_t flags) +{ + return hal_ks_open((flags & HAL_KEY_FLAG_TOKEN) == 0 + ? hal_ks_volatile_driver + : hal_ks_token_driver, + ks); +} + +/* * Receive key from application, store it with supplied name, return a key handle. */ @@ -231,30 +227,44 @@ static hal_error_t pkey_local_load(const hal_client_handle_t client, hal_pkey_handle_t *pkey, const hal_key_type_t type, const hal_curve_name_t curve, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const uint8_t * const der, const size_t der_len, const hal_key_flags_t flags) { - pkey_slot_t *slot; + assert(pkey != NULL && name != NULL); + + hal_pkey_slot_t *slot; + hal_ks_t *ks = NULL; hal_error_t err; - assert(sizeof(slot->name) >= name_len && pkey != NULL); + if ((err = check_writable(client, flags)) != HAL_OK) + return err; if ((slot = alloc_slot(flags)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; - if ((err = hal_ks_store(type, curve, flags, name, name_len, der, der_len, &slot->ks_hint)) != HAL_OK) + if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) return err; - memcpy(slot->name, name, name_len); - slot->client_handle = client; + slot->client_handle = client; slot->session_handle = session; - slot->type = type; + slot->type = type; slot->curve = curve; slot->flags = flags; - slot->name_len = name_len; + + if ((err = ks_open_from_flags(&ks, flags)) == HAL_OK && + (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + + if (err != HAL_OK) { + slot->type = HAL_KEY_TYPE_NONE; + return err; + } *pkey = slot->pkey_handle; + *name = slot->name; return HAL_OK; } @@ -262,29 +272,38 @@ static hal_error_t pkey_local_load(const hal_client_handle_t client, * Look up a key given its name, return a key handle. */ -static hal_error_t pkey_local_find(const hal_client_handle_t client, +static hal_error_t pkey_local_open(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const hal_key_type_t type, - const uint8_t * const name, const size_t name_len, + const hal_uuid_t * const name, const hal_key_flags_t flags) { - pkey_slot_t *slot; + assert(pkey != NULL && name != NULL); + + hal_pkey_slot_t *slot; + hal_ks_t *ks = NULL; hal_error_t err; - assert(sizeof(slot->name) >= name_len && pkey != NULL); + if ((err = check_readable(client, flags)) != HAL_OK) + return err; if ((slot = alloc_slot(flags)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; - if ((err = hal_ks_fetch(type, name, name_len, &slot->curve, &slot->flags, NULL, NULL, 0, &slot->ks_hint)) != HAL_OK) - return err; - - memcpy(slot->name, name, name_len); + slot->name = *name; slot->client_handle = client; slot->session_handle = session; - slot->type = type; - slot->name_len = name_len; + + if ((err = ks_open_from_flags(&ks, flags)) == HAL_OK && + (err = hal_ks_fetch(ks, slot, NULL, NULL, 0)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + + if (err != HAL_OK) { + slot->type = HAL_KEY_TYPE_NONE; + return err; + } *pkey = slot->pkey_handle; return HAL_OK; @@ -297,48 +316,60 @@ static hal_error_t pkey_local_find(const hal_client_handle_t client, static hal_error_t pkey_local_generate_rsa(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const unsigned key_length, const uint8_t * const public_exponent, const size_t public_exponent_len, const hal_key_flags_t flags) { - pkey_slot_t *slot; + assert(pkey != NULL && name != NULL && (key_length & 7) == 0); + + uint8_t keybuf[hal_rsa_key_t_size]; + hal_rsa_key_t *key = NULL; + hal_pkey_slot_t *slot; + hal_ks_t *ks = NULL; hal_error_t err; - assert(sizeof(slot->name) >= name_len && pkey != NULL && (key_length & 7) == 0); + if ((err = check_writable(client, flags)) != HAL_OK) + return err; if ((slot = alloc_slot(flags)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; - uint8_t keybuf[hal_rsa_key_t_size]; - hal_rsa_key_t *key = NULL; + if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) + return err; + + slot->client_handle = client; + slot->session_handle = session; + slot->type = HAL_KEY_TYPE_RSA_PRIVATE; + slot->curve = HAL_CURVE_NONE; + slot->flags = flags; if ((err = hal_rsa_key_gen(NULL, &key, keybuf, sizeof(keybuf), key_length / 8, - public_exponent, public_exponent_len)) != HAL_OK) + public_exponent, public_exponent_len)) != HAL_OK) { + slot->type = HAL_KEY_TYPE_NONE; return err; + } uint8_t der[hal_rsa_private_key_to_der_len(key)]; size_t der_len; - if ((err = hal_rsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK) - err = hal_ks_store(HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE, flags, - name, name_len, der, der_len, &slot->ks_hint); + if ((err = hal_rsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK && + (err = ks_open_from_flags(&ks, flags)) == HAL_OK && + (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); - if (err != HAL_OK) + if (err != HAL_OK) { + slot->type = HAL_KEY_TYPE_NONE; return err; - - memcpy(slot->name, name, name_len); - slot->client_handle = client; - slot->session_handle = session; - slot->type = HAL_KEY_TYPE_RSA_PRIVATE; - slot->curve = HAL_CURVE_NONE; - slot->flags = flags; - slot->name_len = name_len; + } *pkey = slot->pkey_handle; + *name = slot->name; return HAL_OK; } @@ -350,46 +381,58 @@ static hal_error_t pkey_local_generate_rsa(const hal_client_handle_t client, static hal_error_t pkey_local_generate_ec(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, - const uint8_t * const name, const size_t name_len, + hal_uuid_t *name, const hal_curve_name_t curve, const hal_key_flags_t flags) { - pkey_slot_t *slot; + assert(pkey != NULL && name != NULL); + + uint8_t keybuf[hal_ecdsa_key_t_size]; + hal_ecdsa_key_t *key = NULL; + hal_pkey_slot_t *slot; + hal_ks_t *ks = NULL; hal_error_t err; - assert(sizeof(slot->name) >= name_len && pkey != NULL); + if ((err = check_writable(client, flags)) != HAL_OK) + return err; if ((slot = alloc_slot(flags)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; - uint8_t keybuf[hal_ecdsa_key_t_size]; - hal_ecdsa_key_t *key = NULL; + if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) + return err; - if ((err = hal_ecdsa_key_gen(NULL, &key, keybuf, sizeof(keybuf), curve)) != HAL_OK) + slot->client_handle = client; + slot->session_handle = session; + slot->type = HAL_KEY_TYPE_EC_PRIVATE; + slot->curve = curve; + slot->flags = flags; + + if ((err = hal_ecdsa_key_gen(NULL, &key, keybuf, sizeof(keybuf), curve)) != HAL_OK) { + slot->type = HAL_KEY_TYPE_NONE; return err; + } uint8_t der[hal_ecdsa_private_key_to_der_len(key)]; size_t der_len; - if ((err = hal_ecdsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK) - err = hal_ks_store(HAL_KEY_TYPE_EC_PRIVATE, curve, flags, - name, name_len, der, der_len, &slot->ks_hint); + if ((err = hal_ecdsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK && + (err = ks_open_from_flags(&ks, flags)) == HAL_OK && + (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); - if (err != HAL_OK) + if (err != HAL_OK) { + slot->type = HAL_KEY_TYPE_NONE; return err; - - memcpy(slot->name, name, name_len); - slot->client_handle = client; - slot->session_handle = session; - slot->type = HAL_KEY_TYPE_EC_PRIVATE; - slot->curve = curve; - slot->flags = flags; - slot->name_len = name_len; + } *pkey = slot->pkey_handle; + *name = slot->name; return HAL_OK; } @@ -399,7 +442,7 @@ static hal_error_t pkey_local_generate_ec(const hal_client_handle_t client, static hal_error_t pkey_local_close(const hal_pkey_handle_t pkey) { - pkey_slot_t *slot; + hal_pkey_slot_t *slot; if ((slot = find_handle(pkey)) == NULL) return HAL_ERROR_KEY_NOT_FOUND; @@ -415,12 +458,22 @@ static hal_error_t pkey_local_close(const hal_pkey_handle_t pkey) static hal_error_t pkey_local_delete(const hal_pkey_handle_t pkey) { - pkey_slot_t *slot = find_handle(pkey); + hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; - hal_error_t err = hal_ks_delete(slot->type, slot->name, slot->name_len, &slot->ks_hint); + hal_ks_t *ks = NULL; + hal_error_t err; + + if ((err = check_writable(slot->client_handle, slot->flags)) != HAL_OK) + return err; + + if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && + (err = hal_ks_delete(ks, slot)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); if (err == HAL_OK || err == HAL_ERROR_KEY_NOT_FOUND) memset(slot, 0, sizeof(*slot)); @@ -429,45 +482,41 @@ static hal_error_t pkey_local_delete(const hal_pkey_handle_t pkey) } /* - * Rename a key in the key store, given its key handle and a new name. + * Get type of key associated with handle. */ -static hal_error_t pkey_local_rename(const hal_pkey_handle_t pkey, - const uint8_t * const name, const size_t name_len) +static hal_error_t pkey_local_get_key_type(const hal_pkey_handle_t pkey, + hal_key_type_t *type) { - pkey_slot_t *slot = find_handle(pkey); + if (type == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; - hal_error_t err = hal_ks_rename(slot->type, slot->name, slot->name_len, name, name_len, &slot->ks_hint); - - if (err == HAL_OK) { - assert(name_len <= sizeof(slot->name)); - memcpy(slot->name, name, name_len); - memset(slot->name + name_len, 0, sizeof(slot->name) - name_len); - slot->name_len = name_len; - } + *type = slot->type; - return err; + return HAL_OK; } /* - * Get type of key associated with handle. + * Get curve of key associated with handle. */ -static hal_error_t pkey_local_get_key_type(const hal_pkey_handle_t pkey, - hal_key_type_t *type) +static hal_error_t pkey_local_get_key_curve(const hal_pkey_handle_t pkey, + hal_curve_name_t *curve) { - if (type == NULL) + if (curve == NULL) return HAL_ERROR_BAD_ARGUMENTS; - pkey_slot_t *slot = find_handle(pkey); + hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; - *type = slot->type; + *curve = slot->curve; return HAL_OK; } @@ -482,7 +531,7 @@ static hal_error_t pkey_local_get_key_flags(const hal_pkey_handle_t pkey, if (flags == NULL) return HAL_ERROR_BAD_ARGUMENTS; - pkey_slot_t *slot = find_handle(pkey); + hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; @@ -498,7 +547,7 @@ static hal_error_t pkey_local_get_key_flags(const hal_pkey_handle_t pkey, static size_t pkey_local_get_public_key_len(const hal_pkey_handle_t pkey) { - pkey_slot_t *slot = find_handle(pkey); + hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return 0; @@ -510,9 +559,16 @@ static size_t pkey_local_get_public_key_len(const hal_pkey_handle_t pkey) hal_ecdsa_key_t *ecdsa_key = NULL; uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len; + hal_ks_t *ks = NULL; + hal_error_t err; - if (hal_ks_fetch(slot->type, slot->name, slot->name_len, NULL, NULL, - der, &der_len, sizeof(der), &slot->ks_hint) == HAL_OK) { + if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && + (err = hal_ks_fetch(ks, slot, der, &der_len, sizeof(der))) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + + if (err == HAL_OK) { switch (slot->type) { case HAL_KEY_TYPE_RSA_PUBLIC: @@ -548,7 +604,7 @@ static size_t pkey_local_get_public_key_len(const hal_pkey_handle_t pkey) static hal_error_t pkey_local_get_public_key(const hal_pkey_handle_t pkey, uint8_t *der, size_t *der_len, const size_t der_max) { - pkey_slot_t *slot = find_handle(pkey); + hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; @@ -558,10 +614,16 @@ static hal_error_t pkey_local_get_public_key(const hal_pkey_handle_t pkey, hal_ecdsa_key_t *ecdsa_key = NULL; uint8_t buf[HAL_KS_WRAPPED_KEYSIZE]; size_t buf_len; + hal_ks_t *ks = NULL; hal_error_t err; - if ((err = hal_ks_fetch(slot->type, slot->name, slot->name_len, NULL, NULL, - buf, &buf_len, sizeof(buf), &slot->ks_hint)) == HAL_OK) { + if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && + (err = hal_ks_fetch(ks, slot, buf, &buf_len, sizeof(buf))) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + + if (err == HAL_OK) { switch (slot->type) { case HAL_KEY_TYPE_RSA_PUBLIC: @@ -622,8 +684,8 @@ static hal_error_t pkey_local_sign_rsa(uint8_t *keybuf, const size_t keybuf_len, if (*signature_len > signature_max) return HAL_ERROR_RESULT_TOO_LONG; - if (input == NULL) { - if ((err = hal_rpc_pkey_pkcs1_construct_digestinfo(hash, signature, &input_len, *signature_len)) != HAL_OK) + if (input == NULL || input_len == 0) { + if ((err = hal_rpc_pkcs1_construct_digestinfo(hash, signature, &input_len, *signature_len)) != HAL_OK) return err; input = signature; } @@ -650,7 +712,7 @@ static hal_error_t pkey_local_sign_ecdsa(uint8_t *keybuf, const size_t keybuf_le if ((err = hal_ecdsa_private_key_from_der(&key, keybuf, keybuf_len, der, der_len)) != HAL_OK) return err; - if (input == NULL) { + if (input == NULL || input_len == 0) { hal_digest_algorithm_t alg; if ((err = hal_rpc_hash_get_algorithm(hash, &alg)) != HAL_OK || @@ -672,13 +734,12 @@ static hal_error_t pkey_local_sign_ecdsa(uint8_t *keybuf, const size_t keybuf_le return HAL_OK; } -static hal_error_t pkey_local_sign(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +static hal_error_t pkey_local_sign(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, uint8_t * signature, size_t *signature_len, const size_t signature_max) { - pkey_slot_t *slot = find_handle(pkey); + hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; @@ -703,9 +764,14 @@ static hal_error_t pkey_local_sign(const hal_session_handle_t session, uint8_t keybuf[hal_rsa_key_t_size > hal_ecdsa_key_t_size ? hal_rsa_key_t_size : hal_ecdsa_key_t_size]; uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len; + hal_ks_t *ks = NULL; hal_error_t err; - err = hal_ks_fetch(slot->type, slot->name, slot->name_len, NULL, NULL, der, &der_len, sizeof(der), &slot->ks_hint); + if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && + (err = hal_ks_fetch(ks, slot, der, &der_len, sizeof(der))) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); if (err == HAL_OK) err = signer(keybuf, sizeof(keybuf), der, der_len, hash, input, input_len, signature, signature_len, signature_max); @@ -750,8 +816,8 @@ static hal_error_t pkey_local_verify_rsa(uint8_t *keybuf, const size_t keybuf_le if (err != HAL_OK) return err; - if (input == NULL) { - if ((err = hal_rpc_pkey_pkcs1_construct_digestinfo(hash, expected, &input_len, sizeof(expected))) != HAL_OK) + if (input == NULL || input_len == 0) { + if ((err = hal_rpc_pkcs1_construct_digestinfo(hash, expected, &input_len, sizeof(expected))) != HAL_OK) return err; input = expected; } @@ -797,7 +863,7 @@ static hal_error_t pkey_local_verify_ecdsa(uint8_t *keybuf, const size_t keybuf_ if (err != HAL_OK) return err; - if (input == NULL) { + if (input == NULL || input_len == 0) { hal_digest_algorithm_t alg; if ((err = hal_rpc_hash_get_algorithm(hash, &alg)) != HAL_OK || @@ -814,13 +880,12 @@ static hal_error_t pkey_local_verify_ecdsa(uint8_t *keybuf, const size_t keybuf_ return HAL_OK; } -static hal_error_t pkey_local_verify(const hal_session_handle_t session, - const hal_pkey_handle_t pkey, +static hal_error_t pkey_local_verify(const hal_pkey_handle_t pkey, const hal_hash_handle_t hash, const uint8_t * const input, const size_t input_len, const uint8_t * const signature, const size_t signature_len) { - pkey_slot_t *slot = find_handle(pkey); + hal_pkey_slot_t *slot = find_handle(pkey); if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; @@ -847,9 +912,14 @@ static hal_error_t pkey_local_verify(const hal_session_handle_t session, uint8_t keybuf[hal_rsa_key_t_size > hal_ecdsa_key_t_size ? hal_rsa_key_t_size : hal_ecdsa_key_t_size]; uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len; + hal_ks_t *ks = NULL; hal_error_t err; - err = hal_ks_fetch(slot->type, slot->name, slot->name_len, NULL, NULL, der, &der_len, sizeof(der), &slot->ks_hint); + if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && + (err = hal_ks_fetch(ks, slot, der, &der_len, sizeof(der))) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); if (err == HAL_OK) err = verifier(keybuf, sizeof(keybuf), slot->type, der, der_len, hash, input, input_len, signature, signature_len); @@ -860,34 +930,107 @@ static hal_error_t pkey_local_verify(const hal_session_handle_t session, return err; } +static hal_error_t pkey_local_match(const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t flags, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + hal_uuid_t *result, + unsigned *result_len, + const unsigned result_max, + const hal_uuid_t * const previous_uuid) +{ + hal_ks_t *ks = NULL; + hal_error_t err; -/* - * List keys in the key store. - */ + err = check_readable(client, flags); + + if (err == HAL_ERROR_FORBIDDEN) { + assert(result_len != NULL); + *result_len = 0; + return HAL_OK; + } + + if (err != HAL_OK) + return err; + + if ((err = ks_open_from_flags(&ks, flags)) == HAL_OK && + (err = hal_ks_match(ks, client, session, type, curve, flags, attributes, attributes_len, + result, result_len, result_max, previous_uuid)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + + return err; +} + +static hal_error_t pkey_local_set_attributes(const hal_pkey_handle_t pkey, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len) +{ + hal_pkey_slot_t *slot = find_handle(pkey); + + if (slot == NULL) + return HAL_ERROR_KEY_NOT_FOUND; + + hal_ks_t *ks = NULL; + hal_error_t err; + + if ((err = check_writable(slot->client_handle, slot->flags)) != HAL_OK) + return err; + + if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && + (err = hal_ks_set_attributes(ks, slot, attributes, attributes_len)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + + return err; +} -static hal_error_t pkey_local_list(hal_pkey_info_t *result, - unsigned *result_len, - const unsigned result_max, - hal_key_flags_t flags) +static hal_error_t pkey_local_get_attributes(const hal_pkey_handle_t pkey, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + uint8_t *attributes_buffer, + const size_t attributes_buffer_len) { - return hal_ks_list(result, result_len, result_max); + hal_pkey_slot_t *slot = find_handle(pkey); + + if (slot == NULL) + return HAL_ERROR_KEY_NOT_FOUND; + + hal_ks_t *ks = NULL; + hal_error_t err; + + if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && + (err = hal_ks_get_attributes(ks, slot, attributes, attributes_len, + attributes_buffer, attributes_buffer_len)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + + return err; } const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch = { - pkey_local_load, - pkey_local_find, - pkey_local_generate_rsa, - pkey_local_generate_ec, - pkey_local_close, - pkey_local_delete, - pkey_local_rename, - pkey_local_get_key_type, - pkey_local_get_key_flags, - pkey_local_get_public_key_len, - pkey_local_get_public_key, - pkey_local_sign, - pkey_local_verify, - pkey_local_list + .load = pkey_local_load, + .open = pkey_local_open, + .generate_rsa = pkey_local_generate_rsa, + .generate_ec = pkey_local_generate_ec, + .close = pkey_local_close, + .delete = pkey_local_delete, + .get_key_type = pkey_local_get_key_type, + .get_key_curve = pkey_local_get_key_curve, + .get_key_flags = pkey_local_get_key_flags, + .get_public_key_len = pkey_local_get_public_key_len, + .get_public_key = pkey_local_get_public_key, + .sign = pkey_local_sign, + .verify = pkey_local_verify, + .match = pkey_local_match, + .set_attributes = pkey_local_set_attributes, + .get_attributes = pkey_local_get_attributes }; /* diff --git a/rpc_server.c b/rpc_server.c index a0de42d..a21679a 100644 --- a/rpc_server.c +++ b/rpc_server.c @@ -44,6 +44,8 @@ #define pad(n) (((n) + 3) & ~3) +#define nargs(n) ((n) * 4) + static hal_error_t get_version(const uint8_t **iptr, const uint8_t * const ilimit, uint8_t **optr, const uint8_t * const olimit) { @@ -54,7 +56,8 @@ static hal_error_t get_version(const uint8_t **iptr, const uint8_t * const ilimi check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); /* call the local function */ - ret = hal_rpc_local_misc_dispatch.get_version(&version); + ret = hal_rpc_get_version(&version); + if (ret == HAL_OK) check(hal_xdr_encode_int(optr, olimit, version)); @@ -77,7 +80,7 @@ static hal_error_t get_random(const uint8_t **iptr, const uint8_t * const ilimit /* call the local function */ /* get the data directly into the output buffer */ check(hal_xdr_encode_int(optr, olimit, length)); - ret = hal_rpc_local_misc_dispatch.get_random(*optr, (size_t)length); + ret = hal_rpc_get_random(*optr, (size_t)length); if (ret == HAL_OK) *optr += pad(length); else @@ -101,7 +104,8 @@ static hal_error_t set_pin(const uint8_t **iptr, const uint8_t * const ilimit, check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &pin, &pin_len)); /* call the local function */ - ret = hal_rpc_local_misc_dispatch.set_pin(client, user, (const char * const)pin, pin_len); + ret = hal_rpc_set_pin(client, user, (const char * const)pin, pin_len); + return ret; } @@ -119,7 +123,8 @@ static hal_error_t login(const uint8_t **iptr, const uint8_t * const ilimit, check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &pin, &pin_len)); /* call the local function */ - ret = hal_rpc_local_misc_dispatch.login(client, user, (const char * const)pin, pin_len); + ret = hal_rpc_login(client, user, (const char * const)pin, pin_len); + return ret; } @@ -132,7 +137,8 @@ static hal_error_t logout(const uint8_t **iptr, const uint8_t * const ilimit, check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); /* call the local function */ - ret = hal_rpc_local_misc_dispatch.logout(client); + ret = hal_rpc_logout(client); + return ret; } @@ -145,7 +151,8 @@ static hal_error_t logout_all(const uint8_t **iptr, const uint8_t * const ilimit check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); /* call the local function */ - ret = hal_rpc_local_misc_dispatch.logout_all(); + ret = hal_rpc_logout_all(); + return ret; } @@ -160,7 +167,8 @@ static hal_error_t is_logged_in(const uint8_t **iptr, const uint8_t * const ilim check(hal_xdr_decode_int(iptr, ilimit, &user)); /* call the local function */ - ret = hal_rpc_local_misc_dispatch.is_logged_in(client, user); + ret = hal_rpc_is_logged_in(client, user); + return ret; } @@ -176,9 +184,11 @@ static hal_error_t hash_get_digest_len(const uint8_t **iptr, const uint8_t * con check(hal_xdr_decode_int(iptr, ilimit, &alg)); /* call the local function */ - ret = hal_rpc_local_hash_dispatch.get_digest_length(alg, &length); + ret = hal_rpc_hash_get_digest_length(alg, &length); + if (ret == HAL_OK) check(hal_xdr_encode_int(optr, olimit, length)); + return ret; } @@ -202,7 +212,7 @@ static hal_error_t hash_get_digest_algorithm_id(const uint8_t **iptr, const uint /* call the local function */ /* get the data directly into the output buffer */ *optr += 4; /* reserve 4 bytes for length */ - ret = hal_rpc_local_hash_dispatch.get_digest_algorithm_id(alg, *optr, &len, (size_t)len_max); + ret = hal_rpc_hash_get_digest_algorithm_id(alg, *optr, &len, (size_t)len_max); if (ret == HAL_OK) { *optr = optr_orig; check(hal_xdr_encode_int(optr, olimit, len)); @@ -227,9 +237,11 @@ static hal_error_t hash_get_algorithm(const uint8_t **iptr, const uint8_t * cons check(hal_xdr_decode_int(iptr, ilimit, &hash.handle)); /* call the local function */ - ret = hal_rpc_local_hash_dispatch.get_algorithm(hash, &alg); + ret = hal_rpc_hash_get_algorithm(hash, &alg); + if (ret == HAL_OK) check(hal_xdr_encode_int(optr, olimit, alg)); + return ret; } @@ -250,9 +262,11 @@ static hal_error_t hash_initialize(const uint8_t **iptr, const uint8_t * const i check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &key, &key_len)); /* call the local function */ - ret = hal_rpc_local_hash_dispatch.initialize(client, session, &hash, (hal_digest_algorithm_t)alg, key, (size_t)key_len); + ret = hal_rpc_hash_initialize(client, session, &hash, (hal_digest_algorithm_t)alg, key, (size_t)key_len); + if (ret == HAL_OK) check(hal_xdr_encode_int(optr, olimit, hash.handle)); + return ret; } @@ -270,7 +284,8 @@ static hal_error_t hash_update(const uint8_t **iptr, const uint8_t * const ilimi check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &data, &length)); /* call the local function */ - ret = hal_rpc_local_hash_dispatch.update(hash, data, (size_t)length); + ret = hal_rpc_hash_update(hash, data, (size_t)length); + return ret; } @@ -292,7 +307,7 @@ static hal_error_t hash_finalize(const uint8_t **iptr, const uint8_t * const ili /* call the local function */ /* get the data directly into the output buffer */ check(hal_xdr_encode_int(optr, olimit, length)); - ret = hal_rpc_local_hash_dispatch.finalize(hash, *optr, (size_t)length); + ret = hal_rpc_hash_finalize(hash, *optr, (size_t)length); if (ret == HAL_OK) *optr += pad(length); else @@ -309,8 +324,9 @@ static hal_error_t pkey_load(const uint8_t **iptr, const uint8_t * const ilimit, hal_pkey_handle_t pkey; uint32_t type; uint32_t curve; - const uint8_t *name, *der; - uint32_t name_len, der_len; + hal_uuid_t name; + const uint8_t *der; + uint32_t der_len; hal_key_flags_t flags; hal_error_t ret; @@ -318,39 +334,47 @@ static hal_error_t pkey_load(const uint8_t **iptr, const uint8_t * const ilimit, check(hal_xdr_decode_int(iptr, ilimit, &session.handle)); check(hal_xdr_decode_int(iptr, ilimit, &type)); check(hal_xdr_decode_int(iptr, ilimit, &curve)); - check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &name, &name_len)); check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &der, &der_len)); check(hal_xdr_decode_int(iptr, ilimit, &flags)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.load(client, session, &pkey, type, curve, name, name_len, der, der_len, flags); - if (ret == HAL_OK) - check(hal_xdr_encode_int(optr, olimit, pkey.handle)); + ret = hal_rpc_pkey_load(client, session, &pkey, type, curve, &name, der, der_len, flags); + + if (ret == HAL_OK) { + uint8_t *optr_orig = *optr; + if ((ret = hal_xdr_encode_int(optr, olimit, pkey.handle)) != HAL_OK || + (ret = hal_xdr_encode_buffer(optr, olimit, name.uuid, sizeof(name.uuid))) != HAL_OK) + *optr = optr_orig; + } + return ret; } -static hal_error_t pkey_find(const uint8_t **iptr, const uint8_t * const ilimit, +static hal_error_t pkey_open(const uint8_t **iptr, const uint8_t * const ilimit, uint8_t **optr, const uint8_t * const olimit) { hal_client_handle_t client; hal_session_handle_t session; hal_pkey_handle_t pkey; - uint32_t type; - const uint8_t *name; + const uint8_t *name_ptr; uint32_t name_len; hal_key_flags_t flags; hal_error_t ret; check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); check(hal_xdr_decode_int(iptr, ilimit, &session.handle)); - check(hal_xdr_decode_int(iptr, ilimit, &type)); - check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &name, &name_len)); + check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &name_ptr, &name_len)); check(hal_xdr_decode_int(iptr, ilimit, &flags)); + if (name_len != sizeof(hal_uuid_t)) + return HAL_ERROR_KEY_NAME_TOO_LONG; + /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.find(client, session, &pkey, type, name, name_len, flags); + ret = hal_rpc_pkey_open(client, session, &pkey, (const hal_uuid_t *) name_ptr, flags); + if (ret == HAL_OK) check(hal_xdr_encode_int(optr, olimit, pkey.handle)); + return ret; } @@ -360,8 +384,7 @@ static hal_error_t pkey_generate_rsa(const uint8_t **iptr, const uint8_t * const hal_client_handle_t client; hal_session_handle_t session; hal_pkey_handle_t pkey; - const uint8_t *name; - uint32_t name_len; + hal_uuid_t name; uint32_t key_len; const uint8_t *exp; uint32_t exp_len; @@ -370,15 +393,20 @@ static hal_error_t pkey_generate_rsa(const uint8_t **iptr, const uint8_t * const check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); check(hal_xdr_decode_int(iptr, ilimit, &session.handle)); - check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &name, &name_len)); check(hal_xdr_decode_int(iptr, ilimit, &key_len)); check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &exp, &exp_len)); check(hal_xdr_decode_int(iptr, ilimit, &flags)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.generate_rsa(client, session, &pkey, name, name_len, key_len, exp, exp_len, flags); - if (ret == HAL_OK) - check(hal_xdr_encode_int(optr, olimit, pkey.handle)); + ret = hal_rpc_pkey_generate_rsa(client, session, &pkey, &name, key_len, exp, exp_len, flags); + + if (ret == HAL_OK) { + uint8_t *optr_orig = *optr; + if ((ret = hal_xdr_encode_int(optr, olimit, pkey.handle)) != HAL_OK || + (ret = hal_xdr_encode_buffer(optr, olimit, name.uuid, sizeof(name.uuid))) != HAL_OK) + *optr = optr_orig; + } + return ret; } @@ -388,22 +416,26 @@ static hal_error_t pkey_generate_ec(const uint8_t **iptr, const uint8_t * const hal_client_handle_t client; hal_session_handle_t session; hal_pkey_handle_t pkey; - const uint8_t *name; - uint32_t name_len; + hal_uuid_t name; uint32_t curve; hal_key_flags_t flags; hal_error_t ret; check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); check(hal_xdr_decode_int(iptr, ilimit, &session.handle)); - check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &name, &name_len)); check(hal_xdr_decode_int(iptr, ilimit, &curve)); check(hal_xdr_decode_int(iptr, ilimit, &flags)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.generate_ec(client, session, &pkey, name, name_len, curve, flags); - if (ret == HAL_OK) - check(hal_xdr_encode_int(optr, olimit, pkey.handle)); + ret = hal_rpc_pkey_generate_ec(client, session, &pkey, &name, curve, flags); + + if (ret == HAL_OK) { + uint8_t *optr_orig = *optr; + if ((ret = hal_xdr_encode_int(optr, olimit, pkey.handle)) != HAL_OK || + (ret = hal_xdr_encode_buffer(optr, olimit, name.uuid, sizeof(name.uuid))) != HAL_OK) + *optr = optr_orig; + } + return ret; } @@ -418,7 +450,8 @@ static hal_error_t pkey_close(const uint8_t **iptr, const uint8_t * const ilimit check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.close(pkey); + ret = hal_rpc_pkey_close(pkey); + return ret; } @@ -433,43 +466,48 @@ static hal_error_t pkey_delete(const uint8_t **iptr, const uint8_t * const ilimi check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.delete(pkey); + ret = hal_rpc_pkey_delete(pkey); + return ret; } -static hal_error_t pkey_rename(const uint8_t **iptr, const uint8_t * const ilimit, - uint8_t **optr, const uint8_t * const olimit) +static hal_error_t pkey_get_key_type(const uint8_t **iptr, const uint8_t * const ilimit, + uint8_t **optr, const uint8_t * const olimit) { hal_client_handle_t client __attribute__((unused)); hal_pkey_handle_t pkey; - const uint8_t *name; - uint32_t name_len; + hal_key_type_t type; hal_error_t ret; check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); - check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &name, &name_len)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.rename(pkey, name, name_len); + ret = hal_rpc_pkey_get_key_type(pkey, &type); + + if (ret == HAL_OK) + check(hal_xdr_encode_int(optr, olimit, type)); + return ret; } -static hal_error_t pkey_get_key_type(const uint8_t **iptr, const uint8_t * const ilimit, +static hal_error_t pkey_get_key_curve(const uint8_t **iptr, const uint8_t * const ilimit, uint8_t **optr, const uint8_t * const olimit) { - hal_client_handle_t client __attribute__((unused)); + hal_client_handle_t client; hal_pkey_handle_t pkey; - hal_key_type_t type; + hal_curve_name_t curve; hal_error_t ret; check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.get_key_type(pkey, &type); + ret = hal_rpc_pkey_get_key_curve(pkey, &curve); + if (ret == HAL_OK) - check(hal_xdr_encode_int(optr, olimit, type)); + check(hal_xdr_encode_int(optr, olimit, curve)); + return ret; } @@ -485,9 +523,11 @@ static hal_error_t pkey_get_key_flags(const uint8_t **iptr, const uint8_t * cons check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.get_key_flags(pkey, &flags); + ret = hal_rpc_pkey_get_key_flags(pkey, &flags); + if (ret == HAL_OK) check(hal_xdr_encode_int(optr, olimit, flags)); + return ret; } @@ -502,8 +542,10 @@ static hal_error_t pkey_get_public_key_len(const uint8_t **iptr, const uint8_t * check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); /* call the local function */ - len = hal_rpc_local_pkey_dispatch.get_public_key_len(pkey); + len = hal_rpc_pkey_get_public_key_len(pkey); + check(hal_xdr_encode_int(optr, olimit, len)); + return HAL_OK; } @@ -527,7 +569,7 @@ static hal_error_t pkey_get_public_key(const uint8_t **iptr, const uint8_t * con /* call the local function */ /* get the data directly into the output buffer */ *optr += 4; /* reserve 4 bytes for length */ - ret = hal_rpc_local_pkey_dispatch.get_public_key(pkey, *optr, &len, len_max); + ret = hal_rpc_pkey_get_public_key(pkey, *optr, &len, len_max); if (ret == HAL_OK) { *optr = optr_orig; check(hal_xdr_encode_int(optr, olimit, len)); @@ -540,11 +582,10 @@ static hal_error_t pkey_get_public_key(const uint8_t **iptr, const uint8_t * con return ret; } -static hal_error_t pkey_remote_sign(const uint8_t **iptr, const uint8_t * const ilimit, - uint8_t **optr, const uint8_t * const olimit) +static hal_error_t pkey_sign(const uint8_t **iptr, const uint8_t * const ilimit, + uint8_t **optr, const uint8_t * const olimit) { - hal_client_handle_t client __attribute__((unused)); - hal_session_handle_t session; + hal_client_handle_t client; hal_pkey_handle_t pkey; hal_hash_handle_t hash; const uint8_t *input; @@ -555,7 +596,6 @@ static hal_error_t pkey_remote_sign(const uint8_t **iptr, const uint8_t * const hal_error_t ret; check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); - check(hal_xdr_decode_int(iptr, ilimit, &session.handle)); check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); check(hal_xdr_decode_int(iptr, ilimit, &hash.handle)); check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &input, &input_len)); @@ -567,7 +607,7 @@ static hal_error_t pkey_remote_sign(const uint8_t **iptr, const uint8_t * const /* call the local function */ /* get the data directly into the output buffer */ *optr += 4; /* reserve 4 bytes for length */ - ret = hal_rpc_local_pkey_dispatch.sign(session, pkey, hash, input, input_len, *optr, &sig_len, sig_max); + ret = hal_rpc_pkey_sign(pkey, hash, input, input_len, *optr, &sig_len, sig_max); *optr = optr_orig; if (ret == HAL_OK) { check(hal_xdr_encode_int(optr, olimit, sig_len)); @@ -576,11 +616,10 @@ static hal_error_t pkey_remote_sign(const uint8_t **iptr, const uint8_t * const return ret; } -static hal_error_t pkey_remote_verify(const uint8_t **iptr, const uint8_t * const ilimit, - uint8_t **optr, const uint8_t * const olimit) +static hal_error_t pkey_verify(const uint8_t **iptr, const uint8_t * const ilimit, + uint8_t **optr, const uint8_t * const olimit) { - hal_client_handle_t client __attribute__((unused)); - hal_session_handle_t session; + hal_client_handle_t client; hal_pkey_handle_t pkey; hal_hash_handle_t hash; const uint8_t *input; @@ -590,62 +629,160 @@ static hal_error_t pkey_remote_verify(const uint8_t **iptr, const uint8_t * cons hal_error_t ret; check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); - check(hal_xdr_decode_int(iptr, ilimit, &session.handle)); check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); check(hal_xdr_decode_int(iptr, ilimit, &hash.handle)); check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &input, &input_len)); check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &sig, &sig_len)); /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.verify(session, pkey, hash, input, input_len, sig, sig_len); + ret = hal_rpc_pkey_verify(pkey, hash, input, input_len, sig, sig_len); + return ret; } -static hal_error_t hal_xdr_encode_pkey_info(uint8_t **optr, const uint8_t * const olimit, const hal_pkey_info_t *info) +static hal_error_t pkey_match(const uint8_t **iptr, const uint8_t * const ilimit, + uint8_t **optr, const uint8_t * const olimit) { - uint8_t *optr_orig = *optr; + hal_client_handle_t client; + hal_session_handle_t session; + uint32_t type, curve, attributes_len, result_max, previous_uuid_len; + const uint8_t *previous_uuid_ptr; + hal_key_flags_t flags; hal_error_t ret; - if ((ret = hal_xdr_encode_int(optr, olimit, info->type)) != HAL_OK || - (ret = hal_xdr_encode_int(optr, olimit, info->curve)) != HAL_OK || - (ret = hal_xdr_encode_int(optr, olimit, info->flags)) != HAL_OK || - (ret = hal_xdr_encode_buffer(optr, olimit, (uint8_t *)&info->name[0], info->name_len)) != HAL_OK) - *optr = optr_orig; + check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); + check(hal_xdr_decode_int(iptr, ilimit, &session.handle)); + check(hal_xdr_decode_int(iptr, ilimit, &type)); + check(hal_xdr_decode_int(iptr, ilimit, &curve)); + check(hal_xdr_decode_int(iptr, ilimit, &flags)); + check(hal_xdr_decode_int(iptr, ilimit, &attributes_len)); + + hal_pkey_attribute_t attributes[attributes_len > 0 ? attributes_len : 1]; + + for (int i = 0; i < attributes_len; i++) { + hal_pkey_attribute_t *a = &attributes[i]; + const uint8_t *value; + uint32_t value_len; + check(hal_xdr_decode_int(iptr, ilimit, &a->type)); + check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &value, &value_len)); + a->value = value; + a->length = value_len; + } + + check(hal_xdr_decode_int(iptr, ilimit, &result_max)); + check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &previous_uuid_ptr, &previous_uuid_len)); + + if (previous_uuid_len != sizeof(hal_uuid_t)) + return HAL_ERROR_KEY_NAME_TOO_LONG; + + const hal_uuid_t * const previous_uuid = (const void *) previous_uuid_ptr; + + hal_uuid_t result[result_max]; + unsigned result_len; + + ret = hal_rpc_pkey_match(client, session, type, curve, flags, + attributes, attributes_len, + result, &result_len, result_max, + previous_uuid); + + if (ret == HAL_OK) { + uint8_t *optr_orig = *optr; + ret = hal_xdr_encode_int(optr, olimit, result_len); + for (int i = 0; ret == HAL_OK && i < result_len; ++i) + ret = hal_xdr_encode_buffer(optr, olimit, result[i].uuid, + sizeof(result[i].uuid)); + if (ret != HAL_OK) + *optr = optr_orig; + } + return ret; } +static hal_error_t pkey_set_attributes(const uint8_t **iptr, const uint8_t * const ilimit, + uint8_t **optr, const uint8_t * const olimit) +{ + hal_client_handle_t client; + hal_pkey_handle_t pkey; + uint32_t attributes_len; + hal_error_t ret; -static hal_error_t pkey_list(const uint8_t **iptr, const uint8_t * const ilimit, - uint8_t **optr, const uint8_t * const olimit) + check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); + check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); + check(hal_xdr_decode_int(iptr, ilimit, &attributes_len)); + + hal_pkey_attribute_t attributes[attributes_len > 0 ? attributes_len : 1]; + + for (int i = 0; i < attributes_len; i++) { + hal_pkey_attribute_t *a = &attributes[i]; + check(hal_xdr_decode_int(iptr, ilimit, &a->type)); + const uint8_t *iptr_prior_to_decoding_length = *iptr; + check(hal_xdr_decode_int(iptr, ilimit, &a->length)); + if (a->length == HAL_PKEY_ATTRIBUTE_NIL) { + a->value = NULL; + } + else { + *iptr = iptr_prior_to_decoding_length; + const uint8_t *value; + check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &value, &a->length)); + a->value = value; + } + } + + ret = hal_rpc_pkey_set_attributes(pkey, attributes, attributes_len); + + return ret; +} + +static hal_error_t pkey_get_attributes(const uint8_t **iptr, const uint8_t * const ilimit, + uint8_t **optr, const uint8_t * const olimit) { - hal_client_handle_t client __attribute__((unused)); + hal_client_handle_t client; + hal_pkey_handle_t pkey; + uint32_t attributes_len, u32; uint8_t *optr_orig = *optr; - uint32_t result_max; - hal_key_flags_t flags; hal_error_t ret; check(hal_xdr_decode_int(iptr, ilimit, &client.handle)); - check(hal_xdr_decode_int(iptr, ilimit, &result_max)); - check(hal_xdr_decode_int(iptr, ilimit, &flags)); + check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle)); + check(hal_xdr_decode_int(iptr, ilimit, &attributes_len)); - hal_pkey_info_t result[result_max]; - unsigned result_len; + hal_pkey_attribute_t attributes[attributes_len > 0 ? attributes_len : 1]; + + for (int i = 0; i < attributes_len; i++) + check(hal_xdr_decode_int(iptr, ilimit, &attributes[i].type)); + + check(hal_xdr_decode_int(iptr, ilimit, &u32)); + + const size_t attributes_buffer_len = u32; + + if (nargs(1 + 2 * attributes_len) + attributes_buffer_len > olimit - *optr) + return HAL_ERROR_RPC_PACKET_OVERFLOW; + + uint8_t attributes_buffer[attributes_buffer_len > 0 ? attributes_buffer_len : 1]; + + ret = hal_rpc_pkey_get_attributes(pkey, attributes, attributes_len, + attributes_buffer, attributes_buffer_len); - /* call the local function */ - ret = hal_rpc_local_pkey_dispatch.list(result, &result_len, result_max, flags); if (ret == HAL_OK) { - int i; - check(hal_xdr_encode_int(optr, olimit, result_len)); - for (i = 0; i < result_len; ++i) { - if ((ret = hal_xdr_encode_pkey_info(optr, olimit, &result[i])) != HAL_OK) { - *optr = optr_orig; + ret = hal_xdr_encode_int(optr, olimit, attributes_len); + for (int i = 0; ret == HAL_OK && i < attributes_len; i++) { + ret = hal_xdr_encode_int(optr, olimit, attributes[i].type); + if (ret != HAL_OK) break; - } + if (attributes_buffer_len == 0) + ret = hal_xdr_encode_int(optr, olimit, attributes[i].length); + else + ret = hal_xdr_encode_buffer(optr, olimit, attributes[i].value, attributes[i].length); } } + + if (ret != HAL_OK) + *optr = optr_orig; + return ret; } + hal_error_t hal_rpc_server_dispatch(const uint8_t * const ibuf, const size_t ilen, uint8_t * const obuf, size_t * const olen) { @@ -656,96 +793,108 @@ hal_error_t hal_rpc_server_dispatch(const uint8_t * const ibuf, const size_t ile uint32_t rpc_func_num; uint32_t client_handle; hal_error_t ret; + hal_error_t (*handler)(const uint8_t **iptr, const uint8_t * const ilimit, + uint8_t **optr, const uint8_t * const olimit) = NULL; check(hal_xdr_decode_int(&iptr, ilimit, &rpc_func_num)); check(hal_xdr_decode_int(&iptr, ilimit, &client_handle)); check(hal_xdr_undecode_int(&iptr)); + switch (rpc_func_num) { case RPC_FUNC_GET_VERSION: - ret = get_version(&iptr, ilimit, &optr, olimit); + handler = get_version; break; case RPC_FUNC_GET_RANDOM: - ret = get_random(&iptr, ilimit, &optr, olimit); + handler = get_random; break; case RPC_FUNC_SET_PIN: - ret = set_pin(&iptr, ilimit, &optr, olimit); + handler = set_pin; break; case RPC_FUNC_LOGIN: - ret = login(&iptr, ilimit, &optr, olimit); + handler = login; break; case RPC_FUNC_LOGOUT: - ret = logout(&iptr, ilimit, &optr, olimit); + handler = logout; break; case RPC_FUNC_LOGOUT_ALL: - ret = logout_all(&iptr, ilimit, &optr, olimit); + handler = logout_all; break; case RPC_FUNC_IS_LOGGED_IN: - ret = is_logged_in(&iptr, ilimit, &optr, olimit); + handler = is_logged_in; break; case RPC_FUNC_HASH_GET_DIGEST_LEN: - ret = hash_get_digest_len(&iptr, ilimit, &optr, olimit); + handler = hash_get_digest_len; break; case RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID: - ret = hash_get_digest_algorithm_id(&iptr, ilimit, &optr, olimit); + handler = hash_get_digest_algorithm_id; break; case RPC_FUNC_HASH_GET_ALGORITHM: - ret = hash_get_algorithm(&iptr, ilimit, &optr, olimit); + handler = hash_get_algorithm; break; case RPC_FUNC_HASH_INITIALIZE: - ret = hash_initialize(&iptr, ilimit, &optr, olimit); + handler = hash_initialize; break; case RPC_FUNC_HASH_UPDATE: - ret = hash_update(&iptr, ilimit, &optr, olimit); + handler = hash_update; break; case RPC_FUNC_HASH_FINALIZE: - ret = hash_finalize(&iptr, ilimit, &optr, olimit); + handler = hash_finalize; break; case RPC_FUNC_PKEY_LOAD: - ret = pkey_load(&iptr, ilimit, &optr, olimit); + handler = pkey_load; break; - case RPC_FUNC_PKEY_FIND: - ret = pkey_find(&iptr, ilimit, &optr, olimit); + case RPC_FUNC_PKEY_OPEN: + handler = pkey_open; break; case RPC_FUNC_PKEY_GENERATE_RSA: - ret = pkey_generate_rsa(&iptr, ilimit, &optr, olimit); + handler = pkey_generate_rsa; break; case RPC_FUNC_PKEY_GENERATE_EC: - ret = pkey_generate_ec(&iptr, ilimit, &optr, olimit); + handler = pkey_generate_ec; break; case RPC_FUNC_PKEY_CLOSE: - ret = pkey_close(&iptr, ilimit, &optr, olimit); + handler = pkey_close; break; case RPC_FUNC_PKEY_DELETE: - ret = pkey_delete(&iptr, ilimit, &optr, olimit); + handler = pkey_delete; break; case RPC_FUNC_PKEY_GET_KEY_TYPE: - ret = pkey_get_key_type(&iptr, ilimit, &optr, olimit); + handler = pkey_get_key_type; + break; + case RPC_FUNC_PKEY_GET_KEY_CURVE: + handler = pkey_get_key_curve; break; case RPC_FUNC_PKEY_GET_KEY_FLAGS: - ret = pkey_get_key_flags(&iptr, ilimit, &optr, olimit); + handler = pkey_get_key_flags; break; case RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN: - ret = pkey_get_public_key_len(&iptr, ilimit, &optr, olimit); + handler = pkey_get_public_key_len; break; case RPC_FUNC_PKEY_GET_PUBLIC_KEY: - ret = pkey_get_public_key(&iptr, ilimit, &optr, olimit); + handler = pkey_get_public_key; break; case RPC_FUNC_PKEY_SIGN: - ret = pkey_remote_sign(&iptr, ilimit, &optr, olimit); + handler = pkey_sign; break; case RPC_FUNC_PKEY_VERIFY: - ret = pkey_remote_verify(&iptr, ilimit, &optr, olimit); + handler = pkey_verify; break; - case RPC_FUNC_PKEY_LIST: - ret = pkey_list(&iptr, ilimit, &optr, olimit); + case RPC_FUNC_PKEY_MATCH: + handler = pkey_match; break; - case RPC_FUNC_PKEY_RENAME: - ret = pkey_rename(&iptr, ilimit, &optr, olimit); + case RPC_FUNC_PKEY_SET_ATTRIBUTES: + handler = pkey_set_attributes; break; - default: - ret = HAL_ERROR_RPC_BAD_FUNCTION; + case RPC_FUNC_PKEY_GET_ATTRIBUTES: + handler = pkey_get_attributes; break; } + + if (handler) + ret = handler(&iptr, ilimit, &optr, olimit); + else + ret = HAL_ERROR_RPC_BAD_FUNCTION; + /* Encode opcode, client ID, and response code at the beginning of the payload */ *olen = optr - obuf; optr = obuf; @@ -788,12 +937,26 @@ const hal_rpc_pkey_dispatch_t *hal_rpc_pkey_dispatch = &hal_rpc_local_pkey_dispa hal_error_t hal_rpc_server_init(void) { - return hal_rpc_server_transport_init(); + hal_error_t err; + + if ((err = hal_ks_init(hal_ks_volatile_driver, 1)) != HAL_OK || + (err = hal_ks_init(hal_ks_token_driver, 1)) != HAL_OK || + (err = hal_rpc_server_transport_init()) != HAL_OK) + return err; + + return HAL_OK; } hal_error_t hal_rpc_server_close(void) { - return hal_rpc_server_transport_close(); + hal_error_t err; + + if ((err = hal_rpc_server_transport_close()) != HAL_OK || + (err = hal_ks_shutdown(hal_ks_token_driver)) != HAL_OK || + (err = hal_ks_shutdown(hal_ks_volatile_driver)) != HAL_OK) + return err; + + return HAL_OK; } @@ -148,7 +148,7 @@ hal_error_t hal_slip_recv(uint8_t * const buf, size_t * const len, const size_t { int complete; hal_error_t ret; - + while (1) { ret = hal_slip_recv_char(buf, len, maxlen, &complete); if ((ret != HAL_OK) || complete) diff --git a/tests/test-bus.c b/tests/test-bus.c index b4a3e1c..94d7a70 100644 --- a/tests/test-bus.c +++ b/tests/test-bus.c @@ -68,7 +68,8 @@ static int sanity(const hal_core_t *board_core) } if (data != rnd) { - printf("Data bus fail: expected %08x, got %08x, diff %08x\n", rnd, data, data ^ rnd); + printf("Data bus fail: expected %08lx, got %08lx, diff %08lx\n", + (unsigned long) rnd, (unsigned long) data, (unsigned long) (data ^ rnd)); return 1; } diff --git a/tests/test-mkmif.c b/tests/test-mkmif.c index ab5801e..3645577 100644 --- a/tests/test-mkmif.c +++ b/tests/test-mkmif.c @@ -61,12 +61,12 @@ static hal_error_t write_test(hal_core_t *core) uint32_t write_address; int i; hal_error_t err; - + for (write_data = 0x01020304, write_address = 0, i = 0; i < 0x10; write_data += 0x01010101, write_address += 4, ++i) { - printf("Trying to write 0x%08x to memory address 0x%08x.\n", + printf("Trying to write 0x%08x to memory address 0x%08x.\n", (unsigned int)write_data, (unsigned int)write_address); if ((err = hal_mkmif_write_word(core, write_address, write_data)) != HAL_OK) { @@ -84,7 +84,7 @@ static hal_error_t read_test(hal_core_t *core) uint32_t read_address; int i; hal_error_t err; - + for (read_address = 0, i = 0; i < 0x10; read_address += 4, ++i) { @@ -115,7 +115,7 @@ static hal_error_t write_read_test(hal_core_t *core) printf("write error: %s\n", hal_error_string(err)); return err; } - + if ((err = hal_mkmif_read_word(core, 0x00000000, &readback)) != HAL_OK) { printf("read error: %s\n", hal_error_string(err)); return err; @@ -125,7 +125,7 @@ static hal_error_t write_read_test(hal_core_t *core) printf("read %08x, expected %08x\n", (unsigned int)readback, (unsigned int)data); return HAL_ERROR_IO_UNEXPECTED; } - + return HAL_OK; } diff --git a/tests/test-rpc_bighash.c b/tests/test-rpc_bighash.c index e18d9b0..823baf6 100644 --- a/tests/test-rpc_bighash.c +++ b/tests/test-rpc_bighash.c @@ -108,7 +108,7 @@ int main (int argc, char *argv[]) expected = NULL; check(hal_rpc_client_init()); - check(hal_rpc_hash_initialize(client, session, &hash, hal_digest_algorithm_sha256, NULL, 0)); + check(hal_rpc_hash_initialize(client, session, &hash, HAL_DIGEST_ALGORITHM_SHA256, NULL, 0)); for (int i = 0; i < iterations; ++i) { check(hal_rpc_hash_update(hash, block, sizeof(block))); diff --git a/tests/test-rpc_hash.c b/tests/test-rpc_hash.c index a0c571c..24f8ede 100644 --- a/tests/test-rpc_hash.c +++ b/tests/test-rpc_hash.c @@ -656,52 +656,52 @@ int main (int argc, char *argv[]) ok &= hal_rpc_client_init(); - ok &= test_hash(hal_digest_algorithm_sha1, nist_512_single, sha1_single_digest, "SHA-1 single block"); - ok &= test_hash(hal_digest_algorithm_sha1, nist_512_double, sha1_double_digest, "SHA-1 double block"); - - ok &= test_hash(hal_digest_algorithm_sha256, nist_512_single, sha256_single_digest, "SHA-256 single block"); - ok &= test_hash(hal_digest_algorithm_sha256, nist_512_double, sha256_double_digest, "SHA-256 double block"); - - ok &= test_hash(hal_digest_algorithm_sha512_224, nist_1024_single, sha512_224_single_digest, "SHA-512/224 single block"); - ok &= test_hash(hal_digest_algorithm_sha512_224, nist_1024_double, sha512_224_double_digest, "SHA-512/224 double block"); - - ok &= test_hash(hal_digest_algorithm_sha512_256, nist_1024_single, sha512_256_single_digest, "SHA-512/256 single block"); - ok &= test_hash(hal_digest_algorithm_sha512_256, nist_1024_double, sha512_256_double_digest, "SHA-512/256 double block"); - - ok &= test_hash(hal_digest_algorithm_sha384, nist_1024_single, sha384_single_digest, "SHA-384 single block"); - ok &= test_hash(hal_digest_algorithm_sha384, nist_1024_double, sha384_double_digest, "SHA-384 double block"); - - ok &= test_hash(hal_digest_algorithm_sha512, nist_1024_single, sha512_single_digest, "SHA-512 single block"); - ok &= test_hash(hal_digest_algorithm_sha512, nist_1024_double, sha512_double_digest, "SHA-512 double block"); - - ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_1_key, hmac_sha1_tc_1_data, hmac_sha1_tc_1_result_sha1, "HMAC-SHA-1 test case 1"); - ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_2_key, hmac_sha1_tc_2_data, hmac_sha1_tc_2_result_sha1, "HMAC-SHA-1 test case 2"); - ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_3_key, hmac_sha1_tc_3_data, hmac_sha1_tc_3_result_sha1, "HMAC-SHA-1 test case 3"); - ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_4_key, hmac_sha1_tc_4_data, hmac_sha1_tc_4_result_sha1, "HMAC-SHA-1 test case 4"); - ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_5_key, hmac_sha1_tc_5_data, hmac_sha1_tc_5_result_sha1, "HMAC-SHA-1 test case 5"); - ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_6_key, hmac_sha1_tc_6_data, hmac_sha1_tc_6_result_sha1, "HMAC-SHA-1 test case 6"); - ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_7_key, hmac_sha1_tc_7_data, hmac_sha1_tc_7_result_sha1, "HMAC-SHA-1 test case 7"); - - ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha256, "HMAC-SHA-256 test case 1"); - ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha256, "HMAC-SHA-256 test case 2"); - ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha256, "HMAC-SHA-256 test case 3"); - ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha256, "HMAC-SHA-256 test case 4"); - ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha256, "HMAC-SHA-256 test case 6"); - ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha256, "HMAC-SHA-256 test case 7"); - - ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha384, "HMAC-SHA-384 test case 1"); - ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha384, "HMAC-SHA-384 test case 2"); - ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha384, "HMAC-SHA-384 test case 3"); - ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha384, "HMAC-SHA-384 test case 4"); - ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha384, "HMAC-SHA-384 test case 6"); - ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha384, "HMAC-SHA-384 test case 7"); - - ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha512, "HMAC-SHA-512 test case 1"); - ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha512, "HMAC-SHA-512 test case 2"); - ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha512, "HMAC-SHA-512 test case 3"); - ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha512, "HMAC-SHA-512 test case 4"); - ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha512, "HMAC-SHA-512 test case 6"); - ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha512, "HMAC-SHA-512 test case 7"); + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA1, nist_512_single, sha1_single_digest, "SHA-1 single block"); + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA1, nist_512_double, sha1_double_digest, "SHA-1 double block"); + + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA256, nist_512_single, sha256_single_digest, "SHA-256 single block"); + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA256, nist_512_double, sha256_double_digest, "SHA-256 double block"); + + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA512_224, nist_1024_single, sha512_224_single_digest, "SHA-512/224 single block"); + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA512_224, nist_1024_double, sha512_224_double_digest, "SHA-512/224 double block"); + + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA512_256, nist_1024_single, sha512_256_single_digest, "SHA-512/256 single block"); + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA512_256, nist_1024_double, sha512_256_double_digest, "SHA-512/256 double block"); + + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA384, nist_1024_single, sha384_single_digest, "SHA-384 single block"); + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA384, nist_1024_double, sha384_double_digest, "SHA-384 double block"); + + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA512, nist_1024_single, sha512_single_digest, "SHA-512 single block"); + ok &= test_hash(HAL_DIGEST_ALGORITHM_SHA512, nist_1024_double, sha512_double_digest, "SHA-512 double block"); + + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA1, hmac_sha1_tc_1_key, hmac_sha1_tc_1_data, hmac_sha1_tc_1_result_sha1, "HMAC-SHA-1 test case 1"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA1, hmac_sha1_tc_2_key, hmac_sha1_tc_2_data, hmac_sha1_tc_2_result_sha1, "HMAC-SHA-1 test case 2"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA1, hmac_sha1_tc_3_key, hmac_sha1_tc_3_data, hmac_sha1_tc_3_result_sha1, "HMAC-SHA-1 test case 3"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA1, hmac_sha1_tc_4_key, hmac_sha1_tc_4_data, hmac_sha1_tc_4_result_sha1, "HMAC-SHA-1 test case 4"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA1, hmac_sha1_tc_5_key, hmac_sha1_tc_5_data, hmac_sha1_tc_5_result_sha1, "HMAC-SHA-1 test case 5"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA1, hmac_sha1_tc_6_key, hmac_sha1_tc_6_data, hmac_sha1_tc_6_result_sha1, "HMAC-SHA-1 test case 6"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA1, hmac_sha1_tc_7_key, hmac_sha1_tc_7_data, hmac_sha1_tc_7_result_sha1, "HMAC-SHA-1 test case 7"); + + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA256, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha256, "HMAC-SHA-256 test case 1"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA256, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha256, "HMAC-SHA-256 test case 2"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA256, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha256, "HMAC-SHA-256 test case 3"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA256, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha256, "HMAC-SHA-256 test case 4"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA256, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha256, "HMAC-SHA-256 test case 6"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA256, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha256, "HMAC-SHA-256 test case 7"); + + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA384, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha384, "HMAC-SHA-384 test case 1"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA384, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha384, "HMAC-SHA-384 test case 2"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA384, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha384, "HMAC-SHA-384 test case 3"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA384, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha384, "HMAC-SHA-384 test case 4"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA384, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha384, "HMAC-SHA-384 test case 6"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA384, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha384, "HMAC-SHA-384 test case 7"); + + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA512, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha512, "HMAC-SHA-512 test case 1"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA512, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha512, "HMAC-SHA-512 test case 2"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA512, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha512, "HMAC-SHA-512 test case 3"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA512, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha512, "HMAC-SHA-512 test case 4"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA512, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha512, "HMAC-SHA-512 test case 6"); + ok &= test_hmac(HAL_DIGEST_ALGORITHM_SHA512, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha512, "HMAC-SHA-512 test case 7"); ok &= hal_rpc_client_close(); diff --git a/tests/test-rpc_pkey.c b/tests/test-rpc_pkey.c index f6b6f15..c07a318 100644 --- a/tests/test-rpc_pkey.c +++ b/tests/test-rpc_pkey.c @@ -44,6 +44,8 @@ #include "test-rsa.h" #include "test-ecdsa.h" +#define lose(...) do { printf(__VA_ARGS__); goto fail; } while (0) + static inline const char *ecdsa_curve_to_string(const hal_curve_name_t curve) { switch (curve) { @@ -54,287 +56,452 @@ static inline const char *ecdsa_curve_to_string(const hal_curve_name_t curve) } } -static int test_rsa_testvec(const rsa_tc_t * const tc) +static int test_attributes(const hal_pkey_handle_t pkey, + const hal_uuid_t * const name, + const hal_key_flags_t flags) +{ + static const size_t sizes[] = { 32, 100, 260, 1000, 2000, 0 }; + static const char format[] = "Test attribute %lu"; + + hal_error_t err; + + for (const size_t *size = sizes; *size; size++) { + uint8_t buf_1[*size], buf_2[*size]; + memset(buf_1, 0x55, sizeof(buf_1)); + snprintf((char *) buf_1, sizeof(buf_1), format, (unsigned long) *size); + hal_pkey_attribute_t attr_set = { .type = *size, .length = sizeof(buf_1), .value = buf_1 }; + hal_pkey_attribute_t attr_get = { .type = *size }; + hal_pkey_attribute_t attr_del = { .type = *size, .length = HAL_PKEY_ATTRIBUTE_NIL }; + + if ((err = hal_rpc_pkey_set_attributes(pkey, &attr_set, 1)) != HAL_OK) + lose("Could not set attribute %lu: %s\n", + (unsigned long) *size, hal_error_string(err)); + + if ((err = hal_rpc_pkey_get_attributes(pkey, &attr_get, 1, buf_2, sizeof(buf_2))) != HAL_OK) + lose("Could not get attribute %lu: %s\n", + (unsigned long) *size, hal_error_string(err)); + + if (attr_get.length != *size) + lose("Unexpected size returned for attribute %lu: %lu\n", + (unsigned long) *size, (unsigned long) attr_get.length); + + if ((err = hal_rpc_pkey_set_attributes(pkey, &attr_del, 1)) != HAL_OK) + lose("Could not delete attribute %lu: %s\n", + (unsigned long) *size, hal_error_string(err)); + + if ((err = hal_rpc_pkey_set_attributes(pkey, &attr_set, 1)) != HAL_OK) + lose("Could not (re)set attribute %lu: %s\n", + (unsigned long) *size, hal_error_string(err)); + } + + { + const hal_client_handle_t client = {HAL_HANDLE_NONE}; + const hal_session_handle_t session = {HAL_HANDLE_NONE}; + hal_uuid_t result[10], previous_uuid = {{0}}; + unsigned result_len; + + if ((err = hal_rpc_pkey_match(client, session, HAL_KEY_TYPE_NONE, HAL_CURVE_NONE, flags, NULL, 0, + result, &result_len, sizeof(result)/sizeof(*result), + &previous_uuid)) != HAL_OK) + lose("Unrestricted match() failed: %s\n", hal_error_string(err)); + + if (result_len == 0) + lose("Unrestricted match found no results\n"); + + for (const size_t *size = sizes; *size; size++) { + uint8_t buf[*size]; + memset(buf, 0x55, sizeof(buf)); + snprintf((char *) buf, sizeof(buf), format, (unsigned long) *size); + hal_pkey_attribute_t attribute[1] = {{ *size, sizeof(buf), buf }}; + + if ((err = hal_rpc_pkey_match(client, session, HAL_KEY_TYPE_NONE, HAL_CURVE_NONE, flags, + attribute, sizeof(attribute)/sizeof(*attribute), + result, &result_len, sizeof(result)/sizeof(*result), + &previous_uuid)) != HAL_OK) + lose("Restricted match() for attribute %lu failed: %s\n", + (unsigned long) *size, hal_error_string(err)); + + if (result_len == 0) + lose("Restricted match for attribute %lu found no results\n", (unsigned long) *size); + } + +#warning More hal_rpc_pkey_match() testing here. + + } + + return 1; + + fail: + return 0; +} + +static int test_rsa_testvec(const rsa_tc_t * const tc, hal_key_flags_t flags) { - const hal_client_handle_t client = {0}; - const hal_session_handle_t session = {0}; - hal_pkey_handle_t private_key, public_key; + const hal_client_handle_t client = {HAL_HANDLE_NONE}; + const hal_session_handle_t session = {HAL_HANDLE_NONE}; + hal_pkey_handle_t private_key = {HAL_HANDLE_NONE}; + hal_pkey_handle_t public_key = {HAL_HANDLE_NONE}; hal_error_t err; size_t len; assert(tc != NULL); - printf("Starting %lu-bit RSA test vector tests\n", (unsigned long) tc->size); + { + flags |= HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE; - uint8_t tc_keybuf[hal_rsa_key_t_size]; - hal_rsa_key_t *tc_key = NULL; + printf("Starting %lu-bit RSA test vector tests, flags 0x%lx\n", + (unsigned long) tc->size, (unsigned long) flags); - if ((err = hal_rsa_key_load_private(&tc_key, - tc_keybuf, sizeof(tc_keybuf), - tc->n.val, tc->n.len, - tc->e.val, tc->e.len, - tc->d.val, tc->d.len, - tc->p.val, tc->p.len, - tc->q.val, tc->q.len, - tc->u.val, tc->u.len, - tc->dP.val, tc->dP.len, - tc->dQ.val, tc->dQ.len)) != HAL_OK) - return printf("Could not load RSA private key from test vector: %s\n", hal_error_string(err)), 0; + uint8_t tc_keybuf[hal_rsa_key_t_size]; + hal_rsa_key_t *tc_key = NULL; - const uint8_t private_label[] = "RSA private key", public_label[] = "RSA public key"; + if ((err = hal_rsa_key_load_private(&tc_key, + tc_keybuf, sizeof(tc_keybuf), + tc->n.val, tc->n.len, + tc->e.val, tc->e.len, + tc->d.val, tc->d.len, + tc->p.val, tc->p.len, + tc->q.val, tc->q.len, + tc->u.val, tc->u.len, + tc->dP.val, tc->dP.len, + tc->dQ.val, tc->dQ.len)) != HAL_OK) + lose("Could not load RSA private key from test vector: %s\n", hal_error_string(err)); - uint8_t private_der[hal_rsa_private_key_to_der_len(tc_key)]; - uint8_t public_der[hal_rsa_public_key_to_der_len(tc_key)]; + hal_uuid_t private_name, public_name; - if ((err = hal_rsa_private_key_to_der(tc_key, private_der, &len, sizeof(private_der))) != HAL_OK) - return printf("Could not DER encode private key from test vector: %s\n", hal_error_string(err)), 0; + uint8_t private_der[hal_rsa_private_key_to_der_len(tc_key)]; + uint8_t public_der[hal_rsa_public_key_to_der_len(tc_key)]; - assert(len == sizeof(private_der)); + if ((err = hal_rsa_private_key_to_der(tc_key, private_der, &len, sizeof(private_der))) != HAL_OK) + lose("Could not DER encode private key from test vector: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_load(client, session, &private_key, HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE, - private_label, sizeof(private_label), private_der, sizeof(private_der), - HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)) != HAL_OK) - return printf("Could not load private key into RPC: %s\n", hal_error_string(err)), 0; + assert(len == sizeof(private_der)); - if ((err = hal_rsa_public_key_to_der(tc_key, public_der, &len, sizeof(public_der))) != HAL_OK) - return printf("Could not DER encode public key from test vector: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_load(client, session, &private_key, HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE, + &private_name, private_der, sizeof(private_der), flags)) != HAL_OK) + lose("Could not load private key into RPC: %s\n", hal_error_string(err)); - assert(len == sizeof(public_der)); + if ((err = hal_rsa_public_key_to_der(tc_key, public_der, &len, sizeof(public_der))) != HAL_OK) + lose("Could not DER encode public key from test vector: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_load(client, session, &public_key, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, - public_label, sizeof(public_label), public_der, sizeof(public_der), - HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)) != HAL_OK) - return printf("Could not load public key into RPC: %s\n", hal_error_string(err)), 0; + assert(len == sizeof(public_der)); - uint8_t sig[tc->s.len]; + if ((err = hal_rpc_pkey_load(client, session, &public_key, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, + &public_name, public_der, sizeof(public_der), flags)) != HAL_OK) + lose("Could not load public key into RPC: %s\n", hal_error_string(err)); - /* - * Raw RSA test cases include PKCS #1.5 padding, we need to drill down to the DigestInfo. - */ - assert(tc->m.len > 4 && tc->m.val[0] == 0x00 && tc->m.val[1] == 0x01 && tc->m.val[2] == 0xff); - const uint8_t *digestinfo = memchr(tc->m.val + 2, 0x00, tc->m.len - 2); - assert(digestinfo != NULL); - const size_t digestinfo_len = tc->m.val + tc->m.len - ++digestinfo; + uint8_t sig[tc->s.len]; - if ((err = hal_rpc_pkey_sign(session, private_key, hal_hash_handle_none, - digestinfo, digestinfo_len, sig, &len, sizeof(sig))) != HAL_OK) - return printf("Could not sign: %s\n", hal_error_string(err)), 0; + /* + * Raw RSA test cases include PKCS #1.5 padding, we need to drill down to the DigestInfo. + */ + assert(tc->m.len > 4 && tc->m.val[0] == 0x00 && tc->m.val[1] == 0x01 && tc->m.val[2] == 0xff); + const uint8_t *digestinfo = memchr(tc->m.val + 2, 0x00, tc->m.len - 2); + assert(digestinfo != NULL); + const size_t digestinfo_len = tc->m.val + tc->m.len - ++digestinfo; - if (tc->s.len != len || memcmp(sig, tc->s.val, tc->s.len) != 0) - return printf("MISMATCH\n"), 0; + if ((err = hal_rpc_pkey_sign(private_key, hal_hash_handle_none, + digestinfo, digestinfo_len, sig, &len, sizeof(sig))) != HAL_OK) + lose("Could not sign: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_verify(session, public_key, hal_hash_handle_none, - digestinfo, digestinfo_len, tc->s.val, tc->s.len)) != HAL_OK) - return printf("Could not verify: %s\n", hal_error_string(err)), 0; + if (tc->s.len != len || memcmp(sig, tc->s.val, tc->s.len) != 0) + lose("MISMATCH\n"); - if ((err = hal_rpc_pkey_delete(private_key)) != HAL_OK) - return printf("Could not delete private key: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_verify(public_key, hal_hash_handle_none, + digestinfo, digestinfo_len, tc->s.val, tc->s.len)) != HAL_OK) + lose("Could not verify: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_delete(public_key)) != HAL_OK) - return printf("Could not delete public key: %s\n", hal_error_string(err)), 0; + if (!test_attributes(private_key, &private_name, flags) || !test_attributes(public_key, &public_name, flags)) + goto fail; - printf("OK\n"); - return 1; + if ((err = hal_rpc_pkey_delete(private_key)) != HAL_OK) + lose("Could not delete private key: %s\n", hal_error_string(err)); + + if ((err = hal_rpc_pkey_delete(public_key)) != HAL_OK) + lose("Could not delete public key: %s\n", hal_error_string(err)); + + printf("OK\n"); + return 1; + } + + fail: + if (private_key.handle != HAL_HANDLE_NONE && + (err = hal_rpc_pkey_delete(private_key)) != HAL_OK) + printf("Warning: could not delete private key: %s\n", hal_error_string(err)); + + if (public_key.handle != HAL_HANDLE_NONE && + (err = hal_rpc_pkey_delete(public_key)) != HAL_OK) + printf("Warning: could not delete public key: %s\n", hal_error_string(err)); + + return 0; } -static int test_ecdsa_testvec(const ecdsa_tc_t * const tc) +static int test_ecdsa_testvec(const ecdsa_tc_t * const tc, hal_key_flags_t flags) { - const hal_client_handle_t client = {0}; - const hal_session_handle_t session = {0}; - hal_pkey_handle_t private_key, public_key; + const hal_client_handle_t client = {HAL_HANDLE_NONE}; + const hal_session_handle_t session = {HAL_HANDLE_NONE}; + hal_pkey_handle_t private_key = {HAL_HANDLE_NONE}; + hal_pkey_handle_t public_key = {HAL_HANDLE_NONE}; hal_error_t err; size_t len; assert(tc != NULL); - printf("Starting ECDSA %s test vector tests\n", ecdsa_curve_to_string(tc->curve)); + { + flags |= HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE; - uint8_t tc_keybuf[hal_ecdsa_key_t_size]; - hal_ecdsa_key_t *tc_key = NULL; + printf("Starting ECDSA %s test vector tests, flags 0x%lx\n", + ecdsa_curve_to_string(tc->curve), (unsigned long) flags); - if ((err = hal_ecdsa_key_load_private(&tc_key, tc_keybuf, sizeof(tc_keybuf), tc->curve, - tc->Qx, tc->Qx_len, tc->Qy, tc->Qy_len, - tc->d, tc->d_len)) != HAL_OK) - return printf("Could not load ECDSA private key from test vector: %s\n", hal_error_string(err)), 0; + uint8_t tc_keybuf[hal_ecdsa_key_t_size]; + hal_ecdsa_key_t *tc_key = NULL; - const uint8_t private_label[] = "ECDSA private key", public_label[] = "ECDSA public key"; + if ((err = hal_ecdsa_key_load_private(&tc_key, tc_keybuf, sizeof(tc_keybuf), tc->curve, + tc->Qx, tc->Qx_len, tc->Qy, tc->Qy_len, + tc->d, tc->d_len)) != HAL_OK) + lose("Could not load ECDSA private key from test vector: %s\n", hal_error_string(err)); - uint8_t private_der[hal_ecdsa_private_key_to_der_len(tc_key)]; - uint8_t public_der[hal_ecdsa_public_key_to_der_len(tc_key)]; + hal_uuid_t private_name, public_name; - if ((err = hal_ecdsa_private_key_to_der(tc_key, private_der, &len, sizeof(private_der))) != HAL_OK) - return printf("Could not DER encode private key from test vector: %s\n", hal_error_string(err)), 0; + uint8_t private_der[hal_ecdsa_private_key_to_der_len(tc_key)]; + uint8_t public_der[hal_ecdsa_public_key_to_der_len(tc_key)]; - assert(len == sizeof(private_der)); + if ((err = hal_ecdsa_private_key_to_der(tc_key, private_der, &len, sizeof(private_der))) != HAL_OK) + lose("Could not DER encode private key from test vector: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_load(client, session, &private_key, HAL_KEY_TYPE_EC_PRIVATE, tc->curve, - private_label, sizeof(private_label), private_der, sizeof(private_der), - HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)) != HAL_OK) - return printf("Could not load private key into RPC: %s\n", hal_error_string(err)), 0; + assert(len == sizeof(private_der)); - if ((err = hal_ecdsa_public_key_to_der(tc_key, public_der, &len, sizeof(public_der))) != HAL_OK) - return printf("Could not DER encode public key from test vector: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_load(client, session, &private_key, HAL_KEY_TYPE_EC_PRIVATE, tc->curve, + &private_name, private_der, sizeof(private_der), flags)) != HAL_OK) + lose("Could not load private key into RPC: %s\n", hal_error_string(err)); - assert(len == sizeof(public_der)); + if ((err = hal_ecdsa_public_key_to_der(tc_key, public_der, &len, sizeof(public_der))) != HAL_OK) + lose("Could not DER encode public key from test vector: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_load(client, session, &public_key, HAL_KEY_TYPE_EC_PUBLIC, tc->curve, - public_label, sizeof(public_label), public_der, sizeof(public_der), - HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)) != HAL_OK) - return printf("Could not load public key into RPC: %s\n", hal_error_string(err)), 0; + assert(len == sizeof(public_der)); - if ((err = hal_rpc_pkey_verify(session, public_key, hal_hash_handle_none, - tc->H, tc->H_len, tc->sig, tc->sig_len)) != HAL_OK) - return printf("Could not verify signature from test vector: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_load(client, session, &public_key, HAL_KEY_TYPE_EC_PUBLIC, tc->curve, + &public_name, public_der, sizeof(public_der), flags)) != HAL_OK) + lose("Could not load public key into RPC: %s\n", hal_error_string(err)); - uint8_t sig[tc->sig_len + 4]; + if ((err = hal_rpc_pkey_verify(public_key, hal_hash_handle_none, + tc->H, tc->H_len, tc->sig, tc->sig_len)) != HAL_OK) + lose("Could not verify signature from test vector: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_sign(session, private_key, hal_hash_handle_none, - tc->H, tc->H_len, sig, &len, sizeof(sig))) != HAL_OK) - return printf("Could not sign: %s\n", hal_error_string(err)), 0; + uint8_t sig[tc->sig_len + 4]; - if ((err = hal_rpc_pkey_verify(session, public_key, hal_hash_handle_none, - tc->H, tc->H_len, sig, len)) != HAL_OK) - return printf("Could not verify own signature: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_sign(private_key, hal_hash_handle_none, + tc->H, tc->H_len, sig, &len, sizeof(sig))) != HAL_OK) + lose("Could not sign: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_delete(private_key)) != HAL_OK) - return printf("Could not delete private key: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_verify(public_key, hal_hash_handle_none, + tc->H, tc->H_len, sig, len)) != HAL_OK) + lose("Could not verify own signature: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_delete(public_key)) != HAL_OK) - return printf("Could not delete public key: %s\n", hal_error_string(err)), 0; + if (!test_attributes(private_key, &private_name, flags) || !test_attributes(public_key, &public_name, flags)) + goto fail; - printf("OK\n"); - return 1; + if ((err = hal_rpc_pkey_delete(private_key)) != HAL_OK) + lose("Could not delete private key: %s\n", hal_error_string(err)); + + if ((err = hal_rpc_pkey_delete(public_key)) != HAL_OK) + lose("Could not delete public key: %s\n", hal_error_string(err)); + + printf("OK\n"); + return 1; + } + + fail: + if (private_key.handle != HAL_HANDLE_NONE && + (err = hal_rpc_pkey_delete(private_key)) != HAL_OK) + printf("Warning: could not delete private key: %s\n", hal_error_string(err)); + + if (public_key.handle != HAL_HANDLE_NONE && + (err = hal_rpc_pkey_delete(public_key)) != HAL_OK) + printf("Warning: could not delete public key: %s\n", hal_error_string(err)); + + return 0; } -static int test_rsa_generate(const rsa_tc_t * const tc) +static int test_rsa_generate(const rsa_tc_t * const tc, hal_key_flags_t flags) { - const hal_client_handle_t client = {0}; - const hal_session_handle_t session = {0}; - hal_pkey_handle_t private_key, public_key; + const hal_client_handle_t client = {HAL_HANDLE_NONE}; + const hal_session_handle_t session = {HAL_HANDLE_NONE}; + hal_pkey_handle_t private_key = {HAL_HANDLE_NONE}; + hal_pkey_handle_t public_key = {HAL_HANDLE_NONE}; hal_error_t err; size_t len; assert(tc != NULL); - printf("Starting %lu-bit RSA key generation tests\n", (unsigned long) tc->size); + { + flags |= HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE; - const uint8_t private_label[] = "Generated RSA private key", public_label[] = "Generated RSA public key"; + printf("Starting %lu-bit RSA key generation tests, flags 0x%lx\n", + (unsigned long) tc->size, (unsigned long) flags); - if ((err = hal_rpc_pkey_generate_rsa(client, session, &private_key, private_label, sizeof(private_label), - tc->size, tc->e.val, tc->e.len, - HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)) != HAL_OK) - return printf("Could not generate RSA private key: %s\n", hal_error_string(err)), 0; + hal_uuid_t private_name, public_name; - uint8_t public_der[hal_rpc_pkey_get_public_key_len(private_key)]; + if ((err = hal_rpc_pkey_generate_rsa(client, session, &private_key, &private_name, + tc->size, tc->e.val, tc->e.len, flags)) != HAL_OK) + lose("Could not generate RSA private key: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_get_public_key(private_key, public_der, &len, sizeof(public_der))) != HAL_OK) - return printf("Could not DER encode RPC RSA public key from RPC RSA private key: %s\n", hal_error_string(err)), 0; + uint8_t public_der[hal_rpc_pkey_get_public_key_len(private_key)]; - assert(len == sizeof(public_der)); + if ((err = hal_rpc_pkey_get_public_key(private_key, public_der, &len, sizeof(public_der))) != HAL_OK) + lose("Could not DER encode RPC RSA public key from RPC RSA private key: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_load(client, session, &public_key, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, - public_label, sizeof(public_label), public_der, sizeof(public_der), - HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)) != HAL_OK) - return printf("Could not load public key into RPC: %s\n", hal_error_string(err)), 0; + assert(len == sizeof(public_der)); - uint8_t sig[tc->s.len]; + if ((err = hal_rpc_pkey_load(client, session, &public_key, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, + &public_name, public_der, sizeof(public_der), flags)) != HAL_OK) + lose("Could not load public key into RPC: %s\n", hal_error_string(err)); - /* - * Raw RSA test cases include PKCS #1.5 padding, we need to drill down to the DigestInfo. - */ - assert(tc->m.len > 4 && tc->m.val[0] == 0x00 && tc->m.val[1] == 0x01 && tc->m.val[2] == 0xff); - const uint8_t *digestinfo = memchr(tc->m.val + 2, 0x00, tc->m.len - 2); - assert(digestinfo != NULL); - const size_t digestinfo_len = tc->m.val + tc->m.len - ++digestinfo; + uint8_t sig[tc->s.len]; - if ((err = hal_rpc_pkey_sign(session, private_key, hal_hash_handle_none, - digestinfo, digestinfo_len, sig, &len, sizeof(sig))) != HAL_OK) - return printf("Could not sign: %s\n", hal_error_string(err)), 0; + /* + * Raw RSA test cases include PKCS #1.5 padding, we need to drill down to the DigestInfo. + */ + assert(tc->m.len > 4 && tc->m.val[0] == 0x00 && tc->m.val[1] == 0x01 && tc->m.val[2] == 0xff); + const uint8_t *digestinfo = memchr(tc->m.val + 2, 0x00, tc->m.len - 2); + assert(digestinfo != NULL); + const size_t digestinfo_len = tc->m.val + tc->m.len - ++digestinfo; - if ((err = hal_rpc_pkey_verify(session, public_key, hal_hash_handle_none, - digestinfo, digestinfo_len, sig, len)) != HAL_OK) - return printf("Could not verify: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_sign(private_key, hal_hash_handle_none, + digestinfo, digestinfo_len, sig, &len, sizeof(sig))) != HAL_OK) + lose("Could not sign: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_delete(private_key)) != HAL_OK) - return printf("Could not delete private key: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_verify(public_key, hal_hash_handle_none, + digestinfo, digestinfo_len, sig, len)) != HAL_OK) + lose("Could not verify: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_delete(public_key)) != HAL_OK) - return printf("Could not delete public key: %s\n", hal_error_string(err)), 0; + if (!test_attributes(private_key, &private_name, flags) || !test_attributes(public_key, &public_name, flags)) + goto fail; - printf("OK\n"); - return 1; + if ((err = hal_rpc_pkey_delete(private_key)) != HAL_OK) + lose("Could not delete private key: %s\n", hal_error_string(err)); + + if ((err = hal_rpc_pkey_delete(public_key)) != HAL_OK) + lose("Could not delete public key: %s\n", hal_error_string(err)); + + printf("OK\n"); + return 1; + } + + fail: + if (private_key.handle != HAL_HANDLE_NONE && + (err = hal_rpc_pkey_delete(private_key)) != HAL_OK) + printf("Warning: could not delete private key: %s\n", hal_error_string(err)); + + if (public_key.handle != HAL_HANDLE_NONE && + (err = hal_rpc_pkey_delete(public_key)) != HAL_OK) + printf("Warning: could not delete public key: %s\n", hal_error_string(err)); + + return 0; } -static int test_ecdsa_generate(const ecdsa_tc_t * const tc) +static int test_ecdsa_generate(const ecdsa_tc_t * const tc, hal_key_flags_t flags) { - const hal_client_handle_t client = {0}; - const hal_session_handle_t session = {0}; - hal_pkey_handle_t private_key, public_key; + const hal_client_handle_t client = {HAL_HANDLE_NONE}; + const hal_session_handle_t session = {HAL_HANDLE_NONE}; + hal_pkey_handle_t private_key = {HAL_HANDLE_NONE}; + hal_pkey_handle_t public_key = {HAL_HANDLE_NONE}; hal_error_t err; size_t len; assert(tc != NULL); - printf("Starting ECDSA %s key generation tests\n", ecdsa_curve_to_string(tc->curve)); + { + flags |= HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE; - const uint8_t private_label[] = "Generated ECDSA private key", public_label[] = "Generated ECDSA public key"; + printf("Starting ECDSA %s key generation tests, flags 0x%lx\n", + ecdsa_curve_to_string(tc->curve), (unsigned long) flags); - if ((err = hal_rpc_pkey_generate_ec(client, session, &private_key, - private_label, sizeof(private_label), - tc->curve, HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)) != HAL_OK) - return printf("Could not generate EC key pair: %s\n", hal_error_string(err)), 0; + hal_uuid_t private_name, public_name; - uint8_t public_der[hal_rpc_pkey_get_public_key_len(private_key)]; + if ((err = hal_rpc_pkey_generate_ec(client, session, &private_key, &private_name, tc->curve, flags)) != HAL_OK) + lose("Could not generate EC key pair: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_get_public_key(private_key, public_der, &len, sizeof(public_der))) != HAL_OK) - return printf("Could not DER encode public key from test vector: %s\n", hal_error_string(err)), 0; + uint8_t public_der[hal_rpc_pkey_get_public_key_len(private_key)]; - assert(len == sizeof(public_der)); + if ((err = hal_rpc_pkey_get_public_key(private_key, public_der, &len, sizeof(public_der))) != HAL_OK) + lose("Could not DER encode public key from test vector: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_load(client, session, &public_key, HAL_KEY_TYPE_EC_PUBLIC, tc->curve, - public_label, sizeof(public_label), public_der, sizeof(public_der), - HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE)) != HAL_OK) - return printf("Could not load public key into RPC: %s\n", hal_error_string(err)), 0; + assert(len == sizeof(public_der)); - uint8_t sig[tc->sig_len + 4]; + if ((err = hal_rpc_pkey_load(client, session, &public_key, HAL_KEY_TYPE_EC_PUBLIC, tc->curve, + &public_name, public_der, sizeof(public_der), flags)) != HAL_OK) + lose("Could not load public key into RPC: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_sign(session, private_key, hal_hash_handle_none, - tc->H, tc->H_len, sig, &len, sizeof(sig))) != HAL_OK) - return printf("Could not sign: %s\n", hal_error_string(err)), 0; + uint8_t sig[tc->sig_len + 4]; - if ((err = hal_rpc_pkey_verify(session, public_key, hal_hash_handle_none, - tc->H, tc->H_len, sig, len)) != HAL_OK) - return printf("Could not verify own signature: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_sign(private_key, hal_hash_handle_none, + tc->H, tc->H_len, sig, &len, sizeof(sig))) != HAL_OK) + lose("Could not sign: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_delete(private_key)) != HAL_OK) - return printf("Could not delete private key: %s\n", hal_error_string(err)), 0; + if ((err = hal_rpc_pkey_verify(public_key, hal_hash_handle_none, + tc->H, tc->H_len, sig, len)) != HAL_OK) + lose("Could not verify own signature: %s\n", hal_error_string(err)); - if ((err = hal_rpc_pkey_delete(public_key)) != HAL_OK) - return printf("Could not delete public key: %s\n", hal_error_string(err)), 0; + if (!test_attributes(private_key, &private_name, flags) || !test_attributes(public_key, &public_name, flags)) + goto fail; - printf("OK\n"); - return 1; + if ((err = hal_rpc_pkey_delete(private_key)) != HAL_OK) + lose("Could not delete private key: %s\n", hal_error_string(err)); + + if ((err = hal_rpc_pkey_delete(public_key)) != HAL_OK) + lose("Could not delete public key: %s\n", hal_error_string(err)); + + printf("OK\n"); + return 1; + } + + fail: + if (private_key.handle != HAL_HANDLE_NONE && + (err = hal_rpc_pkey_delete(private_key)) != HAL_OK) + printf("Warning: could not delete private key: %s\n", hal_error_string(err)); + + if (public_key.handle != HAL_HANDLE_NONE && + (err = hal_rpc_pkey_delete(public_key)) != HAL_OK) + printf("Warning: could not delete public key: %s\n", hal_error_string(err)); + + return 0; } int main (int argc, char *argv[]) { + const hal_client_handle_t client = {HAL_HANDLE_NONE}; + const char *pin = argc > 1 ? argv[1] : "fnord"; + hal_error_t err; int ok = 1; - hal_rpc_client_init(); + if ((err = hal_rpc_client_init()) != HAL_OK) + printf("Warning: Trouble initializing RPC client: %s\n", hal_error_string(err)); + + if ((err = hal_rpc_login(client, HAL_USER_NORMAL, pin, strlen(pin))) != HAL_OK) + printf("Warning: Trouble logging into HSM: %s\n", hal_error_string(err)); for (int i = 0; i < (sizeof(rsa_tc)/sizeof(*rsa_tc)); i++) - ok &= test_rsa_testvec(&rsa_tc[i]); + for (int j = 0; j < 2; j++) + ok &= test_rsa_testvec(&rsa_tc[i], j * HAL_KEY_FLAG_TOKEN); for (int i = 0; i < (sizeof(ecdsa_tc)/sizeof(*ecdsa_tc)); i++) - ok &= test_ecdsa_testvec(&ecdsa_tc[i]); + for (int j = 0; j < 2; j++) + ok &= test_ecdsa_testvec(&ecdsa_tc[i], j * HAL_KEY_FLAG_TOKEN); for (int i = 0; i < (sizeof(rsa_tc)/sizeof(*rsa_tc)); i++) - ok &= test_rsa_generate(&rsa_tc[i]); + for (int j = 0; j < 2; j++) + ok &= test_rsa_generate(&rsa_tc[i], j * HAL_KEY_FLAG_TOKEN); for (int i = 0; i < (sizeof(ecdsa_tc)/sizeof(*ecdsa_tc)); i++) - ok &= test_ecdsa_generate(&ecdsa_tc[i]); + for (int j = 0; j < 2; j++) + ok &= test_ecdsa_generate(&ecdsa_tc[i], j * HAL_KEY_FLAG_TOKEN); + + if ((err = hal_rpc_logout(client)) != HAL_OK) + printf("Warning: Trouble logging out of HSM: %s\n", hal_error_string(err)); - ok &= hal_rpc_client_close(); + if ((err = hal_rpc_client_close()) != HAL_OK) + printf("Warning: Trouble shutting down RPC client: %s\n", hal_error_string(err)); return !ok; } diff --git a/tests/test-trng.c b/tests/test-trng.c index ebfe701..f570752 100644 --- a/tests/test-trng.c +++ b/tests/test-trng.c @@ -84,7 +84,7 @@ static hal_error_t test_random(const char *name) return err; } - printf("%08x ", rnd); + printf("%08lx ", (unsigned long) rnd); } printf("\n"); @@ -106,7 +106,7 @@ int main(void) } else { for (i = 0; i < 8; ++i) { - printf("%08x ", rnd[i]); + printf("%08lx ", (unsigned long) rnd[i]); } printf("\n"); } diff --git a/unit-tests.py b/unit-tests.py new file mode 100644 index 0000000..a8779c5 --- /dev/null +++ b/unit-tests.py @@ -0,0 +1,1035 @@ +#!/usr/bin/env python + +# 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. + +""" +LibHAL unit tests, using libhal.py and the Python unit_test framework. +""" + +# There's some overlap between these tests and the PKCS #11 unit tests, +# because in many cases we're testing the same functionality, just via +# different APIs. + +import unittest +import datetime +import sys + +from libhal import * + +try: + from Crypto.Util.number import inverse + from Crypto.PublicKey import RSA + from Crypto.Signature import PKCS1_v1_5 + from Crypto.Hash.SHA256 import SHA256Hash as SHA256 + from Crypto.Hash.SHA384 import SHA384Hash as SHA384 + from Crypto.Hash.SHA512 import SHA512Hash as SHA512 + pycrypto_loaded = True +except ImportError: + pycrypto_loaded = False + + +try: + from ecdsa.keys import SigningKey as ECDSA_SigningKey, VerifyingKey as ECDSA_VerifyingKey + from ecdsa.ellipticcurve import Point + from ecdsa.curves import NIST256p, NIST384p, NIST521p + if not pycrypto_loaded: + from hashlib import sha256 as SHA256, sha384 as SHA384, sha512 as SHA512 + ecdsa_loaded = True +except ImportError: + ecdsa_loaded = False + + +def log(msg): + if not args.quiet: + sys.stderr.write(msg) + sys.stderr.write("\n") + + +def main(): + from sys import argv + global args + args = parse_arguments(argv[1:]) + argv = argv[:1] + args.only_test + unittest.main(verbosity = 1 if args.quiet else 2, argv = argv, catchbreak = True, testRunner = TextTestRunner) + +def parse_arguments(argv = ()): + from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter + parser = ArgumentParser(description = __doc__, formatter_class = ArgumentDefaultsHelpFormatter) + parser.add_argument("--quiet", action = "store_true", help = "suppress chatter") + parser.add_argument("--wheel-pin", default = "fnord", help = "PIN for wheel user") + parser.add_argument("--so-pin", default = "fnord", help = "PIN for security officer") + parser.add_argument("--user-pin", default = "fnord", help = "PIN for normal user") + parser.add_argument("--all-tests", action = "store_true", help = "enable tests usually skipped") + parser.add_argument("--only-test", default = [], nargs = "+", help = "only run tests named here") + return parser.parse_args(argv) + +args = parse_arguments() +hsm = None + +pin_map = { HAL_USER_NORMAL : "user_pin", HAL_USER_SO : "so_pin", HAL_USER_WHEEL : "wheel_pin" } + + +def setUpModule(): + global hsm + hsm = HSM() + +def tearDownModule(): + hsm.logout() + #hsm.close() + + +# Subclass a few bits of unittest to add timing reports for individual tests. + +class TestCase(unittest.TestCase): + + def setUp(self): + super(TestCase, self).setUp() + self.startTime = datetime.datetime.now() + + def tearDown(self): + self.endTime = datetime.datetime.now() + super(TestCase, self).tearDown() + +class TextTestResult(unittest.TextTestResult): + + def addSuccess(self, test): + if self.showAll and hasattr(test, "startTime") and hasattr(test, "endTime"): + self.stream.write("runtime {} ... ".format(test.endTime - test.startTime)) + self.stream.flush() + super(TextTestResult, self).addSuccess(test) + +class TextTestRunner(unittest.TextTestRunner): + resultclass = TextTestResult + + +# Tests below here + + +class TestBasic(TestCase): + """ + Test basic functions that don't involve keys, digests, or PINs. + """ + + def test_get_version(self): + version = hsm.get_version() + # Might want to inspect the result here + self.assertIsInstance(version, int) + + def test_get_random(self): + length = 32 + random = hsm.get_random(length) + self.assertIsInstance(random, str) + self.assertEqual(length, len(random)) + + +class TestPIN(TestCase): + """ + Test functions involving PINs. + """ + + def setUp(self): + hsm.logout() + super(TestPIN, self).setUp() + + def tearDown(self): + super(TestPIN, self).tearDown() + hsm.logout() + + def test_is_logged_in(self): + for user in pin_map: + self.assertRaises(HAL_ERROR_FORBIDDEN, hsm.is_logged_in, user) + + def login_logout(self, user1): + pin = getattr(args, pin_map[user1]) + hsm.login(user1, pin) + for user2 in pin_map: + if user2 == user1: + hsm.is_logged_in(user2) + else: + self.assertRaises(HAL_ERROR_FORBIDDEN, hsm.is_logged_in, user2) + hsm.logout() + + def test_login_wheel(self): + self.login_logout(HAL_USER_WHEEL) + + def test_login_so(self): + self.login_logout(HAL_USER_SO) + + def test_login_user(self): + self.login_logout(HAL_USER_NORMAL) + + # Eventually we will want a test of set_pin(), probably under a + # @unittest.skipUnless to prevent it from being run unless the + # user requests it. Punt that one for the moment. + + +class TestDigest(TestCase): + """ + Test digest/HMAC functions. + """ + + # Should use NIST test vectors, this is just a placeholder. + + def test_basic_hash(self): + h = hsm.hash_initialize(HAL_DIGEST_ALGORITHM_SHA256) + h.update("Hi, Mom") + h.finalize() + + def test_basic_hmac(self): + h = hsm.hash_initialize(HAL_DIGEST_ALGORITHM_SHA256, key = "secret") + h.update("Hi, Dad") + h.finalize() + + +# Will need something to test for pkey access when not logged in +# properly (ie, test that we get an appropriate exception under a long +# list of screwy conditions and that we don't get it under another +# long list of screwy conditions, due to the PKCS #11 compatible +# access check semantics). Defer for now. + + +class TestCaseLoggedIn(TestCase): + """ + Abstract class to handle login for PKey tests. + """ + + @classmethod + def setUpClass(cls): + hsm.login(HAL_USER_NORMAL, args.user_pin) + + @classmethod + def tearDownClass(cls): + hsm.logout() + + +class TestPKeyGen(TestCaseLoggedIn): + """ + Tests involving key generation. + """ + + def sign_verify(self, hashalg, k1, k2): + h = hsm.hash_initialize(hashalg) + h.update("Your mother was a hamster") + data = h.finalize() + sig = k1.sign(data = data) + k1.verify(signature = sig, data = data) + k2.verify(signature = sig, data = data) + + def gen_sign_verify_rsa(self, hashalg, keylen): + k1 = hsm.pkey_generate_rsa(keylen) + self.addCleanup(k1.delete) + k2 = hsm.pkey_load(HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, k1.public_key) + self.addCleanup(k2.delete) + self.sign_verify(hashalg, k1, k2) + + def gen_sign_verify_ecdsa(self, hashalg, curve): + k1 = hsm.pkey_generate_ec(curve) + self.addCleanup(k1.delete) + k2 = hsm.pkey_load(HAL_KEY_TYPE_EC_PUBLIC, curve, k1.public_key) + self.addCleanup(k2.delete) + self.sign_verify(hashalg, k1, k2) + + def test_gen_sign_verify_ecdsa_p256_sha256(self): + self.gen_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA256, HAL_CURVE_P256) + + def test_gen_sign_verify_ecdsa_p384_sha384(self): + self.gen_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA384, HAL_CURVE_P384) + + def test_gen_sign_verify_ecdsa_p521_sha512(self): + self.gen_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521) + + def test_gen_sign_verify_rsa_1024_p256_sha256(self): + self.gen_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024) + + @unittest.skipUnless(args.all_tests, "Slow") + def test_gen_sign_verify_rsa_2048_sha384(self): + self.gen_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048) + + @unittest.skipUnless(args.all_tests, "Hideously slow") + def test_gen_sign_verify_rsa_4096_sha512(self): + self.gen_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096) + + def test_gen_unsupported_length(self): + with self.assertRaises(HAL_ERROR_BAD_ARGUMENTS): + hsm.pkey_generate_rsa(1028).delete() + +class TestPKeyHashing(TestCaseLoggedIn): + """ + Tests involving various ways of doing the hashing for public key operations. + """ + + def load_sign_verify_rsa(self, alg, keylen, method): + k1 = hsm.pkey_load(HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE, + PreloadedKey.db[HAL_KEY_TYPE_RSA_PRIVATE, keylen].der) + self.addCleanup(k1.delete) + k2 = hsm.pkey_load(HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, + PreloadedKey.db[HAL_KEY_TYPE_RSA_PUBLIC, keylen].der) + self.addCleanup(k2.delete) + method(alg, k1, k2) + + def load_sign_verify_ecdsa(self, alg, curve, method): + k1 = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, curve, + PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, curve].der) + self.addCleanup(k1.delete) + k2 = hsm.pkey_load(HAL_KEY_TYPE_EC_PUBLIC, curve, + PreloadedKey.db[HAL_KEY_TYPE_EC_PUBLIC, curve].der) + self.addCleanup(k2.delete) + method(alg, k1, k2) + + @staticmethod + def h(alg, mixed_mode = False): + h = hsm.hash_initialize(alg, mixed_mode = mixed_mode) + h.update("Your mother was a hamster") + return h + + def sign_verify_data(self, alg, k1, k2): + data = self.h(alg, mixed_mode = True).finalize() + sig = k1.sign(data = data) + k1.verify(signature = sig, data = data) + k2.verify(signature = sig, data = data) + + def sign_verify_remote_remote(self, alg, k1, k2): + sig = k1.sign(hash = self.h(alg, mixed_mode = False)) + k1.verify(signature = sig, hash = self.h(alg, mixed_mode = False)) + k2.verify(signature = sig, hash = self.h(alg, mixed_mode = False)) + + def sign_verify_remote_local(self, alg, k1, k2): + sig = k1.sign(hash = self.h(alg, mixed_mode = False)) + k1.verify(signature = sig, hash = self.h(alg, mixed_mode = True)) + k2.verify(signature = sig, hash = self.h(alg, mixed_mode = True)) + + def sign_verify_local_remote(self, alg, k1, k2): + sig = k1.sign(hash = self.h(alg, mixed_mode = True)) + k1.verify(signature = sig, hash = self.h(alg, mixed_mode = False)) + k2.verify(signature = sig, hash = self.h(alg, mixed_mode = False)) + + def sign_verify_local_local(self, alg, k1, k2): + sig = k1.sign(hash = self.h(alg, mixed_mode = True)) + k1.verify(signature = sig, hash = self.h(alg, mixed_mode = True)) + k2.verify(signature = sig, hash = self.h(alg, mixed_mode = True)) + + def test_load_sign_verify_rsa_1024_sha256_data(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_data) + + def test_load_sign_verify_rsa_2048_sha384_data(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_data) + + def test_load_sign_verify_rsa_4096_sha512_data(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_data) + + def test_load_sign_verify_ecdsa_p256_sha256_data(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA256, HAL_CURVE_P256, self.sign_verify_data) + + def test_load_sign_verify_ecdsa_p384_sha384_data(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA384, HAL_CURVE_P384, self.sign_verify_data) + + def test_load_sign_verify_ecdsa_p521_sha512_data(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_data) + + def test_load_sign_verify_rsa_1024_sha256_remote_remote(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_remote_remote) + + def test_load_sign_verify_rsa_2048_sha384_remote_remote(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_remote_remote) + + def test_load_sign_verify_rsa_4096_sha512_remote_remote(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_remote_remote) + + def test_load_sign_verify_ecdsa_p256_sha256_remote_remote(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA256, HAL_CURVE_P256, self.sign_verify_remote_remote) + + def test_load_sign_verify_ecdsa_p384_sha384_remote_remote(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA384, HAL_CURVE_P384, self.sign_verify_remote_remote) + + def test_load_sign_verify_ecdsa_p521_sha512_remote_remote(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_remote_remote) + + def test_load_sign_verify_rsa_1024_sha256_remote_local(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_remote_local) + + def test_load_sign_verify_rsa_2048_sha384_remote_local(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_remote_local) + + def test_load_sign_verify_rsa_4096_sha512_remote_local(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_remote_local) + + def test_load_sign_verify_ecdsa_p256_sha256_remote_local(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA256, HAL_CURVE_P256, self.sign_verify_remote_local) + + def test_load_sign_verify_ecdsa_p384_sha384_remote_local(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA384, HAL_CURVE_P384, self.sign_verify_remote_local) + + def test_load_sign_verify_ecdsa_p521_sha512_remote_local(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_remote_local) + + def test_load_sign_verify_rsa_1024_sha256_local_remote(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_local_remote) + + def test_load_sign_verify_rsa_2048_sha384_local_remote(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_local_remote) + + def test_load_sign_verify_rsa_4096_sha512_local_remote(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_local_remote) + + def test_load_sign_verify_ecdsa_p256_sha256_local_remote(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA256, HAL_CURVE_P256, self.sign_verify_local_remote) + + def test_load_sign_verify_ecdsa_p384_sha384_local_remote(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA384, HAL_CURVE_P384, self.sign_verify_local_remote) + + def test_load_sign_verify_ecdsa_p521_sha512_local_remote(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_local_remote) + + def test_load_sign_verify_rsa_1024_sha256_local_local(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, 1024, self.sign_verify_local_local) + + def test_load_sign_verify_rsa_2048_sha384_local_local(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, 2048, self.sign_verify_local_local) + + def test_load_sign_verify_rsa_4096_sha512_local_local(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, 4096, self.sign_verify_local_local) + + def test_load_sign_verify_ecdsa_p256_sha256_local_local(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA256, HAL_CURVE_P256, self.sign_verify_local_local) + + def test_load_sign_verify_ecdsa_p384_sha384_local_local(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA384, HAL_CURVE_P384, self.sign_verify_local_local) + + def test_load_sign_verify_ecdsa_p521_sha512_local_local(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, HAL_CURVE_P521, self.sign_verify_local_local) + + +@unittest.skipUnless(pycrypto_loaded, "Requires Python Crypto package") +class TestPKeyRSAInterop(TestCaseLoggedIn): + + @staticmethod + def h(alg, text): + h = hsm.hash_initialize(alg, mixed_mode = True) + h.update(text) + return h + + def load_sign_verify_rsa(self, alg, pyhash, keylen): + hamster = "Your mother was a hamster" + sk = PreloadedKey.db[HAL_KEY_TYPE_RSA_PRIVATE, keylen] + vk = PreloadedKey.db[HAL_KEY_TYPE_RSA_PUBLIC, keylen] + k1 = hsm.pkey_load(HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE, sk.der) + self.addCleanup(k1.delete) + k2 = hsm.pkey_load(HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, vk.der) + self.addCleanup(k2.delete) + sig1 = k1.sign(hash = self.h(alg, hamster)) + sig2 = sk.sign(hamster, pyhash) + self.assertEqual(sig1, sig2) + k1.verify(signature = sig2, hash = self.h(alg, hamster)) + k2.verify(signature = sig2, hash = self.h(alg, hamster)) + sk.verify(hamster, pyhash, sig1) + vk.verify(hamster, pyhash, sig1) + + def test_interop_rsa_1024_sha256(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA256, SHA256, 1024) + + def test_interop_rsa_2048_sha384(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA384, SHA384, 2048) + + def test_interop_rsa_4096_sha512(self): + self.load_sign_verify_rsa(HAL_DIGEST_ALGORITHM_SHA512, SHA512, 4096) + + +@unittest.skipUnless(ecdsa_loaded, "Requires Python ECDSA package") +class TestPKeyECDSAInterop(TestCaseLoggedIn): + + @staticmethod + def h(alg, text): + h = hsm.hash_initialize(alg, mixed_mode = True) + h.update(text) + return h + + def load_sign_verify_ecdsa(self, alg, pyhash, curve): + hamster = "Your mother was a hamster" + sk = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, curve] + vk = PreloadedKey.db[HAL_KEY_TYPE_EC_PUBLIC, curve] + k1 = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, curve, sk.der) + self.addCleanup(k1.delete) + k2 = hsm.pkey_load(HAL_KEY_TYPE_EC_PUBLIC, curve, vk.der) + self.addCleanup(k2.delete) + sig1 = k1.sign(hash = self.h(alg, hamster)) + sig2 = sk.sign(hamster, pyhash) + k1.verify(signature = sig2, hash = self.h(alg, hamster)) + k2.verify(signature = sig2, hash = self.h(alg, hamster)) + vk.verify(hamster, pyhash, sig1) + + def test_interop_ecdsa_p256_sha256(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA256, SHA256, HAL_CURVE_P256) + + def test_interop_ecdsa_p384_sha384(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA384, SHA384, HAL_CURVE_P384) + + def test_interop_ecdsa_p521_sha512(self): + self.load_sign_verify_ecdsa(HAL_DIGEST_ALGORITHM_SHA512, SHA512, HAL_CURVE_P521) + + +class TestPKeyList(TestCaseLoggedIn): + """ + Tests involving PKey list and match functions. + """ + + def load_keys(self, flags): + uuids = set() + for obj in PreloadedKey.db.itervalues(): + with hsm.pkey_load(obj.keytype, obj.curve, obj.der, flags) as k: + self.addCleanup(lambda uuid: hsm.pkey_open(uuid, flags = flags).delete(), k.uuid) + uuids.add(k.uuid) + k.set_attributes(dict((i, a) for i, a in enumerate((str(obj.keytype), str(obj.fn2))))) + return uuids + + def match(self, flags, **kwargs): + uuids = kwargs.pop("uuids", None) + kwargs.update(flags = flags) + n = 0 + for uuid in hsm.pkey_match(**kwargs): + if uuids is None or uuid in uuids: + with hsm.pkey_open(uuid, flags) as k: + n += 1 + yield n, k + + def ks_match(self, flags): + tags = [] + uuids = set() + for i in xrange(2): + uuids |= self.load_keys(flags) + tags.extend(PreloadedKey.db) + self.assertEqual(len(tags), len(uuids)) + + matched_uuids = set(k.uuid for n, k in self.match(flags = flags)) + self.assertGreaterEqual(matched_uuids, uuids) + + for keytype in set(HALKeyType.index.itervalues()) - {HAL_KEY_TYPE_NONE}: + for n, k in self.match(flags = flags, uuids = uuids, type = keytype): + self.assertEqual(k.key_type, keytype) + self.assertEqual(k.get_attributes({0}).pop(0), str(keytype)) + self.assertEqual(n, sum(1 for t1, t2 in tags if t1 == keytype)) + + for curve in set(HALCurve.index.itervalues()) - {HAL_CURVE_NONE}: + for n, k in self.match(flags = flags, uuids = uuids, curve = curve): + self.assertEqual(k.key_curve, curve) + self.assertEqual(k.get_attributes({1}).pop(1), str(curve)) + self.assertIn(k.key_type, (HAL_KEY_TYPE_EC_PUBLIC, + HAL_KEY_TYPE_EC_PRIVATE)) + self.assertEqual(n, sum(1 for t1, t2 in tags if t2 == curve)) + + for keylen in set(kl for kt, kl in tags if not isinstance(kl, Enum)): + for n, k in self.match(flags = flags, uuids = uuids, + attributes = {1 : str(keylen)}): + self.assertEqual(keylen, int(k.get_attributes({1}).pop(1))) + self.assertIn(k.key_type, (HAL_KEY_TYPE_RSA_PUBLIC, + HAL_KEY_TYPE_RSA_PRIVATE)) + self.assertEqual(n, sum(1 for t1, t2 in tags + if not isinstance(t2, Enum) and t2 == keylen)) + + for n, k in self.match(flags = flags, uuids = uuids, + type = HAL_KEY_TYPE_RSA_PUBLIC, attributes = {1 : "2048"}): + self.assertEqual(k.key_type, HAL_KEY_TYPE_RSA_PUBLIC) + self.assertEqual(n, sum(1 for t1, t2 in tags + if t1 == HAL_KEY_TYPE_RSA_PUBLIC and t2 == 2048)) + + def test_ks_match_token(self): + self.ks_match(HAL_KEY_FLAG_TOKEN) + + def test_ks_match_volatile(self): + self.ks_match(0) + + +class TestPKeyAttribute(TestCaseLoggedIn): + """ + Attribute creation/lookup/deletion tests. + """ + + def load_and_fill(self, flags, n_keys = 1, n_attrs = 2, n_fill = 0): + pinwheel = Pinwheel() + for i in xrange(n_keys): + for obj in PreloadedKey.db.itervalues(): + with hsm.pkey_load(obj.keytype, obj.curve, obj.der, flags) as k: + pinwheel() + self.addCleanup(lambda uuid: hsm.pkey_open(uuid, flags = flags).delete(), k.uuid) + k.set_attributes(dict((j, "Attribute {}{}".format(j, "*" * n_fill)) + for j in xrange(n_attrs))) + pinwheel() + + def test_attribute_bloat_volatile_many(self): + self.load_and_fill(0, n_attrs = 128) # 192 + + def test_attribute_bloat_volatile_big(self): + self.load_and_fill(0, n_attrs = 6, n_fill = 512) + + def test_attribute_bloat_token_many(self): + self.load_and_fill(HAL_KEY_FLAG_TOKEN, n_attrs = 128) + + def test_attribute_bloat_token_big(self): + self.load_and_fill(HAL_KEY_FLAG_TOKEN, n_attrs = 4, n_fill = 512) # [16, 1024] + + +class TestPKeyAttributeP11(TestCaseLoggedIn): + """ + Attribute creation/lookup/deletion tests based on a PKCS #11 trace. + """ + + def setUp(self): + der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der + self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, HAL_KEY_FLAG_TOKEN) + self.addCleanup(self.k.delete) + super(TestPKeyAttributeP11, self).setUp() + + def test_set_many_attributes(self): + self.k.set_attributes({ + 0x001 : "\x01", + 0x108 : "\x01", + 0x105 : "\x00", + 0x002 : "\x01", + 0x107 : "\x00", + 0x102 : "\x45\x43\x2d\x50\x32\x35\x36", + 0x003 : "\x45\x43\x2d\x50\x32\x35\x36", + 0x162 : "\x00", + 0x103 : "\x01", + 0x000 : "\x03\x00\x00\x00", + 0x100 : "\x03\x00\x00\x00", + 0x101 : "", + 0x109 : "\x00", + 0x10c : "\x00", + 0x110 : "", + 0x111 : "", + 0x163 : "\x00", + 0x166 : "\xff\xff\xff\xff", + 0x170 : "\x01", + 0x210 : "\x00", + 0x163 : "\x01", + 0x166 : "\x40\x10\x00\x00", + 0x180 : "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" }) + + def test_set_many_attributes_with_deletions(self): + self.k.set_attributes({ + 0x001 : "\x01", + 0x108 : "\x01", + 0x105 : "\x00", + 0x002 : "\x01", + 0x107 : "\x00", + 0x102 : "\x45\x43\x2d\x50\x32\x35\x36", + 0x003 : "\x45\x43\x2d\x50\x32\x35\x36", + 0x162 : "\x00", + 0x103 : "\x01", + 0x000 : "\x03\x00\x00\x00", + 0x100 : "\x03\x00\x00\x00", + 0x101 : None, + 0x109 : "\x00", + 0x10c : "\x00", + 0x110 : None, + 0x111 : None, + 0x163 : "\x00", + 0x166 : "\xff\xff\xff\xff", + 0x170 : "\x01", + 0x210 : "\x00", + 0x163 : "\x01", + 0x166 : "\x40\x10\x00\x00", + 0x180 : "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" }) + + +class TestPKeyAttributeWriteSpeedToken(TestCaseLoggedIn): + """ + Attribute speed tests. + """ + + def setUp(self): + der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der + self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, HAL_KEY_FLAG_TOKEN) + self.addCleanup(self.k.delete) + super(TestPKeyAttributeWriteSpeedToken, self).setUp() + + def set_attributes(self, n_attrs): + self.k.set_attributes(dict((i, "Attribute {}".format(i)) + for i in xrange(n_attrs))) + + def test_set_1_attribute(self): + self.set_attributes(1) + + def test_set_6_attributes(self): + self.set_attributes(6) + + def test_set_12_attributes(self): + self.set_attributes(12) + +class TestPKeyAttributeWriteSpeedVolatile(TestCaseLoggedIn): + """ + Attribute speed tests. + """ + + def setUp(self): + der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der + self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, 0) + self.addCleanup(self.k.delete) + super(TestPKeyAttributeWriteSpeedVolatile, self).setUp() + + def set_attributes(self, n_attrs): + self.k.set_attributes(dict((i, "Attribute {}".format(i)) + for i in xrange(n_attrs))) + + def test_set_1_attribute(self): + self.set_attributes(1) + + def test_set_6_attributes(self): + self.set_attributes(6) + + def test_set_12_attributes(self): + self.set_attributes(12) + +class TestPKeyAttributeReadSpeedToken(TestCaseLoggedIn): + """ + Attribute speed tests. + """ + + def setUp(self): + der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der + self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, HAL_KEY_FLAG_TOKEN) + self.addCleanup(self.k.delete) + self.k.set_attributes(dict((i, "Attribute {}".format(i)) + for i in xrange(12))) + super(TestPKeyAttributeReadSpeedToken, self).setUp() + + def verify_attributes(self, n_attrs, attributes): + expected = dict((i, "Attribute {}".format(i)) + for i in xrange(n_attrs)) + self.assertEqual(attributes, expected) + + def get_attributes(self, n_attrs): + attributes = self.k.get_attributes(range(n_attrs)) + self.verify_attributes(n_attrs, attributes) + + def test_get_1_attribute(self): + self.get_attributes(1) + + def test_get_6_attributes(self): + self.get_attributes(6) + + def test_get_12_attributes(self): + self.get_attributes(12) + +class TestPKeyAttributeReadSpeedVolatile(TestCaseLoggedIn): + """ + Attribute speed tests. + """ + + def setUp(self): + der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der + self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, 0) + self.addCleanup(self.k.delete) + self.k.set_attributes(dict((i, "Attribute {}".format(i)) + for i in xrange(12))) + super(TestPKeyAttributeReadSpeedVolatile, self).setUp() + + def verify_attributes(self, n_attrs, attributes): + expected = dict((i, "Attribute {}".format(i)) + for i in xrange(n_attrs)) + self.assertEqual(attributes, expected) + + def get_attributes(self, n_attrs): + attributes = self.k.get_attributes(range(n_attrs)) + self.verify_attributes(n_attrs, attributes) + + def test_get_1_attribute(self): + self.get_attributes(1) + + def test_get_6_attributes(self): + self.get_attributes(6) + + def test_get_12_attributes(self): + self.get_attributes(12) + + +@unittest.skipUnless(ecdsa_loaded, "Requires Python ECDSA package") +class TestPkeyECDSAVerificationNIST(TestCaseLoggedIn): + """ + ECDSA verification tests based on Suite B Implementer's Guide to FIPS 186-3. + """ + + def verify(self, Qx, Qy, H, r, s, hal_curve, py_curve, py_hash): + Q = ECDSA_VerifyingKey.from_public_point(Point(py_curve.curve, Qx, Qy), + py_curve, py_hash).to_der() + k = hsm.pkey_load(HAL_KEY_TYPE_EC_PUBLIC, hal_curve, Q) + self.addCleanup(k.delete) + k.verify(signature = (r + s).decode("hex"), data = H.decode("hex")) + + def test_suite_b_p256_verify(self): + self.verify( + Qx = 0x8101ece47464a6ead70cf69a6e2bd3d88691a3262d22cba4f7635eaff26680a8, + Qy = 0xd8a12ba61d599235f67d9cb4d58f1783d3ca43e78f0a5abaa624079936c0c3a9, + H = "7c3e883ddc8bd688f96eac5e9324222c8f30f9d6bb59e9c5f020bd39ba2b8377", + r = "7214bc9647160bbd39ff2f80533f5dc6ddd70ddf86bb815661e805d5d4e6f27c", + s = "7d1ff961980f961bdaa3233b6209f4013317d3e3f9e1493592dbeaa1af2bc367", + hal_curve = HAL_CURVE_P256, + py_curve = NIST256p, + py_hash = SHA256) + + def test_suite_b__p384_verify(self): + self.verify( + Qx = 0x1fbac8eebd0cbf35640b39efe0808dd774debff20a2a329e91713baf7d7f3c3e81546d883730bee7e48678f857b02ca0, + Qy = 0xeb213103bd68ce343365a8a4c3d4555fa385f5330203bdd76ffad1f3affb95751c132007e1b240353cb0a4cf1693bdf9, + H = "b9210c9d7e20897ab86597266a9d5077e8db1b06f7220ed6ee75bd8b45db37891f8ba5550304004159f4453dc5b3f5a1", + r = "a0c27ec893092dea1e1bd2ccfed3cf945c8134ed0c9f81311a0f4a05942db8dbed8dd59f267471d5462aa14fe72de856", + s = "20ab3f45b74f10b6e11f96a2c8eb694d206b9dda86d3c7e331c26b22c987b7537726577667adadf168ebbe803794a402", + hal_curve = HAL_CURVE_P384, + py_curve = NIST384p, + py_hash = SHA384) + + +# Entire classes of tests still missing: +# +# * pkey attribute functions +# +# * pkey list and match functions +# +# * token vs session key tests +# +# Preloaded keys should suffice for all of these. + + + +class Pinwheel(object): + """ + Activity pinwheel, as needed. + """ + + def __init__(self): + self.pinwheel = tuple("\b\b{} ".format(c) for c in "-/|\\") + self.modulo = len(self.pinwheel) + self.position = 0 + if not args.quiet: + from sys import stdout + stdout.write(". ") + stdout.flush() + + def __call__(self): + if not args.quiet: + from sys import stdout + stdout.write(self.pinwheel[self.position]) + stdout.flush() + self.position = (self.position + 1) % self.modulo + + +class PreloadedKey(object): + """ + Keys for preload tests, here at the end because they're large. + For the moment, we use PKCS #1.5 format for RSA and secg format + for ECDSA, because those are the formats that everything + (including our own ASN.1 code) supports. Arguably, we should be + using PKCS #8 to get a single, consistent, self-identifying + private key format, but we're not there yet and it's not + particularly urgent given widespread availablity of conversion + tools (eg, "openssl pkcs8"). + """ + + db = {} + + def __init__(self, keytype, fn2, obj, der, keylen = None, curve = HAL_CURVE_NONE): + self.keytype = keytype + self.fn2 = fn2 + self.obj = obj + self.der = der + self.keylen = keylen + self.curve = curve + self.db[keytype, fn2] = self + +class PreloadedRSAKey(PreloadedKey): + + @classmethod + def importKey(cls, keylen, pem): + if pycrypto_loaded: + k1 = RSA.importKey(pem) + k2 = k1.publickey() + cls(HAL_KEY_TYPE_RSA_PRIVATE, keylen, k1, k1.exportKey("DER"), keylen = keylen) + cls(HAL_KEY_TYPE_RSA_PUBLIC, keylen, k2, k2.exportKey("DER"), keylen = keylen) + + def sign(self, text, hash): + return PKCS1_v1_5.PKCS115_SigScheme(self.obj).sign(hash(text)) + + def verify(self, text, hash, signature): + return PKCS1_v1_5.PKCS115_SigScheme(self.obj).verify(hash(text), signature) + +class PreloadedECKey(PreloadedKey): + + @classmethod + def importKey(cls, curve, pem): + if ecdsa_loaded: + k1 = ECDSA_SigningKey.from_pem(pem) + k2 = k1.get_verifying_key() + cls(HAL_KEY_TYPE_EC_PRIVATE, curve, k1, k1.to_der(), curve = curve) + cls(HAL_KEY_TYPE_EC_PUBLIC, curve, k2, k2.to_der(), curve = curve) + + def sign(self, text, hash): + return self.obj.sign(text, hashfunc = hash) + + def verify(self, text, hash, signature): + return self.obj.verify(signature, text, hashfunc = hash) + + +# openssl genrsa 1024 +PreloadedRSAKey.importKey(1024, '''\ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC95QlDOvlQhdCe/a7eIoX9SGPVfXfA8/62ilnF+NcwLrhxkr2R +4EVQB65+9AbxqM8Hqol6fhZzmDs48cl2tOFGJpE8iMhiFm4i6SGl2RXYaG0xi+lJ +FrXXCLFQovIEMuukBE129wra1xIB72tYR14lu8REX+Mhpbuz44M1jlCrlQIDAQAB +AoGAeU928l8bZIiH9PnlG318kYkMVhd4SGjXQK/zl9hXSC2goNV4i1d1kCHIJMwq +H3mTALe+aeVg3GnU85Tq+g2llzogoyXl8q902KbvImrM/XSbsue9/oj0OSgw+jKB +faFzX6FxAtNV5pmU9QiwauBIl/3yPCF9ifim5zg+pWCqLaECQQD59Z/R6TrTHxp6 +w2vH4CJyP5KORcf+eMa50SAriMVBXsJzsBiLLVxKIZfWbQn9gytJqJZKmIHezZQm +dyam84fpAkEAwnvSF27RhxLXE037+t7k5MZti6BfNTeUBrwffteepL6qax9HK+h9 +IQZ1vfNIqjZm8i7kQQyy4L8tRnk8mjZmzQJBAIUwfXWTilW+yBRMFx1M7+3itAv9 +YODWqEWRCkxIN5tqi8CrP5jBleCmX8rRFTaxcxpvq42aD/GRp3SLntvs/ikCQCSg +GOKc1gyv+Z0DFK8cBtMmoz6mRwfInbHe/7dtd8zis0lVLJwSPm5Xvxi0ljyn3h9B +wW6Wq6Ezn50j+8u27wkCQQCcIFE01BDAdtFHtTJ3aaEM9IdMCYrcJ0I/Y0NTE2M6 +lsTSiPyQjc4dQQJxFduvWHLx28bx+l7FTav7FaKntCJo +-----END RSA PRIVATE KEY----- +''') + +# openssl genrsa 2048 +PreloadedRSAKey.importKey(2048, '''\ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsbvq6syhDXD/OMVAuLoMceGQLUIiezfShVqFfyMqADjqhFRW +Wbonn0XV9ZkypU4Ib9n6PtLATNaefhpsUlI4s+20YTlQ7GiwJ9p97/N1o1u060ja +4LdqhfYtn8GZX+JAfa5NqpmLKCJ58XJ3q28MPLRwYp5yKckjkzchZHFyjs1W7r5a +JfeJ/vsQusX3klmCehJ1jxSHPh8o6lTjFMnBK8t360YTu0UGK/RUcEAYO7l7FWjd +8PjZfawXIrOAhCLkVvDFfpsl2oyFIL9d1QE87WdyyZXAtWLs62gnX+kiBq9gUhu5 +GsgcQifHBcRiGZfH0TRIMgIsSjsorzHqJ9uoQwIDAQABAoIBAGqzx5/5A8NfEEpT +2bxNLcV8xqL1LmBNLh0TMEwYn1GM2fZh74lkwf7T3VTaCVbGlzgXZC4tNneq7XIF +iPyPEi2rSnyH/XZAj2kNukfBIOHW37HVhloco14TYmajwuGWomMRrtz521pYAF+c ++g042N7k8Qez2hQOBkaOdYSouz7RNdJUGUocRhcSkh+QZTBwtQxrkuhhHN+zkIri ++Q09hF2hAliHrh6mow8ci0gRsXnZzsdJfTX8CasHWTIll4gfrvWnUY7iYqB6ynRU +YN+7IgQXMUFLziIlH1qN+DlEYdznsgAPSS3JdTWh0cvjiO8wTFAnOIdsj+BpKoDB +PK2zzDkCgYEA3TP8h4Ds/y1tDijE3Sarrg0vWuY97sJmAla4qFHH4hscZ84NDzTM +I/ohLPJgpeR2MaBqZmVk9UFrd3rdt3/lX6kSO7Kffa9rVgfOB4CqJ4joso3j0qY8 +V/iVBcDcD1h4aXCRX2tMJICUTgVU/N8/2wBEElcOUjZHGlcHmbHndlUCgYEAzbFm +ttPuIHrYHkmOm/DjYB65+WD8gfPMdeUbnx+yIt5xwimIbq1qWzl4++goTAAyaU/7 +qM9IfveRue0B7yjnPa8fjN+CcXMGM6a3BIqeIv1icfgjHxlt7D+64FpENWXHvFE0 +MhRliINfkTHm+U4+1s0045a+bLdTbfVly1gATDcCgYEAyOaoWmFL3k7hl1SLx9eR +YVj0Q3iNk0XX5BPjTmxIQCEjYVwRHFh1d897Rhk0kja26kepmypH0UADXNaofDqa +lpE10CZhGIOz1sTr6ICBCbscrN6VpgH5GGTa5AjPVNijNBBa1/DZjOWCzIGnOKuC +kWLicE3E4gIN/exBKOQdNqkCgYEAjA5PMg38BoGexoCvad8L81b4qqUvSg0HGv91 +X1Plp3hvXRWKoFHUKWlox528UoOPz8V2ReteIZXQ1BhdSMtBKO8lPHa0CyuW/XR3 +CdCY/Jorfg7HW1WlU0fRpxHPf8xdxAxGzhK1T86kM+kWrIpqnzf62zy5TK1HUYfW +WC8DhOECgYBzU8hIA0PU7aRPUs0o9MO9XcvVPvdX6UOKdNb9CnBMudS/chKHJUYP +d0fFAiVaRX0JMQ0RSrenxCqfWVtW3T3wFYNHB/IFRIUT3I44wwXJTNOeoi3FDTMx +EQfc0UFoFHyc3mYEKR4zHheqQG5OFBN89LqG3S+O69vc1qwCvNKL+Q== +-----END RSA PRIVATE KEY----- +''') + +# openssl genrsa 4096 +PreloadedRSAKey.importKey(4096, '''\ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAzWpYSQt+DrUNI+EJT/ko92wM2POfFnmm3Kc34nmuK6sez0DJ +r9Vib9R5K46RNgqcUdAodjU6cy3/MZA53SqP7RwR/LQtWmK2a+T4iey2vQZ0iCDA +2NI4gjgmCAjZnOD/m5yUXjCig/wJ8pGXolg8oHnvdeLg1joIOSF9OudLrI6aJnDg +OfdegzmCWXmWl7TXrqHVIWZhZZF7qQZRso6ZQ1/8mjpvVD0drASBxMnIzvpe4ynr +Y2NB807X/D5bbScp292ZKTNf5unPN1SsFy5ymzfLZrfNksYef6xPXcVr6OiObi49 +De8e11aNPj6fgLzzqAu1rjjrDkgvXx5G7gPJXq1aq6uxB2cKMrRS+ivmyC8vQlzP +lQwW20oYeeOfCg7ddNAJcu3jNTuNJaZdhc9szpVhV8DXZoXe/RzUNjZH7wUqueHy +fpbLwS+h3McJqrbWFdCQBivZnoI05cF2JIHEeR3S0Gyo2/IheNeFX2Tt8oDnHY4a +olRHiR5CMdM8UoGSxR9Y12fZ9dcqdCH3d6wDAsBDHTCE8ZIwFwhW6iA+g54YE3X7 +BlsgWr60poCDgH+CJjh0VDVxqL7r+w76sD9WAQMa7Gb+Mp2XCYnIZPXTrsmwVbZ9 +s5vFXUEODYom6qBlbZB8gyZzee5Skc1jx2fnmqxRtflA4W3xVAQFof2rFiUCAwEA +AQKCAgBxhQXJSFqf0hqy61h0I+Qp6EKpWuleSFiYtKjDti803tql+s37KFfAKZHV +KnLBhNeitwDFYuEsag0P3P69ZRopFUwzdXdi7g6WTfG0d2b9y6V23XL14Cduf400 +/38TnZxk6QFtlD8b5ZuxvBgqlczbeseFRJ6whV2qBQHqHYzKjfxOpi6kmjpXFt8c +h39b04smbTUVwjitIttOK7nWjcvRWiiFKyn/Sc8uE0eL81/QUrlBnRcC1AXMapQe +SG/KQMx3P123UTb8q9XiZB6+qOKZORplZ8pqBKcyM42g6suZ6XtdFJyVKMLIioKA +FaecQ8/73IzI/ZeZSvcy/85/FwSfGjHD7C7vL9kfg77no+IvHYlBYiIqtTddpQH5 +LGJAJnOGtk047/OjTmL8QyylvDAv8jBeZZdbOX7L+8jk5DbHmfUcDjvBS9g+Fbfk +jDurphrp1dHn/YgaA27NZs87TPWX1aVPiOlXEhO9SHHiiKCHDpBzV1gW/eiho33s ++uEr57ZoakzonN/zNb7KqHUO/ZGwMg+V9bVIgThqbdgmxNz7JFz14CN79yPmW5QT +1P1v7a6xWaZTALe2HGvy0B+iRzhLpay1tI4O/omPj9vUzVJwGHztVt0RddcmA9wV +Y3qglRNl+YvNlm6BUn8KwPIqki8JoioA8J1EQ5mz/K0fbrzcOQKCAQEA8TCqq0pb +mfxtsf42zhsSUw1VdcCIG0IYcSWxIiCfujryAQX1tmstZeRchlykXmdO+JDcpvMy +BKBD7188JEWjCX1IRRtHxTJ5WG+pE8sNPLNL8eZVZ+CEbNjVk4dtWGLwyNm+rQkM +NmOlm+7ZHdezBXljZOeqZbdsTSDQcGYG8JxlvLpAN60pjIGvTdTrdnksMhB4PK+l +7KtyEVDWXU/VT6kqhP3Ri1doHv/81BplgfjEJM8ZxmasfP4SlJ1olKqsHMFSrclj +ZCAemKEexVyzg8cHm9ghj6MLQZe3gs94V6h8I2ifrBBNHMrZgYg2Db0GeyYrr+kZ +GDjT0DZp0jgyfwKCAQEA2gdTclmrfAU/67ziOJbjkMgfhBdteE1BbJDNUca0zB6q +Ju4BwNgt0cxHMbltgE2/8hWP95HIaQdh1mIgwSK93MxRbaAvAQfF3muJxvMR5Mru +DejE+IEK9eZetgkNHGWyfiFzBWHda/Z9PQkqYtRfop5qFBVAPZ4YzR5hT0j64eDQ +N/z9C0ZB6RL9EcXJgEYgGI3wP8Qsrw3JRBQN0SCVRmrEJm4WIXs+CEHOk56/VbPM +v82uwbHVghS0U9bEZvNoeq7ZQjS2tRXXRJeOgQyCNvYy670T0KvQZoDb59EbEDSz +eQZS1J7rDEBHW+VwRSJA8noMEgZdEv8AxbEF2CddWwKCAQAMwH71iXvoW1FNbNxm +70V7wKO5ExHfJxJ1wQFphYIMbZtn9HG2UFpZHcbKj9Fc8GdbewU/inIljnepC0b5 +v/jLwqT0imm0AmQqCdVNp5mukOg+BOiVEmjN/HTmVO2yE6EZbXHIYkcUBRa3dNxj +2IitjGp15k27DQSb21VJ7AsH46z5WnuUtgIRXLXxDoXYgLWWfApvYvYJ2lKwma6L +xnHHwXDvESBoFpn5sZ0jdbXSNl3geFarh7gs753534ys940cBBij+ZbYr14Owc4H +r0wKdpZvZfD4UC2DLUtVjjSVpeHSWXC/vyjkkdEIKTR6a3kRP8ZliZR7FF4Wjxnv +NGtvAoIBAEu5g6gRsNewUxUjU0boUT115ExSfrjrzC9S05z1cNH8TIic3YsHClL1 +qjyA9KE9X89K4efQgFTKNZbqGgo6cMsBQ77ZhbnL41Nu8jlhLvPR74BxOgg9eXsS +eg6rchxMzgO0xmg2J1taDwFl74zHyjeG4bz77IX6JQ8I4C9TX5+YH3lyqsiBrF6x +M6g6k9Ozh24/zhO3pPVfymmUtX/O20nLxzi5v4H9dfwULxVia33upsxvOaUYiNlX +K5J641gGbmE93UN7X4HhhhTStrHnkEpalDEASKOPKSCQ3M/U9ptYUoVURuyGDYkB +wkcOl0HLtdcBwLN59lWkr7X519fNREUCggEBAMk39k+shD2DW8ubE/LgoforwfT2 +558FPxpZ+pGwMHL3ZnLuQuiROyPyQZj/ZmmLAa2TPrwS/ssln46Y2KesejWK/0Hq +8SaFLhOjacF8u5IOOKBZvx+HOT6ctRNBVyzt9A8wu0DE6nzc5HQpm9TMXrOLuZ0L +u22yFikwoIgYpU6hBdbg1mnirZS/ZyqJV9gWB6ZYyUAUGdgBqL6euSAAqBp93qz8 +sQLesqTufT1mVZd/ndLyvjDJjNKUE0w1g/1xNtg6N5aM+7pc/DwE/s+EtCxc/858 +dQYLBHIPcw6e0FdL3nTs44BpAqcK28N5eWbe/KaZ3EA0lHRmyOQ++WgU6jo= +-----END RSA PRIVATE KEY----- +''') + +# openssl ecparam -genkey -name prime256v1 | openssl ec +PreloadedECKey.importKey(HAL_CURVE_P256, '''\ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPBrVhD1iFF2e8wPkPf4N1038iR8xPgku/CVOT8lcSztoAoGCCqGSM49 +AwEHoUQDQgAE3mB5BmN5Fa4fV74LsDWIpBUxktPqYGJ6WOBrjPs1HWkNU7JHO3qY +9yy+CXFSPb89GWQgb5wLtNPn4QYMj+KRTA== +-----END EC PRIVATE KEY----- +''') + +# openssl ecparam -genkey -name secp384r1 | openssl ec +PreloadedECKey.importKey(HAL_CURVE_P384, '''\ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCVGo35Hbrf3Iys7mWRIm5yjg+6vPIgzbp2jCbDyszBo+wTxmQambG4 +g8yocp4wM6+gBwYFK4EEACKhZANiAATYwa+M8T8jsNHKmMZTvPPflUIfrjuZZo1D +3kkkmN4r6cTNctjaeRdAfD0X40l4yPnGIP9ParuKVVl1y0TdQ7BS3g/Gj/LP33HD +ESP8gFDIKFCWSDX0uhmy+HsGsPwgNoY= +-----END EC PRIVATE KEY----- +''') + +# openssl ecparam -genkey -name secp521r1 | openssl ec +PreloadedECKey.importKey(HAL_CURVE_P521, '''\ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBtf+LKhJNQEJRFQ2cGQPcviwfp9IKSnD5EFTqAPtv7/+t2FtmdHHP +/fWIlZ7jcC5N9dWy6bKGu3+FqwgZAYLpsqigBwYFK4EEACOhgYkDgYYABADdfcUe +P0oAZQ5308v5ijcg4hePvhvVi+QKcrwmE9kirXCFoYN1tzPmXZmw8lNJenrbwaNz +opJR84LBHnomGPogAQGF0aRk0jE8w1j1oMfrrzV6vCWnkh7pyzsDnrLU1HrkWeqw +ihzwMzYJgFzToDH+fCh7nrBFZZZ9P9gPYMlSM5UMeA== +-----END EC PRIVATE KEY----- +''') + + +if __name__ == "__main__": + main() @@ -0,0 +1,111 @@ +/* + * uuid.c + * ------ + * UUID support for keystore database. + * + * 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. + */ + +#include <stdio.h> +#include <assert.h> + +#include "hal.h" +#include "hal_internal.h" + +hal_error_t hal_uuid_gen(hal_uuid_t *uuid) +{ + hal_error_t err; + + if (uuid == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + /* + * Generate a version 4 UUID as specified in RFC 4122. + * This is basically a 122-bit random number. + */ + + if ((err = hal_rpc_get_random(uuid->uuid, sizeof(uuid->uuid))) != HAL_OK) + return err; + + /* + * Set high order bits of clock_seq_hi_and_reserved and + * time_hi_and_version fields to magic values as specified by RFC + * 4122 section 4.4. + * + * Not recommended reading if you've eaten recently. + */ + + uuid->uuid[6] &= 0x0f; + uuid->uuid[6] |= 0x40; + uuid->uuid[8] &= 0x3f; + uuid->uuid[8] |= 0x80; + + return HAL_OK; +} + +hal_error_t hal_uuid_parse(hal_uuid_t *uuid, const char * const string) +{ + static const char fmt[] + = "%2hhx%2hhx%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx"; + + if (uuid == NULL || string == NULL || + sscanf(string, fmt, + uuid->uuid + 0, uuid->uuid + 1, uuid->uuid + 2, uuid->uuid + 3, + uuid->uuid + 4, uuid->uuid + 5, uuid->uuid + 6, uuid->uuid + 7, + uuid->uuid + 8, uuid->uuid + 9, uuid->uuid + 10, uuid->uuid + 11, + uuid->uuid + 12, uuid->uuid + 13, uuid->uuid + 14, uuid->uuid + 15) != 16) + return HAL_ERROR_BAD_ARGUMENTS; + + return HAL_OK; +} + +hal_error_t hal_uuid_format(const hal_uuid_t * const uuid, char *buffer, const size_t buffer_len) +{ + static const char fmt[] + = "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"; + + if (uuid == NULL || buffer == NULL || buffer_len < HAL_UUID_TEXT_SIZE) + return HAL_ERROR_BAD_ARGUMENTS; + + if (snprintf(buffer, buffer_len, fmt, + uuid->uuid[ 0], uuid->uuid[ 1], uuid->uuid[ 2], uuid->uuid[ 3], + uuid->uuid[ 4], uuid->uuid[ 5], uuid->uuid[ 6], uuid->uuid[ 7], + uuid->uuid[ 8], uuid->uuid[ 9], uuid->uuid[10], uuid->uuid[11], + uuid->uuid[12], uuid->uuid[13], uuid->uuid[14], uuid->uuid[15]) != HAL_UUID_TEXT_SIZE - 1) + return HAL_ERROR_RESULT_TOO_LONG; + + return HAL_OK; +} + + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ |