Attest: Add attest key id to the COSE header

Key id is part of the unprotected COSE header and
it is an optional field. Make key id inclusion optional
with a compile time define.

Change-Id: I5458c1f74c36015d433b5922e2a5038fb0ea31b7
Signed-off-by: Tamas Ban <tamas.ban@arm.com>
diff --git a/platform/include/tfm_plat_crypto_keys.h b/platform/include/tfm_plat_crypto_keys.h
index 82202f0..7ee9570 100644
--- a/platform/include/tfm_plat_crypto_keys.h
+++ b/platform/include/tfm_plat_crypto_keys.h
@@ -22,20 +22,6 @@
 #endif
 
 /**
- * Elliptic curve key type identifiers according to RFC8152 (COSE encoding)
- * https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
- */
-enum cose_ecc_curve_t {
-    P_256        = 1,  /* NIST P-256 also known as secp256r1 */
-    P_384        = 2,  /* NIST P-384 also known as secp384r1 */
-    P_521        = 3,  /* NIST P-521 also known as secp521r1 */
-    X25519       = 4,  /* X25519 for use with ECDH only      */
-    X448         = 5,  /* X448 for use with ECDH only        */
-    ED25519      = 6,  /* Ed25519 for use with EdDSA only    */
-    ED448        = 7,  /* Ed448 for use with EdDSA only      */
-};
-
-/**
  * Structure definition to carry pointer and size information about an Elliptic
  * curve key which is stored in a buffer(key_buf) in raw format (without
  * encoding):
diff --git a/secure_fw/services/initial_attestation/attest_token.c b/secure_fw/services/initial_attestation/attest_token.c
index 78c0ee0..4e582ae 100644
--- a/secure_fw/services/initial_attestation/attest_token.c
+++ b/secure_fw/services/initial_attestation/attest_token.c
@@ -97,14 +97,24 @@
     int32_t                 t_cose_options = 0;
     struct t_cose_key attest_key;
     psa_key_handle_t private_key;
+    struct q_useful_buf_c attest_key_id = NULL_Q_USEFUL_BUF_C;
+
 
     /* Remember some of the configuration values */
     me->opt_flags  = opt_flags;
     me->key_select = key_select;
 
+#ifdef INCLUDE_TEST_CODE_AND_KEY_ID
     if (opt_flags & TOKEN_OPT_SHORT_CIRCUIT_SIGN) {
         t_cose_options |= T_COSE_OPT_SHORT_CIRCUIT_SIG;
+    } else {
+        attest_ret = attest_get_initial_attestation_key_id(&attest_key_id);
+        if (attest_ret != PSA_ATTEST_ERR_SUCCESS) {
+            return ATTEST_TOKEN_ERR_GENERAL;
+        }
     }
+#endif
+
     t_cose_sign1_sign_init(&(me->signer_ctx), t_cose_options, cose_alg_id);
 
     attest_ret =
@@ -117,7 +127,7 @@
 
     t_cose_sign1_set_signing_key(&(me->signer_ctx),
                                  attest_key,
-                                 NULL_Q_USEFUL_BUF_C); /* No kid */
+                                 attest_key_id);
 
     /* Spin up the CBOR encoder */
     QCBOREncode_Init(&(me->cbor_enc_ctx), *out_buf);
diff --git a/secure_fw/services/initial_attestation/attestation_key.c b/secure_fw/services/initial_attestation/attestation_key.c
index 2dcab02..7dedd0b 100644
--- a/secure_fw/services/initial_attestation/attestation_key.c
+++ b/secure_fw/services/initial_attestation/attestation_key.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2019, Laurence Lundblade.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -11,6 +12,9 @@
 #include "psa/initial_attestation.h"
 #include "platform/include/tfm_plat_defs.h"
 #include "platform/include/tfm_plat_crypto_keys.h"
