Skip to content

Commit

Permalink
COSE: Add t_cose_key_encode() and t_cose_key_decode() APIs
Browse files Browse the repository at this point in the history
Multiple partial implementations of the COSE_Key
encode/decode APIs exists across the tf.org repos.
To replace these with a full implementation copy the
relevant functions from t_cose_psa_crypto.c from the
upstream repo.

On top of this, backporting two open PRs:
 - Only t_cose_key_decode(): laurencelundblade/t_cose#287
 - laurencelundblade/t_cose#285

Signed-off-by: Tamas Ban <[email protected]>
Change-Id: I6c7a67a4f2a57b90363ffeac9b023296a123e966
  • Loading branch information
tamasban authored and adeaarm committed Oct 15, 2024
1 parent 21ff86a commit 10690fb
Show file tree
Hide file tree
Showing 7 changed files with 474 additions and 4 deletions.
187 changes: 186 additions & 1 deletion lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* t_cose_psa_crypto.c
*
* Copyright 2019, Laurence Lundblade
* Copyright 2019-2023, Laurence Lundblade
* Copyright (c) 2020-2021, Arm Limited. All rights reserved
*
* SPDX-License-Identifier: BSD-3-Clause
Expand Down Expand Up @@ -411,6 +411,191 @@ t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
Done:
return psa_status_to_t_cose_error_hash(hash_ctx->status);
}


/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_import_ec2_pubkey(int32_t cose_ec_curve_id,
struct q_useful_buf_c x_coord,
struct q_useful_buf_c y_coord,
bool y_bool,
struct t_cose_key *key_handle)
{
psa_status_t status;
psa_key_attributes_t attributes;
psa_key_type_t type_public;
psa_algorithm_t alg;
struct q_useful_buf_c import;
// TODO: really make sure this size is right for the curve types supported
UsefulOutBuf_MakeOnStack (import_form, T_COSE_EXPORT_PUBLIC_KEY_MAX_SIZE + 5);

switch (cose_ec_curve_id) {
case COSE_ELLIPTIC_CURVE_P_256:
type_public = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1);
alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256);
break;
case COSE_ELLIPTIC_CURVE_P_384:
type_public = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1);
alg = PSA_ALG_ECDSA(PSA_ALG_SHA_384);
break;
case COSE_ELLIPTIC_CURVE_P_521:
type_public = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1);
alg = PSA_ALG_ECDSA(PSA_ALG_SHA_512);
break;

default:
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}

// TODO: These attributes only suites to ECDSA(...) operations
attributes = psa_key_attributes_init();
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&attributes, alg);
psa_set_key_type(&attributes, type_public);

/* This converts to a serialized representation of an EC Point
* described in
* Certicom Research, "SEC 1: Elliptic Curve Cryptography", Standards for
* Efficient Cryptography, May 2009, <https://www.secg.org/sec1-v2.pdf>.
* The description is very mathematical and hard to read for us
* coder types. It was much easier to understand reading Jim's
* COSE-C implementation. See mbedtls_ecp_keypair() in COSE-C.
*
* This string is the format used by Mbed TLS to import an EC
* public key.
*
* This does implement point compression. The patents for it have
* run out so it's OK to implement. Point compression is commented
* out in Jim's implementation, presumably because of the paten
* issue.
*
* A simple English description of the format is this. The first
* byte is 0x04 for no point compression and 0x02 or 0x03 if there
* is point compression. 0x02 indicates a positive y and 0x03 a
* negative y (or is the other way). Following the first byte
* are the octets of x. If the first byte is 0x04 then following
* x is the y value.
*
* UsefulOutBut is used to safely construct this string.
*/
uint8_t first_byte;
if(q_useful_buf_c_is_null(y_coord)) {
/* This is point compression */
first_byte = y_bool ? 0x03 : 0x02;
} else {
first_byte = 0x04;
}

// TODO: is padding of x necessary? Jim's code goes to
// a lot of trouble to look up the group and get the length.

UsefulOutBuf_AppendByte(&import_form, first_byte);
UsefulOutBuf_AppendUsefulBuf(&import_form, x_coord);
if(first_byte == 0x04) {
UsefulOutBuf_AppendUsefulBuf(&import_form, y_coord);
}
import = UsefulOutBuf_OutUBuf(&import_form);


