diff --git a/.github/workflows/_resolve-wolfssl.yml b/.github/workflows/_resolve-wolfssl.yml new file mode 100644 index 0000000..98a1ea1 --- /dev/null +++ b/.github/workflows/_resolve-wolfssl.yml @@ -0,0 +1,63 @@ +name: Resolve wolfSSL versions + +on: + workflow_call: + outputs: + matrix: + description: 'JSON matrix include of wolfSSL refs (master + latest -stable), each with a pqc flag' + value: ${{ jobs.resolve.outputs.matrix }} + refs: + description: 'JSON array of wolfSSL refs ([latest -stable, master]) for use as a matrix axis' + value: ${{ jobs.resolve.outputs.refs }} + latest-stable: + description: 'Latest wolfSSL v*-stable tag resolved at run time' + value: ${{ jobs.resolve.outputs.latest-stable }} + latest-pqc: + description: 'true when latest -stable is strictly newer than the v5.9.1 PQC floor' + value: ${{ jobs.resolve.outputs.latest-pqc }} + +permissions: + contents: read + +jobs: + resolve: + name: Resolve wolfSSL version matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + refs: ${{ steps.set-matrix.outputs.refs }} + latest-stable: ${{ steps.set-matrix.outputs.latest-stable }} + latest-pqc: ${{ steps.set-matrix.outputs.latest-pqc }} + steps: + - name: Resolve latest -stable wolfSSL tag and PQC eligibility + id: set-matrix + run: | + set -euo pipefail + LATEST=$(git ls-remote --tags --refs https://github.com/wolfSSL/wolfssl.git 'v*-stable' \ + | awk -F/ '{print $NF}' | sort -V | tail -n 1) + if [ -z "${LATEST:-}" ]; then + echo "::error::Could not resolve latest wolfSSL -stable tag from remote" + exit 1 + fi + echo "Latest stable wolfSSL: $LATEST" + echo "latest-stable=$LATEST" >> "$GITHUB_OUTPUT" + # Enable PQC only when $LATEST is strictly newer than v5.9.1-stable. + # The wc_MlDsaKey API lands post-v5.9.1-stable; older stables only + # ship the legacy wc_dilithium_* API wolfCOSE no longer calls. + PQC_FLOOR="v5.9.1-stable" + if [ "$(printf '%s\n%s\n' "$PQC_FLOOR" "$LATEST" | sort -V | tail -n 1)" != "$PQC_FLOOR" ]; then + LATEST_PQC=true + else + LATEST_PQC=false + fi + echo "latest-stable PQC eligible: $LATEST_PQC" + echo "latest-pqc=$LATEST_PQC" >> "$GITHUB_OUTPUT" + MATRIX=$(jq -nc --arg latest "$LATEST" --argjson latest_pqc "$LATEST_PQC" '{ + include: [ + {"wolfssl-version":$latest,"wolfssl-ref":$latest,"pqc":$latest_pqc}, + {"wolfssl-version":"master","wolfssl-ref":"master","pqc":true} + ] + }') + echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT" + REFS=$(jq -nc --arg latest "$LATEST" '[$latest, "master"]') + echo "refs=$REFS" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 9c0b79d..25b15dd 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -12,13 +12,18 @@ concurrency: cancel-in-progress: true jobs: + discover: + uses: ./.github/workflows/_resolve-wolfssl.yml + build: - name: ${{ matrix.os }} + name: ${{ matrix.os }} (wolfSSL ${{ matrix.wolfssl-ref }}) + needs: discover runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, ubuntu-22.04, macos-latest] + wolfssl-ref: ${{ fromJson(needs.discover.outputs.refs) }} steps: - uses: actions/checkout@v4 @@ -33,27 +38,38 @@ jobs: if: runner.os == 'macOS' run: brew install autoconf automake libtool - - name: Cache wolfSSL + - name: Cache wolfSSL (${{ matrix.wolfssl-ref }}) + if: matrix.wolfssl-ref != 'master' id: cache-wolfssl uses: actions/cache@v4 with: path: ~/wolfssl-install - key: wolfssl-${{ matrix.os }}-v3-full + key: wolfssl-${{ matrix.os }}-${{ matrix.wolfssl-ref }}-v4 - - name: Build wolfSSL - if: steps.cache-wolfssl.outputs.cache-hit != 'true' + - name: Build wolfSSL (${{ matrix.wolfssl-ref }}) + if: matrix.wolfssl-ref == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' run: | cd ~ - git clone --depth 1 https://github.com/wolfSSL/wolfssl.git + git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ + https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh + # PQC (ML-DSA wc_MlDsaKey) only exists on master or stable releases + # strictly newer than the v5.9.1 floor. + REF="${{ matrix.wolfssl-ref }}" + if [ "$REF" = "master" ] || \ + [ "$(printf '%s\n%s\n' v5.9.1-stable "$REF" | sort -V | tail -n1)" != "v5.9.1-stable" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-curve448 \ --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 \ --enable-keygen --enable-hkdf --enable-aeskeywrap \ --enable-chacha --enable-poly1305 \ - --enable-dilithium --enable-rsapss \ + $PQC_FLAGS --enable-rsapss \ --prefix=$HOME/wolfssl-install make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) make install @@ -81,8 +97,12 @@ jobs: LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" coverage: - name: Code Coverage + name: Code Coverage (wolfSSL ${{ matrix.wolfssl-version }}) + needs: discover runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.discover.outputs.matrix) }} steps: - uses: actions/checkout@v4 @@ -92,27 +112,36 @@ jobs: sudo apt-get update sudo apt-get install -y autoconf automake libtool lcov - - name: Cache wolfSSL + - name: Cache wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version != 'master' id: cache-wolfssl uses: actions/cache@v4 with: path: ~/wolfssl-install - key: wolfssl-ubuntu-latest-coverage-v3-full + key: wolfssl-coverage-${{ matrix.wolfssl-version }}-v4 - - name: Build wolfSSL - if: steps.cache-wolfssl.outputs.cache-hit != 'true' + - name: Build wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' + env: + PQC: ${{ matrix.pqc }} run: | cd ~ - git clone --depth 1 https://github.com/wolfSSL/wolfssl.git + git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ + https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh + if [ "$PQC" = "true" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-curve448 \ --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 \ --enable-keygen --enable-hkdf --enable-aeskeywrap \ --enable-chacha --enable-poly1305 \ - --enable-dilithium --enable-rsapss \ + $PQC_FLAGS --enable-rsapss \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install @@ -149,6 +178,7 @@ jobs: echo "Coverage ${COVERAGE}% meets threshold of 75%" - name: Upload coverage report + if: matrix.wolfssl-version == 'master' uses: actions/upload-artifact@v4 with: name: coverage-report diff --git a/.github/workflows/cmdline-test.yml b/.github/workflows/cmdline-test.yml new file mode 100644 index 0000000..f63f775 --- /dev/null +++ b/.github/workflows/cmdline-test.yml @@ -0,0 +1,79 @@ +name: Command-Line Tool Test + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + discover: + uses: ./.github/workflows/_resolve-wolfssl.yml + + cmdline: + name: Command-line tool (wolfSSL ${{ matrix.wolfssl-version }}) + needs: discover + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.discover.outputs.matrix) }} + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake libtool + + - name: Cache wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version != 'master' + id: cache-wolfssl + uses: actions/cache@v4 + with: + path: ~/wolfssl-install + key: wolfssl-clitest-${{ matrix.wolfssl-version }}-v3 + + - name: Build wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' + env: + PQC: ${{ matrix.pqc }} + run: | + cd ~ + git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ + https://github.com/wolfSSL/wolfssl.git + cd wolfssl + ./autogen.sh + if [ "$PQC" = "true" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi + # Full algorithm set so the command-line tool test covers every path. + ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ + --enable-curve25519 --enable-curve448 \ + --enable-aesgcm --enable-aesccm --enable-aescbc \ + --enable-sha384 --enable-sha512 \ + --enable-keygen --enable-hkdf --enable-aeskeywrap \ + --enable-chacha --enable-poly1305 \ + $PQC_FLAGS --enable-rsapss \ + --prefix=$HOME/wolfssl-install + make -j$(nproc) + make install + + - name: Build wolfCOSE command-line tool + run: | + export WOLFSSL_DIR=$HOME/wolfssl-install + make tool CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -I./include -I$WOLFSSL_DIR/include" \ + LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" + + - name: Run command-line tool test (all algorithms, all subcommands) + run: | + export WOLFSSL_DIR=$HOME/wolfssl-install + export LD_LIBRARY_PATH=$WOLFSSL_DIR/lib + ./scripts/cmdline-test.sh ./tools/wolfcose_tool diff --git a/.github/workflows/comprehensive-tests.yml b/.github/workflows/comprehensive-tests.yml index 38cd993..52563ef 100644 --- a/.github/workflows/comprehensive-tests.yml +++ b/.github/workflows/comprehensive-tests.yml @@ -12,9 +12,16 @@ concurrency: cancel-in-progress: true jobs: + discover: + uses: ./.github/workflows/_resolve-wolfssl.yml + comprehensive: - name: Algorithm Matrix Tests + name: Algorithm Matrix Tests (wolfSSL ${{ matrix.wolfssl-version }}) + needs: discover runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.discover.outputs.matrix) }} steps: - uses: actions/checkout@v4 @@ -24,25 +31,34 @@ jobs: sudo apt-get update sudo apt-get install -y autoconf automake libtool - - name: Cache wolfSSL + - name: Cache wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version != 'master' id: cache-wolfssl uses: actions/cache@v4 with: path: ~/wolfssl-install - key: wolfssl-ubuntu-latest-comprehensive-v1 + key: wolfssl-comprehensive-${{ matrix.wolfssl-version }}-v1 - - name: Build wolfSSL - if: steps.cache-wolfssl.outputs.cache-hit != 'true' + - name: Build wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' + env: + PQC: ${{ matrix.pqc }} run: | cd ~ - git clone --depth 1 https://github.com/wolfSSL/wolfssl.git + git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ + https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh + if [ "$PQC" = "true" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium --enable-hkdf --enable-aeskeywrap \ + $PQC_FLAGS --enable-hkdf --enable-aeskeywrap \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 0174004..7137577 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -37,11 +37,14 @@ jobs: git clone --depth 1 https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh + # Coverage thresholds (99%/100%) require the ML-DSA paths compiled in, + # so build against master with the PQC/experimental wc_MlDsaKey API. ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium --enable-hkdf --enable-aeskeywrap \ + --enable-dilithium --enable-experimental \ + --enable-hkdf --enable-aeskeywrap \ --enable-aescbc \ --prefix=$HOME/wolfssl-install make -j$(nproc) diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 129fe38..fc21d5b 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -32,10 +32,11 @@ jobs: path: ~/wolfssl-install key: wolfssl-coverity-v3 - - name: Build wolfSSL + - name: Build wolfSSL (latest stable + master, PQC-gated) if: steps.cache-wolfssl.outputs.cache-hit != 'true' run: | cd ~ + # Coverity scans against master; ML-DSA always available there. git clone --depth 1 https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh @@ -43,7 +44,7 @@ jobs: --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + --enable-dilithium --enable-experimental \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 5db507c..cc394a2 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -12,9 +12,16 @@ concurrency: cancel-in-progress: true jobs: - lifecycle-demo: - name: Lifecycle Demo + discover: + uses: ./.github/workflows/_resolve-wolfssl.yml + + examples: + name: Examples (wolfSSL ${{ matrix.wolfssl-version }}) + needs: discover runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.discover.outputs.matrix) }} steps: - uses: actions/checkout@v4 @@ -24,53 +31,51 @@ jobs: sudo apt-get update sudo apt-get install -y autoconf automake libtool - - name: Cache wolfSSL + - name: Cache wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version != 'master' id: cache-wolfssl uses: actions/cache@v4 with: path: ~/wolfssl-install - key: wolfssl-ubuntu-latest-v3 + key: wolfssl-examples-${{ matrix.wolfssl-version }}-v1 - - name: Build wolfSSL - if: steps.cache-wolfssl.outputs.cache-hit != 'true' + - name: Build wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' + env: + PQC: ${{ matrix.pqc }} run: | cd ~ - git clone --depth 1 https://github.com/wolfSSL/wolfssl.git + git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ + https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh + if [ "$PQC" = "true" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + $PQC_FLAGS \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install - - name: Build wolfCOSE - run: | - export WOLFSSL_DIR=$HOME/wolfssl-install - make CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -fstack-usage -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" - - - name: Run lifecycle demo (all algorithms) - run: | - export WOLFSSL_DIR=$HOME/wolfssl-install - export LD_LIBRARY_PATH=$WOLFSSL_DIR/lib - make demo CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -fstack-usage -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" - - - name: Run tool round-trip test (all algorithms) + - name: Build and run examples run: | export WOLFSSL_DIR=$HOME/wolfssl-install export LD_LIBRARY_PATH=$WOLFSSL_DIR/lib - make tool-test CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -fstack-usage -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" + make demos CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -I./include -I$WOLFSSL_DIR/include" \ + LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" - name: Binary size audit run: | - echo "=== Object sizes ===" - size src/wolfcose_cbor.o src/wolfcose.o - echo "" - echo "=== Per-function stack usage ===" - cat src/*.su + export WOLFSSL_DIR=$HOME/wolfssl-install + make clean + make CFLAGS="-std=c11 -Os -I./include -I$WOLFSSL_DIR/include" \ + LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" + echo "=== Library size ===" + size libwolfcose.a || true + ls -la libwolfcose.a diff --git a/.github/workflows/minimal-build.yml b/.github/workflows/minimal-build.yml index 6ebf99f..17c9c88 100644 --- a/.github/workflows/minimal-build.yml +++ b/.github/workflows/minimal-build.yml @@ -35,8 +35,8 @@ jobs: wolfssl_flags: "--enable-cryptonly --enable-rsapss --enable-keygen --enable-sha384 --enable-sha512" cache_key: wolfssl-rsa-only-v5 - name: PQ (ML-DSA) only - wolfssl_flags: "--enable-cryptonly --enable-dilithium" - cache_key: wolfssl-pq-only-v3 + wolfssl_flags: "--enable-cryptonly --enable-dilithium --enable-experimental" + cache_key: wolfssl-pq-only-v4 - name: ECDH-ES (key agreement) wolfssl_flags: "--enable-cryptonly --enable-ecc --enable-aesgcm --enable-keygen --enable-hkdf" cache_key: wolfssl-ecdh-es-v1 diff --git a/.github/workflows/misra-2012.yml b/.github/workflows/misra-2012.yml index fcdba9d..9c5a73e 100644 --- a/.github/workflows/misra-2012.yml +++ b/.github/workflows/misra-2012.yml @@ -48,7 +48,7 @@ jobs: --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + --enable-dilithium --enable-experimental \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install @@ -123,7 +123,7 @@ jobs: -DHAVE_ED25519 \ -DHAVE_ED448 \ -DWC_RSA_PSS \ - -DHAVE_DILITHIUM \ + -DWOLFSSL_HAVE_MLDSA \ -DHAVE_AESGCM \ -DHAVE_AESCCM \ -DHAVE_CHACHA \ diff --git a/.github/workflows/misra-2023.yml b/.github/workflows/misra-2023.yml index b51b5e7..9ac0151 100644 --- a/.github/workflows/misra-2023.yml +++ b/.github/workflows/misra-2023.yml @@ -50,7 +50,7 @@ jobs: --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + --enable-dilithium --enable-experimental \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install @@ -91,7 +91,7 @@ jobs: -Wlogical-op -Wjump-misses-init -Wdouble-promotion \ -Wnull-dereference -Wsign-conversion \ -DHAVE_ECC -DHAVE_ED25519 -DHAVE_ED448 \ - -DWC_RSA_PSS -DHAVE_DILITHIUM \ + -DWC_RSA_PSS -DWOLFSSL_HAVE_MLDSA \ -DHAVE_AESGCM -DHAVE_AESCCM \ -DHAVE_CHACHA -DHAVE_POLY1305 \ -DWOLFSSL_SHA384 -DWOLFSSL_SHA512 \ @@ -184,7 +184,7 @@ jobs: --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + --enable-dilithium --enable-experimental \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install @@ -215,7 +215,7 @@ jobs: -checks='-*,bugprone-*,cert-*,clang-analyzer-*,misc-*,-misc-include-cleaner,-bugprone-branch-clone,-bugprone-easily-swappable-parameters,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling' \ -- -std=c11 -I./include -I$WOLFSSL_DIR/include \ -DHAVE_ECC -DHAVE_ED25519 -DHAVE_ED448 \ - -DWC_RSA_PSS -DHAVE_DILITHIUM \ + -DWC_RSA_PSS -DWOLFSSL_HAVE_MLDSA \ -DHAVE_AESGCM -DHAVE_AESCCM \ -DHAVE_CHACHA -DHAVE_POLY1305 \ -DWOLFSSL_SHA384 -DWOLFSSL_SHA512 \ diff --git a/.github/workflows/multi-compiler.yml b/.github/workflows/multi-compiler.yml index ebb1eab..8ce494a 100644 --- a/.github/workflows/multi-compiler.yml +++ b/.github/workflows/multi-compiler.yml @@ -1,4 +1,4 @@ -name: Multiple Compilers +name: Multi-Compiler on: push: @@ -12,57 +12,65 @@ concurrency: cancel-in-progress: true jobs: - compiler_test: - name: ${{ matrix.cc }} + discover: + uses: ./.github/workflows/_resolve-wolfssl.yml + + compilers: + name: ${{ matrix.cc }} (wolfSSL ${{ matrix.wolfssl-ref }}) + needs: discover runs-on: ubuntu-latest - timeout-minutes: 15 strategy: fail-fast: false matrix: - include: - - cc: gcc-11 - - cc: gcc-12 - - cc: gcc-13 - - cc: clang-14 - - cc: clang-15 - - cc: clang-17 + cc: [gcc-11, gcc-12, gcc-13, clang-14, clang-15, clang-17] + wolfssl-ref: ${{ fromJson(needs.discover.outputs.refs) }} steps: - uses: actions/checkout@v4 - - name: Install compiler and tools + - name: Install compiler run: | sudo apt-get update - sudo apt-get install -y ${{ matrix.cc }} autoconf automake libtool + sudo apt-get install -y autoconf automake libtool ${{ matrix.cc }} - - name: Cache wolfSSL + - name: Cache wolfSSL (${{ matrix.wolfssl-ref }}) + if: matrix.wolfssl-ref != 'master' id: cache-wolfssl uses: actions/cache@v4 with: path: ~/wolfssl-install - key: wolfssl-ubuntu-latest-v3 + key: wolfssl-multicompiler-${{ matrix.wolfssl-ref }}-v1 - - name: Build wolfSSL - if: steps.cache-wolfssl.outputs.cache-hit != 'true' + - name: Build wolfSSL (${{ matrix.wolfssl-ref }}) + if: matrix.wolfssl-ref == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' run: | cd ~ - git clone --depth 1 https://github.com/wolfSSL/wolfssl.git + git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ + https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh + # PQC (ML-DSA wc_MlDsaKey) only on master or stable newer than v5.9.1. + REF="${{ matrix.wolfssl-ref }}" + if [ "$REF" = "master" ] || \ + [ "$(printf '%s\n%s\n' v5.9.1-stable "$REF" | sort -V | tail -n1)" != "v5.9.1-stable" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + $PQC_FLAGS \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install - - name: Build wolfCOSE with ${{ matrix.cc }} + - name: Build wolfCOSE run: | export WOLFSSL_DIR=$HOME/wolfssl-install make CC=${{ matrix.cc }} \ - CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Werror -Wshadow -Wconversion -I./include -I$WOLFSSL_DIR/include" \ + CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -I./include -I$WOLFSSL_DIR/include" \ LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" - name: Run unit tests @@ -70,5 +78,5 @@ jobs: export WOLFSSL_DIR=$HOME/wolfssl-install export LD_LIBRARY_PATH=$WOLFSSL_DIR/lib make test CC=${{ matrix.cc }} \ - CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Werror -Wshadow -Wconversion -I./include -I$WOLFSSL_DIR/include" \ + CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -I./include -I$WOLFSSL_DIR/include" \ LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index a3290a6..68ef833 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -1,4 +1,4 @@ -name: Sanitizer +name: Sanitizers on: push: @@ -12,9 +12,16 @@ concurrency: cancel-in-progress: true jobs: + discover: + uses: ./.github/workflows/_resolve-wolfssl.yml + asan: - name: ASan (ubuntu-latest) + name: AddressSanitizer (wolfSSL ${{ matrix.wolfssl-version }}) + needs: discover runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.discover.outputs.matrix) }} steps: - uses: actions/checkout@v4 @@ -24,42 +31,55 @@ jobs: sudo apt-get update sudo apt-get install -y autoconf automake libtool - - name: Cache wolfSSL (ASan) - id: cache-wolfssl-asan + - name: Cache wolfSSL (ASan, ${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version != 'master' + id: cache-wolfssl uses: actions/cache@v4 with: path: ~/wolfssl-asan - key: wolfssl-asan-v3 + key: wolfssl-asan-${{ matrix.wolfssl-version }}-v1 - - name: Build wolfSSL with ASan - if: steps.cache-wolfssl-asan.outputs.cache-hit != 'true' + - name: Build wolfSSL (ASan, ${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' + env: + PQC: ${{ matrix.pqc }} run: | cd ~ - git clone --depth 1 https://github.com/wolfSSL/wolfssl.git wolfssl-asan-src + git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ + https://github.com/wolfSSL/wolfssl.git wolfssl-asan-src cd wolfssl-asan-src ./autogen.sh - CFLAGS="-fsanitize=address -fno-omit-frame-pointer" \ - LDFLAGS="-fsanitize=address" \ + if [ "$PQC" = "true" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi + CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \ ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + $PQC_FLAGS \ --prefix=$HOME/wolfssl-asan make -j$(nproc) make install - - name: Build and test wolfCOSE with ASan + - name: Build wolfCOSE (ASan) + run: | + export WOLFSSL_DIR=$HOME/wolfssl-asan + make CFLAGS="-std=c11 -Og -g -fsanitize=address -fno-omit-frame-pointer -I./include -I$WOLFSSL_DIR/include" \ + LDFLAGS="-fsanitize=address -L$WOLFSSL_DIR/lib -lwolfssl" + + - name: Run unit tests (ASan) + run: | + export WOLFSSL_DIR=$HOME/wolfssl-asan + export LD_LIBRARY_PATH=$WOLFSSL_DIR/lib + make test CFLAGS="-std=c11 -Og -g -fsanitize=address -fno-omit-frame-pointer -I./include -I$WOLFSSL_DIR/include" \ + LDFLAGS="-fsanitize=address -L$WOLFSSL_DIR/lib -lwolfssl" + + - name: Forced-failure coverage (ASan) run: | export WOLFSSL_DIR=$HOME/wolfssl-asan export LD_LIBRARY_PATH=$WOLFSSL_DIR/lib - ASAN_FLAGS="-fsanitize=address -fno-omit-frame-pointer" - make clean - make CFLAGS="-std=c11 -O1 -g -Wall -Wextra -Wpedantic $ASAN_FLAGS -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl $ASAN_FLAGS" - make test CFLAGS="-std=c11 -O1 -g -Wall -Wextra -Wpedantic $ASAN_FLAGS -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl $ASAN_FLAGS" - make tool-test CFLAGS="-std=c11 -O1 -g -Wall -Wextra -Wpedantic $ASAN_FLAGS -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl $ASAN_FLAGS" - make demo CFLAGS="-std=c11 -O1 -g -Wall -Wextra -Wpedantic $ASAN_FLAGS -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl $ASAN_FLAGS" + make coverage-force-failure CFLAGS="-std=c11 -Og -g -fsanitize=address -fno-omit-frame-pointer -I./include -I$WOLFSSL_DIR/include" \ + LDFLAGS="-fsanitize=address -L$WOLFSSL_DIR/lib -lwolfssl" diff --git a/.github/workflows/scenarios.yml b/.github/workflows/scenarios.yml index d86ddb5..80353a4 100644 --- a/.github/workflows/scenarios.yml +++ b/.github/workflows/scenarios.yml @@ -1,4 +1,4 @@ -name: Scenario Examples +name: Real-World Scenarios on: push: @@ -12,9 +12,16 @@ concurrency: cancel-in-progress: true jobs: + discover: + uses: ./.github/workflows/_resolve-wolfssl.yml + scenarios: - name: Real-World Scenarios + name: Scenarios (wolfSSL ${{ matrix.wolfssl-version }}) + needs: discover runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.discover.outputs.matrix) }} steps: - uses: actions/checkout@v4 @@ -24,49 +31,52 @@ jobs: sudo apt-get update sudo apt-get install -y autoconf automake libtool - - name: Cache wolfSSL + - name: Cache wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version != 'master' id: cache-wolfssl uses: actions/cache@v4 with: path: ~/wolfssl-install - key: wolfssl-ubuntu-latest-scenarios-v1 + key: wolfssl-scenarios-${{ matrix.wolfssl-version }}-v1 - - name: Build wolfSSL - if: steps.cache-wolfssl.outputs.cache-hit != 'true' + - name: Build wolfSSL (${{ matrix.wolfssl-version }}) + if: matrix.wolfssl-version == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' + env: + PQC: ${{ matrix.pqc }} run: | cd ~ - git clone --depth 1 https://github.com/wolfSSL/wolfssl.git + git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ + https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh + if [ "$PQC" = "true" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium --enable-hkdf --enable-aeskeywrap \ + $PQC_FLAGS --enable-hkdf --enable-aeskeywrap \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install - - name: Build wolfCOSE - run: | - export WOLFSSL_DIR=$HOME/wolfssl-install - make CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -fstack-usage -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" - - - name: Run scenario examples + - name: Build and run scenarios run: | export WOLFSSL_DIR=$HOME/wolfssl-install export LD_LIBRARY_PATH=$WOLFSSL_DIR/lib - make scenarios CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -fstack-usage -I./include -I$WOLFSSL_DIR/include" \ - LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" + make scenarios CFLAGS="-std=c11 -Os -Wall -Wextra -Wpedantic -Wshadow -Wconversion -I./include -I$WOLFSSL_DIR/include" \ + LDFLAGS="-L$WOLFSSL_DIR/lib -lwolfssl" - - name: Scenario Summary + - name: Scenario summary if: always() run: | - echo "=== Real-World Scenario Summary ===" - echo "Scenarios demonstrate production use cases:" - echo " - firmware_update: ML-DSA post-quantum firmware signing" - echo " - multi_party_approval: Dual-control firmware approval" - echo " - iot_fleet_config: Encrypted config push to IoT fleet" - echo " - sensor_attestation: EAT-style attestation with replay protection" - echo " - group_broadcast_mac: Authenticated broadcast to subscribers" + echo "=== Real-World Scenario Tests ===" + echo "Demonstrated wolfCOSE in production-like contexts:" + echo " - Firmware signing and verification" + echo " - Multi-party approval workflows" + echo " - IoT fleet configuration distribution" + echo " - Sensor data attestation" + echo " - Group broadcast with shared MAC" diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 1bdcf08..77d0742 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -38,7 +38,7 @@ jobs: --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + --enable-dilithium --enable-experimental \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install @@ -89,7 +89,7 @@ jobs: --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + --enable-dilithium --enable-experimental \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install @@ -137,7 +137,7 @@ jobs: --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium \ + --enable-dilithium --enable-experimental \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install diff --git a/.github/workflows/wolfssl-versions.yml b/.github/workflows/wolfssl-versions.yml index 33b9b43..f73d588 100644 --- a/.github/workflows/wolfssl-versions.yml +++ b/.github/workflows/wolfssl-versions.yml @@ -42,11 +42,21 @@ jobs: fi echo "Latest stable wolfSSL: $LATEST" echo "latest-stable=$LATEST" >> "$GITHUB_OUTPUT" - MATRIX=$(jq -nc --arg latest "$LATEST" '{ + # Enable PQC only when $LATEST is strictly newer than v5.9.1-stable. + # The wc_MlDsaKey API lands post-v5.9.1-stable; older stables only + # ship the legacy wc_dilithium_* API wolfCOSE no longer calls. + PQC_FLOOR="v5.9.1-stable" + if [ "$(printf '%s\n%s\n' "$PQC_FLOOR" "$LATEST" | sort -V | tail -n 1)" != "$PQC_FLOOR" ]; then + LATEST_PQC=true + else + LATEST_PQC=false + fi + echo "latest-stable PQC eligible: $LATEST_PQC" + MATRIX=$(jq -nc --arg latest "$LATEST" --argjson latest_pqc "$LATEST_PQC" '{ include: [ - {"wolfssl-version":"v5.8.0-stable","wolfssl-ref":"v5.8.0-stable","cache-key":"wolfssl-version-v5.8.0-v1"}, - {"wolfssl-version":$latest,"wolfssl-ref":$latest,"cache-key":("wolfssl-version-" + $latest + "-v1")}, - {"wolfssl-version":"master","wolfssl-ref":"master","cache-key":""} + {"wolfssl-version":"v5.8.0-stable","wolfssl-ref":"v5.8.0-stable","cache-key":"wolfssl-nopqc-v5.8.0-v1","pqc":false}, + {"wolfssl-version":$latest,"wolfssl-ref":$latest,"cache-key":("wolfssl-" + (if $latest_pqc then "pqc" else "nopqc" end) + "-" + $latest + "-v1"),"pqc":$latest_pqc}, + {"wolfssl-version":"master","wolfssl-ref":"master","cache-key":"","pqc":true} ] }') echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT" @@ -79,19 +89,26 @@ jobs: - name: Build wolfSSL (${{ matrix.wolfssl-version }}) if: matrix.wolfssl-version == 'master' || steps.cache-wolfssl.outputs.cache-hit != 'true' + env: + PQC: ${{ matrix.pqc }} run: | cd ~ git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} \ https://github.com/wolfSSL/wolfssl.git cd wolfssl ./autogen.sh + if [ "$PQC" = "true" ]; then + PQC_FLAGS="--enable-dilithium --enable-experimental" + else + PQC_FLAGS="" + fi ./configure --enable-ecc --enable-ed25519 --enable-ed448 \ --enable-curve25519 --enable-curve448 \ --enable-aesgcm --enable-aesccm --enable-aescbc \ --enable-sha384 --enable-sha512 \ --enable-keygen --enable-hkdf --enable-aeskeywrap \ --enable-chacha --enable-poly1305 \ - --enable-dilithium --enable-rsapss \ + $PQC_FLAGS --enable-rsapss \ --prefix=$HOME/wolfssl-install make -j$(nproc) make install diff --git a/Makefile b/Makefile index d3d2739..8a09cda 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ SCEN_IOTFLEET = examples/scenarios/iot_fleet_config SCEN_SENSOR = examples/scenarios/sensor_attestation SCEN_BROADCAST = examples/scenarios/group_broadcast_mac -.PHONY: all shared test coverage tool tool-test demo demos comprehensive scenarios clean +.PHONY: all shared test coverage tool tool-test cmdline-test demo demos comprehensive scenarios clean # --- Core library --- all: $(LIB_A) @@ -110,6 +110,10 @@ tool-test: tool -i /tmp/wolfcose_test.cose @echo "PASS: round-trip sign/verify" +# --- Command-line tool test: every subcommand across all algorithms --- +cmdline-test: tool + ./scripts/cmdline-test.sh ./$(TOOL_BIN) + # --- Lifecycle demo --- demo: $(LIB_A) $(CC) $(CFLAGS) -o $(DEMO_BIN) $(DEMO_SRC) $(LIB_A) $(LDFLAGS) diff --git a/README.md b/README.md index 569eb22..10bf8c4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ wolfCOSE is a lightweight C library implementing [CBOR (RFC 8949)](https://www.r ## Main Features - **Complete RFC 9052 message set**: all six COSE message types, including multi-signer `COSE_Sign` and multi-recipient `COSE_Encrypt` / `COSE_Mac` -- **Post-quantum signing**: ML-DSA (Dilithium) at all three security levels +- **Post-quantum signing**: ML-DSA (FIPS 204) at all three security levels - **40 algorithms** across signing, encryption, MAC, and key distribution - **Zero dynamic allocation**: all operations use caller-provided buffers - **Tiny footprint**: 7.5 KB `.text` minimal build (Sign1+ECC), 25.6 KB full (40 algorithms), zero `.data`/`.bss` @@ -38,7 +38,7 @@ wolfCOSE has implemented all RFC 9052 messages both single-actor and multi-actor ## Prerequisites (wolfSSL) -wolfCOSE requires [wolfSSL](https://www.wolfssl.com/) as its crypto backend. **Minimum supported version: v5.8.0-stable** (first release with the public `wc_ForceZero` symbol alongside the FIPS 204 final ML-DSA and context-aware `wc_dilithium_*_ctx_msg` APIs). Older 5.x releases can technically be supported but require source-level changes; contact [wolfSSL](https://www.wolfssl.com/contact/) for commercial support. +wolfCOSE requires [wolfSSL](https://www.wolfssl.com/) as its crypto backend. **Minimum supported version: v5.8.0-stable** (first release with the public `wc_ForceZero` symbol). Post-quantum signing uses the canonical FIPS 204 `wc_MlDsaKey` API, which lands in wolfSSL **after v5.9.1-stable**; building wolfCOSE against v5.8.0–v5.9.1 works for everything except ML-DSA. Older 5.x releases can technically be supported but require source-level changes; contact [wolfSSL](https://www.wolfssl.com/contact/) for commercial support. Choose a build configuration based on the algorithms you need. @@ -64,13 +64,15 @@ For pure post-quantum signing with ML-DSA-44/65/87: ```bash cd wolfssl ./autogen.sh -./configure --enable-cryptonly --enable-dilithium +./configure --enable-cryptonly --enable-dilithium --enable-experimental make && sudo make install sudo ldconfig ``` **Algorithms enabled:** ML-DSA-44, ML-DSA-65, ML-DSA-87 -(SHAKE-128/256 are pulled in automatically by `--enable-dilithium`.) +(SHAKE-128/256 are pulled in automatically by `--enable-dilithium`. The +`wc_MlDsaKey` API requires `--enable-experimental` and wolfSSL newer than +v5.9.1-stable.) ### Full Build (All Algorithms) @@ -81,7 +83,8 @@ cd wolfssl --enable-curve25519 --enable-aesgcm --enable-aesccm \ --enable-sha384 --enable-sha512 --enable-keygen \ --enable-rsapss --enable-chacha --enable-poly1305 \ - --enable-dilithium --enable-hkdf --enable-aeskeywrap + --enable-dilithium --enable-experimental \ + --enable-hkdf --enable-aeskeywrap make && sudo make install sudo ldconfig ``` diff --git a/docs/_posts/2026-04-30-wolfcose-full-rfc9052.md b/docs/_posts/2026-04-30-wolfcose-full-rfc9052.md index 511694d..4432a32 100644 --- a/docs/_posts/2026-04-30-wolfcose-full-rfc9052.md +++ b/docs/_posts/2026-04-30-wolfcose-full-rfc9052.md @@ -27,7 +27,7 @@ These are exactly the scenarios `COSE_Sign`, `COSE_Encrypt`, and `COSE_Mac` were ```c /* vendorKey and oemKey are WOLFCOSE_KEY*, prepared earlier via - wc_CoseKey_SetEcc() and wc_CoseKey_SetDilithium() respectively. */ + wc_CoseKey_SetEcc() and wc_CoseKey_SetMlDsa() respectively. */ WOLFCOSE_SIGNATURE signers[2] = { { .algId = WOLFCOSE_ALG_ES256, .key = &vendorKey, diff --git a/docs/_posts/2026-04-30-wolfcose-pqc-cose.md b/docs/_posts/2026-04-30-wolfcose-pqc-cose.md index 6ba6b8b..382cde2 100644 --- a/docs/_posts/2026-04-30-wolfcose-pqc-cose.md +++ b/docs/_posts/2026-04-30-wolfcose-pqc-cose.md @@ -28,7 +28,7 @@ A note on the project: wolfCOSE was developed by Aidan Garske, a wolfSSL develop The cleanest way to describe wolfCOSE's ML-DSA implementation is to be precise about which spec each layer comes from: -- **The cryptographic primitive** is ML-DSA from [FIPS 204 final](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) (published August 2024). We get it from wolfCrypt, which holds [FIPS 140-3 Certificate #4718](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4718). We use the context-aware API (`wc_dilithium_sign_ctx_msg`), which is what FIPS 204 final requires. +- **The cryptographic primitive** is ML-DSA from [FIPS 204 final](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) (published August 2024). We get it from wolfCrypt, which holds [FIPS 140-3 Certificate #4718](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4718). We use the context-aware API (`wc_MlDsaKey_SignCtx`), which is what FIPS 204 final requires. - **The COSE algorithm registration** comes from [`draft-ietf-cose-dilithium`](https://datatracker.ietf.org/doc/draft-ietf-cose-dilithium/) (consolidating into `draft-ietf-cose-pqc-algs`). That draft assigns COSE algorithm IDs `-48`, `-49`, `-50` to ML-DSA-44 / 65 / 87 and defines the COSE_Key encoding (`kty=OKP`, `crv=ML-DSA-*`). - **The COSE message envelope** is RFC 9052. Once you have a signature primitive and an algorithm ID, ML-DSA drops into `COSE_Sign1` and `COSE_Sign` exactly the way ES256 does. @@ -42,17 +42,17 @@ Once your wolfSSL is built with `--enable-dilithium`, signing a CBOR payload wit #include #include -dilithium_key dlKey; +wc_MlDsaKey mlDsaKey; WOLFCOSE_KEY coseKey; WC_RNG rng; wc_InitRng(&rng); -wc_dilithium_init(&dlKey); -wc_dilithium_set_level(&dlKey, WC_ML_DSA_44); -wc_dilithium_make_key(&dlKey, &rng); +wc_MlDsaKey_Init(&mlDsaKey, NULL, INVALID_DEVID); +wc_MlDsaKey_SetParams(&mlDsaKey, WC_ML_DSA_44); +wc_MlDsaKey_MakeKey(&mlDsaKey, &rng); wc_CoseKey_Init(&coseKey); -wc_CoseKey_SetDilithium(&coseKey, WOLFCOSE_ALG_ML_DSA_44, &dlKey); +wc_CoseKey_SetMlDsa(&coseKey, WOLFCOSE_ALG_ML_DSA_44, &mlDsaKey); uint8_t scratch[8192]; int ret = wc_CoseSign1_Sign(&coseKey, WOLFCOSE_ALG_ML_DSA_44, @@ -63,7 +63,7 @@ int ret = wc_CoseSign1_Sign(&coseKey, WOLFCOSE_ALG_ML_DSA_44, out, sizeof(out), &outLen, &rng); ``` -That is the entire integration surface. The verifier side uses `wc_CoseSign1_Verify` with a public-only `dilithium_key`, and the COSE_Key serialization works for ML-DSA the same way it works for Ed25519: `kty=OKP`, with `crv` set to the ML-DSA level. +That is the entire integration surface. The verifier side uses `wc_CoseSign1_Verify` with a public-only `wc_MlDsaKey`, and the COSE_Key serialization works for ML-DSA the same way it works for Ed25519: `kty=OKP`, with `crv` set to the ML-DSA level. ## Hybrid Signatures with COSE_Sign @@ -73,7 +73,7 @@ Here is a firmware manifest signed by both ES256 (today's verifier) and ML-DSA-6 ```c /* eccKey and mlDsaKey are WOLFCOSE_KEY*, set up earlier via - wc_CoseKey_SetEcc() and wc_CoseKey_SetDilithium() respectively. */ + wc_CoseKey_SetEcc() and wc_CoseKey_SetMlDsa() respectively. */ WOLFCOSE_SIGNATURE signers[2] = { { .algId = WOLFCOSE_ALG_ES256, .key = &eccKey, diff --git a/examples/lifecycle_demo.c b/examples/lifecycle_demo.c index f428269..911f454 100644 --- a/examples/lifecycle_demo.c +++ b/examples/lifecycle_demo.c @@ -57,7 +57,7 @@ #ifdef WC_RSA_PSS #include #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA #include #endif #include @@ -344,12 +344,12 @@ static int demo_sign1_ps256(void) } #endif /* WC_RSA_PSS && WOLFSSL_KEY_GEN */ -/* ----- COSE_Sign1 lifecycle: ML-DSA-44 (Dilithium) ----- */ -#ifdef HAVE_DILITHIUM +/* ----- COSE_Sign1 lifecycle: ML-DSA-44 (ML-DSA) ----- */ +#ifdef WOLFSSL_HAVE_MLDSA static int demo_sign1_ml_dsa_44(void) { int ret = 0; - dilithium_key dlKey; + wc_MlDsaKey dlKey; WOLFCOSE_KEY signKey; WC_RNG rng; uint8_t payload[64]; @@ -374,23 +374,23 @@ static int demo_sign1_ml_dsa_44(void) } } if (ret == 0) { - ret = wc_dilithium_init(&dlKey); + ret = wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); if (ret == 0) { dlInited = 1; } } if (ret == 0) { - ret = wc_dilithium_set_level(&dlKey, 2); + ret = wc_MlDsaKey_SetParams(&dlKey, 2); } if (ret == 0) { - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); if (ret != 0) { printf(" ML-DSA keygen failed: %d\n", ret); } } if (ret == 0) { wc_CoseKey_Init(&signKey); - wc_CoseKey_SetDilithium(&signKey, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + 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, @@ -417,7 +417,7 @@ static int demo_sign1_ml_dsa_44(void) } if (dlInited != 0) { - wc_dilithium_free(&dlKey); + wc_MlDsaKey_Free(&dlKey); } if (rngInited != 0) { wc_FreeRng(&rng); @@ -426,7 +426,7 @@ static int demo_sign1_ml_dsa_44(void) return ret; } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ /* ----- COSE_Encrypt0 lifecycle: AES-GCM ----- */ #ifdef HAVE_AESGCM @@ -799,7 +799,7 @@ int main(int argc, char* argv[]) if (demo_sign1_ps256() != 0) { failures++; } } #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if (demoAlg == DEMO_ALG_ALL || demoAlg == DEMO_ALG_ML_DSA_44) { tests++; if (demo_sign1_ml_dsa_44() != 0) { failures++; } diff --git a/examples/scenarios/firmware_update.c b/examples/scenarios/firmware_update.c index 3b6508d..eaf411b 100644 --- a/examples/scenarios/firmware_update.c +++ b/examples/scenarios/firmware_update.c @@ -48,7 +48,7 @@ #ifdef HAVE_ECC #include #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA #include #endif #include @@ -68,28 +68,28 @@ static const uint8_t g_firmwareBinary[] = { }; /* ----- Step 1: OEM generates signing key (done once, stored securely) ----- */ -#ifdef HAVE_DILITHIUM -static int oem_generate_key_mldsa(dilithium_key* key, WC_RNG* rng) +#ifdef WOLFSSL_HAVE_MLDSA +static int oem_generate_key_mldsa(wc_MlDsaKey* key, WC_RNG* rng) { int ret; printf("[OEM] Generating ML-DSA-65 key pair...\n"); - ret = wc_dilithium_init(key); + ret = wc_MlDsaKey_Init(key, NULL, INVALID_DEVID); if (ret != 0) { - printf(" ERROR: wc_dilithium_init failed: %d\n", ret); + printf(" ERROR: wc_MlDsaKey_Init failed: %d\n", ret); return ret; } - ret = wc_dilithium_set_level(key, 3); /* Level 3 = ML-DSA-65 */ + ret = wc_MlDsaKey_SetParams(key, 3); /* Level 3 = ML-DSA-65 */ if (ret != 0) { - printf(" ERROR: wc_dilithium_set_level failed: %d\n", ret); + printf(" ERROR: wc_MlDsaKey_SetParams failed: %d\n", ret); return ret; } - ret = wc_dilithium_make_key(key, rng); + ret = wc_MlDsaKey_MakeKey(key, rng); if (ret != 0) { - printf(" ERROR: wc_dilithium_make_key failed: %d\n", ret); + printf(" ERROR: wc_MlDsaKey_MakeKey failed: %d\n", ret); return ret; } @@ -98,7 +98,7 @@ static int oem_generate_key_mldsa(dilithium_key* key, WC_RNG* rng) } #endif -#if defined(HAVE_ECC) && !defined(HAVE_DILITHIUM) +#if defined(HAVE_ECC) && !defined(WOLFSSL_HAVE_MLDSA) static int oem_generate_key_ecdsa(ecc_key* key, WC_RNG* rng) { int ret; @@ -120,7 +120,7 @@ static int oem_generate_key_ecdsa(ecc_key* key, WC_RNG* rng) printf(" SUCCESS: ECDSA P-256 key generated\n"); return 0; } -#endif /* HAVE_ECC && !HAVE_DILITHIUM */ +#endif /* HAVE_ECC && !WOLFSSL_HAVE_MLDSA */ /* ----- Step 2: OEM signs firmware with detached payload ----- */ static int oem_sign_firmware(WOLFCOSE_KEY* signingKey, int32_t alg, @@ -256,8 +256,8 @@ int main(void) WOLFCOSE_KEY signingKey; int32_t alg = 0; -#ifdef HAVE_DILITHIUM - dilithium_key dlKey; +#ifdef WOLFSSL_HAVE_MLDSA + wc_MlDsaKey dlKey; int dlInit = 0; #endif #ifdef HAVE_ECC @@ -276,7 +276,7 @@ int main(void) } rngInit = 1; -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA /* Prefer ML-DSA (post-quantum) if available */ if (ret == 0) { ret = oem_generate_key_mldsa(&dlKey, &rng); @@ -287,10 +287,10 @@ int main(void) if (ret == 0) { wc_CoseKey_Init(&signingKey); - ret = wc_CoseKey_SetDilithium(&signingKey, WOLFCOSE_ALG_ML_DSA_65, + ret = wc_CoseKey_SetMlDsa(&signingKey, WOLFCOSE_ALG_ML_DSA_65, &dlKey); if (ret != 0) { - printf("ERROR: wc_CoseKey_SetDilithium failed: %d\n", ret); + printf("ERROR: wc_CoseKey_SetMlDsa failed: %d\n", ret); } } @@ -362,8 +362,8 @@ int main(void) } /* Cleanup */ -#ifdef HAVE_DILITHIUM - if (dlInit != 0) { wc_dilithium_free(&dlKey); } +#ifdef WOLFSSL_HAVE_MLDSA + if (dlInit != 0) { wc_MlDsaKey_Free(&dlKey); } #endif #ifdef HAVE_ECC if (eccInit != 0) { wc_ecc_free(&eccKey); } diff --git a/include/wolfcose/wolfcose.h b/include/wolfcose/wolfcose.h index fa12151..39d3564 100644 --- a/include/wolfcose/wolfcose.h +++ b/include/wolfcose/wolfcose.h @@ -47,7 +47,7 @@ #ifdef HAVE_ED448 #include #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA #include #endif #include @@ -230,7 +230,7 @@ extern "C" { /* ----- Configurable limits ----- */ #ifndef WOLFCOSE_MAX_SCRATCH_SZ - #if defined(HAVE_DILITHIUM) + #if defined(WOLFSSL_HAVE_MLDSA) #define WOLFCOSE_MAX_SCRATCH_SZ 8192u #else #define WOLFCOSE_MAX_SCRATCH_SZ 512u @@ -246,7 +246,7 @@ extern "C" { #define WOLFCOSE_MAX_MAP_ITEMS 16u #endif #ifndef WOLFCOSE_MAX_SIG_SZ - #if defined(HAVE_DILITHIUM) + #if defined(WOLFSSL_HAVE_MLDSA) #define WOLFCOSE_MAX_SIG_SZ 4627u #elif defined(WC_RSA_PSS) #define WOLFCOSE_MAX_SIG_SZ 512u @@ -460,8 +460,8 @@ typedef struct WOLFCOSE_KEY { #ifdef WC_RSA_PSS RsaKey* rsa; /**< Caller-owned RSA key */ #endif -#ifdef HAVE_DILITHIUM - dilithium_key* dilithium; /**< PQC future: ML-DSA */ +#ifdef WOLFSSL_HAVE_MLDSA + wc_MlDsaKey* mldsa; /**< ML-DSA (FIPS 204), caller-owned */ #endif void* pqc; /**< Generic PQC handle for future algos */ struct { @@ -729,9 +729,9 @@ WOLFCOSE_API int wc_CoseKey_SetEd25519(WOLFCOSE_KEY* key, WOLFCOSE_API int wc_CoseKey_SetEd448(WOLFCOSE_KEY* key, ed448_key* edKey); #endif -#ifdef HAVE_DILITHIUM -WOLFCOSE_API int wc_CoseKey_SetDilithium(WOLFCOSE_KEY* key, int32_t alg, - dilithium_key* dlKey); +#ifdef WOLFSSL_HAVE_MLDSA +WOLFCOSE_API int wc_CoseKey_SetMlDsa(WOLFCOSE_KEY* key, int32_t alg, + wc_MlDsaKey* mlDsaKey); #endif #ifdef WC_RSA_PSS diff --git a/scripts/cmdline-test.sh b/scripts/cmdline-test.sh new file mode 100755 index 0000000..359e1c1 --- /dev/null +++ b/scripts/cmdline-test.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# Exercise every wolfcose_tool command-line subcommand across algorithms. +# +# Drives the actual built binary (not the unit-test harness) so the command +# line paths — argument parsing, key file round-trips, and the +# keygen/sign/verify/enc/dec/mac/macverify/info dispatch — are covered end +# to end. Algorithm names match the tool's own parser (see `wolfcose_tool` +# usage). An algorithm whose keygen fails is treated as "not built in this +# configuration" and skipped, so this script is safe on minimal builds. +# +# Usage: scripts/cmdline-test.sh [path-to-wolfcose_tool] +set -u + +TOOL="${1:-./tools/wolfcose_tool}" +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +if [ ! -x "$TOOL" ]; then + echo "cmdline-test: tool not found or not executable: $TOOL" >&2 + exit 2 +fi + +PASS=0 +FAIL=0 +SKIP=0 +IN="$WORK/payload.bin" +printf 'wolfCOSE cmdline test payload \x01\x02\x03\xde\xad\xbe\xef' > "$IN" + +ok() { PASS=$((PASS+1)); printf ' PASS %s\n' "$1"; } +bad() { FAIL=$((FAIL+1)); printf ' FAIL %s\n' "$1"; } +skip() { SKIP=$((SKIP+1)); printf ' SKIP %s (%s)\n' "$1" "${2:-not built}"; } + +# Names exactly as wolfcose_tool's parser accepts them. +SIGN_ALGS="ES256 EdDSA Ed448 ML-DSA-44 ML-DSA-65 ML-DSA-87" +ENC_ALGS="A128GCM A192GCM A256GCM ChaCha20 AES-CCM" +MAC_ALGS="HMAC256 HMAC384 HMAC512" + +echo "== Signing: keygen -> sign -> verify -> self-test ==" +for A in $SIGN_ALGS; do + K="$WORK/sig.key"; C="$WORK/sig.cose" + if ! "$TOOL" keygen -a "$A" -o "$K" >/dev/null 2>&1; then skip "$A"; continue; fi + if ! "$TOOL" sign -k "$K" -a "$A" -i "$IN" -o "$C" >/dev/null 2>&1; then + bad "$A sign"; continue + fi + if "$TOOL" verify -k "$K" -i "$C" >/dev/null 2>&1; then + ok "$A sign/verify" + else + bad "$A verify" + fi + if "$TOOL" test -a "$A" >/dev/null 2>&1; then + ok "$A self-test" + else + bad "$A self-test" + fi +done + +# RSA-PSS keygen works, but COSE_Key currently stores only the public part +# (n,e,d) — full private round-trip (p,q,qInv) is pending issue #34 — so we +# only smoke-test keygen here, not sign/verify. +echo "== RSA-PSS: keygen only (round-trip pending #34) ==" +for A in PS256 PS384 PS512; do + K="$WORK/rsa.key" + if ! "$TOOL" keygen -a "$A" -o "$K" >/dev/null 2>&1; then skip "$A"; continue; fi + skip "$A sign/verify" "round-trip pending #34" + ok "$A keygen" +done + +echo "== Tamper detection: corrupted COSE_Sign1 must NOT verify ==" +TK="$WORK/tamper.key"; TC="$WORK/tamper.cose" +if "$TOOL" keygen -a ES256 -o "$TK" >/dev/null 2>&1 && \ + "$TOOL" sign -k "$TK" -a ES256 -i "$IN" -o "$TC" >/dev/null 2>&1; then + SZ=$(wc -c < "$TC") + printf '\xff' | dd of="$TC" bs=1 seek=$((SZ-1)) count=1 conv=notrunc >/dev/null 2>&1 + if "$TOOL" verify -k "$TK" -i "$TC" >/dev/null 2>&1; then + bad "tampered signature rejected" + else + ok "tampered signature rejected" + fi +else + skip "tamper (ES256)" +fi + +echo "== Encryption: keygen -> enc -> dec ==" +for A in $ENC_ALGS; do + K="$WORK/enc.key"; C="$WORK/enc.cose"; O="$WORK/enc.out" + if ! "$TOOL" keygen -a "$A" -o "$K" >/dev/null 2>&1; then skip "$A"; continue; fi + if ! "$TOOL" enc -k "$K" -a "$A" -i "$IN" -o "$C" >/dev/null 2>&1; then + bad "$A enc"; continue + fi + if "$TOOL" dec -k "$K" -i "$C" -o "$O" >/dev/null 2>&1 && cmp -s "$IN" "$O"; then + ok "$A enc/dec" + else + bad "$A dec" + fi +done + +echo "== MAC: keygen -> mac -> macverify ==" +for A in $MAC_ALGS; do + K="$WORK/mac.key"; C="$WORK/mac.cose" + if ! "$TOOL" keygen -a "$A" -o "$K" >/dev/null 2>&1; then skip "$A"; continue; fi + if ! "$TOOL" mac -k "$K" -a "$A" -i "$IN" -o "$C" >/dev/null 2>&1; then + bad "$A mac"; continue + fi + if "$TOOL" macverify -k "$K" -i "$C" >/dev/null 2>&1; then + ok "$A mac/macverify" + else + bad "$A macverify" + fi +done + +echo "== info on a signed message ==" +IK="$WORK/info.key"; IC="$WORK/info.cose" +if "$TOOL" keygen -a ES256 -o "$IK" >/dev/null 2>&1 && \ + "$TOOL" sign -k "$IK" -a ES256 -i "$IN" -o "$IC" >/dev/null 2>&1; then + if "$TOOL" info -i "$IC" >/dev/null 2>&1; then + ok "info" + else + bad "info" + fi +else + skip "info (ES256)" +fi + +echo "== Usage errors must exit non-zero ==" +if "$TOOL" >/dev/null 2>&1; then bad "no-args exits non-zero"; else ok "no-args exits non-zero"; fi +if "$TOOL" boguscmd >/dev/null 2>&1; then bad "bad command exits non-zero"; else ok "bad command exits non-zero"; fi + +echo +echo "== Command-line test summary: $PASS passed, $FAIL failed, $SKIP skipped ==" +[ "$FAIL" -eq 0 ] diff --git a/src/wolfcose.c b/src/wolfcose.c index 98fc3f8..c59b465 100644 --- a/src/wolfcose.c +++ b/src/wolfcose.c @@ -29,7 +29,7 @@ #include "wolfcose_internal.h" /* wolfcose.h (via internal.h) includes ecc.h, ed25519.h, ed448.h, - * dilithium.h, rsa.h, random.h. Only list headers not pulled in. */ + * dilithium.h (ML-DSA), rsa.h, random.h. Only list headers not pulled in. */ #include #include #include /* XMEMCPY */ @@ -209,7 +209,7 @@ WOLFCOSE_LOCAL int wolfCose_SigSize(int32_t alg, size_t* sigSz) #endif break; #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA case WOLFCOSE_ALG_ML_DSA_44: *sigSz = 2420; break; @@ -1089,13 +1089,13 @@ int wc_CoseKey_SetEd448(WOLFCOSE_KEY* key, ed448_key* edKey) } #endif /* HAVE_ED448 */ -#ifdef HAVE_DILITHIUM -int wc_CoseKey_SetDilithium(WOLFCOSE_KEY* key, int32_t alg, - dilithium_key* dlKey) +#ifdef WOLFSSL_HAVE_MLDSA +int wc_CoseKey_SetMlDsa(WOLFCOSE_KEY* key, int32_t alg, + wc_MlDsaKey* mlDsaKey) { int ret; - if ((key == NULL) || (dlKey == NULL)) { + if ((key == NULL) || (mlDsaKey == NULL)) { ret = WOLFCOSE_E_INVALID_ARG; } else if ((alg != WOLFCOSE_ALG_ML_DSA_44) && @@ -1115,13 +1115,13 @@ int wc_CoseKey_SetDilithium(WOLFCOSE_KEY* key, int32_t alg, else { key->crv = WOLFCOSE_CRV_ML_DSA_87; } - key->key.dilithium = dlKey; - key->hasPrivate = (dlKey->prvKeySet != 0u) ? 1u : 0u; + key->key.mldsa = mlDsaKey; + key->hasPrivate = (mlDsaKey->prvKeySet != 0u) ? 1u : 0u; ret = WOLFCOSE_SUCCESS; } return ret; } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ #ifdef WC_RSA_PSS int wc_CoseKey_SetRsa(WOLFCOSE_KEY* key, RsaKey* rsaKey) @@ -1467,12 +1467,12 @@ int wc_CoseKey_Encode(WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, } else #endif /* WC_RSA_PSS */ -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if ((key->kty == WOLFCOSE_KTY_OKP) && ((key->crv == WOLFCOSE_CRV_ML_DSA_44) || (key->crv == WOLFCOSE_CRV_ML_DSA_65) || (key->crv == WOLFCOSE_CRV_ML_DSA_87))) { - /* ML-DSA (Dilithium) COSE_Key: OKP with PQC curve. + /* ML-DSA COSE_Key: OKP with PQC curve. * Keys are large (pub up to 2592B, priv up to 4896B), * so we export directly into the output buffer to * avoid large stack allocations. */ @@ -1510,7 +1510,7 @@ int wc_CoseKey_Encode(WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, } if (ret == WOLFCOSE_SUCCESS) { /* Reserve 3 bytes for CBOR bstr header (2-byte length). - * All Dilithium pub sizes (1312-2592) need this form. */ + * All ML-DSA pub sizes (1312-2592) need this form. */ hdrPos = ctx.idx; if ((ctx.idx + 3u) > ctx.bufSz) { ret = WOLFCOSE_E_BUFFER_TOO_SMALL; @@ -1518,9 +1518,9 @@ int wc_CoseKey_Encode(WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, else { ctx.idx += 3u; dlKeyLen = (word32)(ctx.bufSz - ctx.idx); - INJECT_FAILURE(WOLF_FAIL_DILITHIUM_EXPORT_PUB, -1) + INJECT_FAILURE(WOLF_FAIL_MLDSA_EXPORT_PUB, -1) { - ret = wc_dilithium_export_public(key->key.dilithium, + ret = wc_MlDsaKey_ExportPubRaw(key->key.mldsa, &ctx.buf[ctx.idx], &dlKeyLen); } if (ret != 0) { @@ -1554,10 +1554,10 @@ int wc_CoseKey_Encode(WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, else { ctx.idx += 3u; dlKeyLen = (word32)(ctx.bufSz - ctx.idx); - INJECT_FAILURE(WOLF_FAIL_DILITHIUM_EXPORT_PRIV, -1) + INJECT_FAILURE(WOLF_FAIL_MLDSA_EXPORT_PRIV, -1) { - ret = wc_dilithium_export_private( - key->key.dilithium, + ret = wc_MlDsaKey_ExportPrivRaw( + key->key.mldsa, &ctx.buf[ctx.idx], &dlKeyLen); } if (ret != 0) { @@ -1583,7 +1583,7 @@ int wc_CoseKey_Encode(WOLFCOSE_KEY* key, uint8_t* out, size_t outSz, } } else -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ #if defined(HAVE_ED25519) || defined(HAVE_ED448) if (key->kty == WOLFCOSE_KTY_OKP) { uint8_t pubBuf[57]; /* Ed448 pub = 57 bytes, Ed25519 = 32 */ @@ -1945,17 +1945,18 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) ret = WOLFCOSE_E_CRYPTO; } else { - /* Public key only — full private import from raw - * n,e,d components is not currently supported */ + /* TODO(#34): public key only. RSA private round-trip + * needs n,e,d,p,q,qInv via wc_RsaPrivateKeyDecodeRaw; + * the COSE_Key format must also carry p,q,qInv. */ key->hasPrivate = 0u; } } } else #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if ((key->kty == WOLFCOSE_KTY_OKP) && - (key->key.dilithium != NULL) && + (key->key.mldsa != NULL) && ((key->crv == WOLFCOSE_CRV_ML_DSA_44) || (key->crv == WOLFCOSE_CRV_ML_DSA_65) || (key->crv == WOLFCOSE_CRV_ML_DSA_87))) { @@ -1975,33 +1976,34 @@ int wc_CoseKey_Decode(WOLFCOSE_KEY* key, const uint8_t* in, size_t inSz) } else { /* Set level before import */ - ret = wc_dilithium_set_level(key->key.dilithium, + ret = wc_MlDsaKey_SetParams(key->key.mldsa, dlLevel); if (ret != 0) { ret = WOLFCOSE_E_CRYPTO; } else if (dData != NULL) { - INJECT_FAILURE(WOLF_FAIL_DILITHIUM_IMPORT_PRIV, -1) + INJECT_FAILURE(WOLF_FAIL_MLDSA_IMPORT_PRIV, -1) { - ret = wc_dilithium_import_key( + ret = wc_MlDsaKey_ImportKey( + key->key.mldsa, dData, (word32)dLen, - xData, (word32)xLen, key->key.dilithium); + xData, (word32)xLen); } if (ret == 0) { key->hasPrivate = 1; } else { ret = WOLFCOSE_E_CRYPTO; } } else { - INJECT_FAILURE(WOLF_FAIL_DILITHIUM_IMPORT_PUB, -1) + INJECT_FAILURE(WOLF_FAIL_MLDSA_IMPORT_PUB, -1) { - ret = wc_dilithium_import_public( - xData, (word32)xLen, key->key.dilithium); + ret = wc_MlDsaKey_ImportPubRaw( + key->key.mldsa, xData, (word32)xLen); } if (ret != 0) { ret = WOLFCOSE_E_CRYPTO; } } } } else -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ #if defined(HAVE_ED25519) || defined(HAVE_ED448) if (key->kty == WOLFCOSE_KTY_OKP) { if (xData == NULL) { @@ -3341,12 +3343,12 @@ int wc_CoseSign1_Sign(WOLFCOSE_KEY* key, int32_t alg, } else #endif /* WC_RSA_PSS */ -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if ((ret == WOLFCOSE_SUCCESS) && ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { size_t expectedSigSz = 0; - if ((key->kty != WOLFCOSE_KTY_OKP) || (key->key.dilithium == NULL)) { + if ((key->kty != WOLFCOSE_KTY_OKP) || (key->key.mldsa == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } @@ -3361,24 +3363,23 @@ int wc_CoseSign1_Sign(WOLFCOSE_KEY* key, int32_t alg, if (ret == WOLFCOSE_SUCCESS) { word32 dlSigLen = (word32)expectedSigSz; - INJECT_FAILURE(WOLF_FAIL_DILITHIUM_SIGN, -1) + INJECT_FAILURE(WOLF_FAIL_MLDSA_SIGN, -1) { /* wolfSSL gates the legacy non-context ML-DSA API on - * WOLFSSL_DILITHIUM_NO_CTX since the FIPS 204 final + * WOLFSSL_MLDSA_NO_CTX since the FIPS 204 final * transition. When undefined (modern default), only the * context-aware API is available; pass an empty context * since COSE has no application context string. */ -#ifdef WOLFSSL_DILITHIUM_NO_CTX - ret = wc_dilithium_sign_msg( - scratch, (word32)sigStructLen, +#ifdef WOLFSSL_MLDSA_NO_CTX + ret = wc_MlDsaKey_Sign( + key->key.mldsa, &scratch[sigStructLen], &dlSigLen, - key->key.dilithium, rng); + scratch, (word32)sigStructLen, rng); #else - ret = wc_dilithium_sign_ctx_msg( - NULL, 0, - scratch, (word32)sigStructLen, + ret = wc_MlDsaKey_SignCtx( + key->key.mldsa, NULL, 0, &scratch[sigStructLen], &dlSigLen, - key->key.dilithium, rng); + scratch, (word32)sigStructLen, rng); #endif } if (ret != 0) { @@ -3391,7 +3392,7 @@ int wc_CoseSign1_Sign(WOLFCOSE_KEY* key, int32_t alg, } } else -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ if (ret == WOLFCOSE_SUCCESS) { ret = WOLFCOSE_E_COSE_BAD_ALG; } @@ -3764,29 +3765,31 @@ int wc_CoseSign1_Verify(WOLFCOSE_KEY* key, } else #endif /* WC_RSA_PSS */ -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if ((ret == WOLFCOSE_SUCCESS) && ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { int verified = 0; - if ((key->kty != WOLFCOSE_KTY_OKP) || (key->key.dilithium == NULL)) { + if ((key->kty != WOLFCOSE_KTY_OKP) || (key->key.mldsa == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } if (ret == WOLFCOSE_SUCCESS) { - INJECT_FAILURE(WOLF_FAIL_DILITHIUM_VERIFY, -1) + INJECT_FAILURE(WOLF_FAIL_MLDSA_VERIFY, -1) { -#ifdef WOLFSSL_DILITHIUM_NO_CTX - ret = wc_dilithium_verify_msg( +#ifdef WOLFSSL_MLDSA_NO_CTX + ret = wc_MlDsaKey_Verify( + key->key.mldsa, sigData, (word32)sigDataLen, scratch, (word32)sigStructLen, - &verified, key->key.dilithium); + &verified); #else - ret = wc_dilithium_verify_ctx_msg( + ret = wc_MlDsaKey_VerifyCtx( + key->key.mldsa, sigData, (word32)sigDataLen, NULL, 0, scratch, (word32)sigStructLen, - &verified, key->key.dilithium); + &verified); #endif } if (ret != 0) { @@ -3798,7 +3801,7 @@ int wc_CoseSign1_Verify(WOLFCOSE_KEY* key, } } else -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ if (ret == WOLFCOSE_SUCCESS) { ret = WOLFCOSE_E_COSE_BAD_ALG; } @@ -3963,7 +3966,7 @@ int wc_CoseSign_Sign(const WOLFCOSE_SIGNATURE* signers, size_t signerCount, ret = WOLFCOSE_E_COSE_KEY_TYPE; } #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA else if (((signers[i].algId == WOLFCOSE_ALG_ML_DSA_44) || (signers[i].algId == WOLFCOSE_ALG_ML_DSA_65) || (signers[i].algId == WOLFCOSE_ALG_ML_DSA_87)) && @@ -4189,13 +4192,13 @@ int wc_CoseSign_Sign(const WOLFCOSE_SIGNATURE* signers, size_t signerCount, } else #endif /* WC_RSA_PSS */ -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if ((ret == WOLFCOSE_SUCCESS) && ((signer->algId == WOLFCOSE_ALG_ML_DSA_44) || (signer->algId == WOLFCOSE_ALG_ML_DSA_65) || (signer->algId == WOLFCOSE_ALG_ML_DSA_87))) { size_t expectedSigSz = 0; - if (signer->key->key.dilithium == NULL) { + if (signer->key->key.mldsa == NULL) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } if (ret == WOLFCOSE_SUCCESS) { @@ -4208,17 +4211,16 @@ int wc_CoseSign_Sign(const WOLFCOSE_SIGNATURE* signers, size_t signerCount, } if (ret == WOLFCOSE_SUCCESS) { word32 dlSigLen = (word32)expectedSigSz; -#ifdef WOLFSSL_DILITHIUM_NO_CTX - ret = wc_dilithium_sign_msg( - scratch, (word32)sigStructLen, +#ifdef WOLFSSL_MLDSA_NO_CTX + ret = wc_MlDsaKey_Sign( + signer->key->key.mldsa, &scratch[sigStructLen], &dlSigLen, - signer->key->key.dilithium, rng); + scratch, (word32)sigStructLen, rng); #else - ret = wc_dilithium_sign_ctx_msg( - NULL, 0, - scratch, (word32)sigStructLen, + ret = wc_MlDsaKey_SignCtx( + signer->key->key.mldsa, NULL, 0, &scratch[sigStructLen], &dlSigLen, - signer->key->key.dilithium, rng); + scratch, (word32)sigStructLen, rng); #endif if (ret != 0) { ret = WOLFCOSE_E_CRYPTO; @@ -4230,7 +4232,7 @@ int wc_CoseSign_Sign(const WOLFCOSE_SIGNATURE* signers, size_t signerCount, } } else -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ if (ret == WOLFCOSE_SUCCESS) { ret = WOLFCOSE_E_COSE_BAD_ALG; } @@ -4632,27 +4634,29 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, } else #endif /* WC_RSA_PSS */ -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if ((ret == WOLFCOSE_SUCCESS) && ((alg == WOLFCOSE_ALG_ML_DSA_44) || (alg == WOLFCOSE_ALG_ML_DSA_65) || (alg == WOLFCOSE_ALG_ML_DSA_87))) { int verified = 0; if ((verifyKey->kty != WOLFCOSE_KTY_OKP) || - (verifyKey->key.dilithium == NULL)) { + (verifyKey->key.mldsa == NULL)) { ret = WOLFCOSE_E_COSE_KEY_TYPE; } if (ret == WOLFCOSE_SUCCESS) { -#ifdef WOLFSSL_DILITHIUM_NO_CTX - ret = wc_dilithium_verify_msg( +#ifdef WOLFSSL_MLDSA_NO_CTX + ret = wc_MlDsaKey_Verify( + verifyKey->key.mldsa, signature, (word32)signatureLen, scratch, (word32)sigStructLen, - &verified, verifyKey->key.dilithium); + &verified); #else - ret = wc_dilithium_verify_ctx_msg( + ret = wc_MlDsaKey_VerifyCtx( + verifyKey->key.mldsa, signature, (word32)signatureLen, NULL, 0, scratch, (word32)sigStructLen, - &verified, verifyKey->key.dilithium); + &verified); #endif if (ret != 0) { ret = WOLFCOSE_E_CRYPTO; @@ -4663,7 +4667,7 @@ int wc_CoseSign_Verify(const WOLFCOSE_KEY* verifyKey, } } else -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ if (ret == WOLFCOSE_SUCCESS) { ret = WOLFCOSE_E_COSE_BAD_ALG; } diff --git a/tests/force_failure.h b/tests/force_failure.h index a6af4fa..9012889 100644 --- a/tests/force_failure.h +++ b/tests/force_failure.h @@ -56,13 +56,13 @@ typedef enum { WOLF_FAIL_ED448_IMPORT_PUB, /* wc_ed448_import_public */ WOLF_FAIL_ED448_IMPORT_PRIV, /* wc_ed448_import_private_key */ - /* Dilithium failures */ - WOLF_FAIL_DILITHIUM_SIGN, /* wc_dilithium_sign_msg */ - WOLF_FAIL_DILITHIUM_VERIFY, /* wc_dilithium_verify_msg */ - WOLF_FAIL_DILITHIUM_EXPORT_PUB, /* wc_dilithium_export_public */ - WOLF_FAIL_DILITHIUM_EXPORT_PRIV, /* wc_dilithium_export_private */ - WOLF_FAIL_DILITHIUM_IMPORT_PUB, /* wc_dilithium_import_public */ - WOLF_FAIL_DILITHIUM_IMPORT_PRIV, /* wc_dilithium_import_private */ + /* ML-DSA failures */ + WOLF_FAIL_MLDSA_SIGN, /* wc_MlDsaKey_SignCtx */ + WOLF_FAIL_MLDSA_VERIFY, /* wc_MlDsaKey_VerifyCtx */ + WOLF_FAIL_MLDSA_EXPORT_PUB, /* wc_MlDsaKey_ExportPubRaw */ + WOLF_FAIL_MLDSA_EXPORT_PRIV, /* wc_MlDsaKey_ExportPrivRaw */ + WOLF_FAIL_MLDSA_IMPORT_PUB, /* wc_MlDsaKey_ImportPubRaw */ + WOLF_FAIL_MLDSA_IMPORT_PRIV, /* wc_MlDsaKey_ImportKey */ /* HMAC failures */ WOLF_FAIL_HMAC_SET_KEY, /* wc_HmacSetKey */ diff --git a/tests/test_cose.c b/tests/test_cose.c index cf6d611..cb9a673 100644 --- a/tests/test_cose.c +++ b/tests/test_cose.c @@ -53,7 +53,7 @@ #ifdef HAVE_ED448 #include #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA #include #endif #ifdef WC_RSA_PSS @@ -1313,12 +1313,12 @@ static void test_cose_sign1_pss(const char* label, int32_t alg) } #endif /* WC_RSA_PSS && WOLFSSL_KEY_GEN */ -/* ----- COSE_Sign1 ML-DSA (Dilithium) tests ----- */ -#ifdef HAVE_DILITHIUM +/* ----- COSE_Sign1 ML-DSA (ML-DSA) tests ----- */ +#ifdef WOLFSSL_HAVE_MLDSA static void test_cose_sign1_ml_dsa(const char* label, int32_t alg, byte level) { WOLFCOSE_KEY signKey; - dilithium_key dlKey; + wc_MlDsaKey dlKey; WC_RNG rng; int ret = 0; int rngInited = 0; @@ -1341,7 +1341,7 @@ static void test_cose_sign1_ml_dsa(const char* label, int32_t alg, byte level) } if (ret == 0) { - ret = wc_dilithium_init(&dlKey); + ret = wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); if (ret != 0) { TEST_ASSERT(0, "dl init"); } if (ret == 0) { dlInited = 1; @@ -1349,18 +1349,18 @@ static void test_cose_sign1_ml_dsa(const char* label, int32_t alg, byte level) } if (ret == 0) { - ret = wc_dilithium_set_level(&dlKey, level); + ret = wc_MlDsaKey_SetParams(&dlKey, level); if (ret != 0) { TEST_ASSERT(0, "dl set level"); } } if (ret == 0) { - ret = wc_dilithium_make_key(&dlKey, &rng); + 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_SetDilithium(&signKey, alg, &dlKey); + (void)wc_CoseKey_SetMlDsa(&signKey, alg, &dlKey); /* Sign */ ret = wc_CoseSign1_Sign(&signKey, alg, @@ -1389,15 +1389,15 @@ static void test_cose_sign1_ml_dsa(const char* label, int32_t alg, byte level) if (ret == 0) { /* Wrong key should fail */ - dilithium_key dlWrong; + wc_MlDsaKey dlWrong; WOLFCOSE_KEY wrongKey; int wrongRet; - wc_dilithium_init(&dlWrong); - wc_dilithium_set_level(&dlWrong, level); - wrongRet = wc_dilithium_make_key(&dlWrong, &rng); + wc_MlDsaKey_Init(&dlWrong, NULL, INVALID_DEVID); + wc_MlDsaKey_SetParams(&dlWrong, level); + wrongRet = wc_MlDsaKey_MakeKey(&dlWrong, &rng); if (wrongRet == 0) { (void)wc_CoseKey_Init(&wrongKey); - (void)wc_CoseKey_SetDilithium(&wrongKey, alg, &dlWrong); + (void)wc_CoseKey_SetMlDsa(&wrongKey, alg, &dlWrong); wrongRet = wc_CoseSign1_Verify(&wrongKey, out, outLen, NULL, 0, /* detachedPayload, detachedLen */ NULL, 0, /* extAad, extAadLen */ @@ -1405,18 +1405,18 @@ static void test_cose_sign1_ml_dsa(const char* label, int32_t alg, byte level) &hdr, &decPayload, &decPayloadLen); TEST_ASSERT(wrongRet != 0, "sign1 ml-dsa wrong key fails"); } - (void)wc_dilithium_free(&dlWrong); + (void)wc_MlDsaKey_Free(&dlWrong); } /* Cleanup */ if (dlInited != 0) { - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); } if (rngInited != 0) { (void)wc_FreeRng(&rng); } } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ /* ----- COSE_Sign1 with external AAD ----- */ #ifdef HAVE_ECC @@ -1589,13 +1589,13 @@ static void test_cose_key_rsa(void) } #endif /* WC_RSA_PSS && WOLFSSL_KEY_GEN */ -/* ----- COSE_Key Dilithium encode/decode round-trip ----- */ -#ifdef HAVE_DILITHIUM -static void test_cose_key_dilithium(const char* label, int32_t alg, +/* ----- COSE_Key ML-DSA encode/decode round-trip ----- */ +#ifdef WOLFSSL_HAVE_MLDSA +static void test_cose_key_mldsa(const char* label, int32_t alg, int level) { WOLFCOSE_KEY key; - dilithium_key dlKey; + wc_MlDsaKey dlKey; WC_RNG rng; static const uint8_t kid[] = "ml-dsa-key-1"; int ret; @@ -1606,21 +1606,21 @@ static void test_cose_key_dilithium(const char* label, int32_t alg, ret = wc_InitRng(&rng); if (ret != 0) { TEST_ASSERT(0, "rng init"); return; } - ret = wc_dilithium_init(&dlKey); + ret = wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); if (ret != 0) { TEST_ASSERT(0, "dl init"); wc_FreeRng(&rng); return; } - ret = wc_dilithium_set_level(&dlKey, (byte)level); + ret = wc_MlDsaKey_SetParams(&dlKey, (byte)level); if (ret != 0) { TEST_ASSERT(0, "dl set level"); - (void)wc_dilithium_free(&dlKey); wc_FreeRng(&rng); return; + (void)wc_MlDsaKey_Free(&dlKey); wc_FreeRng(&rng); return; } - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); TEST_ASSERT(ret == 0, "dl keygen"); - if (ret != 0) { wc_dilithium_free(&dlKey); wc_FreeRng(&rng); return; } + if (ret != 0) { wc_MlDsaKey_Free(&dlKey); wc_FreeRng(&rng); return; } (void)wc_CoseKey_Init(&key); - ret = wc_CoseKey_SetDilithium(&key, alg, &dlKey); + ret = wc_CoseKey_SetMlDsa(&key, alg, &dlKey); TEST_ASSERT(ret == 0 && key.kty == WOLFCOSE_KTY_OKP, "key set dl"); key.kid = kid; key.kidLen = sizeof(kid) - 1u; @@ -1631,14 +1631,14 @@ static void test_cose_key_dilithium(const char* label, int32_t alg, uint8_t cbuf[8192]; size_t cLen = 0; WOLFCOSE_KEY key2; - dilithium_key dlKey2; + wc_MlDsaKey dlKey2; ret = wc_CoseKey_Encode(&key, cbuf, sizeof(cbuf), &cLen); TEST_ASSERT(ret == 0 && cLen > 0, "key dl encode"); - wc_dilithium_init(&dlKey2); + wc_MlDsaKey_Init(&dlKey2, NULL, INVALID_DEVID); (void)wc_CoseKey_Init(&key2); - key2.key.dilithium = &dlKey2; + key2.key.mldsa = &dlKey2; ret = wc_CoseKey_Decode(&key2, cbuf, cLen); TEST_ASSERT(ret == 0 && key2.kty == WOLFCOSE_KTY_OKP && key2.crv == key.crv && key2.hasPrivate == 1 && @@ -1650,7 +1650,7 @@ static void test_cose_key_dilithium(const char* label, int32_t alg, /* Verify decoded key can sign/verify */ /* empty-brace-scan: allow - test-local temporary scope */ { - uint8_t payload[] = "Dilithium key round-trip"; + uint8_t payload[] = "ML-DSA key round-trip"; uint8_t scratch[8192]; uint8_t out[8192]; size_t outLen = 0; @@ -1676,14 +1676,14 @@ static void test_cose_key_dilithium(const char* label, int32_t alg, TEST_ASSERT(ret == 0, "key dl rt verify"); } - (void)wc_dilithium_free(&dlKey2); + (void)wc_MlDsaKey_Free(&dlKey2); } wc_CoseKey_Free(&key); - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); (void)wc_FreeRng(&rng); } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ /* ----- COSE_Mac0 tests ----- */ #ifndef NO_HMAC @@ -2306,58 +2306,58 @@ static void test_cose_key_encode_errors(void) } #endif -#ifdef HAVE_DILITHIUM - /* Dilithium key encode with buffer too small */ +#ifdef WOLFSSL_HAVE_MLDSA + /* ML-DSA key encode with buffer too small */ /* empty-brace-scan: allow - test-local temporary scope */ { - dilithium_key dlKey; + wc_MlDsaKey dlKey; WC_RNG rng; wc_InitRng(&rng); - wc_dilithium_init(&dlKey); - wc_dilithium_set_level(&dlKey, 2); - wc_dilithium_make_key(&dlKey, &rng); + wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); + wc_MlDsaKey_SetParams(&dlKey, 2); + wc_MlDsaKey_MakeKey(&dlKey, &rng); (void)wc_CoseKey_Init(&key); - (void)wc_CoseKey_SetDilithium(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + (void)wc_CoseKey_SetMlDsa(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); /* Very small buffer should fail */ ret = wc_CoseKey_Encode(&key, buf, 10, &len); - TEST_ASSERT(ret != 0, "dilithium encode buf too small"); + TEST_ASSERT(ret != 0, "ml-dsa encode buf too small"); - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); (void)wc_FreeRng(&rng); } #endif } -#ifdef HAVE_DILITHIUM -static void test_cose_key_set_dilithium_errors(void) +#ifdef WOLFSSL_HAVE_MLDSA +static void test_cose_key_set_mldsa_errors(void) { WOLFCOSE_KEY key; - dilithium_key dlKey; + wc_MlDsaKey dlKey; int ret; - TEST_LOG(" [SetDilithium Errors]\n"); + TEST_LOG(" [SetMlDsa Errors]\n"); (void)wc_CoseKey_Init(&key); - wc_dilithium_init(&dlKey); + wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); /* NULL args */ - ret = wc_CoseKey_SetDilithium(NULL, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + ret = wc_CoseKey_SetMlDsa(NULL, WOLFCOSE_ALG_ML_DSA_44, &dlKey); TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "set dl null key"); - ret = wc_CoseKey_SetDilithium(&key, WOLFCOSE_ALG_ML_DSA_44, NULL); + ret = wc_CoseKey_SetMlDsa(&key, WOLFCOSE_ALG_ML_DSA_44, NULL); TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "set dl null dlkey"); /* invalid alg */ - ret = wc_CoseKey_SetDilithium(&key, -99, &dlKey); + ret = wc_CoseKey_SetMlDsa(&key, -99, &dlKey); TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, "set dl bad alg"); - ret = wc_CoseKey_SetDilithium(&key, 0, &dlKey); + ret = wc_CoseKey_SetMlDsa(&key, 0, &dlKey); TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, "set dl zero alg"); - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ #ifdef HAVE_ED25519 static void test_cose_key_ed25519_public_only(void) @@ -2472,11 +2472,11 @@ static void test_cose_key_ed448_public_only(void) } #endif /* HAVE_ED448 */ -#ifdef HAVE_DILITHIUM -static void test_cose_key_dilithium_public_only(void) +#ifdef WOLFSSL_HAVE_MLDSA +static void test_cose_key_mldsa_public_only(void) { WOLFCOSE_KEY key; - dilithium_key dlKey, dlKey2; + wc_MlDsaKey dlKey, dlKey2; WC_RNG rng; uint8_t pubBuf[2048]; WOLFCOSE_CBOR_CTX enc; @@ -2487,11 +2487,11 @@ static void test_cose_key_dilithium_public_only(void) TEST_LOG(" [Key ML-DSA-44 Public-Only]\n"); wc_InitRng(&rng); - wc_dilithium_init(&dlKey); - wc_dilithium_init(&dlKey2); - wc_dilithium_set_level(&dlKey, 2); - wc_dilithium_make_key(&dlKey, &rng); - wc_dilithium_export_public(&dlKey, xBuf, &xSz); + wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); + wc_MlDsaKey_Init(&dlKey2, NULL, INVALID_DEVID); + wc_MlDsaKey_SetParams(&dlKey, 2); + wc_MlDsaKey_MakeKey(&dlKey, &rng); + wc_MlDsaKey_ExportPubRaw(&dlKey, xBuf, &xSz); /* Build a public-only OKP key (no d label) */ enc.buf = pubBuf; enc.bufSz = sizeof(pubBuf); enc.idx = 0; @@ -2505,7 +2505,7 @@ static void test_cose_key_dilithium_public_only(void) (void)wc_CoseKey_Init(&key); key.kty = WOLFCOSE_KTY_OKP; - key.key.dilithium = &dlKey2; + key.key.mldsa = &dlKey2; ret = wc_CoseKey_Decode(&key, pubBuf, enc.idx); TEST_ASSERT(ret == 0, "dl pub-only decode"); TEST_ASSERT(key.hasPrivate == 0, "dl pub-only no priv"); @@ -2523,7 +2523,7 @@ static void test_cose_key_dilithium_public_only(void) size_t decLen; (void)wc_CoseKey_Init(&signKey); - (void)wc_CoseKey_SetDilithium(&signKey, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + (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), @@ -2542,11 +2542,11 @@ static void test_cose_key_dilithium_public_only(void) } wc_CoseKey_Free(&key); - (void)wc_dilithium_free(&dlKey); - (void)wc_dilithium_free(&dlKey2); + (void)wc_MlDsaKey_Free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey2); (void)wc_FreeRng(&rng); } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ #ifdef HAVE_ECC /* Test ECC public-only key decode (no d label) */ @@ -3367,11 +3367,11 @@ static void test_cose_sign_multi_signer(void) (void)wc_FreeRng(&rng); } -#if defined(HAVE_DILITHIUM) && defined(WOLFCOSE_SIGN) +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFCOSE_SIGN) static void test_cose_sign_ml_dsa_level_mismatch(void) { WOLFCOSE_KEY signKey; - dilithium_key dlKey; + wc_MlDsaKey dlKey; WOLFCOSE_SIGNATURE signers[1]; WC_RNG rng; int ret; @@ -3384,15 +3384,15 @@ static void test_cose_sign_ml_dsa_level_mismatch(void) ret = wc_InitRng(&rng); TEST_ASSERT(ret == 0, "ml-dsa rng"); - ret = wc_dilithium_init(&dlKey); + ret = wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); TEST_ASSERT(ret == 0, "ml-dsa init"); - ret = wc_dilithium_set_level(&dlKey, 2); + ret = wc_MlDsaKey_SetParams(&dlKey, 2); TEST_ASSERT(ret == 0, "ml-dsa set level 2"); - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); TEST_ASSERT(ret == 0, "ml-dsa keygen"); (void)wc_CoseKey_Init(&signKey); - ret = wc_CoseKey_SetDilithium(&signKey, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + ret = wc_CoseKey_SetMlDsa(&signKey, WOLFCOSE_ALG_ML_DSA_44, &dlKey); TEST_ASSERT(ret == 0, "ml-dsa set key"); /* algId says ML-DSA-65 but the key is level 2 (ML-DSA-44). */ @@ -3411,17 +3411,17 @@ static void test_cose_sign_ml_dsa_level_mismatch(void) "Sign_Sign rejects ML-DSA-65 vs key-44 mismatch"); wc_CoseKey_Free(&signKey); - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); /* Level-3 key with ML-DSA-44 algId */ - ret = wc_dilithium_init(&dlKey); + ret = wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); TEST_ASSERT(ret == 0, "ml-dsa3 init"); - ret = wc_dilithium_set_level(&dlKey, 3); + ret = wc_MlDsaKey_SetParams(&dlKey, 3); TEST_ASSERT(ret == 0, "ml-dsa3 set level"); - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); TEST_ASSERT(ret == 0, "ml-dsa3 keygen"); (void)wc_CoseKey_Init(&signKey); - ret = wc_CoseKey_SetDilithium(&signKey, WOLFCOSE_ALG_ML_DSA_65, &dlKey); + ret = wc_CoseKey_SetMlDsa(&signKey, WOLFCOSE_ALG_ML_DSA_65, &dlKey); TEST_ASSERT(ret == 0, "ml-dsa3 set key"); signers[0].algId = WOLFCOSE_ALG_ML_DSA_44; signers[0].key = &signKey; @@ -3432,17 +3432,17 @@ static void test_cose_sign_ml_dsa_level_mismatch(void) TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, "Sign_Sign rejects ML-DSA-44 vs key-65 mismatch"); wc_CoseKey_Free(&signKey); - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); /* Level-5 key with ML-DSA-44 algId */ - ret = wc_dilithium_init(&dlKey); + ret = wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); TEST_ASSERT(ret == 0, "ml-dsa5 init"); - ret = wc_dilithium_set_level(&dlKey, 5); + ret = wc_MlDsaKey_SetParams(&dlKey, 5); TEST_ASSERT(ret == 0, "ml-dsa5 set level"); - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); TEST_ASSERT(ret == 0, "ml-dsa5 keygen"); (void)wc_CoseKey_Init(&signKey); - ret = wc_CoseKey_SetDilithium(&signKey, WOLFCOSE_ALG_ML_DSA_87, &dlKey); + ret = wc_CoseKey_SetMlDsa(&signKey, WOLFCOSE_ALG_ML_DSA_87, &dlKey); TEST_ASSERT(ret == 0, "ml-dsa5 set key"); signers[0].algId = WOLFCOSE_ALG_ML_DSA_44; signers[0].key = &signKey; @@ -3453,7 +3453,7 @@ static void test_cose_sign_ml_dsa_level_mismatch(void) TEST_ASSERT(ret == WOLFCOSE_E_COSE_BAD_ALG, "Sign_Sign rejects ML-DSA-44 vs key-87 mismatch"); wc_CoseKey_Free(&signKey); - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); (void)wc_FreeRng(&rng); } @@ -7314,13 +7314,13 @@ static void test_cose_null_params(void) TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "SetRsa null rsaKey"); #endif - /* Test SetDilithium with NULL */ -#ifdef HAVE_DILITHIUM - ret = wc_CoseKey_SetDilithium(NULL, WOLFCOSE_ALG_ML_DSA_44, NULL); - TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "SetDilithium null key"); + /* Test SetMlDsa with NULL */ +#ifdef WOLFSSL_HAVE_MLDSA + ret = wc_CoseKey_SetMlDsa(NULL, WOLFCOSE_ALG_ML_DSA_44, NULL); + TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "SetMlDsa null key"); - ret = wc_CoseKey_SetDilithium(&key, WOLFCOSE_ALG_ML_DSA_44, NULL); - TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "SetDilithium null dlKey"); + ret = wc_CoseKey_SetMlDsa(&key, WOLFCOSE_ALG_ML_DSA_44, NULL); + TEST_ASSERT(ret == WOLFCOSE_E_INVALID_ARG, "SetMlDsa null dlKey"); #endif } @@ -9456,11 +9456,11 @@ static void test_cose_sign_multi_pss_roundtrip(void) } #endif -#if defined(HAVE_DILITHIUM) && defined(WOLFCOSE_SIGN) -static void test_cose_sign_multi_dilithium_roundtrip(void) +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFCOSE_SIGN) +static void test_cose_sign_multi_mldsa_roundtrip(void) { WOLFCOSE_KEY key; - dilithium_key dlKey; + wc_MlDsaKey dlKey; WC_RNG rng; WOLFCOSE_SIGNATURE signers[1]; WOLFCOSE_HDR hdr; @@ -9476,15 +9476,15 @@ static void test_cose_sign_multi_dilithium_roundtrip(void) ret = wc_InitRng(&rng); TEST_ASSERT(ret == 0, "multi dl rng init"); - ret = wc_dilithium_init(&dlKey); + ret = wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); TEST_ASSERT(ret == 0, "multi dl init"); - ret = wc_dilithium_set_level(&dlKey, WC_ML_DSA_44); + ret = wc_MlDsaKey_SetParams(&dlKey, WC_ML_DSA_44); TEST_ASSERT(ret == 0, "multi dl set level"); - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); TEST_ASSERT(ret == 0, "multi dl keygen"); (void)wc_CoseKey_Init(&key); - ret = wc_CoseKey_SetDilithium(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + ret = wc_CoseKey_SetMlDsa(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); TEST_ASSERT(ret == 0, "multi dl key set"); signers[0].algId = WOLFCOSE_ALG_ML_DSA_44; @@ -9515,7 +9515,7 @@ static void test_cose_sign_multi_dilithium_roundtrip(void) "multi dl payload match"); wc_CoseKey_Free(&key); - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); (void)wc_FreeRng(&rng); } #endif @@ -11330,46 +11330,46 @@ static void test_force_failure_crypto(void) } #endif /* WC_RSA_PSS && WOLFSSL_KEY_GEN */ -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA /* empty-brace-scan: allow - test-local temporary scope */ { WOLFCOSE_KEY key; - dilithium_key dlKey; + wc_MlDsaKey dlKey; uint8_t keyBuf[8192]; - uint8_t dlScratch[4096]; /* Larger scratch for Dilithium sig */ + uint8_t dlScratch[4096]; /* Larger scratch for ML-DSA sig */ uint8_t dlCoseMsg[4096]; size_t dlCoseMsgLen; size_t keyLen; (void)wc_CoseKey_Init(&key); - wc_dilithium_init(&dlKey); - ret = wc_dilithium_set_level(&dlKey, 2); + wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); + ret = wc_MlDsaKey_SetParams(&dlKey, 2); if (ret == 0) { - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); } if (ret == 0) { - (void)wc_CoseKey_SetDilithium(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + (void)wc_CoseKey_SetMlDsa(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); - /* Test Dilithium export public failure */ + /* Test ML-DSA export public failure */ keyLen = sizeof(keyBuf); - wolfForceFailure_Set(WOLF_FAIL_DILITHIUM_EXPORT_PUB); + wolfForceFailure_Set(WOLF_FAIL_MLDSA_EXPORT_PUB); ret = wc_CoseKey_Encode(&key, keyBuf, sizeof(keyBuf), &keyLen); - TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "Dilithium export pub forced failure"); + TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "ML-DSA export pub forced failure"); - /* Test Dilithium export private failure */ + /* Test ML-DSA export private failure */ keyLen = sizeof(keyBuf); - wolfForceFailure_Set(WOLF_FAIL_DILITHIUM_EXPORT_PRIV); + wolfForceFailure_Set(WOLF_FAIL_MLDSA_EXPORT_PRIV); ret = wc_CoseKey_Encode(&key, keyBuf, sizeof(keyBuf), &keyLen); - TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "Dilithium export priv forced failure"); + TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "ML-DSA export priv forced failure"); - /* Test Dilithium sign failure */ + /* Test ML-DSA sign failure */ dlCoseMsgLen = sizeof(dlCoseMsg); - wolfForceFailure_Set(WOLF_FAIL_DILITHIUM_SIGN); + wolfForceFailure_Set(WOLF_FAIL_MLDSA_SIGN); ret = wc_CoseSign1_Sign(&key, WOLFCOSE_ALG_ML_DSA_44, NULL, 0, payload, sizeof(payload), NULL, 0, NULL, 0, dlScratch, sizeof(dlScratch), dlCoseMsg, sizeof(dlCoseMsg), &dlCoseMsgLen, &rng); - TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "Dilithium sign forced failure"); + TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "ML-DSA sign forced failure"); /* Create valid signature for verify test */ dlCoseMsgLen = sizeof(dlCoseMsg); @@ -11382,18 +11382,18 @@ static void test_force_failure_crypto(void) size_t decodedPayloadLen; WOLFCOSE_HDR hdr; - /* Test Dilithium verify failure */ - wolfForceFailure_Set(WOLF_FAIL_DILITHIUM_VERIFY); + /* Test ML-DSA verify failure */ + wolfForceFailure_Set(WOLF_FAIL_MLDSA_VERIFY); ret = wc_CoseSign1_Verify(&key, dlCoseMsg, dlCoseMsgLen, NULL, 0, NULL, 0, dlScratch, sizeof(dlScratch), &hdr, &decodedPayload, &decodedPayloadLen); - TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "Dilithium verify forced failure"); + TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "ML-DSA verify forced failure"); } } - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); wc_CoseKey_Free(&key); } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ #ifdef HAVE_AESCCM /* empty-brace-scan: allow - test-local temporary scope */ @@ -11691,46 +11691,46 @@ static void test_force_failure_crypto(void) } #endif /* HAVE_ED448 */ -#ifdef HAVE_DILITHIUM - /* Test Dilithium import failure via CoseKey_Decode */ +#ifdef WOLFSSL_HAVE_MLDSA + /* Test ML-DSA import failure via CoseKey_Decode */ /* empty-brace-scan: allow - test-local temporary scope */ { WOLFCOSE_KEY key; - dilithium_key dlKey; + wc_MlDsaKey dlKey; uint8_t keyBuf[8192]; size_t keyLen; WOLFCOSE_KEY decodedKey; - dilithium_key decodedDlKey; + wc_MlDsaKey decodedDlKey; (void)wc_CoseKey_Init(&key); - wc_dilithium_init(&dlKey); - ret = wc_dilithium_set_level(&dlKey, 2); + wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); + ret = wc_MlDsaKey_SetParams(&dlKey, 2); if (ret == 0) { - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); } if (ret == 0) { - (void)wc_CoseKey_SetDilithium(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + (void)wc_CoseKey_SetMlDsa(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); /* Encode the key */ keyLen = sizeof(keyBuf); ret = wc_CoseKey_Encode(&key, keyBuf, sizeof(keyBuf), &keyLen); if (ret == 0) { - /* Test Dilithium import failure - must pre-allocate internal key */ - wc_dilithium_init(&decodedDlKey); - wc_dilithium_set_level(&decodedDlKey, 2); + /* Test ML-DSA import failure - must pre-allocate internal key */ + wc_MlDsaKey_Init(&decodedDlKey, NULL, INVALID_DEVID); + wc_MlDsaKey_SetParams(&decodedDlKey, 2); (void)wc_CoseKey_Init(&decodedKey); - decodedKey.key.dilithium = &decodedDlKey; + decodedKey.key.mldsa = &decodedDlKey; decodedKey.crv = WOLFCOSE_CRV_ML_DSA_44; - wolfForceFailure_Set(WOLF_FAIL_DILITHIUM_IMPORT_PRIV); + wolfForceFailure_Set(WOLF_FAIL_MLDSA_IMPORT_PRIV); ret = wc_CoseKey_Decode(&decodedKey, keyBuf, keyLen); - TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "Dilithium import priv forced failure"); - (void)wc_dilithium_free(&decodedDlKey); + TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "ML-DSA import priv forced failure"); + (void)wc_MlDsaKey_Free(&decodedDlKey); } } - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); wc_CoseKey_Free(&key); } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ /* Test WOLF_FAIL_HASH - covers hash operations in sign/verify paths */ #if defined(WC_RSA_PSS) && defined(WOLFSSL_KEY_GEN) @@ -11867,44 +11867,44 @@ static void test_force_failure_crypto(void) } #endif /* HAVE_ED448 */ -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA /* empty-brace-scan: allow - test-local temporary scope */ { WOLFCOSE_KEY dlPubKey; - dilithium_key dlWolfKey; + wc_MlDsaKey dlWolfKey; uint8_t dlKeyBuf[4096]; size_t dlKeyLen; WOLFCOSE_KEY dlDecKey; - dilithium_key dlDecWolfKey; + wc_MlDsaKey dlDecWolfKey; (void)wc_CoseKey_Init(&dlPubKey); - wc_dilithium_init(&dlWolfKey); - ret = wc_dilithium_set_level(&dlWolfKey, 2); + wc_MlDsaKey_Init(&dlWolfKey, NULL, INVALID_DEVID); + ret = wc_MlDsaKey_SetParams(&dlWolfKey, 2); if (ret == 0) { - ret = wc_dilithium_make_key(&dlWolfKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlWolfKey, &rng); } if (ret == 0) { - (void)wc_CoseKey_SetDilithium(&dlPubKey, WOLFCOSE_ALG_ML_DSA_44, &dlWolfKey); + (void)wc_CoseKey_SetMlDsa(&dlPubKey, WOLFCOSE_ALG_ML_DSA_44, &dlWolfKey); dlPubKey.hasPrivate = 0; /* Encode as public key only */ dlKeyLen = sizeof(dlKeyBuf); ret = wc_CoseKey_Encode(&dlPubKey, dlKeyBuf, sizeof(dlKeyBuf), &dlKeyLen); if (ret == 0) { - wc_dilithium_init(&dlDecWolfKey); - wc_dilithium_set_level(&dlDecWolfKey, 2); + wc_MlDsaKey_Init(&dlDecWolfKey, NULL, INVALID_DEVID); + wc_MlDsaKey_SetParams(&dlDecWolfKey, 2); (void)wc_CoseKey_Init(&dlDecKey); - dlDecKey.key.dilithium = &dlDecWolfKey; + dlDecKey.key.mldsa = &dlDecWolfKey; dlDecKey.crv = WOLFCOSE_CRV_ML_DSA_44; - wolfForceFailure_Set(WOLF_FAIL_DILITHIUM_IMPORT_PUB); + wolfForceFailure_Set(WOLF_FAIL_MLDSA_IMPORT_PUB); ret = wc_CoseKey_Decode(&dlDecKey, dlKeyBuf, dlKeyLen); - TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "Dilithium import pub forced failure"); - (void)wc_dilithium_free(&dlDecWolfKey); + TEST_ASSERT(ret == WOLFCOSE_E_CRYPTO, "ML-DSA import pub forced failure"); + (void)wc_MlDsaKey_Free(&dlDecWolfKey); } } - (void)wc_dilithium_free(&dlWolfKey); + (void)wc_MlDsaKey_Free(&dlWolfKey); wc_CoseKey_Free(&dlPubKey); } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ (void)wc_FreeRng(&rng); @@ -13324,51 +13324,51 @@ static void test_rsa_key_encode_buffer_small(void) } #endif /* WC_RSA_PSS && WOLFSSL_KEY_GEN */ -#ifdef HAVE_DILITHIUM -static void test_dilithium_key_encode_buffer_small(void) +#ifdef WOLFSSL_HAVE_MLDSA +static void test_mldsa_key_encode_buffer_small(void) { WOLFCOSE_KEY key; - dilithium_key dlKey; + wc_MlDsaKey dlKey; WC_RNG rng; - uint8_t tinyBuf[64]; /* Too small for Dilithium key */ + uint8_t tinyBuf[64]; /* Too small for ML-DSA key */ size_t outLen = 0; int ret; - TEST_LOG(" [Dilithium Key Encode - Buffer Too Small]\n"); + TEST_LOG(" [ML-DSA Key Encode - Buffer Too Small]\n"); ret = wc_InitRng(&rng); if (ret != 0) { TEST_ASSERT(0, "rng init"); return; } - wc_dilithium_init(&dlKey); - ret = wc_dilithium_set_level(&dlKey, 2); + wc_MlDsaKey_Init(&dlKey, NULL, INVALID_DEVID); + ret = wc_MlDsaKey_SetParams(&dlKey, 2); if (ret != 0) { - TEST_ASSERT(0, "dilithium set level"); - (void)wc_dilithium_free(&dlKey); + TEST_ASSERT(0, "ml-dsa set level"); + (void)wc_MlDsaKey_Free(&dlKey); (void)wc_FreeRng(&rng); return; } - ret = wc_dilithium_make_key(&dlKey, &rng); + ret = wc_MlDsaKey_MakeKey(&dlKey, &rng); if (ret != 0) { - TEST_ASSERT(0, "dilithium keygen"); - (void)wc_dilithium_free(&dlKey); + TEST_ASSERT(0, "ml-dsa keygen"); + (void)wc_MlDsaKey_Free(&dlKey); (void)wc_FreeRng(&rng); return; } (void)wc_CoseKey_Init(&key); - (void)wc_CoseKey_SetDilithium(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); + (void)wc_CoseKey_SetMlDsa(&key, WOLFCOSE_ALG_ML_DSA_44, &dlKey); - /* Dilithium key encode with tiny buffer */ + /* ML-DSA key encode with tiny buffer */ ret = wc_CoseKey_Encode(&key, tinyBuf, sizeof(tinyBuf), &outLen); /* Could be BUFFER_TOO_SMALL or CRYPTO error depending on how failure occurs */ - TEST_ASSERT(ret != WOLFCOSE_SUCCESS, "dilithium key encode tiny buf"); + TEST_ASSERT(ret != WOLFCOSE_SUCCESS, "ml-dsa key encode tiny buf"); wc_CoseKey_Free(&key); - (void)wc_dilithium_free(&dlKey); + (void)wc_MlDsaKey_Free(&dlKey); (void)wc_FreeRng(&rng); } -#endif /* HAVE_DILITHIUM */ +#endif /* WOLFSSL_HAVE_MLDSA */ static void test_key_decode_bad_kty(void) { @@ -14531,10 +14531,10 @@ int test_cose(void) #if defined(WC_RSA_PSS) && defined(WOLFSSL_KEY_GEN) test_cose_key_rsa(); #endif -#ifdef HAVE_DILITHIUM - test_cose_key_dilithium("ML-DSA-44", WOLFCOSE_ALG_ML_DSA_44, 2); - test_cose_key_dilithium("ML-DSA-65", WOLFCOSE_ALG_ML_DSA_65, 3); - test_cose_key_dilithium("ML-DSA-87", WOLFCOSE_ALG_ML_DSA_87, 5); +#ifdef WOLFSSL_HAVE_MLDSA + test_cose_key_mldsa("ML-DSA-44", WOLFCOSE_ALG_ML_DSA_44, 2); + test_cose_key_mldsa("ML-DSA-65", WOLFCOSE_ALG_ML_DSA_65, 3); + test_cose_key_mldsa("ML-DSA-87", WOLFCOSE_ALG_ML_DSA_87, 5); #endif /* Sign1 basic tests */ @@ -14582,8 +14582,8 @@ int test_cose(void) test_cose_sign1_pss("PS512", WOLFCOSE_ALG_PS512); #endif - /* ML-DSA (Dilithium) signature tests */ -#ifdef HAVE_DILITHIUM + /* ML-DSA (ML-DSA) signature tests */ +#ifdef WOLFSSL_HAVE_MLDSA test_cose_sign1_ml_dsa("ML-DSA-44", WOLFCOSE_ALG_ML_DSA_44, 2); test_cose_sign1_ml_dsa("ML-DSA-65", WOLFCOSE_ALG_ML_DSA_65, 3); test_cose_sign1_ml_dsa("ML-DSA-87", WOLFCOSE_ALG_ML_DSA_87, 5); @@ -14622,7 +14622,7 @@ int test_cose(void) #if defined(WOLFCOSE_SIGN) && defined(HAVE_ECC) test_cose_sign_multi_signer(); test_cose_sign_both_payloads(); -#if defined(HAVE_DILITHIUM) && defined(WOLFCOSE_SIGN) +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFCOSE_SIGN) test_cose_sign_ml_dsa_level_mismatch(); #endif test_cose_sign_verify_key_alg_mismatch(); @@ -14779,8 +14779,8 @@ int test_cose(void) defined(WOLFSSL_KEY_GEN) test_cose_sign_multi_pss_roundtrip(); #endif -#if defined(HAVE_DILITHIUM) && defined(WOLFCOSE_SIGN) - test_cose_sign_multi_dilithium_roundtrip(); +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFCOSE_SIGN) + test_cose_sign_multi_mldsa_roundtrip(); #endif #if defined(WOLFCOSE_ENCRYPT) && defined(HAVE_AESCCM) test_cose_encrypt_multi_ccm_roundtrip(); @@ -14874,8 +14874,8 @@ int test_cose(void) #endif test_cose_key_encode_errors(); test_cose_key_decode_optional_labels(); -#ifdef HAVE_DILITHIUM - test_cose_key_set_dilithium_errors(); +#ifdef WOLFSSL_HAVE_MLDSA + test_cose_key_set_mldsa_errors(); #endif #ifdef HAVE_ED25519 test_cose_key_ed25519_public_only(); @@ -14883,8 +14883,8 @@ int test_cose(void) #ifdef HAVE_ED448 test_cose_key_ed448_public_only(); #endif -#ifdef HAVE_DILITHIUM - test_cose_key_dilithium_public_only(); +#ifdef WOLFSSL_HAVE_MLDSA + test_cose_key_mldsa_public_only(); #endif #ifdef HAVE_ECC test_cose_key_ecc_public_only(); @@ -15000,8 +15000,8 @@ int test_cose(void) #if defined(WC_RSA_PSS) && defined(WOLFSSL_KEY_GEN) test_rsa_key_encode_buffer_small(); #endif -#ifdef HAVE_DILITHIUM - test_dilithium_key_encode_buffer_small(); +#ifdef WOLFSSL_HAVE_MLDSA + test_mldsa_key_encode_buffer_small(); #endif test_key_decode_bad_kty(); #if defined(WOLFCOSE_ECDH_ES_DIRECT) && defined(HAVE_ECC) && \ diff --git a/tools/wolfcose_tool.c b/tools/wolfcose_tool.c index 9f6f569..bfe371b 100644 --- a/tools/wolfcose_tool.c +++ b/tools/wolfcose_tool.c @@ -56,7 +56,7 @@ #ifdef WC_RSA_PSS #include #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA #include #endif @@ -69,11 +69,12 @@ #endif #ifndef WOLFCOSE_TOOL_MAX_KEY - #ifdef HAVE_DILITHIUM + #ifdef WOLFSSL_HAVE_MLDSA /* ML-DSA-87: pub=2592 + priv=4896 + CBOR overhead */ #define WOLFCOSE_TOOL_MAX_KEY 8192 #else - #define WOLFCOSE_TOOL_MAX_KEY 1024 + /* RSA-2048 private COSE_Key (n+d + export scratch) needs ~1KB+ */ + #define WOLFCOSE_TOOL_MAX_KEY 4096 #endif #endif @@ -137,7 +138,7 @@ static int parse_alg(const char* name, int32_t* alg) *alg = WOLFCOSE_ALG_PS512; } #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA else if (strcmp(name, "ML-DSA-44") == 0) { *alg = WOLFCOSE_ALG_ML_DSA_44; } @@ -306,26 +307,26 @@ static int tool_keygen(int32_t alg, const char* algStr, const char* outPath) } else #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if (alg == WOLFCOSE_ALG_ML_DSA_44 || alg == WOLFCOSE_ALG_ML_DSA_65 || alg == WOLFCOSE_ALG_ML_DSA_87) { - dilithium_key dl; + wc_MlDsaKey dl; byte level; if (alg == WOLFCOSE_ALG_ML_DSA_44) level = 2; else if (alg == WOLFCOSE_ALG_ML_DSA_65) level = 3; else level = 5; - wc_dilithium_init(&dl); - wc_dilithium_set_level(&dl, level); - ret = wc_dilithium_make_key(&dl, &rng); + wc_MlDsaKey_Init(&dl, NULL, INVALID_DEVID); + wc_MlDsaKey_SetParams(&dl, level); + ret = wc_MlDsaKey_MakeKey(&dl, &rng); if (ret != 0) { fprintf(stderr, "ML-DSA keygen failed: %d\n", ret); - wc_dilithium_free(&dl); + wc_MlDsaKey_Free(&dl); wc_FreeRng(&rng); return EXIT_CRYPTO; } - wc_CoseKey_SetDilithium(&coseKey, alg, &dl); + wc_CoseKey_SetMlDsa(&coseKey, alg, &dl); ret = wc_CoseKey_Encode(&coseKey, keyBuf, sizeof(keyBuf), &keyLen); - wc_dilithium_free(&dl); + wc_MlDsaKey_Free(&dl); } else #endif @@ -533,22 +534,22 @@ static int tool_sign(const char* keyPath, int32_t alg, const char* algStr, } else #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if (alg == WOLFCOSE_ALG_ML_DSA_44 || alg == WOLFCOSE_ALG_ML_DSA_65 || alg == WOLFCOSE_ALG_ML_DSA_87) { - dilithium_key dl; - wc_dilithium_init(&dl); - coseKey.key.dilithium = &dl; + wc_MlDsaKey dl; + wc_MlDsaKey_Init(&dl, NULL, INVALID_DEVID); + coseKey.key.mldsa = &dl; ret = wc_CoseKey_Decode(&coseKey, keyBuf, keyLen); if (ret != 0) { fprintf(stderr, "Key decode failed: %d\n", ret); - wc_dilithium_free(&dl); + wc_MlDsaKey_Free(&dl); return EXIT_CRYPTO; } ret = wc_InitRng(&rng); if (ret != 0) { - wc_dilithium_free(&dl); + wc_MlDsaKey_Free(&dl); return EXIT_CRYPTO; } @@ -558,7 +559,7 @@ static int tool_sign(const char* keyPath, int32_t alg, const char* algStr, outBuf, sizeof(outBuf), &outLen, &rng); wc_FreeRng(&rng); - wc_dilithium_free(&dl); + wc_MlDsaKey_Free(&dl); } else #endif @@ -594,135 +595,125 @@ static int tool_verify(const char* keyPath, const char* inPath) const uint8_t* payload = NULL; size_t payloadLen = 0; int keyMatched = 0; + int32_t kty = 0; + int32_t crv = 0; ret = read_file(keyPath, keyBuf, sizeof(keyBuf), &keyLen); if (ret == 0) { ret = read_file(inPath, msgBuf, sizeof(msgBuf), &msgLen); } - /* Decode key to determine kty, then dispatch to correct wolfCrypt - * key type. OKP keys need crv to distinguish Ed25519/Ed448/Dilithium. */ + /* Peek the COSE_Key metadata with an empty key union so no wolfCrypt + * importer runs (each is gated on a non-NULL union member). This yields + * kty/crv without writing key material through a mismatched union member, + * then we dispatch to exactly one correctly-typed key. */ if (ret == 0) { wc_CoseKey_Init(&coseKey); + (void)wc_CoseKey_Decode(&coseKey, keyBuf, keyLen); + kty = coseKey.kty; + crv = coseKey.crv; } #ifdef HAVE_ECC - if (ret == 0 && keyMatched == 0) { + if (ret == 0 && kty == WOLFCOSE_KTY_EC2) { ecc_key ecc; + keyMatched = 1; + wc_CoseKey_Init(&coseKey); wc_ecc_init(&ecc); coseKey.key.ecc = &ecc; ret = wc_CoseKey_Decode(&coseKey, keyBuf, keyLen); - if (ret == 0 && coseKey.kty == WOLFCOSE_KTY_EC2) { - keyMatched = 1; + if (ret == 0) { ret = wc_CoseSign1_Verify(&coseKey, msgBuf, msgLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), &hdr, &payload, &payloadLen); } - else if (ret != 0 || coseKey.kty != WOLFCOSE_KTY_EC2) { - ret = 0; /* Reset for next key type attempt */ - } wc_ecc_free(&ecc); } + else #endif - #ifdef WC_RSA_PSS - if (ret == 0 && keyMatched == 0) { + if (ret == 0 && kty == WOLFCOSE_KTY_RSA) { RsaKey rsa; + keyMatched = 1; wc_CoseKey_Init(&coseKey); wc_InitRsaKey(&rsa, NULL); coseKey.key.rsa = &rsa; ret = wc_CoseKey_Decode(&coseKey, keyBuf, keyLen); - if (ret == 0 && coseKey.kty == WOLFCOSE_KTY_RSA) { - keyMatched = 1; + if (ret == 0) { ret = wc_CoseSign1_Verify(&coseKey, msgBuf, msgLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), &hdr, &payload, &payloadLen); } - else if (ret != 0 || coseKey.kty != WOLFCOSE_KTY_RSA) { - ret = 0; /* Reset for next key type attempt */ - } wc_FreeRsaKey(&rsa); } + else #endif - #ifdef HAVE_ED25519 - if (ret == 0 && keyMatched == 0) { + if (ret == 0 && kty == WOLFCOSE_KTY_OKP && + crv == WOLFCOSE_CRV_ED25519) { ed25519_key ed; + keyMatched = 1; wc_CoseKey_Init(&coseKey); wc_ed25519_init(&ed); coseKey.key.ed25519 = &ed; ret = wc_CoseKey_Decode(&coseKey, keyBuf, keyLen); - if (ret == 0 && coseKey.kty == WOLFCOSE_KTY_OKP && - coseKey.crv == WOLFCOSE_CRV_ED25519) { - keyMatched = 1; + if (ret == 0) { ret = wc_CoseSign1_Verify(&coseKey, msgBuf, msgLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), &hdr, &payload, &payloadLen); } - else if (ret != 0 || coseKey.kty != WOLFCOSE_KTY_OKP || - coseKey.crv != WOLFCOSE_CRV_ED25519) { - ret = 0; /* Reset for next key type attempt */ - } wc_ed25519_free(&ed); } + else #endif - #ifdef HAVE_ED448 - if (ret == 0 && keyMatched == 0) { + if (ret == 0 && kty == WOLFCOSE_KTY_OKP && + crv == WOLFCOSE_CRV_ED448) { ed448_key ed; + keyMatched = 1; wc_CoseKey_Init(&coseKey); wc_ed448_init(&ed); coseKey.key.ed448 = &ed; ret = wc_CoseKey_Decode(&coseKey, keyBuf, keyLen); - if (ret == 0 && coseKey.kty == WOLFCOSE_KTY_OKP && - coseKey.crv == WOLFCOSE_CRV_ED448) { - keyMatched = 1; + if (ret == 0) { ret = wc_CoseSign1_Verify(&coseKey, msgBuf, msgLen, NULL, 0, NULL, 0, scratch, sizeof(scratch), &hdr, &payload, &payloadLen); } - else if (ret != 0 || coseKey.kty != WOLFCOSE_KTY_OKP || - coseKey.crv != WOLFCOSE_CRV_ED448) { - ret = 0; /* Reset for next key type attempt */ - } wc_ed448_free(&ed); } + else #endif - -#ifdef HAVE_DILITHIUM - if (ret == 0 && keyMatched == 0) { - dilithium_key dl; +#ifdef WOLFSSL_HAVE_MLDSA + if (ret == 0 && kty == WOLFCOSE_KTY_OKP && + (crv == WOLFCOSE_CRV_ML_DSA_44 || + crv == WOLFCOSE_CRV_ML_DSA_65 || + crv == WOLFCOSE_CRV_ML_DSA_87)) { + wc_MlDsaKey dl; + keyMatched = 1; wc_CoseKey_Init(&coseKey); - wc_dilithium_init(&dl); - coseKey.key.dilithium = &dl; - ret = wc_CoseKey_Decode(&coseKey, keyBuf, keyLen); - if (ret == 0 && coseKey.kty == WOLFCOSE_KTY_OKP && - (coseKey.crv == WOLFCOSE_CRV_ML_DSA_44 || - coseKey.crv == WOLFCOSE_CRV_ML_DSA_65 || - coseKey.crv == WOLFCOSE_CRV_ML_DSA_87)) { - keyMatched = 1; - ret = wc_CoseSign1_Verify(&coseKey, msgBuf, msgLen, - NULL, 0, NULL, 0, scratch, sizeof(scratch), - &hdr, &payload, &payloadLen); - } - else if (ret != 0 || coseKey.kty != WOLFCOSE_KTY_OKP || - (coseKey.crv != WOLFCOSE_CRV_ML_DSA_44 && - coseKey.crv != WOLFCOSE_CRV_ML_DSA_65 && - coseKey.crv != WOLFCOSE_CRV_ML_DSA_87)) { - ret = 0; /* Reset for next key type attempt */ + ret = wc_MlDsaKey_Init(&dl, NULL, INVALID_DEVID); + if (ret == 0) { + coseKey.key.mldsa = &dl; + ret = wc_CoseKey_Decode(&coseKey, keyBuf, keyLen); + if (ret == 0) { + ret = wc_CoseSign1_Verify(&coseKey, msgBuf, msgLen, + NULL, 0, NULL, 0, scratch, sizeof(scratch), + &hdr, &payload, &payloadLen); + } + wc_MlDsaKey_Free(&dl); } - wc_dilithium_free(&dl); } + else #endif - - /* Check if key type was matched */ - if (ret == 0 && keyMatched == 0) { + { + (void)crv; fprintf(stderr, "Unsupported key type\n"); ret = EXIT_CRYPTO; } /* Report result */ - if (ret != 0 && keyMatched != 0) { + if (keyMatched != 0 && ret != 0) { fprintf(stderr, "Verification FAILED: %d\n", ret); ret = EXIT_CRYPTO; } @@ -1268,12 +1259,12 @@ static int test_sign_pss(const char* name, int32_t alg) } #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA static int test_sign_mldsa(const char* name, int32_t alg, byte level) { int ret = 0; WC_RNG rng; - dilithium_key dl; + wc_MlDsaKey dl; WOLFCOSE_KEY key; uint8_t payload[] = "wolfCOSE roundtrip"; uint8_t scratch[8192]; @@ -1289,18 +1280,18 @@ static int test_sign_mldsa(const char* name, int32_t alg, byte level) ret = wc_InitRng(&rng); if (ret == 0) { rngInit = 1; - ret = wc_dilithium_init(&dl); + ret = wc_MlDsaKey_Init(&dl, NULL, INVALID_DEVID); } if (ret == 0) { dlInit = 1; - ret = wc_dilithium_set_level(&dl, level); + ret = wc_MlDsaKey_SetParams(&dl, level); } if (ret == 0) { - ret = wc_dilithium_make_key(&dl, &rng); + ret = wc_MlDsaKey_MakeKey(&dl, &rng); } if (ret == 0) { wc_CoseKey_Init(&key); - wc_CoseKey_SetDilithium(&key, alg, &dl); + wc_CoseKey_SetMlDsa(&key, alg, &dl); ret = wc_CoseSign1_Sign(&key, alg, NULL, 0, payload, sizeof(payload) - 1, NULL, 0, NULL, 0, @@ -1321,7 +1312,7 @@ static int test_sign_mldsa(const char* name, int32_t alg, byte level) /* Cleanup */ if (dlInit != 0) { - wc_dilithium_free(&dl); + wc_MlDsaKey_Free(&dl); } if (rngInit != 0) { wc_FreeRng(&rng); @@ -1483,7 +1474,7 @@ static int tool_test(const char* filter) if (test_sign_pss("PS512", WOLFCOSE_ALG_PS512) != 0) failures++; } #endif -#ifdef HAVE_DILITHIUM +#ifdef WOLFSSL_HAVE_MLDSA if (all || strcmp(filter, "ML-DSA-44") == 0) { tests++; if (test_sign_mldsa("ML-DSA-44", WOLFCOSE_ALG_ML_DSA_44, 2) != 0)