Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/downstream-wolfboot-suit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Downstream integration guard: build wolfBoot's SUIT support against THIS
# wolfCOSE and run its tests. wolfBoot is wolfCOSE's flagship lean verify-only
# consumer; the sim build links lean wolfCOSE against a verify-only (NO_ASN,
# NO_ECC_SIGN) wolfCrypt, which is exactly the configuration that surfaces
# verify-only regressions (e.g. a signing helper leaking into a verify build).
# A change to wolfCOSE that breaks wolfBoot fails here instead of in the field.
name: Downstream wolfBoot SUIT

on:
push:
pull_request:

jobs:
wolfboot-suit:
name: wolfBoot SUIT lean sim + host test against this wolfCOSE
runs-on: ubuntu-latest
steps:
- name: Checkout wolfCOSE (under test)
uses: actions/checkout@v4
with:
path: wolfcose-under-test

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential autoconf automake libtool \
python3 python3-pip

# wolfBoot carries the SUIT sources + tests. Track the branch until SUIT
# lands on wolfBoot master, then switch this to wolfSSL/wolfBoot.
- name: Checkout wolfBoot (SUIT branch) + wolfSSL submodule
run: |
git clone --depth 1 --branch suit-manifest \
https://github.com/aidangarske/wolfBoot.git
cd wolfBoot
git submodule update --init --depth 1 lib/wolfssl
rm -rf lib/wolfCOSE
cp -r "$GITHUB_WORKSPACE/wolfcose-under-test" lib/wolfCOSE

# The critical guard: lean wolfCOSE linked against wolfBoot's verify-only
# wolfCrypt. A verify-only regression in wolfCOSE fails this link.
- name: Build wolfBoot sim WOLFBOOT_SUIT=1 (lean verify-only)
run: |
cd wolfBoot
cp config/examples/sim.config .config
make WOLFBOOT_SUIT=1 SIGN=ECC256

- name: Build host wolfSSL (ECC) for the authoring-side host test
run: |
git clone --depth 1 https://github.com/wolfSSL/wolfssl.git
cd wolfssl
./autogen.sh
./configure --enable-cryptonly --enable-ecc \
--prefix=$HOME/wolfssl-install
make -j"$(nproc)"
make install

- name: Host SUIT test (author + verify + install + tamper)
run: |
cd wolfBoot
WOLFSSL_DIR=$HOME/wolfssl-install ./tests/suit_host_test.sh

- name: Independent cross-check (cbor2 + cryptography)
run: |
pip3 install --quiet cbor2 cryptography
cd wolfBoot
python3 tests/suit_cross_check.py
17 changes: 13 additions & 4 deletions .github/workflows/lean-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand All @@ -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: |
Expand Down
23 changes: 21 additions & 2 deletions src/wolfcose.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)) {
Expand All @@ -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,
Expand All @@ -636,6 +654,7 @@ int wolfCose_EccVerifyRaw(const uint8_t* sigBuf, size_t sigLen,
}
}
(void)wolfCose_ForceZero(derSig, sizeof(derSig));
#endif
}
return ret;
}
Expand Down
2 changes: 2 additions & 0 deletions src/wolfcose_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading