blob: 4228c2babda7cc72f27407634ebfd26ba3fbc7f9 [file] [log] [blame]
/*
* t_cose_util.c
*
* Copyright 2019, Laurence Lundblade
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.mdE.
*/
#include "t_cose_util.h"
#include "qcbor.h"
#include "t_cose_defines.h"
#include "t_cose_common.h"
#include "t_cose_crypto.h"
/**
* \file t_cose_util.c
*
* \brief Implementation of t_cose utility functions.
*
*/
/*
* Public function. See t_cose_util.h
*/
int32_t hash_alg_id_from_sig_alg_id(int32_t cose_sig_alg_id)
{
/* if other hashes, particularly those that output bigger hashes
* are added here, various other parts of this code have to be
* changed to have larger buffers.
*/
switch(cose_sig_alg_id) {
case COSE_ALGORITHM_ES256:
return COSE_ALG_SHA256_PROPRIETARY;
default:
return INT32_MAX;
}
}
/*
* Format of to-be-signed bytes used by create_tbs_hash().
* This is defined in COSE (RFC 8152). It is the input
* to the hash.
*
* Sig_structure = [
* context : "Signature" / "Signature1" / "CounterSignature",
* body_protected : empty_or_serialized_map,
* ? sign_protected : empty_or_serialized_map,
* external_aad : bstr,
* payload : bstr
* ]
*/
/**
* This is the size of the first part of the CBOR encoded TBS
* bytes. It is around 20 bytes. See create_tbs_hash().
*/
#define T_COSE_SIZE_OF_TBS \
1 + /* For opening the array */ \
sizeof(COSE_SIG_CONTEXT_STRING_SIGNATURE1) + /* "Signature1" */ \
2 + /* Overhead for encoding string */ \
T_COSE_SIGN1_MAX_PROT_HEADER + /* entire protected headers */ \
3 * ( /* 3 NULL bstrs for fields not used */ \
1 /* size of a NULL bstr */ \
)
/*
* Public function. See t_cose_util.h
*/
enum t_cose_err_t create_tbs_hash(int32_t cose_alg_id,
struct q_useful_buf buffer_for_hash,
struct q_useful_buf_c *hash,
struct q_useful_buf_c protected_headers,
struct q_useful_buf_c payload)
{
/* approximate stack use on 32-bit machine:
* local use: 320
* with calls: 360
*/
enum t_cose_err_t return_value;
QCBOREncodeContext cbor_encode_ctx;
UsefulBuf_MAKE_STACK_UB( buffer_for_TBS_first_part, T_COSE_SIZE_OF_TBS);
struct q_useful_buf_c tbs_first_part;
QCBORError qcbor_result;
struct t_cose_crypto_hash hash_ctx;
int32_t hash_alg_id;
/* This builds the CBOR-format to-be-signed bytes */
QCBOREncode_Init(&cbor_encode_ctx, buffer_for_TBS_first_part);
QCBOREncode_OpenArray(&cbor_encode_ctx);
/* context */
QCBOREncode_AddSZString(&cbor_encode_ctx,
COSE_SIG_CONTEXT_STRING_SIGNATURE1);
/* body_protected */
QCBOREncode_AddBytes(&cbor_encode_ctx,
protected_headers);
/* sign_protected */
QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
/* external_aad */
QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
/* fake payload */
QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
QCBOREncode_CloseArray(&cbor_encode_ctx);
/* get the result and convert it to struct q_useful_buf_c representation */
qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part);
if(qcbor_result) {
/* Mainly means that the protected_headers were too big
(which should never happen) */
return_value = T_COSE_ERR_SIG_STRUCT;
goto Done;
}
/* Start the hashing */
hash_alg_id = hash_alg_id_from_sig_alg_id(cose_alg_id);
/* Don't check hash_alg_id for failure. t_cose_crypto_hash_start()
will handle it properly
*/
return_value = t_cose_crypto_hash_start(&hash_ctx, hash_alg_id);
if(return_value) {
goto Done;
}
/* Hash the first part of the TBS. Take all but the last two
* bytes. The last two bytes are the fake payload from above. It
* is replaced by the real payload which is hashed next. The fake
* payload is needed so the array count is right. This is one of
* the main things that make it possible to implement with one
* buffer for the whole cose sign1.
*/
t_cose_crypto_hash_update(&hash_ctx,
q_useful_buf_head(tbs_first_part,
tbs_first_part.len - 2));
/* Hash the payload */
t_cose_crypto_hash_update(&hash_ctx, payload);
/* Finish the hash and set up to return it */
return_value = t_cose_crypto_hash_finish(&hash_ctx,
buffer_for_hash,
hash);
Done:
return return_value;
}
/*
* Public function. See t_cose_util.h
*/
enum t_cose_err_t
get_short_circuit_kid(struct q_useful_buf buffer_for_kid,
struct q_useful_buf_c *kid)
{
/* This is a random hard coded key ID that is used to indicate
* short-circuit signing. It is OK to hard code this as the
* probability of collision with this ID is very low and the same
* as for collision between any two key IDs of any sort.
*/
uint8_t defined_short_circuit_kid[] = {
0xef, 0x95, 0x4b, 0x4b, 0xd9, 0xbd, 0xf6, 0x70,
0xd0, 0x33, 0x60, 0x82, 0xf5, 0xef, 0x15, 0x2a,
0xf8, 0xf3, 0x5b, 0x6a, 0x6c, 0x00, 0xef, 0xa6,
0xa9, 0xa7, 0x1f, 0x49, 0x51, 0x7e, 0x18, 0xc6};
/* Prevent a dumb error where the size constant in the header is
* wrong.This check will be evaluated at compile time and optimize
* out when all is correct.
*/
if(sizeof(defined_short_circuit_kid) != T_COSE_SHORT_CIRCUIT_KID_SIZE) {
return T_COSE_ERR_BAD_SHORT_CIRCUIT_KID;
}
*kid = q_useful_buf_copy(buffer_for_kid,
Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(
defined_short_circuit_kid));
return q_useful_buf_c_is_null(*kid) ?
T_COSE_ERR_KEY_BUFFER_SIZE :
T_COSE_SUCCESS;
}