COSE: Rework of crypto hash portability layer
This address the unaligned memory issue with the previous
design.
Change-Id: I69660d1bc3aeb260f7603f881d2c9e5ac94c75e8
Signed-off-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/lib/t_cose/src/t_cose_crypto.h b/lib/t_cose/src/t_cose_crypto.h
index d66f983..b790912 100644
--- a/lib/t_cose/src/t_cose_crypto.h
+++ b/lib/t_cose/src/t_cose_crypto.h
@@ -290,15 +290,60 @@
+#ifdef T_COSE_USE_B_CON_SHA256
+/* This is code for use with Brad Conte's crypto. See
+ * https://github.com/B-Con/crypto-algorithms and see the description
+ * of t_cose_crypto_hash
+ */
+#include "sha256.h"
+#endif
+
+
/**
* The context for use with the hash adaptation layer here.
+ *
+ * Hash implementations for this porting layer are put into two
+ * different categories.
+ *
+ * The first can be supported generically without any dependency on
+ * the actual hash implementation in this header. These only need a
+ * pointer or handle for the hash context. Usually these are
+ * implemented by a service, system API or crypto HW that runs in a
+ * separate context or process. They probably allocate memory
+ * internally. These can use context.ptr or context.handle to hold the
+ * pointer or handle to the hash context.
+ *
+ * The second sort of hash implementations need more than just a
+ * pointer or handle. Typically these are libraries that are linked
+ * with this code and run in the same process / context / thread as
+ * this code. These can be efficient requiring no context switches or
+ * memory allocations. These type require this header be modified for
+ * the #include which defines the hash context and so this struct
+ * includes that context as a member. This context is allocated on the
+ * stack, so any members added here should be small enough to go on
+ * the stack. USE_B_CON_SHA256 is an example of this type.
+ *
+ * The actual implementation of the hash is in a separate .c file
+ * that will be specific to the particular platform, library,
+ * service or such used.
*/
struct t_cose_crypto_hash {
- /* Can't put the actual size here without creating dependecy on
- * actual hash implementation, so this is a fairly large and
- * accommodating size.
+
+#ifdef T_COSE_USE_B_CON_SHA256
+ /* Specific context for Brad Conte's sha256.c */
+ SHA256_CTX context;
+#else
+ /*
+ * Generic pointer / handle that can work for many
+ * hash implementations.
*/
- uint8_t bytes[128];
+ union {
+ void *ptr;
+ uint64_t handle;
+ } context;
+ int64_t status;
+#endif
+
};
@@ -362,6 +407,11 @@
* hash_ctx and returned when t_cose_crypto_hash_finish() is called.
* Once in the error state, this function may be called, but it will
* not do anything.
+ *
+ * This function can be called with \c data_to_hash.ptr NULL and it
+ * will pretend to hash. This allows the same code that is used to
+ * produce the real hash to be used to return a length of the would be
+ * hash for encoded data structure size calculations.
*/
void t_cose_crypto_hash_update(struct t_cose_crypto_hash *hash_ctx,
struct q_useful_buf_c data_to_hash);
@@ -389,8 +439,8 @@
* returned here.
*
* See the note in the Detailed Description (the \\file comment block)
- * for details on how \c q_useful_buf and \c q_useful_buf_c are used to
- * return the hash.
+ * for details on how \c q_useful_buf and \c q_useful_buf_c are used
+ * to return the hash.
*/
enum t_cose_err_t
t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
diff --git a/lib/t_cose/src/t_cose_psa_crypto_hash.c b/lib/t_cose/src/t_cose_psa_crypto_hash.c
index 0ca3ba3..b796754 100644
--- a/lib/t_cose/src/t_cose_psa_crypto_hash.c
+++ b/lib/t_cose/src/t_cose_psa_crypto_hash.c
@@ -14,35 +14,6 @@
#include "psa_crypto.h"
/**
- * \brief Context for PSA hash adaptation.
- *
- * Hash context for PSA hash implementation. This is fit into and cast
- * to/from struct \ref t_cose_crypto_hash.
- */
-struct t_cose_psa_crypto_hash {
- psa_status_t status;
- psa_hash_operation_t operation;
-};
-
-/**
- * \brief Check some of the sizes for hash implementation.
- *
- * \return Value from \ref t_cose_err_t error if sizes are not correct.
- *
- * It makes sure the constants in the header file match the local
- * implementation. This gets evaluated at compile time and will
- * optimize out to nothing when all checks pass.
- */
-static inline enum t_cose_err_t check_hash_sizes(void)
-{
- if (T_COSE_CRYPTO_SHA256_SIZE != PSA_HASH_SIZE(PSA_ALG_SHA_256)) {
- return T_COSE_ERR_HASH_GENERAL_FAIL;
- }
-
- return T_COSE_SUCCESS;
-}
-
-/**
* \brief Convert COSE algorithm ID to a PSA algorithm ID
*
* \param[in] cose_hash_alg_id The COSE-based ID for the
@@ -55,9 +26,11 @@
psa_algorithm_t return_value;
switch (cose_hash_alg_id) {
+
case COSE_ALG_SHA256_PROPRIETARY:
return_value = PSA_ALG_SHA_256;
break;
+
default:
return_value = PSA_ALG_MD4;
break;
@@ -66,115 +39,145 @@
return return_value;
}
-enum t_cose_err_t
-t_cose_crypto_hash_start(struct t_cose_crypto_hash *hash_ctx,
- int32_t cose_hash_alg_id)
+
+/**
+ * \brief Map a PSA error into a t_cose error.
+ *
+ * \param[in] status The PSA status.
+ *
+ * \return The t_cose error.
+ */
+static inline enum t_cose_err_t psa_status_to_t_cose_error(psa_status_t status)
{
- enum t_cose_err_t cose_ret = T_COSE_SUCCESS;
- psa_status_t psa_ret;
- struct t_cose_psa_crypto_hash *psa_hash_ctx;
+ switch (status) {
- /* These next 3 lines optimize to nothing except when there is
- * failure.
- */
- cose_ret = check_hash_sizes();
- if (cose_ret) {
- return cose_ret;
- }
+ case PSA_SUCCESS:
+ return T_COSE_SUCCESS;
- /* This mostly turns into a compile-time check. If it succeeds,
- * then it optimizes out to nothing. If it fails it will
- * short-circuit this function to always create an error.
- */
- if (sizeof(struct t_cose_crypto_hash) <
- sizeof(struct t_cose_psa_crypto_hash)) {
+ case PSA_ERROR_NOT_SUPPORTED:
+ return T_COSE_ERR_UNSUPPORTED_HASH;
+
+ case PSA_ERROR_BUFFER_TOO_SMALL:
+ return T_COSE_ERR_HASH_BUFFER_SIZE;
+
+ default:
return T_COSE_ERR_HASH_GENERAL_FAIL;
}
- psa_hash_ctx = (struct t_cose_psa_crypto_hash *)hash_ctx;
-
- psa_ret = psa_hash_setup(&psa_hash_ctx->operation,
- cose_hash_alg_id_to_psa(cose_hash_alg_id));
-
- if (psa_ret == PSA_SUCCESS) {
- psa_hash_ctx->status = PSA_SUCCESS;
- cose_ret = T_COSE_SUCCESS;
- } else if (psa_ret == PSA_ERROR_NOT_SUPPORTED) {
- cose_ret = T_COSE_ERR_UNSUPPORTED_HASH;
- } else {
- cose_ret = T_COSE_ERR_HASH_GENERAL_FAIL;
- }
-
- return cose_ret;
}
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
+enum t_cose_err_t t_cose_crypto_hash_start(struct t_cose_crypto_hash *hash_ctx,
+ int32_t cose_hash_alg_id)
+{
+ /* Here's how t_cose_crypto_hash is used with PSA hashes.
+ *
+ * If you look inside psa_hash.handle is just a uint32_t that is
+ * used as a handle. To avoid modifying t_cose_crypto.h in a
+ * PSA-specific way, this implementation just copies the PSA
+ * handle from the generic t_cose_crypto_hash on entry to a hash
+ * function, and back on exit.
+ *
+ * This could have been implemented by modifying t_cose_crypto.h
+ * so that psa_hash_operation_t is a member of t_cose_crypto_hash.
+ * It's nice to not have to modify t_cose_crypto.h.
+ *
+ * This would have been cleaner if psa_hash_operation_t didn't
+ * exist and the PSA crypto just used a plain pointer or integer
+ * handle. If psa_hash_operation_t is changed to be different
+ * than just the single uint32_t, then this code has to change.
+ *
+ * The status member of t_cose_crypto_hash is used to hold a
+ * psa_status_t error code.
+ */
+ psa_hash_operation_t psa_hash;
+ psa_algorithm_t psa_alg;
+
+ /* Copy the PSA handle out of the generic context */
+ psa_hash.handle = (uint32_t)hash_ctx->context.handle;
+
+ /* Map the algorithm ID */
+ psa_alg = cose_hash_alg_id_to_psa(cose_hash_alg_id);
+
+ /* Actually do the hash set up */
+ hash_ctx->status = psa_hash_setup(&psa_hash, psa_alg);
+
+ /* Copy the PSA handle back into the context */
+ hash_ctx->context.handle = psa_hash.handle;
+
+ /* Map errors and return */
+ return psa_status_to_t_cose_error((psa_status_t)hash_ctx->status);
+}
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
void t_cose_crypto_hash_update(struct t_cose_crypto_hash *hash_ctx,
struct q_useful_buf_c data_to_hash)
{
- struct t_cose_psa_crypto_hash *psa_hash_ctx;
+ /* See t_cose_crypto_hash_start() for context handling details */
+ psa_hash_operation_t psa_hash;
- /* This mostly turns into a compile-time check. If it succeeds,
- * then it optimizes out to nothing. If it fails it will
- * short-circuit this function to always create an error.
- */
- if (sizeof(struct t_cose_crypto_hash) <
- sizeof(struct t_cose_psa_crypto_hash)) {
+ /* Copy the PSA handle out of the generic context */
+ psa_hash.handle = (uint32_t)hash_ctx->context.handle;
+
+ if(hash_ctx->status != PSA_SUCCESS) {
+ /* In error state. Nothing to do. */
return;
}
- psa_hash_ctx = (struct t_cose_psa_crypto_hash *)hash_ctx;
- if (psa_hash_ctx->status == PSA_SUCCESS) {
- if (data_to_hash.ptr != NULL) {
- psa_hash_ctx->status = psa_hash_update(&psa_hash_ctx->operation,
- data_to_hash.ptr,
- data_to_hash.len);
- } else {
- /* No data was passed in to be hashed indicating the mode of use is
- * the computation of the size of hash. This mode is hashing is used
- * by t_cose when it is requested to compute the size of the signed
- * data it might compute, which is in turn used to compute the
- * size of a would be token. When computing the size, the size
- * like this, there is nothing to do in update()
- */
- }
+ if(data_to_hash.ptr == NULL) {
+ /* No data was passed in to be hashed indicating the mode of use is
+ * the computation of the size of hash. This mode is hashing is used
+ * by t_cose when it is requested to compute the size of the signed
+ * data it might compute, which is in turn used to compute the
+ * size of a would be token. When computing the size, the size
+ * like this, there is nothing to do in update()
+ */
+ return;
}
+
+ /* Actually hash the data */
+ hash_ctx->status = psa_hash_update(&psa_hash,
+ data_to_hash.ptr,
+ data_to_hash.len);
+
+ /* Copy the PSA handle back into the context. */
+ hash_ctx->context.handle = psa_hash.handle;
}
+/*
+ * See documentation in t_cose_crypto.h
+ */
enum t_cose_err_t
t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
struct q_useful_buf buffer_to_hold_result,
struct q_useful_buf_c *hash_result)
{
- enum t_cose_err_t cose_ret = T_COSE_SUCCESS;
- psa_status_t psa_ret;
- struct t_cose_psa_crypto_hash *psa_hash_ctx;
+ /* See t_cose_crypto_hash_start() for context handling details */
+ psa_hash_operation_t psa_hash;
- /* This mostly turns into a compile-time check. If it succeeds,
- * then it optimizes out to nothing. If it fails it will
- * short-circuit this function to always create an error.
- */
- if (sizeof(struct t_cose_crypto_hash) <
- sizeof(struct t_cose_psa_crypto_hash)) {
- return T_COSE_ERR_HASH_GENERAL_FAIL;
- }
- psa_hash_ctx = (struct t_cose_psa_crypto_hash *)hash_ctx;
+ /* Copy the PSA handle out of the generic context */
+ psa_hash.handle = (uint32_t)hash_ctx->context.handle;
- if (psa_hash_ctx->status == PSA_SUCCESS) {
- psa_ret = psa_hash_finish(&psa_hash_ctx->operation,
- buffer_to_hold_result.ptr,
- buffer_to_hold_result.len,
- &(hash_result->len));
-
- if (psa_ret == PSA_SUCCESS) {
- hash_result->ptr = buffer_to_hold_result.ptr;
- cose_ret = T_COSE_SUCCESS;
- } else if (psa_ret == PSA_ERROR_BUFFER_TOO_SMALL) {
- cose_ret = T_COSE_ERR_HASH_BUFFER_SIZE;
- } else {
- cose_ret = T_COSE_ERR_HASH_GENERAL_FAIL;
- }
- } else {
- cose_ret = T_COSE_ERR_HASH_GENERAL_FAIL;
+ if(hash_ctx->status != PSA_SUCCESS) {
+ /* Error state. Nothing to do */
+ goto Done;
}
- return cose_ret;
+ /* Actually finish up the hash */
+ hash_ctx->status = psa_hash_finish(&psa_hash,
+ buffer_to_hold_result.ptr,
+ buffer_to_hold_result.len,
+ &(hash_result->len));
+
+ hash_result->ptr = buffer_to_hold_result.ptr;
+
+ /* Copy the PSA handle back into the context. */
+ hash_ctx->context.handle = psa_hash.handle;
+
+Done:
+ return psa_status_to_t_cose_error((psa_status_t)hash_ctx->status);
}