From a9492570cab29e48287711f66cc54ef779229795 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 8 Sep 2026 15:57:50 -0700 Subject: [PATCH 01/11] F-12135 - Allow metadata-only OKP key decoding --- src/wolfcose_key.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index 00c0eda..f9d71f9 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -2201,6 +2201,42 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) } #endif +#if defined(WOLFCOSE_HAVE_EDDSA) || defined(WOLFCOSE_HAVE_ED448) + /* Validate OKP key material before the attachment-dependent import. + * Metadata-only decoding must not turn malformed input into a + * successfully validated COSE_Key. */ + if ((ret == WOLFCOSE_SUCCESS) && + (key->kty == WOLFCOSE_KTY_OKP)) { + size_t okpPubSz = 0u; + size_t okpPrivSz = 0u; + + if ((xData == NULL) && (dData == NULL)) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } +#ifdef WOLFCOSE_HAVE_EDDSA + else if (key->crv == WOLFCOSE_CRV_ED25519) { + okpPubSz = (size_t)ED25519_PUB_KEY_SIZE; + okpPrivSz = (size_t)ED25519_KEY_SIZE; + } +#endif +#ifdef WOLFCOSE_HAVE_ED448 + else if (key->crv == WOLFCOSE_CRV_ED448) { + okpPubSz = (size_t)ED448_PUB_KEY_SIZE; + okpPrivSz = (size_t)ED448_KEY_SIZE; + } +#endif + else { + ret = WOLFCOSE_E_COSE_BAD_ALG; + } + + if ((ret == WOLFCOSE_SUCCESS) && + (((xData != NULL) && (xLen != okpPubSz)) || + ((dData != NULL) && (dLen != okpPrivSz)))) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } + } +#endif + if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_KeyAttachedTypeCheck(key); } @@ -2525,7 +2561,8 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) else #endif /* WOLFCOSE_HAVE_LMS */ #if defined(WOLFCOSE_HAVE_EDDSA) || defined(WOLFCOSE_HAVE_ED448) - if (key->kty == WOLFCOSE_KTY_OKP) { + if ((key->kty == WOLFCOSE_KTY_OKP) && + (key->attachedType != WOLFCOSE_ATT_NONE)) { /* RFC 9052: x is recommended, not required, for a private OKP * key, so accept {kty, crv, d} and recompute the public key. */ if ((xData == NULL) && (dData == NULL)) { From 4e7c154cd742e69ed991e35fb72d0d1ac78814e5 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 09:13:09 -0700 Subject: [PATCH 02/11] F-11109 - Remove duplicate AKP validation --- src/wolfcose_key.c | 60 +++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index f9d71f9..709e15d 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -2476,11 +2476,6 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) /* RFC 9964 AKP keys carry no crv. */ ret = WOLFCOSE_E_COSE_BAD_HDR; } - else if (akpPub == NULL) { - /* RFC 9964: pub is REQUIRED for AKP keys, public or - * private. Reject a seed-only key with no public part. */ - ret = WOLFCOSE_E_COSE_BAD_HDR; - } else { ret = wc_MlDsaKey_SetParams(key->key.mldsa, dlLevel); if (ret != 0) { @@ -2499,39 +2494,34 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) } #ifndef WOLFSSL_MLDSA_NO_MAKE_KEY if ((ret == WOLFCOSE_SUCCESS) && (akpSeed != NULL)) { - if (akpSeedLen != WOLFCOSE_MLDSA_SEED_SZ) { + INJECT_FAILURE(WOLF_FAIL_MLDSA_IMPORT_PRIV, -1, + ret = wc_MlDsaKey_MakeKeyFromSeed( + key->key.mldsa, akpSeed)); + if ((ret == 0) && + (XMEMCMP(key->key.mldsa->p, akpPub, + akpPubLen) != 0)) { + wolfCose_MlDsaImportRollback( + key->key.mldsa, dlLevel); + key->hasPrivate = 0u; + key->mldsaSeed = NULL; + key->mldsaSeedLen = 0u; ret = WOLFCOSE_E_COSE_BAD_HDR; } + else if (ret == 0) { + key->hasPrivate = 1; + /* Retain the seed (zero-copy into the input, + * like kid) so a decode->encode round-trip can + * re-emit the private key. */ + key->mldsaSeed = akpSeed; + key->mldsaSeedLen = akpSeedLen; + } else { - INJECT_FAILURE(WOLF_FAIL_MLDSA_IMPORT_PRIV, -1, - ret = wc_MlDsaKey_MakeKeyFromSeed( - key->key.mldsa, akpSeed)); - if ((ret == 0) && - (XMEMCMP(key->key.mldsa->p, akpPub, - akpPubLen) != 0)) { - wolfCose_MlDsaImportRollback( - key->key.mldsa, dlLevel); - key->hasPrivate = 0u; - key->mldsaSeed = NULL; - key->mldsaSeedLen = 0u; - ret = WOLFCOSE_E_COSE_BAD_HDR; - } - else if (ret == 0) { - key->hasPrivate = 1; - /* Retain the seed (zero-copy into the input, - * like kid) so a decode->encode round-trip can - * re-emit the private key. */ - key->mldsaSeed = akpSeed; - key->mldsaSeedLen = akpSeedLen; - } - else { - wolfCose_MlDsaImportRollback( - key->key.mldsa, dlLevel); - key->hasPrivate = 0u; - key->mldsaSeed = NULL; - key->mldsaSeedLen = 0u; - ret = WOLFCOSE_E_CRYPTO; - } + wolfCose_MlDsaImportRollback( + key->key.mldsa, dlLevel); + key->hasPrivate = 0u; + key->mldsaSeed = NULL; + key->mldsaSeedLen = 0u; + ret = WOLFCOSE_E_CRYPTO; } } #endif From 7733fcf899f8d98c92198f152ab5a42c8f966617 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 09:16:44 -0700 Subject: [PATCH 03/11] F-11111 - Reject empty symmetric key encoding --- src/wolfcose_key.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index 709e15d..dbe304b 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -1223,6 +1223,11 @@ int wc_CoseKey_Encode_ex(WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, if ((flags & WOLFCOSE_KEY_PUBLIC_ONLY) != 0u) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } + if ((ret == WOLFCOSE_SUCCESS) && + ((key->key.symm.key == NULL) || + (key->key.symm.keyLen == 0u))) { + ret = WOLFCOSE_E_COSE_KEY_TYPE; + } #if defined(WOLFCOSE_EXT_SIGN) if ((ret == WOLFCOSE_SUCCESS) && (key->signCb != NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; @@ -1739,6 +1744,11 @@ int wc_CoseKey_EncodeSize_ex(const WOLFCOSE_KEY* key, size_t* outLen, if ((flags & WOLFCOSE_KEY_PUBLIC_ONLY) != 0u) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } + if ((ret == WOLFCOSE_SUCCESS) && + ((key->key.symm.key == NULL) || + (key->key.symm.keyLen == 0u))) { + ret = WOLFCOSE_E_COSE_KEY_TYPE; + } #if defined(WOLFCOSE_EXT_SIGN) if ((ret == WOLFCOSE_SUCCESS) && (key->signCb != NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; From 886e87db9474a7eee541dc5382a8f00654ead14a Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 09:27:19 -0700 Subject: [PATCH 04/11] F-11119 - Make extended key encoding const --- docs/API-Reference.md | 2 +- include/wolfcose/wolfcose.h | 2 +- src/wolfcose_key.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/API-Reference.md b/docs/API-Reference.md index bbddb15..e766062 100644 --- a/docs/API-Reference.md +++ b/docs/API-Reference.md @@ -364,7 +364,7 @@ Encode a COSE key to CBOR format. ### wc_CoseKey_Encode_ex ```c -int wc_CoseKey_Encode_ex(WOLFCOSE_KEY* key, uint8_t* buf, size_t bufSz, +int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* buf, size_t bufSz, size_t* outLen, uint32_t flags); ``` diff --git a/include/wolfcose/wolfcose.h b/include/wolfcose/wolfcose.h index 53a30c6..221e535 100644 --- a/include/wolfcose/wolfcose.h +++ b/include/wolfcose/wolfcose.h @@ -935,7 +935,7 @@ WOLFCOSE_API int wc_CoseKey_Encode(WOLFCOSE_KEY* key, uint8_t* out, * \param flags Bitmask of WOLFCOSE_KEY_* output options. * \return WOLFCOSE_SUCCESS or negative error code. */ -WOLFCOSE_API int wc_CoseKey_Encode_ex(WOLFCOSE_KEY* key, uint8_t* out, +WOLFCOSE_API int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, size_t* outLen, uint32_t flags); diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index dbe304b..7edeb33 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -641,8 +641,8 @@ int wc_CoseKey_Encode(WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, return wc_CoseKey_Encode_ex(key, out, outSz, outLen, 0u); } -int wc_CoseKey_Encode_ex(WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, - size_t* outLen, uint32_t flags) +int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, + size_t* outLen, uint32_t flags) { int ret = WOLFCOSE_SUCCESS; WOLFCOSE_CBOR_CTX ctx; From 647a9ce59c5ec37ef4221d240d41aba798c41fcf Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 09:31:08 -0700 Subject: [PATCH 05/11] F-12136 - Restrict metadata EC2 curves --- src/wolfcose_key.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index 7edeb33..200fcfd 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -2201,7 +2201,15 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) * equal the curve size, even when no key is attached for import. */ if ((ret == WOLFCOSE_SUCCESS) && (key->kty == WOLFCOSE_KTY_EC2)) { size_t coordSz = 0; - ret = wolfCose_CrvKeySize(key->crv, &coordSz); + + if ((key->crv != WOLFCOSE_CRV_P256) && + (key->crv != WOLFCOSE_CRV_P384) && + (key->crv != WOLFCOSE_CRV_P521)) { + ret = WOLFCOSE_E_COSE_BAD_ALG; + } + else { + ret = wolfCose_CrvKeySize(key->crv, &coordSz); + } if ((ret == WOLFCOSE_SUCCESS) && (((xData != NULL) && (xLen != coordSz)) || ((yData != NULL) && (yLen != coordSz)) || From c4cb38e9e2f0598d3f840767f9d4470e64141a03 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 09:31:47 -0700 Subject: [PATCH 06/11] F-12137 - Clarify key peek guarantees --- src/wolfcose_key.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index 200fcfd..61fb122 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -1945,8 +1945,8 @@ int wc_CoseKey_PeekInfo(const uint8_t* in, size_t inSz, if ((ret == WOLFCOSE_SUCCESS) && (info->kty == 0)) { ret = WOLFCOSE_E_COSE_BAD_HDR; } - /* RFC 8949 Section 5.3.1: reject trailing data, as wc_CoseKey_Decode - * does, so a successful peek predicts a successful decode. */ + /* RFC 8949 Section 5.3.1: reject trailing data so peeking and decoding + * agree on framing. Decoding still performs per-key validation. */ if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { ret = WOLFCOSE_E_CBOR_MALFORMED; } From f53ac91e48f35de48e556e960674f11a0d056597 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 09:41:12 -0700 Subject: [PATCH 07/11] F-12939 - Document metadata-only key decoding --- docs/API-Reference.md | 20 ++++++++++---------- include/wolfcose/wolfcose.h | 23 ++++++++++++----------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/API-Reference.md b/docs/API-Reference.md index e766062..926e113 100644 --- a/docs/API-Reference.md +++ b/docs/API-Reference.md @@ -473,18 +473,20 @@ Decode a COSE key from CBOR format. **Parameters:** | Name | Description | |------|-------------| -| `key` | Pointer to COSE key structure (with pre-allocated wolfCrypt key) | +| `key` | Initialized COSE key structure; may have a wolfCrypt key attached | | `buf` | Input CBOR buffer | | `bufSz` | Size of input buffer | **Returns:** `WOLFCOSE_SUCCESS` or error code -Attach the wolfCrypt key with `wc_CoseKey_SetEcc()`, `wc_CoseKey_SetEd25519()`, -`wc_CoseKey_SetEd448()`, `wc_CoseKey_SetRsa()`, `wc_CoseKey_SetMlDsa()`, or -`wc_CoseKey_SetSymmetric()`. These record which wolfCrypt object is attached; -assigning the `key.*` union directly does not, and no key material is imported. +Attach a wolfCrypt key with `wc_CoseKey_SetEcc()`, `wc_CoseKey_SetEd25519()`, +`wc_CoseKey_SetEd448()`, `wc_CoseKey_SetRsa()`, or `wc_CoseKey_SetMlDsa()` when +asymmetric key material should be imported. Without an attachment, supported +asymmetric metadata is still validated and returned, but key material is not +imported. Assigning the `key.*` union directly records no attached type and +imports nothing. -The decoded `kty`/`crv` must name the attached key type or +When a key is attached, the decoded `kty`/`crv` must name that key type or `WOLFCOSE_E_COSE_KEY_TYPE` is returned before any importer runs. To learn which key type a buffer holds before attaching anything, use [`wc_CoseKey_PeekInfo()`](#wc_cosekey_peekinfo). @@ -525,10 +527,8 @@ int wc_CoseKey_PeekInfo(const uint8_t* in, size_t inSz, Read `kty`, `alg`, `crv`, and `kid` out of a `COSE_Key` buffer without importing any key material and without needing a wolfCrypt key object. -`wc_CoseKey_Decode()` requires the caller to have attached a key of the -matching type up front and returns `WOLFCOSE_E_COSE_KEY_TYPE` otherwise, so a -parser that accepts more than one key type would have to guess and retry. -Peek first, then attach once: +When asymmetric key material is to be imported, peek first to identify its +type, then attach the matching wolfCrypt object once: ```c WOLFCOSE_KEY_INFO info; diff --git a/include/wolfcose/wolfcose.h b/include/wolfcose/wolfcose.h index 221e535..84a325b 100644 --- a/include/wolfcose/wolfcose.h +++ b/include/wolfcose/wolfcose.h @@ -1031,10 +1031,9 @@ typedef struct WOLFCOSE_KEY_INFO { /** * \brief Read kty/alg/crv/kid from a COSE_Key buffer without importing it. * - * wc_CoseKey_Decode() needs a wolfCrypt key of the matching type attached up - * front and returns WOLFCOSE_E_COSE_KEY_TYPE otherwise, so a parser that - * accepts more than one key type would have to guess and retry. This reads - * the metadata first so the caller can attach the right key object once. + * wc_CoseKey_Decode() can validate metadata without an attached wolfCrypt + * key. When asymmetric key material is to be imported, this reads the + * metadata first so the caller can attach the right key object once. * * Nothing is imported, no key object is needed, and \p in is not modified. * The same structural checks wc_CoseKey_Decode() applies are applied here @@ -1055,11 +1054,13 @@ WOLFCOSE_API int wc_CoseKey_PeekInfo(const uint8_t* in, size_t inSz, /** * \brief Decode a CBOR COSE_Key map into a WOLFCOSE_KEY structure. * For symmetric keys, pointers reference the input buffer. - * For ECC/Ed25519, caller must attach a key struct via - * wc_CoseKey_SetEcc()/SetEd25519()/SetEd448()/SetRsa()/SetMlDsa(); - * assigning key.* directly records no type and imports nothing. - * A decoded kty/crv that does not match the attached type returns - * WOLFCOSE_E_COSE_KEY_TYPE before any import runs. + * Attach a matching asymmetric key object with wc_CoseKey_SetEcc(), + * SetEd25519(), SetEd448(), SetRsa(), or SetMlDsa() to import key + * material. Without one, supported metadata is still validated and + * returned, but asymmetric key material is not imported. Assigning + * key.* directly records no type and imports nothing. A decoded kty/crv + * that does not match an attached type returns WOLFCOSE_E_COSE_KEY_TYPE + * before any import runs. * Keys containing key_ops return WOLFCOSE_E_UNSUPPORTED before any * key material is imported. * An attached ECC object receiving private EC2 material must be @@ -1068,8 +1069,8 @@ WOLFCOSE_API int wc_CoseKey_PeekInfo(const uint8_t* in, size_t inSz, * or a non-transactional callback or hardware backend, private EC2 * decode returns WOLFCOSE_E_UNSUPPORTED before importing key * material. - * \param key Key structure (should be initialized, with wolfCrypt key - * attached for asymmetric types). + * \param key Initialized key structure. Attach a wolfCrypt key when + * asymmetric key material should be imported. * \param in Input CBOR buffer. * \param inSz Input buffer size. * \return WOLFCOSE_SUCCESS or negative error code. From 035ed1770bf7bde9b12287c04985957cc2ace911 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 09:17:21 -0700 Subject: [PATCH 08/11] F-11115 - Use sized constant compare types --- src/wolfcose_internal.h | 4 ++-- src/wolfcose_util.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wolfcose_internal.h b/src/wolfcose_internal.h index e5918cb..9661caa 100644 --- a/src/wolfcose_internal.h +++ b/src/wolfcose_internal.h @@ -657,8 +657,8 @@ WOLFCOSE_LOCAL void wolfCose_HdrClearOnFail(int ret, WOLFCOSE_HDR* hdr); #if defined(WOLFCOSE_MAC0_VERIFY) || defined(WOLFCOSE_MAC_VERIFY) /* ConstantCompare -- defined in wolfcose_util.c */ -WOLFCOSE_LOCAL int wolfCose_ConstantCompare(const byte* a, const byte* b, - word32 length); +WOLFCOSE_LOCAL int32_t wolfCose_ConstantCompare(const byte* a, const byte* b, + word32 length); #endif #ifdef __cplusplus diff --git a/src/wolfcose_util.c b/src/wolfcose_util.c index 1b9f5b4..d3e97fe 100644 --- a/src/wolfcose_util.c +++ b/src/wolfcose_util.c @@ -332,18 +332,18 @@ void wolfCose_HdrClearOnFail(int ret, WOLFCOSE_HDR* hdr) * Returns 0 if equal, non-zero otherwise. * Timing is independent of comparison result. */ -int wolfCose_ConstantCompare(const byte* a, const byte* b, - word32 length) +int32_t wolfCose_ConstantCompare(const byte* a, const byte* b, + word32 length) { word32 i; /* volatile prevents the compiler from converting the OR-accumulate * loop into an early-exit comparison once result is non-zero. */ - volatile unsigned int result = 0; + volatile uint32_t result = 0u; - for (i = 0; i < length; i++) { - result |= (unsigned int)a[i] ^ (unsigned int)b[i]; + for (i = 0u; i < length; i++) { + result |= (uint32_t)a[i] ^ (uint32_t)b[i]; } - return (int)result; + return (int32_t)result; } #endif /* WOLFCOSE_MAC0_VERIFY || WOLFCOSE_MAC_VERIFY */ From 7c1af40a9d92ea6d50294041e35e6e1d6fa5cf20 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 09:43:11 -0700 Subject: [PATCH 09/11] F-12952 - Compare MLDSA public keys in constant time --- src/wolfcose_internal.h | 5 ++++- src/wolfcose_key.c | 4 ++-- src/wolfcose_util.c | 8 +++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/wolfcose_internal.h b/src/wolfcose_internal.h index 9661caa..ae8f9f4 100644 --- a/src/wolfcose_internal.h +++ b/src/wolfcose_internal.h @@ -655,7 +655,10 @@ WOLFCOSE_LOCAL void wolfCose_EccPrivateImportRollback(ecc_key* ecc, WOLFCOSE_LOCAL void wolfCose_HdrClearOnFail(int ret, WOLFCOSE_HDR* hdr); #endif -#if defined(WOLFCOSE_MAC0_VERIFY) || defined(WOLFCOSE_MAC_VERIFY) +#if defined(WOLFCOSE_MAC0_VERIFY) || defined(WOLFCOSE_MAC_VERIFY) || \ + (defined(WOLFCOSE_KEY_DECODE) && defined(WOLFCOSE_HAVE_MLDSA) && \ + !defined(WOLFSSL_MLDSA_NO_MAKE_KEY)) +#define WOLFCOSE_NEED_CONSTANT_COMPARE /* ConstantCompare -- defined in wolfcose_util.c */ WOLFCOSE_LOCAL int32_t wolfCose_ConstantCompare(const byte* a, const byte* b, word32 length); diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index 61fb122..e8de56a 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -2516,8 +2516,8 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) ret = wc_MlDsaKey_MakeKeyFromSeed( key->key.mldsa, akpSeed)); if ((ret == 0) && - (XMEMCMP(key->key.mldsa->p, akpPub, - akpPubLen) != 0)) { + (wolfCose_ConstantCompare(key->key.mldsa->p, + akpPub, (word32)akpPubLen) != 0)) { wolfCose_MlDsaImportRollback( key->key.mldsa, dlLevel); key->hasPrivate = 0u; diff --git a/src/wolfcose_util.c b/src/wolfcose_util.c index d3e97fe..6e4c09a 100644 --- a/src/wolfcose_util.c +++ b/src/wolfcose_util.c @@ -323,10 +323,8 @@ void wolfCose_HdrClearOnFail(int ret, WOLFCOSE_HDR* hdr) /* ----- Constant-time comparison (side-channel safe) ----- */ -/* Only the MAC verify paths compare secret tags, so this helper is compiled - * only when one of them is enabled. This keeps sign-only and verify-only - * builds free of an unused-function warning. */ -#if defined(WOLFCOSE_MAC0_VERIFY) || defined(WOLFCOSE_MAC_VERIFY) +/* Compile this helper only for paths that use it. */ +#ifdef WOLFCOSE_NEED_CONSTANT_COMPARE /** * Constant-time memory comparison (matches wolfSSL ConstantCompare pattern). * Returns 0 if equal, non-zero otherwise. @@ -345,7 +343,7 @@ int32_t wolfCose_ConstantCompare(const byte* a, const byte* b, } return (int32_t)result; } -#endif /* WOLFCOSE_MAC0_VERIFY || WOLFCOSE_MAC_VERIFY */ +#endif /* WOLFCOSE_NEED_CONSTANT_COMPARE */ /* ----- RFC 9052 context strings ----- */ WOLFCOSE_LOCAL const uint8_t WOLFCOSE_CTX_SIGNATURE1[10] = { From 62c4543cc4da199d46a0b3dbadae26eb18260f0f Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 9 Sep 2026 15:33:59 -0700 Subject: [PATCH 10/11] Use unsigned private-key flag --- src/wolfcose_key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index e8de56a..ad3980a 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -2526,7 +2526,7 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) ret = WOLFCOSE_E_COSE_BAD_HDR; } else if (ret == 0) { - key->hasPrivate = 1; + key->hasPrivate = 1u; /* Retain the seed (zero-copy into the input, * like kid) so a decode->encode round-trip can * re-emit the private key. */ From 966e9a3fc9eaed1f246590f661a1de76de1b2c6a Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 10 Sep 2026 09:26:50 -0700 Subject: [PATCH 11/11] Fix MISRA const handling in key encoding --- src/wolfcose_key.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/wolfcose_key.c b/src/wolfcose_key.c index ad3980a..8736b57 100644 --- a/src/wolfcose_key.c +++ b/src/wolfcose_key.c @@ -666,8 +666,9 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, word32 dLen = (word32)sizeof(dBuf); size_t coordSz; int emitPriv = 0; + ecc_key* eccKey = key->key.ecc; - if (key->key.ecc == NULL) { + if (eccKey == NULL) { ret = WOLFCOSE_E_INVALID_ARG; } if (ret == WOLFCOSE_SUCCESS) { @@ -676,7 +677,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, if (ret == WOLFCOSE_SUCCESS) { INJECT_FAILURE(WOLF_FAIL_ECC_EXPORT_X963, -1, - ret = wc_ecc_export_public_raw(key->key.ecc, xBuf, &xLen, + ret = wc_ecc_export_public_raw(eccKey, xBuf, &xLen, yBuf, &yLen)); if (ret != 0) { ret = WOLFCOSE_E_CRYPTO; @@ -694,7 +695,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, } if ((ret == WOLFCOSE_SUCCESS) && (emitPriv != 0)) { INJECT_FAILURE(WOLF_FAIL_ECC_EXPORT_PRIVATE, -1, - ret = wc_ecc_export_private_only(key->key.ecc, dBuf, + ret = wc_ecc_export_private_only(eccKey, dBuf, &dLen)); if (ret != 0) { ret = WOLFCOSE_E_CRYPTO; @@ -733,11 +734,12 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, size_t hdrPos; size_t mapEntries; int rsaPriv = 0; + RsaKey* rsaKey = key->key.rsa; #ifdef WOLFCOSE_HAVE_RSA_PRIVATE_KEY word32 halfSz = 0; #endif - if (key->key.rsa == NULL) { + if (rsaKey == NULL) { ret = WOLFCOSE_E_INVALID_ARG; } /* Private round-trip needs the CRT export; else public-only. */ @@ -746,7 +748,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, rsaPriv = 1; #ifdef WOLF_CRYPTO_CB /* Device-backed keys have no local CRT to export. */ - if (key->key.rsa->devId != INVALID_DEVID) { + if (rsaKey->devId != INVALID_DEVID) { rsaPriv = 0; } #endif @@ -783,7 +785,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, else { ctx.idx += 3u; /* reserve bstr header */ nLen = (word32)(ctx.bufSz - ctx.idx); - ret = wc_RsaFlattenPublicKey(key->key.rsa, + ret = wc_RsaFlattenPublicKey(rsaKey, eBuf, &eLen, &ctx.buf[ctx.idx], &nLen); if (ret != 0) { ret = WOLFCOSE_E_CRYPTO; @@ -824,7 +826,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, int rsaEncSz = 0; INJECT_FAILURE(WOLF_FAIL_RSA_ENCRYPT_SIZE, rsaEncSz, - rsaEncSz = wc_RsaEncryptSize(key->key.rsa)); + rsaEncSz = wc_RsaEncryptSize(rsaKey)); if (rsaEncSz <= 0) { /* cppcheck-suppress redundantAssignment */ ret = WOLFCOSE_E_CRYPTO; @@ -850,7 +852,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, qSz = (word32)((word32)rsaEncSz / 2u); INJECT_FAILURE(WOLF_FAIL_RSA_EXPORT_KEY, -1, ret = wc_RsaExportKey( - key->key.rsa, + rsaKey, &ctx.buf[scrOff], &eSz2, &ctx.buf[scrOff + 8u], &nSz2, &ctx.buf[dOff], &dSz, @@ -890,7 +892,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, #ifdef WOLFCOSE_HAVE_RSA_PRIVATE_KEY /* -4 p, -5 q, -8 qInv: CRT factors so a decoded key can sign. */ if ((ret == WOLFCOSE_SUCCESS) && (rsaPriv != 0)) { - int modSz = wc_RsaEncryptSize(key->key.rsa); + int modSz = wc_RsaEncryptSize(rsaKey); if (modSz <= 0) { ret = WOLFCOSE_E_CRYPTO; } @@ -900,26 +902,26 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, } if ((ret == WOLFCOSE_SUCCESS) && (rsaPriv != 0)) { ret = wolfCose_EncodeRsaMp(&ctx, WOLFCOSE_KEY_LABEL_RSA_P, - &key->key.rsa->p, halfSz); + &rsaKey->p, halfSz); } if ((ret == WOLFCOSE_SUCCESS) && (rsaPriv != 0)) { ret = wolfCose_EncodeRsaMp(&ctx, WOLFCOSE_KEY_LABEL_RSA_Q, - &key->key.rsa->q, halfSz); + &rsaKey->q, halfSz); } /* RFC 8230: dP and dQ are MUST-present for a two-prime private key. * Emitting them also avoids wolfCrypt recomputing the CRT exponents * on decode, which is fragile for some key values. */ if ((ret == WOLFCOSE_SUCCESS) && (rsaPriv != 0)) { ret = wolfCose_EncodeRsaMp(&ctx, WOLFCOSE_KEY_LABEL_RSA_DP, - &key->key.rsa->dP, halfSz); + &rsaKey->dP, halfSz); } if ((ret == WOLFCOSE_SUCCESS) && (rsaPriv != 0)) { ret = wolfCose_EncodeRsaMp(&ctx, WOLFCOSE_KEY_LABEL_RSA_DQ, - &key->key.rsa->dQ, halfSz); + &rsaKey->dQ, halfSz); } if ((ret == WOLFCOSE_SUCCESS) && (rsaPriv != 0)) { ret = wolfCose_EncodeRsaMp(&ctx, WOLFCOSE_KEY_LABEL_RSA_QINV, - &key->key.rsa->u, halfSz); + &rsaKey->u, halfSz); } #endif /* WOLFCOSE_HAVE_RSA_PRIVATE_KEY */ @@ -937,6 +939,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, * large (1312-2592B) so it is exported directly into the output * buffer to avoid a large stack copy; the seed is small. */ int emitPriv = 0; + wc_MlDsaKey* mldsaKey = key->key.mldsa; /* Emit priv only when a valid 32-byte seed is attached; wolfCrypt * does not retain the seed, so a keypair without one (e.g. from @@ -986,7 +989,7 @@ int wc_CoseKey_Encode_ex(const WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, ctx.idx += 3u; dlKeyLen = (word32)(ctx.bufSz - ctx.idx); INJECT_FAILURE(WOLF_FAIL_MLDSA_EXPORT_PUB, -1, - ret = wc_MlDsaKey_ExportPubRaw(key->key.mldsa, + ret = wc_MlDsaKey_ExportPubRaw(mldsaKey, &ctx.buf[ctx.idx], &dlKeyLen)); if (ret != 0) { ret = WOLFCOSE_E_CRYPTO;