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);
 }