Skip to content

Commit

Permalink
Support for empty protected headers (#267)
Browse files Browse the repository at this point in the history
* Support for empty protected headers

* decoding/encoding of unprotected alg id param

* documentation formatting

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
  • Loading branch information
laurencelundblade and Laurence Lundblade authored Nov 4, 2023
1 parent 14d5975 commit fa047af
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 36 deletions.
8 changes: 6 additions & 2 deletions inc/t_cose/t_cose_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
* See BSD-3-Clause license in README.md
*/


#ifndef __T_COSE_COMMON_H__
#define __T_COSE_COMMON_H__

Expand Down Expand Up @@ -636,7 +635,12 @@ enum t_cose_err_t {
* to be larger because there are too many protected
* headers, party u/v identities were added or
* supp info was added. TODO: see xxxx*/
T_COSE_ERR_KDF_CONTEXT_SIZE = 88
T_COSE_ERR_KDF_CONTEXT_SIZE = 88,

/** Protected headers exists when they are not allowed. This typically occurs when the
* crypto algorithm is not AEAD and thus can't protect the headers. */
T_COSE_ERR_PROTECTED_NOT_ALLOWED = 89,

};


Expand Down
88 changes: 84 additions & 4 deletions inc/t_cose/t_cose_parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ t_cose_headers_encode(QCBOREncodeContext *cbor_encoder,
*
* \param[in] cbor_decoder QCBOR decoder to decode from.
* \param[in] location Location in message of the parameters.
* \param[in] no_protected Protected headers must be empty.
* \param[in] special_decode_cb Callback for non-integer and
* non-string parameters.
* \param[in] special_decode_ctx Context for the above callback
Expand Down Expand Up @@ -462,6 +463,11 @@ t_cose_headers_encode(QCBOREncodeContext *cbor_encoder,
* non-integer and non-string header parameters. It typically switches
* on the parameter label.
*
* If \c no_protected is \c true, then the protected headers must be
* an empty byte string. This is used when the cryptographic algorithm
* used can't protect headers, for example non-AEAD ciphers for COSE
* encryption.
*
* The crit parameter will be decoded and any parameter label
* listed in it will be marked as crit in the list returned. It is up
* to the caller to check the list for crit parameters and error
Expand All @@ -480,6 +486,7 @@ t_cose_headers_encode(QCBOREncodeContext *cbor_encoder,
enum t_cose_err_t
t_cose_headers_decode(QCBORDecodeContext *cbor_decoder,
const struct t_cose_header_location location,
bool no_protected,
t_cose_param_special_decode_cb *special_decode_cb,
void *special_decode_ctx,
struct t_cose_parameter_storage *parameter_storage,
Expand Down Expand Up @@ -557,6 +564,11 @@ t_cose_params_append(struct t_cose_parameter **existing,
static struct t_cose_parameter
t_cose_param_make_alg_id(int32_t alg_id);

/* For the rare case of a non-AEAD algorithm */
static struct t_cose_parameter
t_cose_param_make_unprot_alg_id(int32_t alg_id);


/**
* Make a struct t_cose_parameter for an unsigned integer content typ.
*
Expand Down Expand Up @@ -642,19 +654,52 @@ t_cose_param_find_bstr(const struct t_cose_parameter *parameter_list, int64_t la


/**
* \brief Find the algorithm ID parameter in a linked list
* \brief Find the algorithm ID parameter in a linked list.
*
* \param[in] parameter_list The parameter list to search.
* \param[in] prot If \c true, parameter must be protected and vice versa.
* \param[in] prot Place to return whether ID is protected or not.
*
* \return The algorithm ID or \ref T_COSE_ALGORITHM_NONE.
*
* This returns \ref T_COSE_ALGORITHM_NONE on all errors including
* errors such as the parameter not being present, the parameter being
* of the wrong type and the parameter not being protected.
* of the wrong type.
*/
int32_t
t_cose_param_find_alg_id(const struct t_cose_parameter *parameter_list, bool prot);
t_cose_param_find_alg_id(const struct t_cose_parameter *parameter_list, bool *prot);


/**
* \brief Find the protected algorithm ID parameter in a linked list.
*
* \param[in] parameter_list The parameter list to search.
*
* \return The algorithm ID or \ref T_COSE_ALGORITHM_NONE.
*
* This returns \ref T_COSE_ALGORITHM_NONE on all errors including
* errors such as the parameter not being present, the parameter being
* of the wrong type or the parameter not being protected.
*/
static int32_t
t_cose_param_find_alg_id_prot(const struct t_cose_parameter *parameter_list);


/**
* \brief Find the unprotected algorithm ID parameter in a linked list.
*
* \param[in] parameter_list The parameter list to search.
*
* \return The algorithm ID or \ref T_COSE_ALGORITHM_NONE.
*
* This is for the unusual case were the algorithm ID must NOT be a
* protected parameter, such as for non-AEAD algorithms.
*
* This returns \ref T_COSE_ALGORITHM_NONE on all errors including
* errors such as the parameter not being present, the parameter being
* of the wrong type or the parameter being protected.
*/
static int32_t
t_cose_param_find_alg_id_unprot(const struct t_cose_parameter *parameter_list);


/**
Expand Down Expand Up @@ -848,6 +893,15 @@ t_cose_param_make_alg_id(int32_t alg_id)
return parameter;
}

static inline struct t_cose_parameter
t_cose_param_make_unprot_alg_id(int32_t alg_id) {
struct t_cose_parameter parameter;

parameter = t_cose_param_make_alg_id(alg_id);
parameter.in_protected = false;
return parameter;
}

static inline struct t_cose_parameter
t_cose_param_make_unprot_bstr(struct q_useful_buf_c string, int32_t label)
{
Expand Down Expand Up @@ -950,6 +1004,32 @@ t_cose_param_make_partial_iv(struct q_useful_buf_c iv)
return parameter;
}

static inline int32_t
t_cose_param_find_alg_id_prot(const struct t_cose_parameter *parameter_list)
{
int32_t alg_id;
bool prot;
alg_id = t_cose_param_find_alg_id(parameter_list, &prot);
if(prot != true) {
return T_COSE_ALGORITHM_NONE;
}

return alg_id;
}


static inline int32_t
t_cose_param_find_alg_id_unprot(const struct t_cose_parameter *parameter_list)
{
int32_t alg_id;
bool prot;
alg_id = t_cose_param_find_alg_id(parameter_list, &prot);
if(prot != true) {
return T_COSE_ALGORITHM_NONE;
}

return alg_id;
}


static inline void
Expand Down
3 changes: 2 additions & 1 deletion src/t_cose_encrypt_dec.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ t_cose_encrypt_dec_detached(struct t_cose_encrypt_dec_ctx* me,
t_cose_headers_decode(
&cbor_decoder, /* in: cbor decoder context */
header_location, /* in: location of headers in message */
false, /* in: no_protected headers */
NULL, /* TODO: in: header decode callback function */
NULL, /* TODO: in: header decode callback context */
me->p_storage, /* in: pool of nodes for linked list */
Expand All @@ -191,7 +192,7 @@ t_cose_encrypt_dec_detached(struct t_cose_encrypt_dec_ctx* me,
}

nonce_cbor = t_cose_param_find_iv(body_params_list);
ce_alg.cose_alg_id = t_cose_param_find_alg_id(body_params_list, true);
ce_alg.cose_alg_id = t_cose_param_find_alg_id_prot(body_params_list);
all_params_list = body_params_list;

ce_alg.bits_in_key = bits_in_crypto_alg(ce_alg.cose_alg_id);
Expand Down
3 changes: 2 additions & 1 deletion src/t_cose_mac_validate.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ t_cose_mac_validate_private(struct t_cose_mac_validate_ctx *me,
decoded_params = NULL;
t_cose_headers_decode(&decode_context,
l,
false,
NULL,
NULL,
&me->parameter_storage,
Expand Down Expand Up @@ -146,7 +147,7 @@ t_cose_mac_validate_private(struct t_cose_mac_validate_ctx *me,
* payload, to save a bigger buffer containing the entire ToBeMaced.
*/
return_value = t_cose_crypto_hmac_validate_setup(&hmac_ctx,
t_cose_param_find_alg_id(decoded_params, true),
t_cose_param_find_alg_id_prot(decoded_params),
me->validation_key);

if(return_value) {
Expand Down
46 changes: 27 additions & 19 deletions src/t_cose_parameters.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,13 +487,14 @@ param_dup_detect(const struct t_cose_parameter *params_list)
enum t_cose_err_t
t_cose_headers_decode(QCBORDecodeContext *cbor_decoder,
const struct t_cose_header_location location,
const bool no_protected,
t_cose_param_special_decode_cb *special_decode_cb,
void *special_decode_ctx,
struct t_cose_parameter_storage *param_storage,
struct t_cose_parameter **decoded_params,
struct q_useful_buf_c *protected_parameters)
{
/* Approximate stack usage
/* Approximate stack usage
* 64-bit 32-bit
* local vars 24 12
* largest call, t_cose_params_decode 416 352
Expand All @@ -503,27 +504,36 @@ t_cose_headers_decode(QCBORDecodeContext *cbor_decoder,
QCBORError cbor_error;
enum t_cose_err_t return_value;
struct t_cose_parameter *newly_decode_params;
struct q_useful_buf_c empty_protected;

newly_decode_params = NULL;

/* --- The protected parameters --- */
QCBORDecode_EnterBstrWrapped(cbor_decoder,
QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
protected_parameters);

if(protected_parameters->len) {
return_value = t_cose_params_decode(cbor_decoder,
location,
true,
special_decode_cb,
special_decode_ctx,
param_storage,
&newly_decode_params);
if(return_value != T_COSE_SUCCESS) {
if(no_protected) {
QCBORDecode_GetByteString(cbor_decoder, &empty_protected);
if(empty_protected.len != 0) {
return_value = T_COSE_ERR_PROTECTED_NOT_ALLOWED;
goto Done;
}
} else {
QCBORDecode_EnterBstrWrapped(cbor_decoder,
QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
protected_parameters);

if(protected_parameters->len) {
return_value = t_cose_params_decode(cbor_decoder,
location,
true,
special_decode_cb,
special_decode_ctx,
param_storage,
&newly_decode_params);
if(return_value != T_COSE_SUCCESS) {
goto Done;
}
}
QCBORDecode_ExitBstrWrapped(cbor_decoder);
}
QCBORDecode_ExitBstrWrapped(cbor_decoder);

/* --- The unprotected parameters --- */
return_value = t_cose_params_decode(cbor_decoder,
Expand Down Expand Up @@ -722,7 +732,7 @@ t_cose_param_find(const struct t_cose_parameter *parameter_list, int64_t label)
* Public function. See t_cose_parameters.h
*/
int32_t
t_cose_param_find_alg_id(const struct t_cose_parameter *parameter_list, bool prot)
t_cose_param_find_alg_id(const struct t_cose_parameter *parameter_list, bool *prot)
{
const struct t_cose_parameter *p_found;

Expand All @@ -734,9 +744,7 @@ t_cose_param_find_alg_id(const struct t_cose_parameter *parameter_list, bool pro
return T_COSE_ALGORITHM_NONE;
}

if(prot != p_found->in_protected) { /* effective exclusive OR */
return T_COSE_ALGORITHM_NONE;
}
*prot = p_found->in_protected;

return (int32_t)p_found->value.int64;
}
Expand Down
3 changes: 2 additions & 1 deletion src/t_cose_recipient_dec_esdh.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ t_cose_recipient_dec_esdh_cb_private(struct t_cose_recipient_dec *me_x,
cose_result = t_cose_headers_decode(
cbor_decoder, /* in: decoder to read from */
loc, /* in: location in COSE message*/
false, /* in: no_protected headers */
decode_ephemeral_key, /* in: callback for specials */
NULL, /* in: context for callback */
p_storage, /* in: parameter storage */
Expand Down Expand Up @@ -181,7 +182,7 @@ t_cose_recipient_dec_esdh_cb_private(struct t_cose_recipient_dec *me_x,
goto done_free_ec;
}

alg = t_cose_param_find_alg_id(*params, true);
alg = t_cose_param_find_alg_id_prot(*params);

// TODO: indention of all case statements. Which is it?
switch(alg) {
Expand Down
3 changes: 2 additions & 1 deletion src/t_cose_recipient_dec_keywrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ t_cose_recipient_dec_keywrap_cb_private(struct t_cose_recipient_dec *me_x,
/* ---- First and second items -- protected & unprotected headers ---- */
err = t_cose_headers_decode(cbor_decoder, /* in: decoder to read from */
loc, /* in: location in COSE message */
false, /* in: no_protected headers */
NULL, /* in: callback for specials */
NULL, /* in: context for callback */
p_storage, /* in: parameter storage */
Expand Down Expand Up @@ -80,7 +81,7 @@ t_cose_recipient_dec_keywrap_cb_private(struct t_cose_recipient_dec *me_x,
T_COSE_ERR_RECIPIENT_FORMAT);
}

cose_algorithm_id = t_cose_param_find_alg_id(*params, false);
cose_algorithm_id = t_cose_param_find_alg_id_unprot(*params);

// TODO: should probably check the kid here

Expand Down
2 changes: 2 additions & 0 deletions src/t_cose_sign_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ decode_cose_signature(QCBORDecodeContext *cbor_decoder,

return_value = t_cose_headers_decode(cbor_decoder,
loc,
false,
special_decode_cb,
special_decode_ctx,
param_storage,
Expand Down Expand Up @@ -482,6 +483,7 @@ t_cose_sign_verify_private(struct t_cose_sign_verify_ctx *me,
decoded_params = NULL;
return_value = t_cose_headers_decode(&cbor_decoder,
header_location,
false,
me->special_param_decode_cb,
me->special_param_decode_ctx,
me->p_storage,
Expand Down
2 changes: 1 addition & 1 deletion src/t_cose_signature_verify_eddsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ t_cose_signature_verify_eddsa_cb(struct t_cose_signature_verify *me_x,
struct q_useful_buf_c tbs;

/* --- Check the algorithm --- */
cose_algorithm_id = t_cose_param_find_alg_id(parameter_list, true);
cose_algorithm_id = t_cose_param_find_alg_id_prot(parameter_list);
if(cose_algorithm_id == T_COSE_ALGORITHM_NONE) {
return_value = T_COSE_ERR_NO_ALG_ID;
goto Done;
Expand Down
2 changes: 1 addition & 1 deletion src/t_cose_signature_verify_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ t_cose_signature_verify_main_cb(struct t_cose_signature_verify *me_x,
struct q_useful_buf_c tbs_hash;

/* --- Get the parameters values needed --- */
cose_algorithm_id = t_cose_param_find_alg_id(parameter_list, true);
cose_algorithm_id = t_cose_param_find_alg_id_prot(parameter_list);
if(cose_algorithm_id == T_COSE_ALGORITHM_NONE) {
return_value = T_COSE_ERR_NO_ALG_ID;
goto Done;
Expand Down
Loading

0 comments on commit fa047af

Please sign in to comment.