+#include "t_cose_standard_constants.h"
+#include "q_useful_buf.h"
+#include "qcbor.h"
 
 #define ECC_P256_PUBLIC_KEY_SIZE PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(256)
 
@@ -43,6 +47,10 @@
 static size_t   attestation_public_key_len = 0;
 static psa_ecc_curve_t attestation_key_curve;
 
+#ifdef INCLUDE_TEST_CODE_AND_KEY_ID
+static uint8_t attestation_key_id[PSA_HASH_SIZE(PSA_ALG_SHA_256)]; /* 32bytes */
+#endif
+
 enum psa_attest_err_t
 attest_register_initial_attestation_key()
 {
@@ -161,3 +169,227 @@
 
     return PSA_ATTEST_ERR_SUCCESS;
 }
+
+
+#ifdef INCLUDE_TEST_CODE_AND_KEY_ID
+
+#define MAX_ENCODED_COSE_KEY_SIZE \
+    1 + /* 1 byte to encode map */ \
+    2 + /* 2 bytes to encode key type */ \
+    2 + /* 2 bytes to encode curve */ \
+    2 * /* the X and Y coordinates at 32 bytes each */ \
+        (ECC_P256_COORD_SIZE + 1 + 2)
+
+/**
+ * \brief     Map PSA curve types to the curve type defined by RFC 8152
+ *            chapter 13.1.
+ *
+ * \param[in]  psa_curve  PSA curve type definition \ref psa_ecc_curve_t.
+ *
+ * \return    Return COSE curve type. If mapping is not possible then return
+ *            with -1.
+ */
+static inline int32_t
+attest_map_psa_ecc_curve_to_cose_ecc_curve(psa_ecc_curve_t psa_curve)
+{
+    int32_t cose_curve;
+
+    /* Note:  Mapping is not complete. */
+    switch (psa_curve) {
+    case PSA_ECC_CURVE_SECP256R1:
+        cose_curve = COSE_ELLIPTIC_CURVE_P_256;
+        break;
+    default:
+        /* Initial attestation currently supports only ECDSA P256 signature
+         * therefore the other options are not mapped to save object code
+         */
+        cose_curve = -1;
+    }
+
+    return cose_curve;
+}
+
+/**
+ * \brief CBOR encode a public key as a \c COSE_Key
+ *
+ * \param[in]  psa_ecc_curve        PSA elliptic curve identifiers
+ * \param[in]  attest_public_key    Pointer and length of the buffer, which
+ *                                  holds the attestation public key encoded as
+ *                                  defined by SEC1 &sect;2.3.3.
+ * \param[in]  buffer_for_cose_key  Pointer and length of buffer into which
+ *                                  the encoded \c COSE_Key is put.
+ * \param[out] cose_key             Pointer and length of the encoded
+ *                                  \c COSE_Key.
+ *
+ * \return This returns one of the error codes defined by \ref psa_attest_err_t.
+ *
+ * \c COSE_Key is the COSE-defined format for serializing a key for
+ * transmission in a protocol. This function encodes an EC public key
+ * expressed as an X and Y coordinate.
+ */
+static enum psa_attest_err_t
+attest_encode_key_to_cose_key(psa_ecc_curve_t         psa_ecc_curve,
+                              struct q_useful_buf_c   attest_public_key,
+                              struct q_useful_buf     buffer_for_cose_key,
+                              struct q_useful_buf_c  *cose_key)
+{
+    QCBORError                 qcbor_result;
+    QCBOREncodeContext         cbor_encode_ctx;
+    struct q_useful_buf_c      x_coord;
+    struct q_useful_buf_c      y_coord;
+    struct q_useful_buf_c      encoded_key_id;
+    size_t                     key_coord_len;
+    int32_t                    cose_ecc_curve;
+    uint8_t                    *x_coord_ptr;
+    uint8_t                    *y_coord_ptr;
+    Q_USEFUL_BUF_MAKE_STACK_UB(buffer_for_x_coord, ECC_P256_COORD_SIZE);
+    Q_USEFUL_BUF_MAKE_STACK_UB(buffer_for_y_coord, ECC_P256_COORD_SIZE);
+
+    /* Key is made up of a 0x4 byte and two coordinates
+     * 0x04 || X_COORD || Y_COORD
+     */
+    key_coord_len = (attest_public_key.len - 1) / 2;
+    x_coord_ptr = ((uint8_t *)attest_public_key.ptr) + 1;
+    y_coord_ptr = ((uint8_t *)attest_public_key.ptr) + 1 + key_coord_len;
+
+    /* Place they key parts into the x and y buffers. Stars at index 1 to skip
+     * the 0x4 byte.
+     */
+    x_coord = q_useful_buf_copy_ptr(buffer_for_x_coord,
+                                    x_coord_ptr,
+                                    key_coord_len);
+
+    y_coord = q_useful_buf_copy_ptr(buffer_for_y_coord,
+                                    y_coord_ptr,
+                                    key_coord_len);
+
+    if (q_useful_buf_c_is_null(x_coord) || q_useful_buf_c_is_null(y_coord)) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    cose_ecc_curve = attest_map_psa_ecc_curve_to_cose_ecc_curve(psa_ecc_curve);
+    if (cose_ecc_curve == -1) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    /* Encode it into a COSE_Key structure */
+    QCBOREncode_Init(&cbor_encode_ctx, buffer_for_cose_key);
+    QCBOREncode_OpenMap(&cbor_encode_ctx);
+    QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx,
+                               COSE_KEY_COMMON_KTY,
+                               COSE_KEY_TYPE_EC2);
+    QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx,
+                               COSE_KEY_PARAM_CRV,
+                               cose_ecc_curve);
+    QCBOREncode_AddBytesToMapN(&cbor_encode_ctx,
+                               COSE_KEY_PARAM_X_COORDINATE,
+                               x_coord);
+    QCBOREncode_AddBytesToMapN(&cbor_encode_ctx,
+                               COSE_KEY_PARAM_Y_COORDINATE,
+                               y_coord);
+    QCBOREncode_CloseMap(&cbor_encode_ctx);
+
+    qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &encoded_key_id);
+    if (qcbor_result != QCBOR_SUCCESS) {
+        /* Mainly means that the COSE_Key was too big for buffer_for_cose_key */
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    /* Finish up and return */
+    *cose_key = encoded_key_id;
+
+    return PSA_ATTEST_ERR_SUCCESS;
+}
+
+/**
+ * \brief Make a key ID based on the public key to go in the kid
+ *        unprotected header.
+ *
+ * \param[in]  psa_ecc_curve             PSA elliptic curve identifiers.
+ * \param[in]  attest_public_key         Pointer and length of the buffer, which
+ *                                       holds the attestation public key
+ *                                       encoded as defined by SEC1 &sect;2.3.3.
+ * \param[in]  buffer_for_attest_key_id  Pointer and length of buffer into which
+ *                                       the encoded \c COSE_Key is put.
+ * \param[out] attest_key_id             Pointer and length of the attestation
+ *                                       key id.
+ *
+ * \return This returns one of the error codes defined by \ref psa_attest_err_t.
+ */
+enum psa_attest_err_t
+attest_get_cose_key_id(psa_ecc_curve_t         psa_ecc_curve,
+                       struct q_useful_buf_c   attest_public_key,
+                       struct q_useful_buf     buffer_for_attest_key_id,
+                       struct q_useful_buf_c  *attest_key_id)
+{
+    enum psa_attest_err_t      attest_res;
+    psa_status_t               crypto_res;
+    struct q_useful_buf_c      cose_key;
+    psa_hash_operation_t       hash = psa_hash_operation_init();
+    Q_USEFUL_BUF_MAKE_STACK_UB(buffer_for_cose_key, MAX_ENCODED_COSE_KEY_SIZE);
+
+    /* Encode the attestation public key to COSE_Key structure */
+    attest_res = attest_encode_key_to_cose_key(psa_ecc_curve,
+                                               attest_public_key,
+                                               buffer_for_cose_key,
+                                               &cose_key);
+    if(attest_res != PSA_ATTEST_ERR_SUCCESS) {
+        return attest_res;
+    }
+
+    crypto_res = psa_hash_setup(&hash, PSA_ALG_SHA_256);
+    if (crypto_res != PSA_SUCCESS) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    crypto_res = psa_hash_update(&hash, cose_key.ptr, cose_key.len);
+    if (crypto_res != PSA_SUCCESS) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    crypto_res = psa_hash_finish(&hash,
+                                  buffer_for_attest_key_id.ptr,
+                                  buffer_for_attest_key_id.len,
+                                 &buffer_for_attest_key_id.len);
+    if (crypto_res != PSA_SUCCESS) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    attest_key_id->ptr = buffer_for_attest_key_id.ptr;
+    attest_key_id->len = buffer_for_attest_key_id.len;
+
+    return PSA_ATTEST_ERR_SUCCESS;
+}
+
+enum psa_attest_err_t
+attest_get_initial_attestation_key_id(struct q_useful_buf_c *attest_key_id)
+{
+    enum psa_attest_err_t  attest_res;
+    static uint8_t         attest_key_id_calculated;
+    struct q_useful_buf_c  buffer_for_attest_public_key;
+    struct q_useful_buf    buffer_for_attest_key_id;
+
+    buffer_for_attest_key_id.ptr = attestation_key_id;
+    buffer_for_attest_key_id.len = PSA_HASH_SIZE(PSA_ALG_SHA_256);
+
+    /* Needs to calculate only once */
+    if (attest_key_id_calculated == 0) {
+        buffer_for_attest_public_key.ptr = attestation_public_key;
+        buffer_for_attest_public_key.len = attestation_public_key_len;
+
+        attest_res = attest_get_cose_key_id(attestation_key_curve,
+                                            buffer_for_attest_public_key,
+                                            buffer_for_attest_key_id,
+                                            attest_key_id);
+        if (attest_res != PSA_ATTEST_ERR_SUCCESS) {
+            return attest_res;
+        }
+        attest_key_id_calculated = 1;
+    } else {
+        attest_key_id->ptr = (const void *)buffer_for_attest_key_id.ptr;
+        attest_key_id->len = buffer_for_attest_key_id.len;
+    }
+
+    return PSA_ATTEST_ERR_SUCCESS;
+}
+#endif
diff --git a/secure_fw/services/initial_attestation/attestation_key.h b/secure_fw/services/initial_attestation/attestation_key.h
index c1b7b3c..cb35a94 100644
--- a/secure_fw/services/initial_attestation/attestation_key.h
+++ b/secure_fw/services/initial_attestation/attestation_key.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, Arm Limited. All rights reserved.
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -10,6 +10,7 @@
 
 #include "psa/initial_attestation.h"
 #include "psa/crypto.h"
+#include "q_useful_buf.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -66,6 +67,20 @@
 attest_get_initial_attestation_public_key(uint8_t **public_key,
                                           size_t *public_key_len,
                                           psa_ecc_curve_t *public_key_curve);
+
+/**
+ * \brief Get the attestation key ID. It is the hash (SHA256) of the COSE_Key
+ *        encoded attestation public key.
+ *
+ * \param[out] attest_key_id  Pointer and length of the key id.
+ *
+ * \retval  PSA_ATTEST_ERR_SUCCESS   Key id calculated successfully.
+ * \retval  PSA_ATTEST_ERR_GENERAL   Key id calculation failed.
+
+ */
+enum psa_attest_err_t
+attest_get_initial_attestation_key_id(struct q_useful_buf_c *attest_key_id);
+
 #ifdef __cplusplus
 }
 #endif