diff --git a/.github/workflows/lean-build.yml b/.github/workflows/lean-build.yml index 20b9a8f..c30b3c2 100644 --- a/.github/workflows/lean-build.yml +++ b/.github/workflows/lean-build.yml @@ -33,10 +33,13 @@ jobs: uses: actions/cache@v4 with: path: ~/wolfssl-lean - key: wolfssl-lean-ecc-v1-${{ steps.wolfssl-rev.outputs.sha }} + key: wolfssl-lean-ecc-verifyonly-v2-${{ steps.wolfssl-rev.outputs.sha }} - # Minimal backend for ES256 verification: ECC + SHA-256 only. - # No keygen and no RNG are needed to verify a COSE_Sign1. + # Verify-only ECC backend: NO_ECC_SIGN compiles ECC signing out entirely, + # mirroring an embedded verify-only target (e.g. wolfBoot). This is the + # config that catches a lean wolfCOSE accidentally pulling in a signing + # helper such as wc_ecc_sign_hash (via wolfCose_EccSignRaw): it links here + # only if the verify path references no sign-side symbols. - name: Build minimal wolfSSL if: steps.cache-wolfssl.outputs.cache-hit != 'true' run: | @@ -45,17 +48,23 @@ jobs: cd wolfssl-lean-src ./autogen.sh ./configure --enable-cryptonly --enable-ecc \ + CFLAGS="-DNO_ECC_SIGN" \ --prefix=$HOME/wolfssl-lean make -j$(nproc) make install + # No --gc-sections here on purpose. Against the NO_ECC_SIGN wolfSSL above, a + # verify path that leaks a signing helper (e.g. wc_ecc_sign_hash via + # wolfCose_EccSignRaw) must fail to link. With dead-code stripping the unused + # helper would be removed and the missing-symbol link error would never fire, + # so the regression would slip through unnoticed. - name: Build and run the lean verify-only example run: | export WOLFSSL_DIR=$HOME/wolfssl-lean export LD_LIBRARY_PATH=$WOLFSSL_DIR/lib make lean-verify \ CFLAGS="-std=c11 -DHAVE_ANONYMOUS_INLINE_AGGREGATES=1 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -ffunction-sections -fdata-sections -I./include -isystem $WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl -Wl,--gc-sections" + LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" - name: Assert the signing API is absent from a verify-only build run: | diff --git a/src/wolfcose.c b/src/wolfcose.c index 0932746..07669ac 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -539,6 +539,7 @@ static int wolfCose_HmacCheckKeyLen(int32_t alg, size_t keyLen) /* ----- Internal: ECC DER <-> raw r||s conversion ----- */ #ifdef WOLFCOSE_HAVE_ECDSA +#if defined(WOLFCOSE_SIGN1_SIGN) || defined(WOLFCOSE_SIGN_SIGN) int wolfCose_EccSignRaw(const uint8_t* hash, size_t hashLen, uint8_t* sigBuf, size_t* sigLen, size_t coordSz, WC_RNG* rng, ecc_key* eccKey) @@ -601,16 +602,20 @@ int wolfCose_EccSignRaw(const uint8_t* hash, size_t hashLen, } return ret; } +#endif /* WOLFCOSE_SIGN1_SIGN || WOLFCOSE_SIGN_SIGN */ int wolfCose_EccVerifyRaw(const uint8_t* sigBuf, size_t sigLen, const uint8_t* hash, size_t hashLen, size_t coordSz, ecc_key* eccKey, int* verified) { int ret; +#ifndef NO_ASN uint8_t derSig[ECC_MAX_SIG_SIZE]; word32 derSigLen = (word32)sizeof(derSig); +#endif - if ((sigBuf == NULL) || (hash == NULL) || (eccKey == NULL) || (verified == NULL)) { + if ((sigBuf == NULL) || (hash == NULL) || (eccKey == NULL) || + (verified == NULL)) { ret = WOLFCOSE_E_INVALID_ARG; } else if (sigLen != (coordSz * 2u)) { @@ -619,7 +624,20 @@ int wolfCose_EccVerifyRaw(const uint8_t* sigBuf, size_t sigLen, else { *verified = 0; - /* Convert raw r||s to DER */ +#ifdef NO_ASN + /* NO_ASN wolfCrypt (e.g. wolfBoot): wc_ecc_verify_hash consumes the raw + * r||s signature directly, so no DER conversion is needed and the + * sign-side helper wc_ecc_rs_raw_to_sig (gated on ASN) is not required. + * wolfCOSE holds no mp_int itself, keeping the verify path allocation + * free at this layer. */ + INJECT_FAILURE(WOLF_FAIL_ECC_VERIFY, -1, + ret = wc_ecc_verify_hash(sigBuf, (word32)sigLen, hash, + (word32)hashLen, verified, eccKey)); + if (ret != 0) { + ret = WOLFCOSE_E_CRYPTO; + } +#else + /* Convert raw r||s to DER, then verify. */ INJECT_FAILURE(WOLF_FAIL_ECC_RS_TO_SIG, -1, ret = wc_ecc_rs_raw_to_sig(sigBuf, (word32)coordSz, &sigBuf[coordSz], (word32)coordSz, @@ -636,6 +654,7 @@ int wolfCose_EccVerifyRaw(const uint8_t* sigBuf, size_t sigLen, } } (void)wolfCose_ForceZero(derSig, sizeof(derSig)); +#endif } return ret; } diff --git a/src/wolfcose_internal.h b/src/wolfcose_internal.h index 8f8d2b9..310f5ba 100644 --- a/src/wolfcose_internal.h +++ b/src/wolfcose_internal.h @@ -292,10 +292,12 @@ WOLFCOSE_LOCAL int wolfCose_HmacType(int32_t alg, int* hmacType); * \param eccKey Caller-owned ECC key with private key. * \return WOLFCOSE_SUCCESS or negative error code. */ +#if defined(WOLFCOSE_SIGN1_SIGN) || defined(WOLFCOSE_SIGN_SIGN) WOLFCOSE_LOCAL int wolfCose_EccSignRaw(const uint8_t* hash, size_t hashLen, uint8_t* sigBuf, size_t* sigLen, size_t coordSz, WC_RNG* rng, ecc_key* eccKey); +#endif /* WOLFCOSE_SIGN1_SIGN || WOLFCOSE_SIGN_SIGN */ /** * \brief Verify a raw r||s ECC signature.