COSE: Add porting layer to psa_crypto
t_cose exposes an API to bind it to arbitrary crypto
library. This change ports t_cose to psa_crypto library.
Change-Id: If59067ee0de02221bb36a5e45c442fe37cd54c68
Signed-off-by: Tamas Ban <tamas.ban@arm.com>
Co-Authored-By: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/lib/t_cose/CMakeLists.txt b/lib/t_cose/CMakeLists.txt
index 544eee9..e1f1985 100644
--- a/lib/t_cose/CMakeLists.txt
+++ b/lib/t_cose/CMakeLists.txt
@@ -25,6 +25,7 @@
list(APPEND ALL_SRC_C
"${T_COSE_DIR}/src/t_cose_sign1_sign.c"
"${T_COSE_DIR}/src/t_cose_util.c"
+ "${T_COSE_DIR}/src/t_cose_psa_crypto.c"
)
#Setting include directories
diff --git a/lib/t_cose/src/t_cose_psa_crypto.c b/lib/t_cose/src/t_cose_psa_crypto.c
new file mode 100644
index 0000000..5c3762b
--- /dev/null
+++ b/lib/t_cose/src/t_cose_psa_crypto.c
@@ -0,0 +1,300 @@
+/*
+ * t_cose_psa_crypto.c
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * Copyright (c) 2019, Arm Limited.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "t_cose_crypto.h"
+#include "attestation_key.h"
+#include "tfm_plat_defs.h"
+#include "tfm_plat_crypto_keys.h"
+#include "secure_utilities.h"
+#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;
+};
+
+enum t_cose_err_t
+t_cose_crypto_pub_key_sign(int32_t cose_alg_id,
+ int32_t key_select,
+ struct useful_buf_c hash_to_sign,
+ struct useful_buf signature_buffer,
+ struct useful_buf_c *signature)
+{
+ enum t_cose_err_t cose_ret = T_COSE_SUCCESS;
+ enum psa_attest_err_t attest_ret;
+ psa_status_t psa_ret;
+ const size_t sig_size = t_cose_signature_size(cose_alg_id);
+
+ /* Avoid compiler warning due to unused argument */
+ (void)key_select;
+
+ if (sig_size > signature_buffer.len) {
+ return T_COSE_ERR_SIG_BUFFER_SIZE;
+ }
+
+ /* FixMe: Registration of key(s) should not be error by attestation service.
+ * Later crypto service is going to get the attestation key from
+ * platform layer.
+ */
+ attest_ret = attest_register_initial_attestation_key();
+ if (attest_ret != PSA_ATTEST_ERR_SUCCESS) {
+ cose_ret = T_COSE_ERR_FAIL;
+ goto error;
+ }
+
+ psa_ret = psa_asymmetric_sign(ATTEST_PRIVATE_KEY_SLOT,
+ 0, /* FixMe: algorithm ID */
+ hash_to_sign.ptr,
+ hash_to_sign.len,
+ signature_buffer.ptr, /* Sig buf */
+ signature_buffer.len, /* Sig buf size */
+ &(signature->len)); /* Sig length */
+
+ if (psa_ret != PSA_SUCCESS) {
+ cose_ret = T_COSE_ERR_FAIL;
+ goto error;
+ } else {
+ signature->ptr = signature_buffer.ptr;
+ }
+
+ attest_ret = attest_unregister_initial_attestation_key();
+ if (attest_ret != PSA_ATTEST_ERR_SUCCESS) {
+ cose_ret = T_COSE_ERR_FAIL;
+ goto error;
+ }
+
+error:
+ return cose_ret;
+}
+
+enum t_cose_err_t
+t_cose_crypto_get_ec_pub_key(int32_t key_select,
+ struct useful_buf_c kid,
+ int32_t *cose_curve_id,
+ struct useful_buf buf_to_hold_x_coord,
+ struct useful_buf buf_to_hold_y_coord,
+ struct useful_buf_c *x_coord,
+ struct useful_buf_c *y_coord)
+{
+ enum tfm_plat_err_t plat_res;
+ enum ecc_curve_t cose_curve;
+ struct ecc_key_t attest_key = {0};
+ uint8_t key_buf[ECC_P_256_KEY_SIZE];
+
+ /* Avoid compiler warning due to unused argument */
+ (void)key_select;
+
+ /* Get the initial attestation key */
+ plat_res = tfm_plat_get_initial_attest_key(key_buf, sizeof(key_buf),
+ &attest_key, &cose_curve);
+
+ /* Check the availability of the private key */
+ if (plat_res != TFM_PLAT_ERR_SUCCESS ||
+ attest_key.pubx_key == NULL ||
+ attest_key.puby_key == NULL) {
+ return T_COSE_ERR_KEY_BUFFER_SIZE;
+ }
+
+ *cose_curve_id = (int32_t)cose_curve;
+
+ /* Check buffer size to avoid overflow */
+ if (buf_to_hold_x_coord.len < attest_key.pubx_key_size) {
+ return T_COSE_ERR_KEY_BUFFER_SIZE;
+ }
+
+ /* Copy the X coordinate of the public key to the buffer */
+ tfm_memcpy(buf_to_hold_x_coord.ptr,
+ (const void *)attest_key.pubx_key,
+ attest_key.pubx_key_size);
+
+ /* Update size */
+ buf_to_hold_x_coord.len = attest_key.pubx_key_size;
+
+ /* Check buffer size to avoid overflow */
+ if (buf_to_hold_y_coord.len < attest_key.puby_key_size) {
+ return T_COSE_ERR_KEY_BUFFER_SIZE;
+ }
+
+ /* Copy the Y coordinate of the public key to the buffer */
+ tfm_memcpy(buf_to_hold_y_coord.ptr,
+ (const void *)attest_key.puby_key,
+ attest_key.puby_key_size);
+
+ /* Update size */
+ buf_to_hold_y_coord.len = attest_key.puby_key_size;
+
+ x_coord->ptr = buf_to_hold_x_coord.ptr;
+ x_coord->len = buf_to_hold_x_coord.len;
+ y_coord->ptr = buf_to_hold_y_coord.ptr;
+ y_coord->len = buf_to_hold_y_coord.len;
+
+ return T_COSE_SUCCESS;
+}
+
+/**
+ * \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()
+{
+ 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
+ *
+ * \return PSA-based hash algorithm ID, or MD4 in the case of error.
+ *
+ */
+static inline psa_algorithm_t cose_hash_alg_id_to_psa(int32_t cose_hash_alg_id)
+{
+ 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;
+ }
+
+ 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)
+{
+ enum t_cose_err_t cose_ret = T_COSE_SUCCESS;
+ psa_status_t psa_ret;
+ struct t_cose_psa_crypto_hash *psa_hash_ctx;
+
+ /* These next 3 lines optimize to nothing except when there is
+ * failure.
+ */
+ cose_ret = check_hash_sizes();
+ if (cose_ret) {
+ goto error;
+ }
+
+ /* 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)) {
+ cose_ret = T_COSE_ERR_HASH_GENERAL_FAIL;
+ goto error;
+ }
+ 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;
+ }
+
+error:
+ return cose_ret;
+}
+
+void t_cose_crypto_hash_update(struct t_cose_crypto_hash *hash_ctx,
+ struct useful_buf_c data_to_hash)
+{
+ struct t_cose_psa_crypto_hash *psa_hash_ctx;
+
+ /* 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;
+ }
+ 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 {
+ /* Intentionally do nothing, just computing the size of the token */
+ }
+ }
+}
+
+enum t_cose_err_t
+t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
+ struct useful_buf buffer_to_hold_result,
+ struct 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;
+
+ /* 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)) {
+ cose_ret = T_COSE_ERR_HASH_GENERAL_FAIL;
+ goto error;
+ }
+ psa_hash_ctx = (struct t_cose_psa_crypto_hash *)hash_ctx;
+
+ 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;
+ }
+
+error:
+ return cose_ret;
+}