status = psa_import_key(&attributes,
import.ptr, import.len,
(psa_key_handle_t *)(&key_handle->k.key_handle));

if (status != PSA_SUCCESS) {
return T_COSE_ERR_FAIL;
}

return T_COSE_SUCCESS;
}


enum t_cose_err_t
t_cose_crypto_export_ec2_key(struct t_cose_key key_handle,
int32_t *curve,
struct q_useful_buf x_coord_buf,
struct q_useful_buf_c *x_coord,
struct q_useful_buf y_coord_buf,
struct q_useful_buf_c *y_coord,
bool *y_bool)
{
psa_status_t psa_status;
uint8_t export_buf[T_COSE_EXPORT_PUBLIC_KEY_MAX_SIZE];
size_t export_len;
struct q_useful_buf_c export;
size_t len;
uint8_t first_byte;
psa_key_attributes_t attributes;

/* Export public key */
psa_status = psa_export_public_key((psa_key_handle_t)key_handle.k.key_handle, /* in: Key handle */
export_buf, /* in: PK buffer */
sizeof(export_buf), /* in: PK buffer size */
&export_len); /* out: Result length */
if(psa_status != PSA_SUCCESS) {
return T_COSE_ERR_FAIL; // TODO: error code
}
first_byte = export_buf[0];
export = (struct q_useful_buf_c){export_buf+1, export_len-1};

/* export_buf is one first byte, the x-coord and maybe the y-coord
* per SEC1.
*/

attributes = psa_key_attributes_init();
psa_status = psa_get_key_attributes((psa_key_handle_t)key_handle.k.key_handle,
&attributes);
if(PSA_KEY_TYPE_ECC_GET_FAMILY(psa_get_key_type(&attributes)) != PSA_ECC_FAMILY_SECP_R1) {
return T_COSE_ERR_FAIL;
}

switch(psa_get_key_bits(&attributes)) {
case 256:
*curve = COSE_ELLIPTIC_CURVE_P_256;
break;
case 384:
*curve = COSE_ELLIPTIC_CURVE_P_384;
break;
case 521:
*curve = COSE_ELLIPTIC_CURVE_P_521;
break;
default:
return T_COSE_ERR_FAIL;
}

switch(first_byte) {
case 0x04:
len = (export_len - 1 ) / 2;
*y_coord = UsefulBuf_Copy(y_coord_buf, UsefulBuf_Tail(export, len));
break;

case 0x02:
len = export_len - 1;
*y_coord = NULL_Q_USEFUL_BUF_C;
*y_bool = true;
break;

case 0x03:
len = export_len - 1;
*y_coord = NULL_Q_USEFUL_BUF_C;
*y_bool = false;
break;

default:
return T_COSE_ERR_FAIL;
}

*x_coord = UsefulBuf_Copy(x_coord_buf, UsefulBuf_Head(export, len));

if ( q_useful_buf_c_is_null(*x_coord) ||
(q_useful_buf_c_is_null(*y_coord) && first_byte == 0x04)) {
return T_COSE_ERR_TOO_SMALL;
}

return T_COSE_SUCCESS;
}

#endif /* !T_COSE_DISABLE_SHORT_CIRCUIT_SIGN || !T_COSE_DISABLE_SIGN1 */

#ifndef T_COSE_DISABLE_MAC0
Expand Down
56 changes: 56 additions & 0 deletions lib/ext/t_cose/inc/t_cose_key.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* t_cose_key.h
*
* Copyright 2019-2023, Laurence Lundblade
*
* SPDX-License-Identifier: BSD-3-Clause
* Created by Laurence Lundblade on 2/6/23.
*
* See BSD-3-Clause license in README.md
*/

#ifndef __T_COSE_KEY_H__
#define __T_COSE_KEY_H__

#include "stdbool.h"
#include "stdint.h"
#include "q_useful_buf.h"
#include "t_cose_common.h"
#include "t_cose_standard_constants.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* \brief Decode a CBOR serialized COSE_Key object and turn it to a t_cose_key.
*
* \param[in] cbor_encoded A buffer which holds a COSE_Key object.
* \param[out] key A pointer to a t_cose_key structure.
*/
enum t_cose_err_t
t_cose_key_decode(struct q_useful_buf_c cbor_encoded,
struct t_cose_key *key);


