From b547096f55aad5aed975e587602c29feb812d4bf Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 15:39:33 -0700 Subject: [PATCH 01/38] F-5213/F-5251 - Reject trailing data after top-level COSE objects --- src/wolfcose.c | 42 +++++++++++++++++++++++++++++ tests/test_cose.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index 98fc3f8..018133c 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -3567,6 +3567,11 @@ int wc_CoseSign1_Verify(WOLFCOSE_KEY* key, ret = wc_CBOR_DecodeBstr(&ctx, &sigData, &sigDataLen); } + /* RFC 8949 Section 5.3.1: reject trailing data after the COSE object. */ + if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + if (ret == WOLFCOSE_SUCCESS) { alg = hdr->alg; } @@ -4466,6 +4471,15 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, ret = wc_CBOR_DecodeBstr(&ctx, &signature, &signatureLen); } + /* Skip remaining signers, then reject trailing data (RFC 8949 5.3.1). */ + for (i = signerIndex + 1u; (i < signatureCount) && (ret == WOLFCOSE_SUCCESS); + i++) { + ret = wc_CBOR_Skip(&ctx); + } + if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + /* Build Sig_structure for verification */ if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_BuildToBeSignedMaced( @@ -5182,6 +5196,11 @@ int wc_CoseEncrypt0_Decrypt(WOLFCOSE_KEY* key, } } + /* RFC 8949 Section 5.3.1: reject trailing data after the COSE object. */ + if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + if (ret == WOLFCOSE_SUCCESS) { alg = hdr->alg; } @@ -5945,6 +5964,11 @@ int wc_CoseMac0_Verify(const WOLFCOSE_KEY* key, ret = wc_CBOR_DecodeBstr(&ctx, &macTag, &macTagLen); } + /* RFC 8949 Section 5.3.1: reject trailing data after the COSE object. */ + if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + if (ret == WOLFCOSE_SUCCESS) { alg = hdr->alg; /* RFC 9052 ยง7: key->alg, when set, must match message alg. */ @@ -6898,6 +6922,15 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, /* No action required */ } + /* Skip remaining recipients, then reject trailing data (RFC 8949 5.3.1). */ + for (i = recipientIndex + 1u; + (ret == WOLFCOSE_SUCCESS) && (i < recipientsCount); i++) { + ret = wc_CBOR_Skip(&ctx); + } + if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + /* Get key/tag lengths */ if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_AeadKeyLen(alg, &keyLen); @@ -7561,6 +7594,15 @@ int wc_CoseMac_Verify(const WOLFCOSE_RECIPIENT* recipient, ret = wc_CBOR_Skip(&ctx); } + /* Skip remaining recipients, then reject trailing data (RFC 8949 5.3.1). */ + for (i = recipientIndex + 1u; + (ret == WOLFCOSE_SUCCESS) && (i < recipientsCount); i++) { + ret = wc_CBOR_Skip(&ctx); + } + if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + /* Validate key and enforce key->alg agreement with the message. */ if ((ret == WOLFCOSE_SUCCESS) && ((recipient->key == NULL) || diff --git a/tests/test_cose.c b/tests/test_cose.c index cf6d611..65d540f 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -6091,6 +6091,72 @@ static void test_cose_sign1_tampered_sig_byte(void) } } +static void test_cose_sign1_trailing_bytes(void) +{ + WOLFCOSE_KEY key; + ecc_key eccKey; + WC_RNG rng; + int ret = 0; + int rngInited = 0; + int eccInited = 0; + uint8_t payload[] = "Trailing data test payload"; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + uint8_t out[512]; + size_t outLen = 0; + const uint8_t* decPayload = NULL; + size_t decPayloadLen = 0; + WOLFCOSE_HDR hdr; + + TEST_LOG(" [Sign1 Trailing Bytes]\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { TEST_ASSERT(0, "rng init"); } + if (ret == 0) { + rngInited = 1; + } + + if (ret == 0) { + wc_ecc_init(&eccKey); + eccInited = 1; + ret = wc_ecc_make_key(&rng, 32, &eccKey); + if (ret != 0) { TEST_ASSERT(0, "keygen"); } + } + + if (ret == 0) { + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetEcc(&key, WOLFCOSE_CRV_P256, &eccKey); + + ret = wc_CoseSign1_Sign(&key, WOLFCOSE_ALG_ES256, + NULL, 0, payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + out, sizeof(out), &outLen, &rng); + TEST_ASSERT(ret == 0, "sign for trailing test"); + } + + if (ret == 0) { + int verifyRet; + /* Append one garbage byte after a valid COSE_Sign1 object. */ + TEST_ASSERT(outLen < sizeof(out), "room for trailing byte"); + out[outLen] = 0xFFu; + + verifyRet = wc_CoseSign1_Verify(&key, out, outLen + 1u, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, &decPayload, &decPayloadLen); + TEST_ASSERT(verifyRet == WOLFCOSE_E_CBOR_MALFORMED, + "trailing bytes rejected"); + } + + /* Cleanup */ + if (eccInited != 0) { + (void)wc_ecc_free(&eccKey); + } + if (rngInited != 0) { + (void)wc_FreeRng(&rng); + } +} + static void test_cose_sign1_tampered_payload_byte(void) { WOLFCOSE_KEY key; @@ -14691,6 +14757,7 @@ int test_cose(void) test_cose_sign1_tampered_protected_hdr(); test_cose_sign1_tampered_payload_byte(); test_cose_sign1_truncated_sig(); + test_cose_sign1_trailing_bytes(); #endif #ifdef HAVE_AESGCM test_cose_encrypt0_tampered_ct_byte(); From 6ca0a88115eb158d6aa6d0a11ec946ed7343803e Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 15:47:02 -0700 Subject: [PATCH 02/38] F-5214/F-5282 - Reject oversized header and key integers before int32 narrowing --- src/wolfcose.c | 32 ++++++++++++++++++++++++++++++++ tests/test_cose.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index 018133c..819a42d 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -572,6 +572,14 @@ int wolfCose_EccVerifyRaw(const uint8_t* sigBuf, size_t sigLen, /* ----- Internal: Protected/Unprotected header encode/decode ----- */ +/* COSE algorithm, key type, and curve identifiers are stored in int32_t + * fields. Reject decoded CBOR integers that do not fit before narrowing so a + * non-representable value cannot alias a valid identifier. */ +static int wolfCose_InInt32Range(int64_t val) +{ + return ((val >= (int64_t)INT32_MIN) && (val <= (int64_t)INT32_MAX)) ? 1 : 0; +} + /* Map a COSE header/key label to a fast-path tracking bit. Labels outside * the small known range fall back to the slower extra-label array. */ static uint32_t wolfCose_LabelBit(int64_t label) @@ -791,6 +799,10 @@ int wolfCose_DecodeProtectedHdr(const uint8_t* data, size_t dataLen, } else { ret = wc_CBOR_DecodeInt(&ctx, &intVal); + if ((ret == WOLFCOSE_SUCCESS) && + (wolfCose_InInt32Range(intVal) == 0)) { + ret = WOLFCOSE_E_COSE_BAD_ALG; + } if (ret == WOLFCOSE_SUCCESS) { hdr->alg = (int32_t)intVal; } @@ -841,6 +853,10 @@ int wolfCose_DecodeProtectedHdr(const uint8_t* data, size_t dataLen, } else { ret = wc_CBOR_DecodeInt(&ctx, &intVal); + if ((ret == WOLFCOSE_SUCCESS) && + (wolfCose_InInt32Range(intVal) == 0)) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } if (ret == WOLFCOSE_SUCCESS) { hdr->contentType = (int32_t)intVal; } @@ -971,6 +987,10 @@ int wolfCose_DecodeUnprotectedHdr(WOLFCOSE_CBOR_CTX* ctx, WOLFCOSE_HDR* hdr, else { int64_t algVal; ret = wc_CBOR_DecodeInt(ctx, &algVal); + if ((ret == WOLFCOSE_SUCCESS) && + (wolfCose_InInt32Range(algVal) == 0)) { + ret = WOLFCOSE_E_COSE_BAD_ALG; + } if (ret == WOLFCOSE_SUCCESS) { hdr->alg = (int32_t)algVal; } @@ -1793,6 +1813,10 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) if ((ret == WOLFCOSE_SUCCESS) && (label == WOLFCOSE_KEY_LABEL_KTY)) { ret = wc_CBOR_DecodeUint(&ctx, &uval); + if ((ret == WOLFCOSE_SUCCESS) && + (uval > (uint64_t)INT32_MAX)) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } if (ret == WOLFCOSE_SUCCESS) { key->kty = (int32_t)uval; } @@ -1814,6 +1838,10 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) else { int64_t algVal; ret = wc_CBOR_DecodeInt(&ctx, &algVal); + if ((ret == WOLFCOSE_SUCCESS) && + (wolfCose_InInt32Range(algVal) == 0)) { + ret = WOLFCOSE_E_COSE_BAD_ALG; + } if (ret == WOLFCOSE_SUCCESS) { key->alg = (int32_t)algVal; } @@ -1840,6 +1868,10 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) /* uint or negint: EC2/OKP crv */ int64_t crvVal; ret = wc_CBOR_DecodeInt(&ctx, &crvVal); + if ((ret == WOLFCOSE_SUCCESS) && + (wolfCose_InInt32Range(crvVal) == 0)) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } if (ret == WOLFCOSE_SUCCESS) { key->crv = (int32_t)crvVal; } diff --git a/tests/test_cose.c b/tests/test_cose.c index 65d540f..f2594b5 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -8856,6 +8856,38 @@ static void test_cose_protected_hdr_trailing(void) "DecodeProtectedHdr rejects trailing bytes"); } +static void test_cose_oversized_int_narrowing(void) +{ + int ret; + WOLFCOSE_HDR hdr; + WOLFCOSE_HDR_STATE hdrState; + WOLFCOSE_KEY key; + /* {1: 0x100000001} -- alg that narrows to A128GCM(1) on 32-bit cast. */ + uint8_t bigAlg[] = { + 0xA1u, 0x01u, + 0x1Bu, 0x00u, 0x00u, 0x00u, 0x01u, 0x00u, 0x00u, 0x00u, 0x01u + }; + /* {1: 0x100000004, -1: h'<16>'} -- kty that narrows to Symmetric(4). */ + uint8_t bigKty[] = { + 0xA2u, + 0x01u, 0x1Bu, 0x00u, 0x00u, 0x00u, 0x01u, 0x00u, 0x00u, 0x00u, 0x04u, + 0x20u, 0x50u, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + }; + + TEST_LOG(" [Oversized integer narrowing]\n"); + + XMEMSET(&hdr, 0, sizeof(hdr)); + ret = wolfCose_DecodeProtectedHdr(bigAlg, sizeof(bigAlg), &hdr, &hdrState); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, + "DecodeProtectedHdr rejects oversized alg"); + + (void)wc_CoseKey_Init(&key); + ret = wc_CoseKey_Decode(&key, bigKty, sizeof(bigKty)); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_HDR, + "CoseKey_Decode rejects oversized kty"); +} + static void test_cose_protected_hdr_content_type(void) { int ret; @@ -14819,6 +14851,7 @@ int test_cose(void) test_cbor_edge_cases(); test_cose_protected_hdr_empty_map(); test_cose_protected_hdr_trailing(); + test_cose_oversized_int_narrowing(); test_cose_protected_hdr_content_type(); test_cose_protected_hdr_tstr_label(); test_cose_protected_hdr_dup_label(); From 6c3245f7fca7abc65290e71c4e73c1c1de3a9a64 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 15:51:46 -0700 Subject: [PATCH 03/38] F-5216/F-5283 - Harden CBOR encoder bounds and reject oversized container counts --- src/wolfcose_cbor.c | 31 +++++++++++++++++++++++-------- tests/test_cbor.c | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/wolfcose_cbor.c b/src/wolfcose_cbor.c index c78eafd..af335c8 100644 --- a/src/wolfcose_cbor.c +++ b/src/wolfcose_cbor.c @@ -31,6 +31,15 @@ #include "wolfcose_internal.h" #include /* memcpy */ +/* WOLFCOSE_CBOR_CTX is public, so a caller can pass a context whose idx has + * been advanced past bufSz. Subtraction-based capacity check that cannot wrap + * in that case. Returns 1 when at least need bytes remain, 0 otherwise. */ +static int wolfCose_CBOR_HasRoom(const WOLFCOSE_CBOR_CTX* ctx, size_t need) +{ + return ((ctx->idx <= ctx->bufSz) && + (need <= (ctx->bufSz - ctx->idx))) ? 1 : 0; +} + /* ----- * Internal: CBOR head encoder * @@ -56,7 +65,7 @@ int wolfCose_CBOR_EncodeHead(WOLFCOSE_CBOR_CTX* ctx, uint8_t majorType, if (val <= 23u) { need = 1; - if ((ctx->idx + need) > ctx->bufSz) { + if (wolfCose_CBOR_HasRoom(ctx, need) == 0) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; } else { @@ -67,7 +76,7 @@ int wolfCose_CBOR_EncodeHead(WOLFCOSE_CBOR_CTX* ctx, uint8_t majorType, } else if (val <= 0xFFu) { need = 2; - if ((ctx->idx + need) > ctx->bufSz) { + if (wolfCose_CBOR_HasRoom(ctx, need) == 0) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; } else { @@ -79,7 +88,7 @@ int wolfCose_CBOR_EncodeHead(WOLFCOSE_CBOR_CTX* ctx, uint8_t majorType, } else if (val <= 0xFFFFu) { need = 3; - if ((ctx->idx + need) > ctx->bufSz) { + if (wolfCose_CBOR_HasRoom(ctx, need) == 0) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; } else { @@ -91,7 +100,7 @@ int wolfCose_CBOR_EncodeHead(WOLFCOSE_CBOR_CTX* ctx, uint8_t majorType, } else if (val <= 0xFFFFFFFFu) { need = 5; - if ((ctx->idx + need) > ctx->bufSz) { + if (wolfCose_CBOR_HasRoom(ctx, need) == 0) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; } else { @@ -103,7 +112,7 @@ int wolfCose_CBOR_EncodeHead(WOLFCOSE_CBOR_CTX* ctx, uint8_t majorType, } else { need = 9; - if ((ctx->idx + need) > ctx->bufSz) { + if (wolfCose_CBOR_HasRoom(ctx, need) == 0) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; } else { @@ -328,7 +337,7 @@ static int wolfCose_CBOR_EncodeSimpleVal(WOLFCOSE_CBOR_CTX* ctx, uint8_t val) if ((ctx == NULL) || (ctx->buf == NULL)) { ret = WOLFCOSE_E_INVALID_ARG; } - else if ((ctx->idx + 1u) > ctx->bufSz) { + else if (wolfCose_CBOR_HasRoom(ctx, 1u) == 0) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; } else { @@ -363,7 +372,7 @@ int wc_CBOR_EncodeFloat(WOLFCOSE_CBOR_CTX* ctx, float val) if ((ctx == NULL) || (ctx->buf == NULL)) { ret = WOLFCOSE_E_INVALID_ARG; } - else if ((ctx->idx + 5u) > ctx->bufSz) { + else if (wolfCose_CBOR_HasRoom(ctx, 5u) == 0) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; } else { @@ -385,7 +394,7 @@ int wc_CBOR_EncodeDouble(WOLFCOSE_CBOR_CTX* ctx, double val) if ((ctx == NULL) || (ctx->buf == NULL)) { ret = WOLFCOSE_E_INVALID_ARG; } - else if ((ctx->idx + 9u) > ctx->bufSz) { + else if (wolfCose_CBOR_HasRoom(ctx, 9u) == 0) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; } else { @@ -528,6 +537,12 @@ static int wolfCose_CBOR_DecodeContainerStart(WOLFCOSE_CBOR_CTX* ctx, if (item.majorType != majorType) { ret = WOLFCOSE_E_CBOR_TYPE; } +#if SIZE_MAX < UINT64_MAX + else if (item.val > (uint64_t)SIZE_MAX) { + /* Definite-length count exceeds addressable range (32-bit). */ + ret = WOLFCOSE_E_CBOR_OVERFLOW; + } +#endif else { *count = (size_t)item.val; } diff --git a/tests/test_cbor.c b/tests/test_cbor.c index 5b48824..47bfdb2 100644 --- a/tests/test_cbor.c +++ b/tests/test_cbor.c @@ -676,6 +676,28 @@ static void test_cbor_encode_bstr_null_with_len(void) "EncodeBstr rejects NULL data with positive length"); } +static void test_cbor_encode_idx_past_bufsz(void) +{ + /* WOLFCOSE_CBOR_CTX is public; a corrupted idx near SIZE_MAX must not + * wrap the capacity check and write out of bounds. */ + uint8_t out[8]; + WOLFCOSE_CBOR_CTX enc; + int ret; + + printf(" [Encode: idx past bufSz does not wrap]\n"); + enc.buf = out; + enc.bufSz = sizeof(out); + enc.idx = SIZE_MAX; + ret = wc_CBOR_EncodeUint(&enc, 1u); + TEST_ASSERT(ret == WOLFCOSE_E_BUFFER_TOO_SMALL, + "EncodeUint rejects idx past bufSz"); + + enc.idx = SIZE_MAX; + ret = wc_CBOR_EncodeNull(&enc); + TEST_ASSERT(ret == WOLFCOSE_E_BUFFER_TOO_SMALL, + "EncodeNull rejects idx past bufSz"); +} + /* ----- Error cases ----- */ static void test_cbor_errors(void) { @@ -866,6 +888,7 @@ int test_cbor(void) test_cbor_skip_tainted_count(); test_cbor_decode_simple_not_well_formed(); test_cbor_encode_bstr_null_with_len(); + test_cbor_encode_idx_past_bufsz(); test_cbor_errors(); test_cbor_negative_map_keys(); From a4f5376d3382fba0e7cc41d3db562a0aeeb61681 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 15:56:53 -0700 Subject: [PATCH 04/38] F-5304/F-5361 - Clear stale state and reject trailing data before COSE_Key import --- src/wolfcose.c | 32 ++++++++++++++++++++++---------- tests/test_cose.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 819a42d..0f833fc 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -1790,6 +1790,16 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) ctx.idx = 0; wolfCose_HdrStateInit(&keyLabelState); + /* Reset decoded metadata so a malformed key cannot reuse caller state + * (e.g. a prior kty/hasPrivate). The key.* union is left untouched: it + * holds the caller-attached wolfCrypt object used for import. */ + key->kty = 0; + key->alg = WOLFCOSE_ALG_UNSET; + key->crv = 0; + key->kid = NULL; + key->kidLen = 0; + key->hasPrivate = 0; + ret = wc_CBOR_DecodeMapStart(&ctx, &mapCount); if ((ret == WOLFCOSE_SUCCESS) && (mapCount > (size_t)WOLFCOSE_MAX_MAP_ITEMS)) { @@ -1896,6 +1906,18 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) } } + /* kty is mandatory. Validate before import so an absent kty cannot + * fall through to a stale key type. */ + if ((ret == WOLFCOSE_SUCCESS) && (key->kty == 0)) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } + + /* RFC 8949 Section 5.3.1: reject trailing data before importing any + * key material so a failed decode leaves no key populated. */ + if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + /* Import key data into wolfCrypt key structs */ if (ret == WOLFCOSE_SUCCESS) { #ifdef HAVE_ECC @@ -2109,16 +2131,6 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) * can inspect it. Nothing to do here. */ } } - - /* kty is mandatory. */ - if ((ret == WOLFCOSE_SUCCESS) && (key->kty == 0)) { - ret = WOLFCOSE_E_COSE_BAD_HDR; - } - - /* RFC 8949 Section 5.3.1: reject trailing data after the map. */ - if ((ret == WOLFCOSE_SUCCESS) && (ctx.idx != ctx.bufSz)) { - ret = WOLFCOSE_E_CBOR_MALFORMED; - } } return ret; diff --git a/tests/test_cose.c b/tests/test_cose.c index f2594b5..fbe5202 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -10654,6 +10654,42 @@ static void test_cose_key_decode_trailing_bytes(void) "CoseKey_Decode rejects trailing bytes"); } +static void test_cose_key_decode_no_material_on_failure(void) +{ + WOLFCOSE_KEY key; + int ret; + uint8_t prior[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; + /* {-1: h'<16>'} -- symmetric k but mandatory kty (label 1) omitted. */ + uint8_t noKty[] = { + 0xA1u, 0x20u, 0x50u, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + }; + /* {1: 4, -1: h'<16>'} valid symmetric key followed by trailing garbage. */ + uint8_t trailing[] = { + 0xA2u, 0x01u, 0x04u, 0x20u, 0x50u, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0xFFu + }; + + TEST_LOG(" [CoseKey_Decode no material on failure]\n"); + + /* Stale state: a pre-populated key must not satisfy a no-kty message. */ + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, prior, sizeof(prior)); + ret = wc_CoseKey_Decode(&key, noKty, sizeof(noKty)); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_HDR, + "CoseKey_Decode rejects missing kty over stale state"); + TEST_ASSERT(key.hasPrivate == 0u, "no private flag after stale reject"); + + /* Trailing data must be rejected before any key material is imported. */ + (void)wc_CoseKey_Init(&key); + ret = wc_CoseKey_Decode(&key, trailing, sizeof(trailing)); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, + "CoseKey_Decode rejects trailing data"); + TEST_ASSERT(key.hasPrivate == 0u, "no private flag after trailing reject"); + TEST_ASSERT(key.key.symm.key == NULL, "no key material after trailing reject"); +} + static void test_cose_key_decode_symmetric_missing_k(void) { WOLFCOSE_KEY key; @@ -14946,6 +14982,7 @@ int test_cose(void) #if defined(WOLFCOSE_KEY_DECODE) test_cose_key_decode_missing_kty(); test_cose_key_decode_trailing_bytes(); + test_cose_key_decode_no_material_on_failure(); test_cose_key_decode_symmetric_missing_k(); #if defined(HAVE_ECC) test_cose_key_decode_ec2_short_coord(); From b87d16ea47db8b946a84cf192113a68464fb2e8a Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 16:02:27 -0700 Subject: [PATCH 05/38] F-5215/F-5220/F-5366 - Require fixed-length EC2 coordinates per RFC 9053 --- src/wolfcose.c | 40 ++++++++++++++++++++++++++-------------- tests/test_cose.c | 4 ++-- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 0f833fc..3fc88a3 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -1918,6 +1918,22 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) ret = WOLFCOSE_E_CBOR_MALFORMED; } +#ifdef HAVE_ECC + /* RFC 9053 Section 7.1.1: EC2 coordinates are fixed length with + * leading zeros preserved. Reject any present coordinate that does not + * 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 ((ret == WOLFCOSE_SUCCESS) && + (((xData != NULL) && (xLen != coordSz)) || + ((yData != NULL) && (yLen != coordSz)) || + ((dData != NULL) && (dLen != coordSz)))) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } + } +#endif + /* Import key data into wolfCrypt key structs */ if (ret == WOLFCOSE_SUCCESS) { #ifdef HAVE_ECC @@ -1932,28 +1948,23 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_CrvKeySize(key->crv, &coordSz); } + if ((ret == WOLFCOSE_SUCCESS) && (coordSz > MAX_ECC_BYTES)) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } if (ret == WOLFCOSE_SUCCESS) { byte tmpX[MAX_ECC_BYTES]; byte tmpY[MAX_ECC_BYTES]; byte tmpD[MAX_ECC_BYTES]; - /* Accept shorter coordinates and right-justify them - * into the fixed-size import buffers. */ - if ((coordSz > sizeof(tmpX)) || - (xLen > coordSz) || (yLen > coordSz) || - ((dData != NULL) && (dLen > coordSz))) { - ret = WOLFCOSE_E_COSE_BAD_HDR; - } - + /* Coordinates are fixed length (validated above). */ if (ret == WOLFCOSE_SUCCESS) { (void)XMEMSET(tmpX, 0, sizeof(tmpX)); (void)XMEMSET(tmpY, 0, sizeof(tmpY)); (void)XMEMSET(tmpD, 0, sizeof(tmpD)); - (void)XMEMCPY(&tmpX[coordSz - xLen], xData, xLen); - (void)XMEMCPY(&tmpY[coordSz - yLen], yData, yLen); + (void)XMEMCPY(tmpX, xData, coordSz); + (void)XMEMCPY(tmpY, yData, coordSz); if (dData != NULL) { - (void)XMEMCPY(&tmpD[coordSz - dLen], dData, - dLen); + (void)XMEMCPY(tmpD, dData, coordSz); INJECT_FAILURE(WOLF_FAIL_ECC_IMPORT_X963, -1) { ret = wc_ecc_import_unsigned( @@ -3082,12 +3093,13 @@ static int wolfCose_DecodeEphemeralKey(WOLFCOSE_CBOR_CTX* ctx, } } - /* Reject oversized coordinates; shorter peers are accepted. */ + /* RFC 9053 Section 7.1.1: ephemeral EC2 coordinates are fixed length with + * leading zeros preserved; require exact-length x and y. */ if (ret == WOLFCOSE_SUCCESS) { size_t coordSz = 0; ret = wolfCose_CrvKeySize(*crv, &coordSz); if ((ret == WOLFCOSE_SUCCESS) && - ((*xLen > coordSz) || (*yLen > coordSz))) { + ((*xLen != coordSz) || (*yLen != coordSz))) { ret = WOLFCOSE_E_COSE_BAD_HDR; } } diff --git a/tests/test_cose.c b/tests/test_cose.c index fbe5202..d828f2b 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -10710,14 +10710,14 @@ static void test_cose_key_decode_ec2_short_coord(void) { WOLFCOSE_KEY key; int ret; - /* COSE EC2 P-256 with x = 31 bytes, y = 32 bytes. + /* COSE EC2 P-256 with x = 31 bytes (one short), y = 32 bytes. * map(4): {1:2 (kty=EC2), -1:1 (crv=P256), * -2: bstr(31) of zeros, -3: bstr(32) of zeros} */ uint8_t shortX[] = { 0xA4u, 0x01u, 0x02u, 0x20u, 0x01u, 0x21u, 0x58u, 0x1Fu, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x22u, 0x58u, 0x20u, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 From c76b5e01e0d38749c5c712f901136fa130c0e39d Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 16:10:30 -0700 Subject: [PATCH 06/38] F-5218/F-5288/F-5365 - Validate recipient and signer unprotected headers --- src/wolfcose.c | 66 +++++++++++++++++++-- tests/test_cose.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 6 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 3fc88a3..197a34a 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -4517,9 +4517,11 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, ret = WOLFCOSE_E_COSE_BAD_ALG; } - /* Signer unprotected headers (skip for now) */ + /* Signer unprotected headers. Decode with duplicate-label tracking so a + * malformed signer header (repeated labels, or labels also present in the + * signer protected bucket) is rejected. */ if (ret == WOLFCOSE_SUCCESS) { - ret = wc_CBOR_Skip(&ctx); + ret = wolfCose_DecodeUnprotectedHdr(&ctx, &signerHdr, &signerHdrState); } /* Signature */ @@ -6873,8 +6875,11 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, ret = WOLFCOSE_E_CBOR_MALFORMED; } - /* [0] recipient protected header */ + /* [0] recipient protected header. Initialize the header state so the + * unprotected decode below can run cross-bucket duplicate checks even when + * the protected bucket is an empty bstr. */ if (ret == WOLFCOSE_SUCCESS) { + wolfCose_HdrStateInit(&recipientHdrState); ret = wc_CBOR_DecodeBstr(&ctx, &recipientProtectedData, &recipientProtectedLen); } if ((ret == WOLFCOSE_SUCCESS) && (recipientProtectedLen > 0u)) { @@ -6915,6 +6920,12 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, ret = wc_CBOR_DecodeInt(&ctx, &label); } + /* Reject duplicate labels within the unprotected map and labels + * also present in the recipient protected bucket. */ + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_HdrStateCheckAndAdd(&recipientHdrState, label); + } + if ((ret == WOLFCOSE_SUCCESS) && (label == WOLFCOSE_HDR_EPHEMERAL_KEY)) { if (haveEphemKey != 0) { @@ -6951,7 +6962,12 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, else #endif if (ret == WOLFCOSE_SUCCESS) { - ret = wc_CBOR_Skip(&ctx); + /* Decode the recipient unprotected map with duplicate-label tracking + * (within the map and against the recipient protected bucket). */ + WOLFCOSE_HDR recipUnprotHdr; + (void)XMEMSET(&recipUnprotHdr, 0, sizeof(recipUnprotHdr)); + ret = wolfCose_DecodeUnprotectedHdr(&ctx, &recipUnprotHdr, + &recipientHdrState); } else { /* No action required */ @@ -7645,9 +7661,47 @@ int wc_CoseMac_Verify(const WOLFCOSE_RECIPIENT* recipient, ret = wc_CBOR_Skip(&ctx); } - /* Parse the recipient (skip it - we use the provided key) */ + /* Parse the selected COSE_recipient: [protected, unprotected, ciphertext]. + * The caller-supplied key is used for the MAC, but the recipient structure + * and header buckets must still be well formed (duplicate-label checks). */ if (ret == WOLFCOSE_SUCCESS) { - ret = wc_CBOR_Skip(&ctx); + size_t recipArrCount = 0; + ret = wc_CBOR_DecodeArrayStart(&ctx, &recipArrCount); + if ((ret == WOLFCOSE_SUCCESS) && (recipArrCount != 3u)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + } + if (ret == WOLFCOSE_SUCCESS) { + const uint8_t* recipProt = NULL; + size_t recipProtLen = 0; + WOLFCOSE_HDR recipHdr; + WOLFCOSE_HDR_STATE recipState; + + ret = wc_CBOR_DecodeBstr(&ctx, &recipProt, &recipProtLen); + if (ret == WOLFCOSE_SUCCESS) { + (void)XMEMSET(&recipHdr, 0, sizeof(recipHdr)); + ret = wolfCose_DecodeProtectedHdr(recipProt, recipProtLen, + &recipHdr, &recipState); + } + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_DecodeUnprotectedHdr(&ctx, &recipHdr, &recipState); + } + /* ciphertext: bstr (wrapped key) or nil (direct). */ + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_CBOR_DecodeHead(&ctx, &item); + } + if (ret == WOLFCOSE_SUCCESS) { + if ((item.majorType == WOLFCOSE_CBOR_SIMPLE) && + (item.val == 22u)) { + /* nil - direct MAC, no wrapped key */ + } + else if (item.majorType == WOLFCOSE_CBOR_BSTR) { + /* bstr - wrapped key present (unused with provided key) */ + } + else { + ret = WOLFCOSE_E_CBOR_TYPE; + } + } } /* Skip remaining recipients, then reject trailing data (RFC 8949 5.3.1). */ diff --git a/tests/test_cose.c b/tests/test_cose.c index d828f2b..18a4103 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -8888,6 +8888,139 @@ static void test_cose_oversized_int_narrowing(void) "CoseKey_Decode rejects oversized kty"); } +#ifdef HAVE_ECC +static void test_cose_sign_dup_signer_unprot_hdr(void) +{ + WOLFCOSE_KEY key; + ecc_key eccKey; + WC_RNG rng; + int ret; + int rngInited = 0; + int eccInited = 0; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + WOLFCOSE_HDR hdr; + const uint8_t* payload = NULL; + size_t payloadLen = 0; + /* COSE_Sign with one signer whose unprotected map repeats label 4 (kid). + * [ h'', {}, 'x', [ [ h'A10126', {4:h'01',4:h'02'}, h'0000' ] ] ] */ + uint8_t msg[] = { + 0x84u, 0x40u, 0xA0u, 0x41u, 0x78u, 0x81u, + 0x83u, 0x43u, 0xA1u, 0x01u, 0x26u, + 0xA2u, 0x04u, 0x41u, 0x01u, 0x04u, 0x41u, 0x02u, + 0x42u, 0x00u, 0x00u + }; + + TEST_LOG(" [Sign multi dup signer unprotected label]\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { TEST_ASSERT(0, "rng init"); } + if (ret == 0) { rngInited = 1; } + if (ret == 0) { + wc_ecc_init(&eccKey); + eccInited = 1; + ret = wc_ecc_make_key(&rng, 32, &eccKey); + if (ret != 0) { TEST_ASSERT(0, "keygen"); } + } + if (ret == 0) { + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetEcc(&key, WOLFCOSE_CRV_P256, &eccKey); + ret = wc_CoseSign_Verify(&key, 0, msg, sizeof(msg), + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, &payload, &payloadLen); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, + "dup signer unprotected label rejected"); + } + + if (eccInited != 0) { (void)wc_ecc_free(&eccKey); } + if (rngInited != 0) { (void)wc_FreeRng(&rng); } +} +#endif /* HAVE_ECC */ + +#ifndef NO_HMAC +static void test_cose_mac_dup_recipient_unprot_hdr(void) +{ + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipient; + uint8_t macKey[32] = {0}; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + WOLFCOSE_HDR hdr; + const uint8_t* payload = NULL; + size_t payloadLen = 0; + int ret; + /* COSE_Mac whose single recipient repeats label 4 in its unprotected map. + * [ h'A10105', {}, 'x', h'<32>', [ [ h'', {4:h'01',4:h'02'}, nil ] ] ] */ + uint8_t msg[] = { + 0x85u, 0x43u, 0xA1u, 0x01u, 0x05u, 0xA0u, 0x41u, 0x78u, + 0x58u, 0x20u, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x81u, 0x83u, 0x40u, + 0xA2u, 0x04u, 0x41u, 0x01u, 0x04u, 0x41u, 0x02u, + 0xF6u + }; + + TEST_LOG(" [Mac multi dup recipient unprotected label]\n"); + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, macKey, sizeof(macKey)); + recipient.algId = 0; + recipient.key = &key; + recipient.kid = NULL; + recipient.kidLen = 0; + + ret = wc_CoseMac_Verify(&recipient, 0, msg, sizeof(msg), + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, &payload, &payloadLen); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, + "dup recipient unprotected label rejected (mac)"); +} +#endif /* NO_HMAC */ + +#ifdef HAVE_AESGCM +static void test_cose_encrypt_dup_recipient_unprot_hdr(void) +{ + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipient; + uint8_t cek[16] = {0}; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + WOLFCOSE_HDR hdr; + uint8_t plaintext[16]; + size_t plaintextLen = 0; + int ret; + /* COSE_Encrypt (A128GCM) whose single direct recipient repeats label 4. + * [ h'A10101', {5:h'<12 IV>'}, h'<16 ct>', [ [ h'', {4:h'01',4:h'02'}, + * nil ] ] ] */ + uint8_t msg[] = { + 0x84u, 0x43u, 0xA1u, 0x01u, 0x01u, + 0xA1u, 0x05u, 0x4Cu, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0x50u, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x81u, 0x83u, 0x40u, + 0xA2u, 0x04u, 0x41u, 0x01u, 0x04u, 0x41u, 0x02u, + 0xF6u + }; + + TEST_LOG(" [Encrypt multi dup recipient unprotected label]\n"); + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, cek, sizeof(cek)); + recipient.algId = 0; + recipient.key = &key; + recipient.kid = NULL; + recipient.kidLen = 0; + + ret = wc_CoseEncrypt_Decrypt(&recipient, 0, msg, sizeof(msg), + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, + "dup recipient unprotected label rejected (encrypt)"); +} +#endif /* HAVE_AESGCM */ + static void test_cose_protected_hdr_content_type(void) { int ret; @@ -14888,6 +15021,15 @@ int test_cose(void) test_cose_protected_hdr_empty_map(); test_cose_protected_hdr_trailing(); test_cose_oversized_int_narrowing(); +#ifdef HAVE_ECC + test_cose_sign_dup_signer_unprot_hdr(); +#endif +#ifndef NO_HMAC + test_cose_mac_dup_recipient_unprot_hdr(); +#endif +#ifdef HAVE_AESGCM + test_cose_encrypt_dup_recipient_unprot_hdr(); +#endif test_cose_protected_hdr_content_type(); test_cose_protected_hdr_tstr_label(); test_cose_protected_hdr_dup_label(); From 078bfb59d7d1f30fb38ed2c3f09d099521ea4230 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 16:15:35 -0700 Subject: [PATCH 07/38] F-5221 - Reject multi-recipient ECDH-ES direct messages on decrypt --- src/wolfcose.c | 10 ++++++ tests/test_cose.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index 197a34a..7bbbf67 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -6897,6 +6897,16 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, alg); } +#if defined(WOLFCOSE_ECDH_ES_DIRECT) && defined(HAVE_ECC) && defined(HAVE_HKDF) + /* RFC 9052 Section 8.5.5: direct key agreement carries exactly one + * recipient. */ + if ((ret == WOLFCOSE_SUCCESS) && + (wolfCose_IsEcdhEsDirectAlg(recipientAlgId) != 0) && + ((recipientsCount != 1u) || (recipientIndex != 0u))) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } +#endif + /* [1] recipient unprotected header */ #if defined(WOLFCOSE_ECDH_ES_DIRECT) && defined(HAVE_ECC) && defined(HAVE_HKDF) if ((ret == WOLFCOSE_SUCCESS) && diff --git a/tests/test_cose.c b/tests/test_cose.c index 18a4103..a4c7f82 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -14730,6 +14730,95 @@ static void test_ecdh_es_multi_recipient_rejected(void) (void)wc_ecc_free(&eccKey2); (void)wc_FreeRng(&rng); } + +static void test_ecdh_es_multi_recipient_decrypt_rejected(void) +{ + WOLFCOSE_KEY recipientKey; + WOLFCOSE_RECIPIENT recipient; + WOLFCOSE_HDR hdr; + ecc_key recipientEcc; + WC_RNG rng; + WOLFCOSE_CBOR_CTX ctx; + int ret; + size_t outerCount = 0; + size_t recipPos = 0; + size_t i; + uint8_t out[512]; + uint8_t spliced[520]; + size_t outLen = 0; + size_t splicedLen = 0; + uint8_t scratch[256]; + uint8_t plaintext[64]; + size_t plaintextLen = 0; + const uint8_t payload[] = "ECDH-ES decode multi"; + uint8_t iv[12] = {1,2,3,4,5,6,7,8,9,10,11,12}; + static const uint8_t dummyRecip[] = {0x83u, 0x40u, 0xA0u, 0xF6u}; + + TEST_LOG(" [ECDH-ES Multi-Recipient Decode Rejected]\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { TEST_ASSERT(0, "rng init"); return; } + wc_ecc_init(&recipientEcc); + wc_ecc_make_key(&rng, 32, &recipientEcc); + (void)wc_CoseKey_Init(&recipientKey); + (void)wc_CoseKey_SetEcc(&recipientKey, WOLFCOSE_CRV_P256, &recipientEcc); + recipientKey.hasPrivate = 0; + + recipient.algId = WOLFCOSE_ALG_ECDH_ES_HKDF_256; + recipient.key = &recipientKey; + recipient.kid = NULL; + recipient.kidLen = 0; + + ret = wc_CoseEncrypt_Encrypt(&recipient, 1, WOLFCOSE_ALG_A128GCM, + iv, sizeof(iv), payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); + TEST_ASSERT(ret == 0, "ecdh-es single encrypt"); + + /* Locate the recipients array (outer element [3]) and rewrite it as a + * two-recipient array by appending a dummy recipient. */ + if (ret == 0) { + uint64_t tag = 0; + ctx.cbuf = out; + ctx.bufSz = outLen; + ctx.idx = 0; + if (wc_CBOR_PeekType(&ctx) == WOLFCOSE_CBOR_TAG) { + ret = wc_CBOR_DecodeTag(&ctx, &tag); + } + if (ret == 0) { + ret = wc_CBOR_DecodeArrayStart(&ctx, &outerCount); + } + for (i = 0; (ret == 0) && (i < 3u); i++) { + ret = wc_CBOR_Skip(&ctx); + } + recipPos = ctx.idx; + TEST_ASSERT((ret == 0) && (outerCount == 4u) && (out[recipPos] == 0x81u), + "located single-recipient array"); + } + + if ((ret == 0) && (out[recipPos] == 0x81u)) { + XMEMCPY(spliced, out, recipPos); + spliced[recipPos] = 0x82u; + XMEMCPY(&spliced[recipPos + 1u], &out[recipPos + 1u], + outLen - recipPos - 1u); + splicedLen = outLen; + XMEMCPY(&spliced[splicedLen], dummyRecip, sizeof(dummyRecip)); + splicedLen += sizeof(dummyRecip); + + recipientKey.hasPrivate = 1; + memset(&hdr, 0, sizeof(hdr)); + ret = wc_CoseEncrypt_Decrypt(&recipient, 0, spliced, splicedLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), &hdr, + plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_HDR, + "ecdh-es multi-recipient decode rejected"); + } + + wc_CoseKey_Free(&recipientKey); + (void)wc_ecc_free(&recipientEcc); + (void)wc_FreeRng(&rng); +} #endif /* Test #9: wc_CoseSign_Verify rejects wrong array count */ @@ -15338,6 +15427,7 @@ int test_cose(void) #if defined(WOLFCOSE_ENCRYPT) && defined(HAVE_AESGCM) && \ defined(WOLFCOSE_ECDH_ES_DIRECT) && defined(HAVE_ECC) && defined(HAVE_HKDF) test_ecdh_es_multi_recipient_rejected(); + test_ecdh_es_multi_recipient_decrypt_rejected(); #endif /* Mock failure injection tests */ From e643270f235a6b90e18c8207b7b18f24d18d4697 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 16:18:25 -0700 Subject: [PATCH 08/38] F-5285 - Use zero-length protected header for direct COSE_Encrypt recipients --- src/wolfcose.c | 7 ++-- tests/test_cose.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 7bbbf67..0339419 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -6598,8 +6598,11 @@ int wc_CoseEncrypt_Encrypt(const WOLFCOSE_RECIPIENT* recipients, /* For direct key agreement, the wrapped CEK is empty */ /* COSE_recipient = [protected, unprotected, ciphertext] */ - /* Encode recipient protected header */ - if (recipients[i].algId != WOLFCOSE_ALG_UNSET) { + /* Encode recipient protected header. RFC 9053 Section 6.1: the direct + * key algorithm uses a zero-length protected header, so treat an + * explicit WOLFCOSE_ALG_DIRECT the same as the unset direct case. */ + if ((recipients[i].algId != WOLFCOSE_ALG_UNSET) && + (recipients[i].algId != WOLFCOSE_ALG_DIRECT)) { ret = wolfCose_EncodeProtectedHdr(recipients[i].algId, recipientProtectedBuf, sizeof(recipientProtectedBuf), &recipientProtectedLen); diff --git a/tests/test_cose.c b/tests/test_cose.c index a4c7f82..c05b544 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -9019,6 +9019,86 @@ static void test_cose_encrypt_dup_recipient_unprot_hdr(void) TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, "dup recipient unprotected label rejected (encrypt)"); } + +static void test_cose_encrypt_direct_empty_protected(void) +{ + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipient; + WOLFCOSE_HDR hdr; + WOLFCOSE_CBOR_CTX ctx; + WC_RNG rng; + int ret; + int rngInited = 0; + uint8_t cek[16] = {0}; + uint8_t iv[12] = {0}; + uint8_t out[256]; + size_t outLen = 0; + uint8_t scratch[512]; + uint8_t plaintext[64]; + size_t plaintextLen = 0; + const uint8_t payload[] = "direct alg payload"; + size_t i; + size_t n = 0; + uint64_t tag = 0; + + TEST_LOG(" [Encrypt direct alg empty protected]\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { TEST_ASSERT(0, "rng init"); } + if (ret == 0) { rngInited = 1; } + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, cek, sizeof(cek)); + recipient.algId = WOLFCOSE_ALG_DIRECT; + recipient.key = &key; + recipient.kid = NULL; + recipient.kidLen = 0; + + if (ret == 0) { + ret = wc_CoseEncrypt_Encrypt(&recipient, 1, WOLFCOSE_ALG_A128GCM, + iv, sizeof(iv), payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); + TEST_ASSERT(ret == 0, "direct encrypt"); + } + + if (ret == 0) { + ctx.cbuf = out; + ctx.bufSz = outLen; + ctx.idx = 0; + if (wc_CBOR_PeekType(&ctx) == WOLFCOSE_CBOR_TAG) { + ret = wc_CBOR_DecodeTag(&ctx, &tag); + } + if (ret == 0) { + ret = wc_CBOR_DecodeArrayStart(&ctx, &n); + } + for (i = 0; (ret == 0) && (i < 3u); i++) { + ret = wc_CBOR_Skip(&ctx); + } + if (ret == 0) { + ret = wc_CBOR_DecodeArrayStart(&ctx, &n); /* recipients */ + } + if (ret == 0) { + ret = wc_CBOR_DecodeArrayStart(&ctx, &n); /* recipient */ + } + TEST_ASSERT((ret == 0) && (out[ctx.idx] == 0x40u), + "direct recipient protected is empty bstr"); + } + + if (ret == 0) { + memset(&hdr, 0, sizeof(hdr)); + ret = wc_CoseEncrypt_Decrypt(&recipient, 0, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), &hdr, + plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret == 0, "direct decrypt roundtrip"); + TEST_ASSERT((plaintextLen == sizeof(payload) - 1) && + (memcmp(plaintext, payload, plaintextLen) == 0), + "direct payload match"); + } + + if (rngInited != 0) { (void)wc_FreeRng(&rng); } +} #endif /* HAVE_AESGCM */ static void test_cose_protected_hdr_content_type(void) @@ -15118,6 +15198,7 @@ int test_cose(void) #endif #ifdef HAVE_AESGCM test_cose_encrypt_dup_recipient_unprot_hdr(); + test_cose_encrypt_direct_empty_protected(); #endif test_cose_protected_hdr_content_type(); test_cose_protected_hdr_tstr_label(); From 66244eff6a628bb20adb4f1ca06c3dc68d8dc7a3 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 16:21:23 -0700 Subject: [PATCH 09/38] F-5367/F-5377 - Classify recipient key-management alg and enforce algId policy --- src/wolfcose.c | 40 +++++++++++++++++++++++++++++++++++ tests/test_cose.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index 0339419..e1996bd 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -6900,6 +6900,46 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, alg); } + /* Classify the recipient key-management algorithm. Only direct, ECDH-ES + * direct, and AES key wrap are supported; reject anything else instead of + * silently treating it as direct-key decryption. */ + if (ret == WOLFCOSE_SUCCESS) { + int recipModeOk = 0; + if ((recipientAlgId == WOLFCOSE_ALG_UNSET) || + (recipientAlgId == WOLFCOSE_ALG_DIRECT)) { + recipModeOk = 1; + } +#if defined(WOLFCOSE_ECDH_ES_DIRECT) && defined(HAVE_ECC) && defined(HAVE_HKDF) + else if (wolfCose_IsEcdhEsDirectAlg(recipientAlgId) != 0) { + recipModeOk = 1; + } +#endif +#if defined(WOLFCOSE_KEY_WRAP) + else if (wolfCose_IsKeyWrapAlg(recipientAlgId) != 0) { + recipModeOk = 1; + } +#endif + else { + /* No action required */ + } + if (recipModeOk == 0) { + ret = WOLFCOSE_E_COSE_BAD_ALG; + } + } + + /* Enforce the caller's recipient->algId policy when set. A message in + * implicit direct mode has no recipient alg, so normalize it to direct. */ + if ((ret == WOLFCOSE_SUCCESS) && + (recipient->algId != WOLFCOSE_ALG_UNSET)) { + int32_t gotAlg = recipientAlgId; + if (gotAlg == WOLFCOSE_ALG_UNSET) { + gotAlg = WOLFCOSE_ALG_DIRECT; + } + if (recipient->algId != gotAlg) { + ret = WOLFCOSE_E_COSE_BAD_ALG; + } + } + #if defined(WOLFCOSE_ECDH_ES_DIRECT) && defined(HAVE_ECC) && defined(HAVE_HKDF) /* RFC 9052 Section 8.5.5: direct key agreement carries exactly one * recipient. */ diff --git a/tests/test_cose.c b/tests/test_cose.c index c05b544..1d32942 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -9099,6 +9099,59 @@ static void test_cose_encrypt_direct_empty_protected(void) if (rngInited != 0) { (void)wc_FreeRng(&rng); } } + +static void test_cose_encrypt_recipient_alg_checks(void) +{ + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipient; + WOLFCOSE_HDR hdr; + uint8_t cek[16] = {0}; + uint8_t scratch[512]; + uint8_t plaintext[16]; + size_t plaintextLen = 0; + int ret; + /* COSE_Encrypt whose recipient protected alg is HMAC-256 (5), an algorithm + * that is not a key-distribution algorithm. */ + uint8_t unsupported[] = { + 0x84u, 0x43u, 0xA1u, 0x01u, 0x01u, + 0xA1u, 0x05u, 0x4Cu, 0,0,0,0,0,0,0,0,0,0,0,0, + 0x50u, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x81u, 0x83u, 0x43u, 0xA1u, 0x01u, 0x05u, 0xA0u, 0xF6u + }; + /* A valid direct-mode COSE_Encrypt (empty recipient protected). */ + uint8_t direct[] = { + 0x84u, 0x43u, 0xA1u, 0x01u, 0x01u, + 0xA1u, 0x05u, 0x4Cu, 0,0,0,0,0,0,0,0,0,0,0,0, + 0x50u, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x81u, 0x83u, 0x40u, 0xA0u, 0xF6u + }; + + TEST_LOG(" [Encrypt recipient alg checks]\n"); + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, cek, sizeof(cek)); + recipient.key = &key; + recipient.kid = NULL; + recipient.kidLen = 0; + + /* 5377: unsupported recipient alg must be rejected, not treated direct. */ + recipient.algId = 0; + ret = wc_CoseEncrypt_Decrypt(&recipient, 0, unsupported, sizeof(unsupported), + NULL, 0, NULL, 0, + scratch, sizeof(scratch), &hdr, + plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, + "unsupported recipient alg rejected"); + + /* 5367: caller key-wrap policy must reject a direct-mode message. */ + recipient.algId = WOLFCOSE_ALG_A128KW; + ret = wc_CoseEncrypt_Decrypt(&recipient, 0, direct, sizeof(direct), + NULL, 0, NULL, 0, + scratch, sizeof(scratch), &hdr, + plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, + "recipient algId policy enforced"); +} #endif /* HAVE_AESGCM */ static void test_cose_protected_hdr_content_type(void) @@ -15199,6 +15252,7 @@ int test_cose(void) #ifdef HAVE_AESGCM test_cose_encrypt_dup_recipient_unprot_hdr(); test_cose_encrypt_direct_empty_protected(); + test_cose_encrypt_recipient_alg_checks(); #endif test_cose_protected_hdr_content_type(); test_cose_protected_hdr_tstr_label(); From 6a5481533a480bafeb0bbddbab2ee5f48c70c1ce Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 16:23:20 -0700 Subject: [PATCH 10/38] F-5284 - Decode kid in the protected header bucket --- src/wolfcose.c | 13 +++++++++++++ tests/test_cose.c | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index e1996bd..232f6fd 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -862,6 +862,19 @@ int wolfCose_DecodeProtectedHdr(const uint8_t* data, size_t dataLen, } } } + else if ((ret == WOLFCOSE_SUCCESS) && + (label == WOLFCOSE_HDR_KID)) { + /* RFC 9052 Section 3.1: kid may appear in the protected + * bucket; populate it the same way the unprotected decoder + * does instead of skipping it as unknown. */ + const uint8_t* kidData; + size_t kidBstrLen; + ret = wc_CBOR_DecodeBstr(&ctx, &kidData, &kidBstrLen); + if (ret == WOLFCOSE_SUCCESS) { + hdr->kid = kidData; + hdr->kidLen = kidBstrLen; + } + } else if ((ret == WOLFCOSE_SUCCESS) && (label == WOLFCOSE_HDR_IV)) { const uint8_t* ivData; diff --git a/tests/test_cose.c b/tests/test_cose.c index 1d32942..183600e 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -8856,6 +8856,22 @@ static void test_cose_protected_hdr_trailing(void) "DecodeProtectedHdr rejects trailing bytes"); } +static void test_cose_protected_hdr_kid(void) +{ + int ret; + WOLFCOSE_HDR hdr; + WOLFCOSE_HDR_STATE hdrState; + uint8_t prot[] = {0xA1u, 0x04u, 0x42u, 0xAAu, 0xBBu}; /* {4: h'AABB'} */ + + TEST_LOG(" [Protected Header: kid]\n"); + XMEMSET(&hdr, 0, sizeof(hdr)); + ret = wolfCose_DecodeProtectedHdr(prot, sizeof(prot), &hdr, &hdrState); + TEST_ASSERT(ret == WOLFCOSE_SUCCESS, "decode protected kid"); + TEST_ASSERT((hdr.kid != NULL) && (hdr.kidLen == 2u) && + (hdr.kid[0] == 0xAAu) && (hdr.kid[1] == 0xBBu), + "protected kid populated"); +} + static void test_cose_oversized_int_narrowing(void) { int ret; @@ -15242,6 +15258,7 @@ int test_cose(void) test_cbor_edge_cases(); test_cose_protected_hdr_empty_map(); test_cose_protected_hdr_trailing(); + test_cose_protected_hdr_kid(); test_cose_oversized_int_narrowing(); #ifdef HAVE_ECC test_cose_sign_dup_signer_unprot_hdr(); From a5247d20d1f9480b95060708de04284c2daef6ee Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 1 Jun 2026 16:27:58 -0700 Subject: [PATCH 11/38] F-5301 - Require ML-DSA key level to match the algorithm level --- src/wolfcose.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_cose.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index 232f6fd..6f2c2fd 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -228,6 +228,31 @@ WOLFCOSE_LOCAL int wolfCose_SigSize(int32_t alg, size_t* sigSz) return ret; } +#ifdef HAVE_DILITHIUM +/* Map an ML-DSA COSE algorithm to the curve identifier its key must carry, so + * a key of the wrong security level cannot satisfy a higher-level alg label. */ +static int wolfCose_MlDsaAlgCrv(int32_t alg, int32_t* crv) +{ + int ret = WOLFCOSE_SUCCESS; + + switch (alg) { + case WOLFCOSE_ALG_ML_DSA_44: + *crv = WOLFCOSE_CRV_ML_DSA_44; + break; + case WOLFCOSE_ALG_ML_DSA_65: + *crv = WOLFCOSE_CRV_ML_DSA_65; + break; + case WOLFCOSE_ALG_ML_DSA_87: + *crv = WOLFCOSE_CRV_ML_DSA_87; + break; + default: + ret = WOLFCOSE_E_COSE_BAD_ALG; + break; + } + return ret; +} +#endif /* HAVE_DILITHIUM */ + int wolfCose_CrvKeySize(int32_t crv, size_t* keySz) { int ret = WOLFCOSE_SUCCESS; @@ -3414,11 +3439,20 @@ int wc_CoseSign1_Sign(WOLFCOSE_KEY* key, int32_t alg, if ((ret == WOLFCOSE_SUCCESS) && ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { size_t expectedSigSz = 0; + int32_t reqCrv = 0; if ((key->kty != WOLFCOSE_KTY_OKP) || (key->key.dilithium == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } + /* Key level must match the algorithm level. */ + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_MlDsaAlgCrv(alg, &reqCrv); + } + if ((ret == WOLFCOSE_SUCCESS) && (key->crv != reqCrv)) { + ret = WOLFCOSE_E_COSE_KEY_TYPE; + } + if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_SigSize(alg, &expectedSigSz); } @@ -3843,10 +3877,18 @@ int wc_CoseSign1_Verify(WOLFCOSE_KEY* key, ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { int verified = 0; + int32_t reqCrv = 0; if ((key->kty != WOLFCOSE_KTY_OKP) || (key->key.dilithium == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } + /* Key level must match the algorithm level. */ + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_MlDsaAlgCrv(alg, &reqCrv); + } + if ((ret == WOLFCOSE_SUCCESS) && (key->crv != reqCrv)) { + ret = WOLFCOSE_E_COSE_KEY_TYPE; + } if (ret == WOLFCOSE_SUCCESS) { INJECT_FAILURE(WOLF_FAIL_DILITHIUM_VERIFY, -1) { @@ -4269,9 +4311,17 @@ int wc_CoseSign_Sign(const WOLFCOSE_SIGNATURE* signers, size_t signerCount, (signer->algId == WOLFCOSE_ALG_ML_DSA_65) || (signer->algId == WOLFCOSE_ALG_ML_DSA_87))) { size_t expectedSigSz = 0; + int32_t reqCrv = 0; if (signer->key->key.dilithium == NULL) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } + /* Key level must match the algorithm level. */ + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_MlDsaAlgCrv(signer->algId, &reqCrv); + } + if ((ret == WOLFCOSE_SUCCESS) && (signer->key->crv != reqCrv)) { + ret = WOLFCOSE_E_COSE_KEY_TYPE; + } if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_SigSize(signer->algId, &expectedSigSz); } @@ -4722,10 +4772,18 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { int verified = 0; + int32_t reqCrv = 0; if ((verifyKey->kty != WOLFCOSE_KTY_OKP) || (verifyKey->key.dilithium == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } + /* Key level must match the algorithm level. */ + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_MlDsaAlgCrv(alg, &reqCrv); + } + if ((ret == WOLFCOSE_SUCCESS) && (verifyKey->crv != reqCrv)) { + ret = WOLFCOSE_E_COSE_KEY_TYPE; + } if (ret == WOLFCOSE_SUCCESS) { #ifdef WOLFSSL_DILITHIUM_NO_CTX ret = wc_dilithium_verify_msg( diff --git a/tests/test_cose.c b/tests/test_cose.c index 183600e..69b14a8 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -1416,6 +1416,67 @@ static void test_cose_sign1_ml_dsa(const char* label, int32_t alg, byte level) (void)wc_FreeRng(&rng); } } + +static void test_cose_sign1_ml_dsa_level_mismatch(void) +{ + WOLFCOSE_KEY signKey; + dilithium_key dlKey; + WC_RNG rng; + int ret = 0; + int rngInited = 0; + int dlInited = 0; + uint8_t payload[] = "ML-DSA level payload"; + uint8_t scratch[8192]; + uint8_t out[8192]; + size_t outLen = 0; + const uint8_t* decPayload = NULL; + size_t decPayloadLen = 0; + WOLFCOSE_HDR hdr; + + TEST_LOG(" [Sign1 ML-DSA level mismatch]\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { TEST_ASSERT(0, "rng init"); } + if (ret == 0) { rngInited = 1; } + if (ret == 0) { + ret = wc_dilithium_init(&dlKey); + if (ret != 0) { TEST_ASSERT(0, "dl init"); } + if (ret == 0) { dlInited = 1; } + } + if (ret == 0) { + ret = wc_dilithium_set_level(&dlKey, 2); + if (ret != 0) { TEST_ASSERT(0, "dl set level"); } + } + if (ret == 0) { + ret = wc_dilithium_make_key(&dlKey, &rng); + if (ret != 0) { TEST_ASSERT(0, "dl keygen"); } + } + if (ret == 0) { + (void)wc_CoseKey_Init(&signKey); + (void)wc_CoseKey_SetDilithium(&signKey, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + ret = wc_CoseSign1_Sign(&signKey, WOLFCOSE_ALG_ML_DSA_44, + NULL, 0, payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); + TEST_ASSERT(ret == 0 && outLen > 0, "ml-dsa level sign"); + } + if (ret == 0) { + /* Key declares level 5 with no alg pin while the message is level 2; + * verify must reject on the level mismatch, not attempt the crypto. */ + signKey.crv = WOLFCOSE_CRV_ML_DSA_87; + signKey.alg = WOLFCOSE_ALG_UNSET; + ret = wc_CoseSign1_Verify(&signKey, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, &decPayload, &decPayloadLen); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_KEY_TYPE, + "ml-dsa level mismatch rejected"); + ret = 0; + } + + if (dlInited != 0) { (void)wc_dilithium_free(&dlKey); } + if (rngInited != 0) { (void)wc_FreeRng(&rng); } +} #endif /* HAVE_DILITHIUM */ /* ----- COSE_Sign1 with external AAD ----- */ @@ -15092,6 +15153,7 @@ int test_cose(void) test_cose_sign1_ml_dsa("ML-DSA-44", WOLFCOSE_ALG_ML_DSA_44, 2); test_cose_sign1_ml_dsa("ML-DSA-65", WOLFCOSE_ALG_ML_DSA_65, 3); test_cose_sign1_ml_dsa("ML-DSA-87", WOLFCOSE_ALG_ML_DSA_87, 5); + test_cose_sign1_ml_dsa_level_mismatch(); #endif /* Mac0 basic tests */ From 2043c333471082ba73d77a6be588d57767837cbe Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 10:42:19 -0700 Subject: [PATCH 12/38] F-5289 - Enforce minimum HMAC key length for COSE MAC --- src/wolfcose.c | 46 ++++++++++++++++++++++++++++++++++++ tests/test_cose.c | 60 ++++++++++++++++++++++++++--------------------- 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 6f2c2fd..42ac2e6 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -480,6 +480,38 @@ int wolfCose_HmacType(int32_t alg, int* hmacType) } return ret; } + +/* RFC 9053 Section 3.1: an HMAC key should be at least the hash output size. + * Reject shorter keys unless the caller explicitly opts in. */ +static int wolfCose_HmacCheckKeyLen(int32_t alg, size_t keyLen) +{ + int ret = WOLFCOSE_SUCCESS; +#ifndef WOLFCOSE_ALLOW_SHORT_HMAC_KEY + size_t minLen = 0; + + switch (alg) { + case WOLFCOSE_ALG_HMAC_256_256: + minLen = 32u; + break; + case WOLFCOSE_ALG_HMAC_384_384: + minLen = 48u; + break; + case WOLFCOSE_ALG_HMAC_512_512: + minLen = 64u; + break; + default: + ret = WOLFCOSE_E_COSE_BAD_ALG; + break; + } + if ((ret == WOLFCOSE_SUCCESS) && (keyLen < minLen)) { + ret = WOLFCOSE_E_COSE_KEY_TYPE; + } +#else + (void)alg; + (void)keyLen; +#endif + return ret; +} #endif /* !NO_HMAC */ /* ----- Internal: ECC DER <-> raw r||s conversion ----- */ @@ -5857,6 +5889,9 @@ int wc_CoseMac0_Create(const WOLFCOSE_KEY* key, int32_t alg, hmacInited = 1; } } + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_HmacCheckKeyLen(alg, key->key.symm.keyLen); + } if (ret == WOLFCOSE_SUCCESS) { INJECT_FAILURE(WOLF_FAIL_HMAC_SET_KEY, -1) { @@ -6136,6 +6171,9 @@ int wc_CoseMac0_Verify(const WOLFCOSE_KEY* key, hmacInited = 1; } } + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_HmacCheckKeyLen(alg, key->key.symm.keyLen); + } if (ret == WOLFCOSE_SUCCESS) { ret = wc_HmacSetKey(&hmac, hmacType, key->key.symm.key, (word32)key->key.symm.keyLen); @@ -7480,6 +7518,10 @@ int wc_CoseMac_Create(const WOLFCOSE_RECIPIENT* recipients, hmacInited = 1; } } + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_HmacCheckKeyLen(macAlgId, + recipients[0].key->key.symm.keyLen); + } if (ret == WOLFCOSE_SUCCESS) { int hmacRet = wc_HmacSetKey(&hmac, hashType, recipients[0].key->key.symm.key, @@ -7880,6 +7922,10 @@ int wc_CoseMac_Verify(const WOLFCOSE_RECIPIENT* recipient, hmacInited = 1; } } + if (ret == WOLFCOSE_SUCCESS) { + ret = wolfCose_HmacCheckKeyLen(alg, + recipient->key->key.symm.keyLen); + } if (ret == WOLFCOSE_SUCCESS) { int hmacRet = wc_HmacSetKey(&hmac, hashType, recipient->key->key.symm.key, diff --git a/tests/test_cose.c b/tests/test_cose.c index 69b14a8..3b64c0f 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -1846,6 +1846,28 @@ static void test_cose_mac0_hmac256(void) } } +static void test_cose_mac0_short_hmac_key(void) +{ + WOLFCOSE_KEY key; + uint8_t shortKey[8] = {1,2,3,4,5,6,7,8}; + uint8_t payload[] = "short key payload"; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + uint8_t out[256]; + size_t outLen = 0; + int ret; + + TEST_LOG(" [Mac0 short HMAC key]\n"); + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, shortKey, sizeof(shortKey)); + ret = wc_CoseMac0_Create(&key, WOLFCOSE_ALG_HMAC_256_256, + NULL, 0, payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_KEY_TYPE, + "mac0 short hmac key rejected"); +} + #ifdef WOLFSSL_SHA384 static void test_cose_mac0_hmac384(void) { @@ -10623,7 +10645,7 @@ static void test_cose_setecc_invalid_curve(void) #endif #if !defined(NO_HMAC) && defined(WOLFCOSE_MAC0_CREATE) -static void test_cose_mac0_hmac_short_key_allowed(void) +static void test_cose_mac0_hmac_short_key_rejected(void) { WOLFCOSE_KEY key; int ret; @@ -10631,12 +10653,9 @@ static void test_cose_mac0_hmac_short_key_allowed(void) uint8_t out[256]; uint8_t scratch[256]; size_t outLen = 0; - WOLFCOSE_HDR hdr; - const uint8_t* decPayload = NULL; - size_t decPayloadLen = 0; const uint8_t payload[] = "wrong key length"; - TEST_LOG(" [Mac0 HMAC short key allowed]\n"); + TEST_LOG(" [Mac0 HMAC short key rejected]\n"); (void)wc_CoseKey_Init(&key); ret = wc_CoseKey_SetSymmetric(&key, shortKey, sizeof(shortKey)); @@ -10649,19 +10668,8 @@ static void test_cose_mac0_hmac_short_key_allowed(void) NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen); - TEST_ASSERT(ret == 0, "Mac0_Create accepts 16B key for HMAC-256/256"); - - memset(&hdr, 0, sizeof(hdr)); - ret = wc_CoseMac0_Verify(&key, out, outLen, - NULL, 0, - NULL, 0, - scratch, sizeof(scratch), - &hdr, &decPayload, &decPayloadLen); - TEST_ASSERT(ret == 0, "Mac0_Verify accepts 16B key for HMAC-256/256"); - TEST_ASSERT(hdr.alg == WOLFCOSE_ALG_HMAC_256_256, "short-key HMAC-256 alg"); - TEST_ASSERT(decPayloadLen == sizeof(payload) - 1, "short-key payload len"); - TEST_ASSERT(XMEMCMP(decPayload, payload, decPayloadLen) == 0, - "short-key payload match"); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_KEY_TYPE, + "Mac0_Create rejects 16B key for HMAC-256/256"); wc_CoseKey_Free(&key); } @@ -10669,7 +10677,7 @@ static void test_cose_mac0_hmac_short_key_allowed(void) #if !defined(NO_HMAC) && defined(WOLFCOSE_MAC0_CREATE) && \ defined(WOLFCOSE_MAC0_VERIFY) -static void test_cose_mac0_verify_short_key_allowed(void) +static void test_cose_mac0_verify_short_key_rejected(void) { WOLFCOSE_KEY signKey; WOLFCOSE_KEY verifyKey; @@ -10684,7 +10692,7 @@ static void test_cose_mac0_verify_short_key_allowed(void) size_t payloadLen = 0; const uint8_t msg[] = "verify wrong keylen"; - TEST_LOG(" [Mac0 verify short key allowed]\n"); + TEST_LOG(" [Mac0 verify short key rejected]\n"); (void)wc_CoseKey_Init(&signKey); ret = wc_CoseKey_SetSymmetric(&signKey, goodKey, sizeof(goodKey)); @@ -10709,11 +10717,8 @@ static void test_cose_mac0_verify_short_key_allowed(void) NULL, 0, scratch, sizeof(scratch), &hdr, &payload, &payloadLen); - TEST_ASSERT(ret == 0, "Mac0_Verify accepts 16B key for HMAC-256/256"); - TEST_ASSERT(hdr.alg == WOLFCOSE_ALG_HMAC_256_256, "verify short-key alg"); - TEST_ASSERT(payloadLen == sizeof(msg) - 1, "verify short-key payload len"); - TEST_ASSERT(XMEMCMP(payload, msg, payloadLen) == 0, - "verify short-key payload match"); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_KEY_TYPE, + "Mac0_Verify rejects 16B key for HMAC-256/256"); wc_CoseKey_Free(&signKey); wc_CoseKey_Free(&verifyKey); @@ -15159,6 +15164,7 @@ int test_cose(void) /* Mac0 basic tests */ #if !defined(NO_HMAC) test_cose_mac0_hmac256(); + test_cose_mac0_short_hmac_key(); test_cose_mac0_with_aad(); test_cose_mac0_detached(); test_cose_mac0_detached_with_aad(); @@ -15406,12 +15412,12 @@ int test_cose(void) test_cose_setecc_invalid_curve(); #endif #if !defined(NO_HMAC) && defined(WOLFCOSE_MAC0_CREATE) - test_cose_mac0_hmac_short_key_allowed(); + test_cose_mac0_hmac_short_key_rejected(); test_cose_mac0_create_key_alg_mismatch(); #endif #if !defined(NO_HMAC) && defined(WOLFCOSE_MAC0_CREATE) && \ defined(WOLFCOSE_MAC0_VERIFY) - test_cose_mac0_verify_short_key_allowed(); + test_cose_mac0_verify_short_key_rejected(); #endif #if defined(HAVE_AESGCM) && defined(WOLFCOSE_ENCRYPT0_ENCRYPT) test_cose_encrypt0_key_alg_mismatch(); From c36b58eef1ff6ab87737480fbe51459ed58cd9c2 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 10:48:14 -0700 Subject: [PATCH 13/38] F-5231/F-5303 - Validate MAC payload arguments for Mac0 and Mac --- src/wolfcose.c | 19 +++++++++++++++---- tests/test_cose.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 42ac2e6..ab44aa6 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -5812,9 +5812,9 @@ int wc_CoseMac0_Create(const WOLFCOSE_KEY* key, int32_t alg, uint8_t isDetached; size_t unprotectedEntries; - /* Determine which payload to use for MAC. A zero-length inline - * payload (NULL, 0) is valid: it authenticates only the protected - * headers and external AAD. */ + /* Determine which payload to use for MAC. A caller wanting to authenticate + * an empty payload passes a non-NULL zero-length buffer; an all-NULL + * payload/detached pair is rejected below to match the other create APIs. */ if (detachedPayload != NULL) { macPayload = detachedPayload; macPayloadLen = detachedLen; @@ -5835,6 +5835,12 @@ int wc_CoseMac0_Create(const WOLFCOSE_KEY* key, int32_t alg, (payload != NULL) && (detachedPayload != NULL)) { ret = WOLFCOSE_E_INVALID_ARG; } + /* Require an explicit payload (inline or detached); do not silently MAC an + * omitted payload. */ + if ((ret == WOLFCOSE_SUCCESS) && + (payload == NULL) && (detachedPayload == NULL)) { + ret = WOLFCOSE_E_INVALID_ARG; + } if ((ret == WOLFCOSE_SUCCESS) && (macPayload == NULL) && (macPayloadLen > 0u)) { ret = WOLFCOSE_E_INVALID_ARG; @@ -7452,11 +7458,16 @@ int wc_CoseMac_Create(const WOLFCOSE_RECIPIENT* recipients, } } - /* Must have either payload or detached */ + /* Must have either payload or detached, and not both (the inline payload + * would be silently ignored). */ if ((ret == WOLFCOSE_SUCCESS) && (payload == NULL) && (detachedPayload == NULL)) { ret = WOLFCOSE_E_INVALID_ARG; } + if ((ret == WOLFCOSE_SUCCESS) && + (payload != NULL) && (detachedPayload != NULL)) { + ret = WOLFCOSE_E_INVALID_ARG; + } /* Get the payload to MAC */ if (ret == WOLFCOSE_SUCCESS) { diff --git a/tests/test_cose.c b/tests/test_cose.c index 3b64c0f..49d6786 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -1868,6 +1868,43 @@ static void test_cose_mac0_short_hmac_key(void) "mac0 short hmac key rejected"); } +static void test_cose_mac_payload_validation(void) +{ + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipients[1]; + uint8_t keyData[32] = {0}; + uint8_t payload[] = "mac payload"; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + uint8_t out[256]; + size_t outLen = 0; + int ret; + + TEST_LOG(" [Mac payload validation]\n"); + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, keyData, sizeof(keyData)); + + /* 5231: Mac0 with neither inline nor detached payload is rejected. */ + ret = wc_CoseMac0_Create(&key, WOLFCOSE_ALG_HMAC_256_256, + NULL, 0, NULL, 0, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen); + TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "mac0 omitted payload rejected"); + + /* 5303: Mac (multi) with both inline and detached payload is rejected. */ + recipients[0].algId = 0; + recipients[0].key = &key; + recipients[0].kid = NULL; + recipients[0].kidLen = 0; + ret = wc_CoseMac_Create(recipients, 1, WOLFCOSE_ALG_HMAC_256_256, + payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1, + NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen); + TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, + "mac both payloads rejected"); +} + #ifdef WOLFSSL_SHA384 static void test_cose_mac0_hmac384(void) { @@ -15165,6 +15202,7 @@ int test_cose(void) #if !defined(NO_HMAC) test_cose_mac0_hmac256(); test_cose_mac0_short_hmac_key(); + test_cose_mac_payload_validation(); test_cose_mac0_with_aad(); test_cose_mac0_detached(); test_cose_mac0_detached_with_aad(); From a24926922e4b13b2dfde6d498d704f22182866a3 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 10:54:04 -0700 Subject: [PATCH 14/38] F-5291 - Clear header outputs on verify and decrypt failure --- src/wolfcose.c | 30 +++++++++++++++++++++++++ tests/test_cose.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index ab44aa6..053e3e5 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -3968,6 +3968,12 @@ int wc_CoseSign1_Verify(WOLFCOSE_KEY* key, /* No action required */ } + /* On failure, clear the header so unauthenticated metadata is not exposed + * to callers that inspect hdr without strictly gating on the return code. */ + if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { + (void)XMEMSET(hdr, 0, sizeof(*hdr)); + } + /* Cleanup: always executed */ (void)wolfCose_ForceZero(hashBuf, sizeof(hashBuf)); if (scratch != NULL) { @@ -4862,6 +4868,12 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, /* No action required */ } + /* On failure, clear the header so unauthenticated metadata is not exposed + * to callers that inspect hdr without strictly gating on the return code. */ + if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { + (void)XMEMSET(hdr, 0, sizeof(*hdr)); + } + /* Cleanup: always executed */ (void)wolfCose_ForceZero(hashBuf, sizeof(hashBuf)); if (scratch != NULL) { @@ -5525,6 +5537,12 @@ int wc_CoseEncrypt0_Decrypt(WOLFCOSE_KEY* key, /* No action required */ } + /* On failure, clear the header so unauthenticated metadata is not exposed + * to callers that inspect hdr without strictly gating on the return code. */ + if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { + (void)XMEMSET(hdr, 0, sizeof(*hdr)); + } + /* Cleanup: always executed */ #if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) if (aesInited != 0) { @@ -6247,6 +6265,12 @@ int wc_CoseMac0_Verify(const WOLFCOSE_KEY* key, /* No action required */ } + /* On failure, clear the header so unauthenticated metadata is not exposed + * to callers that inspect hdr without strictly gating on the return code. */ + if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { + (void)XMEMSET(hdr, 0, sizeof(*hdr)); + } + /* Cleanup: always executed */ #ifndef NO_HMAC if (hmacInited != 0) { @@ -8019,6 +8043,12 @@ int wc_CoseMac_Verify(const WOLFCOSE_RECIPIENT* recipient, /* No action required */ } + /* On failure, clear the header so unauthenticated metadata is not exposed + * to callers that inspect hdr without strictly gating on the return code. */ + if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { + (void)XMEMSET(hdr, 0, sizeof(*hdr)); + } + return ret; } #endif /* WOLFCOSE_MAC_VERIFY */ diff --git a/tests/test_cose.c b/tests/test_cose.c index 49d6786..6b7ab13 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -6277,6 +6277,61 @@ static void test_cose_sign1_trailing_bytes(void) } } +static void test_cose_sign1_hdr_cleared_on_failure(void) +{ + WOLFCOSE_KEY key; + ecc_key eccKey; + WC_RNG rng; + int ret = 0; + int rngInited = 0; + int eccInited = 0; + uint8_t payload[] = "hdr clear payload"; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + uint8_t out[512]; + size_t outLen = 0; + const uint8_t* decPayload = NULL; + size_t decPayloadLen = 0; + WOLFCOSE_HDR hdr; + + TEST_LOG(" [Sign1 hdr cleared on failure]\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { TEST_ASSERT(0, "rng init"); } + if (ret == 0) { rngInited = 1; } + if (ret == 0) { + wc_ecc_init(&eccKey); + eccInited = 1; + ret = wc_ecc_make_key(&rng, 32, &eccKey); + if (ret != 0) { TEST_ASSERT(0, "keygen"); } + } + if (ret == 0) { + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetEcc(&key, WOLFCOSE_CRV_P256, &eccKey); + ret = wc_CoseSign1_Sign(&key, WOLFCOSE_ALG_ES256, + NULL, 0, payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); + TEST_ASSERT(ret == 0, "sign for hdr-clear test"); + } + if (ret == 0) { + int verifyRet; + if (outLen > 5u) { + out[outLen - 2] ^= 0x01; /* corrupt signature */ + } + memset(&hdr, 0xAB, sizeof(hdr)); + verifyRet = wc_CoseSign1_Verify(&key, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, &decPayload, &decPayloadLen); + TEST_ASSERT(verifyRet == WOLFCOSE_E_COSE_SIG_FAIL, "tampered verify fails"); + TEST_ASSERT(hdr.alg == 0 && hdr.kid == NULL && hdr.flags == 0u, + "hdr cleared after verify failure"); + } + + if (eccInited != 0) { (void)wc_ecc_free(&eccKey); } + if (rngInited != 0) { (void)wc_FreeRng(&rng); } +} + static void test_cose_sign1_tampered_payload_byte(void) { WOLFCOSE_KEY key; @@ -15303,6 +15358,7 @@ int test_cose(void) test_cose_sign1_tampered_payload_byte(); test_cose_sign1_truncated_sig(); test_cose_sign1_trailing_bytes(); + test_cose_sign1_hdr_cleared_on_failure(); #endif #ifdef HAVE_AESGCM test_cose_encrypt0_tampered_ct_byte(); From 96a81a892708c6837bd534012bb2cbc90ac2976c Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 10:56:00 -0700 Subject: [PATCH 15/38] F-5295 - Give wolfCose_BuildEncStructure internal linkage --- src/wolfcose.c | 2 +- src/wolfcose_internal.h | 22 ---------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 053e3e5..e350c91 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -2316,7 +2316,7 @@ int wolfCose_BuildToBeSignedMaced( * * [context, body_protected, external_aad] */ -int wolfCose_BuildEncStructure( +static int wolfCose_BuildEncStructure( const uint8_t* context, size_t contextLen, const uint8_t* bodyProtected, size_t bodyProtectedLen, const uint8_t* extAad, size_t extAadLen, diff --git a/src/wolfcose_internal.h b/src/wolfcose_internal.h index 0d3c142..7c3a9aa 100644 --- a/src/wolfcose_internal.h +++ b/src/wolfcose_internal.h @@ -243,28 +243,6 @@ WOLFCOSE_LOCAL int wolfCose_BuildToBeSignedMaced( uint8_t* scratch, size_t scratchSz, size_t* structLen); -/** - * \brief Build an Enc_structure for AEAD operations. - * - * Creates: [context, body_protected, external_aad] - * - * \param context Context string ("Encrypt0" or "Encrypt") - * \param contextLen Length of context string - * \param bodyProtected Serialized protected headers - * \param bodyProtectedLen Length of protected headers - * \param extAad External AAD (may be NULL) - * \param extAadLen Length of external AAD - * \param scratch Output buffer for structure - * \param scratchSz Size of output buffer - * \param structLen Output: bytes written - * \return WOLFCOSE_SUCCESS or error code - */ -WOLFCOSE_LOCAL int wolfCose_BuildEncStructure( - const uint8_t* context, size_t contextLen, - const uint8_t* bodyProtected, size_t bodyProtectedLen, - const uint8_t* extAad, size_t extAadLen, - uint8_t* scratch, size_t scratchSz, - size_t* structLen); /** * \brief Get AEAD key length for any COSE AEAD algorithm. From 874d68841234844d7c9046aef4e724caaa5fc9c5 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 10:59:42 -0700 Subject: [PATCH 16/38] F-5237/F-5368/F-5372/F-5244 - Fix trivial MISRA required-rule violations --- src/wolfcose.c | 2 +- tests/test_cose.c | 32 +++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index e350c91..15613da 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -49,7 +49,7 @@ #include "../tests/force_failure.h" /* Check if a forced failure is set; if so, consume it and set ret */ #define INJECT_FAILURE(failure_type, error_code) \ - if (wolfForceFailure_Check(failure_type) != 0) { \ + if (wolfForceFailure_Check((failure_type)) != 0) { \ ret = (error_code); \ } else #else diff --git a/tests/test_cose.c b/tests/test_cose.c index 6b7ab13..f084480 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -8575,7 +8575,7 @@ static void test_cbor_edge_cases(void) tinyCtx.buf = tiny; tinyCtx.bufSz = sizeof(tiny); tinyCtx.idx = 0; - ret = wc_CBOR_EncodeUint(&tinyCtx, 0xFFFFFFFF); /* needs 5 bytes */ + ret = wc_CBOR_EncodeUint(&tinyCtx, 0xFFFFFFFFu); /* needs 5 bytes */ TEST_ASSERT(ret == WOLFCOSE_E_BUFFER_TOO_SMALL, "encode uint buf small"); } @@ -11409,6 +11409,8 @@ static void test_internal_helpers(void) uint8_t sigBuf[64]; size_t sigLen = sizeof(sigBuf); int verified; + WC_RNG dummyRng; + ecc_key dummyKey; /* EccSignRaw with NULL parameters */ ret = wolfCose_EccSignRaw(NULL, 32, sigBuf, &sigLen, 32, NULL, NULL); @@ -11422,7 +11424,8 @@ static void test_internal_helpers(void) /* EccSignRaw with buffer too small */ sigLen = 10; /* Too small for 64-byte sig */ - ret = wolfCose_EccSignRaw(hash, 32, sigBuf, &sigLen, 32, (WC_RNG*)1, (ecc_key*)1); + ret = wolfCose_EccSignRaw(hash, 32, sigBuf, &sigLen, 32, &dummyRng, + &dummyKey); TEST_ASSERT(ret == WOLFCOSE_E_BUFFER_TOO_SMALL, "EccSignRaw buf small"); /* EccVerifyRaw with NULL parameters */ @@ -11436,7 +11439,8 @@ static void test_internal_helpers(void) TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "EccVerifyRaw NULL verified"); /* EccVerifyRaw with wrong signature length */ - ret = wolfCose_EccVerifyRaw(sigBuf, 63, hash, 32, 32, (ecc_key*)1, &verified); + ret = wolfCose_EccVerifyRaw(sigBuf, 63, hash, 32, 32, &dummyKey, + &verified); TEST_ASSERT(ret == WOLFCOSE_E_COSE_SIG_FAIL, "EccVerifyRaw bad sigLen"); } #endif @@ -11485,10 +11489,13 @@ static void test_internal_helpers(void) /* CBOR map with 17 entries: 0xB1 (map of 17) followed by dummy entries */ uint8_t bigMap[100]; size_t i, idx = 0; - bigMap[idx++] = 0xB1u; /* map(17) */ - for (i = 0; i < 17; i++) { - bigMap[idx++] = (uint8_t)(0x10 + i); /* label: 16+i */ - bigMap[idx++] = 0x00u; /* value: 0 */ + bigMap[idx] = 0xB1u; /* map(17) */ + idx++; + for (i = 0; i < 17u; i++) { + bigMap[idx] = (uint8_t)(0x10u + i); /* label: 16+i */ + idx++; + bigMap[idx] = 0x00u; /* value: 0 */ + idx++; } XMEMSET(&hdr, 0, sizeof(hdr)); ret = wolfCose_DecodeProtectedHdr(bigMap, idx, &hdr, &hdrState); @@ -11541,10 +11548,13 @@ static void test_internal_helpers(void) { uint8_t bigMap[100]; size_t i, idx = 0; - bigMap[idx++] = 0xB1; /* map(17) */ - for (i = 0; i < 17; i++) { - bigMap[idx++] = (uint8_t)(0x10 + i); - bigMap[idx++] = 0x00; + bigMap[idx] = 0xB1u; /* map(17) */ + idx++; + for (i = 0; i < 17u; i++) { + bigMap[idx] = (uint8_t)(0x10u + i); + idx++; + bigMap[idx] = 0x00u; + idx++; } ctx.cbuf = bigMap; ctx.bufSz = idx; From 582ba583d1226a4361d39f5a950537c8f06df95d Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:00:55 -0700 Subject: [PATCH 17/38] F-5235/F-5236/F-5239/F-5242/F-5243/F-5292/F-5293/F-5294/F-5370 - Document MISRA deviations --- docs/misra-deviations.md | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/misra-deviations.md diff --git a/docs/misra-deviations.md b/docs/misra-deviations.md new file mode 100644 index 0000000..40ebd3a --- /dev/null +++ b/docs/misra-deviations.md @@ -0,0 +1,70 @@ +# wolfCOSE MISRA C Deviations + +This file records deviations from MISRA C:2012 / C:2023 for code paths that the +scanners flag but that are intentional. The core library (`src/`, `include/`) +targets full compliance; the deviations below are scoped to test and example +code, or to documented design decisions. Mirror updates here to the +[MISRA Compliance wiki](https://github.com/wolfSSL/wolfCOSE/wiki/MISRA-Compliance). + +Scope note: `tests/` and `examples/` are not part of the shippable library and +are not constrained by the zero-allocation or strict-conformance rules that +apply to `src/` and `include/`. + +## Deviations + +### Rule 21.6 โ€” Standard I/O functions (`printf`/`fprintf`) +- **Where:** `tests/`, `examples/`. +- **Rationale:** Test harnesses and demonstration programs report PASS/FAIL and + human-readable status to the console. Library code under `src/` uses no + standard I/O. Not applicable to shippable code. + +### Rule 11.3 โ€” Cast between pointer-to-object types (string literal โ†’ `const uint8_t*`) +- **Where:** `tests/`, `examples/` (e.g. `(const uint8_t*)"IETF"`). +- **Rationale:** COSE byte-string inputs are conventionally written as string + literals in test vectors and demos. The cast is read-only and the data is + never modified. Library APIs take `const uint8_t*`. + +### Rule 21.15 โ€” `memcmp` pointed-to type compatibility +- **Where:** `tests/` (comparing decoded `uint8_t` payloads against string + literals). +- **Rationale:** Test-only equality checks of recovered payloads against + expected literals; both operands are byte data. + +### Rule 17.7 โ€” Unused return value +- **Where:** `tests/`, `examples/` (e.g. `wc_CoseKey_Init`, `wc_ecc_init` in + setup where failure is not the property under test). +- **Rationale:** Setup calls in tests/demos whose failure is not the assertion + target. Library code checks every return value. + +### Rule 10.4 โ€” Mixed essential type in `sizeof(array) - 1` +- **Where:** `examples/`, `tests/` (string-literal payload lengths). +- **Rationale:** `sizeof(literal) - 1` to drop the NUL terminator is a common, + well-understood idiom in demo/test payload setup. + +### Rule 15.6 โ€” Non-compound selection/iteration bodies +- **Where:** `examples/`, `tests/` (e.g. `if (demo_x() != 0) failures++;`). +- **Rationale:** Localized to test/demo control flow. Library code uses braces + throughout. + +### Rule 14.4 โ€” `do { ... } while (0)` controlling expression not Boolean +- **Where:** Test/demo assertion and macro wrappers. +- **Rationale:** The `do { } while (0)` single-evaluation macro idiom is the + standard, safest multi-statement macro form. + +### Rule 15.1 โ€” `goto` for cleanup +- **Where:** `tests/` (e.g. `goto cleanup;` in multi-key setup). +- **Rationale:** Test-only single-exit cleanup. New library code uses + fallthrough cleanup; pre-existing library `goto cleanup` follows local style. + +### Rule 19.2 โ€” `union` keyword +- **Where:** `WOLFCOSE_KEY.key` (public header). +- **Rationale:** Tagged union discriminated by `kty`/`crv`; only the member + matching the key type is accessed. A tagged union keeps the public key object + small for embedded/zero-allocation use, which is a core wolfCOSE goal. + +### Rule 1.2 โ€” Language extensions (visibility / builtins) +- **Where:** `include/wolfcose/visibility.h` (`__declspec`/`__attribute__`), + `examples/lifecycle_demo.c` (`__builtin_frame_address`). +- **Rationale:** Symbol visibility for shared-library builds requires + compiler-specific attributes; the demo stack marker is diagnostic only and + compiles to a no-op on non-GNU toolchains. From a27d1eb5d03e6bc501456cb01ccc3a89f09bc1e2 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:04:16 -0700 Subject: [PATCH 18/38] F-5238/F-5240/F-5369 - Fix DEMO_ASSERT parens, const cast, shared test header --- examples/encrypt0_demo.c | 2 +- examples/mac0_demo.c | 2 +- examples/sign1_demo.c | 2 +- tests/test_cbor.c | 2 ++ tests/test_cose.c | 10 +++++++++- tests/test_interop.c | 1 + tests/test_main.c | 9 +-------- tests/test_suite.h | 30 ++++++++++++++++++++++++++++++ 8 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 tests/test_suite.h diff --git a/examples/encrypt0_demo.c b/examples/encrypt0_demo.c index e6125f9..f237c67 100644 --- a/examples/encrypt0_demo.c +++ b/examples/encrypt0_demo.c @@ -29,7 +29,7 @@ #define DEMO_ASSERT(cond, msg) do { \ if (!(cond)) { \ - printf(" FAIL: %s\n", msg); \ + printf(" FAIL: %s\n", (msg)); \ return -1; \ } \ } while(0) diff --git a/examples/mac0_demo.c b/examples/mac0_demo.c index ef769db..358f78c 100644 --- a/examples/mac0_demo.c +++ b/examples/mac0_demo.c @@ -40,7 +40,7 @@ #define DEMO_ASSERT(cond, msg) do { \ if (!(cond)) { \ - printf(" FAIL: %s\n", msg); \ + printf(" FAIL: %s\n", (msg)); \ return -1; \ } \ } while(0) diff --git a/examples/sign1_demo.c b/examples/sign1_demo.c index d4d5644..95ae7b4 100644 --- a/examples/sign1_demo.c +++ b/examples/sign1_demo.c @@ -31,7 +31,7 @@ #define DEMO_ASSERT(cond, msg) do { \ if (!(cond)) { \ - printf(" FAIL: %s\n", msg); \ + printf(" FAIL: %s\n", (msg)); \ return -1; \ } \ } while(0) diff --git a/tests/test_cbor.c b/tests/test_cbor.c index 47bfdb2..b681401 100644 --- a/tests/test_cbor.c +++ b/tests/test_cbor.c @@ -40,6 +40,8 @@ #include #include +#include "test_suite.h" + static int g_failures = 0; #define TEST_ASSERT(cond, name) do { \ diff --git a/tests/test_cose.c b/tests/test_cose.c index f084480..84f36a8 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -40,6 +40,7 @@ #include #include "../src/wolfcose_internal.h" /* For testing internal helpers */ +#include "test_suite.h" #include #ifdef HAVE_ECC #include @@ -2804,13 +2805,20 @@ static void test_rfc_sign1_ecdsa_01(void) const uint8_t* decPayload = NULL; size_t decPayloadLen = 0; int ret; + byte keyX[32]; + byte keyY[32]; TEST_LOG(" [RFC ecdsa-sig-01 (ES256)]\n"); + /* Copy the const test vectors into mutable buffers; the import API takes + * non-const pointers. */ + XMEMCPY(keyX, tvKeyX, sizeof(keyX)); + XMEMCPY(keyY, tvKeyY, sizeof(keyY)); + /* Import known public key */ wc_ecc_init(&eccKey); ret = wc_ecc_import_unsigned(&eccKey, - (byte*)tvKeyX, (byte*)tvKeyY, NULL, ECC_SECP256R1); + keyX, keyY, NULL, ECC_SECP256R1); TEST_ASSERT(ret == 0, "rfc es256 key import"); if (ret != 0) { wc_ecc_free(&eccKey); return; } diff --git a/tests/test_interop.c b/tests/test_interop.c index d1a5225..dd5f86b 100644 --- a/tests/test_interop.c +++ b/tests/test_interop.c @@ -40,6 +40,7 @@ #include #include +#include "test_suite.h" #include #ifdef HAVE_ECC #include diff --git a/tests/test_main.c b/tests/test_main.c index 332d633..76bacc6 100644 --- a/tests/test_main.c +++ b/tests/test_main.c @@ -24,14 +24,7 @@ #include -/* Defined in test_cbor.c */ -int test_cbor(void); - -/* Defined in test_cose.c */ -int test_cose(void); - -/* Defined in test_interop.c */ -int test_interop(void); +#include "test_suite.h" int main(void) { diff --git a/tests/test_suite.h b/tests/test_suite.h new file mode 100644 index 0000000..bbdd548 --- /dev/null +++ b/tests/test_suite.h @@ -0,0 +1,30 @@ +/* test_suite.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfCOSE. + * + * wolfCOSE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfCOSE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef WOLFCOSE_TEST_SUITE_H +#define WOLFCOSE_TEST_SUITE_H + +/* Shared prototypes for the test entry points so a compatible declaration is + * visible at each definition (MISRA C:2023 Rule 8.4). */ +int test_cbor(void); +int test_cose(void); +int test_interop(void); + +#endif /* WOLFCOSE_TEST_SUITE_H */ From 3126d0a11c579e02bfa87f7167e3bdd021f9715e Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:07:05 -0700 Subject: [PATCH 19/38] F-5299 - Document detached COSE_Encrypt unsupported and fix misleading test --- include/wolfcose/wolfcose.h | 11 +++++++---- tests/test_cose.c | 10 ++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/wolfcose/wolfcose.h b/include/wolfcose/wolfcose.h index fa12151..fcbd429 100644 --- a/include/wolfcose/wolfcose.h +++ b/include/wolfcose/wolfcose.h @@ -1058,10 +1058,13 @@ WOLFCOSE_API int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, * \param contentAlgId Content encryption algorithm (A128GCM, etc). * \param iv Initialization vector. * \param ivLen IV length. - * \param payload Payload to encrypt (NULL if detached). - * \param payloadLen Payload length (0 if detached). - * \param detachedPayload Detached payload for encryption (NULL if attached). - * \param detachedLen Detached payload length. + * \param payload Payload to encrypt (inline). + * \param payloadLen Payload length. + * \param detachedPayload Not supported for multi-recipient COSE_Encrypt; + * must be NULL. A non-NULL value returns + * WOLFCOSE_E_UNSUPPORTED. See wolfSSL/wolfCOSE + * issue tracker for detached-create support. + * \param detachedLen Must be 0. * \param extAad External additional authenticated data (NULL if none). * \param extAadLen External AAD length. * \param scratch Working buffer for Enc_structure. diff --git a/tests/test_cose.c b/tests/test_cose.c index 84f36a8..a647976 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -14485,6 +14485,7 @@ static void test_multi_encrypt_with_detached(void) 0x64, 0x6D, 0x07, 0xDB, 0xB5, 0x33, 0x56, 0x6E }; uint8_t payload[32] = "Test multi-encrypt detached"; + uint8_t iv[12] = {0}; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[512]; size_t outLen = 0; @@ -14504,17 +14505,18 @@ static void test_multi_encrypt_with_detached(void) recipients[0].kid = NULL; recipients[0].kidLen = 0; - /* Multi-encrypt with detached payload (pass payload in detached slot) */ + /* Detached create is not supported for multi-recipient COSE_Encrypt; with a + * valid IV the call must report WOLFCOSE_E_UNSUPPORTED rather than silently + * succeeding. */ ret = wc_CoseEncrypt_Encrypt(recipients, 1, WOLFCOSE_ALG_A128GCM, - NULL, 0, /* No separate IV - let API generate */ + iv, sizeof(iv), NULL, 0, /* NULL attached payload */ payload, sizeof(payload), /* detached payload */ NULL, 0, /* no AAD */ scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); - /* May succeed or fail depending on detached support */ - TEST_ASSERT(ret == WOLFCOSE_SUCCESS || ret < 0, "multi encrypt detached"); + TEST_ASSERT(ret == WOLFCOSE_E_UNSUPPORTED, "multi encrypt detached unsupported"); (void)wc_FreeRng(&rng); } From bec1ad82a2a6837ba6f0c6634ddee47aed59a011 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:08:25 -0700 Subject: [PATCH 20/38] F-5228/F-5229/F-5230/F-5290 - Document AEAD nonce, short-tag, and alg-pinning guidance --- include/wolfcose/wolfcose.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/wolfcose/wolfcose.h b/include/wolfcose/wolfcose.h index fcbd429..6bebc59 100644 --- a/include/wolfcose/wolfcose.h +++ b/include/wolfcose/wolfcose.h @@ -304,6 +304,22 @@ extern "C" { #define WOLFCOSE_HDR_PARTIAL_IV 6 #define WOLFCOSE_HDR_EPHEMERAL_KEY (-1) /* Ephemeral COSE_Key for ECDH */ +/* + * Security considerations for the algorithms below: + * + * - Nonce/IV uniqueness: the AEAD encrypt APIs take a caller-supplied IV and + * only validate its length. Reusing an (key, IV) pair with AES-GCM, + * AES-CCM, or ChaCha20-Poly1305 breaks confidentiality and/or integrity. + * Callers MUST supply a unique IV per encryption (e.g. from wc_RNG). + * - 64-bit authentication tags: the AES-CCM *_64_* and AES-MAC *_64 + * algorithms produce 8-byte tags with much lower forgery resistance than + * their 128-bit counterparts. They exist for constrained interop only; + * prefer the 128-bit-tag variants unless a peer requires otherwise. + * - Algorithm pinning: verify/decrypt dispatch on the message algorithm. + * To avoid algorithm-confusion, set key->alg (or recipient->algId) so the + * library enforces the expected algorithm rather than trusting the message. + */ + /* Algorithms */ #define WOLFCOSE_ALG_UNSET ((int32_t)0) #define WOLFCOSE_ALG_ES256 (-7) From a987a6bce85d43c544b2a0edc913dfed3edf1a35 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:19:09 -0700 Subject: [PATCH 21/38] F-5250/F-5302 - Reject lengths that would truncate to word32 --- src/wolfcose.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index 15613da..38c3e60 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -637,6 +637,19 @@ static int wolfCose_InInt32Range(int64_t val) return ((val >= (int64_t)INT32_MIN) && (val <= (int64_t)INT32_MAX)) ? 1 : 0; } +/* wolfCrypt message/AAD/structure lengths are word32. Reject size_t inputs + * that would truncate when cast. Only meaningful where size_t is wider than + * 32 bits; on 32-bit targets every size_t already fits. */ +static int wolfCose_LenFitsW32(size_t len) +{ +#if SIZE_MAX > 0xFFFFFFFFu + return (len <= 0xFFFFFFFFu) ? 1 : 0; +#else + (void)len; + return 1; +#endif +} + /* Map a COSE header/key label to a fast-path tracking bit. Labels outside * the small known range fall back to the slower extra-label array. */ static uint32_t wolfCose_LabelBit(int64_t label) @@ -2304,6 +2317,10 @@ int wolfCose_BuildToBeSignedMaced( if (ret == WOLFCOSE_SUCCESS) { ret = wc_CBOR_EncodeBstr(&ctx, payload, payloadLen); } + /* The structure length is later passed to wolfCrypt as word32. */ + if ((ret == WOLFCOSE_SUCCESS) && (wolfCose_LenFitsW32(ctx.idx) == 0)) { + ret = WOLFCOSE_E_INVALID_ARG; + } if (ret == WOLFCOSE_SUCCESS) { *structLen = ctx.idx; } @@ -2346,6 +2363,10 @@ static int wolfCose_BuildEncStructure( ret = wc_CBOR_EncodeBstr(&ctx, extAad, (extAad != NULL) ? extAadLen : 0u); } + /* The structure length is later passed to wolfCrypt as word32. */ + if ((ret == WOLFCOSE_SUCCESS) && (wolfCose_LenFitsW32(ctx.idx) == 0)) { + ret = WOLFCOSE_E_INVALID_ARG; + } if (ret == WOLFCOSE_SUCCESS) { *structLen = ctx.idx; } @@ -4947,6 +4968,13 @@ int wc_CoseEncrypt0_Encrypt(WOLFCOSE_KEY* key, int32_t alg, ret = WOLFCOSE_E_INVALID_ARG; } + /* Plaintext length is passed to the AEAD primitive as word32. */ + if ((ret == WOLFCOSE_SUCCESS) && + ((wolfCose_LenFitsW32(payloadLen) == 0) || + (wolfCose_LenFitsW32(detachedSz) == 0))) { + ret = WOLFCOSE_E_INVALID_ARG; + } + if ((ret == WOLFCOSE_SUCCESS) && (key->kty != WOLFCOSE_KTY_SYMMETRIC)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } @@ -5304,6 +5332,14 @@ int wc_CoseEncrypt0_Decrypt(WOLFCOSE_KEY* key, ret = WOLFCOSE_E_INVALID_ARG; } + /* Ciphertext length (<= inSz / detachedCtLen) is passed to the AEAD + * primitive as word32. */ + if ((ret == WOLFCOSE_SUCCESS) && + ((wolfCose_LenFitsW32(inSz) == 0) || + (wolfCose_LenFitsW32(detachedCtLen) == 0))) { + ret = WOLFCOSE_E_INVALID_ARG; + } + if ((ret == WOLFCOSE_SUCCESS) && (key->kty != WOLFCOSE_KTY_SYMMETRIC)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } @@ -6394,6 +6430,13 @@ int wc_CoseEncrypt_Encrypt(const WOLFCOSE_RECIPIENT* recipients, ret = WOLFCOSE_E_INVALID_ARG; } + /* Plaintext length is passed to the AEAD primitive as word32. */ + if ((ret == WOLFCOSE_SUCCESS) && + ((wolfCose_LenFitsW32(payloadLen) == 0) || + (wolfCose_LenFitsW32(detachedLen) == 0))) { + ret = WOLFCOSE_E_INVALID_ARG; + } + /* Reject inconsistent (kid, kidLen) per recipient to avoid silently * dropping the identifier. */ if (ret == WOLFCOSE_SUCCESS) { @@ -6920,6 +6963,14 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, ret = WOLFCOSE_E_INVALID_ARG; } + /* Ciphertext length (<= inSz / detachedCtLen) is passed to the AEAD + * primitive as word32. */ + if ((ret == WOLFCOSE_SUCCESS) && + ((wolfCose_LenFitsW32(inSz) == 0) || + (wolfCose_LenFitsW32(detachedCtLen) == 0))) { + ret = WOLFCOSE_E_INVALID_ARG; + } + if (ret == WOLFCOSE_SUCCESS) { (void)XMEMSET(hdr, 0, sizeof(*hdr)); ctx.cbuf = in; From be48cb29e76cc38a585e835c91ca8b67bddca80d Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:20:51 -0700 Subject: [PATCH 22/38] F-5217/F-5286/F-5287 - Document recipient encoding analysis (empty-bstr correct; KW alg RFC-legal) --- docs/cose-encoding-notes.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/cose-encoding-notes.md diff --git a/docs/cose-encoding-notes.md b/docs/cose-encoding-notes.md new file mode 100644 index 0000000..2bc014f --- /dev/null +++ b/docs/cose-encoding-notes.md @@ -0,0 +1,33 @@ +# wolfCOSE Encoding Notes (analyzed findings) + +Records the rationale for two recipient-encoding findings that were analyzed +against RFC 9052 and intentionally left as-is or deferred. + +## Recipient ciphertext for direct / ECDH-ES / direct key-wrap: empty bstr (not nil) + +For recipients that carry no encrypted key (direct key `-6`, ECDH-ES direct, +and direct-mode entries), wolfCOSE encodes the `COSE_recipient` ciphertext +field as a zero-length byte string (`h''`). + +This is correct and intentional. RFC 9052 Appendix C.3.1 (the "Direct ECDH" +example) and the cose-wg `Examples` repository both encode the recipient +ciphertext as empty `bstr` (`h''`), not the CBOR `nil` simple value. Changing +to `nil` would diverge from the RFC's own test vectors and break interop, so it +is deliberately not done. + +## AES Key Wrap recipient: `alg` placement + +wolfCOSE currently encodes the key-wrap algorithm in the recipient *protected* +header. RFC 9052 Section 3 permits a header parameter to appear in either the +protected or unprotected bucket, so this is conformant. For AES Key Wrap the +recipient headers are not cryptographically bound in either bucket, so there is +no security difference. + +The canonical RFC/cose-wg examples place the key-wrap `alg` in the *unprotected* +bucket with a zero-length protected header. Aligning with that canonical form +is a worthwhile interoperability enhancement, but it requires reordering the +multi-recipient decrypt path (the algorithm is currently classified from the +protected header before the unprotected map is decoded). Because the current +encoding is already RFC-legal, that change is deferred to a dedicated, reviewed +update rather than bundled here. A robust implementation should accept `alg` +from either bucket on decode (Postel's law) when it is made. From 7fdfb3d002ae313793546c8e7e4120f5218dfdb8 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:22:31 -0700 Subject: [PATCH 23/38] F-5297 - Add CBOR INT64_MAX/INT64_MIN boundary decode tests --- tests/test_cbor.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_cbor.c b/tests/test_cbor.c index b681401..96d626f 100644 --- a/tests/test_cbor.c +++ b/tests/test_cbor.c @@ -302,6 +302,20 @@ static void test_cbor_decode_vectors(void) ret = wc_CBOR_DecodeInt(&ctx, &ival); TEST_ASSERT(ret == 0 && ival == 100, "decode int +100"); } + /* Boundary: unsigned INT64_MAX (0x1B 7F FF ...) decodes exactly. */ + { uint8_t in[] = {0x1B, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF}; + ctx.cbuf = in; ctx.bufSz = sizeof(in); ctx.idx = 0; + ret = wc_CBOR_DecodeInt(&ctx, &ival); + TEST_ASSERT(ret == 0 && ival == INT64_MAX, "decode INT64_MAX"); } + + /* Boundary: negint -1 - INT64_MAX == INT64_MIN. */ + { uint8_t in[] = {0x3B, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF}; + ctx.cbuf = in; ctx.bufSz = sizeof(in); ctx.idx = 0; + ret = wc_CBOR_DecodeInt(&ctx, &ival); + TEST_ASSERT(ret == 0 && ival == INT64_MIN, "decode INT64_MIN"); } + /* bstr empty */ { uint8_t in[] = {0x40}; ctx.cbuf = in; ctx.bufSz = sizeof(in); ctx.idx = 0; From df40fd78a890680cda247c59d26eb2001463cc15 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:24:02 -0700 Subject: [PATCH 24/38] F-5376 - Add empty inline-payload Mac0 roundtrip test --- tests/test_cose.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/test_cose.c b/tests/test_cose.c index a647976..2b863cd 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -1906,6 +1906,42 @@ static void test_cose_mac_payload_validation(void) "mac both payloads rejected"); } +static void test_cose_mac0_empty_inline_payload(void) +{ + WOLFCOSE_KEY key; + uint8_t keyData[32] = {0}; + static const uint8_t empty[1] = {0}; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + uint8_t out[256]; + size_t outLen = 0; + const uint8_t* decPayload = NULL; + size_t decPayloadLen = 1; + WOLFCOSE_HDR hdr; + int ret; + + TEST_LOG(" [Mac0 empty inline payload]\n"); + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, keyData, sizeof(keyData)); + + /* 5376: a non-NULL zero-length inline payload must round-trip. */ + ret = wc_CoseMac0_Create(&key, WOLFCOSE_ALG_HMAC_256_256, + NULL, 0, empty, 0, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen); + TEST_ASSERT(ret == 0 && outLen > 0, "mac0 empty payload create"); + + memset(&hdr, 0, sizeof(hdr)); + ret = wc_CoseMac0_Verify(&key, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, &decPayload, &decPayloadLen); + TEST_ASSERT(ret == 0, "mac0 empty payload verify"); + TEST_ASSERT(decPayloadLen == 0u, "mac0 empty payload len"); + TEST_ASSERT((hdr.flags & WOLFCOSE_HDR_FLAG_DETACHED) == 0u, + "mac0 empty payload not detached"); +} + #ifdef WOLFSSL_SHA384 static void test_cose_mac0_hmac384(void) { @@ -15278,6 +15314,7 @@ int test_cose(void) test_cose_mac0_hmac256(); test_cose_mac0_short_hmac_key(); test_cose_mac_payload_validation(); + test_cose_mac0_empty_inline_payload(); test_cose_mac0_with_aad(); test_cose_mac0_detached(); test_cose_mac0_detached_with_aad(); From f812f49855fe55bdf42a9f3298998fea456a1006 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:25:38 -0700 Subject: [PATCH 25/38] F-5232/F-5233/F-5234 - Assert AEAD/MAC structure context bytes --- tests/test_cose.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_cose.c b/tests/test_cose.c index 2b863cd..136a4ac 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -9984,6 +9984,32 @@ static void test_cose_build_sig_structure_context(void) TEST_ASSERT(scratch[1] == 0x64u, "Mac0 tstr(4) header"); TEST_ASSERT(memcmp(&scratch[2], "MAC0", 4) == 0, "Mac0 context bytes"); + + /* Mac multi-recipient path: array(4), tstr(3) "MAC" (F-5234). */ + ret = wolfCose_BuildToBeSignedMaced( + WOLFCOSE_CTX_MAC, sizeof(WOLFCOSE_CTX_MAC), + protectedHdr, sizeof(protectedHdr), + NULL, 0, + NULL, 0, + payload, sizeof(payload), + scratch, sizeof(scratch), &structLen); + TEST_ASSERT(ret == WOLFCOSE_SUCCESS, + "BuildToBeSignedMaced Mac ok"); + TEST_ASSERT(scratch[0] == 0x84u, "Mac array(4) header"); + TEST_ASSERT(scratch[1] == 0x63u, "Mac tstr(3) header"); + TEST_ASSERT(memcmp(&scratch[2], "MAC", 3) == 0, "Mac context bytes"); + + /* AEAD Enc_structure contexts are AAD inputs; assert the context constants + * directly so a byte mutation is detected (F-5232, F-5233). */ + TEST_ASSERT(sizeof(WOLFCOSE_CTX_ENCRYPT0) == 8u && + memcmp(WOLFCOSE_CTX_ENCRYPT0, "Encrypt0", 8) == 0, + "Encrypt0 context bytes"); + TEST_ASSERT(sizeof(WOLFCOSE_CTX_ENCRYPT) == 7u && + memcmp(WOLFCOSE_CTX_ENCRYPT, "Encrypt", 7) == 0, + "Encrypt context bytes"); + TEST_ASSERT(sizeof(WOLFCOSE_CTX_MAC) == 3u && + memcmp(WOLFCOSE_CTX_MAC, "MAC", 3) == 0, + "MAC context constant bytes"); } /* ----- Coverage boost: exercise multi-signer / multi-recipient paths From e99e7152bf433d1b66003f6f7575c0e3685f091c Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:27:27 -0700 Subject: [PATCH 26/38] F-5296 - Add ECDH-ES recipient-protected-to-CEK binding test --- tests/test_cose.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/tests/test_cose.c b/tests/test_cose.c index 136a4ac..134cb92 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -15208,6 +15208,88 @@ static void test_ecdh_es_multi_recipient_decrypt_rejected(void) (void)wc_ecc_free(&recipientEcc); (void)wc_FreeRng(&rng); } + +static void test_ecdh_es_recipient_protected_bound(void) +{ + WOLFCOSE_KEY recipientKey; + WOLFCOSE_RECIPIENT recipient; + WOLFCOSE_HDR hdr; + ecc_key recipientEcc; + WC_RNG rng; + WOLFCOSE_CBOR_CTX ctx; + int ret; + size_t n = 0; + size_t i; + uint64_t tag = 0; + const uint8_t* recipProt = NULL; + size_t recipProtLen = 0; + size_t protOff = 0; + uint8_t out[512]; + size_t outLen = 0; + uint8_t scratch[256]; + uint8_t plaintext[64]; + size_t plaintextLen = 0; + const uint8_t payload[] = "ECDH-ES kdf bind"; + uint8_t iv[12] = {1,2,3,4,5,6,7,8,9,10,11,12}; + + TEST_LOG(" [ECDH-ES recipient protected bound to CEK]\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { TEST_ASSERT(0, "rng init"); return; } + wc_ecc_init(&recipientEcc); + wc_ecc_make_key(&rng, 32, &recipientEcc); + (void)wc_CoseKey_Init(&recipientKey); + (void)wc_CoseKey_SetEcc(&recipientKey, WOLFCOSE_CRV_P256, &recipientEcc); + recipientKey.hasPrivate = 0; + recipient.algId = WOLFCOSE_ALG_ECDH_ES_HKDF_256; + recipient.key = &recipientKey; + recipient.kid = NULL; + recipient.kidLen = 0; + + ret = wc_CoseEncrypt_Encrypt(&recipient, 1, WOLFCOSE_ALG_A128GCM, + iv, sizeof(iv), payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); + TEST_ASSERT(ret == 0, "ecdh-es bind encrypt"); + + /* Locate the recipient protected header bstr content. */ + if (ret == 0) { + ctx.cbuf = out; + ctx.bufSz = outLen; + ctx.idx = 0; + if (wc_CBOR_PeekType(&ctx) == WOLFCOSE_CBOR_TAG) { + ret = wc_CBOR_DecodeTag(&ctx, &tag); + } + if (ret == 0) { ret = wc_CBOR_DecodeArrayStart(&ctx, &n); } + for (i = 0; (ret == 0) && (i < 3u); i++) { + ret = wc_CBOR_Skip(&ctx); /* body protected/unprotected/ct */ + } + if (ret == 0) { ret = wc_CBOR_DecodeArrayStart(&ctx, &n); } /* recips */ + if (ret == 0) { ret = wc_CBOR_DecodeArrayStart(&ctx, &n); } /* recip */ + if (ret == 0) { ret = wc_CBOR_DecodeBstr(&ctx, &recipProt, + &recipProtLen); } + TEST_ASSERT((ret == 0) && (recipProtLen > 0u), + "located recipient protected"); + protOff = (size_t)(recipProt - out); + } + + /* Flip a byte in the recipient protected header: it feeds the KDF + * context, so the receiver derives a different CEK and decrypt fails. */ + if ((ret == 0) && (recipProtLen > 0u)) { + recipientKey.hasPrivate = 1; + out[protOff] ^= 0xFFu; + memset(&hdr, 0, sizeof(hdr)); + ret = wc_CoseEncrypt_Decrypt(&recipient, 0, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), &hdr, + plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret != 0, "tampered recipient protected fails decrypt"); + } + + wc_CoseKey_Free(&recipientKey); + (void)wc_ecc_free(&recipientEcc); + (void)wc_FreeRng(&rng); +} #endif /* Test #9: wc_CoseSign_Verify rejects wrong array count */ @@ -15825,6 +15907,7 @@ int test_cose(void) defined(WOLFCOSE_ECDH_ES_DIRECT) && defined(HAVE_ECC) && defined(HAVE_HKDF) test_ecdh_es_multi_recipient_rejected(); test_ecdh_es_multi_recipient_decrypt_rejected(); + test_ecdh_es_recipient_protected_bound(); #endif /* Mock failure injection tests */ From 456589ba7c058b1caac5b7ad2dbdffd949a6d504 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:29:58 -0700 Subject: [PATCH 27/38] F-5248/F-5249 - Add per-recipient roundtrip tests for direct multi-recipient Encrypt/MAC --- tests/test_cose.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/tests/test_cose.c b/tests/test_cose.c index 134cb92..1d1e8b8 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -1942,6 +1942,51 @@ static void test_cose_mac0_empty_inline_payload(void) "mac0 empty payload not detached"); } +static void test_cose_mac_multi_per_recipient(void) +{ + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipients[2]; + WOLFCOSE_HDR hdr; + uint8_t keyData[32] = {0}; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + uint8_t out[256]; + const uint8_t* decPayload = NULL; + size_t decPayloadLen = 0; + size_t outLen = 0; + int ret; + size_t r; + const uint8_t payload[] = "multi recipient mac"; + + TEST_LOG(" [Mac multi per-recipient roundtrip]\n"); + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, keyData, sizeof(keyData)); + for (r = 0; r < 2u; r++) { + recipients[r].algId = 0; /* direct: shared MAC key */ + recipients[r].key = &key; + recipients[r].kid = NULL; + recipients[r].kidLen = 0; + } + + ret = wc_CoseMac_Create(recipients, 2, WOLFCOSE_ALG_HMAC_256_256, + payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen); + TEST_ASSERT(ret == 0, "multi mac create"); + + /* Every encoded recipient must verify. */ + for (r = 0; (ret == 0) && (r < 2u); r++) { + memset(&hdr, 0, sizeof(hdr)); + ret = wc_CoseMac_Verify(&recipients[r], r, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, &decPayload, &decPayloadLen); + TEST_ASSERT(ret == 0, "multi recipient mac verify"); + TEST_ASSERT(decPayloadLen == sizeof(payload) - 1, + "multi recipient mac payload len"); + } +} + #ifdef WOLFSSL_SHA384 static void test_cose_mac0_hmac384(void) { @@ -9387,6 +9432,54 @@ static void test_cose_encrypt_recipient_alg_checks(void) TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, "recipient algId policy enforced"); } + +static void test_cose_encrypt_multi_per_recipient(void) +{ + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipients[2]; + WOLFCOSE_HDR hdr; + uint8_t cek[16] = {0}; + uint8_t iv[12] = {0}; + uint8_t scratch[512]; + uint8_t out[256]; + uint8_t plaintext[64]; + size_t outLen = 0; + size_t plaintextLen = 0; + int ret; + size_t r; + const uint8_t payload[] = "multi recipient direct"; + + TEST_LOG(" [Encrypt multi per-recipient roundtrip]\n"); + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, cek, sizeof(cek)); + for (r = 0; r < 2u; r++) { + recipients[r].algId = WOLFCOSE_ALG_DIRECT; + recipients[r].key = &key; /* direct: shared CEK */ + recipients[r].kid = NULL; + recipients[r].kidLen = 0; + } + + ret = wc_CoseEncrypt_Encrypt(recipients, 2, WOLFCOSE_ALG_A128GCM, + iv, sizeof(iv), payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen, NULL); + TEST_ASSERT(ret == 0, "multi encrypt create"); + + /* Every encoded recipient must decrypt to the original plaintext. */ + for (r = 0; (ret == 0) && (r < 2u); r++) { + memset(&hdr, 0, sizeof(hdr)); + plaintextLen = 0; + ret = wc_CoseEncrypt_Decrypt(&recipients[r], r, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), &hdr, + plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret == 0, "multi recipient decrypt"); + TEST_ASSERT((plaintextLen == sizeof(payload) - 1) && + (memcmp(plaintext, payload, plaintextLen) == 0), + "multi recipient payload match"); + } +} #endif /* HAVE_AESGCM */ static void test_cose_protected_hdr_content_type(void) @@ -15423,6 +15516,7 @@ int test_cose(void) test_cose_mac0_short_hmac_key(); test_cose_mac_payload_validation(); test_cose_mac0_empty_inline_payload(); + test_cose_mac_multi_per_recipient(); test_cose_mac0_with_aad(); test_cose_mac0_detached(); test_cose_mac0_detached_with_aad(); @@ -15597,6 +15691,7 @@ int test_cose(void) test_cose_encrypt_dup_recipient_unprot_hdr(); test_cose_encrypt_direct_empty_protected(); test_cose_encrypt_recipient_alg_checks(); + test_cose_encrypt_multi_per_recipient(); #endif test_cose_protected_hdr_content_type(); test_cose_protected_hdr_tstr_label(); From f9760eee9e405b3ee7e602b0ed81c31d1925b99a Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:31:47 -0700 Subject: [PATCH 28/38] F-5300/F-5375 - Cover all eight AES-CCM parameter sets (Encrypt0 and multi) --- tests/test_cose.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/tests/test_cose.c b/tests/test_cose.c index 1d1e8b8..39cad49 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -1213,6 +1213,89 @@ static void test_cose_encrypt0_aes_ccm(void) memcmp(plaintext, payload, plaintextLen) == 0, "enc0 ccm-64-128-128 payload match"); } + +static void test_cose_aes_ccm_all_params(void) +{ + /* alg, keyLen, nonceLen for all eight AES-CCM parameter sets. */ + static const struct { + int32_t alg; + size_t keyLen; + size_t nonceLen; + } ccm[] = { + { WOLFCOSE_ALG_AES_CCM_16_64_128, 16u, 13u }, + { WOLFCOSE_ALG_AES_CCM_16_64_256, 32u, 13u }, + { WOLFCOSE_ALG_AES_CCM_64_64_128, 16u, 7u }, + { WOLFCOSE_ALG_AES_CCM_64_64_256, 32u, 7u }, + { WOLFCOSE_ALG_AES_CCM_16_128_128, 16u, 13u }, + { WOLFCOSE_ALG_AES_CCM_16_128_256, 32u, 13u }, + { WOLFCOSE_ALG_AES_CCM_64_128_128, 16u, 7u }, + { WOLFCOSE_ALG_AES_CCM_64_128_256, 32u, 7u } + }; + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipient; + WOLFCOSE_HDR hdr; + uint8_t keyData[32]; + uint8_t nonce[13]; + uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; + uint8_t out[256]; + uint8_t plaintext[64]; + size_t outLen; + size_t plaintextLen; + const uint8_t payload[] = "ccm param sweep"; + size_t i; + int ret; + + TEST_LOG(" [AES-CCM all parameter sets]\n"); + + for (i = 0; i < sizeof(keyData); i++) { keyData[i] = (uint8_t)(i + 1u); } + for (i = 0; i < sizeof(nonce); i++) { nonce[i] = (uint8_t)(0xA0u + i); } + + for (i = 0; i < (sizeof(ccm) / sizeof(ccm[0])); i++) { + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetSymmetric(&key, keyData, ccm[i].keyLen); + + /* Encrypt0 (F-5300) */ + outLen = 0; + ret = wc_CoseEncrypt0_Encrypt(&key, ccm[i].alg, + nonce, ccm[i].nonceLen, payload, sizeof(payload) - 1, + NULL, 0, NULL, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen); + TEST_ASSERT(ret == 0 && outLen > 0, "ccm sweep enc0 encrypt"); + plaintextLen = 0; + memset(&hdr, 0, sizeof(hdr)); + ret = wc_CoseEncrypt0_Decrypt(&key, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), &hdr, + plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret == 0 && + plaintextLen == sizeof(payload) - 1 && + memcmp(plaintext, payload, plaintextLen) == 0, + "ccm sweep enc0 roundtrip"); + TEST_ASSERT(hdr.alg == ccm[i].alg, "ccm sweep enc0 hdr alg"); + + /* Multi-recipient COSE_Encrypt direct (F-5375) */ + recipient.algId = WOLFCOSE_ALG_DIRECT; + recipient.key = &key; + recipient.kid = NULL; + recipient.kidLen = 0; + outLen = 0; + ret = wc_CoseEncrypt_Encrypt(&recipient, 1, ccm[i].alg, + nonce, ccm[i].nonceLen, payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), out, sizeof(out), &outLen, NULL); + TEST_ASSERT(ret == 0 && outLen > 0, "ccm sweep multi encrypt"); + plaintextLen = 0; + memset(&hdr, 0, sizeof(hdr)); + ret = wc_CoseEncrypt_Decrypt(&recipient, 0, out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), &hdr, + plaintext, sizeof(plaintext), &plaintextLen); + TEST_ASSERT(ret == 0 && + plaintextLen == sizeof(payload) - 1 && + memcmp(plaintext, payload, plaintextLen) == 0, + "ccm sweep multi roundtrip"); + } +} #endif /* HAVE_AESCCM */ /* ----- COSE_Sign1 RSA-PSS tests ----- */ @@ -15493,6 +15576,7 @@ int test_cose(void) /* AES-CCM encryption tests */ #ifdef HAVE_AESCCM test_cose_encrypt0_aes_ccm(); + test_cose_aes_ccm_all_params(); #endif /* RSA-PSS signature tests */ From 9ada36d4b51227e146386ca1032221b9e0040c0f Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:34:20 -0700 Subject: [PATCH 29/38] F-5373 - Assert RSA private-key encode scrubs scratch tail past outLen --- tests/test_cose.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/test_cose.c b/tests/test_cose.c index 39cad49..1c9deba 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -1732,6 +1732,51 @@ static void test_cose_key_rsa(void) (void)wc_FreeRsaKey(&rsaKey); (void)wc_FreeRng(&rng); } + +static void test_cose_key_rsa_scratch_scrubbed(void) +{ + WOLFCOSE_KEY key; + RsaKey rsaKey; + WC_RNG rng; + uint8_t cbuf[2048]; + size_t cLen = 0; + size_t i; + int ret; + int leaked = 0; + + TEST_LOG(" [Key RSA scratch scrubbed]\n"); + + ret = wc_InitRng(&rng); + if (ret != 0) { TEST_ASSERT(0, "rng init"); return; } + ret = wc_InitRsaKey(&rsaKey, NULL); + if (ret != 0) { TEST_ASSERT(0, "rsa init"); wc_FreeRng(&rng); return; } + ret = wc_MakeRsaKey(&rsaKey, 2048, 65537, &rng); + TEST_ASSERT(ret == 0, "rsa keygen 2048"); + if (ret != 0) { wc_FreeRsaKey(&rsaKey); wc_FreeRng(&rng); return; } + + (void)wc_CoseKey_Init(&key); + (void)wc_CoseKey_SetRsa(&key, &rsaKey); + key.alg = WOLFCOSE_ALG_PS256; + + /* Encode a private RSA COSE_Key into a sentinel-filled buffer. The encoder + * exports e/n/p/q into the scratch tail past the encoded length and must + * scrub it; any leftover non-sentinel, non-zero byte is leaked key + * material (F-5373). */ + (void)XMEMSET(cbuf, 0xAAu, sizeof(cbuf)); + ret = wc_CoseKey_Encode(&key, cbuf, sizeof(cbuf), &cLen); + TEST_ASSERT(ret == 0 && cLen > 0, "rsa scrub encode"); + + for (i = cLen; (ret == 0) && (i < sizeof(cbuf)); i++) { + if ((cbuf[i] != 0xAAu) && (cbuf[i] != 0x00u)) { + leaked = 1; + } + } + TEST_ASSERT(leaked == 0, "rsa scratch tail scrubbed past outLen"); + + wc_CoseKey_Free(&key); + (void)wc_FreeRsaKey(&rsaKey); + (void)wc_FreeRng(&rng); +} #endif /* WC_RSA_PSS && WOLFSSL_KEY_GEN */ /* ----- COSE_Key Dilithium encode/decode round-trip ----- */ @@ -15533,6 +15578,7 @@ int test_cose(void) test_cose_key_symmetric(); #if defined(WC_RSA_PSS) && defined(WOLFSSL_KEY_GEN) test_cose_key_rsa(); + test_cose_key_rsa_scratch_scrubbed(); #endif #ifdef HAVE_DILITHIUM test_cose_key_dilithium("ML-DSA-44", WOLFCOSE_ALG_ML_DSA_44, 2); From e9c47ee3a5c88b49ca4b2a17e20f690ad82cba67 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:37:12 -0700 Subject: [PATCH 30/38] F-5374 - Reject non-preferred CBOR encodings per RFC 8949 deterministic rules --- src/wolfcose_cbor.c | 15 +++++++++++++++ tests/test_cbor.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/wolfcose_cbor.c b/src/wolfcose_cbor.c index af335c8..9d5ab98 100644 --- a/src/wolfcose_cbor.c +++ b/src/wolfcose_cbor.c @@ -218,6 +218,21 @@ int wolfCose_CBOR_DecodeHead(WOLFCOSE_CBOR_CTX* ctx, WOLFCOSE_CBOR_ITEM* item) ret = WOLFCOSE_E_CBOR_MALFORMED; } + /* RFC 8949 Section 4.2.1 (deterministic encoding, required for COSE): + * the argument must use the shortest additional-info form. This applies + * to every major type except simple/float (where AI 25/26/27 select + * float16/32/64 rather than encode an integer). */ + if ((ret == WOLFCOSE_SUCCESS) && + (item->majorType != WOLFCOSE_CBOR_SIMPLE)) { + if (((ai == WOLFCOSE_CBOR_AI_1BYTE) && (item->val < 24u)) || + ((ai == WOLFCOSE_CBOR_AI_2BYTE) && (item->val < 256u)) || + ((ai == WOLFCOSE_CBOR_AI_4BYTE) && (item->val < 0x10000u)) || + ((ai == WOLFCOSE_CBOR_AI_8BYTE) && + (item->val < 0x100000000ULL))) { + ret = WOLFCOSE_E_CBOR_MALFORMED; + } + } + /* Advance past bstr/tstr bytes using overflow-safe bounds. */ if (ret == WOLFCOSE_SUCCESS) { if ((item->majorType == WOLFCOSE_CBOR_BSTR) || diff --git a/tests/test_cbor.c b/tests/test_cbor.c index 96d626f..5fc867d 100644 --- a/tests/test_cbor.c +++ b/tests/test_cbor.c @@ -714,6 +714,51 @@ static void test_cbor_encode_idx_past_bufsz(void) "EncodeNull rejects idx past bufSz"); } +static void test_cbor_reject_non_preferred(void) +{ + /* RFC 8949 4.2.1: arguments must use the shortest form. Overlong encodings + * of small values must be rejected (F-5374). */ + WOLFCOSE_CBOR_CTX ctx; + uint64_t uval; + const uint8_t* data; + size_t dataLen; + size_t count; + int ret; + /* uint 0 encoded in 1 extra byte, uint 23 in 2, uint 255 in 4, etc. */ + uint8_t u1[] = {0x18, 0x00}; /* uint 0 overlong */ + uint8_t u2[] = {0x19, 0x00, 0x17}; /* uint 23 overlong */ + uint8_t u4[] = {0x1A, 0x00, 0x00, 0x00, 0x18}; /* uint 24 overlong */ + uint8_t u8[] = {0x1B, 0,0,0,0, 0,0,0x01,0x00}; /* uint 256 overlong */ + uint8_t bs[] = {0x58, 0x00}; /* empty bstr overlong */ + uint8_t arr[] = {0x98, 0x00}; /* array(0) overlong */ + + printf(" [Decode: reject non-preferred encodings]\n"); + + ctx.cbuf = u1; ctx.bufSz = sizeof(u1); ctx.idx = 0; + ret = wc_CBOR_DecodeUint(&ctx, &uval); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, "reject overlong uint 0"); + + ctx.cbuf = u2; ctx.bufSz = sizeof(u2); ctx.idx = 0; + ret = wc_CBOR_DecodeUint(&ctx, &uval); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, "reject overlong uint 23"); + + ctx.cbuf = u4; ctx.bufSz = sizeof(u4); ctx.idx = 0; + ret = wc_CBOR_DecodeUint(&ctx, &uval); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, "reject overlong uint 24"); + + ctx.cbuf = u8; ctx.bufSz = sizeof(u8); ctx.idx = 0; + ret = wc_CBOR_DecodeUint(&ctx, &uval); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, "reject overlong uint 256"); + + ctx.cbuf = bs; ctx.bufSz = sizeof(bs); ctx.idx = 0; + ret = wc_CBOR_DecodeBstr(&ctx, &data, &dataLen); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, "reject overlong bstr len"); + + ctx.cbuf = arr; ctx.bufSz = sizeof(arr); ctx.idx = 0; + ret = wc_CBOR_DecodeArrayStart(&ctx, &count); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, "reject overlong array len"); +} + /* ----- Error cases ----- */ static void test_cbor_errors(void) { @@ -905,6 +950,7 @@ int test_cbor(void) test_cbor_decode_simple_not_well_formed(); test_cbor_encode_bstr_null_with_len(); test_cbor_encode_idx_past_bufsz(); + test_cbor_reject_non_preferred(); test_cbor_errors(); test_cbor_negative_map_keys(); From 715f2c512422f1380fffffc92d47de1c38624dde Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 11:50:59 -0700 Subject: [PATCH 31/38] F-5291 - Also clear header on wc_CoseEncrypt_Decrypt failure (complete all six paths) --- src/wolfcose.c | 5 +++++ tests/test_cose.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/wolfcose.c b/src/wolfcose.c index 38c3e60..9aa0b7d 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -7445,6 +7445,11 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, if (plaintextLen != NULL) { *plaintextLen = 0u; } + /* Clear the header so unauthenticated metadata is not exposed to + * callers that inspect hdr without strictly gating on the return. */ + if (hdr != NULL) { + (void)XMEMSET(hdr, 0, sizeof(*hdr)); + } } else { *plaintextLen = payloadLen; diff --git a/tests/test_cose.c b/tests/test_cose.c index 1c9deba..201fd8c 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -9544,12 +9544,16 @@ static void test_cose_encrypt_recipient_alg_checks(void) /* 5377: unsupported recipient alg must be rejected, not treated direct. */ recipient.algId = 0; + memset(&hdr, 0xAB, sizeof(hdr)); ret = wc_CoseEncrypt_Decrypt(&recipient, 0, unsupported, sizeof(unsupported), NULL, 0, NULL, 0, scratch, sizeof(scratch), &hdr, plaintext, sizeof(plaintext), &plaintextLen); TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, "unsupported recipient alg rejected"); + /* 5291: hdr must be cleared on the failed multi-recipient decrypt. */ + TEST_ASSERT(hdr.alg == 0 && hdr.kid == NULL, + "encrypt decrypt clears hdr on failure"); /* 5367: caller key-wrap policy must reject a direct-mode message. */ recipient.algId = WOLFCOSE_ALG_A128KW; From 1b1278fa96680b5108c66f286467124caa746e0e Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 12:08:37 -0700 Subject: [PATCH 32/38] skoll: document decode-strictness, HMAC-min, and MAC payload API contract changes --- docs/cose-encoding-notes.md | 35 +++++++++++++++++++++++++++++++++++ include/wolfcose/wolfcose.h | 30 +++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/docs/cose-encoding-notes.md b/docs/cose-encoding-notes.md index 2bc014f..65883be 100644 --- a/docs/cose-encoding-notes.md +++ b/docs/cose-encoding-notes.md @@ -31,3 +31,38 @@ protected header before the unprotected map is decoded). Because the current encoding is already RFC-legal, that change is deferred to a dedicated, reviewed update rather than bundled here. A robust implementation should accept `alg` from either bucket on decode (Postel's law) when it is made. + +## Decode strictness (API contract) + +The hardening pass tightened several decode-side behaviors. These apply to all +public decode/verify/decrypt entry points (and, where noted, the generic +`wc_CBOR_Decode*` primitives), so integrators upgrading from older wolfCOSE +should be aware: + +- **Preferred (shortest-form) CBOR is required on decode.** Per RFC 8949 + Section 4.2.1 (deterministic encoding, which COSE mandates for + security-relevant structures), `wolfCose_CBOR_DecodeHead` rejects overlong + integer/length/tag arguments for every major type except simple/float. + Because it is the lowest-level primitive, this also applies to the generic + `wc_CBOR_Decode*` APIs. wolfCOSE's own encoder always emits shortest form, so + round-trips and RFC test vectors are unaffected; only non-preferred input + from lenient third-party encoders is now rejected with + `WOLFCOSE_E_CBOR_MALFORMED`. +- **`inSz` must equal the exact encoded object length.** Every verify/decrypt + API (and `wc_CoseKey_Decode`) now rejects trailing bytes after the COSE + object (RFC 8949 Section 5.3.1) with `WOLFCOSE_E_CBOR_MALFORMED`. Callers must + pass the precise message length, not a fixed receive-buffer capacity. +- **EC2 / ephemeral coordinates must be exactly the curve size.** Per RFC 9053 + Section 7.1.1, `x`/`y`/`d` (and ephemeral `x`/`y`) must equal the curve + coordinate size with leading zeros preserved; leading-zero-stripped (short) + coordinates are rejected with `WOLFCOSE_E_COSE_BAD_HDR`, including on + metadata-only EC2 key decode. +- **HMAC keys must meet the per-algorithm minimum.** HMAC-256/384/512 require a + key of at least 32/48/64 bytes (RFC 9053 Section 3.1); shorter keys are + rejected with `WOLFCOSE_E_COSE_KEY_TYPE`. Define + `WOLFCOSE_ALLOW_SHORT_HMAC_KEY` to restore the legacy accept-any-length + behavior. +- **MAC create requires an explicit payload.** `wc_CoseMac0_Create` / + `wc_CoseMac_Create` reject an omitted payload (`payload == NULL` and + `detachedPayload == NULL`) with `WOLFCOSE_E_INVALID_ARG`. To authenticate an + empty payload, pass a non-NULL zero-length buffer. diff --git a/include/wolfcose/wolfcose.h b/include/wolfcose/wolfcose.h index 6bebc59..801a6be 100644 --- a/include/wolfcose/wolfcose.h +++ b/include/wolfcose/wolfcose.h @@ -318,6 +318,14 @@ extern "C" { * - Algorithm pinning: verify/decrypt dispatch on the message algorithm. * To avoid algorithm-confusion, set key->alg (or recipient->algId) so the * library enforces the expected algorithm rather than trusting the message. + * - HMAC key length: HMAC-256/384/512 require a key of at least 32/48/64 + * bytes (RFC 9053 Section 3.1). Shorter keys are rejected with + * WOLFCOSE_E_COSE_KEY_TYPE unless WOLFCOSE_ALLOW_SHORT_HMAC_KEY is defined. + * - Strict decode: decoders require preferred (shortest-form) CBOR + * (RFC 8949 Section 4.2.1) across all entry points, and verify/decrypt + * APIs require inSz to be exactly the encoded object length (trailing bytes + * are rejected). EC2 coordinates must be exactly the curve size. See + * docs/cose-encoding-notes.md. */ /* Algorithms */ @@ -930,10 +938,15 @@ WOLFCOSE_API int wc_CoseEncrypt0_Decrypt(WOLFCOSE_KEY* key, * \param alg Algorithm (WOLFCOSE_ALG_HMAC_256_256). * \param kid Key identifier for unprotected header (NULL if none). * \param kidLen Key identifier length. - * \param payload Payload to authenticate (NULL if detached). - * \param payloadLen Payload length (0 if detached). - * \param detachedPayload Detached payload for MAC (NULL if attached). - * If non-NULL, payload is encoded as CBOR null. + * \param payload Inline payload to authenticate (NULL only when a + * detached payload is supplied). An omitted payload + * (payload and detachedPayload both NULL) is rejected; + * authenticate an empty payload with a non-NULL + * zero-length buffer. + * \param payloadLen Payload length (0 for an empty inline payload). + * \param detachedPayload Detached payload for MAC (NULL if attached). Must be + * NULL when payload is non-NULL. If non-NULL, payload + * is encoded as CBOR null. * \param detachedLen Detached payload length. * \param extAad External additional authenticated data (NULL if none). * \param extAadLen External AAD length. @@ -1149,9 +1162,12 @@ WOLFCOSE_API int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, * \param recipients Array of WOLFCOSE_RECIPIENT with keys. * \param recipientCount Number of recipients (must be >= 1). * \param macAlgId MAC algorithm (HMAC-256/256, etc). - * \param payload Payload to authenticate (NULL if detached). - * \param payloadLen Payload length (0 if detached). - * \param detachedPayload Detached payload for MAC (NULL if attached). + * \param payload Inline payload (NULL only when detachedPayload is + * supplied; payload and detachedPayload must not both + * be NULL, nor both be non-NULL). + * \param payloadLen Payload length (0 for an empty inline payload). + * \param detachedPayload Detached payload for MAC (NULL if attached). Must be + * NULL when payload is non-NULL. * \param detachedLen Detached payload length. * \param extAad External additional authenticated data (NULL if none). * \param extAadLen External AAD length. From 162467890edd2351362863b51b2b3ae78d66d9a7 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 12:45:08 -0700 Subject: [PATCH 33/38] Deduplicate header-clear and ML-DSA level checks into helpers --- src/wolfcose.c | 82 +++++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 9aa0b7d..173d86c 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -76,6 +76,15 @@ WOLFCOSE_LOCAL void wolfCose_ForceZero(void* mem, size_t len) } } +/* On a failed verify/decrypt, clear the header so unauthenticated metadata is + * not exposed to callers that inspect hdr without gating on the return code. */ +static void wolfCose_HdrClearOnFail(int ret, WOLFCOSE_HDR* hdr) +{ + if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { + (void)XMEMSET(hdr, 0, sizeof(*hdr)); + } +} + /* ----- Constant-time comparison (side-channel safe) ----- */ /** @@ -231,24 +240,29 @@ WOLFCOSE_LOCAL int wolfCose_SigSize(int32_t alg, size_t* sigSz) #ifdef HAVE_DILITHIUM /* Map an ML-DSA COSE algorithm to the curve identifier its key must carry, so * a key of the wrong security level cannot satisfy a higher-level alg label. */ -static int wolfCose_MlDsaAlgCrv(int32_t alg, int32_t* crv) +static int wolfCose_MlDsaKeyLevelOk(int32_t alg, int32_t crv) { int ret = WOLFCOSE_SUCCESS; + int32_t reqCrv = 0; switch (alg) { case WOLFCOSE_ALG_ML_DSA_44: - *crv = WOLFCOSE_CRV_ML_DSA_44; + reqCrv = WOLFCOSE_CRV_ML_DSA_44; break; case WOLFCOSE_ALG_ML_DSA_65: - *crv = WOLFCOSE_CRV_ML_DSA_65; + reqCrv = WOLFCOSE_CRV_ML_DSA_65; break; case WOLFCOSE_ALG_ML_DSA_87: - *crv = WOLFCOSE_CRV_ML_DSA_87; + reqCrv = WOLFCOSE_CRV_ML_DSA_87; break; default: ret = WOLFCOSE_E_COSE_BAD_ALG; break; } + /* The key's declared level (crv) must match the algorithm level. */ + if ((ret == WOLFCOSE_SUCCESS) && (crv != reqCrv)) { + ret = WOLFCOSE_E_COSE_KEY_TYPE; + } return ret; } #endif /* HAVE_DILITHIUM */ @@ -3492,7 +3506,6 @@ int wc_CoseSign1_Sign(WOLFCOSE_KEY* key, int32_t alg, if ((ret == WOLFCOSE_SUCCESS) && ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { size_t expectedSigSz = 0; - int32_t reqCrv = 0; if ((key->kty != WOLFCOSE_KTY_OKP) || (key->key.dilithium == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; @@ -3500,10 +3513,7 @@ int wc_CoseSign1_Sign(WOLFCOSE_KEY* key, int32_t alg, /* Key level must match the algorithm level. */ if (ret == WOLFCOSE_SUCCESS) { - ret = wolfCose_MlDsaAlgCrv(alg, &reqCrv); - } - if ((ret == WOLFCOSE_SUCCESS) && (key->crv != reqCrv)) { - ret = WOLFCOSE_E_COSE_KEY_TYPE; + ret = wolfCose_MlDsaKeyLevelOk(alg, key->crv); } if (ret == WOLFCOSE_SUCCESS) { @@ -3930,17 +3940,13 @@ int wc_CoseSign1_Verify(WOLFCOSE_KEY* key, ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { int verified = 0; - int32_t reqCrv = 0; if ((key->kty != WOLFCOSE_KTY_OKP) || (key->key.dilithium == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } /* Key level must match the algorithm level. */ if (ret == WOLFCOSE_SUCCESS) { - ret = wolfCose_MlDsaAlgCrv(alg, &reqCrv); - } - if ((ret == WOLFCOSE_SUCCESS) && (key->crv != reqCrv)) { - ret = WOLFCOSE_E_COSE_KEY_TYPE; + ret = wolfCose_MlDsaKeyLevelOk(alg, key->crv); } if (ret == WOLFCOSE_SUCCESS) { INJECT_FAILURE(WOLF_FAIL_DILITHIUM_VERIFY, -1) @@ -3989,11 +3995,7 @@ int wc_CoseSign1_Verify(WOLFCOSE_KEY* key, /* No action required */ } - /* On failure, clear the header so unauthenticated metadata is not exposed - * to callers that inspect hdr without strictly gating on the return code. */ - if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { - (void)XMEMSET(hdr, 0, sizeof(*hdr)); - } + wolfCose_HdrClearOnFail(ret, hdr); /* Cleanup: always executed */ (void)wolfCose_ForceZero(hashBuf, sizeof(hashBuf)); @@ -4370,16 +4372,12 @@ int wc_CoseSign_Sign(const WOLFCOSE_SIGNATURE* signers, size_t signerCount, (signer->algId == WOLFCOSE_ALG_ML_DSA_65) || (signer->algId == WOLFCOSE_ALG_ML_DSA_87))) { size_t expectedSigSz = 0; - int32_t reqCrv = 0; if (signer->key->key.dilithium == NULL) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } /* Key level must match the algorithm level. */ if (ret == WOLFCOSE_SUCCESS) { - ret = wolfCose_MlDsaAlgCrv(signer->algId, &reqCrv); - } - if ((ret == WOLFCOSE_SUCCESS) && (signer->key->crv != reqCrv)) { - ret = WOLFCOSE_E_COSE_KEY_TYPE; + ret = wolfCose_MlDsaKeyLevelOk(signer->algId, signer->key->crv); } if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_SigSize(signer->algId, &expectedSigSz); @@ -4831,17 +4829,13 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { int verified = 0; - int32_t reqCrv = 0; if ((verifyKey->kty != WOLFCOSE_KTY_OKP) || (verifyKey->key.dilithium == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } /* Key level must match the algorithm level. */ if (ret == WOLFCOSE_SUCCESS) { - ret = wolfCose_MlDsaAlgCrv(alg, &reqCrv); - } - if ((ret == WOLFCOSE_SUCCESS) && (verifyKey->crv != reqCrv)) { - ret = WOLFCOSE_E_COSE_KEY_TYPE; + ret = wolfCose_MlDsaKeyLevelOk(alg, verifyKey->crv); } if (ret == WOLFCOSE_SUCCESS) { #ifdef WOLFSSL_DILITHIUM_NO_CTX @@ -4889,11 +4883,7 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, /* No action required */ } - /* On failure, clear the header so unauthenticated metadata is not exposed - * to callers that inspect hdr without strictly gating on the return code. */ - if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { - (void)XMEMSET(hdr, 0, sizeof(*hdr)); - } + wolfCose_HdrClearOnFail(ret, hdr); /* Cleanup: always executed */ (void)wolfCose_ForceZero(hashBuf, sizeof(hashBuf)); @@ -5573,11 +5563,7 @@ int wc_CoseEncrypt0_Decrypt(WOLFCOSE_KEY* key, /* No action required */ } - /* On failure, clear the header so unauthenticated metadata is not exposed - * to callers that inspect hdr without strictly gating on the return code. */ - if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { - (void)XMEMSET(hdr, 0, sizeof(*hdr)); - } + wolfCose_HdrClearOnFail(ret, hdr); /* Cleanup: always executed */ #if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) @@ -6301,11 +6287,7 @@ int wc_CoseMac0_Verify(const WOLFCOSE_KEY* key, /* No action required */ } - /* On failure, clear the header so unauthenticated metadata is not exposed - * to callers that inspect hdr without strictly gating on the return code. */ - if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { - (void)XMEMSET(hdr, 0, sizeof(*hdr)); - } + wolfCose_HdrClearOnFail(ret, hdr); /* Cleanup: always executed */ #ifndef NO_HMAC @@ -7445,15 +7427,11 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, if (plaintextLen != NULL) { *plaintextLen = 0u; } - /* Clear the header so unauthenticated metadata is not exposed to - * callers that inspect hdr without strictly gating on the return. */ - if (hdr != NULL) { - (void)XMEMSET(hdr, 0, sizeof(*hdr)); - } } else { *plaintextLen = payloadLen; } + wolfCose_HdrClearOnFail(ret, hdr); return ret; } @@ -8099,11 +8077,7 @@ int wc_CoseMac_Verify(const WOLFCOSE_RECIPIENT* recipient, /* No action required */ } - /* On failure, clear the header so unauthenticated metadata is not exposed - * to callers that inspect hdr without strictly gating on the return code. */ - if ((ret != WOLFCOSE_SUCCESS) && (hdr != NULL)) { - (void)XMEMSET(hdr, 0, sizeof(*hdr)); - } + wolfCose_HdrClearOnFail(ret, hdr); return ret; } From 898606ad97f08f391e2af6ddfae205aca603d48b Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 13:07:48 -0700 Subject: [PATCH 34/38] Resolve MISRA findings: drop word32 guards, fix 10.4 cast and 20.9 SIZE_MAX use --- src/wolfcose.c | 54 ++------------------------------------------- src/wolfcose_cbor.c | 10 ++++----- 2 files changed, 7 insertions(+), 57 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 173d86c..180fa6f 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -651,19 +651,6 @@ static int wolfCose_InInt32Range(int64_t val) return ((val >= (int64_t)INT32_MIN) && (val <= (int64_t)INT32_MAX)) ? 1 : 0; } -/* wolfCrypt message/AAD/structure lengths are word32. Reject size_t inputs - * that would truncate when cast. Only meaningful where size_t is wider than - * 32 bits; on 32-bit targets every size_t already fits. */ -static int wolfCose_LenFitsW32(size_t len) -{ -#if SIZE_MAX > 0xFFFFFFFFu - return (len <= 0xFFFFFFFFu) ? 1 : 0; -#else - (void)len; - return 1; -#endif -} - /* Map a COSE header/key label to a fast-path tracking bit. Labels outside * the small known range fall back to the slower extra-label array. */ static uint32_t wolfCose_LabelBit(int64_t label) @@ -2045,7 +2032,8 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_CrvKeySize(key->crv, &coordSz); } - if ((ret == WOLFCOSE_SUCCESS) && (coordSz > MAX_ECC_BYTES)) { + if ((ret == WOLFCOSE_SUCCESS) && + (coordSz > (size_t)MAX_ECC_BYTES)) { ret = WOLFCOSE_E_COSE_BAD_HDR; } if (ret == WOLFCOSE_SUCCESS) { @@ -2331,10 +2319,6 @@ int wolfCose_BuildToBeSignedMaced( if (ret == WOLFCOSE_SUCCESS) { ret = wc_CBOR_EncodeBstr(&ctx, payload, payloadLen); } - /* The structure length is later passed to wolfCrypt as word32. */ - if ((ret == WOLFCOSE_SUCCESS) && (wolfCose_LenFitsW32(ctx.idx) == 0)) { - ret = WOLFCOSE_E_INVALID_ARG; - } if (ret == WOLFCOSE_SUCCESS) { *structLen = ctx.idx; } @@ -2377,10 +2361,6 @@ static int wolfCose_BuildEncStructure( ret = wc_CBOR_EncodeBstr(&ctx, extAad, (extAad != NULL) ? extAadLen : 0u); } - /* The structure length is later passed to wolfCrypt as word32. */ - if ((ret == WOLFCOSE_SUCCESS) && (wolfCose_LenFitsW32(ctx.idx) == 0)) { - ret = WOLFCOSE_E_INVALID_ARG; - } if (ret == WOLFCOSE_SUCCESS) { *structLen = ctx.idx; } @@ -4958,13 +4938,6 @@ int wc_CoseEncrypt0_Encrypt(WOLFCOSE_KEY* key, int32_t alg, ret = WOLFCOSE_E_INVALID_ARG; } - /* Plaintext length is passed to the AEAD primitive as word32. */ - if ((ret == WOLFCOSE_SUCCESS) && - ((wolfCose_LenFitsW32(payloadLen) == 0) || - (wolfCose_LenFitsW32(detachedSz) == 0))) { - ret = WOLFCOSE_E_INVALID_ARG; - } - if ((ret == WOLFCOSE_SUCCESS) && (key->kty != WOLFCOSE_KTY_SYMMETRIC)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } @@ -5322,14 +5295,6 @@ int wc_CoseEncrypt0_Decrypt(WOLFCOSE_KEY* key, ret = WOLFCOSE_E_INVALID_ARG; } - /* Ciphertext length (<= inSz / detachedCtLen) is passed to the AEAD - * primitive as word32. */ - if ((ret == WOLFCOSE_SUCCESS) && - ((wolfCose_LenFitsW32(inSz) == 0) || - (wolfCose_LenFitsW32(detachedCtLen) == 0))) { - ret = WOLFCOSE_E_INVALID_ARG; - } - if ((ret == WOLFCOSE_SUCCESS) && (key->kty != WOLFCOSE_KTY_SYMMETRIC)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } @@ -6412,13 +6377,6 @@ int wc_CoseEncrypt_Encrypt(const WOLFCOSE_RECIPIENT* recipients, ret = WOLFCOSE_E_INVALID_ARG; } - /* Plaintext length is passed to the AEAD primitive as word32. */ - if ((ret == WOLFCOSE_SUCCESS) && - ((wolfCose_LenFitsW32(payloadLen) == 0) || - (wolfCose_LenFitsW32(detachedLen) == 0))) { - ret = WOLFCOSE_E_INVALID_ARG; - } - /* Reject inconsistent (kid, kidLen) per recipient to avoid silently * dropping the identifier. */ if (ret == WOLFCOSE_SUCCESS) { @@ -6945,14 +6903,6 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, ret = WOLFCOSE_E_INVALID_ARG; } - /* Ciphertext length (<= inSz / detachedCtLen) is passed to the AEAD - * primitive as word32. */ - if ((ret == WOLFCOSE_SUCCESS) && - ((wolfCose_LenFitsW32(inSz) == 0) || - (wolfCose_LenFitsW32(detachedCtLen) == 0))) { - ret = WOLFCOSE_E_INVALID_ARG; - } - if (ret == WOLFCOSE_SUCCESS) { (void)XMEMSET(hdr, 0, sizeof(*hdr)); ctx.cbuf = in; diff --git a/src/wolfcose_cbor.c b/src/wolfcose_cbor.c index 9d5ab98..3a0011c 100644 --- a/src/wolfcose_cbor.c +++ b/src/wolfcose_cbor.c @@ -552,12 +552,12 @@ static int wolfCose_CBOR_DecodeContainerStart(WOLFCOSE_CBOR_CTX* ctx, if (item.majorType != majorType) { ret = WOLFCOSE_E_CBOR_TYPE; } -#if SIZE_MAX < UINT64_MAX - else if (item.val > (uint64_t)SIZE_MAX) { - /* Definite-length count exceeds addressable range (32-bit). */ - ret = WOLFCOSE_E_CBOR_OVERFLOW; + /* A definite-length container needs at least one byte per declared + * element, so a count larger than the bytes remaining is malformed. + * This also rejects any count that would not fit in size_t. */ + else if (item.val > (uint64_t)(ctx->bufSz - ctx->idx)) { + ret = WOLFCOSE_E_CBOR_MALFORMED; } -#endif else { *count = (size_t)item.val; } From 1b9111f5c9a0977072fc97af1472edcf5f20c4df Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 13:16:08 -0700 Subject: [PATCH 35/38] Fix MISRA 10.8: bound EC2 coordSz with sizeof instead of casting MAX_ECC_BYTES --- src/wolfcose.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 180fa6f..358deab 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -2032,15 +2032,14 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) if (ret == WOLFCOSE_SUCCESS) { ret = wolfCose_CrvKeySize(key->crv, &coordSz); } - if ((ret == WOLFCOSE_SUCCESS) && - (coordSz > (size_t)MAX_ECC_BYTES)) { - ret = WOLFCOSE_E_COSE_BAD_HDR; - } if (ret == WOLFCOSE_SUCCESS) { byte tmpX[MAX_ECC_BYTES]; byte tmpY[MAX_ECC_BYTES]; byte tmpD[MAX_ECC_BYTES]; + if (coordSz > sizeof(tmpX)) { + ret = WOLFCOSE_E_COSE_BAD_HDR; + } /* Coordinates are fixed length (validated above). */ if (ret == WOLFCOSE_SUCCESS) { (void)XMEMSET(tmpX, 0, sizeof(tmpX)); From f3f1a5a191f92b25ba37bc9ea56f5ad1fad70bfa Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 13:31:12 -0700 Subject: [PATCH 36/38] Drop dead variable initializers flagged by cppcheck unreadVariable --- tests/test_cose.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_cose.c b/tests/test_cose.c index 201fd8c..164f3ca 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -1555,7 +1555,6 @@ static void test_cose_sign1_ml_dsa_level_mismatch(void) &hdr, &decPayload, &decPayloadLen); TEST_ASSERT(ret == WOLFCOSE_E_COSE_KEY_TYPE, "ml-dsa level mismatch rejected"); - ret = 0; } if (dlInited != 0) { (void)wc_dilithium_free(&dlKey); } @@ -9576,7 +9575,7 @@ static void test_cose_encrypt_multi_per_recipient(void) uint8_t out[256]; uint8_t plaintext[64]; size_t outLen = 0; - size_t plaintextLen = 0; + size_t plaintextLen; int ret; size_t r; const uint8_t payload[] = "multi recipient direct"; @@ -15360,7 +15359,7 @@ static void test_ecdh_es_multi_recipient_decrypt_rejected(void) uint8_t out[512]; uint8_t spliced[520]; size_t outLen = 0; - size_t splicedLen = 0; + size_t splicedLen; uint8_t scratch[256]; uint8_t plaintext[64]; size_t plaintextLen = 0; From 91a2b95151ecf91b16435caa4cae97fadd481aa1 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 2 Jun 2026 13:38:42 -0700 Subject: [PATCH 37/38] Avoid MISRA 10.8 by dropping redundant casts of INT32_MIN/MAX in range check --- src/wolfcose.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wolfcose.c b/src/wolfcose.c index 358deab..e792912 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -648,7 +648,7 @@ int wolfCose_EccVerifyRaw(const uint8_t* sigBuf, size_t sigLen, * non-representable value cannot alias a valid identifier. */ static int wolfCose_InInt32Range(int64_t val) { - return ((val >= (int64_t)INT32_MIN) && (val <= (int64_t)INT32_MAX)) ? 1 : 0; + return ((val >= INT32_MIN) && (val <= INT32_MAX)) ? 1 : 0; } /* Map a COSE header/key label to a fast-path tracking bit. Labels outside From 12dc45650141914584440b46c381c2728b8c0855 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Wed, 3 Jun 2026 11:20:33 -0700 Subject: [PATCH 38/38] Fix MISRA in examples/tests (const literals, unsigned sizes, braces, parens, no goto); document printf/output deviations --- docs/MISRA-Compliance.md | 28 ++++++ docs/misra-deviations.md | 70 --------------- examples/comprehensive/encrypt_all.c | 40 +++++---- examples/comprehensive/errors_all.c | 92 ++++++++++---------- examples/comprehensive/mac_all.c | 36 ++++---- examples/comprehensive/sign_all.c | 81 ++++++++++------- examples/encrypt0_demo.c | 48 +++++----- examples/lifecycle_demo.c | 101 +++++++++++++--------- examples/mac0_demo.c | 62 +++++++------ examples/scenarios/firmware_update.c | 6 +- examples/scenarios/group_broadcast_mac.c | 5 +- examples/scenarios/iot_fleet_config.c | 5 +- examples/scenarios/multi_party_approval.c | 14 +-- examples/scenarios/sensor_attestation.c | 6 +- examples/sign1_demo.c | 58 ++++++++----- tests/test_cose.c | 75 ++++++++-------- 16 files changed, 387 insertions(+), 340 deletions(-) delete mode 100644 docs/misra-deviations.md diff --git a/docs/MISRA-Compliance.md b/docs/MISRA-Compliance.md index 7de1172..e9cc50f 100644 --- a/docs/MISRA-Compliance.md +++ b/docs/MISRA-Compliance.md @@ -116,6 +116,34 @@ The following clang-tidy checks are suppressed in the MISRA 2023 workflow. GCC s **Justification:** Advisory warning about adjacent function parameters of the same type (e.g., `size_t payloadLen, size_t detachedLen`). Fixing requires reordering or wrapping parameters, which would break the public API. The parameter ordering follows RFC 9052 structure conventions. +## Examples and Test Code + +`examples/` and `tests/` are runnable demonstration and test programs, not part of the shippable library, and are held to the same style rules where it keeps them useful as reference implementations. They are clean of the rules the library observes (no `goto`, fixed-length-coordinate checks, const-qualified literal payloads, braced statement bodies, unsigned size arithmetic, explicit precedence). The remaining deviations are inherent to runnable demos: + +### Rule 21.6 โ€” Standard I/O (and 17.7 on its return value) + +**Location:** `examples/`, `tests/`. + +**Justification:** Demos and test harnesses print human-readable status and PASS/FAIL to the console with `printf`/`fprintf`; the ignored return value (Rule 17.7) is part of the same console-output use. A real integration replaces this one call with a platform output routine. Library code under `src/` uses no standard I/O. + +### Rule 15.5 โ€” Multiple return / single point of exit + +**Location:** `examples/`, `tests/`. + +**Justification:** Demonstration code uses early returns for linear, readable top-to-bottom flow. The library itself observes single-exit with cascading `if (ret == 0)` and a single `return`. + +### Rule 2.5 โ€” Unused macro definitions + +**Location:** `examples/`, `tests/`. + +**Justification:** Same false positive as the library: CI passes explicit `-D` feature flags so cppcheck checks one code path, which makes the guarded-away feature `#define`s look unused. + +### Rules 8.6 / 5.9 / 8.9 โ€” External/internal identifier definitions + +**Location:** `examples/`. + +**Justification:** Each demo is a standalone program with its own `main` and `demo_*` helpers. cppcheck reports these when scanning all demo translation units in a single pass; each program links independently, so there is no real multiple-definition. + ## Fully Compliant Rules (Notable) | Rule | Status | Notes | diff --git a/docs/misra-deviations.md b/docs/misra-deviations.md deleted file mode 100644 index 40ebd3a..0000000 --- a/docs/misra-deviations.md +++ /dev/null @@ -1,70 +0,0 @@ -# wolfCOSE MISRA C Deviations - -This file records deviations from MISRA C:2012 / C:2023 for code paths that the -scanners flag but that are intentional. The core library (`src/`, `include/`) -targets full compliance; the deviations below are scoped to test and example -code, or to documented design decisions. Mirror updates here to the -[MISRA Compliance wiki](https://github.com/wolfSSL/wolfCOSE/wiki/MISRA-Compliance). - -Scope note: `tests/` and `examples/` are not part of the shippable library and -are not constrained by the zero-allocation or strict-conformance rules that -apply to `src/` and `include/`. - -## Deviations - -### Rule 21.6 โ€” Standard I/O functions (`printf`/`fprintf`) -- **Where:** `tests/`, `examples/`. -- **Rationale:** Test harnesses and demonstration programs report PASS/FAIL and - human-readable status to the console. Library code under `src/` uses no - standard I/O. Not applicable to shippable code. - -### Rule 11.3 โ€” Cast between pointer-to-object types (string literal โ†’ `const uint8_t*`) -- **Where:** `tests/`, `examples/` (e.g. `(const uint8_t*)"IETF"`). -- **Rationale:** COSE byte-string inputs are conventionally written as string - literals in test vectors and demos. The cast is read-only and the data is - never modified. Library APIs take `const uint8_t*`. - -### Rule 21.15 โ€” `memcmp` pointed-to type compatibility -- **Where:** `tests/` (comparing decoded `uint8_t` payloads against string - literals). -- **Rationale:** Test-only equality checks of recovered payloads against - expected literals; both operands are byte data. - -### Rule 17.7 โ€” Unused return value -- **Where:** `tests/`, `examples/` (e.g. `wc_CoseKey_Init`, `wc_ecc_init` in - setup where failure is not the property under test). -- **Rationale:** Setup calls in tests/demos whose failure is not the assertion - target. Library code checks every return value. - -### Rule 10.4 โ€” Mixed essential type in `sizeof(array) - 1` -- **Where:** `examples/`, `tests/` (string-literal payload lengths). -- **Rationale:** `sizeof(literal) - 1` to drop the NUL terminator is a common, - well-understood idiom in demo/test payload setup. - -### Rule 15.6 โ€” Non-compound selection/iteration bodies -- **Where:** `examples/`, `tests/` (e.g. `if (demo_x() != 0) failures++;`). -- **Rationale:** Localized to test/demo control flow. Library code uses braces - throughout. - -### Rule 14.4 โ€” `do { ... } while (0)` controlling expression not Boolean -- **Where:** Test/demo assertion and macro wrappers. -- **Rationale:** The `do { } while (0)` single-evaluation macro idiom is the - standard, safest multi-statement macro form. - -### Rule 15.1 โ€” `goto` for cleanup -- **Where:** `tests/` (e.g. `goto cleanup;` in multi-key setup). -- **Rationale:** Test-only single-exit cleanup. New library code uses - fallthrough cleanup; pre-existing library `goto cleanup` follows local style. - -### Rule 19.2 โ€” `union` keyword -- **Where:** `WOLFCOSE_KEY.key` (public header). -- **Rationale:** Tagged union discriminated by `kty`/`crv`; only the member - matching the key type is accessed. A tagged union keeps the public key object - small for embedded/zero-allocation use, which is a core wolfCOSE goal. - -### Rule 1.2 โ€” Language extensions (visibility / builtins) -- **Where:** `include/wolfcose/visibility.h` (`__declspec`/`__attribute__`), - `examples/lifecycle_demo.c` (`__builtin_frame_address`). -- **Rationale:** Symbol visibility for shared-library builds requires - compiler-specific attributes; the demo stack marker is diagnostic only and - compiles to a no-op on non-GNU toolchains. diff --git a/examples/comprehensive/encrypt_all.c b/examples/comprehensive/encrypt_all.c index e2e4e7b..86eb620 100644 --- a/examples/comprehensive/encrypt_all.c +++ b/examples/comprehensive/encrypt_all.c @@ -83,8 +83,8 @@ static int test_encrypt0(int32_t alg, int keySz, int detached, int useAad) }; uint8_t out[512]; uint8_t scratch[512]; - uint8_t payload[] = "test payload for encryption"; - uint8_t aad[] = "external additional authenticated data"; + const uint8_t payload[] = "test payload for encryption"; + const uint8_t aad[] = "external additional authenticated data"; uint8_t detachedCt[512]; size_t detachedCtLen = 0; uint8_t plaintext[256]; @@ -104,7 +104,7 @@ static int test_encrypt0(int32_t alg, int keySz, int detached, int useAad) if (detached != 0) { ret = wc_CoseEncrypt0_Encrypt(&cosKey, alg, iv, sizeof(iv), - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, detachedCt, sizeof(detachedCt), &detachedCtLen, (useAad != 0) ? aad : NULL, (useAad != 0) ? (sizeof(aad) - 1u) : 0u, @@ -114,7 +114,7 @@ static int test_encrypt0(int32_t alg, int keySz, int detached, int useAad) else { ret = wc_CoseEncrypt0_Encrypt(&cosKey, alg, iv, sizeof(iv), - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, (useAad != 0) ? aad : NULL, (useAad != 0) ? (sizeof(aad) - 1u) : 0u, @@ -152,7 +152,7 @@ static int test_encrypt0(int32_t alg, int keySz, int detached, int useAad) } } if (ret == 0) { - if (plaintextLen != sizeof(payload) - 1) { + if (plaintextLen != sizeof(payload) - 1u) { ret = -2; } } @@ -181,8 +181,8 @@ static int test_encrypt_multi_direct(int32_t contentAlg, int keySz, }; uint8_t out[1024]; uint8_t scratch[512]; - uint8_t payload[] = "multi-recipient encrypted payload"; - uint8_t aad[] = "multi-recipient aad"; + const uint8_t payload[] = "multi-recipient encrypted payload"; + const uint8_t aad[] = "multi-recipient aad"; uint8_t plaintext[256]; static const uint8_t recipKid[] = { 0x72u, 0x63u, 0x70u, 0x58u }; size_t plaintextLen = 0; @@ -262,9 +262,13 @@ static int test_encrypt_multi_direct(int32_t contentAlg, int keySz, static int test_encrypt_wrong_key(void) { int ret = 0; - WOLFCOSE_KEY cek, wrongKey; + WOLFCOSE_KEY cek; + WOLFCOSE_KEY wrongKey; WOLFCOSE_RECIPIENT recipients[2]; WOLFCOSE_RECIPIENT wrongRecipient; + const uint8_t recip1Kid[] = "recip1"; + const uint8_t recip2Kid[] = "recip2"; + const uint8_t wrongKid[] = "wrong"; uint8_t keyData1[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 @@ -279,7 +283,7 @@ static int test_encrypt_wrong_key(void) }; uint8_t out[512]; uint8_t scratch[512]; - uint8_t payload[] = "wrong key test"; + const uint8_t payload[] = "wrong key test"; uint8_t plaintext[256]; size_t plaintextLen = 0; size_t outLen = 0; @@ -309,19 +313,19 @@ static int test_encrypt_wrong_key(void) if (ret == 0) { recipients[0].algId = WOLFCOSE_ALG_DIRECT; recipients[0].key = &cek; - recipients[0].kid = (const uint8_t*)"recip1"; - recipients[0].kidLen = 6; + recipients[0].kid = recip1Kid; + recipients[0].kidLen = sizeof(recip1Kid) - 1u; recipients[1].algId = WOLFCOSE_ALG_DIRECT; recipients[1].key = &cek; - recipients[1].kid = (const uint8_t*)"recip2"; - recipients[1].kidLen = 6; + recipients[1].kid = recip2Kid; + recipients[1].kidLen = sizeof(recip2Kid) - 1u; /* Wrong recipient with different key */ wrongRecipient.algId = WOLFCOSE_ALG_DIRECT; wrongRecipient.key = &wrongKey; - wrongRecipient.kid = (const uint8_t*)"wrong"; - wrongRecipient.kidLen = 5; + wrongRecipient.kid = wrongKid; + wrongRecipient.kidLen = sizeof(wrongKid) - 1u; } /* Encrypt */ @@ -329,7 +333,7 @@ static int test_encrypt_wrong_key(void) ret = wc_CoseEncrypt_Encrypt(recipients, 2, WOLFCOSE_ALG_A128GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), @@ -534,7 +538,7 @@ static int test_encrypt0_interop(void) PRINT_TEST("interop_encrypt0_a128gcm_roundtrip"); ret = wc_CoseEncrypt0_Encrypt(&cosKey, WOLFCOSE_ALG_A128GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, NULL, 0, scratch, sizeof(scratch), @@ -557,7 +561,7 @@ static int test_encrypt0_interop(void) } } if (ret == 0) { - if (plaintextLen != sizeof(payload) - 1) { + if (plaintextLen != sizeof(payload) - 1u) { ret = -2; } } diff --git a/examples/comprehensive/errors_all.c b/examples/comprehensive/errors_all.c index 885c8e7..0e36616 100644 --- a/examples/comprehensive/errors_all.c +++ b/examples/comprehensive/errors_all.c @@ -86,7 +86,7 @@ static int test_sign1_tamper(int tamperPos) WOLFCOSE_KEY cosKey; WC_RNG rng; int rngInit = 0; - uint8_t payload[] = "tamper test payload"; + const uint8_t payload[] = "tamper test payload"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -112,7 +112,7 @@ static int test_sign1_tamper(int tamperPos) /* Create valid signature */ ret = wc_CoseSign1_Sign(&cosKey, WOLFCOSE_ALG_ES256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); @@ -126,11 +126,11 @@ static int test_sign1_tamper(int tamperPos) } else if (tamperPos == 1) { /* Tamper at middle */ - tampered[outLen / 2] ^= 0xFF; + tampered[outLen / 2u] ^= 0xFF; } else { /* Tamper at last byte */ - tampered[outLen - 1] ^= 0xFF; + tampered[outLen - 1u] ^= 0xFF; } /* Verify should fail */ @@ -173,7 +173,7 @@ static int test_encrypt0_tamper(int tamperPos) 0x02, 0xD1, 0xF7, 0xE6, 0xF2, 0x6C, 0x43, 0xD4, 0x86, 0x8D, 0x87, 0xCE }; - uint8_t payload[] = "tamper test payload"; + const uint8_t payload[] = "tamper test payload"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -188,7 +188,7 @@ static int test_encrypt0_tamper(int tamperPos) /* Encrypt */ ret = wc_CoseEncrypt0_Encrypt(&cosKey, WOLFCOSE_ALG_A128GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, NULL, 0, scratch, sizeof(scratch), @@ -201,10 +201,10 @@ static int test_encrypt0_tamper(int tamperPos) tampered[0] ^= 0xFF; } else if (tamperPos == 1) { - tampered[outLen / 2] ^= 0xFF; + tampered[outLen / 2u] ^= 0xFF; } else { - tampered[outLen - 1] ^= 0xFF; + tampered[outLen - 1u] ^= 0xFF; } /* Decrypt should fail */ @@ -237,7 +237,7 @@ static int test_mac0_tamper(int tamperPos) 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; - uint8_t payload[] = "tamper test payload"; + const uint8_t payload[] = "tamper test payload"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -252,7 +252,7 @@ static int test_mac0_tamper(int tamperPos) /* Create MAC */ ret = wc_CoseMac0_Create(&cosKey, WOLFCOSE_ALG_HMAC_256_256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen); @@ -264,10 +264,10 @@ static int test_mac0_tamper(int tamperPos) tampered[0] ^= 0xFF; } else if (tamperPos == 1) { - tampered[outLen / 2] ^= 0xFF; + tampered[outLen / 2u] ^= 0xFF; } else { - tampered[outLen - 1] ^= 0xFF; + tampered[outLen - 1u] ^= 0xFF; } /* Verify should fail */ @@ -297,7 +297,7 @@ static int test_sign1_truncated(void) WOLFCOSE_KEY cosKey; WC_RNG rng; int rngInit = 0; - uint8_t payload[] = "truncation test payload"; + const uint8_t payload[] = "truncation test payload"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -322,14 +322,14 @@ static int test_sign1_truncated(void) /* Create valid message */ ret = wc_CoseSign1_Sign(&cosKey, WOLFCOSE_ALG_ES256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); } if (ret == 0) { /* Truncate to half and verify - should fail */ - ret = wc_CoseSign1_Verify(&cosKey, out, outLen / 2, + ret = wc_CoseSign1_Verify(&cosKey, out, outLen / 2u, NULL, 0, NULL, 0, scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); @@ -365,7 +365,7 @@ static int test_encrypt0_truncated(void) 0x02, 0xD1, 0xF7, 0xE6, 0xF2, 0x6C, 0x43, 0xD4, 0x86, 0x8D, 0x87, 0xCE }; - uint8_t payload[] = "truncation test payload"; + const uint8_t payload[] = "truncation test payload"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -379,7 +379,7 @@ static int test_encrypt0_truncated(void) /* Encrypt */ ret = wc_CoseEncrypt0_Encrypt(&cosKey, WOLFCOSE_ALG_A128GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, NULL, 0, scratch, sizeof(scratch), @@ -387,7 +387,7 @@ static int test_encrypt0_truncated(void) } if (ret == 0) { /* Truncate and decrypt - should fail */ - ret = wc_CoseEncrypt0_Decrypt(&cosKey, out, outLen / 2, + ret = wc_CoseEncrypt0_Decrypt(&cosKey, out, outLen / 2u, NULL, 0, NULL, 0, scratch, sizeof(scratch), &hdr, @@ -415,7 +415,7 @@ static int test_mac0_truncated(void) 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; - uint8_t payload[] = "truncation test payload"; + const uint8_t payload[] = "truncation test payload"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -429,14 +429,14 @@ static int test_mac0_truncated(void) /* Create MAC */ ret = wc_CoseMac0_Create(&cosKey, WOLFCOSE_ALG_HMAC_256_256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen); } if (ret == 0) { /* Truncate and verify - should fail */ - ret = wc_CoseMac0_Verify(&cosKey, out, outLen / 2, + ret = wc_CoseMac0_Verify(&cosKey, out, outLen / 2u, NULL, 0, NULL, 0, scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); @@ -462,9 +462,9 @@ static int test_sign1_aad_mismatch(void) WOLFCOSE_KEY cosKey; WC_RNG rng; int rngInit = 0; - uint8_t payload[] = "AAD mismatch test"; - uint8_t aad[] = "correct aad"; - uint8_t wrongAad[] = "wrong aad"; + const uint8_t payload[] = "AAD mismatch test"; + const uint8_t aad[] = "correct aad"; + const uint8_t wrongAad[] = "wrong aad"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -489,9 +489,9 @@ static int test_sign1_aad_mismatch(void) /* Sign with AAD */ ret = wc_CoseSign1_Sign(&cosKey, WOLFCOSE_ALG_ES256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, - aad, sizeof(aad) - 1, + aad, sizeof(aad) - 1u, scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); } @@ -499,7 +499,7 @@ static int test_sign1_aad_mismatch(void) /* Verify with wrong AAD - should fail */ ret = wc_CoseSign1_Verify(&cosKey, out, outLen, NULL, 0, - wrongAad, sizeof(wrongAad) - 1, + wrongAad, sizeof(wrongAad) - 1u, scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); if (ret == 0) { @@ -534,9 +534,9 @@ static int test_encrypt0_aad_mismatch(void) 0x02, 0xD1, 0xF7, 0xE6, 0xF2, 0x6C, 0x43, 0xD4, 0x86, 0x8D, 0x87, 0xCE }; - uint8_t payload[] = "AAD mismatch test"; - uint8_t aad[] = "correct aad"; - uint8_t wrongAad[] = "wrong aad"; + const uint8_t payload[] = "AAD mismatch test"; + const uint8_t aad[] = "correct aad"; + const uint8_t wrongAad[] = "wrong aad"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -550,9 +550,9 @@ static int test_encrypt0_aad_mismatch(void) /* Encrypt with AAD */ ret = wc_CoseEncrypt0_Encrypt(&cosKey, WOLFCOSE_ALG_A128GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, - aad, sizeof(aad) - 1, + aad, sizeof(aad) - 1u, scratch, sizeof(scratch), out, sizeof(out), &outLen); } @@ -560,7 +560,7 @@ static int test_encrypt0_aad_mismatch(void) /* Decrypt with wrong AAD - should fail */ ret = wc_CoseEncrypt0_Decrypt(&cosKey, out, outLen, NULL, 0, - wrongAad, sizeof(wrongAad) - 1, + wrongAad, sizeof(wrongAad) - 1u, scratch, sizeof(scratch), &hdr, plaintext, sizeof(plaintext), &plaintextLen); @@ -587,9 +587,9 @@ static int test_mac0_aad_mismatch(void) 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; - uint8_t payload[] = "AAD mismatch test"; - uint8_t aad[] = "correct aad"; - uint8_t wrongAad[] = "wrong aad"; + const uint8_t payload[] = "AAD mismatch test"; + const uint8_t aad[] = "correct aad"; + const uint8_t wrongAad[] = "wrong aad"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -603,9 +603,9 @@ static int test_mac0_aad_mismatch(void) /* Create MAC with AAD */ ret = wc_CoseMac0_Create(&cosKey, WOLFCOSE_ALG_HMAC_256_256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, - aad, sizeof(aad) - 1, + aad, sizeof(aad) - 1u, scratch, sizeof(scratch), out, sizeof(out), &outLen); } @@ -613,7 +613,7 @@ static int test_mac0_aad_mismatch(void) /* Verify with wrong AAD - should fail */ ret = wc_CoseMac0_Verify(&cosKey, out, outLen, NULL, 0, - wrongAad, sizeof(wrongAad) - 1, + wrongAad, sizeof(wrongAad) - 1u, scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); if (ret == 0) { @@ -638,7 +638,7 @@ static int test_sign1_detached_missing(void) WOLFCOSE_KEY cosKey; WC_RNG rng; int rngInit = 0; - uint8_t payload[] = "detached payload"; + const uint8_t payload[] = "detached payload"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -664,7 +664,7 @@ static int test_sign1_detached_missing(void) ret = wc_CoseSign1_Sign(&cosKey, WOLFCOSE_ALG_ES256, NULL, 0, NULL, 0, /* no inline payload */ - payload, sizeof(payload) - 1, /* detached */ + payload, sizeof(payload) - 1u, /* detached */ NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); @@ -704,7 +704,7 @@ static int test_sign1_with_symmetric_key(void) WC_RNG rng; int rngInit = 0; uint8_t keyData[32] = {0}; - uint8_t payload[] = "wrong key type test"; + const uint8_t payload[] = "wrong key type test"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -719,7 +719,7 @@ static int test_sign1_with_symmetric_key(void) if (ret == 0) { ret = wc_CoseSign1_Sign(&cosKey, WOLFCOSE_ALG_ES256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); @@ -752,7 +752,7 @@ static int test_encrypt0_with_signing_key(void) 0x02, 0xD1, 0xF7, 0xE6, 0xF2, 0x6C, 0x43, 0xD4, 0x86, 0x8D, 0x87, 0xCE }; - uint8_t payload[] = "wrong key type test"; + const uint8_t payload[] = "wrong key type test"; uint8_t scratch[512]; uint8_t out[512]; size_t outLen = 0; @@ -774,7 +774,7 @@ static int test_encrypt0_with_signing_key(void) if (ret == 0) { ret = wc_CoseEncrypt0_Encrypt(&cosKey, WOLFCOSE_ALG_A128GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, NULL, 0, scratch, sizeof(scratch), @@ -846,7 +846,7 @@ static int test_sign1_empty_payload(void) } if (ret == 0) { /* Empty payload expected */ - if (decPayloadLen != 0) { + if (decPayloadLen != 0u) { ret = -101; } } diff --git a/examples/comprehensive/mac_all.c b/examples/comprehensive/mac_all.c index 94984c4..3dfe7a2 100644 --- a/examples/comprehensive/mac_all.c +++ b/examples/comprehensive/mac_all.c @@ -82,8 +82,8 @@ static int test_mac0(int32_t alg, int keySz, int detached, int useAad) uint8_t keyData[64]; uint8_t out[512]; uint8_t scratch[512]; - uint8_t payload[] = "test payload for MAC operation"; - uint8_t aad[] = "external additional authenticated data"; + const uint8_t payload[] = "test payload for MAC operation"; + const uint8_t aad[] = "external additional authenticated data"; size_t outLen = 0; const uint8_t* decPayload = NULL; size_t decPayloadLen = 0; @@ -130,7 +130,7 @@ static int test_mac0(int32_t alg, int keySz, int detached, int useAad) /* Validate payload if inline */ if ((ret == 0) && (detached == 0)) { - if (decPayloadLen != sizeof(payload) - 1) { + if (decPayloadLen != sizeof(payload) - 1u) { ret = -2; } else if (XMEMCMP(decPayload, payload, decPayloadLen) != 0) { @@ -153,8 +153,8 @@ static int test_mac_multi_direct(int32_t macAlg, int keySz, uint8_t keyData[64]; uint8_t out[1024]; uint8_t scratch[512]; - uint8_t payload[] = "multi-recipient MAC payload"; - uint8_t aad[] = "multi-recipient mac aad"; + const uint8_t payload[] = "multi-recipient MAC payload"; + const uint8_t aad[] = "multi-recipient mac aad"; static const uint8_t recipKid[] = { 0x6Du, 0x61u, 0x63u, 0x58u }; const uint8_t* decPayload = NULL; size_t decPayloadLen = 0; @@ -216,7 +216,8 @@ static int test_mac_multi_direct(int32_t macAlg, int keySz, static int test_mac_wrong_key(void) { int ret = 0; - WOLFCOSE_KEY macKey, wrongKey; + WOLFCOSE_KEY macKey; + WOLFCOSE_KEY wrongKey; WOLFCOSE_RECIPIENT recipients[2]; WOLFCOSE_RECIPIENT wrongRecipient; uint8_t keyData1[32] = { @@ -233,7 +234,10 @@ static int test_mac_wrong_key(void) }; uint8_t out[512]; uint8_t scratch[512]; - uint8_t payload[] = "wrong key test"; + const uint8_t payload[] = "wrong key test"; + const uint8_t kid1[] = "recip1"; + const uint8_t kid2[] = "recip2"; + const uint8_t kidWrong[] = "wrong"; const uint8_t* decPayload = NULL; size_t decPayloadLen = 0; size_t outLen = 0; @@ -254,26 +258,26 @@ static int test_mac_wrong_key(void) if (ret == 0) { recipients[0].algId = WOLFCOSE_ALG_DIRECT; recipients[0].key = &macKey; - recipients[0].kid = (const uint8_t*)"recip1"; - recipients[0].kidLen = 6; + recipients[0].kid = kid1; + recipients[0].kidLen = sizeof(kid1) - 1u; recipients[1].algId = WOLFCOSE_ALG_DIRECT; recipients[1].key = &macKey; - recipients[1].kid = (const uint8_t*)"recip2"; - recipients[1].kidLen = 6; + recipients[1].kid = kid2; + recipients[1].kidLen = sizeof(kid2) - 1u; /* Wrong recipient with different key */ wrongRecipient.algId = WOLFCOSE_ALG_DIRECT; wrongRecipient.key = &wrongKey; - wrongRecipient.kid = (const uint8_t*)"wrong"; - wrongRecipient.kidLen = 5; + wrongRecipient.kid = kidWrong; + wrongRecipient.kidLen = sizeof(kidWrong) - 1u; } /* Create MAC */ if (ret == 0) { ret = wc_CoseMac_Create(recipients, 2, WOLFCOSE_ALG_HMAC_256_256, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), @@ -511,7 +515,7 @@ static int test_mac0_interop(void) if (ret == 0) { ret = wc_CoseMac0_Create(&cosKey, WOLFCOSE_ALG_HMAC_256_256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen); @@ -532,7 +536,7 @@ static int test_mac0_interop(void) } } if (ret == 0) { - if (decPayloadLen != sizeof(payload) - 1) { + if (decPayloadLen != sizeof(payload) - 1u) { ret = -2; } } diff --git a/examples/comprehensive/sign_all.c b/examples/comprehensive/sign_all.c index f5b232e..d986a46 100644 --- a/examples/comprehensive/sign_all.c +++ b/examples/comprehensive/sign_all.c @@ -109,8 +109,8 @@ static int test_sign1(int32_t alg, int curveSize, int detached, int useAad) int rngInit = 0; uint8_t out[640]; /* Large enough for ES512 */ uint8_t scratch[512]; - uint8_t payload[] = "test payload data for signing"; - uint8_t aad[] = "external additional authenticated data"; + const uint8_t payload[] = "test payload data for signing"; + const uint8_t aad[] = "external additional authenticated data"; size_t outLen = 0; const uint8_t* decPayload = NULL; size_t decPayloadLen = 0; @@ -124,7 +124,7 @@ static int test_sign1(int32_t alg, int curveSize, int detached, int useAad) } /* Key setup based on curve */ - if (ret == 0 && curveSize == 0) { + if ((ret == 0) && (curveSize == 0)) { #ifdef HAVE_ED25519 ret = wc_ed25519_init(&edKey); if (ret == 0) { @@ -182,7 +182,7 @@ static int test_sign1(int32_t alg, int curveSize, int detached, int useAad) /* Validate payload if inline */ if ((ret == 0) && (detached == 0)) { - if (decPayloadLen != sizeof(payload) - 1) { + if (decPayloadLen != sizeof(payload) - 1u) { ret = -1; } else if (XMEMCMP(decPayload, payload, decPayloadLen) != 0) { @@ -213,20 +213,25 @@ static int test_sign_multi_2(int32_t alg1, int keySz1, int32_t alg2, int keySz2, int detached, int useAad) { int ret = 0; - ecc_key eccKey1, eccKey2; - int ecc1Init = 0, ecc2Init = 0; + ecc_key eccKey1; + ecc_key eccKey2; + int ecc1Init = 0; + int ecc2Init = 0; #ifdef HAVE_ED25519 - ed25519_key edKey1, edKey2; - int ed1Init = 0, ed2Init = 0; + ed25519_key edKey1; + ed25519_key edKey2; + int ed1Init = 0; + int ed2Init = 0; #endif - WOLFCOSE_KEY cosKey1, cosKey2; + WOLFCOSE_KEY cosKey1; + WOLFCOSE_KEY cosKey2; WOLFCOSE_SIGNATURE signers[2]; WC_RNG rng; int rngInit = 0; uint8_t out[1024]; uint8_t scratch[512]; - uint8_t payload[] = "multi-signer test payload"; - uint8_t aad[] = "multi-signer aad"; + const uint8_t payload[] = "multi-signer test payload"; + const uint8_t aad[] = "multi-signer aad"; size_t outLen = 0; const uint8_t* decPayload = NULL; size_t decPayloadLen = 0; @@ -242,7 +247,7 @@ static int test_sign_multi_2(int32_t alg1, int keySz1, int32_t alg2, int keySz2, } /* Setup key 1 */ - if (ret == 0 && keySz1 == 0) { + if ((ret == 0) && (keySz1 == 0)) { #ifdef HAVE_ED25519 ret = wc_ed25519_init(&edKey1); if (ret == 0) { @@ -270,7 +275,7 @@ static int test_sign_multi_2(int32_t alg1, int keySz1, int32_t alg2, int keySz2, } /* Setup key 2 */ - if (ret == 0 && keySz2 == 0) { + if ((ret == 0) && (keySz2 == 0)) { #ifdef HAVE_ED25519 ret = wc_ed25519_init(&edKey2); if (ret == 0) { @@ -366,20 +371,30 @@ static int test_sign_multi_3(int32_t alg1, int keySz1, int detached, int useAad) { int ret = 0; - ecc_key eccKey1, eccKey2, eccKey3; - int ecc1Init = 0, ecc2Init = 0, ecc3Init = 0; + ecc_key eccKey1; + ecc_key eccKey2; + ecc_key eccKey3; + int ecc1Init = 0; + int ecc2Init = 0; + int ecc3Init = 0; #ifdef HAVE_ED25519 - ed25519_key edKey1, edKey2, edKey3; - int ed1Init = 0, ed2Init = 0, ed3Init = 0; + ed25519_key edKey1; + ed25519_key edKey2; + ed25519_key edKey3; + int ed1Init = 0; + int ed2Init = 0; + int ed3Init = 0; #endif - WOLFCOSE_KEY cosKey1, cosKey2, cosKey3; + WOLFCOSE_KEY cosKey1; + WOLFCOSE_KEY cosKey2; + WOLFCOSE_KEY cosKey3; WOLFCOSE_SIGNATURE signers[3]; WC_RNG rng; int rngInit = 0; uint8_t out[1536]; uint8_t scratch[512]; - uint8_t payload[] = "three signer payload"; - uint8_t aad[] = "three signer aad"; + const uint8_t payload[] = "three signer payload"; + const uint8_t aad[] = "three signer aad"; size_t outLen = 0; const uint8_t* decPayload = NULL; size_t decPayloadLen = 0; @@ -500,17 +515,25 @@ static int test_sign_multi_3(int32_t alg1, int keySz1, static int test_sign_multi_4(int detached, int useAad) { int ret = 0; - ecc_key eccKey256, eccKey384, eccKey521; + ecc_key eccKey256; + ecc_key eccKey384; + ecc_key eccKey521; ed25519_key edKey; - int ecc256Init = 0, ecc384Init = 0, ecc521Init = 0, edInit = 0; - WOLFCOSE_KEY cosKey256, cosKey384, cosKey521, cosKeyEd; + int ecc256Init = 0; + int ecc384Init = 0; + int ecc521Init = 0; + int edInit = 0; + WOLFCOSE_KEY cosKey256; + WOLFCOSE_KEY cosKey384; + WOLFCOSE_KEY cosKey521; + WOLFCOSE_KEY cosKeyEd; WOLFCOSE_SIGNATURE signers[4]; WC_RNG rng; int rngInit = 0; uint8_t out[2048]; uint8_t scratch[512]; - uint8_t payload[] = "four signer payload"; - uint8_t aad[] = "four signer aad"; + const uint8_t payload[] = "four signer payload"; + const uint8_t aad[] = "four signer aad"; size_t outLen = 0; const uint8_t* decPayload = NULL; size_t decPayloadLen = 0; @@ -1063,7 +1086,7 @@ static int test_sign1_interop(void) if (ret == 0) { ret = wc_CoseSign1_Sign(&cosKey, WOLFCOSE_ALG_ES256, NULL, 0, - payload, sizeof(payload) - 1, + payload, sizeof(payload) - 1u, NULL, 0, NULL, 0, scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); @@ -1078,13 +1101,13 @@ static int test_sign1_interop(void) } /* Validate */ - if (ret == 0 && hdr.alg != WOLFCOSE_ALG_ES256) { + if ((ret == 0) && (hdr.alg != WOLFCOSE_ALG_ES256)) { ret = -1; } - if (ret == 0 && decPayloadLen != sizeof(payload) - 1) { + if ((ret == 0) && (decPayloadLen != (sizeof(payload) - 1u))) { ret = -2; } - if (ret == 0 && XMEMCMP(decPayload, payload, decPayloadLen) != 0) { + if ((ret == 0) && (XMEMCMP(decPayload, payload, decPayloadLen) != 0)) { ret = -3; } diff --git a/examples/encrypt0_demo.c b/examples/encrypt0_demo.c index f237c67..70a1d21 100644 --- a/examples/encrypt0_demo.c +++ b/examples/encrypt0_demo.c @@ -64,7 +64,7 @@ static int demo_encrypt0_a128gcm(void) 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 }; uint8_t iv[12]; - uint8_t payload[] = "A128GCM test payload"; + const uint8_t payload[] = "A128GCM test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -74,7 +74,7 @@ static int demo_encrypt0_a128gcm(void) int ret; printf("--- COSE_Encrypt0 A128GCM ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); wc_CoseKey_Init(&key); ret = wc_CoseKey_SetSymmetric(&key, keyData, sizeof(keyData)); @@ -83,7 +83,7 @@ static int demo_encrypt0_a128gcm(void) ret = wc_CoseEncrypt0_Encrypt(&key, WOLFCOSE_ALG_A128GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, NULL, /* detachedPayload, detachedSz, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -97,7 +97,7 @@ static int demo_encrypt0_a128gcm(void) scratch, sizeof(scratch), &hdr, plaintext, sizeof(plaintext), &plaintextLen); DEMO_ASSERT(ret == 0, "Decrypt"); - DEMO_ASSERT(plaintextLen == sizeof(payload) - 1, "Payload length"); + DEMO_ASSERT(plaintextLen == sizeof(payload) - 1u, "Payload length"); DEMO_ASSERT(memcmp(plaintext, payload, plaintextLen) == 0, "Payload match"); DEMO_ASSERT(hdr.alg == WOLFCOSE_ALG_A128GCM, "Algorithm"); @@ -114,7 +114,7 @@ static int demo_encrypt0_a192gcm(void) 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }; uint8_t iv[12]; - uint8_t payload[] = "A192GCM test payload"; + const uint8_t payload[] = "A192GCM test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -124,7 +124,7 @@ static int demo_encrypt0_a192gcm(void) int ret; printf("--- COSE_Encrypt0 A192GCM ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); wc_CoseKey_Init(&key); ret = wc_CoseKey_SetSymmetric(&key, keyData, sizeof(keyData)); @@ -133,7 +133,7 @@ static int demo_encrypt0_a192gcm(void) ret = wc_CoseEncrypt0_Encrypt(&key, WOLFCOSE_ALG_A192GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, NULL, /* detachedPayload, detachedSz, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -163,7 +163,7 @@ static int demo_encrypt0_a256gcm(void) 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; uint8_t iv[12]; - uint8_t payload[] = "A256GCM test payload"; + const uint8_t payload[] = "A256GCM test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -173,7 +173,7 @@ static int demo_encrypt0_a256gcm(void) int ret; printf("--- COSE_Encrypt0 A256GCM ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); wc_CoseKey_Init(&key); ret = wc_CoseKey_SetSymmetric(&key, keyData, sizeof(keyData)); @@ -182,7 +182,7 @@ static int demo_encrypt0_a256gcm(void) ret = wc_CoseEncrypt0_Encrypt(&key, WOLFCOSE_ALG_A256GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, NULL, /* detachedPayload, detachedSz, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -210,8 +210,8 @@ static int demo_encrypt0_with_aad(void) 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 }; uint8_t iv[12]; - uint8_t payload[] = "Payload with AAD"; - uint8_t aad[] = "Additional authenticated data"; + const uint8_t payload[] = "Payload with AAD"; + const uint8_t aad[] = "Additional authenticated data"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -234,9 +234,9 @@ static int demo_encrypt0_with_aad(void) ret = wc_CoseEncrypt0_Encrypt(&key, WOLFCOSE_ALG_A128GCM, iv, sizeof(iv), - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, NULL, /* detachedPayload, detachedSz, detachedLen */ - aad, sizeof(aad) - 1, /* extAad, extAadLen */ + aad, sizeof(aad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), out, sizeof(out), &outLen); DEMO_ASSERT(ret == 0, "Encrypt with AAD"); @@ -245,7 +245,7 @@ static int demo_encrypt0_with_aad(void) /* Decrypt with correct AAD */ ret = wc_CoseEncrypt0_Decrypt(&key, out, outLen, NULL, 0, /* detachedCt, detachedCtLen */ - aad, sizeof(aad) - 1, /* extAad, extAadLen */ + aad, sizeof(aad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), &hdr, plaintext, sizeof(plaintext), &plaintextLen); DEMO_ASSERT(ret == 0, "Decrypt with correct AAD"); @@ -253,7 +253,7 @@ static int demo_encrypt0_with_aad(void) /* Verify wrong AAD fails */ ret = wc_CoseEncrypt0_Decrypt(&key, out, outLen, NULL, 0, /* detachedCt, detachedCtLen */ - wrongAad, sizeof(wrongAad) - 1, /* extAad, extAadLen */ + wrongAad, sizeof(wrongAad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), &hdr, plaintext, sizeof(plaintext), &plaintextLen); DEMO_ASSERT(ret != 0, "Wrong AAD rejected"); @@ -269,10 +269,18 @@ int main(void) printf("=== wolfCOSE Encrypt0 Demo ===\n\n"); #ifdef HAVE_AESGCM - if (demo_encrypt0_a128gcm() != 0) failures++; - if (demo_encrypt0_a192gcm() != 0) failures++; - if (demo_encrypt0_a256gcm() != 0) failures++; - if (demo_encrypt0_with_aad() != 0) failures++; + if (demo_encrypt0_a128gcm() != 0) { + failures++; + } + if (demo_encrypt0_a192gcm() != 0) { + failures++; + } + if (demo_encrypt0_a256gcm() != 0) { + failures++; + } + if (demo_encrypt0_with_aad() != 0) { + failures++; + } #else printf("AES-GCM not enabled in wolfSSL\n"); #endif diff --git a/examples/lifecycle_demo.c b/examples/lifecycle_demo.c index 4e26bd6..fb4416a 100644 --- a/examples/lifecycle_demo.c +++ b/examples/lifecycle_demo.c @@ -67,7 +67,7 @@ #ifdef __GNUC__ #define STACK_MARKER() ((size_t)__builtin_frame_address(0)) #else - #define STACK_MARKER() 0 + #define STACK_MARKER() ((size_t)0u) #endif static const uint8_t g_kid[] = "edge-sensor-01"; @@ -109,7 +109,8 @@ static int demo_sign1_es256(void) { int ret = 0; ecc_key eccKey; - WOLFCOSE_KEY signKey, verifyKey; + WOLFCOSE_KEY signKey; + WOLFCOSE_KEY verifyKey; WC_RNG rng; uint8_t payload[64]; size_t payloadLen = 0; @@ -148,7 +149,7 @@ static int demo_sign1_es256(void) wc_CoseKey_SetEcc(&signKey, WOLFCOSE_CRV_P256, &eccKey); ret = wc_CoseSign1_Sign(&signKey, WOLFCOSE_ALG_ES256, - g_kid, sizeof(g_kid) - 1, + g_kid, sizeof(g_kid) - 1u, payload, payloadLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), packet, sizeof(packet), &packetLen, &rng); @@ -180,7 +181,7 @@ static int demo_sign1_es256(void) if (rngInited != 0) { wc_FreeRng(&rng); } - printf(" Result: %s\n\n", ret == 0 ? "PASS" : "FAIL"); + printf(" Result: %s\n\n", (ret == 0) ? "PASS" : "FAIL"); return ret; } @@ -228,7 +229,7 @@ static int demo_sign1_eddsa(void) wc_CoseKey_SetEd25519(&signKey, &edKey); ret = wc_CoseSign1_Sign(&signKey, WOLFCOSE_ALG_EDDSA, - g_kid, sizeof(g_kid) - 1, + g_kid, sizeof(g_kid) - 1u, payload, payloadLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), packet, sizeof(packet), &packetLen, &rng); @@ -257,7 +258,7 @@ static int demo_sign1_eddsa(void) if (rngInited != 0) { wc_FreeRng(&rng); } - printf(" Result: %s\n\n", ret == 0 ? "PASS" : "FAIL"); + printf(" Result: %s\n\n", (ret == 0) ? "PASS" : "FAIL"); return ret; } @@ -309,7 +310,7 @@ static int demo_sign1_ps256(void) wc_CoseKey_SetRsa(&signKey, &rsaKey); ret = wc_CoseSign1_Sign(&signKey, WOLFCOSE_ALG_PS256, - g_kid, sizeof(g_kid) - 1, + g_kid, sizeof(g_kid) - 1u, payload, payloadLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), packet, sizeof(packet), &packetLen, &rng); @@ -338,7 +339,7 @@ static int demo_sign1_ps256(void) if (rngInited != 0) { wc_FreeRng(&rng); } - printf(" Result: %s\n\n", ret == 0 ? "PASS" : "FAIL"); + printf(" Result: %s\n\n", (ret == 0) ? "PASS" : "FAIL"); return ret; } @@ -393,7 +394,7 @@ static int demo_sign1_ml_dsa_44(void) wc_CoseKey_SetMlDsa(&signKey, WOLFCOSE_ALG_ML_DSA_44, &dlKey); ret = wc_CoseSign1_Sign(&signKey, WOLFCOSE_ALG_ML_DSA_44, - g_kid, sizeof(g_kid) - 1, + g_kid, sizeof(g_kid) - 1u, payload, payloadLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), packet, sizeof(packet), &packetLen, &rng); @@ -422,7 +423,7 @@ static int demo_sign1_ml_dsa_44(void) if (rngInited != 0) { wc_FreeRng(&rng); } - printf(" Result: %s\n\n", ret == 0 ? "PASS" : "FAIL"); + printf(" Result: %s\n\n", (ret == 0) ? "PASS" : "FAIL"); return ret; } @@ -500,7 +501,7 @@ static int demo_encrypt0_aesgcm(int32_t alg) } wc_FreeRng(&rng); - printf(" Result: %s\n\n", ret == 0 ? "PASS" : "FAIL"); + printf(" Result: %s\n\n", (ret == 0) ? "PASS" : "FAIL"); return ret; } #endif /* HAVE_AESGCM */ @@ -566,7 +567,7 @@ static int demo_encrypt0_chacha20(void) } wc_FreeRng(&rng); - printf(" Result: %s\n\n", ret == 0 ? "PASS" : "FAIL"); + printf(" Result: %s\n\n", (ret == 0) ? "PASS" : "FAIL"); return ret; } #endif /* HAVE_CHACHA && HAVE_POLY1305 */ @@ -632,7 +633,7 @@ static int demo_encrypt0_aes_ccm(void) } wc_FreeRng(&rng); - printf(" Result: %s\n\n", ret == 0 ? "PASS" : "FAIL"); + printf(" Result: %s\n\n", (ret == 0) ? "PASS" : "FAIL"); return ret; } #endif /* HAVE_AESCCM */ @@ -686,7 +687,7 @@ static int demo_mac0_hmac(int32_t alg) wc_CoseKey_SetSymmetric(&key, keyData, keyLen); ret = wc_CoseMac0_Create(&key, alg, - g_kid, sizeof(g_kid) - 1, + g_kid, sizeof(g_kid) - 1u, payload, payloadLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), packet, sizeof(packet), &packetLen); @@ -708,7 +709,7 @@ static int demo_mac0_hmac(int32_t alg) decPayloadLen, hdr.alg); } - printf(" Result: %s\n\n", ret == 0 ? "PASS" : "FAIL"); + printf(" Result: %s\n\n", (ret == 0) ? "PASS" : "FAIL"); return ret; } #endif /* !NO_HMAC */ @@ -731,20 +732,42 @@ enum { static int parse_demo_alg(const char* name) { - if (name == NULL || strcmp(name, "all") == 0) { + if ((name == NULL) || (strcmp(name, "all") == 0)) { return DEMO_ALG_ALL; } - if (strcmp(name, "ES256") == 0) return DEMO_ALG_ES256; - if (strcmp(name, "EdDSA") == 0) return DEMO_ALG_EDDSA; - if (strcmp(name, "PS256") == 0) return DEMO_ALG_PS256; - if (strcmp(name, "A128GCM") == 0) return DEMO_ALG_A128GCM; - if (strcmp(name, "A256GCM") == 0) return DEMO_ALG_A256GCM; - if (strcmp(name, "HMAC256") == 0) return DEMO_ALG_HMAC256; - if (strcmp(name, "HMAC384") == 0) return DEMO_ALG_HMAC384; - if (strcmp(name, "HMAC512") == 0) return DEMO_ALG_HMAC512; - if (strcmp(name, "ChaCha20") == 0) return DEMO_ALG_CHACHA20; - if (strcmp(name, "ML-DSA-44") == 0) return DEMO_ALG_ML_DSA_44; - if (strcmp(name, "AES-CCM") == 0) return DEMO_ALG_AES_CCM; + if (strcmp(name, "ES256") == 0) { + return DEMO_ALG_ES256; + } + if (strcmp(name, "EdDSA") == 0) { + return DEMO_ALG_EDDSA; + } + if (strcmp(name, "PS256") == 0) { + return DEMO_ALG_PS256; + } + if (strcmp(name, "A128GCM") == 0) { + return DEMO_ALG_A128GCM; + } + if (strcmp(name, "A256GCM") == 0) { + return DEMO_ALG_A256GCM; + } + if (strcmp(name, "HMAC256") == 0) { + return DEMO_ALG_HMAC256; + } + if (strcmp(name, "HMAC384") == 0) { + return DEMO_ALG_HMAC384; + } + if (strcmp(name, "HMAC512") == 0) { + return DEMO_ALG_HMAC512; + } + if (strcmp(name, "ChaCha20") == 0) { + return DEMO_ALG_CHACHA20; + } + if (strcmp(name, "ML-DSA-44") == 0) { + return DEMO_ALG_ML_DSA_44; + } + if (strcmp(name, "AES-CCM") == 0) { + return DEMO_ALG_AES_CCM; + } return -1; } @@ -756,7 +779,7 @@ int main(int argc, char* argv[]) int tests = 0; /* Parse -a flag */ - if (argc == 3 && strcmp(argv[1], "-a") == 0) { + if ((argc == 3) && (strcmp(argv[1], "-a") == 0)) { demoAlg = parse_demo_alg(argv[2]); if (demoAlg < 0) { fprintf(stderr, "Unknown algorithm: %s\n", argv[2]); @@ -782,25 +805,25 @@ int main(int argc, char* argv[]) /* COSE_Sign1 demos */ #ifdef HAVE_ECC - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_ES256) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_ES256)) { tests++; if (demo_sign1_es256() != 0) { failures++; } } #endif #ifdef HAVE_ED25519 - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_EDDSA) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_EDDSA)) { tests++; if (demo_sign1_eddsa() != 0) { failures++; } } #endif #if defined(WC_RSA_PSS) && defined(WOLFSSL_KEY_GEN) - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_PS256) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_PS256)) { tests++; if (demo_sign1_ps256() != 0) { failures++; } } #endif #ifdef WOLFSSL_HAVE_MLDSA - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_ML_DSA_44) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_ML_DSA_44)) { tests++; if (demo_sign1_ml_dsa_44() != 0) { failures++; } } @@ -808,23 +831,23 @@ int main(int argc, char* argv[]) /* COSE_Encrypt0 demos */ #ifdef HAVE_AESGCM - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_A128GCM) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_A128GCM)) { tests++; if (demo_encrypt0_aesgcm(WOLFCOSE_ALG_A128GCM) != 0) { failures++; } } - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_A256GCM) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_A256GCM)) { tests++; if (demo_encrypt0_aesgcm(WOLFCOSE_ALG_A256GCM) != 0) { failures++; } } #endif #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_CHACHA20) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_CHACHA20)) { tests++; if (demo_encrypt0_chacha20() != 0) { failures++; } } #endif #ifdef HAVE_AESCCM - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_AES_CCM) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_AES_CCM)) { tests++; if (demo_encrypt0_aes_ccm() != 0) { failures++; } } @@ -832,18 +855,18 @@ int main(int argc, char* argv[]) /* COSE_Mac0 demos */ #if !defined(NO_HMAC) - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_HMAC256) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_HMAC256)) { tests++; if (demo_mac0_hmac(WOLFCOSE_ALG_HMAC256) != 0) { failures++; } } #ifdef WOLFSSL_SHA384 - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_HMAC384) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_HMAC384)) { tests++; if (demo_mac0_hmac(WOLFCOSE_ALG_HMAC384) != 0) { failures++; } } #endif #ifdef WOLFSSL_SHA512 - if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_HMAC512) { + if ((demoAlg == DEMO_ALG_ALL) || (demoAlg == DEMO_ALG_HMAC512)) { tests++; if (demo_mac0_hmac(WOLFCOSE_ALG_HMAC512) != 0) { failures++; } } diff --git a/examples/mac0_demo.c b/examples/mac0_demo.c index 358f78c..e7b7732 100644 --- a/examples/mac0_demo.c +++ b/examples/mac0_demo.c @@ -55,7 +55,7 @@ static int demo_mac0_hmac256(void) 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; - uint8_t payload[] = "HMAC-256 test payload"; + const uint8_t payload[] = "HMAC-256 test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -65,7 +65,7 @@ static int demo_mac0_hmac256(void) int ret; printf("--- COSE_Mac0 HMAC-256/256 ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); wc_CoseKey_Init(&key); ret = wc_CoseKey_SetSymmetric(&key, keyData, sizeof(keyData)); @@ -73,7 +73,7 @@ static int demo_mac0_hmac256(void) ret = wc_CoseMac0_Create(&key, WOLFCOSE_ALG_HMAC_256_256, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -87,7 +87,7 @@ static int demo_mac0_hmac256(void) scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); DEMO_ASSERT(ret == 0, "Verify MAC"); - DEMO_ASSERT(decPayloadLen == sizeof(payload) - 1, "Payload length"); + DEMO_ASSERT(decPayloadLen == sizeof(payload) - 1u, "Payload length"); DEMO_ASSERT(memcmp(decPayload, payload, decPayloadLen) == 0, "Payload match"); DEMO_ASSERT(hdr.alg == WOLFCOSE_ALG_HMAC_256_256, "Algorithm"); @@ -99,7 +99,7 @@ static int demo_mac0_hmac384(void) { WOLFCOSE_KEY key; uint8_t keyData[48]; - uint8_t payload[] = "HMAC-384 test payload"; + const uint8_t payload[] = "HMAC-384 test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -110,11 +110,11 @@ static int demo_mac0_hmac384(void) size_t i; printf("--- COSE_Mac0 HMAC-384/384 ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); /* Initialize 48-byte key */ for (i = 0; i < sizeof(keyData); i++) { - keyData[i] = (uint8_t)(i + 1); + keyData[i] = (uint8_t)(i + 1u); } wc_CoseKey_Init(&key); @@ -123,7 +123,7 @@ static int demo_mac0_hmac384(void) ret = wc_CoseMac0_Create(&key, WOLFCOSE_ALG_HMAC_384_384, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -147,7 +147,7 @@ static int demo_mac0_hmac512(void) { WOLFCOSE_KEY key; uint8_t keyData[64]; - uint8_t payload[] = "HMAC-512 test payload"; + const uint8_t payload[] = "HMAC-512 test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -158,11 +158,11 @@ static int demo_mac0_hmac512(void) size_t i; printf("--- COSE_Mac0 HMAC-512/512 ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); /* Initialize 64-byte key */ for (i = 0; i < sizeof(keyData); i++) { - keyData[i] = (uint8_t)(i + 1); + keyData[i] = (uint8_t)(i + 1u); } wc_CoseKey_Init(&key); @@ -171,7 +171,7 @@ static int demo_mac0_hmac512(void) ret = wc_CoseMac0_Create(&key, WOLFCOSE_ALG_HMAC_512_512, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -200,8 +200,8 @@ static int demo_mac0_with_aad(void) 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; - uint8_t payload[] = "Payload with AAD"; - uint8_t aad[] = "Additional authenticated data"; + const uint8_t payload[] = "Payload with AAD"; + const uint8_t aad[] = "Additional authenticated data"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -223,9 +223,9 @@ static int demo_mac0_with_aad(void) ret = wc_CoseMac0_Create(&key, WOLFCOSE_ALG_HMAC_256_256, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ - aad, sizeof(aad) - 1, /* extAad, extAadLen */ + aad, sizeof(aad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), out, sizeof(out), &outLen); DEMO_ASSERT(ret == 0, "Create MAC with AAD"); @@ -234,7 +234,7 @@ static int demo_mac0_with_aad(void) /* Verify with correct AAD */ ret = wc_CoseMac0_Verify(&key, out, outLen, NULL, 0, /* detachedPayload, detachedLen */ - aad, sizeof(aad) - 1, /* extAad, extAadLen */ + aad, sizeof(aad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); DEMO_ASSERT(ret == 0, "Verify with correct AAD"); @@ -242,7 +242,7 @@ static int demo_mac0_with_aad(void) /* Verify wrong AAD fails */ ret = wc_CoseMac0_Verify(&key, out, outLen, NULL, 0, /* detachedPayload, detachedLen */ - wrongAad, sizeof(wrongAad) - 1, /* extAad, extAadLen */ + wrongAad, sizeof(wrongAad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); DEMO_ASSERT(ret != 0, "Wrong AAD rejected"); @@ -260,7 +260,7 @@ static int demo_mac0_tamper_detection(void) 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; - uint8_t payload[] = "Tamper detection test"; + const uint8_t payload[] = "Tamper detection test"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[256]; size_t outLen = 0; @@ -277,7 +277,7 @@ static int demo_mac0_tamper_detection(void) ret = wc_CoseMac0_Create(&key, WOLFCOSE_ALG_HMAC_256_256, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -285,7 +285,7 @@ static int demo_mac0_tamper_detection(void) DEMO_ASSERT(ret == 0, "Create MAC"); /* Tamper with one byte */ - out[outLen - 5] ^= 0xFF; + out[outLen - 5u] ^= 0xFF; ret = wc_CoseMac0_Verify(&key, out, outLen, NULL, 0, /* detachedPayload, detachedLen */ @@ -304,15 +304,25 @@ int main(void) printf("=== wolfCOSE Mac0 Demo ===\n\n"); - if (demo_mac0_hmac256() != 0) failures++; + if (demo_mac0_hmac256() != 0) { + failures++; + } #ifdef WOLFSSL_SHA384 - if (demo_mac0_hmac384() != 0) failures++; + if (demo_mac0_hmac384() != 0) { + failures++; + } #endif #ifdef WOLFSSL_SHA512 - if (demo_mac0_hmac512() != 0) failures++; + if (demo_mac0_hmac512() != 0) { + failures++; + } #endif - if (demo_mac0_with_aad() != 0) failures++; - if (demo_mac0_tamper_detection() != 0) failures++; + if (demo_mac0_with_aad() != 0) { + failures++; + } + if (demo_mac0_tamper_detection() != 0) { + failures++; + } printf("\n=== Results: %d failure(s) ===\n", failures); return failures; diff --git a/examples/scenarios/firmware_update.c b/examples/scenarios/firmware_update.c index ddc4354..c58214d 100644 --- a/examples/scenarios/firmware_update.c +++ b/examples/scenarios/firmware_update.c @@ -131,7 +131,7 @@ static int oem_sign_firmware(WOLFCOSE_KEY* signingKey, int32_t alg, int ret; int i; uint8_t scratch[8192]; /* Larger for PQC */ - uint8_t kid[] = "OEM-firmware-signing-key-v1"; + const uint8_t kid[] = "OEM-firmware-signing-key-v1"; uint8_t firmwareHash[32]; wc_Sha256 sha; @@ -155,7 +155,7 @@ static int oem_sign_firmware(WOLFCOSE_KEY* signingKey, int32_t alg, /* Sign with detached payload */ ret = wc_CoseSign1_Sign(signingKey, alg, - kid, sizeof(kid) - 1, + kid, sizeof(kid) - 1u, NULL, 0, /* No inline payload */ firmware, firmwareSz, /* Detached payload */ NULL, 0, /* No AAD */ @@ -200,7 +200,7 @@ static int device_verify_firmware(WOLFCOSE_KEY* oemPubKey, printf(" SUCCESS: Signature verified\n"); printf(" Algorithm: %d\n", hdr.alg); - if (hdr.kidLen > 0) { + if (hdr.kidLen > 0u) { printf(" Key ID: %.*s\n", (int)hdr.kidLen, hdr.kid); } printf(" FIRMWARE APPROVED for installation\n"); diff --git a/examples/scenarios/group_broadcast_mac.c b/examples/scenarios/group_broadcast_mac.c index fbad241..351659c 100644 --- a/examples/scenarios/group_broadcast_mac.c +++ b/examples/scenarios/group_broadcast_mac.c @@ -248,6 +248,7 @@ static int unauthorized_subscriber_fails(const uint8_t* macMsg, size_t macMsgLen const uint8_t* payload = NULL; size_t payloadLen = 0; WOLFCOSE_HDR hdr; + const uint8_t unauthorizedKid[] = "unauthorized-subscriber"; printf("[Unauthorized Subscriber] Attempting to verify...\n"); @@ -263,8 +264,8 @@ static int unauthorized_subscriber_fails(const uint8_t* macMsg, size_t macMsgLen XMEMSET(&recipient, 0, sizeof(recipient)); recipient.algId = WOLFCOSE_ALG_DIRECT; recipient.key = &wrongKey; - recipient.kid = (const uint8_t*)"unauthorized-subscriber"; - recipient.kidLen = 23; + recipient.kid = unauthorizedKid; + recipient.kidLen = sizeof(unauthorizedKid) - 1u; /* Verify should fail */ ret = wc_CoseMac_Verify(&recipient, 0, diff --git a/examples/scenarios/iot_fleet_config.c b/examples/scenarios/iot_fleet_config.c index 9f7e1f7..b274dbb 100644 --- a/examples/scenarios/iot_fleet_config.c +++ b/examples/scenarios/iot_fleet_config.c @@ -232,6 +232,7 @@ static int unauthorized_device_fails(const uint8_t* encryptedMsg, int ret; WOLFCOSE_KEY wrongKey; WOLFCOSE_RECIPIENT recipient; + const uint8_t unauthKid[] = "unauthorized-device"; uint8_t wrongKeyData[16] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF @@ -255,8 +256,8 @@ static int unauthorized_device_fails(const uint8_t* encryptedMsg, XMEMSET(&recipient, 0, sizeof(recipient)); recipient.algId = WOLFCOSE_ALG_DIRECT; recipient.key = &wrongKey; - recipient.kid = (const uint8_t*)"unauthorized-device"; - recipient.kidLen = 19; + recipient.kid = unauthKid; + recipient.kidLen = sizeof(unauthKid) - 1u; /* Decrypt should fail */ ret = wc_CoseEncrypt_Decrypt(&recipient, 0, diff --git a/examples/scenarios/multi_party_approval.c b/examples/scenarios/multi_party_approval.c index 74fa971..c227196 100644 --- a/examples/scenarios/multi_party_approval.c +++ b/examples/scenarios/multi_party_approval.c @@ -132,6 +132,8 @@ static int sign_with_dual_control(WOLFCOSE_KEY* vendorKey, WOLFCOSE_KEY* oemKey, int ret; WOLFCOSE_SIGNATURE signers[2]; uint8_t scratch[512]; + const uint8_t vendorKid[] = "silicon-vendor-cert-001"; + const uint8_t oemKid[] = "oem-production-key-v2"; printf("[Signing] Creating dual-signed firmware approval...\n"); @@ -140,19 +142,19 @@ static int sign_with_dual_control(WOLFCOSE_KEY* vendorKey, WOLFCOSE_KEY* oemKey, signers[0].algId = WOLFCOSE_ALG_ES256; signers[0].key = vendorKey; - signers[0].kid = (const uint8_t*)"silicon-vendor-cert-001"; - signers[0].kidLen = 23; + signers[0].kid = vendorKid; + signers[0].kidLen = sizeof(vendorKid) - 1u; #ifdef WOLFSSL_SHA384 signers[1].algId = WOLFCOSE_ALG_ES384; signers[1].key = oemKey; - signers[1].kid = (const uint8_t*)"oem-production-key-v2"; - signers[1].kidLen = 21; + signers[1].kid = oemKid; + signers[1].kidLen = sizeof(oemKid) - 1u; #else signers[1].algId = WOLFCOSE_ALG_ES256; signers[1].key = oemKey; - signers[1].kid = (const uint8_t*)"oem-production-key-v2"; - signers[1].kidLen = 21; + signers[1].kid = oemKid; + signers[1].kidLen = sizeof(oemKid) - 1u; #endif /* Sign with both parties */ diff --git a/examples/scenarios/sensor_attestation.c b/examples/scenarios/sensor_attestation.c index 25bfe20..8abde77 100644 --- a/examples/scenarios/sensor_attestation.c +++ b/examples/scenarios/sensor_attestation.c @@ -122,7 +122,7 @@ static int sensor_create_attestation(WOLFCOSE_KEY* deviceKey, { int ret; uint8_t scratch[512]; - uint8_t kid[] = "device-attestation-key-001"; + const uint8_t kid[] = "device-attestation-key-001"; printf("[Sensor] Creating attestation token...\n"); printf(" Reading size: %zu bytes\n", readingLen); @@ -130,7 +130,7 @@ static int sensor_create_attestation(WOLFCOSE_KEY* deviceKey, /* Sign with nonce as external AAD */ ret = wc_CoseSign1_Sign(deviceKey, WOLFCOSE_ALG_ES256, - kid, sizeof(kid) - 1, + kid, sizeof(kid) - 1u, reading, readingLen, NULL, 0, /* No detached payload */ nonce, nonceLen, /* Nonce as external AAD */ @@ -176,7 +176,7 @@ static int verifier_check_attestation(WOLFCOSE_KEY* devicePubKey, printf(" Nonce binding verified (replay protection)\n"); printf(" Payload: %zu bytes of sensor data\n", payloadLen); - if (hdr.kidLen > 0) { + if (hdr.kidLen > 0u) { printf(" Key ID: %.*s\n", (int)hdr.kidLen, hdr.kid); } diff --git a/examples/sign1_demo.c b/examples/sign1_demo.c index 95ae7b4..2c2359e 100644 --- a/examples/sign1_demo.c +++ b/examples/sign1_demo.c @@ -43,7 +43,7 @@ static int demo_sign1_es256(void) WOLFCOSE_KEY key; ecc_key eccKey; WC_RNG rng; - uint8_t payload[] = "ES256 test payload"; + const uint8_t payload[] ="ES256 test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[512]; size_t outLen = 0; @@ -53,7 +53,7 @@ static int demo_sign1_es256(void) int ret; printf("--- COSE_Sign1 ES256 (P-256) ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); ret = wc_InitRng(&rng); DEMO_ASSERT(ret == 0, "Init RNG"); @@ -68,7 +68,7 @@ static int demo_sign1_es256(void) ret = wc_CoseSign1_Sign(&key, WOLFCOSE_ALG_ES256, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -82,7 +82,7 @@ static int demo_sign1_es256(void) scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); DEMO_ASSERT(ret == 0, "Verify"); - DEMO_ASSERT(decPayloadLen == sizeof(payload) - 1, "Payload length"); + DEMO_ASSERT(decPayloadLen == sizeof(payload) - 1u, "Payload length"); DEMO_ASSERT(memcmp(decPayload, payload, decPayloadLen) == 0, "Payload match"); DEMO_ASSERT(hdr.alg == WOLFCOSE_ALG_ES256, "Algorithm"); @@ -98,7 +98,7 @@ static int demo_sign1_es384(void) WOLFCOSE_KEY key; ecc_key eccKey; WC_RNG rng; - uint8_t payload[] = "ES384 test payload"; + const uint8_t payload[] ="ES384 test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[512]; size_t outLen = 0; @@ -108,7 +108,7 @@ static int demo_sign1_es384(void) int ret; printf("--- COSE_Sign1 ES384 (P-384) ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); ret = wc_InitRng(&rng); DEMO_ASSERT(ret == 0, "Init RNG"); @@ -123,7 +123,7 @@ static int demo_sign1_es384(void) ret = wc_CoseSign1_Sign(&key, WOLFCOSE_ALG_ES384, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -152,7 +152,7 @@ static int demo_sign1_es512(void) WOLFCOSE_KEY key; ecc_key eccKey; WC_RNG rng; - uint8_t payload[] = "ES512 test payload"; + const uint8_t payload[] ="ES512 test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[640]; size_t outLen = 0; @@ -162,7 +162,7 @@ static int demo_sign1_es512(void) int ret; printf("--- COSE_Sign1 ES512 (P-521) ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); ret = wc_InitRng(&rng); DEMO_ASSERT(ret == 0, "Init RNG"); @@ -177,7 +177,7 @@ static int demo_sign1_es512(void) ret = wc_CoseSign1_Sign(&key, WOLFCOSE_ALG_ES512, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -205,8 +205,8 @@ static int demo_sign1_with_aad(void) WOLFCOSE_KEY key; ecc_key eccKey; WC_RNG rng; - uint8_t payload[] = "Payload with AAD"; - uint8_t aad[] = "Additional authenticated data"; + const uint8_t payload[] ="Payload with AAD"; + const uint8_t aad[] ="Additional authenticated data"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[512]; size_t outLen = 0; @@ -235,9 +235,9 @@ static int demo_sign1_with_aad(void) ret = wc_CoseSign1_Sign(&key, WOLFCOSE_ALG_ES256, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ - aad, sizeof(aad) - 1, /* extAad, extAadLen */ + aad, sizeof(aad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), out, sizeof(out), &outLen, &rng); DEMO_ASSERT(ret == 0, "Sign with AAD"); @@ -246,7 +246,7 @@ static int demo_sign1_with_aad(void) /* Verify with correct AAD */ ret = wc_CoseSign1_Verify(&key, out, outLen, NULL, 0, /* detachedPayload, detachedLen */ - aad, sizeof(aad) - 1, /* extAad, extAadLen */ + aad, sizeof(aad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); DEMO_ASSERT(ret == 0, "Verify with correct AAD"); @@ -254,7 +254,7 @@ static int demo_sign1_with_aad(void) /* Verify wrong AAD fails */ ret = wc_CoseSign1_Verify(&key, out, outLen, NULL, 0, /* detachedPayload, detachedLen */ - wrongAad, sizeof(wrongAad) - 1, /* extAad, extAadLen */ + wrongAad, sizeof(wrongAad) - 1u, /* extAad, extAadLen */ scratch, sizeof(scratch), &hdr, &decPayload, &decPayloadLen); DEMO_ASSERT(ret != 0, "Wrong AAD rejected"); @@ -272,7 +272,7 @@ static int demo_sign1_eddsa(void) WOLFCOSE_KEY key; ed25519_key edKey; WC_RNG rng; - uint8_t payload[] = "EdDSA test payload"; + const uint8_t payload[] ="EdDSA test payload"; uint8_t scratch[WOLFCOSE_MAX_SCRATCH_SZ]; uint8_t out[512]; size_t outLen = 0; @@ -282,7 +282,7 @@ static int demo_sign1_eddsa(void) int ret; printf("--- COSE_Sign1 EdDSA (Ed25519) ---\n"); - printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1); + printf(" Payload: \"%s\" (%zu bytes)\n", payload, sizeof(payload) - 1u); ret = wc_InitRng(&rng); DEMO_ASSERT(ret == 0, "Init RNG"); @@ -297,7 +297,7 @@ static int demo_sign1_eddsa(void) ret = wc_CoseSign1_Sign(&key, WOLFCOSE_ALG_EDDSA, NULL, 0, /* kid, kidLen */ - payload, sizeof(payload) - 1, /* payload, payloadLen */ + payload, sizeof(payload) - 1u, /* payload, payloadLen */ NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ scratch, sizeof(scratch), @@ -327,18 +327,28 @@ int main(void) printf("=== wolfCOSE Sign1 Demo ===\n\n"); #ifdef HAVE_ECC - if (demo_sign1_es256() != 0) failures++; + if (demo_sign1_es256() != 0) { + failures++; + } #ifdef WOLFSSL_SHA384 - if (demo_sign1_es384() != 0) failures++; + if (demo_sign1_es384() != 0) { + failures++; + } #endif #ifdef WOLFSSL_SHA512 - if (demo_sign1_es512() != 0) failures++; + if (demo_sign1_es512() != 0) { + failures++; + } #endif - if (demo_sign1_with_aad() != 0) failures++; + if (demo_sign1_with_aad() != 0) { + failures++; + } #endif #ifdef HAVE_ED25519 - if (demo_sign1_eddsa() != 0) failures++; + if (demo_sign1_eddsa() != 0) { + failures++; + } #endif printf("\n=== Results: %d failure(s) ===\n", failures); diff --git a/tests/test_cose.c b/tests/test_cose.c index a42b749..cad6ebe 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -14411,53 +14411,56 @@ static void test_multi_sign_verify_wrong_signer(void) ret = wc_ecc_make_key(&rng, 32, &eccKey1); if (ret != 0) { TEST_ASSERT(0, "ecc keygen 1"); - goto cleanup; } - ret = wc_ecc_make_key(&rng, 32, &eccKey2); - if (ret != 0) { - TEST_ASSERT(0, "ecc keygen 2"); - goto cleanup; + if (ret == 0) { + ret = wc_ecc_make_key(&rng, 32, &eccKey2); + if (ret != 0) { + TEST_ASSERT(0, "ecc keygen 2"); + } } - ret = wc_ecc_make_key(&rng, 32, &eccWrongKey); - if (ret != 0) { - TEST_ASSERT(0, "ecc keygen wrong"); - goto cleanup; + if (ret == 0) { + ret = wc_ecc_make_key(&rng, 32, &eccWrongKey); + if (ret != 0) { + TEST_ASSERT(0, "ecc keygen wrong"); + } } - (void)wc_CoseKey_Init(&key1); - (void)wc_CoseKey_SetEcc(&key1, WOLFCOSE_CRV_P256, &eccKey1); - (void)wc_CoseKey_Init(&key2); - (void)wc_CoseKey_SetEcc(&key2, WOLFCOSE_CRV_P256, &eccKey2); - (void)wc_CoseKey_Init(&wrongKey); - (void)wc_CoseKey_SetEcc(&wrongKey, WOLFCOSE_CRV_P256, &eccWrongKey); + if (ret == 0) { + (void)wc_CoseKey_Init(&key1); + (void)wc_CoseKey_SetEcc(&key1, WOLFCOSE_CRV_P256, &eccKey1); + (void)wc_CoseKey_Init(&key2); + (void)wc_CoseKey_SetEcc(&key2, WOLFCOSE_CRV_P256, &eccKey2); + (void)wc_CoseKey_Init(&wrongKey); + (void)wc_CoseKey_SetEcc(&wrongKey, WOLFCOSE_CRV_P256, &eccWrongKey); - memset(signers, 0, sizeof(signers)); - signers[0].algId = WOLFCOSE_ALG_ES256; - signers[0].key = &key1; - signers[1].algId = WOLFCOSE_ALG_ES256; - signers[1].key = &key2; + memset(signers, 0, sizeof(signers)); + signers[0].algId = WOLFCOSE_ALG_ES256; + signers[0].key = &key1; + signers[1].algId = WOLFCOSE_ALG_ES256; + signers[1].key = &key2; - ret = wc_CoseSign_Sign(signers, 2, - payload, sizeof(payload) - 1, - NULL, 0, NULL, 0, - scratch, sizeof(scratch), - out, sizeof(out), &outLen, &rng); - if (ret != WOLFCOSE_SUCCESS) { - TEST_ASSERT(0, "multi-sign create"); - goto cleanup; + ret = wc_CoseSign_Sign(signers, 2, + payload, sizeof(payload) - 1, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + out, sizeof(out), &outLen, &rng); + if (ret != WOLFCOSE_SUCCESS) { + TEST_ASSERT(0, "multi-sign create"); + } } - /* Try to verify with wrong key for signer 0 */ - ret = wc_CoseSign_Verify(&wrongKey, 0, - out, outLen, - NULL, 0, NULL, 0, - scratch, sizeof(scratch), - &hdr, &decPayload, &decPayloadLen); - TEST_ASSERT(ret == WOLFCOSE_E_COSE_SIG_FAIL, "multi sign wrong key verify"); + if (ret == WOLFCOSE_SUCCESS) { + /* Try to verify with wrong key for signer 0 */ + ret = wc_CoseSign_Verify(&wrongKey, 0, + out, outLen, + NULL, 0, NULL, 0, + scratch, sizeof(scratch), + &hdr, &decPayload, &decPayloadLen); + TEST_ASSERT(ret == WOLFCOSE_E_COSE_SIG_FAIL, "multi sign wrong key verify"); + } -cleanup: (void)wc_ecc_free(&eccKey1); (void)wc_ecc_free(&eccKey2); (void)wc_ecc_free(&eccWrongKey);