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/cose-encoding-notes.md b/docs/cose-encoding-notes.md new file mode 100644 index 0000000..65883be --- /dev/null +++ b/docs/cose-encoding-notes.md @@ -0,0 +1,68 @@ +# 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. + +## 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/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 e6125f9..70a1d21 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) @@ -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 ef769db..e7b7732 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) @@ -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 d4d5644..2c2359e 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) @@ -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/include/wolfcose/wolfcose.h b/include/wolfcose/wolfcose.h index bd524c5..7776e69 100644 --- a/include/wolfcose/wolfcose.h +++ b/include/wolfcose/wolfcose.h @@ -304,6 +304,30 @@ 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. + * - 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 */ #define WOLFCOSE_ALG_UNSET ((int32_t)0) #define WOLFCOSE_ALG_ES256 (-7) @@ -914,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. @@ -1058,10 +1087,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. @@ -1130,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. diff --git a/src/wolfcose.c b/src/wolfcose.c index 2f18d5c..5afa454 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 @@ -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) ----- */ /** @@ -455,6 +464,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 ----- */ @@ -572,6 +613,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 >= INT32_MIN) && (val <= 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 +840,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,11 +894,28 @@ 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; } } } + 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; @@ -971,6 +1041,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; } @@ -1770,6 +1844,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)) { @@ -1793,6 +1877,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 +1902,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 +1932,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; } @@ -1864,6 +1960,34 @@ 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; + } + +#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 @@ -1883,23 +2007,18 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) 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))) { + 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)); (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( @@ -2079,16 +2198,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; @@ -2193,7 +2302,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, @@ -3040,12 +3149,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; } } @@ -3592,6 +3702,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; } @@ -3847,6 +3962,8 @@ int wc_CoseSign1_Verify(WOLFCOSE_KEY* key, /* No action required */ } + wolfCose_HdrClearOnFail(ret, hdr); + /* Cleanup: always executed */ (void)wolfCose_ForceZero(hashBuf, sizeof(hashBuf)); if (scratch != NULL) { @@ -4483,9 +4600,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 */ @@ -4493,6 +4612,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( @@ -4716,6 +4844,8 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, /* No action required */ } + wolfCose_HdrClearOnFail(ret, hdr); + /* Cleanup: always executed */ (void)wolfCose_ForceZero(hashBuf, sizeof(hashBuf)); if (scratch != NULL) { @@ -5211,6 +5341,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; } @@ -5374,6 +5509,8 @@ int wc_CoseEncrypt0_Decrypt(WOLFCOSE_KEY* key, /* No action required */ } + wolfCose_HdrClearOnFail(ret, hdr); + /* Cleanup: always executed */ #if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) if (aesInited != 0) { @@ -5661,9 +5798,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; @@ -5684,6 +5821,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; @@ -5738,6 +5881,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) { @@ -5974,6 +6120,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. */ @@ -6012,6 +6163,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); @@ -6079,6 +6233,8 @@ int wc_CoseMac0_Verify(const WOLFCOSE_KEY* key, /* No action required */ } + wolfCose_HdrClearOnFail(ret, hdr); + /* Cleanup: always executed */ #ifndef NO_HMAC if (hmacInited != 0) { @@ -6545,8 +6701,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); @@ -6822,8 +6981,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)) { @@ -6841,6 +7003,56 @@ 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. */ + 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) && @@ -6864,6 +7076,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) { @@ -6900,7 +7118,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 */ @@ -6927,6 +7150,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); @@ -7130,6 +7362,7 @@ int wc_CoseEncrypt_Decrypt(const WOLFCOSE_RECIPIENT* recipient, else { *plaintextLen = payloadLen; } + wolfCose_HdrClearOnFail(ret, hdr); return ret; } @@ -7214,11 +7447,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) { @@ -7280,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, @@ -7585,10 +7827,57 @@ 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) { + 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). */ + 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) && @@ -7633,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, @@ -7715,6 +8008,8 @@ int wc_CoseMac_Verify(const WOLFCOSE_RECIPIENT* recipient, /* No action required */ } + wolfCose_HdrClearOnFail(ret, hdr); + return ret; } #endif /* WOLFCOSE_MAC_VERIFY */ diff --git a/src/wolfcose_cbor.c b/src/wolfcose_cbor.c index c78eafd..3a0011c 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 { @@ -209,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) || @@ -328,7 +352,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 +387,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 +409,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 +552,12 @@ static int wolfCose_CBOR_DecodeContainerStart(WOLFCOSE_CBOR_CTX* ctx, if (item.majorType != majorType) { ret = WOLFCOSE_E_CBOR_TYPE; } + /* 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; + } else { *count = (size_t)item.val; } 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. diff --git a/tests/test_cbor.c b/tests/test_cbor.c index 5b48824..5fc867d 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 { \ @@ -300,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; @@ -676,6 +692,73 @@ 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"); +} + +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) { @@ -866,6 +949,8 @@ 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_reject_non_preferred(); test_cbor_errors(); test_cbor_negative_map_keys(); diff --git a/tests/test_cose.c b/tests/test_cose.c index a8f62aa..cad6ebe 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 @@ -1212,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 ----- */ @@ -1416,6 +1500,66 @@ 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; + wc_MlDsaKey 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_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); + if (ret != 0) { TEST_ASSERT(0, "dl init"); } + if (ret == 0) { dlInited = 1; } + } + if (ret == 0) { + ret = wc_MlDsaKey_SetParams(&dlKey, WC_ML_DSA_44); + if (ret != 0) { TEST_ASSERT(0, "dl set level"); } + } + if (ret == 0) { + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); + if (ret != 0) { TEST_ASSERT(0, "dl keygen"); } + } + if (ret == 0) { + (void)wc_CoseKey_Init(&signKey); + (void)wc_CoseKey_SetMlDsa(&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"); + } + + if (dlInited != 0) { (void)wc_MlDsaKey_Free(&dlKey); } + if (rngInited != 0) { (void)wc_FreeRng(&rng); } +} #endif /* WOLFSSL_HAVE_MLDSA */ /* ----- COSE_Sign1 with external AAD ----- */ @@ -1587,6 +1731,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 ML-DSA encode/decode round-trip ----- */ @@ -1785,6 +1974,146 @@ 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"); +} + +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"); +} + +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"); +} + +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) { @@ -2684,13 +3013,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; } @@ -6092,6 +6428,127 @@ 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_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; @@ -8335,7 +8792,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"); } @@ -8757,39 +9214,405 @@ static void test_cbor_edge_cases(void) } } -/* ----- Header processing compliance tests ----- */ -static void test_cose_protected_hdr_empty_map(void) +/* ----- Header processing compliance tests ----- */ +static void test_cose_protected_hdr_empty_map(void) +{ + /* RFC 9052 Section 3: recipients must accept both h'' and h'a0'. */ + int ret; + WOLFCOSE_HDR hdr; + WOLFCOSE_HDR_STATE hdrState; + uint8_t emptyMap[] = {0xA0u}; + + TEST_LOG(" [Protected Header: empty serialized map]\n"); + XMEMSET(&hdr, 0, sizeof(hdr)); + ret = wolfCose_DecodeProtectedHdr(emptyMap, sizeof(emptyMap), &hdr, + &hdrState); + TEST_ASSERT(ret == WOLFCOSE_SUCCESS, + "DecodeProtectedHdr accepts serialized empty map"); + TEST_ASSERT((hdrState.labelBits == 0u) && (hdrState.extraCount == 0u), + "DecodeProtectedHdr empty map state"); +} + +static void test_cose_protected_hdr_trailing(void) +{ + int ret; + WOLFCOSE_HDR hdr; + WOLFCOSE_HDR_STATE hdrState; + uint8_t trailing[] = {0xA1u, 0x01u, 0x26u, 0xFFu}; /* {1: -7}, garbage */ + + TEST_LOG(" [Protected Header: trailing bytes]\n"); + XMEMSET(&hdr, 0, sizeof(hdr)); + ret = wolfCose_DecodeProtectedHdr(trailing, sizeof(trailing), &hdr, + &hdrState); + TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, + "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; + 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"); +} + +#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)"); +} + +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); } +} + +static void test_cose_encrypt_recipient_alg_checks(void) { - /* RFC 9052 Section 3: recipients must accept both h'' and h'a0'. */ - int ret; + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipient; WOLFCOSE_HDR hdr; - WOLFCOSE_HDR_STATE hdrState; - uint8_t emptyMap[] = {0xA0u}; + 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(" [Protected Header: empty serialized map]\n"); - XMEMSET(&hdr, 0, sizeof(hdr)); - ret = wolfCose_DecodeProtectedHdr(emptyMap, sizeof(emptyMap), &hdr, - &hdrState); - TEST_ASSERT(ret == WOLFCOSE_SUCCESS, - "DecodeProtectedHdr accepts serialized empty map"); - TEST_ASSERT((hdrState.labelBits == 0u) && (hdrState.extraCount == 0u), - "DecodeProtectedHdr empty map state"); + 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; + 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; + 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"); } -static void test_cose_protected_hdr_trailing(void) +static void test_cose_encrypt_multi_per_recipient(void) { - int ret; + WOLFCOSE_KEY key; + WOLFCOSE_RECIPIENT recipients[2]; WOLFCOSE_HDR hdr; - WOLFCOSE_HDR_STATE hdrState; - uint8_t trailing[] = {0xA1u, 0x01u, 0x26u, 0xFFu}; /* {1: -7}, garbage */ + 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; + int ret; + size_t r; + const uint8_t payload[] = "multi recipient direct"; - TEST_LOG(" [Protected Header: trailing bytes]\n"); - XMEMSET(&hdr, 0, sizeof(hdr)); - ret = wolfCose_DecodeProtectedHdr(trailing, sizeof(trailing), &hdr, - &hdrState); - TEST_ASSERT(ret == WOLFCOSE_E_CBOR_MALFORMED, - "DecodeProtectedHdr rejects trailing bytes"); + 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) { @@ -9386,6 +10209,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 @@ -10183,7 +11032,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; @@ -10191,12 +11040,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)); @@ -10209,19 +11055,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); } @@ -10229,7 +11064,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; @@ -10244,7 +11079,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)); @@ -10269,11 +11104,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); @@ -10557,6 +11389,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; @@ -10577,14 +11445,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 @@ -10836,6 +11704,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); @@ -10849,7 +11719,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 */ @@ -10863,7 +11734,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 @@ -10912,10 +11784,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); @@ -10968,10 +11843,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; @@ -13533,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); @@ -13894,6 +14775,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; @@ -13913,17 +14795,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); } @@ -14464,6 +15347,177 @@ 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; + 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); +} + +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 */ @@ -14531,6 +15585,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 WOLFSSL_HAVE_MLDSA test_cose_key_mldsa("ML-DSA-44", WOLFCOSE_ALG_ML_DSA_44, WC_ML_DSA_44); @@ -14574,6 +15629,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 */ @@ -14583,16 +15639,21 @@ int test_cose(void) test_cose_sign1_pss("PS512", WOLFCOSE_ALG_PS512); #endif - /* ML-DSA (ML-DSA) signature tests */ + /* ML-DSA signature tests */ #ifdef WOLFSSL_HAVE_MLDSA test_cose_sign1_ml_dsa("ML-DSA-44", WOLFCOSE_ALG_ML_DSA_44, WC_ML_DSA_44); test_cose_sign1_ml_dsa("ML-DSA-65", WOLFCOSE_ALG_ML_DSA_65, WC_ML_DSA_65); test_cose_sign1_ml_dsa("ML-DSA-87", WOLFCOSE_ALG_ML_DSA_87, WC_ML_DSA_87); + test_cose_sign1_ml_dsa_level_mismatch(); #endif /* Mac0 basic tests */ #if !defined(NO_HMAC) test_cose_mac0_hmac256(); + 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(); @@ -14692,6 +15753,8 @@ 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(); + test_cose_sign1_hdr_cleared_on_failure(); #endif #ifdef HAVE_AESGCM test_cose_encrypt0_tampered_ct_byte(); @@ -14753,6 +15816,20 @@ 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(); +#endif +#ifndef NO_HMAC + test_cose_mac_dup_recipient_unprot_hdr(); +#endif +#ifdef HAVE_AESGCM + 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(); test_cose_protected_hdr_dup_label(); @@ -14826,12 +15903,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(); @@ -14847,6 +15924,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(); @@ -15060,6 +16138,8 @@ 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(); + test_ecdh_es_recipient_protected_bound(); #endif /* Mock failure injection tests */ 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 */