diff options
author | David Hu <david.hu@arm.com> | 2020-01-25 12:25:41 +0800 |
---|---|---|
committer | David Hu <david.hu@arm.com> | 2020-06-22 02:33:00 +0000 |
commit | 333ca93b117ef3605de9127ee479268aee75348c (patch) | |
tree | 6695fd6b4f867df3c0988514c67af23ce016e2d7 | |
parent | 574f713fe672f67f0a64969bf774d7e1bd1ed39c (diff) | |
download | trusted-firmware-m-333ca93b117ef3605de9127ee479268aee75348c.tar.gz |
COSE: Add COSE_Mac0 support in t_cose library
Add t_cose_mac0_init() and t_cose_mac0_finish() to complete
COSE_Mac0 structure.
Implement HMAC operations for COSE_Mac0 based on PSA crypto MAC
operations.
Change-Id: Ic55a34d24100adb1c839b0d3ff7cb4d2da09d4cb
Signed-off-by: David Hu <david.hu@arm.com>
-rw-r--r-- | lib/ext/t_cose/CMakeLists.txt | 31 | ||||
-rw-r--r-- | lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c | 137 | ||||
-rw-r--r-- | lib/ext/t_cose/inc/t_cose_common.h | 62 | ||||
-rw-r--r-- | lib/ext/t_cose/inc/t_cose_mac0_sign.h | 229 | ||||
-rw-r--r-- | lib/ext/t_cose/inc/t_cose_sign1_sign.h | 13 | ||||
-rw-r--r-- | lib/ext/t_cose/src/t_cose_crypto.h | 113 | ||||
-rw-r--r-- | lib/ext/t_cose/src/t_cose_mac0_sign.c | 305 | ||||
-rw-r--r-- | lib/ext/t_cose/src/t_cose_standard_constants.h | 8 | ||||
-rw-r--r-- | lib/ext/t_cose/src/t_cose_util.c | 61 | ||||
-rw-r--r-- | lib/ext/t_cose/src/t_cose_util.h | 70 |
10 files changed, 1007 insertions, 22 deletions
diff --git a/lib/ext/t_cose/CMakeLists.txt b/lib/ext/t_cose/CMakeLists.txt index 91064e799c..406c9bc246 100644 --- a/lib/ext/t_cose/CMakeLists.txt +++ b/lib/ext/t_cose/CMakeLists.txt @@ -28,19 +28,33 @@ endif() #Append all our source files to global lists. list(APPEND ALL_SRC_C_SIGN - "${T_COSE_DIR}/src/t_cose_sign1_sign.c" "${T_COSE_DIR}/src/t_cose_util.c" "${T_COSE_DIR}/src/t_cose_parameters.c" "${T_COSE_DIR}/crypto_adapters/t_cose_psa_crypto.c" ) +if (SYMMETRIC_INITIAL_ATTESTATION) + list(APPEND T_COSE_COMPILE_TIME_CONFIG "T_COSE_DISABLE_SIGN1") + + list(APPEND ALL_SRC_C_SIGN "${T_COSE_DIR}/src/t_cose_mac0_sign.c") +else() + list(APPEND T_COSE_COMPILE_TIME_CONFIG "T_COSE_DISABLE_MAC0") + + list(APPEND ALL_SRC_C_SIGN "${T_COSE_DIR}/src/t_cose_sign1_sign.c") +endif() + list(APPEND ALL_SRC_C_VERIFY - "${T_COSE_DIR}/src/t_cose_sign1_verify.c" "${T_COSE_DIR}/src/t_cose_util.c" "${T_COSE_DIR}/src/t_cose_parameters.c" - "${T_COSE_DIR}/crypto_adapters/t_cose_psa_crypto.c" ) +if (NOT SYMMETRIC_INITIAL_ATTESTATION) + list(APPEND ALL_SRC_C_VERIFY + "${T_COSE_DIR}/src/t_cose_sign1_verify.c" + "${T_COSE_DIR}/crypto_adapters/t_cose_psa_crypto.c" + ) +endif() + list(APPEND ALL_SRC_C_TEST "${T_COSE_DIR}/test/run_tests.c" "${T_COSE_DIR}/test/t_cose_make_psa_test_key.c" @@ -65,13 +79,18 @@ add_library(tfm_t_cose_test OBJECT ${ALL_SRC_C_TEST}) list(APPEND T_COSE_COMPILE_TIME_CONFIG "T_COSE_USE_PSA_CRYPTO" - "T_COSE_DISABLE_ES384" - "T_COSE_DISABLE_ES512" "T_COSE_DISABLE_CONTENT_TYPE" - "T_COSE_DISABLE_SIGN_VERIFY_TESTS" "T_COSE_USE_PSA_CRYPTO_FROM_TFM" ) +if (NOT SYMMETRIC_INITIAL_ATTESTATION) + list(APPEND T_COSE_COMPILE_TIME_CONFIG + "T_COSE_DISABLE_ES384" + "T_COSE_DISABLE_ES512" + "T_COSE_DISABLE_SIGN_VERIFY_TESTS" + ) +endif() + if (NOT ATTEST_INCLUDE_TEST_CODE) list(APPEND T_COSE_COMPILE_TIME_CONFIG "T_COSE_DISABLE_SHORT_CIRCUIT_SIGN") endif() diff --git a/lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c b/lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c index 927e626511..c6e8749db4 100644 --- a/lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c +++ b/lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c @@ -2,6 +2,7 @@ * t_cose_psa_crypto.c * * Copyright 2019, Laurence Lundblade + * Copyright (c) 2020, Arm Limited. All rights reserved * * SPDX-License-Identifier: BSD-3-Clause * @@ -12,10 +13,10 @@ /** * \file t_cose_psa_crypto.c * - * \brief Crypto Adaptation for t_cose to use ARM's PSA ECDSA and hashes. + * \brief Crypto Adaptation for t_cose to use ARM's PSA Crypto APIs. * * This connects up the abstract interface in t_cose_crypto.h to the - * implementations of ECDSA signing and hashing in ARM's PSA crypto + * implementations of ECDSA signing, hashing and HMAC in ARM's PSA crypto * library. * * This adapter layer doesn't bloat the implementation as everything @@ -60,7 +61,7 @@ /* Avoid compiler warning due to unused argument */ #define ARG_UNUSED(arg) (void)(arg) - +#ifndef T_COSE_DISABLE_SIGN1 /** * \brief Map a COSE signing algorithm ID to a PSA signing algorithm ID * @@ -407,3 +408,133 @@ t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx, Done: return psa_status_to_t_cose_error_hash(hash_ctx->status); } +#endif /* !T_COSE_DISABLE_SIGN1 */ + +#ifndef T_COSE_DISABLE_MAC0 +/** + * \brief Convert COSE algorithm ID to a PSA HMAC algorithm ID + * + * \param[in] cose_hmac_alg_id The COSE-based ID for the + * + * \return PSA-based MAC algorithm ID, or a vendor flag in the case of error. + * + */ +static inline psa_algorithm_t cose_hmac_alg_id_to_psa(int32_t cose_hmac_alg_id) +{ + switch(cose_hmac_alg_id) { + case T_COSE_ALGORITHM_HMAC256: + return PSA_ALG_HMAC(PSA_ALG_SHA_256); + case T_COSE_ALGORITHM_HMAC384: + return PSA_ALG_HMAC(PSA_ALG_SHA_384); + case T_COSE_ALGORITHM_HMAC512: + return PSA_ALG_HMAC(PSA_ALG_SHA_512); + default: + return PSA_ALG_VENDOR_FLAG; + } +} + +/** + * \brief Map a PSA error into a t_cose error for HMAC. + * + * \param[in] status The PSA status. + * + * \return The \ref t_cose_err_t. + */ +static enum t_cose_err_t +psa_status_to_t_cose_error_hmac(psa_status_t status) +{ + /* Intentionally limited to just this minimum set of errors to + * save object code as hashes don't really fail much + */ + return status == PSA_SUCCESS ? T_COSE_SUCCESS : + status == PSA_ERROR_NOT_SUPPORTED ? T_COSE_ERR_UNSUPPORTED_HASH : + status == PSA_ERROR_INVALID_ARGUMENT ? T_COSE_ERR_INVALID_ARGUMENT : + status == PSA_ERROR_INSUFFICIENT_MEMORY ? T_COSE_ERR_INSUFFICIENT_MEMORY : + status == PSA_ERROR_BUFFER_TOO_SMALL ? T_COSE_ERR_TOO_SMALL : + T_COSE_ERR_FAIL; +} + +/* + * See documentation in t_cose_crypto.h + */ +enum t_cose_err_t +t_cose_crypto_hmac_sign_setup(struct t_cose_crypto_hmac *hmac_ctx, + struct t_cose_key signing_key, + const int32_t cose_alg_id) +{ + psa_algorithm_t psa_alg; + psa_status_t psa_ret; + + if(!hmac_ctx) { + return T_COSE_ERR_INVALID_ARGUMENT; + } + + /* Map the algorithm ID */ + psa_alg = cose_hmac_alg_id_to_psa(cose_alg_id); + if(!PSA_ALG_IS_MAC(psa_alg)) { + return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG; + } + + /* + * Verify if HMAC algorithm is valid. + * According to COSE (RFC 8152), only SHA-256, SHA-384 and SHA-512 are + * supported in COSE_Mac0 with HMAC. + */ + if((psa_alg != PSA_ALG_HMAC(PSA_ALG_SHA_256)) && \ + (psa_alg != PSA_ALG_HMAC(PSA_ALG_SHA_384)) && \ + (psa_alg != PSA_ALG_HMAC(PSA_ALG_SHA_512))) { + return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG; + } + + hmac_ctx->op_ctx = psa_mac_operation_init(); + + psa_ret = psa_mac_sign_setup(&hmac_ctx->op_ctx, + (psa_key_handle_t)signing_key.k.key_handle, + psa_alg); + + return psa_status_to_t_cose_error_hmac(psa_ret); +} + +/* + * See documentation in t_cose_crypto.h + */ +enum t_cose_err_t +t_cose_crypto_hmac_update(struct t_cose_crypto_hmac *hmac_ctx, + struct q_useful_buf_c payload) +{ + psa_status_t psa_ret; + + if(!hmac_ctx) { + return T_COSE_ERR_INVALID_ARGUMENT; + } + + psa_ret = psa_mac_update(&hmac_ctx->op_ctx, + payload.ptr, payload.len); + + return psa_status_to_t_cose_error_hmac(psa_ret); +} + +/* + * See documentation in t_cose_crypto.h + */ +enum t_cose_err_t +t_cose_crypto_hmac_sign_finish(struct t_cose_crypto_hmac *hmac_ctx, + struct q_useful_buf tag_buf, + struct q_useful_buf_c *tag) +{ + psa_status_t psa_ret; + + if(!hmac_ctx) { + return T_COSE_ERR_INVALID_ARGUMENT; + } + + psa_ret = psa_mac_sign_finish(&hmac_ctx->op_ctx, + tag_buf.ptr, tag_buf.len, + &(tag->len)); + if(psa_ret == PSA_SUCCESS) { + tag->ptr = tag_buf.ptr; + } + + return psa_status_to_t_cose_error_hmac(psa_ret); +} +#endif /* !T_COSE_DISABLE_MAC0 */ diff --git a/lib/ext/t_cose/inc/t_cose_common.h b/lib/ext/t_cose/inc/t_cose_common.h index 17ca21adb6..0481ac7e15 100644 --- a/lib/ext/t_cose/inc/t_cose_common.h +++ b/lib/ext/t_cose/inc/t_cose_common.h @@ -2,6 +2,7 @@ * t_cose_common.h * * Copyright 2019, Laurence Lundblade + * Copyright (c) 2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -88,7 +89,41 @@ extern "C" { */ #define T_COSE_ALGORITHM_ES512 -36 +/** + * \def T_COSE_ALGORITHM_HMAC256 + * + * \brief Indicates HMAC with SHA256 + * + * This value comes from the + * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml). + * + * Value for \ref COSE_HEADER_PARAM_ALG to indicate HMAC w/ SHA-256 + */ +#define T_COSE_ALGORITHM_HMAC256 5 +/** + * \def T_COSE_ALGORITHM_HMAC384 + * + * \brief Indicates HMAC with SHA384 + * + * This value comes from the + * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml). + * + * Value for \ref COSE_HEADER_PARAM_ALG to indicate HMAC w/ SHA-384 + */ +#define T_COSE_ALGORITHM_HMAC384 6 + +/** + * \def T_COSE_ALGORITHM_HMAC512 + * + * \brief Indicates HMAC with SHA512 + * + * This value comes from the + * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml). + * + * Value for \ref COSE_HEADER_PARAM_ALG to indicate HMAC w/ SHA-512 + */ +#define T_COSE_ALGORITHM_HMAC512 7 /** @@ -154,6 +189,18 @@ struct t_cose_key { */ #define T_COSE_SIGN1_MAX_SIZE_PROTECTED_PARAMETERS (1+1+5+17) +/* Private value. Intentionally not documented for Doxygen. + * This is the size allocated for the encoded protected headers. It + * needs to be big enough for make_protected_header() to succeed. It + * currently sized for one header with an algorithm ID up to 32 bits + * long -- one byte for the wrapping map, one byte for the label, 5 + * bytes for the ID. If this is made accidentially too small, QCBOR will + * only return an error, and not overrun any buffers. + * + * 9 extra bytes are added, rounding it up to 16 total, in case some + * other protected header is to be added. + */ +#define T_COSE_MAC0_MAX_SIZE_PROTECTED_PARAMETERS (1 + 1 + 5 + 9) /** * Error codes return by t_cose. @@ -347,6 +394,21 @@ enum t_cose_err_t { #define T_COSE_EMPTY_UINT_CONTENT_TYPE UINT16_MAX+1 + + +/** + * An \c option_flag to not add the CBOR type 6 tag for a COSE message. + * Some uses of COSE may require this tag be absent because its COSE + * message type is known from surrounding context. + * + * Or said another way, per the COSE RFC, this code produces a \c + * COSE_Sign1_Tagged/ \c COSE_Mac0_Tagged by default and + * a \c COSE_Sign1/ \c COSE_Mac0 when this flag is set. + * The only difference between these two is the CBOR tag. + */ +#define T_COSE_OPT_OMIT_CBOR_TAG 0x00000002 + + #ifdef __cplusplus } #endif diff --git a/lib/ext/t_cose/inc/t_cose_mac0_sign.h b/lib/ext/t_cose/inc/t_cose_mac0_sign.h new file mode 100644 index 0000000000..c3839f5f0d --- /dev/null +++ b/lib/ext/t_cose/inc/t_cose_mac0_sign.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved. + * Copyright (c) 2020 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __T_COSE_MAC0_SIGN_H_ +#define __T_COSE_MAC0_SIGN_H_ + +#include <stdint.h> +#include "qcbor.h" +#include "t_cose_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * This is the context for creating a \c COSE_Mac0 structure. The caller + * should allocate it and pass it to the functions here. This is + * about 32 bytes so it fits easily on the stack. + */ +struct t_cose_mac0_sign_ctx { + /* Private data structure */ + uint8_t protected_parameters_buffer[ + T_COSE_MAC0_MAX_SIZE_PROTECTED_PARAMETERS]; + struct q_useful_buf_c protected_parameters; /* The encoded protected parameters */ + int32_t cose_algorithm_id; + struct t_cose_key signing_key; + int32_t option_flags; + struct q_useful_buf_c kid; +#ifndef T_COSE_DISABLE_CONTENT_TYPE + uint32_t content_type_uint; + const char *content_type_tstr; +#endif +}; + + +/** + * \brief Initialize to start creating a \c COSE_Mac0. + * + * \param[in] context The t_cose signing context. + * \param[in] option_flags One of \c T_COSE_OPT_XXXX. + * \param[in] cose_algorithm_id The algorithm to generate the authentication + * tag, for example + * \ref T_COSE_ALGORITHM_HMAC256. + * + * Initialize the \ref t_cose_mac0_sign_ctx context. Typically, no + * \c option_flags are needed and 0 is passed. A \c cose_algorithm_id + * must always be given. + * + * The algorithm ID space is from + * [COSE (RFC8152)](https://tools.ietf.org/html/rfc8152) and the + * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml). + * \ref T_COSE_ALGORITHM_HMAC256 is defined here for convenience. + * So far, only HMAC is supported in \c COSE_Mac0. + * + * Errors such as the passing of an unsupported \c cose_algorithm_id + * are reported when t_cose_mac0_encode_parameters() is called. + */ +static void +t_cose_mac0_sign_init(struct t_cose_mac0_sign_ctx *me, + int32_t option_flags, + int32_t cose_algorithm_id); + +/** + * \brief Set the key and kid (key ID) for signing. + * + * \param[in] context The t_cose signing context. + * \param[in] signing_key The signing key to use or \ref T_COSE_NULL_KEY. + * \param[in] kid COSE key ID parameter or \c NULL_Q_USEFUL_BUF_C. + * + * This needs to be called to set the signing key to use. The \c kid + * may be omitted by giving \c NULL_Q_USEFUL_BUF_C. + */ +static void +t_cose_mac0_set_signing_key(struct t_cose_mac0_sign_ctx *context, + struct t_cose_key signing_key, + struct q_useful_buf_c kid); + +/** + * \brief Output first part and parameters for a \c COSE_Mac0 message. + * + * \param[in] context The t_cose signing context. + * \param[in] cbor_encode_ctx Encoding context to output to. + * + * t_cose_mac0_sign_init() and t_cose_mac0_set_signing_key() must be + * called before calling this. + * + * When this is called, the opening parts of the \c COSE_Mac0 message + * are output to the \c cbor_encode_ctx. + * + * After this is called, the CBOR-formatted payload must be written to + * the \c cbor_encode_ctx by calling all the various + * \c QCBOREncode_AddXxx calls. It can be as simple or complex as needed. + * + * To complete the \c COSE_Mac0 call t_cose_mac0_encode_tag(). + * + * The \c cbor_encode_ctx must have been initialized with an output + * buffer to hold the \c COSE_Mac0 header parameters, the payload and the + * signature. + * + * This and t_cose_mac0_encode_tag() can be used to calculate + * the size of the \c COSE_Mac0 in the way \c QCBOREncode is usually + * used to calculate sizes. In this case the \c t_cose_mac0_sign_ctx must + * be initialized with the options, algorithm, key and kid just as + * normal as these are needed to calculate the size. Then set up the + * QCBOR encoder context with a \c NULL pointer and large length like + * \c UINT32_MAX. Call t_cose_mac0_encode_parameters(), then format + * the payload into the encoder context, then call + * t_cose_mac0_encode_tag(). Finally call \c + * QCBOREncode_FinishGetSize() to get the length. + */ +enum t_cose_err_t +t_cose_mac0_encode_parameters(struct t_cose_mac0_sign_ctx *context, + QCBOREncodeContext *cbor_encode_ctx); + +/** + * \brief Finish a \c COSE_Mac0 message by outputting the authentication tag. + * + * \param[in] context The t_cose signing context. + * \param[in] cbor_encode_ctx Encoding context to output to. + * + * \return This returns one of the error codes defined by \ref t_cose_err_t. + * + * Call this to complete creation of a tagged \c COSE_Mac0 started + * with t_cose_mac0_encode_parameters(). + * + * This is when the cryptographic MAC algorithm is run. + * + * The completed \c COSE_Mac0 message is retrieved from the + * \c cbor_encode_ctx by calling \c QCBOREncode_Finish(). + */ +enum t_cose_err_t +t_cose_mac0_encode_tag(struct t_cose_mac0_sign_ctx *context, + QCBOREncodeContext *cbor_encode_ctx); + + +#ifndef T_COSE_DISABLE_CONTENT_TYPE +/** + * \brief Set the payload content type using CoAP content types. + * + * \param[in] context The t_cose signing context. + * \param[in] content_type The content type of the payload as defined + * in the IANA CoAP Content-Formats registry. + * + * It is not allowed to have both a CoAP and MIME content type. This + * error will show up when t_cose_mac0_encode_parameters() is called + * as no error is returned by this function. + * + * The IANA CoAP Content-Formats registry is found + * [here](https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats). + */ +static inline void +t_cose_mac0_set_content_type_uint(struct t_cose_mac0_sign_ctx *context, + uint16_t content_type); + +/** + * \brief Set the payload content type using MIME content types. + * + * \param[in] context The t_cose signing context. + * \param[in] content_type The content type of the payload as defined + * in the IANA Media Types registry. + + * + * It is not allowed to have both a CoAP and MIME content type. This + * error will show up when t_cose_mac0_encode_parameters() is called. + * + * The IANA Media Types registry can be found + * [here](https://www.iana.org/assignments/media-types/media-types.xhtml). + * These have been known as MIME types in the past. + */ +static inline void +t_cose_mac0_set_content_type_tstr(struct t_cose_mac0_sign_ctx *context, + const char *content_type); +#endif /* T_COSE_DISABLE_CONTENT_TYPE */ + +/* ------------------------------------------------------------------------ + * Inline implementations of public functions defined above. + */ +static inline void +t_cose_mac0_sign_init(struct t_cose_mac0_sign_ctx *me, + int32_t option_flags, + int32_t cose_algorithm_id) +{ + memset(me, 0, sizeof(*me)); + +#ifndef T_COSE_DISABLE_CONTENT_TYPE + /* Only member for which 0 is not the empty state */ + me->content_type_uint = T_COSE_EMPTY_UINT_CONTENT_TYPE; +#endif + + me->cose_algorithm_id = cose_algorithm_id; + me->option_flags = option_flags; +} + +static inline void +t_cose_mac0_set_signing_key(struct t_cose_mac0_sign_ctx *me, + struct t_cose_key signing_key, + struct q_useful_buf_c kid) +{ + me->kid = kid; + me->signing_key = signing_key; +} + + +#ifndef T_COSE_DISABLE_CONTENT_TYPE +static inline void +t_cose_mac0_set_content_type_uint(struct t_cose_mac0_sign_ctx *me, + uint16_t content_type) +{ + me->content_type_uint = content_type; +} + +static inline void +t_cose_mac0_set_content_type_tstr(struct t_cose_mac0_sign_ctx *me, + const char *content_type) +{ + me->content_type_tstr = content_type; +} +#endif /* T_COSE_DISABLE_CONTENT_TYPE */ + +#ifdef __cplusplus +} +#endif + +#endif /* __T_COSE_MAC0_SIGN_H_ */ diff --git a/lib/ext/t_cose/inc/t_cose_sign1_sign.h b/lib/ext/t_cose/inc/t_cose_sign1_sign.h index fcb3fbe2da..0dcf0962d8 100644 --- a/lib/ext/t_cose/inc/t_cose_sign1_sign.h +++ b/lib/ext/t_cose/inc/t_cose_sign1_sign.h @@ -104,19 +104,6 @@ struct t_cose_sign1_sign_ctx { #define T_COSE_OPT_SHORT_CIRCUIT_SIG 0x00000001 -/** - * An \c option_flag for t_cose_sign1_sign_init() to not add the CBOR - * type 6 tag for \c COSE_Sign1 whose value is 18. Some uses of COSE - * may require this tag be absent because it is known that it is a \c - * COSE_Sign1 from surrounding context. - * - * Or said another way, per the COSE RFC, this code produces a \c - * COSE_Sign1_Tagged by default and a \c COSE_Sign1 when this flag is - * set. The only difference between these two is the CBOR tag. - */ -#define T_COSE_OPT_OMIT_CBOR_TAG 0x00000002 - - /** diff --git a/lib/ext/t_cose/src/t_cose_crypto.h b/lib/ext/t_cose/src/t_cose_crypto.h index ec68ae458e..80263a90ba 100644 --- a/lib/ext/t_cose/src/t_cose_crypto.h +++ b/lib/ext/t_cose/src/t_cose_crypto.h @@ -2,6 +2,7 @@ * t_cose_crypto.h * * Copyright 2019, Laurence Lundblade + * Copyright (c) 2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -371,6 +372,23 @@ struct t_cose_crypto_hash { }; +/** + * The context for use with the HMAC adaptation layer here. + * Borrow the structure of t_cose_crypto_hash. + */ +struct t_cose_crypto_hmac { + #ifdef T_COSE_USE_PSA_CRYPTO + /* --- The context for PSA Crypto (MBed Crypto) --- */ + psa_mac_operation_t op_ctx; + #else + /* --- Default: generic pointer / handle --- */ + union { + void *ptr; + uint64_t handle; + } context; + int64_t status; + #endif +}; /** * The size of the output of SHA-256. @@ -391,6 +409,26 @@ struct t_cose_crypto_hash { */ #define T_COSE_CRYPTO_SHA512_SIZE 64 +/** + * Size of the signature (tag) output for the HMAC-SHA256. + */ +#define T_COSE_CRYPTO_HMAC256_TAG_SIZE T_COSE_CRYPTO_SHA256_SIZE + +/** + * Size of the signature (tag) output for the HMAC-SHA384. + */ +#define T_COSE_CRYPTO_HMAC384_TAG_SIZE T_COSE_CRYPTO_SHA384_SIZE + +/** + * Size of the signature (tag) output for the HMAC-SHA512. + */ +#define T_COSE_CRYPTO_HMAC512_TAG_SIZE T_COSE_CRYPTO_SHA512_SIZE + +/** + * Max size of the tag output for the HMAC operations. + */ +#define T_COSE_CRYPTO_HMAC_TAG_MAX_SIZE T_COSE_CRYPTO_SHA512_SIZE + /** * The maximum needed to hold a hash. It is smaller and less stack is used @@ -502,7 +540,68 @@ 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); +/** + * \brief Set up a multipart HMAC calculation operation + * + * \param[in,out] hmac_ctx Pointer to the HMAC context. + * \param[in] signing_key The key for the HMAC operation + * \param[in] cose_alg_id The algorithm used in HMAC. + * + * \retval T_COSE_SUCCESS + * Tag calculation succeeds. + * \retval T_COSE_ERR_UNSUPPORTED_SIGNING_ALG + * The algorithm is unsupported. + * \retval T_COSE_ERR_INVALID_ARGUMENT + * Invalid arguments. + * \retval T_COSE_ERR_FAIL + * Some general failure of the HMAC function. + */ +enum t_cose_err_t +t_cose_crypto_hmac_sign_setup(struct t_cose_crypto_hmac *hmac_ctx, + struct t_cose_key signing_key, + const int32_t cose_alg_id); + +/** + * \brief Add a message fragment to a multipart HMAC operation + * + * \param[in,out] hmac_ctx Pointer to the HMAC context. + * \param[in] payload Pointer and length of payload + * + * \retval T_COSE_SUCCESS + * Tag calculation succeeds. + * \retval T_COSE_ERR_SIG_BUFFER_SIZE + * The size of the buffer to hold the tag result was too small. + * \retval T_COSE_ERR_INVALID_ARGUMENT + * Invalid arguments. + * \retval T_COSE_ERR_FAIL + * Some general failure of the HMAC function. + */ +enum t_cose_err_t +t_cose_crypto_hmac_update(struct t_cose_crypto_hmac *hmac_ctx, + struct q_useful_buf_c payload); +/** + * \brief Finish the calculation of the HMAC of a message. + * + * \param[in,out] hmac_ctx Pointer to the HMAC context. + * \param[in] tag_buf Pointer and length into which + * the resulting tag is put. + * \param[out] tag Pointer and length of the + * resulting tag. + * + * \retval T_COSE_SUCCESS + * Tag calculation succeeds. + * \retval T_COSE_ERR_SIG_BUFFER_SIZE + * The size of the buffer to hold the tag result was too small. + * \retval T_COSE_ERR_INVALID_ARGUMENT + * Invalid arguments. + * \retval T_COSE_ERR_FAIL + * Some general failure of the HMAC function. + */ +enum t_cose_err_t +t_cose_crypto_hmac_sign_finish(struct t_cose_crypto_hmac *hmac_ctx, + struct q_useful_buf tag_buf, + struct q_useful_buf_c *tag); /** * \brief Indicate whether a COSE algorithm is ECDSA or not. @@ -571,6 +670,20 @@ static inline bool t_cose_algorithm_is_ecdsa(int32_t cose_algorithm_id) return t_cose_check_list(cose_algorithm_id, ecdsa_list); } +static inline size_t t_cose_tag_size(int32_t cose_alg_id) +{ + switch(cose_alg_id) { + case T_COSE_ALGORITHM_HMAC256: + return T_COSE_CRYPTO_HMAC256_TAG_SIZE; + case T_COSE_ALGORITHM_HMAC384: + return T_COSE_CRYPTO_HMAC384_TAG_SIZE; + case T_COSE_ALGORITHM_HMAC512: + return T_COSE_CRYPTO_HMAC512_TAG_SIZE; + default: + return INT32_MAX; + } +} + #ifdef __cplusplus } #endif diff --git a/lib/ext/t_cose/src/t_cose_mac0_sign.c b/lib/ext/t_cose/src/t_cose_mac0_sign.c new file mode 100644 index 0000000000..7a57409768 --- /dev/null +++ b/lib/ext/t_cose/src/t_cose_mac0_sign.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved. + * Copyright (c) 2020, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * See BSD-3-Clause license in README.md + */ + +#include "qcbor.h" +#include "t_cose_crypto.h" +#include "t_cose_mac0_sign.h" +#include "t_cose_util.h" + +/** + * \file t_cose_mac0_sign.c + * + * \brief This creates t_cose Mac authentication structure without a recipient + * structure. + * Only HMAC is supported so far. + */ + +/** + * \brief Makes the protected header parameters for COSE. + * + * \param[in] cose_algorithm_id The COSE algorithm ID to put in the + * header parameters. + * \param[in] buffer_for_parameters Pointer and length of buffer into which + * the resulting encoded protected + * parameters is put. See return value. + * + * \return The pointer and length of the encoded protected + * parameters is returned, or \c NULL_Q_USEFUL_BUF_C if this fails. + * This will have the same pointer as \c buffer_for_parameters, + * but the pointer is conts and the length is that of the valid + * data, not of the size of the buffer. + * + * The protected parameters are returned in fully encoded CBOR format as + * they are added to the \c COSE_Mac0 message as a binary string. This is + * different from the unprotected parameters which are not handled this + * way. + * + * This returns \c NULL_Q_USEFUL_BUF_C if buffer_for_parameters was too + * small. See also definition of \c T_COSE_MAC0_MAX_SIZE_PROTECTED_PARAMETERS + */ +static inline struct q_useful_buf_c +encode_protected_parameters(int32_t cose_algorithm_id, + struct q_useful_buf buffer_for_parameters) +{ + /* approximate stack use on 32-bit machine: + * CBOR encode context 148 + * local use: 20 + * total: 168 + */ + struct q_useful_buf_c protected_parameters; + QCBORError qcbor_result; + QCBOREncodeContext cbor_encode_ctx; + struct q_useful_buf_c return_value; + + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_parameters); + QCBOREncode_OpenMap(&cbor_encode_ctx); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_HEADER_PARAM_ALG, + cose_algorithm_id); + QCBOREncode_CloseMap(&cbor_encode_ctx); + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &protected_parameters); + + if(qcbor_result == QCBOR_SUCCESS) { + return_value = protected_parameters; + } else { + return_value = NULL_Q_USEFUL_BUF_C; + } + + return return_value; +} + +/** + * \brief Add the unprotected parameters to a CBOR encoding context + * + * \param[in] me The t_cose signing context. + * \param[in] kid The key ID. + * \param[in] cbor_encode_ctx CBOR encoding context to output to + * + * No error is returned. If an error occurred it will be returned when + * \c QCBOR_Finish() is called on \c cbor_encode_ctx. + * + * The unprotected parameters added by this are the kid and content type. + */ +static inline enum t_cose_err_t +add_unprotected_parameters(const struct t_cose_mac0_sign_ctx *me, + const struct q_useful_buf_c kid, + QCBOREncodeContext *cbor_encode_ctx) +{ + QCBOREncode_OpenMap(cbor_encode_ctx); + + if(!q_useful_buf_c_is_null_or_empty(kid)) { + QCBOREncode_AddBytesToMapN(cbor_encode_ctx, + COSE_HEADER_PARAM_KID, + kid); + } + +#ifndef T_COSE_DISABLE_CONTENT_TYPE + if(me->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE && + me->content_type_tstr != NULL) { + /* Both the string and int content types are not allowed */ + return T_COSE_ERR_DUPLICATE_PARAMETER; + } + + if(me->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE) { + QCBOREncode_AddUInt64ToMapN(cbor_encode_ctx, + COSE_HEADER_PARAM_CONTENT_TYPE, + me->content_type_uint); + } + + if(me->content_type_tstr != NULL) { + QCBOREncode_AddSZStringToMapN(cbor_encode_ctx, + COSE_HEADER_PARAM_CONTENT_TYPE, + me->content_type_tstr); + } +#else + (void)me; /* avoid unused parameter warning */ +#endif + + QCBOREncode_CloseMap(cbor_encode_ctx); + + return T_COSE_SUCCESS; +} + +/* + * Public function. See t_cose_mac0.h + */ +enum t_cose_err_t +t_cose_mac0_encode_parameters(struct t_cose_mac0_sign_ctx *me, + QCBOREncodeContext *cbor_encode_ctx) + +{ + size_t tag_len; + enum t_cose_err_t return_value; + struct q_useful_buf buffer_for_protected_parameters; + struct q_useful_buf_c kid; + + /* + * Check the algorithm now by getting the algorithm as an early + * error check even though it is not used until later. + */ + tag_len = t_cose_tag_size(me->cose_algorithm_id); + if(tag_len == INT32_MAX) { + return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG; + } + + /* Add the CBOR tag indicating COSE_Mac0 */ + if(!(me->option_flags & T_COSE_OPT_OMIT_CBOR_TAG)) { + QCBOREncode_AddTag(cbor_encode_ctx, CBOR_TAG_COSE_MAC0); + } + + /* Get started with the tagged array that holds the parts of + * a COSE_Mac0 message + */ + QCBOREncode_OpenArray(cbor_encode_ctx); + + /* The protected headers, which are added as a wrapped bstr */ + buffer_for_protected_parameters = + Q_USEFUL_BUF_FROM_BYTE_ARRAY(me->protected_parameters_buffer); + me->protected_parameters = + encode_protected_parameters(me->cose_algorithm_id, + buffer_for_protected_parameters); + if(q_useful_buf_c_is_null(me->protected_parameters)) { + /* The sizing of storage for protected headers is + * off (should never happen in tested, released code) + */ + return_value = T_COSE_ERR_MAKING_PROTECTED; + goto Done; + } + /* The use of _AddBytes here achieves the bstr wrapping */ + QCBOREncode_AddBytes(cbor_encode_ctx, me->protected_parameters); + + /* The Unprotected parameters */ + /* Get the kid because it goes into the parameters that are about + * to be made. + */ + kid = me->kid; + + return_value = add_unprotected_parameters(me, kid, cbor_encode_ctx); + if(return_value != T_COSE_SUCCESS) { + goto Done; + } + + QCBOREncode_BstrWrap(cbor_encode_ctx); + + /* + * Any failures in CBOR encoding will be caught in finish + * when the CBOR encoding is closed off. No need to track + * here as the CBOR encoder tracks it internally. + */ + +Done: + return return_value; +} + +/* + * Public function. See t_cose_mac0.h + */ +enum t_cose_err_t +t_cose_mac0_encode_tag(struct t_cose_mac0_sign_ctx *me, + QCBOREncodeContext *cbor_encode_ctx) + +{ + enum t_cose_err_t return_value; + QCBORError cbor_err; + /* Pointer and length of the completed tag */ + struct q_useful_buf_c tag; + /* Buffer for the actual tag */ + Q_USEFUL_BUF_MAKE_STACK_UB( tag_buf, + T_COSE_CRYPTO_HMAC_TAG_MAX_SIZE); + struct q_useful_buf_c tbm_first_part; + /* Buffer for the ToBeMaced */ + UsefulBuf_MAKE_STACK_UB( tbm_first_part_buf, + T_COSE_SIZE_OF_TBM); + struct t_cose_crypto_hmac hmac_ctx; + struct q_useful_buf_c maced_payload; + + QCBOREncode_CloseBstrWrap(cbor_encode_ctx, &maced_payload); + + /* Check that there are no CBOR encoding errors before proceeding + * with hashing and tagging. This is not actually necessary as the + * errors will be caught correctly later, but it does make it a + * bit easier for the caller to debug problems. + */ + cbor_err = QCBOREncode_GetErrorState(cbor_encode_ctx); + if(cbor_err == QCBOR_ERR_BUFFER_TOO_SMALL) { + return_value = T_COSE_ERR_TOO_SMALL; + goto Done; + } else if(cbor_err != QCBOR_SUCCESS) { + return_value = T_COSE_ERR_CBOR_FORMATTING; + goto Done; + } + + if(QCBOREncode_IsBufferNULL(cbor_encode_ctx)) { + /* Just calculating sizes. All that is needed is the tag size. */ + tag.ptr = NULL; + tag.len = t_cose_tag_size(me->cose_algorithm_id); + + return_value = T_COSE_SUCCESS; + goto CloseArray; + } + + /* Create the hash of the ToBeMaced bytes. Inputs to the + * MAC are the protected parameters, the payload that is + * getting MACed. + */ + return_value = create_tbm(tbm_first_part_buf, + me->protected_parameters, + &tbm_first_part, + T_COSE_TBM_PAYLOAD_IS_BSTR_WRAPPED, + maced_payload); + if(return_value) { + goto Done; + } + + /* + * Start the HMAC. + * Calculate the tag of the first part of ToBeMaced and the wrapped + * payload, to save a bigger buffer containing the entire ToBeMaced. + */ + return_value = t_cose_crypto_hmac_sign_setup(&hmac_ctx, + me->signing_key, + me->cose_algorithm_id); + if(return_value) { + goto Done; + } + + /* Compute the tag of the first part. */ + return_value = t_cose_crypto_hmac_update(&hmac_ctx, + q_useful_buf_head(tbm_first_part, + tbm_first_part.len)); + if(return_value) { + goto Done; + } + + /* + * It is assumed that the context payload has been wrapped in a byte + * string in CBOR format. + */ + return_value = t_cose_crypto_hmac_update(&hmac_ctx, maced_payload); + if(return_value) { + goto Done; + } + + return_value = t_cose_crypto_hmac_sign_finish(&hmac_ctx, tag_buf, &tag); + if(return_value) { + goto Done; + } + +CloseArray: + /* Add tag to CBOR and close out the array */ + QCBOREncode_AddBytes(cbor_encode_ctx, tag); + QCBOREncode_CloseArray(cbor_encode_ctx); + + /* CBOR encoding errors are tracked in the CBOR encoding context + * and handled in the layer above this + */ + +Done: + return return_value; +} diff --git a/lib/ext/t_cose/src/t_cose_standard_constants.h b/lib/ext/t_cose/src/t_cose_standard_constants.h index 886dc1f58d..4f8f9a734e 100644 --- a/lib/ext/t_cose/src/t_cose_standard_constants.h +++ b/lib/ext/t_cose/src/t_cose_standard_constants.h @@ -2,6 +2,7 @@ * t_cose_standard_constants.h * * Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved. + * Copyright (c) 2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -401,5 +402,12 @@ */ #define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1" +/** + * \def COSE_MAC_CONTEXT_STRING_MAC0 + * + * \brief This is a string constant used by COSE to label \c COSE_Mac0 + * structures. See RFC 8152, section 6.3. + */ +#define COSE_MAC_CONTEXT_STRING_MAC0 "MAC0" #endif /* __T_COSE_STANDARD_CONSTANTS_H__ */ diff --git a/lib/ext/t_cose/src/t_cose_util.c b/lib/ext/t_cose/src/t_cose_util.c index a0fae2d1cc..b6c9f11fa7 100644 --- a/lib/ext/t_cose/src/t_cose_util.c +++ b/lib/ext/t_cose/src/t_cose_util.c @@ -2,6 +2,7 @@ * t_cose_util.c * * Copyright 2019, Laurence Lundblade + * Copyright (c) 2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -49,6 +50,65 @@ int32_t hash_alg_id_from_sig_alg_id(int32_t cose_algorithm_id) } +#ifndef T_COSE_DISABLE_MAC0 +enum t_cose_err_t create_tbm(UsefulBuf tbm_first_part_buf, + struct q_useful_buf_c protected_headers, + struct q_useful_buf_c *tbm_first_part, + enum t_cose_tbm_payload_mode_t payload_mode, + struct q_useful_buf_c payload) +{ + QCBOREncodeContext cbor_encode_ctx; + QCBORError qcbor_result; + size_t bytes_to_omit; + + /* This builds the CBOR-format to-be-maced bytes */ + QCBOREncode_Init(&cbor_encode_ctx, tbm_first_part_buf); + QCBOREncode_OpenArray(&cbor_encode_ctx); + /* context */ + QCBOREncode_AddSZString(&cbor_encode_ctx, COSE_MAC_CONTEXT_STRING_MAC0); + /* body_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, protected_headers); + + /* external_aad. There is none so an empty bstr */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C); + + /* The short fake payload. */ + if(payload_mode == T_COSE_TBM_PAYLOAD_IS_BSTR_WRAPPED) { + /* Fake payload is just an empty bstr. It is here only + * to make the array count right. It must be omitted + * in the actual MAC below + */ + bytes_to_omit = 1; + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C); + } else { + /* Fake payload is the type and length of the wrapping + * bstr. It gets MACed with the first part, so no + * bytes to omit. + */ + bytes_to_omit = 0; + QCBOREncode_AddBytesLenOnly(&cbor_encode_ctx, payload); + } + + /* Close of the array */ + QCBOREncode_CloseArray(&cbor_encode_ctx); + + /* get the encoded results, except for payload */ + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, tbm_first_part); + if(qcbor_result) { + /* Mainly means that the protected_headers were too big + * (which should never happen) + */ + return T_COSE_ERR_SIG_STRUCT; + } + + tbm_first_part->len -= bytes_to_omit; + + return T_COSE_SUCCESS; +} +#endif /* !T_COSE_DISABLE_MAC0 */ + + +#ifndef T_COSE_DISABLE_SIGN1 /* * Format of to-be-signed bytes used by create_tbs_hash(). This is * defined in COSE (RFC 8152) section 4.4. It is the input to the @@ -198,6 +258,7 @@ enum t_cose_err_t create_tbs_hash(int32_t cose_algorithm_id, Done: return return_value; } +#endif /* !T_COSE_DISABLE_SIGN1 */ #ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN diff --git a/lib/ext/t_cose/src/t_cose_util.h b/lib/ext/t_cose/src/t_cose_util.h index b171d039cc..c2542b2637 100644 --- a/lib/ext/t_cose/src/t_cose_util.h +++ b/lib/ext/t_cose/src/t_cose_util.h @@ -2,6 +2,7 @@ * t_cose_util.h * * Copyright 2019, Laurence Lundblade + * Copyright (c) 2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -46,6 +47,22 @@ enum t_cose_tbs_hash_mode_t { }; +/** + * The modes in which the payload is passed to create_tbm(). This + * exists so the ToBeMaced bytes can be hashed in two separate chunks and + * avoids needing a second buffer the size of the payload in the + * t_cose implementation. + */ +enum t_cose_tbm_payload_mode_t { + /** The bytes passed for the payload include a wrapping bstr so + * one does not need to be added. + */ + T_COSE_TBM_PAYLOAD_IS_BSTR_WRAPPED, + /** The bytes passed for the payload do NOT have a wrapping bstr + * so one must be added. + */ + T_COSE_TBM_BARE_PAYLOAD +}; /** * This value represents an invalid or in-error algorithm ID. The @@ -56,6 +73,32 @@ enum t_cose_tbs_hash_mode_t { #define T_COSE_INVALID_ALGORITHM_ID COSE_ALGORITHM_RESERVED +/* + * Format of ToBeMaced bytes + * This is defined in COSE (RFC 8152) section 6.2. It is the input to the HMAC + * operation. + * + * MAC_structure = [ + * context : "MAC0", + * protected : empty_or_serialized_map, + * external_aad : bstr, + * payload : bstr + * ] + */ + +/** + * This is the size of the first part of the CBOR encoded ToBeMaced + * bytes. It is around 30 bytes. + */ +#define T_COSE_SIZE_OF_TBM \ + 1 + /* For opening the array */ \ + sizeof(COSE_MAC_CONTEXT_STRING_MAC0) + /* "MAC0" */ \ + 2 + /* Overhead for encoding string */ \ + T_COSE_MAC0_MAX_SIZE_PROTECTED_PARAMETERS + /* entire protected headers */ \ + 1 + /* Empty bstr for absent external_aad */ \ + 9 /* The max CBOR length encoding for start of payload */ + + /** * \brief Return hash algorithm ID from a signature algorithm ID * @@ -83,6 +126,33 @@ enum t_cose_tbs_hash_mode_t { */ int32_t hash_alg_id_from_sig_alg_id(int32_t cose_algorithm_id); +/** + * \brief Create the ToBeMaced (TBM) structure bytes for COSE. + * + * \param[in] tbm_first_part_buf The buffer to contain the first part + * \param[in] protected_headers The CBOR encoded protected headers. + * \param[out] tbm_first_part Pointer and length of buffer into which + * the resulting TBM is put. + * \param[in] payload_mode See \ref t_cose_tbm_payload_mode_t. + * \param[in] payload The CBOR encoded payload. It may or may + * not have a wrapping bstr per + * \c payload_mode. + * + * \return This returns one of the error codes defined by \ref t_cose_err_t. + * + * \retval T_COSE_ERR_SIG_STRUCT + * Most likely this is because the protected_headers passed in + * is larger than \ref T_COSE_MAC0_MAX_PROT_HEADER. + * \retval T_COSE_ERR_UNSUPPORTED_HASH + * If the hash algorithm is not known. + * \retval T_COSE_ERR_HASH_GENERAL_FAIL + * In case of some general hash failure. + */ +enum t_cose_err_t create_tbm(UsefulBuf tbm_first_part_buf, + struct q_useful_buf_c protected_headers, + struct q_useful_buf_c *tbm_first_part, + enum t_cose_tbm_payload_mode_t payload_mode, + struct q_useful_buf_c payload); /** * \brief Create the hash of the to-be-signed (TBS) bytes for COSE. |