blob: fb5f633cfbd8afcd6c5addfbd0d9028484de4e24 [file] [log] [blame]
/*
* Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
* Copyright (c) 2020-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#include "qcbor/qcbor.h"
#include "t_cose_crypto.h"
#include "t_cose_mac0_verify.h"
#include "t_cose_parameters.h"
#include "t_cose_util.h"
#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
/**
* \brief Verify a short-circuit tag
*
* \param[in] cose_alg_id Algorithm ID. This is used only to make
* the short-circuit signature the same size as the
* real tag would be for the particular algorithm.
* \param[in] header The Header of COSE_Mac0.
* \param[in] payload The payload of COSE_Mac0
* \param[in] tag Pointer and length of tag to be verified
*
* \return This returns one of the error codes defined by \ref
* t_cose_err_t.
*
* See short_circuit_tag() in t_cose_mac0_sign.c for description of
* the short-circuit tag.
*/
static inline enum t_cose_err_t
short_circuit_verify(int32_t cose_alg_id,
struct q_useful_buf_c header,
struct q_useful_buf_c payload,
struct q_useful_buf_c tag_to_verify)
{
/* approximate stack use on 32-bit machine: local use: 16 bytes */
enum t_cose_err_t return_value;
struct t_cose_crypto_hash hash_ctx;
Q_USEFUL_BUF_MAKE_STACK_UB(tag_buffer, T_COSE_CRYPTO_HMAC_TAG_MAX_SIZE);
struct q_useful_buf_c tag;
int32_t hash_alg_id;
hash_alg_id = t_cose_hmac_to_hash_alg_id(cose_alg_id);
if (hash_alg_id == INT32_MAX) {
return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
goto Done;
}
return_value = t_cose_crypto_hash_start(&hash_ctx, hash_alg_id);
if (return_value != T_COSE_SUCCESS) {
goto Done;
}
/* Hash the Header */
t_cose_crypto_hash_update(&hash_ctx, q_useful_buf_head(header, header.len));
/* Hash the payload */
t_cose_crypto_hash_update(&hash_ctx, payload);
return_value = t_cose_crypto_hash_finish(&hash_ctx, tag_buffer, &tag);
if (return_value != T_COSE_SUCCESS) {
goto Done;
}
if (q_useful_buf_compare(tag_to_verify, tag)) {
return_value = T_COSE_ERR_SIG_VERIFY;
} else {
return_value = T_COSE_SUCCESS;
}
Done:
return return_value;
}
#endif /* T_COSE_DISABLE_SHORT_CIRCUIT_SIGN */
/**
* \file t_cose_mac0_verify.c
*
* \brief This verifies t_cose Mac authentication structure without a recipient
* structure.
* Only HMAC is supported so far.
*/
/*
* Public function. See t_cose_mac0.h
*/
enum t_cose_err_t t_cose_mac0_verify(struct t_cose_mac0_verify_ctx *context,
struct q_useful_buf_c cose_mac0,
struct q_useful_buf_c *payload,
struct t_cose_parameters *parameters)
{
QCBORDecodeContext decode_context;
QCBORItem item;
struct q_useful_buf_c protected_parameters;
struct t_cose_parameters parsed_protected_parameters;
struct t_cose_parameters unprotected_parameters;
struct t_cose_label_list critical_labels;
struct t_cose_label_list unknown_labels;
enum t_cose_err_t return_value;
struct q_useful_buf_c tag;
struct q_useful_buf_c tbm_first_part;
/* Buffer for the ToBeMaced */
Q_USEFUL_BUF_MAKE_STACK_UB( tbm_first_part_buf,
T_COSE_SIZE_OF_TBM);
struct t_cose_crypto_hmac hmac_ctx;
*payload = NULL_Q_USEFUL_BUF_C;
QCBORDecode_Init(&decode_context, cose_mac0, QCBOR_DECODE_MODE_NORMAL);
/* Calls to QCBORDecode_GetNext() rely on item.uDataType != QCBOR_TYPE_ARRAY
* to detect decoding errors rather than checking the return code.
*/
/* -- The array of four -- */
(void)QCBORDecode_GetNext(&decode_context, &item);
if(item.uDataType != QCBOR_TYPE_ARRAY) {
return_value = T_COSE_ERR_MAC0_FORMAT;
goto Done;
}
if((context->option_flags & T_COSE_OPT_TAG_REQUIRED) &&
!QCBORDecode_IsTagged(&decode_context, &item, CBOR_TAG_COSE_MAC0)) {
return_value = T_COSE_ERR_INCORRECTLY_TAGGED;
goto Done;
}
/* -- Clear list where unknown labels are accumulated -- */
clear_label_list(&unknown_labels);
/* -- Get the protected header parameters -- */
(void)QCBORDecode_GetNext(&decode_context, &item);
if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
return_value = T_COSE_ERR_MAC0_FORMAT;
goto Done;
}
protected_parameters = item.val.string;
return_value = parse_protected_header_parameters(protected_parameters,
&parsed_protected_parameters,
&critical_labels,
&unknown_labels);
if(return_value != T_COSE_SUCCESS) {
goto Done;
}
/* -- Get the unprotected parameters -- */
return_value = parse_unprotected_header_parameters(&decode_context,
&unprotected_parameters,
&unknown_labels);
if(return_value != T_COSE_SUCCESS) {
goto Done;
}
if((context->option_flags & T_COSE_OPT_REQUIRE_KID) &&
q_useful_buf_c_is_null(unprotected_parameters.kid)) {
return_value = T_COSE_ERR_NO_KID;
goto Done;
}
/* -- Check critical parameter labels -- */
return_value = check_critical_labels(&critical_labels, &unknown_labels);
if(return_value != T_COSE_SUCCESS) {
goto Done;
}
/* -- Check for duplicate parameters and copy to returned parameters -- */
return_value = check_and_copy_parameters(&parsed_protected_parameters,
&unprotected_parameters,
parameters);
if(return_value != T_COSE_SUCCESS) {
goto Done;
}
/* -- Get the payload -- */
QCBORDecode_GetNext(&decode_context, &item);
if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
return_value = T_COSE_ERR_MAC0_FORMAT;
goto Done;
}
*payload = item.val.string;
/* -- Get the tag -- */
QCBORDecode_GetNext(&decode_context, &item);
if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
return_value = T_COSE_ERR_MAC0_FORMAT;
goto Done;
}
tag = item.val.string;
/* -- Finish up the CBOR decode -- */
/* This check make sure the array only had the expected four
* items. Works for definite and indefinite length arrays. Also
* make sure there were no extra bytes.
*/
if(QCBORDecode_Finish(&decode_context) != QCBOR_SUCCESS) {
return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
goto Done;
}
/* -- Skip tag verification if such is requested --*/
if(context->option_flags & T_COSE_OPT_DECODE_ONLY) {
return_value = T_COSE_SUCCESS;
goto Done;
}
/* -- Compute the ToBeMaced -- */
return_value = create_tbm(tbm_first_part_buf,
protected_parameters,
&tbm_first_part,
T_COSE_TBM_BARE_PAYLOAD,
*payload);
if(return_value) {
goto Done;
}
if (context->option_flags & T_COSE_OPT_ALLOW_SHORT_CIRCUIT) {
#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
/* Short-circuit tag. Hash is used to generated tag instead of HMAC */
return_value = short_circuit_verify(
parsed_protected_parameters.cose_algorithm_id,
tbm_first_part,
*payload,
tag);
#else
return_value = T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED;
#endif
goto Done;
}
/*
* Start the HMAC verification.
* 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_verify_setup(&hmac_ctx,
parsed_protected_parameters.cose_algorithm_id,
context->verification_key);
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;
}
return_value = t_cose_crypto_hmac_update(&hmac_ctx, *payload);
if(return_value) {
goto Done;
}
return_value = t_cose_crypto_hmac_verify_finish(&hmac_ctx, tag);
Done:
return return_value;
}