diff --git a/.github/workflows/mldsa-composite-config.yml b/.github/workflows/mldsa-composite-config.yml new file mode 100644 index 000000000..45e379cff --- /dev/null +++ b/.github/workflows/mldsa-composite-config.yml @@ -0,0 +1,188 @@ +name: ML-DSA Composite Configs + +# Builds wolfSSH with ML-DSA on but traditional halves off, exercising the +# per-composite gates in wolfssh/internal.h in states the all-on and all-off +# builds never reach. + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_wolfssl: + name: Build wolfSSL + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout wolfSSL + uses: actions/checkout@v6 + with: + repository: wolfssl/wolfssl + ref: master + path: wolfssl + + - name: Build wolfSSL + working-directory: ./wolfssl + run: | + ./autogen.sh + ./configure --enable-all --enable-mldsa --enable-ed25519-stream \ + --enable-ed448-stream + make -j$(nproc) + sudo make install + sudo ldconfig + + - name: tar build-dir + run: tar -zcf wolfssl-install.tgz /usr/local/lib/libwolfssl* /usr/local/include/wolfssl + + - name: Upload built lib + uses: actions/upload-artifact@v7 + with: + name: wolfssl-mldsa-composite + path: wolfssl-install.tgz + retention-days: 5 + + partial_config: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + timeout-minutes: 30 + needs: build_wolfssl + strategy: + fail-fast: false + matrix: + include: + # Only the Ed448 half is left, so exactly one composite survives. + - name: ed448-only + defines: -DWOLFSSH_NO_ECDSA_SHA2_NISTP256 -DWOLFSSH_NO_ECDSA_SHA2_NISTP384 -DWOLFSSH_NO_ED25519 + expect: ssh-mldsa87-ed448@wolfssl.com + exclude_tests: tests/api.test tests/testsuite.test + # Nothing pairs with ML-DSA, so WOLFSSH_NO_MLDSA_COMPOSITES must + # be derived rather than given. + - name: no-pairing + defines: -DWOLFSSH_NO_ECDSA_SHA2_NISTP256 -DWOLFSSH_NO_ECDSA_SHA2_NISTP384 -DWOLFSSH_NO_ED25519 -DWOLFSSH_NO_MLDSA87 + expect: '' + exclude_tests: tests/api.test tests/testsuite.test + # Sets one gate directly rather than deriving it, the rest left on + # to expose a missed site. Only row with --enable-ossh-certs. + - name: single-composite-gate + defines: -DWOLFSSH_NO_MLDSA44_ES256 + configure_flags: --enable-ossh-certs + expect: ssh-mldsa44-ed25519@openssh.com ssh-mldsa65-ed25519@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-ed448@wolfssl.com ssh-mldsa87-es384@wolfssl.com + # Every composite off while ML-DSA and the traditional halves stay + # on; the six gates must follow. + - name: composites-off + defines: -DWOLFSSH_NO_MLDSA_COMPOSITES + expect: '' + # HAVE_ED448 stays set but WOLFSSH_HAVE_COMPOSITE_ED448 does not, + # the state that macro exists for. settings.h is re-processed per + # translation unit, so no second wolfSSL build is needed. + - name: no-ed448-sign + defines: -DNO_ED448_SIGN + expect: ssh-mldsa44-ed25519@openssh.com ssh-mldsa44-es256@wolfssl.com ssh-mldsa65-ed25519@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-es384@wolfssl.com + # Same for the widened WOLFSSH_NO_ED25519 gate. NO_ED25519_VERIFY + # also suppresses WOLFSSL_ED25519_PERSISTENT_SHA, a member of + # struct ed25519_key, so it is re-defined to keep the layout + # matching the installed library's. + - name: no-ed25519-verify + defines: -DNO_ED25519_VERIFY -DWOLFSSL_ED25519_PERSISTENT_SHA + expect: ssh-mldsa44-es256@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-ed448@wolfssl.com ssh-mldsa87-es384@wolfssl.com + # The remaining Ed conjuncts. Both are layout-neutral, so neither + # needs a compensating define the way no-ed25519-verify does. + - name: no-ed25519-sign + defines: -DNO_ED25519_SIGN + expect: ssh-mldsa44-es256@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-ed448@wolfssl.com ssh-mldsa87-es384@wolfssl.com + - name: no-ed448-key-export + defines: -DNO_ED448_KEY_EXPORT + expect: ssh-mldsa44-ed25519@openssh.com ssh-mldsa44-es256@wolfssl.com ssh-mldsa65-ed25519@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-es384@wolfssl.com + # The last four conjuncts, so all eight the two derivations rest on + # are covered. NO_ED448_VERIFY needs the same layout compensation + # as no-ed25519-verify, since the wolfSSL build enables both + # streams rather than leaving either to --enable-all. + - name: no-ed448-verify + defines: -DNO_ED448_VERIFY -DWOLFSSL_ED448_PERSISTENT_SHA + expect: ssh-mldsa44-ed25519@openssh.com ssh-mldsa44-es256@wolfssl.com ssh-mldsa65-ed25519@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-es384@wolfssl.com + - name: no-ed448-key-import + defines: -DNO_ED448_KEY_IMPORT + expect: ssh-mldsa44-ed25519@openssh.com ssh-mldsa44-es256@wolfssl.com ssh-mldsa65-ed25519@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-es384@wolfssl.com + - name: no-ed25519-key-import + defines: -DNO_ED25519_KEY_IMPORT + expect: ssh-mldsa44-es256@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-ed448@wolfssl.com ssh-mldsa87-es384@wolfssl.com + - name: no-ed25519-key-export + defines: -DNO_ED25519_KEY_EXPORT + expect: ssh-mldsa44-es256@wolfssl.com ssh-mldsa65-es256@wolfssl.com ssh-mldsa87-ed448@wolfssl.com ssh-mldsa87-es384@wolfssl.com + steps: + - name: Checkout wolfSSH + uses: actions/checkout@v6 + + - name: Download wolfSSL + uses: actions/download-artifact@v8 + with: + name: wolfssl-mldsa-composite + + - name: Install wolfSSL + run: | + sudo tar -zxf wolfssl-install.tgz -C / + sudo ldconfig + + - name: Build wolfSSH + run: | + ./autogen.sh + ./configure --enable-all ${{ matrix.configure_flags }} CPPFLAGS="${{ matrix.defines }}" + make -j$(nproc) + + # The -E list must hold exactly the composites the gates leave on. + - name: Check negotiated composites + run: | + out=$(./examples/client/client -E -u "${USER:-runner}" 2>&1) + # A crash must not pass vacuously with an empty list. + echo "$out" | grep -q '^Set Key: ' + keys=$(echo "$out" | sed -n 's/^Set Key: //p') + # ML-DSA itself must be on, or an empty composite list proves nothing. + echo "$keys" | grep -q 'ssh-mldsa-44' + got=$(echo "$keys" | tr ',' '\n' | { grep -o 'ssh-mldsa[0-9][0-9]-.*' || true; } | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//') + echo "composites offered: '$got'" + test "$got" = "${{ matrix.expect }}" + # Second table, gated in parallel with the host-key one. + echo "$out" | grep -q '^Set Key Accepted: ' + acc=$(echo "$out" | sed -n 's/^Set Key Accepted: //p') + gotAcc=$(echo "$acc" | tr ',' '\n' | { grep -o 'ssh-mldsa[0-9][0-9]-.*' || true; } | LC_ALL=C sort -u | tr '\n' ' ' | sed 's/ $//') + echo "composites accepted: '$gotAcc'" + test "$gotAcc" = "${{ matrix.expect }}" + + # api and testsuite are the only suites needing the NISTP256 sample + # keys and certs. Subtract them from the TESTS the Makefile computes, + # so suites added later still run. + - name: Run tests + run: | + if [ -n '${{ matrix.exclude_tests }}' ]; then + printf 'include Makefile\nprint-tests:\n\t@echo $(TESTS)\n' \ + > "$RUNNER_TEMP/print-tests.mk" + excl="" + for t in ${{ matrix.exclude_tests }}; do + excl="$excl -e $t" + done + keep=$(make -s -f "$RUNNER_TEMP/print-tests.mk" print-tests \ + | tr ' ' '\n' \ + | grep -vxF $excl \ + | tr '\n' ' ') + echo "running: $keep" + test -n "$keep" + timeout -k 30 1200 make check TESTS="$keep" + else + timeout -k 30 1200 make check + fi + + - name: Print test logs on failure + if: failure() + run: | + echo "=== test-suite.log ===" + cat test-suite.log || true + for f in tests/*.log apps/wolfsshd/test/*.log; do + echo "=== $f ==="; cat "$f" || true + done diff --git a/apps/wolfssh-options.c b/apps/wolfssh-options.c index ba1d42531..9b03f9da9 100644 --- a/apps/wolfssh-options.c +++ b/apps/wolfssh-options.c @@ -147,13 +147,27 @@ int main(void) #ifndef WOLFSSH_NO_MLDSA printf("MLDSA\n"); #endif - /* Same guard as cannedKeyAlgoNamesHostKey in src/internal.c: composites - * as a whole can be compiled out, and this one needs the ECDSA half. */ -#if !defined(WOLFSSH_NO_MLDSA_COMPOSITES) && \ - !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA_COMPOSITES + printf("MLDSA_COMPOSITES\n"); +#endif +#ifndef WOLFSSH_NO_MLDSA44_ES256 + printf("MLDSA44_ES256\n"); +#endif +#ifndef WOLFSSH_NO_MLDSA65_ES256 + printf("MLDSA65_ES256\n"); +#endif +#ifndef WOLFSSH_NO_MLDSA87_ES384 printf("MLDSA87_ES384\n"); #endif +#ifndef WOLFSSH_NO_MLDSA44_ED25519 + printf("MLDSA44_ED25519\n"); +#endif +#ifndef WOLFSSH_NO_MLDSA65_ED25519 + printf("MLDSA65_ED25519\n"); +#endif +#ifndef WOLFSSH_NO_MLDSA87_ED448 + printf("MLDSA87_ED448\n"); +#endif /* Applications. */ #ifdef WOLFSSH_SSHCLIENT diff --git a/apps/wolfsshd/test/test_configuration.c b/apps/wolfsshd/test/test_configuration.c index 28af9170f..9de5b64fd 100644 --- a/apps/wolfsshd/test/test_configuration.c +++ b/apps/wolfsshd/test/test_configuration.c @@ -3082,26 +3082,22 @@ static int test_CheckAuthKeysLineTypes(void) #endif #endif #endif - #if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) + #ifndef WOLFSSH_NO_MLDSA44_ES256 "ssh-mldsa44-es256@wolfssl.com", #endif - #if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA65_ES256 "ssh-mldsa65-es256@wolfssl.com", #endif - #if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA87_ES384 "ssh-mldsa87-es384@wolfssl.com", #endif - #if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA44_ED25519 "ssh-mldsa44-ed25519@openssh.com", #endif - #if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA65_ED25519 "ssh-mldsa65-ed25519@wolfssl.com", #endif - #if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) + #ifndef WOLFSSH_NO_MLDSA87_ED448 "ssh-mldsa87-ed448@wolfssl.com", #endif }; @@ -3264,9 +3260,9 @@ static int test_CheckAuthKeysLineMaxSz(void) #if !defined(WOLFSSH_NO_MLDSA87) keySz = WC_MLDSA_87_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ; - #if !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA87_ES384 type = "ssh-mldsa87-es384@wolfssl.com"; - #elif defined(HAVE_ED448) + #elif !defined(WOLFSSH_NO_MLDSA87_ED448) type = "ssh-mldsa87-ed448@wolfssl.com"; #else type = "ssh-mldsa-87"; @@ -3274,9 +3270,9 @@ static int test_CheckAuthKeysLineMaxSz(void) #endif #elif !defined(WOLFSSH_NO_MLDSA65) keySz = WC_MLDSA_65_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ; - #if !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA65_ES256 type = "ssh-mldsa65-es256@wolfssl.com"; - #elif !defined(WOLFSSH_NO_ED25519) && !defined(NO_SHA512) + #elif !defined(WOLFSSH_NO_MLDSA65_ED25519) type = "ssh-mldsa65-ed25519@wolfssl.com"; #else type = "ssh-mldsa-65"; @@ -3284,9 +3280,9 @@ static int test_CheckAuthKeysLineMaxSz(void) #endif #else keySz = WC_MLDSA_44_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ; - #if !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) + #ifndef WOLFSSH_NO_MLDSA44_ES256 type = "ssh-mldsa44-es256@wolfssl.com"; - #elif !defined(WOLFSSH_NO_ED25519) && !defined(NO_SHA512) + #elif !defined(WOLFSSH_NO_MLDSA44_ED25519) type = "ssh-mldsa44-ed25519@openssh.com"; #else type = "ssh-mldsa-44"; diff --git a/examples/client/client.c b/examples/client/client.c index 0041bd361..be0a1c393 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -999,7 +999,10 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) printf("\t%d: %s\n", idx, current); } } while (current != NULL); - printf("Set Key: %s\n\n", wolfSSH_GetAlgoListKey(ssh)); + printf("Set Key: %s\n", wolfSSH_GetAlgoListKey(ssh)); + /* Separate table from the host key list; both must gate alike. */ + printf("Set Key Accepted: %s\n\n", + wolfSSH_GetAlgoListKeyAccepted(ssh)); idx = 0; printf("Cipher:\n"); diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 4dde815b6..39c240191 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -2440,39 +2440,32 @@ typedef struct { const char* label; } MlDsaCompositeEntry; -/* NULL-terminated so the table is never empty if composites are compiled - * out or ECDSA and Ed25519/Ed448 are both disabled */ +/* NULL-terminated so the table is never empty. */ static const MlDsaCompositeEntry mldsaCompositeEntries[] = { -#ifndef WOLFSSH_NO_MLDSA_COMPOSITES -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 { "mldsa44-ed25519", "./keys/server-key-mldsa44ed25519", "ML-DSA-44+Ed25519" }, #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 { "mldsa44-es256", "./keys/server-key-mldsa44es256", "ML-DSA-44+ES256" }, #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 { "mldsa65-ed25519", "./keys/server-key-mldsa65ed25519", "ML-DSA-65+Ed25519" }, #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 { "mldsa65-es256", "./keys/server-key-mldsa65es256", "ML-DSA-65+ES256" }, #endif -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 { "mldsa87-ed448", "./keys/server-key-mldsa87ed448", "ML-DSA-87+Ed448" }, #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 { "mldsa87-es384", "./keys/server-key-mldsa87es384", "ML-DSA-87+ES384" }, #endif -#endif /* !WOLFSSH_NO_MLDSA_COMPOSITES */ { NULL, NULL, NULL } }; diff --git a/src/internal.c b/src/internal.c index 371a67008..b8c1836d4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -205,6 +205,11 @@ typedef char wolfSSH_channel_overhead_check[ WOLFSSH_NO_SSH_RSA_SHA1 Set when RSA or SHA1 are disabled. Set to disable use of RSA server authentication. + WOLFSSH_NO_ED25519 + Set unless HAVE_ED25519, HAVE_ED25519_SIGN, HAVE_ED25519_VERIFY, + WOLFSSL_ED25519_STREAMING_VERIFY, HAVE_ED25519_KEY_IMPORT and + HAVE_ED25519_KEY_EXPORT are all set. Disables ssh-ed25519 server and + user authentication as well as the ML-DSA+Ed25519 composites. WOLFSSH_NO_MLDSA Set when MLDSA is disabled and/or not included in wolfssl downloaded. WOLFSSH_NO_MLDSA44 @@ -213,6 +218,19 @@ typedef char wolfSSH_channel_overhead_check[ Set for ML-DSA-65. WOLFSSH_NO_MLDSA87 Set for ML-DSA-87. + WOLFSSH_NO_MLDSA44_ES256, WOLFSSH_NO_MLDSA65_ES256, + WOLFSSH_NO_MLDSA87_ES384, WOLFSSH_NO_MLDSA44_ED25519, + WOLFSSH_NO_MLDSA65_ED25519, WOLFSSH_NO_MLDSA87_ED448 + Set when the ML-DSA level or the traditional algorithm it pairs with is + disabled. Set to disable that one composite. The four prehashing with + SHA-512 (MLDSA65_ES256, MLDSA87_ES384, MLDSA44_ED25519, MLDSA65_ED25519) + also need WOLFSSL_SHA512, not merely the absence of NO_SHA512. + WOLFSSH_NO_MLDSA_COMPOSITES + Set when every composite above is disabled. Set to disable them all. + WOLFSSH_HAVE_COMPOSITE_ED448 + Derived, not user-settable. Set when HAVE_ED448, the four Ed448 + sub-feature macros, and WOLFSSL_SHAKE256 (the combo's prehash) are set. + WOLFSSH_NO_MLDSA87_ED448 follows from it. WOLFSSH_NO_ECDSA Set when ECC is disabled. Set to disable use of ECDSA server and user authentication. @@ -1121,30 +1139,24 @@ static const char cannedKexAlgoNames[] = /* ML-DSA listed first (post-quantum priority), then ECDSA, ED25519, RSA. */ static const char cannedKeyAlgoNames[] = -#ifndef WOLFSSH_NO_MLDSA_COMPOSITES -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 "ssh-mldsa87-ed448@wolfssl.com," #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 "ssh-mldsa87-es384@wolfssl.com," #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 "ssh-mldsa65-ed25519@wolfssl.com," #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 "ssh-mldsa65-es256@wolfssl.com," #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 "ssh-mldsa44-ed25519@openssh.com," #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 "ssh-mldsa44-es256@wolfssl.com," #endif -#endif /* !WOLFSSH_NO_MLDSA_COMPOSITES */ #ifndef WOLFSSH_NO_MLDSA87 "ssh-mldsa-87," #endif @@ -1230,30 +1242,24 @@ static const char cannedKeyAlgoNames[] = * ("*-cert-v01@openssh.com") names: host-cert verification is unimplemented, so * a client must not advertise them as host keys. Keep plain/X.509 in sync. */ static const char cannedKeyAlgoNamesHostKey[] = -#ifndef WOLFSSH_NO_MLDSA_COMPOSITES -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 "ssh-mldsa87-ed448@wolfssl.com," #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 "ssh-mldsa87-es384@wolfssl.com," #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 "ssh-mldsa65-ed25519@wolfssl.com," #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 "ssh-mldsa65-es256@wolfssl.com," #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 "ssh-mldsa44-ed25519@openssh.com," #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 "ssh-mldsa44-es256@wolfssl.com," #endif -#endif /* !WOLFSSH_NO_MLDSA_COMPOSITES */ #ifndef WOLFSSH_NO_MLDSA87 "ssh-mldsa-87," #endif @@ -3811,30 +3817,24 @@ static const NameIdPair NameIdMap[] = { #ifndef WOLFSSH_NO_MLDSA44 { ID_MLDSA44, TYPE_KEY, "ssh-mldsa-44" }, #endif -#ifndef WOLFSSH_NO_MLDSA_COMPOSITES -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 { ID_MLDSA44_ES256, TYPE_KEY, "ssh-mldsa44-es256@wolfssl.com" }, #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 { ID_MLDSA65_ES256, TYPE_KEY, "ssh-mldsa65-es256@wolfssl.com" }, #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 { ID_MLDSA87_ES384, TYPE_KEY, "ssh-mldsa87-es384@wolfssl.com" }, #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 { ID_MLDSA44_ED25519, TYPE_KEY, "ssh-mldsa44-ed25519@openssh.com" }, #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 { ID_MLDSA65_ED25519, TYPE_KEY, "ssh-mldsa65-ed25519@wolfssl.com" }, #endif -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 { ID_MLDSA87_ED448, TYPE_KEY, "ssh-mldsa87-ed448@wolfssl.com" }, #endif -#endif /* !WOLFSSH_NO_MLDSA_COMPOSITES */ #ifndef WOLFSSH_NO_MLDSA65 { ID_MLDSA65, TYPE_KEY, "ssh-mldsa-65" }, #endif @@ -5983,30 +5983,24 @@ static const byte cannedKeyAlgoClient[] = { #endif /* WOLFSSH_NO_SSH_RSA_SHA1 */ #endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */ #endif /* WOLFSSH_CERTS */ -#ifndef WOLFSSH_NO_MLDSA_COMPOSITES -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 ID_MLDSA87_ED448, #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 ID_MLDSA87_ES384, #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 ID_MLDSA65_ED25519, #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 ID_MLDSA65_ES256, #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 ID_MLDSA44_ED25519, #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 ID_MLDSA44_ES256, #endif -#endif /* !WOLFSSH_NO_MLDSA_COMPOSITES */ #ifndef WOLFSSH_NO_MLDSA87 ID_MLDSA87, #endif @@ -24432,7 +24426,7 @@ int WS_GetCompositeParams(byte keyId, CompositeParams* params) return WS_NOT_COMPILED; #else switch (keyId) { -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 case ID_MLDSA44_ES256: params->mldsaLevel = WC_ML_DSA_44; params->mldsaSigSz = WC_MLDSA_44_SIG_SIZE; @@ -24450,8 +24444,7 @@ int WS_GetCompositeParams(byte keyId, CompositeParams* params) params->eccCurveId = ECC_SECP256R1; break; #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 case ID_MLDSA65_ES256: params->mldsaLevel = WC_ML_DSA_65; params->mldsaSigSz = WC_MLDSA_65_SIG_SIZE; @@ -24469,8 +24462,7 @@ int WS_GetCompositeParams(byte keyId, CompositeParams* params) params->eccCurveId = ECC_SECP256R1; break; #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 case ID_MLDSA87_ES384: params->mldsaLevel = WC_ML_DSA_87; params->mldsaSigSz = WC_MLDSA_87_SIG_SIZE; @@ -24488,8 +24480,7 @@ int WS_GetCompositeParams(byte keyId, CompositeParams* params) params->eccCurveId = ECC_SECP384R1; break; #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 case ID_MLDSA44_ED25519: params->mldsaLevel = WC_ML_DSA_44; params->mldsaSigSz = WC_MLDSA_44_SIG_SIZE; @@ -24504,8 +24495,7 @@ int WS_GetCompositeParams(byte keyId, CompositeParams* params) params->tradPrivSz = ED25519_KEY_SIZE; break; #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 case ID_MLDSA65_ED25519: params->mldsaLevel = WC_ML_DSA_65; params->mldsaSigSz = WC_MLDSA_65_SIG_SIZE; @@ -24520,7 +24510,7 @@ int WS_GetCompositeParams(byte keyId, CompositeParams* params) params->tradPrivSz = ED25519_KEY_SIZE; break; #endif -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 case ID_MLDSA87_ED448: params->mldsaLevel = WC_ML_DSA_87; params->mldsaSigSz = WC_MLDSA_87_SIG_SIZE; @@ -24907,7 +24897,7 @@ static const CompositeTradOps compositeEd25519Ops = { }; #endif /* !WOLFSSH_NO_ED25519 */ -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 /* returns 0 on success, negative wc_ed448_init_ex() code on failure */ static int CompositeEd448Init(void* key, void* heap) { @@ -25014,7 +25004,7 @@ static const CompositeTradOps compositeEd448Ops = { CompositeEd448Sign, CompositeEd448Verify, TRAD_TYPE_ED448 }; -#endif /* HAVE_ED448 */ +#endif /* WOLFSSH_HAVE_COMPOSITE_ED448 */ /* returns matching CompositeTradOps for tradType, NULL if unsupported */ const CompositeTradOps* WS_GetTradOps(byte tradType) @@ -25028,7 +25018,7 @@ const CompositeTradOps* WS_GetTradOps(byte tradType) case TRAD_TYPE_ED25519: return &compositeEd25519Ops; #endif -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 case TRAD_TYPE_ED448: return &compositeEd448Ops; #endif diff --git a/src/keygen.c b/src/keygen.c index 58cb3d786..de0b3250c 100644 --- a/src/keygen.c +++ b/src/keygen.c @@ -376,7 +376,7 @@ int wolfSSH_MakeMlDsaKey(byte* out, word32 outSz, word32 level) /* Build OpenSSH-key-v1 envelope. */ #if !defined(WOLFSSH_NO_MLDSA) /* Guard matches MakeCompositeTradKey() callers to avoid unused warnings. */ -#if !defined(WOLFSSH_NO_ED25519) || defined(HAVE_ED448) || \ +#if !defined(WOLFSSH_NO_ED25519) || defined(WOLFSSH_HAVE_COMPOSITE_ED448) || \ !defined(WOLFSSH_NO_ECDSA) /* Shared init/makeKey/export/free chain for traditional types. */ static int MakeCompositeTradKeyGeneric(const CompositeTradOps* ops, void* key, @@ -386,6 +386,7 @@ static int MakeCompositeTradKeyGeneric(const CompositeTradOps* ops, void* key, int ret; word32 sz; + /* NULL heap: ops init with INVALID_DEVID, so keygen stays software. */ if (ops->init(key, NULL) != 0) { return WS_CRYPTO_FAILED; } @@ -434,7 +435,7 @@ static int MakeCompositeTradKey(WC_RNG* rng, const CompositeParams* params, #endif } else if (params->tradType == TRAD_TYPE_ED448) { -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 ed448_key key; ret = MakeCompositeTradKeyGeneric(ops, &key, rng, params, tradPub, tradPriv); diff --git a/tests/kex.c b/tests/kex.c index 08c00064c..6c9231b5d 100644 --- a/tests/kex.c +++ b/tests/kex.c @@ -518,39 +518,33 @@ int wolfSSH_KexTest(int argc, char** argv) #ifndef WOLFSSH_NO_MLDSA87 AssertIntEQ(wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa-87"), EXIT_SUCCESS); #endif -#ifndef WOLFSSH_NO_MLDSA_COMPOSITES -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 /* Uses the "@openssh.com" wire name that OpenSSH negotiates for this * algorithm, matching what wolfSSH now emits (see NameIdMap). */ AssertIntEQ( wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa44-ed25519@openssh.com"), EXIT_SUCCESS); #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 AssertIntEQ(wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa44-es256@wolfssl.com"), EXIT_SUCCESS); #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 AssertIntEQ(wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa65-es256@wolfssl.com"), EXIT_SUCCESS); #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 AssertIntEQ(wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa65-ed25519@wolfssl.com"), EXIT_SUCCESS); #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 AssertIntEQ(wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa87-es384@wolfssl.com"), EXIT_SUCCESS); #endif -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 AssertIntEQ(wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa87-ed448@wolfssl.com"), EXIT_SUCCESS); #endif -#endif /* WOLFSSH_NO_MLDSA_COMPOSITES */ AssertIntEQ(wolfSSH_Cleanup(), WS_SUCCESS); diff --git a/tests/unit.c b/tests/unit.c index dc07c65d2..a7d0c6bab 100644 --- a/tests/unit.c +++ b/tests/unit.c @@ -1166,33 +1166,28 @@ static int test_MlDsaKeyGen(void) #ifndef WOLFSSH_NO_MLDSA_COMPOSITES static int test_MlDsaCompositeKeyGen(void) { - /* NULL-terminated so the table is never empty if ECDSA and - * Ed25519/Ed448 are all disabled while ML-DSA is enabled */ + /* NULL-terminated sentinel bounds the loop. */ static const struct { word32 level; word32 tradType; const char* name; } params[] = { - #if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA44_ED25519 { WOLFSSH_MLDSAKEY_44, WOLFSSH_COMPOSITE_TRAD_ED25519, "44+Ed25519" }, #endif - #if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) + #ifndef WOLFSSH_NO_MLDSA44_ES256 { WOLFSSH_MLDSAKEY_44, WOLFSSH_COMPOSITE_TRAD_ECDSA, "44+ES256" }, #endif - #if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA65_ED25519 { WOLFSSH_MLDSAKEY_65, WOLFSSH_COMPOSITE_TRAD_ED25519, "65+Ed25519" }, #endif - #if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA65_ES256 { WOLFSSH_MLDSAKEY_65, WOLFSSH_COMPOSITE_TRAD_ECDSA, "65+ES256" }, #endif - #if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) + #ifndef WOLFSSH_NO_MLDSA87_ED448 { WOLFSSH_MLDSAKEY_87, WOLFSSH_COMPOSITE_TRAD_ED448, "87+Ed448" }, #endif - #if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) + #ifndef WOLFSSH_NO_MLDSA87_ES384 { WOLFSSH_MLDSAKEY_87, WOLFSSH_COMPOSITE_TRAD_ECDSA, "87+ES384" }, #endif { 0, 0, NULL } @@ -1220,7 +1215,7 @@ static int test_MlDsaCompositeKeyGen(void) sz = wolfSSH_MakeMlDsaCompositeKey(buf, bufSz, params[i].level, params[i].tradType); - if (sz < 0) { + if (sz <= 0) { printf("MlDsaCompositeKeyGen: MakeMlDsaCompositeKey %s " "failed (%d)\n", params[i].name, sz); WFREE(buf, NULL, DYNTYPE_BUFFER); @@ -1291,7 +1286,34 @@ static int test_MlDsaCompositeKeyGen(void) } } - /* Skip undersize check if no algorithms enabled. */ + /* A gated-off pair must report WS_NOT_COMPILED, not WS_BAD_ARGUMENT. */ +#if defined(WOLFSSH_NO_MLDSA44_ES256) && !defined(WOLFSSH_NO_MLDSA44) && \ + !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) + if (result == 0) { + byte dummy[1]; + int sz = wolfSSH_MakeMlDsaCompositeKey(dummy, sizeof(dummy), + WOLFSSH_MLDSAKEY_44, WOLFSSH_COMPOSITE_TRAD_ECDSA); + if (sz != WS_NOT_COMPILED) { + printf("MlDsaCompositeKeyGen: gated-off composite wrong " + "result %d\n", sz); + result = -129; + } + } +#endif + /* Same, for a derived gate rather than a user-set one. */ +#if defined(WOLFSSH_NO_MLDSA87_ED448) && !defined(WOLFSSH_NO_MLDSA87) + if (result == 0) { + byte dummy[1]; + int sz = wolfSSH_MakeMlDsaCompositeKey(dummy, sizeof(dummy), + WOLFSSH_MLDSAKEY_87, WOLFSSH_COMPOSITE_TRAD_ED448); + if (sz != WS_NOT_COMPILED) { + printf("MlDsaCompositeKeyGen: derived gated-off composite wrong " + "result %d\n", sz); + result = -130; + } + } +#endif + if (result == 0 && firstSz > 0) { byte* buf = (byte*)WMALLOC(firstSz, NULL, DYNTYPE_BUFFER); @@ -1316,6 +1338,35 @@ static int test_MlDsaCompositeKeyGen(void) } #endif /* WOLFSSH_NO_MLDSA_COMPOSITES */ +/* With every composite off the entry point stays compiled: a valid pair must + * report WS_NOT_COMPILED, a bad one still WS_BAD_ARGUMENT. */ +#if defined(WOLFSSH_NO_MLDSA_COMPOSITES) && !defined(WOLFSSH_NO_MLDSA) +static int test_MlDsaCompositesDisabled(void) +{ + byte dummy[1]; + int result = 0; + int sz; + + sz = wolfSSH_MakeMlDsaCompositeKey(dummy, sizeof(dummy), + WOLFSSH_MLDSAKEY_44, WOLFSSH_COMPOSITE_TRAD_ECDSA); + if (sz != WS_NOT_COMPILED) { + printf("MlDsaCompositesDisabled: valid pair wrong result %d\n", sz); + result = -131; + } + + if (result == 0) { + sz = wolfSSH_MakeMlDsaCompositeKey(dummy, sizeof(dummy), 9999, 9999); + if (sz != WS_BAD_ARGUMENT) { + printf("MlDsaCompositesDisabled: invalid pair wrong result %d\n", + sz); + result = -132; + } + } + + return result; +} +#endif + #endif /* WOLFSSH_NO_MLDSA */ #endif /* WOLFSSH_KEYGEN */ @@ -14816,7 +14867,7 @@ static int test_DoUserAuthRequestMlDsaComposite_Params(const char* keyTypeName, ed25519_key ed25519Key; int ed25519Init = 0; #endif -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 ed448_key ed448Key; int ed448Init = 0; #endif @@ -14885,7 +14936,7 @@ static int test_DoUserAuthRequestMlDsaComposite_Params(const char* keyTypeName, #endif } else if (params.tradType == TRAD_TYPE_ED448) { -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 if (wc_ed448_init(&ed448Key) != 0) { result = -710; goto done; } ed448Init = 1; if (wc_ed448_make_key(&rng, 57, @@ -14923,7 +14974,7 @@ static int test_DoUserAuthRequestMlDsaComposite_Params(const char* keyTypeName, #endif } else if (params.tradType == TRAD_TYPE_ED448) { -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 word32 sz = params.tradPubSz; if (wc_ed448_export_public(&ed448Key, tradPub, &sz) != 0 || sz != params.tradPubSz) { result = -720; goto done; } @@ -15018,7 +15069,7 @@ static int test_DoUserAuthRequestMlDsaComposite_Params(const char* keyTypeName, #endif } else if (params.tradType == TRAD_TYPE_ED448) { -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 tradSigSz = 114; if (wc_ed448_sign_msg(m_prime, m_prime_len, tradSig, &tradSigSz, &ed448Key, NULL, 0) != 0) { result = -730; goto done; } @@ -15154,7 +15205,7 @@ static int test_DoUserAuthRequestMlDsaComposite_Params(const char* keyTypeName, #ifndef WOLFSSH_NO_ED25519 if (ed25519Init) wc_ed25519_free(&ed25519Key); #endif -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 if (ed448Init) wc_ed448_free(&ed448Key); #endif if (rngInit) wc_FreeRng(&rng); @@ -15243,36 +15294,32 @@ static int test_SignHMlDsaComposite_Params(const char* label, byte keyId) static int test_SignHMlDsaComposite(void) { int ret = 0; -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 ret = test_SignHMlDsaComposite_Params("ssh-mldsa44-es256@wolfssl.com", ID_MLDSA44_ES256); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 ret = test_SignHMlDsaComposite_Params("ssh-mldsa65-es256@wolfssl.com", ID_MLDSA65_ES256); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 ret = test_SignHMlDsaComposite_Params("ssh-mldsa87-es384@wolfssl.com", ID_MLDSA87_ES384); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 ret = test_SignHMlDsaComposite_Params("ssh-mldsa44-ed25519@openssh.com", ID_MLDSA44_ED25519); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 ret = test_SignHMlDsaComposite_Params("ssh-mldsa65-ed25519@wolfssl.com", ID_MLDSA65_ED25519); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 ret = test_SignHMlDsaComposite_Params("ssh-mldsa87-ed448@wolfssl.com", ID_MLDSA87_ED448); if (ret != 0) return ret; @@ -15280,8 +15327,7 @@ static int test_SignHMlDsaComposite(void) return 0; } -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 /* Reset a WS_KeySignature to a clean slate before (re)using it as an * ID_MLDSA44_ED25519 decode target. */ static void MlDsaTest_ResetKeySig(WS_KeySignature* keySig) @@ -15295,8 +15341,7 @@ static void MlDsaTest_ResetKeySig(WS_KeySignature* keySig) /* E2E parser test with minimal OpenSSH-key-v1 envelope. */ static int test_PrepareUserAuthRequestMlDsaComposite_OpenSshEnvelope(void) { -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 static const char keyTypeName[] = "ssh-mldsa44-ed25519@openssh.com"; static const char magic[] = "openssh-key-v1"; static const char none[] = "none"; @@ -15753,41 +15798,37 @@ static int test_BuildUserAuthRequestMlDsaComposite_Params( static int test_BuildUserAuthRequestMlDsaComposite(void) { int ret = 0; -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 ret = test_BuildUserAuthRequestMlDsaComposite_Params( "ssh-mldsa44-es256@wolfssl.com", WOLFSSH_MLDSAKEY_44, WOLFSSH_COMPOSITE_TRAD_ECDSA, ID_MLDSA44_ES256); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 ret = test_BuildUserAuthRequestMlDsaComposite_Params( "ssh-mldsa65-es256@wolfssl.com", WOLFSSH_MLDSAKEY_65, WOLFSSH_COMPOSITE_TRAD_ECDSA, ID_MLDSA65_ES256); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 ret = test_BuildUserAuthRequestMlDsaComposite_Params( "ssh-mldsa87-es384@wolfssl.com", WOLFSSH_MLDSAKEY_87, WOLFSSH_COMPOSITE_TRAD_ECDSA, ID_MLDSA87_ES384); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 ret = test_BuildUserAuthRequestMlDsaComposite_Params( "ssh-mldsa44-ed25519@openssh.com", WOLFSSH_MLDSAKEY_44, WOLFSSH_COMPOSITE_TRAD_ED25519, ID_MLDSA44_ED25519); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 ret = test_BuildUserAuthRequestMlDsaComposite_Params( "ssh-mldsa65-ed25519@wolfssl.com", WOLFSSH_MLDSAKEY_65, WOLFSSH_COMPOSITE_TRAD_ED25519, ID_MLDSA65_ED25519); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 ret = test_BuildUserAuthRequestMlDsaComposite_Params( "ssh-mldsa87-ed448@wolfssl.com", WOLFSSH_MLDSAKEY_87, WOLFSSH_COMPOSITE_TRAD_ED448, ID_MLDSA87_ED448); @@ -15802,7 +15843,7 @@ static int test_BuildUserAuthRequestMlDsaComposite(void) static int test_DoUserAuthRequestMlDsaComposite(void) { int ret = 0; -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) +#ifndef WOLFSSH_NO_MLDSA44_ES256 ret = test_DoUserAuthRequestMlDsaComposite_Params("ssh-mldsa44-es256@wolfssl.com", ID_MLDSA44_ES256, 0); if (ret != 0) return ret; @@ -15819,8 +15860,7 @@ static int test_DoUserAuthRequestMlDsaComposite(void) ID_MLDSA44_ES256, 4); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA65) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ES256 ret = test_DoUserAuthRequestMlDsaComposite_Params("ssh-mldsa65-es256@wolfssl.com", ID_MLDSA65_ES256, 0); if (ret != 0) return ret; @@ -15837,8 +15877,7 @@ static int test_DoUserAuthRequestMlDsaComposite(void) ID_MLDSA65_ES256, 4); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA87) && \ - !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA87_ES384 ret = test_DoUserAuthRequestMlDsaComposite_Params("ssh-mldsa87-es384@wolfssl.com", ID_MLDSA87_ES384, 0); if (ret != 0) return ret; @@ -15855,8 +15894,7 @@ static int test_DoUserAuthRequestMlDsaComposite(void) ID_MLDSA87_ES384, 4); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA44_ED25519 ret = test_DoUserAuthRequestMlDsaComposite_Params( "ssh-mldsa44-ed25519@openssh.com", ID_MLDSA44_ED25519, 0); if (ret != 0) return ret; @@ -15873,8 +15911,7 @@ static int test_DoUserAuthRequestMlDsaComposite(void) "ssh-mldsa44-ed25519@openssh.com", ID_MLDSA44_ED25519, 4); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \ - !defined(NO_SHA512) +#ifndef WOLFSSH_NO_MLDSA65_ED25519 ret = test_DoUserAuthRequestMlDsaComposite_Params("ssh-mldsa65-ed25519@wolfssl.com", ID_MLDSA65_ED25519, 0); if (ret != 0) return ret; @@ -15891,7 +15928,7 @@ static int test_DoUserAuthRequestMlDsaComposite(void) ID_MLDSA65_ED25519, 4); if (ret != 0) return ret; #endif -#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448) +#ifndef WOLFSSH_NO_MLDSA87_ED448 ret = test_DoUserAuthRequestMlDsaComposite_Params("ssh-mldsa87-ed448@wolfssl.com", ID_MLDSA87_ED448, 0); if (ret != 0) return ret; @@ -23487,6 +23524,11 @@ int wolfSSH_UnitTest(int argc, char** argv) printf("MlDsaCompositeKeyGen: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; +#else + unitResult = test_MlDsaCompositesDisabled(); + printf("MlDsaCompositesDisabled: %s\n", + (unitResult == 0 ? "SUCCESS" : "FAILED")); + testResult = testResult || unitResult; #endif /* WOLFSSH_NO_MLDSA_COMPOSITES */ #endif #endif /* WOLFSSH_KEYGEN */ diff --git a/wolfssh/internal.h b/wolfssh/internal.h index d41e93309..c44bccf5a 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -153,6 +153,8 @@ extern "C" { #endif #if !defined(HAVE_ED25519) \ + || !defined(HAVE_ED25519_SIGN) \ + || !defined(HAVE_ED25519_VERIFY) \ || !defined(WOLFSSL_ED25519_STREAMING_VERIFY) \ || !defined(HAVE_ED25519_KEY_IMPORT) \ || !defined(HAVE_ED25519_KEY_EXPORT) @@ -381,11 +383,53 @@ extern "C" { #undef WOLFSSH_NO_PUBKEY_AUTH #define WOLFSSH_NO_PUBKEY_AUTH #endif -/* A composite needs an ML-DSA level and a traditional algorithm. */ -#if (defined(WOLFSSH_NO_MLDSA44) && defined(WOLFSSH_NO_MLDSA65) && \ - defined(WOLFSSH_NO_MLDSA87)) || \ - (defined(WOLFSSH_NO_ECDSA) && defined(WOLFSSH_NO_ED25519) && \ - !defined(HAVE_ED448)) +/* Ed448 composites need the sub-feature macros and the SHAKE-256 prehash, + * not just HAVE_ED448. Every composite Ed448 site keys off this one, so the + * copies can't drift. Derived, so clear any supplied definition first. */ +#undef WOLFSSH_HAVE_COMPOSITE_ED448 +#if defined(HAVE_ED448) && defined(WOLFSSL_SHAKE256) && \ + defined(HAVE_ED448_SIGN) && \ + defined(HAVE_ED448_VERIFY) && defined(HAVE_ED448_KEY_IMPORT) && \ + defined(HAVE_ED448_KEY_EXPORT) + #define WOLFSSH_HAVE_COMPOSITE_ED448 +#endif +/* One gate per ML-DSA composite, so the pairing rules live here only. + * WOLFSSH_NO_MLDSA_COMPOSITES turns them all off and is set when none are + * left. Governs negotiation and key loading, not code size. */ +#if defined(WOLFSSH_NO_MLDSA44) || defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) || \ + defined(WOLFSSH_NO_MLDSA_COMPOSITES) + #undef WOLFSSH_NO_MLDSA44_ES256 + #define WOLFSSH_NO_MLDSA44_ES256 +#endif +#if defined(WOLFSSH_NO_MLDSA65) || defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) || \ + !defined(WOLFSSL_SHA512) || defined(WOLFSSH_NO_MLDSA_COMPOSITES) + #undef WOLFSSH_NO_MLDSA65_ES256 + #define WOLFSSH_NO_MLDSA65_ES256 +#endif +#if defined(WOLFSSH_NO_MLDSA87) || defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) || \ + !defined(WOLFSSL_SHA512) || defined(WOLFSSH_NO_MLDSA_COMPOSITES) + #undef WOLFSSH_NO_MLDSA87_ES384 + #define WOLFSSH_NO_MLDSA87_ES384 +#endif +#if defined(WOLFSSH_NO_MLDSA44) || defined(WOLFSSH_NO_ED25519) || \ + !defined(WOLFSSL_SHA512) || defined(WOLFSSH_NO_MLDSA_COMPOSITES) + #undef WOLFSSH_NO_MLDSA44_ED25519 + #define WOLFSSH_NO_MLDSA44_ED25519 +#endif +#if defined(WOLFSSH_NO_MLDSA65) || defined(WOLFSSH_NO_ED25519) || \ + !defined(WOLFSSL_SHA512) || defined(WOLFSSH_NO_MLDSA_COMPOSITES) + #undef WOLFSSH_NO_MLDSA65_ED25519 + #define WOLFSSH_NO_MLDSA65_ED25519 +#endif +#if defined(WOLFSSH_NO_MLDSA87) || !defined(WOLFSSH_HAVE_COMPOSITE_ED448) || \ + defined(WOLFSSH_NO_MLDSA_COMPOSITES) + #undef WOLFSSH_NO_MLDSA87_ED448 + #define WOLFSSH_NO_MLDSA87_ED448 +#endif +#if defined(WOLFSSH_NO_MLDSA44_ES256) && defined(WOLFSSH_NO_MLDSA65_ES256) && \ + defined(WOLFSSH_NO_MLDSA87_ES384) && \ + defined(WOLFSSH_NO_MLDSA44_ED25519) && \ + defined(WOLFSSH_NO_MLDSA65_ED25519) && defined(WOLFSSH_NO_MLDSA87_ED448) #undef WOLFSSH_NO_MLDSA_COMPOSITES #define WOLFSSH_NO_MLDSA_COMPOSITES #endif @@ -1504,11 +1548,11 @@ typedef struct WS_MlDsaCompositeBody { #ifndef WOLFSSH_NO_ED25519 ed25519_key ed25519; #endif -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 ed448_key ed448; #endif #if defined(WOLFSSH_NO_ECDSA) && defined(WOLFSSH_NO_ED25519) && \ - !defined(HAVE_ED448) + !defined(WOLFSSH_HAVE_COMPOSITE_ED448) /* keep union non-empty */ byte placeholder; #endif @@ -1575,13 +1619,13 @@ typedef struct WS_KeySignature { /* max trad pubkey size */ #define COMPOSITE_MAX_TRAD_PUB_SZ (1 + (2 * ECC_P384_COORD_SZ)) /* max trad privkey size */ -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 #define COMPOSITE_MAX_TRAD_PRIV_SZ ED448_KEY_SIZE #else #define COMPOSITE_MAX_TRAD_PRIV_SZ ECC_P384_COORD_SZ #endif /* max trad sig size */ -#ifdef HAVE_ED448 +#ifdef WOLFSSH_HAVE_COMPOSITE_ED448 #define COMPOSITE_MAX_TRAD_SIG_SZ ED448_SIG_SIZE #else #define COMPOSITE_MAX_TRAD_SIG_SZ (2 * (LENGTH_SZ + ECC_P384_COORD_SZ + 1))