aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ext/qcbor/util/qcbor_util.c2
-rw-r--r--secure_fw/partitions/initial_attestation/attest_token.h9
-rw-r--r--test/suites/attestation/CMakeLists.inc2
-rw-r--r--test/suites/attestation/attest_symmetric_iat_decode.c159
-rw-r--r--test/suites/attestation/attest_token_decode.c667
-rw-r--r--test/suites/attestation/attest_token_decode.h89
-rw-r--r--test/suites/attestation/attest_token_decode_common.c601
7 files changed, 858 insertions, 671 deletions
diff --git a/lib/ext/qcbor/util/qcbor_util.c b/lib/ext/qcbor/util/qcbor_util.c
index 5da3898dbc..8d0930ec75 100644
--- a/lib/ext/qcbor/util/qcbor_util.c
+++ b/lib/ext/qcbor/util/qcbor_util.c
@@ -230,7 +230,7 @@ qcbor_util_get_top_level_item_in_map(struct q_useful_buf_c payload,
QCBORError cbor_error;
if(q_useful_buf_c_is_null(payload)) {
- return_value = ATTEST_TOKEN_ERR_COSE_SIGN1_VALIDATION;
+ return_value = ATTEST_TOKEN_ERR_COSE_VALIDATION;
goto Done;
}
diff --git a/secure_fw/partitions/initial_attestation/attest_token.h b/secure_fw/partitions/initial_attestation/attest_token.h
index 4897c9e971..33c935408c 100644
--- a/secure_fw/partitions/initial_attestation/attest_token.h
+++ b/secure_fw/partitions/initial_attestation/attest_token.h
@@ -72,11 +72,12 @@ enum attest_token_err_t {
/** Integer too large, for example an \c int32_t is required, but
value only fits in \c int64_t */
ATTEST_TOKEN_ERR_INTEGER_VALUE,
- /** Something is wrong with the COSE signing structure, missing
+ /** Something is wrong with the COSE message structure, missing
headers or such. */
- ATTEST_TOKEN_ERR_COSE_SIGN1_FORMAT,
- /** COSE signature is invalid, data is corrupted. */
- ATTEST_TOKEN_ERR_COSE_SIGN1_VALIDATION,
+ ATTEST_TOKEN_ERR_COSE_FORMAT,
+ /** COSE signature or authentication tag is invalid, data
+ is corrupted. */
+ ATTEST_TOKEN_ERR_COSE_VALIDATION,
/** The signing algorithm is not supported. */
ATTEST_TOKEN_ERR_UNSUPPORTED_SIG_ALG,
/** Out of memory. */
diff --git a/test/suites/attestation/CMakeLists.inc b/test/suites/attestation/CMakeLists.inc
index 4c9478f462..f761640e58 100644
--- a/test/suites/attestation/CMakeLists.inc
+++ b/test/suites/attestation/CMakeLists.inc
@@ -38,6 +38,7 @@ elseif(ENABLE_ATTESTATION_SERVICE_TESTS)
list(APPEND ATTEST_TEST_SRC_S
"${ATTESTATION_TEST_DIR}/secure/attestation_s_interface_testsuite.c"
"${ATTESTATION_TEST_DIR}/attest_token_test.c"
+ "${ATTESTATION_TEST_DIR}/attest_token_decode_common.c"
"${ATTESTATION_TEST_DIR}/attest_token_decode.c"
"${ATTESTATION_TEST_DIR}/attest_public_key.c"
"${TFM_ROOT_DIR}/lib/ext/qcbor/util/qcbor_util.c"
@@ -46,6 +47,7 @@ elseif(ENABLE_ATTESTATION_SERVICE_TESTS)
list(APPEND ATTEST_TEST_SRC_NS
"${ATTESTATION_TEST_DIR}/non_secure/attestation_ns_interface_testsuite.c"
"${ATTESTATION_TEST_DIR}/attest_token_test.c"
+ "${ATTESTATION_TEST_DIR}/attest_token_decode_common.c"
"${ATTESTATION_TEST_DIR}/attest_token_decode.c"
"${ATTESTATION_TEST_DIR}/attest_public_key.c"
"${TFM_ROOT_DIR}/lib/ext/qcbor/util/qcbor_util.c"
diff --git a/test/suites/attestation/attest_symmetric_iat_decode.c b/test/suites/attestation/attest_symmetric_iat_decode.c
new file mode 100644
index 0000000000..69c32b0f7d
--- /dev/null
+++ b/test/suites/attestation/attest_symmetric_iat_decode.c
@@ -0,0 +1,159 @@
+/*
+ * attest_symmetric_iat_decode.c
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_decode.h"
+#include "attestation.h"
+#include "psa/crypto.h"
+#include "q_useful_buf.h"
+#include "qcbor_util.h"
+#include "t_cose_common.h"
+#include "t_cose_mac0_verify.h"
+#include "tfm_plat_crypto_keys.h"
+
+/* Only support HMAC as MAC algorithm in COSE_Mac0 so far */
+#define SYMMETRIC_IAK_MAX_SIZE PSA_MAC_MAX_SIZE
+
+#if DOMAIN_NS == 1U
+/*
+ * Public function. See attest_token_decode.h
+ * It is not allowed to let NS side fetch the symmetric IAK and perform the MAC
+ * verification.
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+ struct q_useful_buf_c token)
+{
+ enum t_cose_err_t t_cose_error;
+ enum attest_token_err_t return_value;
+ /* Decode only without signature verification */
+ int32_t t_cose_options = T_COSE_OPT_DECODE_ONLY;
+ struct t_cose_mac0_verify_ctx verify_ctx;
+ struct t_cose_key attest_key = T_COSE_NULL_KEY;
+
+ t_cose_mac0_verify_init(&verify_ctx, t_cose_options);
+
+ t_cose_mac0_set_verify_key(&verify_ctx, attest_key);
+
+ t_cose_error = t_cose_mac0_verify(&verify_ctx,
+ token, /* COSE to verify */
+ &me->payload, /* Payload from token */
+ NULL
+ );
+
+ return_value = map_t_cose_errors(t_cose_error);
+ me->last_error = return_value;
+
+ return return_value;
+}
+#else /* DOMAIN_NS == 1U */
+/*
+ * \note The symmetric Initial Attestation key (IAK) will be fetched for
+ * authentication tag verification in secure test cases.
+ * Authentication tag verification in tests is for debug purpose only.
+ * Do not fetch the IAK outside attestation service in real products.
+ */
+static inline enum attest_token_err_t
+decode_register_verify_key(psa_key_handle_t *verify_key_handle)
+{
+ uint8_t key_buf[SYMMETRIC_IAK_MAX_SIZE];
+ psa_algorithm_t key_alg;
+ psa_key_handle_t key_handle;
+ size_t key_len;
+ enum tfm_plat_err_t plat_res;
+ psa_status_t psa_res;
+ psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+ /* Get the symmetric initial attestation key for HMAC operation */
+ plat_res = tfm_plat_get_symmetric_iak(key_buf, sizeof(key_buf),
+ &key_len, &key_alg);
+ if (plat_res != TFM_PLAT_ERR_SUCCESS) {
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ }
+
+ /*
+ * Verify if HMAC algorithm is valid.
+ * According to COSE (RFC 8152), only SHA-256, SHA-384 and SHA-512 are
+ * supported in HMAC.
+ */
+ if ((key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_256)) && \
+ (key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_384)) && \
+ (key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_512))) {
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ }
+
+ /* Setup the key attributes */
+ psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY);
+ psa_set_key_algorithm(&key_attributes, key_alg);
+ psa_set_key_type(&key_attributes, PSA_KEY_TYPE_HMAC);
+
+ /* Register the symmetric key to Crypto service */
+ psa_res = psa_import_key(&key_attributes, key_buf, key_len, &key_handle);
+ if (psa_res != PSA_SUCCESS) {
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ }
+
+ *verify_key_handle = key_handle;
+
+ return ATTEST_TOKEN_ERR_SUCCESS;
+}
+
+static inline enum attest_token_err_t
+decode_unregister_verify_key(psa_key_handle_t verify_key_handle)
+{
+ psa_status_t status;
+
+ status = psa_destroy_key(verify_key_handle);
+ if (status == PSA_SUCCESS) {
+ return ATTEST_TOKEN_ERR_SUCCESS;
+ }
+
+ return ATTEST_TOKEN_ERR_GENERAL;
+}
+
+/*
+ * Public function. See attest_token_decode.h
+ * Decode the received COSE_Mac0 structure and verify the tag.
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+ struct q_useful_buf_c token)
+{
+ enum t_cose_err_t t_cose_error;
+ enum attest_token_err_t return_value;
+ int32_t t_cose_options = 0;
+ struct t_cose_mac0_verify_ctx verify_ctx;
+ struct t_cose_key attest_key;
+ psa_key_handle_t key_handle;
+
+ return_value = decode_register_verify_key(&key_handle);
+ if (return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ return return_value;
+ }
+
+ t_cose_mac0_verify_init(&verify_ctx, t_cose_options);
+
+ attest_key.crypto_lib = T_COSE_CRYPTO_LIB_PSA;
+ attest_key.k.key_handle = (uint64_t)key_handle;
+ t_cose_mac0_set_verify_key(&verify_ctx, attest_key);
+
+ t_cose_error = t_cose_mac0_verify(&verify_ctx,
+ token, /* COSE to verify */
+ &me->payload, /* Payload from token */
+ NULL);
+
+ return_value = map_t_cose_errors(t_cose_error);
+ me->last_error = return_value;
+
+ decode_unregister_verify_key(key_handle);
+
+ return return_value;
+}
+#endif /* DOMAIN_NS == 1U */
diff --git a/test/suites/attestation/attest_token_decode.c b/test/suites/attestation/attest_token_decode.c
index 18fef5e073..90fbcf5299 100644
--- a/test/suites/attestation/attest_token_decode.c
+++ b/test/suites/attestation/attest_token_decode.c
@@ -20,129 +20,8 @@
/**
* \file attest_token_decode.c
*
- * \brief Attestation token decoder.
- *
- * This decodes and verifies an attestation token giving access to the
- * data items in the token. The data items are also known as claims.
- *
- * This is written primarily as tests for the token encoder, though it
- * close to a full commercial token decoder. The main thing missing is
- * a thorough test suite for it. Test before commercial use is
- * important as this is a parser / decoder and thus subject to attack
- * by malicious input. It does however, use QCBOR for most base
- * parsing, and QCBOR is thoroughly tested and commercial.
- *
- * This is oriented around the Arm-defined initial attestation token.
- *
- * \c uint_fast8_t is used for type and nest levels. They are
- * 8-bit quantities, but making using uint8_t variables
- * and parameters can result in bigger, slower code.
- * \c uint_fast8_t is part of \c <stdint.h>. It is not
- * used in structures where it is more important to keep
- * the size smaller.
- */
-
-
-/**
- * Compute the bit indicating a claim is present
+ * \brief Attestation token decoder for COSE_Sign1.
*/
-#define CLAIM_PRESENT_BIT(item_index) (0x01U << (item_index))
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-void attest_token_decode_init(struct attest_token_decode_context *me,
- uint32_t options)
-{
- memset(me, 0, sizeof(struct attest_token_decode_context));
- me->options = options;
- me->last_error = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
-}
-
-
-/**
-
- \brief Map t_cose errors into attestation token errors
-
- \param[in] t_cose_error The t_cose error to map
-
- \return The attestation token error.
- */
-static inline enum attest_token_err_t
-map_t_cose_errors(enum t_cose_err_t t_cose_error)
-{
- switch (t_cose_error) {
- case T_COSE_SUCCESS:
- return ATTEST_TOKEN_ERR_SUCCESS;
- break;
- case T_COSE_ERR_UNSUPPORTED_SIGNING_ALG:
- return ATTEST_TOKEN_ERR_UNSUPPORTED_SIG_ALG;
- break;
- case T_COSE_ERR_UNSUPPORTED_HASH:
- return ATTEST_TOKEN_ERR_HASH_UNAVAILABLE;
- break;
- case T_COSE_ERR_CBOR_NOT_WELL_FORMED:
- return ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
- break;
- case T_COSE_ERR_INSUFFICIENT_MEMORY:
- return ATTEST_TOKEN_ERR_INSUFFICIENT_MEMORY;
- break;
- case T_COSE_ERR_TAMPERING_DETECTED:
- return ATTEST_TOKEN_ERR_TAMPERING_DETECTED;
- break;
- case T_COSE_ERR_CBOR_FORMATTING:
- return ATTEST_TOKEN_ERR_CBOR_FORMATTING;
- break;
- case T_COSE_ERR_TOO_SMALL:
- return ATTEST_TOKEN_ERR_TOO_SMALL;
- break;
-
- case T_COSE_ERR_PARAMETER_CBOR:
- case T_COSE_ERR_NON_INTEGER_ALG_ID:
- return ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
- break;
-
- case T_COSE_ERR_SIG_VERIFY:
- case T_COSE_ERR_SHORT_CIRCUIT_SIG:
- return ATTEST_TOKEN_ERR_COSE_SIGN1_VALIDATION;
- break;
-
- case T_COSE_ERR_SIGN1_FORMAT:
- case T_COSE_ERR_NO_ALG_ID:
- case T_COSE_ERR_NO_KID:
- case T_COSE_ERR_BAD_SHORT_CIRCUIT_KID:
- case T_COSE_ERR_SIG_STRUCT:
- return ATTEST_TOKEN_ERR_COSE_SIGN1_FORMAT;
- break;
-
- case T_COSE_ERR_UNKNOWN_KEY:
- case T_COSE_ERR_WRONG_TYPE_OF_KEY:
- return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
- break;
-
- case T_COSE_ERR_MAKING_PROTECTED:
- case T_COSE_ERR_HASH_GENERAL_FAIL:
- case T_COSE_ERR_HASH_BUFFER_SIZE:
- case T_COSE_ERR_SIG_BUFFER_SIZE:
- case T_COSE_ERR_INVALID_ARGUMENT:
- case T_COSE_ERR_FAIL:
- case T_COSE_ERR_SIG_FAIL:
- case T_COSE_ERR_TOO_MANY_PARAMETERS:
- case T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER:
- case T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED:
- case T_COSE_ERR_INCORRECT_KEY_FOR_LIB:
- case T_COSE_ERR_BAD_CONTENT_TYPE:
- case T_COSE_ERR_INCORRECTLY_TAGGED:
- case T_COSE_ERR_EMPTY_KEY:
- case T_COSE_ERR_DUPLICATE_PARAMETER:
- case T_COSE_ERR_PARAMETER_NOT_PROTECTED:
- case T_COSE_ERR_CRIT_PARAMETER:
- default:
- return ATTEST_TOKEN_ERR_GENERAL;
- }
-}
-
/*
* Public function. See attest_token_decode.h
@@ -190,547 +69,3 @@ attest_token_decode_validate_token(struct attest_token_decode_context *me,
return return_value;
}
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-enum attest_token_err_t
-attest_token_decode_get_bstr(struct attest_token_decode_context *me,
- int32_t label,
- struct q_useful_buf_c *claim)
-{
- enum attest_token_err_t return_value;
- QCBORItem item;
-
- if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
- return_value = me->last_error;
- *claim = NULL_Q_USEFUL_BUF_C;
- goto Done;
- }
-
- return_value = qcbor_util_get_top_level_item_in_map(me->payload,
- label,
- QCBOR_TYPE_BYTE_STRING,
- &item);
- if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
- goto Done;
- }
-
- *claim = item.val.string;
-
-Done:
- return return_value;
-}
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-enum attest_token_err_t
-attest_token_decode_get_tstr(struct attest_token_decode_context *me,
- int32_t label,
- struct q_useful_buf_c *claim)
-{
- enum attest_token_err_t return_value;
- QCBORItem item;
-
- if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
- return_value = me->last_error;
- *claim = NULL_Q_USEFUL_BUF_C;
- goto Done;
- }
-
- return_value = qcbor_util_get_top_level_item_in_map(me->payload,
- label,
- QCBOR_TYPE_TEXT_STRING,
- &item);
- if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
- goto Done;
- }
-
- *claim = item.val.string;
-
-Done:
- return return_value;
-}
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-enum attest_token_err_t
-attest_token_decode_get_int(struct attest_token_decode_context *me,
- int32_t label,
- int64_t *integer)
-{
- enum attest_token_err_t return_value;
- QCBORItem item;
- QCBORDecodeContext decode_context;
-
- if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
- return_value = me->last_error;
- *integer = 0;
- goto Done;
- }
-
- QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
-
- return_value = qcbor_util_get_item_in_map(&decode_context,
- label,
- &item);
- if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
- goto Done;
- }
-
- if(QCBORDecode_Finish(&decode_context)) {
- return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
- }
-
- if(item.uDataType == QCBOR_TYPE_INT64) {
- *integer = item.val.int64;
- } else if(item.uDataType == QCBOR_TYPE_UINT64) {
- if(item.val.uint64 < INT64_MAX) {
- *integer = (int64_t)item.val.uint64;
- } else {
- return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
- }
- } else {
- return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
- }
-
-Done:
- return return_value;
-}
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-enum attest_token_err_t
-attest_token_decode_get_uint(struct attest_token_decode_context *me,
- int32_t label,
- uint64_t *integer)
-{
- enum attest_token_err_t return_value;
- QCBORItem item;
- QCBORDecodeContext decode_context;
-
- if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
- return_value = me->last_error;
- *integer = 0;
- goto Done;
- }
-
- QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
-
- return_value = qcbor_util_get_item_in_map(&decode_context,
- label,
- &item);
- if(return_value != 0) {
- goto Done;
- }
-
- if(QCBORDecode_Finish(&decode_context)) {
- return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
- }
-
- if(item.uDataType == QCBOR_TYPE_UINT64) {
- *integer = item.val.uint64;
- } else if(item.uDataType == QCBOR_TYPE_INT64) {
- if(item.val.int64 >= 0) {
- *integer = (uint64_t)item.val.int64;
- } else {
- return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
- }
- } else {
- return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
- }
-
-Done:
- return return_value;
-}
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-enum attest_token_err_t
-attest_token_decode_get_payload(struct attest_token_decode_context *me,
- struct q_useful_buf_c *payload)
-{
- enum attest_token_err_t return_value;
-
- if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
- return_value = me->last_error;
- *payload = NULL_Q_USEFUL_BUF_C;
- goto Done;
- }
-
- if(q_useful_buf_c_is_null_or_empty(me->payload)) {
- return_value = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
- goto Done;
- }
-
- *payload = me->payload;
- return_value = ATTEST_TOKEN_ERR_SUCCESS;
-
-Done:
- return return_value;
-}
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-enum attest_token_err_t
-attest_token_decode_get_iat_simple(struct attest_token_decode_context *me,
- struct attest_token_iat_simple_t *items)
-{
- struct qcbor_util_items_to_get_t list[NUMBER_OF_ITEMS+1];
- QCBORDecodeContext decode_context;
- int64_t client_id_64;
- enum attest_token_err_t return_value;
-
- /* Set all q_useful_bufs to NULL and flags to 0 */
- memset(items, 0, sizeof(struct attest_token_iat_simple_t));
-
- /* Re use flags as array indexes because it works nicely */
- list[NONCE_FLAG].label = EAT_CBOR_ARM_LABEL_CHALLENGE;
- list[UEID_FLAG].label = EAT_CBOR_ARM_LABEL_UEID;
- list[BOOT_SEED_FLAG].label = EAT_CBOR_ARM_LABEL_BOOT_SEED;
- list[HW_VERSION_FLAG].label = EAT_CBOR_ARM_LABEL_HW_VERSION;
- list[IMPLEMENTATION_ID_FLAG].label = EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID;
- list[CLIENT_ID_FLAG].label = EAT_CBOR_ARM_LABEL_CLIENT_ID;
- list[SECURITY_LIFECYCLE_FLAG].label = EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE;
- list[PROFILE_DEFINITION_FLAG].label = EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION;
- list[ORIGINATION_FLAG].label = EAT_CBOR_ARM_LABEL_ORIGINATION;
- list[NUMBER_OF_ITEMS].label = 0; /* terminate the list. */
-
- if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
- return_value = me->last_error;
- goto Done;
- }
-
- QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
-
- return_value = qcbor_util_get_items_in_map(&decode_context,
- list);
- if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
- goto Done;
- }
-
- /* ---- NONCE ---- */
- if(list[NONCE_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
- items->nonce = list[NONCE_FLAG].item.val.string;
- items->item_flags |= CLAIM_PRESENT_BIT(NONCE_FLAG);
- }
-
- /* ---- UEID -------*/
- if(list[UEID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
- items->ueid = list[UEID_FLAG].item.val.string;
- items->item_flags |= CLAIM_PRESENT_BIT(UEID_FLAG);
- }
-
- /* ---- BOOT SEED -------*/
- if(list[BOOT_SEED_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
- items->boot_seed = list[BOOT_SEED_FLAG].item.val.string;
- items->item_flags |= CLAIM_PRESENT_BIT(BOOT_SEED_FLAG);\
- }
-
- /* ---- HW VERSION -------*/
- if(list[HW_VERSION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
- items->hw_version = list[HW_VERSION_FLAG].item.val.string;
- items->item_flags |= CLAIM_PRESENT_BIT(HW_VERSION_FLAG);
-
- }
-
- /* ----IMPLEMENTATION ID -------*/
- if(list[IMPLEMENTATION_ID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
- items->implementation_id = list[IMPLEMENTATION_ID_FLAG].item.val.string;
- items->item_flags |= CLAIM_PRESENT_BIT(IMPLEMENTATION_ID_FLAG);
- }
-
- /* ----CLIENT ID -------*/
- if(list[CLIENT_ID_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
- client_id_64 = list[CLIENT_ID_FLAG].item.val.int64;
- if(client_id_64 < INT32_MAX || client_id_64 > INT32_MIN) {
- items->client_id = (int32_t)client_id_64;
- items->item_flags |= CLAIM_PRESENT_BIT(CLIENT_ID_FLAG);
- }
- }
-
- /* ----SECURITY LIFECYCLE -------*/
- if(list[SECURITY_LIFECYCLE_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
- if(list[SECURITY_LIFECYCLE_FLAG].item.val.int64 < UINT32_MAX) {
- items->security_lifecycle =
- (uint32_t)list[SECURITY_LIFECYCLE_FLAG].item.val.int64;
- items->item_flags |=CLAIM_PRESENT_BIT(SECURITY_LIFECYCLE_FLAG);
- }
- }
-
- /* ---- PROFILE_DEFINITION -------*/
- if(list[PROFILE_DEFINITION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
- items->profile_definition=list[PROFILE_DEFINITION_FLAG].item.val.string;
- items->item_flags |= CLAIM_PRESENT_BIT(PROFILE_DEFINITION_FLAG);
- }
-
- /* ---- ORIGINATION -------*/
- if(list[ORIGINATION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
- items->origination = list[ORIGINATION_FLAG].item.val.string;
- items->item_flags |= CLAIM_PRESENT_BIT(ORIGINATION_FLAG);
- }
-
-Done:
- return return_value;
-}
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-enum attest_token_err_t
-attest_token_get_num_sw_components(struct attest_token_decode_context *me,
- uint32_t *num_sw_components)
-{
- enum attest_token_err_t return_value;
- QCBORItem item;
-
- if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
- return_value = me->last_error;
- goto Done;
- }
-
- return_value = qcbor_util_get_top_level_item_in_map(me->payload,
- EAT_CBOR_ARM_LABEL_SW_COMPONENTS,
- QCBOR_TYPE_ARRAY,
- &item);
- if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
- if(return_value != ATTEST_TOKEN_ERR_NOT_FOUND) {
- /* Something very wrong. Bail out passing on the return_value */
- goto Done;
- } else {
- /* Now decide if it was intentionally left out. */
- return_value = qcbor_util_get_top_level_item_in_map(me->payload,
- EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS,
- QCBOR_TYPE_INT64,
- &item);
- if(return_value == ATTEST_TOKEN_ERR_SUCCESS) {
- if(item.val.int64 == NO_SW_COMPONENT_FIXED_VALUE) {
- /* Successful omission of SW components. Pass on the
- * success return_value */
- *num_sw_components = 0;
- } else {
- /* Indicator for no SW components malformed */
- return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
- }
- } else if(return_value == ATTEST_TOKEN_ERR_NOT_FOUND) {
- /* Should have been an indicator for no SW components */
- return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
- }
- }
- } else {
- /* The SW components claim exists */
- if(item.val.uCount == 0) {
- /* Empty SW component not allowed */
- return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
- } else {
- /* SUCESSS! Pass on the success return_value */
- /* Note that this assumes the array is definite length */
- *num_sw_components = item.val.uCount;
- }
- }
-
-Done:
- return return_value;
-}
-
-
-/**
- * \brief Decode a single SW component
- *
- * \param[in] decode_context The CBOR decoder context to decode from
- * \param[in] sw_component_item The top-level map item for this SW
- * component.
- * \param[out] sw_component The structure to fill in with decoded data.
- *
- * \return An error from \ref attest_token_err_t.
- *
- */
-static inline enum attest_token_err_t
-decode_sw_component(QCBORDecodeContext *decode_context,
- const QCBORItem *sw_component_item,
- struct attest_token_sw_component_t *sw_component)
-{
- enum attest_token_err_t return_value;
- QCBORItem claim_item;
- QCBORError cbor_error;
- uint_fast8_t next_nest_level; /* nest levels are 8-bit, but a uint8_t
- var is often slower and more code */
-
- if(sw_component_item->uDataType != QCBOR_TYPE_MAP) {
- return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
- goto Done;
- }
-
- /* Zero it, setting booleans to false, pointers to NULL and
- lengths to 0 */
- memset(sw_component, 0, sizeof(struct attest_token_sw_component_t));
-
- return_value = ATTEST_TOKEN_ERR_SUCCESS;
-
- while(1) {
- cbor_error = QCBORDecode_GetNext(decode_context, &claim_item);
- if(cbor_error != QCBOR_SUCCESS) {
- /* no tolerance for any errors here */
- return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
- goto Done;
- }
-
- if(claim_item.uLabelType == QCBOR_TYPE_INT64) {
- switch(claim_item.label.int64) {
- case EAT_CBOR_SW_COMPONENT_MEASUREMENT_TYPE:
- if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
- return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
- goto Done;
- }
- sw_component->measurement_type = claim_item.val.string;
- sw_component->item_flags |=
- CLAIM_PRESENT_BIT(SW_MEASUREMENT_TYPE_FLAG);
-
- break;
-
- case EAT_CBOR_SW_COMPONENT_MEASUREMENT_VALUE:
- if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
- return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
- goto Done;
- }
- sw_component->measurement_val = claim_item.val.string;
- sw_component->item_flags |=
- CLAIM_PRESENT_BIT(SW_MEASURMENT_VAL_FLAG);
- break;
-
- case EAT_CBOR_SW_COMPONENT_VERSION:
- if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
- return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
- goto Done;
- }
- sw_component->version = claim_item.val.string;
- sw_component->item_flags |=
- CLAIM_PRESENT_BIT(SW_VERSION_FLAG);
- break;
-
- case EAT_CBOR_SW_COMPONENT_SIGNER_ID:
- if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
- return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
- goto Done;
- }
- sw_component->signer_id = claim_item.val.string;
- sw_component->item_flags |=
- CLAIM_PRESENT_BIT(SW_SIGNER_ID_FLAG);
- break;
-
- case EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC:
- if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
- return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
- goto Done;
- }
- sw_component->measurement_desc = claim_item.val.string;
- sw_component->item_flags |=
- CLAIM_PRESENT_BIT(SW_MEASUREMENT_DESC_FLAG);
- break;
- }
- }
-
- if(qcbor_util_consume_item(decode_context,
- &claim_item,
- &next_nest_level)) {
- return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
- goto Done;
- }
- if(next_nest_level < sw_component_item->uNextNestLevel) {
- /* Got all the items in the map */
- break;
- }
- }
-
-Done:
- return return_value;
-}
-
-
-/*
- * Public function. See attest_token_decode.h
- */
-enum attest_token_err_t
-attest_token_get_sw_component(struct attest_token_decode_context *me,
- uint32_t requested_index,
- struct attest_token_sw_component_t *sw_components)
-{
- enum attest_token_err_t return_value;
- QCBORItem sw_components_array_item;
- QCBORDecodeContext decode_context;
- QCBORItem sw_component_item;
- QCBORError qcbor_error;
- uint_fast8_t exit_array_level;
-
- if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
- return_value = me->last_error;
- goto Done;
- }
-
- QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
-
- /* Find the map containing all the SW Components */
- return_value = qcbor_util_decode_to_labeled_item(&decode_context,
- EAT_CBOR_ARM_LABEL_SW_COMPONENTS,
- &sw_components_array_item);
- if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
- goto Done;
- }
-
- if(sw_components_array_item.uDataType != QCBOR_TYPE_ARRAY) {
- return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
- goto Done;
- }
-
- exit_array_level = sw_components_array_item.uNextNestLevel;
-
- /* Loop over contents of SW Components array */
- while(1) {
- qcbor_error = QCBORDecode_GetNext(&decode_context, &sw_component_item);
- if(qcbor_error) {
- /* no tolerance for any errors here */
- return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
- goto Done;
- }
-
- if(sw_component_item.uNextNestLevel <= exit_array_level) {
- /* Next item will be outside the array */
- return_value = ATTEST_TOKEN_ERR_NOT_FOUND;
- /* The end of the array containing SW components
- and didn't get to the requested_index. */
- goto Done;
- }
-
- if(requested_index == 0) {
- /* Found the one of interest. Decode it and break out */
- return_value = decode_sw_component(&decode_context,
- &sw_component_item,
- sw_components);
- break; /* The normal, non-error exit from this loop */
- }
-
- /* Every member in the array counts even if they are not
- * what is expected */
- requested_index--;
-
- if(qcbor_util_consume_item(&decode_context, &sw_component_item, NULL)) {
- return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
- goto Done;
- }
- }
-
-Done:
- return return_value;
-}
diff --git a/test/suites/attestation/attest_token_decode.h b/test/suites/attestation/attest_token_decode.h
index b735e59c62..880336bf47 100644
--- a/test/suites/attestation/attest_token_decode.h
+++ b/test/suites/attestation/attest_token_decode.h
@@ -2,6 +2,7 @@
* attest_token_decode.h
*
* Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
@@ -836,6 +837,94 @@ attest_token_decode_get_origination(struct attest_token_decode_context*me,
origination);
}
+/**
+
+ \brief Map t_cose errors into attestation token errors
+
+ \param[in] t_cose_error The t_cose error to map
+
+ \return The attestation token error.
+ */
+static inline enum attest_token_err_t
+map_t_cose_errors(enum t_cose_err_t t_cose_error)
+{
+ switch (t_cose_error) {
+ case T_COSE_SUCCESS:
+ return ATTEST_TOKEN_ERR_SUCCESS;
+ break;
+ case T_COSE_ERR_UNSUPPORTED_SIGNING_ALG:
+ return ATTEST_TOKEN_ERR_UNSUPPORTED_SIG_ALG;
+ break;
+ case T_COSE_ERR_UNSUPPORTED_HASH:
+ return ATTEST_TOKEN_ERR_HASH_UNAVAILABLE;
+ break;
+ case T_COSE_ERR_CBOR_NOT_WELL_FORMED:
+ return ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ break;
+ case T_COSE_ERR_INSUFFICIENT_MEMORY:
+ return ATTEST_TOKEN_ERR_INSUFFICIENT_MEMORY;
+ break;
+ case T_COSE_ERR_TAMPERING_DETECTED:
+ return ATTEST_TOKEN_ERR_TAMPERING_DETECTED;
+ break;
+ case T_COSE_ERR_CBOR_FORMATTING:
+ return ATTEST_TOKEN_ERR_CBOR_FORMATTING;
+ break;
+ case T_COSE_ERR_TOO_SMALL:
+ return ATTEST_TOKEN_ERR_TOO_SMALL;
+ break;
+
+ case T_COSE_ERR_PARAMETER_CBOR:
+ case T_COSE_ERR_NON_INTEGER_ALG_ID:
+ return ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+ break;
+
+ case T_COSE_ERR_SIG_VERIFY:
+ case T_COSE_ERR_SHORT_CIRCUIT_SIG:
+ return ATTEST_TOKEN_ERR_COSE_VALIDATION;
+ break;
+
+ case T_COSE_ERR_SIGN1_FORMAT:
+ return ATTEST_TOKEN_ERR_COSE_FORMAT;
+ break;
+
+ case T_COSE_ERR_MAC0_FORMAT:
+ return ATTEST_TOKEN_ERR_COSE_FORMAT;
+ break;
+
+ case T_COSE_ERR_NO_ALG_ID:
+ case T_COSE_ERR_NO_KID:
+ case T_COSE_ERR_BAD_SHORT_CIRCUIT_KID:
+ case T_COSE_ERR_SIG_STRUCT:
+ return ATTEST_TOKEN_ERR_COSE_FORMAT;
+ break;
+
+ case T_COSE_ERR_UNKNOWN_KEY:
+ case T_COSE_ERR_WRONG_TYPE_OF_KEY:
+ return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+ break;
+
+ case T_COSE_ERR_MAKING_PROTECTED:
+ case T_COSE_ERR_HASH_GENERAL_FAIL:
+ case T_COSE_ERR_HASH_BUFFER_SIZE:
+ case T_COSE_ERR_SIG_BUFFER_SIZE:
+ case T_COSE_ERR_INVALID_ARGUMENT:
+ case T_COSE_ERR_FAIL:
+ case T_COSE_ERR_SIG_FAIL:
+ case T_COSE_ERR_TOO_MANY_PARAMETERS:
+ case T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER:
+ case T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED:
+ case T_COSE_ERR_INCORRECT_KEY_FOR_LIB:
+ case T_COSE_ERR_BAD_CONTENT_TYPE:
+ case T_COSE_ERR_INCORRECTLY_TAGGED:
+ case T_COSE_ERR_EMPTY_KEY:
+ case T_COSE_ERR_DUPLICATE_PARAMETER:
+ case T_COSE_ERR_PARAMETER_NOT_PROTECTED:
+ case T_COSE_ERR_CRIT_PARAMETER:
+ default:
+ return ATTEST_TOKEN_ERR_GENERAL;
+ }
+}
#ifdef __cplusplus
}
diff --git a/test/suites/attestation/attest_token_decode_common.c b/test/suites/attestation/attest_token_decode_common.c
new file mode 100644
index 0000000000..52987c9a0d
--- /dev/null
+++ b/test/suites/attestation/attest_token_decode_common.c
@@ -0,0 +1,601 @@
+/*
+ * attest_token_decode_common.c
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_decode.h"
+#include "attestation.h"
+#include "q_useful_buf.h"
+#include "qcbor_util.h"
+
+/**
+ * \file attest_token_decode_common.c
+ *
+ * \brief Common functions in attestation token decoder.
+ *
+ * This decodes and verifies an attestation token giving access to the
+ * data items in the token. The data items are also known as claims.
+ *
+ * This is written primarily as tests for the token encoder, though it
+ * close to a full commercial token decoder. The main thing missing is
+ * a thorough test suite for it. Test before commercial use is
+ * important as this is a parser / decoder and thus subject to attack
+ * by malicious input. It does however, use QCBOR for most base
+ * parsing, and QCBOR is thoroughly tested and commercial.
+ *
+ * This is oriented around the Arm-defined initial attestation token.
+ *
+ * \c uint_fast8_t is used for type and nest levels. They are
+ * 8-bit quantities, but making using uint8_t variables
+ * and parameters can result in bigger, slower code.
+ * \c uint_fast8_t is part of \c <stdint.h>. It is not
+ * used in structures where it is more important to keep
+ * the size smaller.
+ */
+
+
+/**
+ * Compute the bit indicating a claim is present
+ */
+#define CLAIM_PRESENT_BIT(item_index) (0x01U << (item_index))
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+void attest_token_decode_init(struct attest_token_decode_context *me,
+ uint32_t options)
+{
+ memset(me, 0, sizeof(struct attest_token_decode_context));
+ me->options = options;
+ me->last_error = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
+}
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_bstr(struct attest_token_decode_context *me,
+ int32_t label,
+ struct q_useful_buf_c *claim)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *claim = NULL_Q_USEFUL_BUF_C;
+ goto Done;
+ }
+
+ return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+ label,
+ QCBOR_TYPE_BYTE_STRING,
+ &item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ *claim = item.val.string;
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_tstr(struct attest_token_decode_context *me,
+ int32_t label,
+ struct q_useful_buf_c *claim)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *claim = NULL_Q_USEFUL_BUF_C;
+ goto Done;
+ }
+
+ return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+ label,
+ QCBOR_TYPE_TEXT_STRING,
+ &item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ *claim = item.val.string;
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_int(struct attest_token_decode_context *me,
+ int32_t label,
+ int64_t *integer)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+ QCBORDecodeContext decode_context;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *integer = 0;
+ goto Done;
+ }
+
+ QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+ return_value = qcbor_util_get_item_in_map(&decode_context,
+ label,
+ &item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ if(QCBORDecode_Finish(&decode_context)) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+ }
+
+ if(item.uDataType == QCBOR_TYPE_INT64) {
+ *integer = item.val.int64;
+ } else if(item.uDataType == QCBOR_TYPE_UINT64) {
+ if(item.val.uint64 < INT64_MAX) {
+ *integer = (int64_t)item.val.uint64;
+ } else {
+ return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+ }
+ } else {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_uint(struct attest_token_decode_context *me,
+ int32_t label,
+ uint64_t *integer)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+ QCBORDecodeContext decode_context;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *integer = 0;
+ goto Done;
+ }
+
+ QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+ return_value = qcbor_util_get_item_in_map(&decode_context,
+ label,
+ &item);
+ if(return_value != 0) {
+ goto Done;
+ }
+
+ if(QCBORDecode_Finish(&decode_context)) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+ }
+
+ if(item.uDataType == QCBOR_TYPE_UINT64) {
+ *integer = item.val.uint64;
+ } else if(item.uDataType == QCBOR_TYPE_INT64) {
+ if(item.val.int64 >= 0) {
+ *integer = (uint64_t)item.val.int64;
+ } else {
+ return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+ }
+ } else {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_payload(struct attest_token_decode_context *me,
+ struct q_useful_buf_c *payload)
+{
+ enum attest_token_err_t return_value;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ *payload = NULL_Q_USEFUL_BUF_C;
+ goto Done;
+ }
+
+ if(q_useful_buf_c_is_null_or_empty(me->payload)) {
+ return_value = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
+ goto Done;
+ }
+
+ *payload = me->payload;
+ return_value = ATTEST_TOKEN_ERR_SUCCESS;
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_iat_simple(struct attest_token_decode_context *me,
+ struct attest_token_iat_simple_t *items)
+{
+ struct qcbor_util_items_to_get_t list[NUMBER_OF_ITEMS+1];
+ QCBORDecodeContext decode_context;
+ int64_t client_id_64;
+ enum attest_token_err_t return_value;
+
+ /* Set all q_useful_bufs to NULL and flags to 0 */
+ memset(items, 0, sizeof(struct attest_token_iat_simple_t));
+
+ /* Re use flags as array indexes because it works nicely */
+ list[NONCE_FLAG].label = EAT_CBOR_ARM_LABEL_CHALLENGE;
+ list[UEID_FLAG].label = EAT_CBOR_ARM_LABEL_UEID;
+ list[BOOT_SEED_FLAG].label = EAT_CBOR_ARM_LABEL_BOOT_SEED;
+ list[HW_VERSION_FLAG].label = EAT_CBOR_ARM_LABEL_HW_VERSION;
+ list[IMPLEMENTATION_ID_FLAG].label = EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID;
+ list[CLIENT_ID_FLAG].label = EAT_CBOR_ARM_LABEL_CLIENT_ID;
+ list[SECURITY_LIFECYCLE_FLAG].label = EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE;
+ list[PROFILE_DEFINITION_FLAG].label = EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION;
+ list[ORIGINATION_FLAG].label = EAT_CBOR_ARM_LABEL_ORIGINATION;
+ list[NUMBER_OF_ITEMS].label = 0; /* terminate the list. */
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ goto Done;
+ }
+
+ QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+ return_value = qcbor_util_get_items_in_map(&decode_context,
+ list);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ /* ---- NONCE ---- */
+ if(list[NONCE_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ items->nonce = list[NONCE_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(NONCE_FLAG);
+ }
+
+ /* ---- UEID -------*/
+ if(list[UEID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ items->ueid = list[UEID_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(UEID_FLAG);
+ }
+
+ /* ---- BOOT SEED -------*/
+ if(list[BOOT_SEED_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ items->boot_seed = list[BOOT_SEED_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(BOOT_SEED_FLAG);\
+ }
+
+ /* ---- HW VERSION -------*/
+ if(list[HW_VERSION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ items->hw_version = list[HW_VERSION_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(HW_VERSION_FLAG);
+
+ }
+
+ /* ----IMPLEMENTATION ID -------*/
+ if(list[IMPLEMENTATION_ID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+ items->implementation_id = list[IMPLEMENTATION_ID_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(IMPLEMENTATION_ID_FLAG);
+ }
+
+ /* ----CLIENT ID -------*/
+ if(list[CLIENT_ID_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
+ client_id_64 = list[CLIENT_ID_FLAG].item.val.int64;
+ if(client_id_64 < INT32_MAX || client_id_64 > INT32_MIN) {
+ items->client_id = (int32_t)client_id_64;
+ items->item_flags |= CLAIM_PRESENT_BIT(CLIENT_ID_FLAG);
+ }
+ }
+
+ /* ----SECURITY LIFECYCLE -------*/
+ if(list[SECURITY_LIFECYCLE_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
+ if(list[SECURITY_LIFECYCLE_FLAG].item.val.int64 < UINT32_MAX) {
+ items->security_lifecycle =
+ (uint32_t)list[SECURITY_LIFECYCLE_FLAG].item.val.int64;
+ items->item_flags |=CLAIM_PRESENT_BIT(SECURITY_LIFECYCLE_FLAG);
+ }
+ }
+
+ /* ---- PROFILE_DEFINITION -------*/
+ if(list[PROFILE_DEFINITION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ items->profile_definition=list[PROFILE_DEFINITION_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(PROFILE_DEFINITION_FLAG);
+ }
+
+ /* ---- ORIGINATION -------*/
+ if(list[ORIGINATION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ items->origination = list[ORIGINATION_FLAG].item.val.string;
+ items->item_flags |= CLAIM_PRESENT_BIT(ORIGINATION_FLAG);
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_get_num_sw_components(struct attest_token_decode_context *me,
+ uint32_t *num_sw_components)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem item;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ goto Done;
+ }
+
+ return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+ EAT_CBOR_ARM_LABEL_SW_COMPONENTS,
+ QCBOR_TYPE_ARRAY,
+ &item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ if(return_value != ATTEST_TOKEN_ERR_NOT_FOUND) {
+ /* Something very wrong. Bail out passing on the return_value */
+ goto Done;
+ } else {
+ /* Now decide if it was intentionally left out. */
+ return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+ EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS,
+ QCBOR_TYPE_INT64,
+ &item);
+ if(return_value == ATTEST_TOKEN_ERR_SUCCESS) {
+ if(item.val.int64 == NO_SW_COMPONENT_FIXED_VALUE) {
+ /* Successful omission of SW components. Pass on the
+ * success return_value */
+ *num_sw_components = 0;
+ } else {
+ /* Indicator for no SW components malformed */
+ return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+ }
+ } else if(return_value == ATTEST_TOKEN_ERR_NOT_FOUND) {
+ /* Should have been an indicator for no SW components */
+ return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+ }
+ }
+ } else {
+ /* The SW components claim exists */
+ if(item.val.uCount == 0) {
+ /* Empty SW component not allowed */
+ return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+ } else {
+ /* SUCESSS! Pass on the success return_value */
+ /* Note that this assumes the array is definite length */
+ *num_sw_components = item.val.uCount;
+ }
+ }
+
+Done:
+ return return_value;
+}
+
+
+/**
+ * \brief Decode a single SW component
+ *
+ * \param[in] decode_context The CBOR decoder context to decode from
+ * \param[in] sw_component_item The top-level map item for this SW
+ * component.
+ * \param[out] sw_component The structure to fill in with decoded data.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ */
+static inline enum attest_token_err_t
+decode_sw_component(QCBORDecodeContext *decode_context,
+ const QCBORItem *sw_component_item,
+ struct attest_token_sw_component_t *sw_component)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem claim_item;
+ QCBORError cbor_error;
+ uint_fast8_t next_nest_level; /* nest levels are 8-bit, but a uint8_t
+ var is often slower and more code */
+
+ if(sw_component_item->uDataType != QCBOR_TYPE_MAP) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+ goto Done;
+ }
+
+ /* Zero it, setting booleans to false, pointers to NULL and
+ lengths to 0 */
+ memset(sw_component, 0, sizeof(struct attest_token_sw_component_t));
+
+ return_value = ATTEST_TOKEN_ERR_SUCCESS;
+
+ while(1) {
+ cbor_error = QCBORDecode_GetNext(decode_context, &claim_item);
+ if(cbor_error != QCBOR_SUCCESS) {
+ /* no tolerance for any errors here */
+ return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+ if(claim_item.uLabelType == QCBOR_TYPE_INT64) {
+ switch(claim_item.label.int64) {
+ case EAT_CBOR_SW_COMPONENT_MEASUREMENT_TYPE:
+ if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->measurement_type = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_MEASUREMENT_TYPE_FLAG);
+
+ break;
+
+ case EAT_CBOR_SW_COMPONENT_MEASUREMENT_VALUE:
+ if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->measurement_val = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_MEASURMENT_VAL_FLAG);
+ break;
+
+ case EAT_CBOR_SW_COMPONENT_VERSION:
+ if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->version = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_VERSION_FLAG);
+ break;
+
+ case EAT_CBOR_SW_COMPONENT_SIGNER_ID:
+ if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->signer_id = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_SIGNER_ID_FLAG);
+ break;
+
+ case EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC:
+ if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+ sw_component->measurement_desc = claim_item.val.string;
+ sw_component->item_flags |=
+ CLAIM_PRESENT_BIT(SW_MEASUREMENT_DESC_FLAG);
+ break;
+ }
+ }
+
+ if(qcbor_util_consume_item(decode_context,
+ &claim_item,
+ &next_nest_level)) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+ if(next_nest_level < sw_component_item->uNextNestLevel) {
+ /* Got all the items in the map */
+ break;
+ }
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_get_sw_component(struct attest_token_decode_context *me,
+ uint32_t requested_index,
+ struct attest_token_sw_component_t *sw_components)
+{
+ enum attest_token_err_t return_value;
+ QCBORItem sw_components_array_item;
+ QCBORDecodeContext decode_context;
+ QCBORItem sw_component_item;
+ QCBORError qcbor_error;
+ uint_fast8_t exit_array_level;
+
+ if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+ return_value = me->last_error;
+ goto Done;
+ }
+
+ QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+ /* Find the map containing all the SW Components */
+ return_value = qcbor_util_decode_to_labeled_item(&decode_context,
+ EAT_CBOR_ARM_LABEL_SW_COMPONENTS,
+ &sw_components_array_item);
+ if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+ goto Done;
+ }
+
+ if(sw_components_array_item.uDataType != QCBOR_TYPE_ARRAY) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+ goto Done;
+ }
+
+ exit_array_level = sw_components_array_item.uNextNestLevel;
+
+ /* Loop over contents of SW Components array */
+ while(1) {
+ qcbor_error = QCBORDecode_GetNext(&decode_context, &sw_component_item);
+ if(qcbor_error) {
+ /* no tolerance for any errors here */
+ return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+ if(sw_component_item.uNextNestLevel <= exit_array_level) {
+ /* Next item will be outside the array */
+ return_value = ATTEST_TOKEN_ERR_NOT_FOUND;
+ /* The end of the array containing SW components
+ and didn't get to the requested_index. */
+ goto Done;
+ }
+
+ if(requested_index == 0) {
+ /* Found the one of interest. Decode it and break out */
+ return_value = decode_sw_component(&decode_context,
+ &sw_component_item,
+ sw_components);
+ break; /* The normal, non-error exit from this loop */
+ }
+
+ /* Every member in the array counts even if they are not
+ * what is expected */
+ requested_index--;
+
+ if(qcbor_util_consume_item(&decode_context, &sw_component_item, NULL)) {
+ return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+ }
+
+Done:
+ return return_value;
+}