/**
* \brief Encode a t_cose_key as a CBOR serialized COSE_Key object
*
* \param[in] key A t_cose_key to encode it as a COSE_Key.
* \param[in] key_buf A buffer to hold the COSE_Key.
* \param[out] cbor_encoded Place to return pointer and length of
* COSE_Key.
*
* The t_cose_key must be imported to the crypto library before this call.
*/
enum t_cose_err_t
t_cose_key_encode(struct t_cose_key key,
struct q_useful_buf key_buf,
struct q_useful_buf_c *cbor_encoded);


#ifdef __cplusplus
}
#endif

#endif /* __T_COSE_KEY_H__ */
66 changes: 65 additions & 1 deletion lib/ext/t_cose/src/t_cose_crypto.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* t_cose_crypto.h
*
* Copyright 2019, Laurence Lundblade
* Copyright 2019-2023, Laurence Lundblade
* Copyright (c) 2020-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
Expand Down Expand Up @@ -98,7 +98,19 @@ extern "C" {
* works, whether dead stripping of object code is on and such.
*/

/** Helper macro to convert bits to bytes */
#define T_COSE_BITS_TO_BYTES(bits) (((bits) + 7) / 8)

/** Constant for maximum ECC curve size in bits */
#define T_COSE_ECC_MAX_CURVE_BITS 521

/** Export of EC key in SEC1 uncompressed format */
#define T_COSE_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) \
(2 * T_COSE_BITS_TO_BYTES(key_bits) + 1)

/** Wrapper for T_COSE_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE() */
#define T_COSE_EXPORT_PUBLIC_KEY_MAX_SIZE \
(T_COSE_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(T_COSE_ECC_MAX_CURVE_BITS))


#define T_COSE_EC_P256_SIG_SIZE 64 /* size for secp256r1 */
Expand Down Expand Up @@ -660,6 +672,58 @@ t_cose_crypto_hmac_verify_finish(struct t_cose_crypto_hmac *hmac_ctx,
static bool t_cose_algorithm_is_ecdsa(int32_t cose_algorithm_id);


/* Import a COSE_Key in EC2 format into a key handle.
*
* \param[in] curve EC curve from COSE curve registry.
* \param[in] x_coord The X coordinate as a byte string.
* \param[in] y_coord The Y coordinate or NULL.
* \param[in] y_bool The Y sign bit when y_coord is NULL.
* \param[out] key_handle The key handle.
*
* This doesn't do the actual CBOR decoding, just the import
* into a key handle for the crypto library.
*
* For most crypto libraries, this must be freed by
* t_cose_crypto_free_ec_key();
*
* The coordinates are as specified in SECG 1.
*
* TODO: also support the private key.
*/
enum t_cose_err_t
t_cose_crypto_import_ec2_pubkey(int32_t cose_ec_curve_id,
struct q_useful_buf_c x_coord,
struct q_useful_buf_c y_coord,
bool y_bool,
struct t_cose_key *key_handle);


/* Export a key handle into COSE_Key in EC2 format.
*
* \param[in] key_handle The key handle.
* \param[out] curve EC curve from COSE curve registry.
* \param[out] x_coord_buf Buffer in which to put X coordinate.
* \param[out] x_coord The X coordinate as a byte string.
* \param[out] y_coord_buf Buffer in which to put Y coordinate.
* \param[out] y_coord The Y coordinate or NULL.
* \param[out] y_bool The Y sign bit when y_coord is NULL.
*
* This doesn't do the actual CBOR decoding, just the export
* from a key handle for the crypto library.
*
* The coordinates are as specified in SECG 1.
*
* TODO: also support the private key.
* TODO: a way to turn point compression on / off?
*/
enum t_cose_err_t
t_cose_crypto_export_ec2_key(struct t_cose_key key_handle,
int32_t *cose_ec_curve_id,
struct q_useful_buf x_coord_buf,
struct q_useful_buf_c *x_coord,
struct q_useful_buf y_coord_buf,
struct q_useful_buf_c *y_coord,
bool *y_bool);


/*
Expand Down
Loading

0 comments on commit 10690fb

Please sign in to comment.