diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | GNUmakefile | 20 | ||||
-rw-r--r-- | hal.h | 14 | ||||
-rw-r--r-- | hal_rpc.c | 324 | ||||
-rw-r--r-- | hal_rpc.h | 243 | ||||
-rw-r--r-- | hash.c | 102 | ||||
-rw-r--r-- | rpc_client.c | 287 | ||||
-rw-r--r-- | rpc_hash.c | 300 | ||||
-rw-r--r-- | rpc_internal.h | 191 | ||||
-rw-r--r-- | tests/test-bus.c | 3 | ||||
-rw-r--r-- | utils/cores.c | 2 |
11 files changed, 1467 insertions, 22 deletions
@@ -6,8 +6,11 @@ autom4te.cache config.log config.status tests/test-aes-key-wrap +tests/test-bus tests/test-ecdsa tests/test-hash tests/test-pbkdf2 tests/test-rsa +tests/test-trng +utils/cores utils/eim_peek_poke diff --git a/GNUmakefile b/GNUmakefile index 769d11f..8fc0d14 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -27,21 +27,37 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Number of static hash and HMAC state blocks to allocate + +STATIC_HASH_STATE_BLOCKS = 10 +STATIC_HMAC_STATE_BLOCKS = 4 + INC = hal.h LIB = libhal.a OBJ = ${IO_OBJ} core.o csprng.o hash.o aes_keywrap.o pbkdf2.o \ - modexp.o rsa.o ecdsa.o asn1.o errorstrings.o - + modexp.o rsa.o ecdsa.o asn1.o errorstrings.o ${RPC_OBJ} IO_OBJ_EIM = hal_io_eim.o novena-eim.o IO_OBJ_I2C = hal_io_i2c.o # Default I/O bus is EIM, override this to use I2C instead IO_OBJ = ${IO_OBJ_EIM} +RPC_OBJ_COMMON = hal_rpc.o rpc_hash.o +RPC_OBJ_CLIENT = ${RPC_OBJ_COMMON} rpc_client.o +RPC_OBJ_SERVER = ${RPC_OBJ_COMMON} rpc_misc.o rpc_pkey.o + +# Default should be to build the RPC server code, but we haven't +# written even the skeleton of that yet. We'll probably end up +# needing a makefile conditional to handle all this properly +RPC_OBJ = ${RPC_OBJ_CLIENT} + TFMDIR := $(abspath ../thirdparty/libtfm) CFLAGS += -g3 -Wall -fPIC -std=c99 -I${TFMDIR} -DHAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM=1 LDFLAGS := -g3 -L${TFMDIR} -ltfm +CFLAGS += -DHAL_STATIC_HASH_STATE_BLOCKS=${STATIC_HASH_STATE_BLOCKS} +CFLAGS += -DHAL_STATIC_HMAC_STATE_BLOCKS=${STATIC_HMAC_STATE_BLOCKS} + all: ${LIB} cd tests; ${MAKE} CFLAGS='${CFLAGS} -I..' LDFLAGS='${LDFLAGS}' $@ cd utils; ${MAKE} CFLAGS='${CFLAGS} -I..' LDFLAGS='${LDFLAGS}' $@ @@ -221,7 +221,17 @@ typedef struct hal_hash_driver hal_hash_driver_t; * problem. */ +typedef enum { + hal_digest_algorithm_sha1, + 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 { + hal_digest_algorithm_t digest_algorithm; size_t block_length; size_t digest_length; size_t hash_state_length; @@ -284,6 +294,10 @@ extern void hal_hash_cleanup(hal_hash_state_t **state); extern void hal_hmac_cleanup(hal_hmac_state_t **state); +extern const hal_hash_descriptor_t *hal_hash_get_descriptor(const hal_hash_state_t * const state); + +extern const hal_hash_descriptor_t *hal_hmac_get_descriptor(const hal_hmac_state_t * const state); + /* * AES key wrap functions. */ diff --git a/hal_rpc.c b/hal_rpc.c new file mode 100644 index 0000000..6ad198e --- /dev/null +++ b/hal_rpc.c @@ -0,0 +1,324 @@ +/* + * hal_rpc.c + * --------- + * Remote procedure call public API implementation. + * + * 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 "rpc_internal.h" + +#ifndef HAL_RPC_IS_CLIENT +#warning HAL_RPC_IS_CLIENT not set, assuming we're building for the HSM +#define HAL_RPC_IS_CLIENT 0 +#endif + +/* + * Maybe we'll let the client configure this at runtime, later. For + * now, wire in the obvious defaults: hashing is done locally, + * everything else is done via RPC. For the server everything is + * always done locally. + */ + +#if HAL_RPC_IS_CLIENT + +static const hal_rpc_misc_dispatch_t * const misc_dispatch = &hal_rpc_remote_misc_dispatch; +static const hal_rpc_hash_dispatch_t * const hash_dispatch = &hal_rpc_remote_hash_dispatch; +static const hal_rpc_pkey_dispatch_t * const pkey_dispatch = &hal_rpc_mixed_pkey_dispatch; + +#else + +static const hal_rpc_misc_dispatch_t * const misc_dispatch = &hal_rpc_local_misc_dispatch; +static const hal_rpc_hash_dispatch_t * const hash_dispatch = &hal_rpc_local_hash_dispatch; +static const hal_rpc_pkey_dispatch_t * const pkey_dispatch = &hal_rpc_local_pkey_dispatch; + +#endif + +const hal_rpc_hash_handle_t hal_rpc_hash_handle_none = {0}; + +static inline int check_pkey_type(const hal_rpc_pkey_key_type_t type) +{ + switch (type) { + case HAL_RPC_PKEY_RSA_PRIVATE: + case HAL_RPC_PKEY_RSA_PUBLIC: + case HAL_RPC_PKEY_ECDSA_PRIVATE: + case HAL_RPC_PKEY_ECDSA_PUBLIC: + return 1; + default: + return 0; + } +} + +static inline int check_pkey_flags(const hal_rpc_pkey_flags_t flags) +{ + return (flags &~ (HAL_RPC_PKEY_FLAG_USAGE_DIGITALSIGNATURE | + HAL_RPC_PKEY_FLAG_USAGE_KEYENCIPHERMENT | + HAL_RPC_PKEY_FLAG_USAGE_DATAENCIPHERMENT)) == 0; +} + +static inline int check_pkey_type_curve_flags(const hal_rpc_pkey_key_type_t type, + const hal_rpc_pkey_curve_t curve, + const hal_rpc_pkey_flags_t flags) +{ + if (!check_pkey_flags(flags)) + return 0; + + switch (type) { + + case HAL_RPC_PKEY_RSA_PRIVATE: + case HAL_RPC_PKEY_RSA_PUBLIC: + return curve == HAL_RPC_PKEY_CURVE_NONE; + + case HAL_RPC_PKEY_ECDSA_PRIVATE: + case HAL_RPC_PKEY_ECDSA_PUBLIC: + switch (curve) { + case HAL_RPC_PKEY_CURVE_ECDSA_P256: + case HAL_RPC_PKEY_CURVE_ECDSA_P384: + case HAL_RPC_PKEY_CURVE_ECDSA_P521: + return 1; + default: + return 0; + } + + default: + return 0; + } +} + + +hal_error_t hal_rpc_get_random(void *buffer, const size_t length) +{ + if (buffer == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + if (length == 0) + return HAL_OK; + return misc_dispatch->get_random(buffer, length); +} + +#warning Perhaps we should be enforcing a minimum PIN length here + +hal_error_t hal_rpc_set_pin(const hal_rpc_user_t which, + const char * const newpin, const size_t newpin_len) +{ + if (newpin == NULL || newpin_len == 0 || (which != HAL_RPC_USER_NORMAL && which != HAL_RPC_USER_SO)) + return HAL_ERROR_BAD_ARGUMENTS; + return misc_dispatch->set_pin(which, newpin, newpin_len); +} + +hal_error_t hal_rpc_login(const hal_rpc_client_handle_t client, + const hal_rpc_user_t user, + const char * const pin, const size_t pin_len) +{ + if (pin == NULL || pin_len == 0 || (user != HAL_RPC_USER_NORMAL && user != HAL_RPC_USER_SO)) + return HAL_ERROR_BAD_ARGUMENTS; + return misc_dispatch->login(client, user, pin, pin_len); +} + +hal_error_t hal_rpc_logout(const hal_rpc_client_handle_t client) +{ + return misc_dispatch->logout(client); +} + +hal_error_t hal_rpc_hash_get_digest_length(const hal_digest_algorithm_t alg, size_t *length) +{ + if (length == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + return hash_dispatch->get_digest_length(alg, length); +} + +hal_error_t hal_rpc_hash_get_digest_algorithm_id(const hal_digest_algorithm_t alg, + uint8_t *id, size_t *len, const size_t len_max) +{ + return hash_dispatch->get_digest_algorithm_id(alg, id, len, len_max); +} + +hal_error_t hal_rpc_hash_get_algorithm(const hal_rpc_hash_handle_t hash, hal_digest_algorithm_t *alg) +{ + if (hash.handle == hal_rpc_hash_handle_none.handle || alg == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + return hash_dispatch->get_algorithm(hash, alg); +} + +hal_error_t hal_rpc_hash_initialize(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_hash_handle_t *hash, + const hal_digest_algorithm_t alg, + const uint8_t * const key, const size_t key_len) +{ + if (hash == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + return hash_dispatch->initialize(client, session, hash, alg, key, key_len); +} + +hal_error_t hal_rpc_hash_update(const hal_rpc_hash_handle_t hash, + const uint8_t * data, const size_t length) +{ + if (hash.handle == hal_rpc_hash_handle_none.handle || data == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + if (length == 0) + return HAL_OK; + return hash_dispatch->update(hash, data, length); +} + +hal_error_t hal_rpc_hash_finalize(const hal_rpc_hash_handle_t hash, + uint8_t *digest, const size_t length) +{ + if (hash.handle == hal_rpc_hash_handle_none.handle || digest == NULL || length == 0) + return HAL_ERROR_BAD_ARGUMENTS; + return hash_dispatch->finalize(hash, digest, length); +} + +hal_error_t hal_rpc_pkey_load(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const hal_rpc_pkey_key_type_t type, + const hal_rpc_pkey_curve_t curve, + const uint8_t * const name, const size_t name_len, + const uint8_t * const der, const size_t der_len, + const hal_rpc_pkey_flags_t flags) +{ + if (pkey == NULL || + name == NULL || name_len == 0 || + der == NULL || der_len == 0 || + !check_pkey_type_curve_flags(type, curve, flags)) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->load(client, session, pkey, type, curve, name, name_len, der, der_len, flags); +} + +hal_error_t hal_rpc_pkey_find(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const hal_rpc_pkey_key_type_t type, + const uint8_t * const name, const size_t name_len) +{ + if (pkey == NULL || name == NULL || name_len == 0 || !check_pkey_type(type)) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->find(client, session, pkey, type, name, name_len); +} + +hal_error_t hal_rpc_pkey_generate_rsa(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const uint8_t * const name, const size_t name_len, + const unsigned key_len, + const uint8_t * const exp, const size_t exp_len, + const hal_rpc_pkey_flags_t flags) +{ + if (pkey == NULL || name == NULL || name_len == 0 || key_len == 0 || + exp == NULL || exp_len == 0 || !check_pkey_flags(flags)) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->generate_rsa(client, session, pkey, name, name_len, key_len, exp, exp_len, flags); +} + +hal_error_t hal_rpc_pkey_generate_ec(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const uint8_t * const name, const size_t name_len, + const hal_rpc_pkey_curve_t curve, + const hal_rpc_pkey_flags_t flags) +{ + if (pkey == NULL || name == NULL || name_len == 0 || + !check_pkey_type_curve_flags(HAL_RPC_PKEY_ECDSA_PRIVATE, curve, flags)) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->generate_ec(client, session, pkey, name, name_len, curve, flags); +} + +hal_error_t hal_rpc_pkey_delete(const hal_rpc_pkey_handle_t pkey) +{ + return pkey_dispatch->delete(pkey); +} + +hal_error_t hal_rpc_pkey_get_key_type(const hal_rpc_pkey_handle_t pkey, + hal_rpc_pkey_key_type_t *type) +{ + if (type == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->get_key_type(pkey, type); +} + +hal_error_t hal_rpc_pkey_get_key_flags(const hal_rpc_pkey_handle_t pkey, + hal_rpc_pkey_flags_t *flags) +{ + if (flags == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->get_key_flags(pkey, flags); +} + +size_t hal_rpc_pkey_get_public_key_len(const hal_rpc_pkey_handle_t pkey) +{ + return pkey_dispatch->get_public_key_len(pkey); +} + +hal_error_t hal_rpc_pkey_get_public_key(const hal_rpc_pkey_handle_t pkey, + uint8_t *der, size_t *der_len, const size_t der_len_max) +{ + if (der == NULL || der_len == NULL || der_len_max == 0) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->get_public_key(pkey, der, der_len, der_len_max); +} + +hal_error_t hal_rpc_pkey_sign(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len) +{ + if (output == NULL || output_len == 0 || + (hash.handle == hal_rpc_hash_handle_none.handle) == (input == NULL || input_len == 0)) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->sign(session, pkey, hash, input, input_len, output, output_len); +} + +hal_error_t hal_rpc_pkey_verify(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len) +{ + if (output == NULL || output_len == 0 || + (hash.handle == hal_rpc_hash_handle_none.handle) == (input == NULL || input_len == 0)) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->verify(session, pkey, hash, input, input_len, output, output_len); +} + +hal_error_t hal_rpc_pkey_list(hal_rpc_pkey_key_info_t *result, + unsigned *result_len, + const unsigned result_max) +{ + if (result == NULL || result_len == NULL || result_max == 0) + return HAL_ERROR_BAD_ARGUMENTS; + return pkey_dispatch->list(result, result_len, result_max); +} + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/hal_rpc.h b/hal_rpc.h new file mode 100644 index 0000000..553fb6b --- /dev/null +++ b/hal_rpc.h @@ -0,0 +1,243 @@ +/* + * hal_rpc.h + * --------- + * Remote procedure call API to extrude libhal across the green/yellow boundary. + * + * Authors: Rob Austein, Paul Selkirk + * 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. + */ + +#ifndef _HAL_RPC_H_ +#define _HAL_RPC_H_ + +#include <stdint.h> +#include <stdlib.h> +#include "hal.h" + +/* + * Session handles are pretty much as in PKCS #11: from our viewpoint, + * a session is a lock-step stream of operations, so while operations + * from different sessions can interleave, operations within a single + * session cannot. + * + * Client handles are a small extension to the PKCS #11 model, + * intended to support multiple PKCS #11 using applications sharing a + * single HSM. Technically, sessions are per-client, but in practice + * there's no sane reason why we'd use the same session handle + * concurrently in multiple clients. Mostly, the client abstraction + * is to handle login and logout against the HSM's PIN. Clients add + * nothing whatsoever to the security model (the HSM has no way of + * knowing whether the host is lumping multiple applications into a + * single "client"), the point of the exercise is just to make the + * C_Login()/C_Logout() semantics work as expected in the presence of + * multiple applications. + * + * NB: Unlike other handles used in this protocol, session and client + * handles are created by the client (host) side of the RPC mechanism, + * not the server (HSM) side. + */ + +typedef struct { uint32_t handle; } hal_rpc_client_handle_t; +typedef struct { uint32_t handle; } hal_rpc_session_handle_t; + +typedef enum { HAL_RPC_USER_NONE, HAL_RPC_USER_NORMAL, HAL_RPC_USER_SO } hal_rpc_user_t; + +extern hal_error_t hal_rpc_set_pin(const hal_rpc_user_t which, + const char * const newpin, const size_t newpin_len); + +extern hal_error_t hal_rpc_login(const hal_rpc_client_handle_t client, + const hal_rpc_user_t user, + const char * const pin, const size_t pin_len); + +extern hal_error_t hal_rpc_logout(const hal_rpc_client_handle_t client); + +/* + * Get random bytes. + */ + +extern hal_error_t hal_rpc_get_random(void *buffer, const size_t length); + +/* + * Combined hash and HMAC functions: pass NULL key for plain hashing. + */ + +typedef struct { uint32_t handle; } hal_rpc_hash_handle_t; + +extern const hal_rpc_hash_handle_t hal_rpc_hash_handle_none; + +extern hal_error_t hal_rpc_hash_get_digest_length(const hal_digest_algorithm_t alg, size_t *length); + +extern hal_error_t hal_rpc_hash_get_digest_algorithm_id(const hal_digest_algorithm_t alg, + uint8_t *id, size_t *len, const size_t len_max); + +extern hal_error_t hal_rpc_hash_get_algorithm(const hal_rpc_hash_handle_t hash, hal_digest_algorithm_t *alg); + +/* + * Once started, a hash or HMAC operation is bound to a particular + * session, so we only need the client and session arguments to initialize. + */ + +extern hal_error_t hal_rpc_hash_initialize(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_hash_handle_t *hash, + const hal_digest_algorithm_t alg, + const uint8_t * const key, const size_t key_length); + +extern hal_error_t hal_rpc_hash_update(const hal_rpc_hash_handle_t hash, + const uint8_t * data, const size_t length); + +extern hal_error_t hal_rpc_hash_finalize(const hal_rpc_hash_handle_t hash, + uint8_t *digest, const size_t length); + +/* + * Public key functions. + * + * The _sign() and _verify() methods accept a hash OR an input string; + * either "hash" should be hal_rpc_hash_handle_none or input should be NULL, + * but not both. + * + * Use of client and session handles here needs a bit more thought. + * + * Client handles are straightforward: basically, anything that + * creates a new pkey handle should take a client handle, which should + * suffice, as object handles never cross clients. + * + * Session handles are more interesting, as PKCS #11's versions of + * session and object handles do in effect allow one session to hand + * an object handle to another session. So any action which can do + * significant work (ie, which is complicated enough that we can't + * guarantee an immediate response) needs to take a session handle. + * + * There will probably be a few cases where a session handle isn't + * strictly required but we ask for one anyway because the API turns + * out to be easier to understand that way (eg, we probably want to + * ask for a session handle anywhere we ask for a client handle, + * whether we need the session handle or not, so that users of this + * API don't have to remember which pkey-handle-creating calls require + * a session handle and which ones don't...). + */ + +#define HAL_RPC_PKEY_NAME_MAX 128 + +typedef enum { + HAL_RPC_PKEY_RSA_PRIVATE, + HAL_RPC_PKEY_RSA_PUBLIC, + HAL_RPC_PKEY_ECDSA_PRIVATE, + HAL_RPC_PKEY_ECDSA_PUBLIC +} hal_rpc_pkey_key_type_t; + +typedef enum { + HAL_RPC_PKEY_CURVE_NONE, + HAL_RPC_PKEY_CURVE_ECDSA_P256, + HAL_RPC_PKEY_CURVE_ECDSA_P384, + HAL_RPC_PKEY_CURVE_ECDSA_P521 +} hal_rpc_pkey_curve_t; + +typedef struct { uint32_t handle; } hal_rpc_pkey_handle_t; + +typedef uint32_t hal_rpc_pkey_flags_t; + +#define HAL_RPC_PKEY_FLAG_USAGE_DIGITALSIGNATURE (1 << 0) +#define HAL_RPC_PKEY_FLAG_USAGE_KEYENCIPHERMENT (1 << 1) +#define HAL_RPC_PKEY_FLAG_USAGE_DATAENCIPHERMENT (1 << 2) + +extern hal_error_t hal_rpc_pkey_load(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const hal_rpc_pkey_key_type_t type, + const hal_rpc_pkey_curve_t curve, + const uint8_t * const name, const size_t name_len, + const uint8_t * const der, const size_t der_len, + const hal_rpc_pkey_flags_t flags); + +extern hal_error_t hal_rpc_pkey_find(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const hal_rpc_pkey_key_type_t type, + const uint8_t * const name, const size_t name_len); + +extern hal_error_t hal_rpc_pkey_generate_rsa(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_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_rpc_pkey_flags_t flags); + +extern hal_error_t hal_rpc_pkey_generate_ec(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const uint8_t * const name, const size_t name_len, + const hal_rpc_pkey_curve_t curve, + const hal_rpc_pkey_flags_t flags); + +extern hal_error_t hal_rpc_pkey_delete(const hal_rpc_pkey_handle_t pkey); + +extern hal_error_t hal_rpc_pkey_get_key_type(const hal_rpc_pkey_handle_t pkey, + hal_rpc_pkey_key_type_t *type); + +extern hal_error_t hal_rpc_pkey_get_key_flags(const hal_rpc_pkey_handle_t pkey, + hal_rpc_pkey_flags_t *flags); + +extern size_t hal_rpc_pkey_get_public_key_len(const hal_rpc_pkey_handle_t pkey); + +extern hal_error_t hal_rpc_pkey_get_public_key(const hal_rpc_pkey_handle_t pkey, + uint8_t *der, size_t *der_len, const size_t der_len_max); + +extern hal_error_t hal_rpc_pkey_sign(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len); + +extern hal_error_t hal_rpc_pkey_verify(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len); + +typedef struct { + hal_rpc_pkey_key_type_t type; + hal_rpc_pkey_curve_t curve; + hal_rpc_pkey_flags_t flags; + char name[HAL_RPC_PKEY_NAME_MAX]; + /* ... */ +} hal_rpc_pkey_key_info_t; + +extern hal_error_t hal_rpc_pkey_list(hal_rpc_pkey_key_info_t *result, + unsigned *result_len, + const unsigned result_max); + +#endif /* _HAL_RPC_H_ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ @@ -54,13 +54,6 @@ * Driver. This encapsulates whatever per-algorithm voodoo we need * this week. At the moment, this is mostly Cryptech core addresses, * but this is subject to change without notice. - * - * Most of the addresses in the current version could be calculated - * from a single address (the core base address), but this week's - * theory prefers the precomputed composite addresses, and doing it - * this way saves some microscopic bit of addition at runtime. - * Whatever. It'll probably all change again once we have a dynamic - * memory map, so it's not really worth overthinking at the moment. */ struct hal_hash_driver { @@ -105,10 +98,6 @@ struct hal_hmac_state { /* * Drivers for known digest algorithms. - * - * Initialization of the core_name field is not a typo, we're - * concatenating two string constants and trusting the compiler to - * whine if the resulting string doesn't fit into the field. */ static const hal_hash_driver_t sha1_driver = { @@ -165,6 +154,7 @@ static const uint8_t */ const hal_hash_descriptor_t hal_hash_sha1[1] = {{ + 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), @@ -172,6 +162,7 @@ const hal_hash_descriptor_t hal_hash_sha1[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha256[1] = {{ + 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), @@ -179,6 +170,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, SHA512_BLOCK_LEN, SHA512_224_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha512_224, sizeof(dalgid_sha512_224), @@ -186,6 +178,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, SHA512_BLOCK_LEN, SHA512_256_DIGEST_LEN, sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha512_256, sizeof(dalgid_sha512_256), @@ -193,6 +186,7 @@ const hal_hash_descriptor_t hal_hash_sha512_256[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha384[1] = {{ + 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), @@ -200,6 +194,7 @@ const hal_hash_descriptor_t hal_hash_sha384[1] = {{ }}; const hal_hash_descriptor_t hal_hash_sha512[1] = {{ + 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), @@ -207,6 +202,33 @@ const hal_hash_descriptor_t hal_hash_sha512[1] = {{ }}; /* + * Static state blocks. This library is intended for a style of + * embedded programming in which one avoids heap-based allocation + * functions such as malloc() wherever possible and instead uses + * static variables when just allocating on the stack won't do. + * + * The number of each kind of state block to be allocated this way + * must be configured at compile-time. Sorry, that's life in the + * deeply embedded universe. + */ + +#ifndef HAL_STATIC_HASH_STATE_BLOCKS +#define HAL_STATIC_HASH_STATE_BLOCKS 0 +#endif + +#ifndef HAL_STATIC_HMAC_STATE_BLOCKS +#define HAL_STATIC_HMAC_STATE_BLOCKS 0 +#endif + +#if HAL_STATIC_HASH_STATE_BLOCKS > 0 +static hal_hash_state_t static_hash_state[HAL_STATIC_HASH_STATE_BLOCKS]; +#endif + +#if HAL_STATIC_HMAC_STATE_BLOCKS > 0 +static hal_hmac_state_t static_hmac_state[HAL_STATIC_HMAC_STATE_BLOCKS]; +#endif + +/* * Debugging control. */ @@ -218,6 +240,38 @@ void hal_hash_set_debug(int onoff) } /* + * Internal utilities to allocate static state blocks. + */ + +static inline hal_hash_state_t *alloc_static_hash_state(void) +{ + +#if HAL_STATIC_HASH_STATE_BLOCKS > 0 + + for (int i = 0; i < sizeof(static_hash_state)/sizeof(*static_hash_state); i++) + if ((static_hash_state[i].flags & STATE_FLAG_STATE_ALLOCATED) == 0) + return &static_hash_state[i]; + +#endif + + return NULL; +} + +static inline hal_hmac_state_t *alloc_static_hmac_state(void) +{ + +#if HAL_STATIC_HMAC_STATE_BLOCKS > 0 + + for (int i = 0; i < sizeof(static_hmac_state)/sizeof(*static_hmac_state); i++) + if ((static_hmac_state[i].hash_state.flags & STATE_FLAG_STATE_ALLOCATED) == 0) + return &static_hmac_state[i]; + +#endif + + return NULL; +} + +/* * Internal utility to do whatever checking we need of a descriptor, * then extract the driver pointer in a way that works nicely with * initialization of an automatic const pointer. @@ -225,7 +279,7 @@ void hal_hash_set_debug(int onoff) * Returns the driver pointer on success, NULL on failure. */ -static const hal_hash_driver_t *check_driver(const hal_hash_descriptor_t * const descriptor) +static inline const hal_hash_driver_t *check_driver(const hal_hash_descriptor_t * const descriptor) { return descriptor == NULL ? NULL : descriptor->driver; } @@ -235,8 +289,8 @@ static const hal_hash_driver_t *check_driver(const hal_hash_descriptor_t * const * attempting to locate an appropriate core if we weren't given one. */ -static hal_error_t check_core(const hal_core_t **core, - const hal_hash_descriptor_t * const descriptor) +static inline hal_error_t check_core(const hal_core_t **core, + const hal_hash_descriptor_t * const descriptor) { assert(descriptor != NULL && descriptor->driver != NULL); return hal_core_check_name(core, descriptor->core_name); @@ -264,7 +318,7 @@ hal_error_t hal_hash_initialize(const hal_core_t *core, if ((err = check_core(&core, descriptor)) != HAL_OK) return err; - if (state_buffer == NULL && (state = malloc(descriptor->hash_state_length)) == NULL) + if (state_buffer == NULL && (state = alloc_static_hash_state()) == NULL) return HAL_ERROR_ALLOCATION_FAILURE; memset(state, 0, sizeof(*state)); @@ -295,7 +349,6 @@ void hal_hash_cleanup(hal_hash_state_t **state_) return; memset(state, 0, state->descriptor->hash_state_length); - free(state); *state_ = NULL; } @@ -542,7 +595,7 @@ hal_error_t hal_hmac_initialize(const hal_core_t *core, if ((err = check_core(&core, descriptor)) != HAL_OK) return err; - if (state_buffer == NULL && (state = malloc(descriptor->hmac_state_length)) == NULL) + if (state_buffer == NULL && (state = alloc_static_hmac_state()) == NULL) return HAL_ERROR_ALLOCATION_FAILURE; hal_hash_state_t *h = &state->hash_state; @@ -639,7 +692,6 @@ void hal_hmac_cleanup(hal_hmac_state_t **state_) return; memset(state, 0, h->descriptor->hmac_state_length); - free(state); *state_ = NULL; } @@ -690,6 +742,20 @@ hal_error_t hal_hmac_finalize(hal_hmac_state_t *state, } /* + * Pull descriptor pointer from state block. + */ + +const hal_hash_descriptor_t *hal_hash_get_descriptor(const hal_hash_state_t * const state) +{ + return state == NULL ? NULL : state->descriptor; +} + +const hal_hash_descriptor_t *hal_hmac_get_descriptor(const hal_hmac_state_t * const state) +{ + return state == NULL ? NULL : state->hash_state.descriptor; +} + +/* * "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 diff --git a/rpc_client.c b/rpc_client.c new file mode 100644 index 0000000..20cc26f --- /dev/null +++ b/rpc_client.c @@ -0,0 +1,287 @@ +/* + * rpc_client.c + * ------------ + * Remote procedure call client-side private API implementation. + * + * 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 "rpc_internal.h" + +/* + * RPC calls. Not implemented yet. + */ + +#warning These are all placeholders, waiting to be filled in with the real RPC calls + +static hal_error_t get_random(void *buffer, const size_t length) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t set_pin(const hal_rpc_user_t which, + const char * const newpin, const size_t newpin_len) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t login(const hal_rpc_client_handle_t client, + const hal_rpc_user_t user, + const char * const pin, const size_t pin_len) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t logout(const hal_rpc_client_handle_t client) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t hash_get_digest_len(const hal_digest_algorithm_t alg, size_t *length) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t hash_get_digest_algorithm_id(const hal_digest_algorithm_t alg, + uint8_t *id, size_t *len, const size_t len_max) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t hash_get_algorithm(const hal_rpc_hash_handle_t hash, hal_digest_algorithm_t *alg) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t hash_initialize(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_hash_handle_t *hash, + const hal_digest_algorithm_t alg, + const uint8_t * const key, const size_t key_len) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t hash_update(const hal_rpc_hash_handle_t hash, + const uint8_t * data, const size_t length) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t hash_finalize(const hal_rpc_hash_handle_t hash, + uint8_t *digest, const size_t length) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_load(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const hal_rpc_pkey_key_type_t type, + const hal_rpc_pkey_curve_t curve, + const uint8_t * const name, const size_t name_len, + const uint8_t * const der, const size_t der_len, + const hal_rpc_pkey_flags_t flags) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_find(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const hal_rpc_pkey_key_type_t type, + const uint8_t * const name, const size_t name_len) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_generate_rsa(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const uint8_t * const name, const size_t name_len, + const unsigned key_len, + const uint8_t * const exp, const size_t exp_len, + const hal_rpc_pkey_flags_t flags) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_generate_ec(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const uint8_t * const name, const size_t name_len, + const hal_rpc_pkey_curve_t curve, + const hal_rpc_pkey_flags_t flags) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_delete(const hal_rpc_pkey_handle_t pkey) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_get_key_type(const hal_rpc_pkey_handle_t pkey, + hal_rpc_pkey_key_type_t *type) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_get_key_flags(const hal_rpc_pkey_handle_t pkey, + hal_rpc_pkey_flags_t *flags) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static size_t pkey_get_public_key_len(const hal_rpc_pkey_handle_t pkey) +{ + return 0; +} + +static hal_error_t pkey_get_public_key(const hal_rpc_pkey_handle_t pkey, + uint8_t *der, size_t *der_len, const size_t der_len_max) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_remote_sign(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_remote_verify(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len) +{ + return HAL_ERROR_IMPOSSIBLE; +} + +static hal_error_t pkey_list(hal_rpc_pkey_key_info_t *result, + unsigned *result_len, + const unsigned result_max) +{ + return HAL_ERROR_IMPOSSIBLE; +} + + +/* + * "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. + */ + +static hal_error_t pkey_mixed_sign(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len) +{ + if (input != NULL) + return pkey_remote_sign(session, pkey, hash, input, input_len, output, output_len); + + hal_digest_algorithm_t alg; + size_t digest_len; + hal_error_t err; + + if ((err = hal_rpc_hash_get_algorithm(hash, &alg)) != HAL_OK || + (err = hal_rpc_hash_get_digest_length(alg, &digest_len)) != HAL_OK) + return err; + + uint8_t digest[digest_len]; + + if ((err = hal_rpc_hash_finalize(hash, digest, digest_len)) != HAL_OK) + return err; + + return pkey_remote_sign(session, pkey, hal_rpc_hash_handle_none, digest, digest_len, output, output_len); +} + +static hal_error_t pkey_mixed_verify(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len) +{ + if (input != NULL) + return pkey_remote_verify(session, pkey, hash, input, input_len, output, output_len); + + hal_digest_algorithm_t alg; + size_t digest_len; + hal_error_t err; + + if ((err = hal_rpc_hash_get_algorithm(hash, &alg)) != HAL_OK || + (err = hal_rpc_hash_get_digest_length(alg, &digest_len)) != HAL_OK) + return err; + + uint8_t digest[digest_len]; + + if ((err = hal_rpc_hash_finalize(hash, digest, digest_len)) != HAL_OK) + return err; + + return pkey_remote_verify(session, pkey, hal_rpc_hash_handle_none, digest, digest_len, output, output_len); +} + +/* + * Dispatch vectors. + */ + +const hal_rpc_misc_dispatch_t hal_rpc_remote_misc_dispatch = { + set_pin, login, logout, get_random +}; + +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 +}; + +const hal_rpc_pkey_dispatch_t hal_rpc_remote_pkey_dispatch = { + pkey_load, pkey_find, pkey_generate_rsa, pkey_generate_ec, pkey_delete, + pkey_get_key_type, pkey_get_key_flags, pkey_get_public_key_len, pkey_get_public_key, + pkey_remote_sign, pkey_remote_verify, + pkey_list +}; + +const hal_rpc_pkey_dispatch_t hal_rpc_mixed_pkey_dispatch = { + pkey_load, pkey_find, pkey_generate_rsa, pkey_generate_ec, pkey_delete, + pkey_get_key_type, pkey_get_key_flags, pkey_get_public_key_len, pkey_get_public_key, + pkey_mixed_sign, pkey_mixed_verify, + pkey_list +}; + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/rpc_hash.c b/rpc_hash.c new file mode 100644 index 0000000..4ceadb6 --- /dev/null +++ b/rpc_hash.c @@ -0,0 +1,300 @@ +/* + * rpc_hash.c + * ---------- + * Remote procedure call server-side hash implementation. + * + * 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 "hal.h" +#include "rpc_internal.h" + +/* + * Need table and handle allocation, including some kind of in_use + * flag (perhaps just handle == none). + * + * Hash and HMAC aren't really things for which we need permission + * bits, so not sure we even care about login stuff here. + */ + +typedef struct { + hal_rpc_client_handle_t client_handle; + hal_rpc_session_handle_t session_handle; + hal_rpc_hash_handle_t hash_handle; + union { + hal_hash_state_t *hash; + hal_hmac_state_t *hmac; + } state; +} handle_slot_t; + +#ifndef HAL_STATIC_HASH_STATE_BLOCKS +#define HAL_STATIC_HASH_STATE_BLOCKS 0 +#endif + +#ifndef HAL_STATIC_HMAC_STATE_BLOCKS +#define HAL_STATIC_HMAC_STATE_BLOCKS 0 +#endif + +#if HAL_STATIC_HASH_STATE_BLOCKS > 0 +static handle_slot_t hash_handle[HAL_STATIC_HASH_STATE_BLOCKS]; +#endif + +#if HAL_STATIC_HMAC_STATE_BLOCKS > 0 +static handle_slot_t hmac_handle[HAL_STATIC_HMAC_STATE_BLOCKS]; +#endif + +/* + * Handle allocation is simple: we look for an unused (state == NULL) + * slot in the appropriate table, and, assuming we find one, construct + * a composite handle consisting of a flag telling us which table this + * is, the index into the table, and a counter whose sole purpose is + * to keep the same handle from reoccurring anytime soon, to help + * identify use-after-free bugs in calling code. + */ + +#define HANDLE_FLAG_HMAC 0x80000000 + +static inline handle_slot_t *alloc_handle(const int is_hmac) +{ +#if HAL_STATIC_HASH_STATE_BLOCKS > 0 || HAL_STATIC_HMAC_STATE_BLOCKS > 0 + static uint16_t next_glop = 0; + uint32_t glop = ++next_glop << 16; + next_glop %= 0x7FFF; +#endif + +#if HAL_STATIC_HASH_STATE_BLOCKS > 0 + if (!is_hmac) { + for (int i = 0; i < sizeof(hash_handle)/sizeof(*hash_handle); i++) { + if (hash_handle[i].state.hash == NULL) { + hash_handle[i].hash_handle.handle = i | glop; + return &hash_handle[i]; + } + } + } +#endif + +#if HAL_STATIC_HMAC_STATE_BLOCKS > 0 + if (is_hmac) { + for (int i = 0; i < sizeof(hmac_handle)/sizeof(*hmac_handle); i++) { + if (hmac_handle[i].state.hmac == NULL) { + hmac_handle[i].hash_handle.handle = i | glop | HANDLE_FLAG_HMAC; + return &hmac_handle[i]; + } + } + } +#endif + + return NULL; +} + +/* + * Check a caller-supplied handle. Must be in range, in use, and have + * the right glop. Returns slot pointer on success, NULL otherwise. + */ + +static inline handle_slot_t *find_handle(const hal_rpc_hash_handle_t handle) +{ +#if HAL_STATIC_HASH_STATE_BLOCKS > 0 || HAL_STATIC_HMAC_STATE_BLOCKS > 0 + const int i = (int) (handle.handle & 0xFFFF); + const int is_hmac = (handle.handle & HANDLE_FLAG_HMAC) != 0; +#endif + +#if HAL_STATIC_HASH_STATE_BLOCKS > 0 + if (!is_hmac && i < sizeof(hash_handle)/sizeof(*hash_handle) && + hash_handle[i].hash_handle.handle == handle.handle && hash_handle[i].state.hash != NULL) + return &hash_handle[i]; +#endif + +#if HAL_STATIC_HASH_STATE_BLOCKS > 0 + if (is_hmac && i < sizeof(hmac_handle)/sizeof(*hmac_handle) && + hmac_handle[i].hash_handle.handle == handle.handle && hmac_handle[i].state.hmac != NULL) + return &hmac_handle[i]; +#endif + + return NULL; +} + +/* + * Translate an algorithm number to a descriptor. + */ + +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; + default: return NULL; + } +} + +/* + * Given a slot pointer, fetch the descriptor. + */ + +static inline const hal_hash_descriptor_t *slot_to_descriptor(const handle_slot_t * const slot) +{ + if (slot == NULL) + return NULL; + + if ((slot->hash_handle.handle & HANDLE_FLAG_HMAC) == 0) + return hal_hash_get_descriptor(slot->state.hash); + else + return hal_hmac_get_descriptor(slot->state.hmac); +} + +/* + * Public API + */ + +static hal_error_t get_digest_length(const hal_digest_algorithm_t alg, size_t *length) +{ + const hal_hash_descriptor_t * const d = alg_to_descriptor(alg); + + if (d == NULL || length == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + *length = d->digest_length; + return HAL_OK; +} + +static hal_error_t get_digest_algorithm_id(const hal_digest_algorithm_t alg, + uint8_t *id, size_t *len, const size_t len_max) +{ + const hal_hash_descriptor_t * const d = alg_to_descriptor(alg); + + if (d == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (len != NULL) + *len = d->digest_algorithm_id_length; + + if (id == NULL) + return HAL_OK; + + if (len_max < d->digest_algorithm_id_length) + return HAL_ERROR_RESULT_TOO_LONG; + + memcpy(id, d->digest_algorithm_id, d->digest_algorithm_id_length); + return HAL_OK; +} + +static hal_error_t get_algorithm(const hal_rpc_hash_handle_t handle, hal_digest_algorithm_t *alg) +{ + handle_slot_t *slot = find_handle(handle); + const hal_hash_descriptor_t *descriptor = slot_to_descriptor(slot); + + if (slot == NULL || alg == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (descriptor == NULL) + return HAL_ERROR_IMPOSSIBLE; + + *alg = descriptor->digest_algorithm; + return HAL_OK; +} + +static hal_error_t initialize(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_hash_handle_t *hash, + const hal_digest_algorithm_t alg, + const uint8_t * const key, const size_t key_len) +{ + const hal_hash_descriptor_t *descriptor; + handle_slot_t *slot; + + if (hash == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if ((descriptor = alg_to_descriptor(alg)) == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if ((slot = alloc_handle(key != NULL)) == NULL) + return HAL_ERROR_ALLOCATION_FAILURE; + + slot->client_handle = client; + slot->session_handle = session; + + if (key == NULL) + return hal_hash_initialize(NULL, descriptor, &slot->state.hash, NULL, 0); + else + return hal_hmac_initialize(NULL, descriptor, &slot->state.hmac, NULL, 0, key, key_len); +} + +static hal_error_t update(const hal_rpc_hash_handle_t handle, + const uint8_t * data, const size_t length) +{ + handle_slot_t *slot = find_handle(handle); + + if (slot == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if ((handle.handle & HANDLE_FLAG_HMAC) == 0) + return hal_hash_update(slot->state.hash, data, length); + else + return hal_hmac_update(slot->state.hmac, data, length); +} + +static hal_error_t finalize(const hal_rpc_hash_handle_t handle, + uint8_t *digest, const size_t length) +{ + handle_slot_t *slot = find_handle(handle); + hal_error_t err; + + if (slot == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if ((handle.handle & HANDLE_FLAG_HMAC) == 0) { + err = hal_hash_finalize(slot->state.hash, digest, length); + hal_hash_cleanup(&slot->state.hash); + } + + else { + err = hal_hmac_finalize(slot->state.hmac, digest, length); + hal_hmac_cleanup(&slot->state.hmac); + } + + return err; +} + +const hal_rpc_hash_dispatch_t hal_rpc_remote_hash_dispatch = { + get_digest_length, get_digest_algorithm_id, get_algorithm, initialize, update, finalize +}; + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/rpc_internal.h b/rpc_internal.h new file mode 100644 index 0000000..b861ec7 --- /dev/null +++ b/rpc_internal.h @@ -0,0 +1,191 @@ +/* + * rpc_internal.h + * -------------- + * Internal (not public API) declarations for HAL RPC mechanism. + * + * Authors: Rob Austein, Paul Selkirk + * 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. + */ + +#ifndef _HAL_RPC_INTERNAL_H_ +#define _HAL_RPC_INTERNAL_H_ + +#include "hal_rpc.h" + +/* + * Everything in this file is part of the internal API, that is, + * subject to change without notice. Nothing outside of libhal itself + * should be looking at this file. Access from outside of libhal + * should use the public hal_rpc_*() API. + * + * In particular, the breakdown of which functions go into which + * dispatch vectors is based entirely on pesky details like making + * sure that the right functions get linked in the right cases, and + * should not be construed as making any particular sense in any + * larger context. + */ + +/* + * In theory eventually we might want a fully general mechanism to + * allow us to dispatch arbitrary groups of functions either locally + * or remotely on a per-user basis. In practice, we probably want to + * run everything on the HSM except for hashing and digesting, so just + * code for that case initially while leaving the design open for a + * more general mechanism later if warranted. + * + * So we have three cases: + * + * - We're the HSM, so we do everything locally (ie, we run the RPC + * server functions. + * + * - We're the host, so we do everything remotely (ie, we do + * everything using the client-side RPC calls. + * + * - We're the host but are doing hashing locally, so we do a mix. + * This is slightly more complicated than it might at first appear, + * because we must handle the case of one of the pkey functions + * taking a hash context instead of a literal hash value, in which + * case we have to extract the hash value from the context and + * supply it to the pkey RPC client code as a literal value. + */ + +typedef struct { + + hal_error_t (*set_pin)(const hal_rpc_user_t which, + const char * const newpin, const size_t newpin_len); + + hal_error_t (*login)(const hal_rpc_client_handle_t client, + const hal_rpc_user_t user, + const char * const newpin, const size_t newpin_len); + + hal_error_t (*logout)(const hal_rpc_client_handle_t client); + + hal_error_t (*get_random)(void *buffer, const size_t length); + +} hal_rpc_misc_dispatch_t; + + +typedef struct { + + hal_error_t (*get_digest_length)(const hal_digest_algorithm_t alg, size_t *length); + + hal_error_t (*get_digest_algorithm_id)(const hal_digest_algorithm_t alg, + uint8_t *id, size_t *len, const size_t len_max); + + hal_error_t (*get_algorithm)(const hal_rpc_hash_handle_t hash, hal_digest_algorithm_t *alg); + + hal_error_t (*initialize)(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_hash_handle_t *hash, + const hal_digest_algorithm_t alg, + const uint8_t * const key, const size_t key_length); + + hal_error_t (*update)(const hal_rpc_hash_handle_t hash, + const uint8_t * data, const size_t length); + + hal_error_t (*finalize)(const hal_rpc_hash_handle_t hash, + uint8_t *digest, const size_t length); +} hal_rpc_hash_dispatch_t; + + +typedef struct { + + hal_error_t (*load)(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const hal_rpc_pkey_key_type_t type, + const hal_rpc_pkey_curve_t curve, + const uint8_t * const name, const size_t name_len, + const uint8_t * const der, const size_t der_len, + const hal_rpc_pkey_flags_t flags); + + hal_error_t (*find)(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const hal_rpc_pkey_key_type_t type, + const uint8_t * const name, const size_t name_len); + + hal_error_t (*generate_rsa)(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_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_rpc_pkey_flags_t flags); + + hal_error_t (*generate_ec)(const hal_rpc_client_handle_t client, + const hal_rpc_session_handle_t session, + hal_rpc_pkey_handle_t *pkey, + const uint8_t * const name, const size_t name_len, + const hal_rpc_pkey_curve_t curve, + const hal_rpc_pkey_flags_t flags); + + hal_error_t (*delete)(const hal_rpc_pkey_handle_t pkey); + + hal_error_t (*get_key_type)(const hal_rpc_pkey_handle_t pkey, + hal_rpc_pkey_key_type_t *key_type); + + hal_error_t (*get_key_flags)(const hal_rpc_pkey_handle_t pkey, + hal_rpc_pkey_flags_t *flags); + + size_t (*get_public_key_len)(const hal_rpc_pkey_handle_t pkey); + + hal_error_t (*get_public_key)(const hal_rpc_pkey_handle_t pkey, + uint8_t *der, size_t *der_len, const size_t der_len_max); + + hal_error_t (*sign)(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len); + + hal_error_t (*verify)(const hal_rpc_session_handle_t session, + const hal_rpc_pkey_handle_t pkey, + const hal_rpc_hash_handle_t hash, + const uint8_t * const input, const size_t input_len, + uint8_t * output, const size_t output_len); + + hal_error_t (*list)(hal_rpc_pkey_key_info_t *result, + unsigned *result_len, + const unsigned result_max); + +} hal_rpc_pkey_dispatch_t; + + +extern const hal_rpc_misc_dispatch_t hal_rpc_local_misc_dispatch, hal_rpc_remote_misc_dispatch; +extern const hal_rpc_hash_dispatch_t hal_rpc_local_hash_dispatch, hal_rpc_remote_hash_dispatch; +extern const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch, hal_rpc_remote_pkey_dispatch, hal_rpc_mixed_pkey_dispatch; + +#endif /* _HAL_RPC_INTERNAL_H_ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/tests/test-bus.c b/tests/test-bus.c index 1c60e5d..b4a3e1c 100644 --- a/tests/test-bus.c +++ b/tests/test-bus.c @@ -91,7 +91,8 @@ static void _time_check(char *label, const struct timeval t0, const int err) t.tv_sec -= 1; } rounds = (float)TEST_NUM_ROUNDS/((float)t.tv_sec + ((float)t.tv_usec / 1000000)); - printf("%s%lu.%06lu seconds, %u/sec\n", label, t.tv_sec, t.tv_usec, (unsigned)rounds); + printf("%s%lu.%06lu seconds, %u/sec\n", label, + (unsigned long)t.tv_sec, (unsigned long)t.tv_usec, (unsigned)rounds); } #define time_check(_label_, _expr_) \ diff --git a/utils/cores.c b/utils/cores.c index d59f834..18e994d 100644 --- a/utils/cores.c +++ b/utils/cores.c @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) for (core = hal_core_iterate(NULL); core != NULL; core = hal_core_iterate(core)) { info = hal_core_info(core); - printf("%08lx: %8.8s %4.4s\n", info->base, info->name, info->version); + printf("%08lx: %8.8s %4.4s\n", (unsigned long)info->base, info->name, info->version); } return 0; |