From bb75134c3ee94490450c628ba87874cf9a2155eb Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 4 Nov 2026 14:53:10 -0700 Subject: [PATCH 1/4] TLS: use rsa_pss_pss_* with id-RSASSA-PSS keys Pick, label and check RSA-PSS signature schemes by the key's OID in TLS 1.2 and 1.3, honour the key's PSS parameters, and accept rsa_pss_pss_* in a TLS 1.2 CertificateVerify. Wire rsapss.test into make check and add a unit test. Fixes #11988 --- scripts/include.am | 4 +- scripts/rsapss.test | 205 +++++++++++++++++++++++++++++++----- src/internal.c | 130 ++++++++++++++++++++--- src/ssl_load.c | 4 + src/tls13.c | 56 +++++++--- tests/api.c | 228 ++++++++++++++++++++++++++++++++++++++++ wolfcrypt/src/asn.c | 3 + wolfssl/internal.h | 11 ++ wolfssl/wolfcrypt/asn.h | 2 + 9 files changed, 583 insertions(+), 60 deletions(-) diff --git a/scripts/include.am b/scripts/include.am index acbe4646562..21fa5e5473d 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -90,6 +90,9 @@ endif if BUILD_TLS13 dist_noinst_SCRIPTS+= scripts/tls13.test endif +if BUILD_RSA +dist_noinst_SCRIPTS+= scripts/rsapss.test +endif endif # end of BUILD_EXAMPLE_SERVERS @@ -160,7 +163,6 @@ EXTRA_DIST += scripts/sniffer-static-rsa.pcap \ # leave openssl.test as extra until non bash works EXTRA_DIST += scripts/openssl.test -EXTRA_DIST += scripts/rsapss.test EXTRA_DIST += scripts/dertoc.pl diff --git a/scripts/rsapss.test b/scripts/rsapss.test index b80325f0697..0965fc1a95e 100755 --- a/scripts/rsapss.test +++ b/scripts/rsapss.test @@ -26,8 +26,16 @@ if ./examples/server/server -? 2>&1 | grep "Server not compiled in!" ; then exit 77 fi -if ! ./examples/client/client -V | grep -q 4; then - echo "skipping because TLS 1.3 not enabled in this build" +HAVE_TLS12=no +HAVE_TLS13=no +HAVE_DTLS12=no +./examples/client/client -V | grep -q 3 && HAVE_TLS12=yes +./examples/client/client -V | grep -q 4 && HAVE_TLS13=yes +if [ "$HAVE_TLS12" = yes ] && grep -q -- '-DWOLFSSL_DTLS\b' config.log 2>/dev/null; then + HAVE_DTLS12=yes +fi +if [ "$HAVE_TLS12" = no ] && [ "$HAVE_TLS13" = no ]; then + echo "skipping because neither TLS 1.2 nor TLS 1.3 is enabled in this build" exit 0 fi if ! grep -q -- -DWC_RSA_PSS config.log 2>/dev/null; then @@ -121,37 +129,182 @@ generate_port() { } WOLFSSL_SERVER=./examples/server/server +WOLFSSL_CLIENT=./examples/client/client -start_wolfssl_server() { - generate_port - server_port=$port - $TIMEOUT_KILL_2M $WOLFSSL_SERVER -p "$server_port" -v 4 -c "$CERT_DIR"/rsapss/server-rsapss.pem -k "$CERT_DIR"/rsapss/server-rsapss-priv.pem -A "$CERT_DIR"/rsapss/root-rsapss.pem -d & +# id-RSASSA-PSS keys. The 2048-bit keys declare SHA-256 and a 32-byte salt, +# binding per RFC 4055 1.2; the 3072-bit keys declare no parameters. +PSS_SVR="-c $CERT_DIR/rsapss/server-rsapss.pem -k $CERT_DIR/rsapss/server-rsapss-priv.pem" +PSS3072_SVR="-c $CERT_DIR/rsapss/server-3072-rsapss.pem -k $CERT_DIR/rsapss/server-3072-rsapss-priv.pem" +PSS_CLI_OPENSSL="-cert $CERT_DIR/rsapss/client-rsapss.pem -key $CERT_DIR/rsapss/client-rsapss-priv.pem" +PSS_CLI_WOLFSSL="-c $CERT_DIR/rsapss/client-rsapss.pem -k $CERT_DIR/rsapss/client-rsapss-priv.pem" +PSS3072_CLI_WOLFSSL="-c $CERT_DIR/rsapss/client-3072-rsapss.pem -k $CERT_DIR/rsapss/client-3072-rsapss-priv.pem" +# rsaEncryption key, for the control cases. +RSA_SVR="-c $CERT_DIR/server-cert.pem -k $CERT_DIR/server-key.pem" + +cases=0 +fails=0 + +# The examples check CRLs whenever CRL support is compiled in, and the RSA-PSS +# test certificates have none, so turn that off on both sides. +SVR_NO_CRL="" +CLI_NO_CRL="" +if grep -q -- '-DHAVE_CRL\b' config.log 2>/dev/null; then + SVR_NO_CRL="-V" + CLI_NO_CRL="-C" +fi + +if ! command -v "$OPENSSL" >/dev/null 2>&1; then + echo "skipping because $OPENSSL is not available" + exit 0 +fi +# The rsa_pss_pss_* scheme names need OpenSSL 1.1.1 or later. +if ! "$OPENSSL" version 2>/dev/null | grep -Eq 'OpenSSL (1\.1\.1|[3-9]\.)'; then + echo "skipping because $OPENSSL is older than 1.1.1" + exit 0 +fi + +fail_case() { + echo "FAIL: $1" + echo "--- wolfSSL/OpenSSL server output ---" + cat rsapss.server.log + echo "--- client output ---" + cat rsapss.client.log + fails=$((fails+1)) } -# -# Run OpenSSL client against wolfSSL server -# -do_openssl_client() { - echo "test connection" | $TIMEOUT_KILL_2M $OPENSSL s_client -connect 127.0.0.1:"$server_port" -cert "$CERT_DIR"/rsapss/client-rsapss.pem -key "$CERT_DIR"/rsapss/client-rsapss-priv.pem -CAfile "$CERT_DIR"/rsapss/root-rsapss.pem > rsapss.test.log +# wolfSSL server, OpenSSL s_client: $1 name, $2 "Peer signature type" s_client +# must report or FAIL, $3 server arguments, $4 s_client arguments. +wolfssl_server_case() { + local name="$1" expect="$2" sargs="$3" cargs="$4" result + cases=$((cases+1)) + generate_port + $TIMEOUT_KILL_2M $WOLFSSL_SERVER -p "$port" $SVR_NO_CRL $sargs \ + > rsapss.server.log 2>&1 & + server_pid=$! + sleep 1 + echo "test connection" | $TIMEOUT_KILL_2M $OPENSSL s_client \ + -connect 127.0.0.1:"$port" $cargs > rsapss.client.log 2>&1 result=$? - cat rsapss.test.log - if [ "$result" != 0 ] - then - echo "$OPENSSL s_client command failed" - exit 1 + sleep 0.2 + kill $server_pid 2>/dev/null + wait $server_pid 2>/dev/null + if [ "$expect" = FAIL ]; then + if [ "$result" = 0 ]; then + fail_case "$name: handshake completed but must fail" + fi + elif [ "$result" != 0 ]; then + fail_case "$name: handshake failed" + elif ! grep -q "^Peer signature type: $expect\$" rsapss.client.log; then + fail_case "$name: expected peer signature type $expect" fi - grep -q "Peer signature type:.*rsa_pss_rsae_sha256" rsapss.test.log + rm -f rsapss.server.log rsapss.client.log +} + +# OpenSSL s_server with client auth, wolfSSL client: $1 name, $2 OK (expects an +# rsa_pss_pss_* CertificateVerify) or FAIL, $3 s_server arguments, $4 client +# arguments, $5 client certificate and key arguments, $6 that self-signed +# certificate as the s_server trust anchor. +openssl_server_case() { + local name="$1" expect="$2" sargs="$3" cargs="$4" ccert="$5" cca="$6" + local result + cases=$((cases+1)) + generate_port + $TIMEOUT_KILL_2M $OPENSSL s_server -www -accept "$port" -Verify 1 \ + -CAfile "$cca" \ + -cert "$CERT_DIR"/server-cert.pem -key "$CERT_DIR"/server-key.pem \ + -msg $sargs > rsapss.server.log 2>&1 & + server_pid=$! + sleep 1 + $TIMEOUT_KILL_2M $WOLFSSL_CLIENT -p "$port" $CLI_NO_CRL -g \ + -A "$CERT_DIR"/ca-cert.pem $ccert $cargs > rsapss.client.log 2>&1 result=$? - rm -f rsapss.test.log - if [ "$result" == 0 ] - then - echo "Test failed: Peer signature type identified as rsa_pss_rsae_sha256" - exit 1 + kill $server_pid 2>/dev/null + wait $server_pid 2>/dev/null + if [ "$expect" = FAIL ]; then + if [ "$result" = 0 ]; then + fail_case "$name: handshake completed but must fail" + fi + elif [ "$result" != 0 ]; then + fail_case "$name: handshake failed" + elif ! grep -A1 'CertificateVerify' rsapss.server.log | \ + grep -Eq '^ +0f 00 [0-9a-f]{2} [0-9a-f]{2} 08 (09|0a|0b) '; then + fail_case "$name: CertificateVerify is not rsa_pss_pss_*" fi + rm -f rsapss.server.log rsapss.client.log } -start_wolfssl_server -sleep 1 -do_openssl_client -echo -e "\nSuccess!\n\n" +# An id-RSASSA-PSS key signs with rsa_pss_pss_* only, from the peer's offer, and +# an rsaEncryption key never does (RFC 8446 4.2.3); the schemes apply to +# (D)TLS 1.2 as well (RFC 8446 1.3). +for ver in tls12 tls13 dtls12; do + case $ver in + tls12) [ "$HAVE_TLS12" = yes ] || continue + sv="-v 3"; cv="-tls1_2" ;; + tls13) [ "$HAVE_TLS13" = yes ] || continue + sv="-v 4"; cv="-tls1_3" ;; + dtls12) [ "$HAVE_DTLS12" = yes ] || continue + # Explicit MTU: s_client cannot query the loopback path MTU on + # every platform and would otherwise fragment the ClientHello. + sv="-v 3 -u"; cv="-dtls1_2 -mtu 1500" ;; + esac + wolfssl_server_case "$ver PSS server, default list" rsa_pss_pss_sha256 \ + "$sv -d $PSS_SVR" "$cv" + wolfssl_server_case "$ver PSS server, rsa_pss_pss_sha256 only" rsa_pss_pss_sha256 \ + "$sv -d $PSS_SVR" "$cv -sigalgs rsa_pss_pss_sha256" + wolfssl_server_case "$ver PSS server, key parameters fix SHA-256" FAIL \ + "$sv -d $PSS_SVR" "$cv -sigalgs rsa_pss_pss_sha384" + wolfssl_server_case "$ver PSS 3072 server, rsa_pss_pss_sha384 only" rsa_pss_pss_sha384 \ + "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha384" + wolfssl_server_case "$ver PSS 3072 server, rsa_pss_pss_sha512 only" rsa_pss_pss_sha512 \ + "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha512" + wolfssl_server_case "$ver PSS 3072 server, hash from the pss_pss offer" rsa_pss_pss_sha384 \ + "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha384:rsa_pss_rsae_sha256" + wolfssl_server_case "$ver PSS server, never rsa_pkcs1_*" rsa_pss_pss_sha256 \ + "$sv -d $PSS_SVR" "$cv -sigalgs rsa_pss_pss_sha256:rsa_pkcs1_sha256" + wolfssl_server_case "$ver PSS server, hash from the pss_pss offer" rsa_pss_pss_sha256 \ + "$sv -d $PSS_SVR" "$cv -sigalgs rsa_pss_pss_sha256:rsa_pss_rsae_sha384" + wolfssl_server_case "$ver PSS server, rsa_pss_rsae_* only offered" FAIL \ + "$sv -d $PSS_SVR" "$cv -sigalgs rsa_pss_rsae_sha256" + wolfssl_server_case "$ver PSS server, PSS client certificate" rsa_pss_pss_sha256 \ + "$sv $PSS_SVR -A $CERT_DIR/rsapss/client-rsapss.pem" "$cv $PSS_CLI_OPENSSL" + wolfssl_server_case "$ver rsaEncryption server, rsa_pss_rsae_*" rsa_pss_rsae_sha256 \ + "$sv -d $RSA_SVR" "$cv -sigalgs rsa_pss_rsae_sha256:rsa_pss_pss_sha256" + wolfssl_server_case "$ver rsaEncryption server, rsa_pss_pss_* only offered" FAIL \ + "$sv -d $RSA_SVR" "$cv -sigalgs rsa_pss_pss_sha256" +done + +for ver in tls12 tls13; do + case $ver in + tls12) [ "$HAVE_TLS12" = yes ] || continue + ov="-tls1_2"; wv="-v 3" ;; + tls13) [ "$HAVE_TLS13" = yes ] || continue + ov="-tls1_3"; wv="-v 4" ;; + esac + cli="$PSS_CLI_WOLFSSL" + cca="$CERT_DIR/rsapss/client-rsapss.pem" + openssl_server_case "$ver PSS client, default list" OK \ + "$ov" "$wv" "$cli" "$cca" + openssl_server_case "$ver PSS client, rsa_pss_pss_sha256 only" OK \ + "$ov -client_sigalgs rsa_pss_pss_sha256" "$wv" "$cli" "$cca" + openssl_server_case "$ver PSS client, never rsa_pkcs1_*" OK \ + "$ov -client_sigalgs rsa_pss_pss_sha256:rsa_pkcs1_sha256" "$wv" "$cli" "$cca" + openssl_server_case "$ver PSS client, rsa_pss_rsae_* only offered" FAIL \ + "$ov -client_sigalgs rsa_pss_rsae_sha256" "$wv" "$cli" "$cca" + openssl_server_case "$ver PSS client, key parameters fix SHA-256" FAIL \ + "$ov -client_sigalgs rsa_pss_pss_sha384" "$wv" "$cli" "$cca" + if [ "$ver" = tls12 ]; then + openssl_server_case "$ver PSS client, rsa_pkcs1_* only offered" FAIL \ + "$ov -client_sigalgs rsa_pkcs1_sha256" "$wv" "$cli" "$cca" + fi + cli="$PSS3072_CLI_WOLFSSL" + cca="$CERT_DIR/rsapss/client-3072-rsapss.pem" + openssl_server_case "$ver PSS 3072 client, rsa_pss_pss_sha384 only" OK \ + "$ov -client_sigalgs rsa_pss_pss_sha384" "$wv" "$cli" "$cca" +done + +if [ "$fails" != 0 ]; then + echo -e "\n$fails of $cases cases failed\n" + exit 1 +fi +echo -e "\nSuccess! ($cases cases)\n\n" exit 0 diff --git a/src/internal.c b/src/internal.c index 1c5196874d2..d28b753335f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -4130,11 +4130,14 @@ static WC_INLINE void AddSuiteHashSigAlgo(byte* hashSigAlgo, byte macAlgo, if (sigAlgo == rsa_pss_sa_algo) { /* RSA PSS is sig then mac */ ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, sigAlgo, macAlgo); - #ifdef WOLFSSL_TLS13 - /* Add the certificate algorithm as well */ + /* rsa_pss_pss_* too, also in TLS 1.2 (RFC 8446 1.3). */ ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, sigAlgo, PSS_RSAE_TO_PSS_PSS(macAlgo)); - #endif + } + else if (sigAlgo == rsa_pss_pss_algo) { + /* Only the id-RSASSA-PSS key scheme. */ + ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, rsa_pss_sa_algo, + PSS_RSAE_TO_PSS_PSS(macAlgo)); } else #endif @@ -6259,11 +6262,16 @@ static WC_INLINE void EncodeSigAlg(byte hashAlgo, byte hsType, byte* output) output[1] = rsa_sa_algo; break; #ifdef WC_RSA_PSS - /* PSS signatures: 0x080[4-6] */ + /* PSS signatures with an rsaEncryption key: 0x080[4-6] */ case rsa_pss_sa_algo: output[0] = rsa_pss_sa_algo; output[1] = hashAlgo; break; + /* PSS signatures with an id-RSASSA-PSS key: 0x080[9-b] */ + case rsa_pss_pss_algo: + output[0] = rsa_pss_sa_algo; + output[1] = PSS_RSAE_TO_PSS_PSS(hashAlgo); + break; #endif #endif default: @@ -6438,7 +6446,7 @@ int RsaSign(WOLFSSL* ssl, const byte* in, word32 inSz, byte* out, #endif #if defined(WC_RSA_PSS) - if (sigAlgo == rsa_pss_sa_algo) { + if (sigAlgo == rsa_pss_sa_algo || sigAlgo == rsa_pss_pss_algo) { enum wc_HashType hashType = WC_HASH_TYPE_NONE; int mgf = 0; @@ -6491,6 +6499,41 @@ int RsaSign(WOLFSSL* ssl, const byte* in, word32 inSz, byte* out, } #endif +#if !defined(NO_RSA) && defined(WC_RSA_PSS) +/* Check the peer's RSA signature scheme against its key: rsa_pss_rsae_* needs + * an rsaEncryption key and rsa_pss_pss_* an id-RSASSA-PSS key (RFC 8446 4.2.3), + * used only with the hash and salt length its parameters declare (RFC 4055 + * 1.2). Returns 0 or INVALID_PARAMETER. */ +int CheckPeerRsaSigAlgo(const WOLFSSL* ssl, byte sigAlgo, byte hashAlgo) +{ + if (ssl->peerRsaPss) { + if (sigAlgo != rsa_pss_pss_algo) { + WOLFSSL_MSG("Peer id-RSASSA-PSS key used without rsa_pss_pss_*"); + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + if (ssl->peerRsaPssHash != 0 && HashAlgoToType(hashAlgo) != + (enum wc_HashType)ssl->peerRsaPssHash) { + WOLFSSL_MSG("Peer RSA-PSS hash differs from key parameters"); + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + if (ssl->peerRsaPssSaltLen != 0 && (int)ssl->peerRsaPssSaltLen != + wc_HashGetDigestSize(HashAlgoToType(hashAlgo))) { + WOLFSSL_MSG("Peer RSA-PSS salt length differs from key parameters"); + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + } + else if (sigAlgo == rsa_pss_pss_algo) { + WOLFSSL_MSG("Peer sent rsa_pss_pss_* with an rsaEncryption key"); + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + return 0; +} +#endif /* !NO_RSA && WC_RSA_PSS */ + int RsaVerify(WOLFSSL* ssl, byte* in, word32 inSz, byte** out, int sigAlgo, int hashAlgo, RsaKey* key, buffer* keyBufInfo) { @@ -6613,7 +6656,7 @@ int VerifyRsaSign(WOLFSSL* ssl, byte* verifySig, word32 sigSz, #endif #if defined(WC_RSA_PSS) - if (sigAlgo == rsa_pss_sa_algo) { + if (sigAlgo == rsa_pss_sa_algo || sigAlgo == rsa_pss_pss_algo) { enum wc_HashType hashType = WC_HASH_TYPE_NONE; int mgf = 0; @@ -8645,6 +8688,8 @@ int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) ssl->options.minRsaKeySz = ctx->minRsaKeySz; #ifdef WC_RSA_PSS ssl->useRsaPss = ctx->useRsaPss; + ssl->rsaPssHash = ctx->rsaPssHash; + ssl->rsaPssSaltLen = ctx->rsaPssSaltLen; #endif #endif #ifdef HAVE_ECC @@ -18810,6 +18855,11 @@ static int ProcessPeerCertDecodeKey(WOLFSSL* ssl, ProcPeerCertArgs* args, } else { ssl->peerRsaKeyPresent = 1; + #ifdef WC_RSA_PSS + ssl->peerRsaPss = (args->dCert->keyOID == RSAPSSk); + ssl->peerRsaPssHash = args->dCert->keyPssHash; + ssl->peerRsaPssSaltLen = args->dCert->keyPssSaltLen; + #endif #if defined(WOLFSSL_RENESAS_TSIP_TLS) || \ defined(WOLFSSL_RENESAS_FSPSM_TLS) /* copy encrypted tsip key index into ssl object */ @@ -34771,6 +34821,8 @@ struct sig_algs { #ifdef WC_RSA_PSS { rsa_pss_sa_algo, "RSA-PSS" }, { rsa_pss_sa_algo, "PSS" }, + { rsa_pss_pss_algo, "RSA-PSS-PSS" }, + { rsa_pss_pss_algo, "PSS-PSS" }, #endif #endif #ifdef HAVE_ECC @@ -34858,7 +34910,7 @@ int SetSuitesHashSigAlgo(Suites* suites, const char* list) } { word32 needed = 2; -#if defined(WC_RSA_PSS) && defined(WOLFSSL_TLS13) +#if defined(WC_RSA_PSS) if (sig_alg == rsa_pss_sa_algo) needed = 4; #endif @@ -34997,14 +35049,31 @@ static int MatchSigAlgo(WOLFSSL* ssl, int hashAlgo, int sigAlgo) #ifdef WC_RSA_PSS /* RSA certificate and PSS sig alg. */ if (ssl->options.sigAlgo == rsa_sa_algo) { + if (ssl->useRsaPss) { + /* id-RSASSA-PSS key: rsa_pss_pss_* only (RFC 8446 4.2.3), with + * the hash and salt length its parameters declare (RFC 4055 1.2). */ + if (sigAlgo != rsa_pss_pss_algo) + return 0; + if (ssl->rsaPssHash != 0 && HashAlgoToType(hashAlgo) != + (enum wc_HashType)ssl->rsaPssHash) + return 0; + if (ssl->rsaPssSaltLen != 0 && (int)ssl->rsaPssSaltLen != + wc_HashGetDigestSize(HashAlgoToType(hashAlgo))) + return 0; + } + else { #if defined(WOLFSSL_TLS13) - /* TLS 1.3 only supports RSA-PSS. */ - if (IsAtLeastTLSv1_3(ssl->version)) - return sigAlgo == rsa_pss_sa_algo; + /* TLS 1.3 only supports RSA-PSS. */ + if (IsAtLeastTLSv1_3(ssl->version)) + return sigAlgo == rsa_pss_sa_algo; #endif - /* TLS 1.2 and below - RSA-PSS allowed, but only if the RSA key is big - * enough to actually produce an RSA-PSS signature with this hash. */ - if (sigAlgo == rsa_pss_sa_algo) { + /* rsa_pss_pss_* needs an id-RSASSA-PSS key. */ + if (sigAlgo == rsa_pss_pss_algo) + return 0; + } + /* RSA-PSS allowed, but only if the RSA key is big enough to actually + * produce an RSA-PSS signature with this hash. */ + if (sigAlgo == rsa_pss_sa_algo || sigAlgo == rsa_pss_pss_algo) { word32 minSz = MinRsaPssKeySz(hashAlgo); if (minSz != 0 && ssl->buffers.keySz != 0 && @@ -38683,6 +38752,15 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input, DecodeSigAlg(&input[args->idx], &ssl->options.peerHashAlgo, &sigAlgo); + #if !defined(NO_RSA) && defined(WC_RSA_PSS) + if (ssl->peerRsaKeyPresent) { + ret = CheckPeerRsaSigAlgo(ssl, sigAlgo, + ssl->options.peerHashAlgo); + if (ret != 0) { + ERROR_OUT(ret, exit_dske); + } + } + #endif #ifndef NO_RSA if (sigAlgo == rsa_pss_sa_algo && ssl->options.peerSigAlgo == rsa_sa_algo) { @@ -40495,7 +40573,11 @@ int SendCertificateVerify(WOLFSSL* ssl) if (ssl->hsType == DYNAMIC_TYPE_RSA) { #ifdef WC_RSA_PSS - if (IsAtLeastTLSv1_2(ssl) && + if (IsAtLeastTLSv1_2(ssl) && ssl->useRsaPss) { + /* id-RSASSA-PSS key: rsa_pss_pss_* (RFC 8446 4.2.3). */ + args->sigAlgo = rsa_pss_pss_algo; + } + else if (IsAtLeastTLSv1_2(ssl) && (ssl->pssAlgo & (1 << ssl->options.hashAlgo))) { args->sigAlgo = rsa_pss_sa_algo; } @@ -40549,7 +40631,8 @@ int SendCertificateVerify(WOLFSSL* ssl) c16toa((word16)args->length, args->verify + args->extraSz); } #ifdef WC_RSA_PSS - else if (args->sigAlgo == rsa_pss_sa_algo) { + else if (args->sigAlgo == rsa_pss_sa_algo || + args->sigAlgo == rsa_pss_pss_algo) { XMEMCPY(ssl->buffers.sig.buffer, ssl->buffers.digest.buffer, ssl->buffers.digest.length); ssl->buffers.sig.length = ssl->buffers.digest.length; @@ -41077,6 +41160,7 @@ const byte* MaskCurve25519PeerKey(const byte* pub, word32 pubSz, #ifndef NO_RSA #ifdef WC_RSA_PSS case rsa_pss_sa_algo: + case rsa_pss_pss_algo: #endif case rsa_sa_algo: sigSz = ssl->buffers.keySz; @@ -41819,6 +41903,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #ifndef NO_RSA #ifdef WC_RSA_PSS case rsa_pss_sa_algo: + case rsa_pss_pss_algo: #endif case rsa_sa_algo: { @@ -42483,6 +42568,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) } #ifdef WC_RSA_PSS case rsa_pss_sa_algo: + case rsa_pss_pss_algo: /* write sig size here */ c16toa((word16)args->sigSz, args->output + args->idx); @@ -42659,6 +42745,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) !defined(WOLFSSL_RSA_VERIFY_ONLY) #ifdef WC_RSA_PSS case rsa_pss_sa_algo: + case rsa_pss_pss_algo: #endif case rsa_sa_algo: { @@ -42775,6 +42862,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) !defined(WOLFSSL_RSA_VERIFY_ONLY) #ifdef WC_RSA_PSS case rsa_pss_sa_algo: + case rsa_pss_pss_algo: #endif case rsa_sa_algo: { @@ -42853,6 +42941,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #ifndef NO_RSA #ifdef WC_RSA_PSS case rsa_pss_sa_algo: + case rsa_pss_pss_algo: #endif case rsa_sa_algo: { @@ -44834,12 +44923,20 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) if (ssl->options.peerSigAlgo != rsa_sa_algo #ifdef WC_RSA_PSS && ssl->options.peerSigAlgo != rsa_pss_sa_algo + && ssl->options.peerSigAlgo != rsa_pss_pss_algo #endif ) { WOLFSSL_MSG("Oops, peer sent RSA key but not in " "verify"); ERROR_OUT(INVALID_PARAMETER, exit_dcv); } + #ifdef WC_RSA_PSS + ret = CheckPeerRsaSigAlgo(ssl, ssl->options.peerSigAlgo, + ssl->options.peerHashAlgo); + if (ret != 0) { + ERROR_OUT(ret, exit_dcv); + } + #endif } else #endif @@ -45086,7 +45183,8 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) if (ssl->peerRsaKey != NULL && ssl->peerRsaKeyPresent != 0) { if (IsAtLeastTLSv1_2(ssl)) { #ifdef WC_RSA_PSS - if (ssl->options.peerSigAlgo == rsa_pss_sa_algo) { + if (ssl->options.peerSigAlgo == rsa_pss_sa_algo || + ssl->options.peerSigAlgo == rsa_pss_pss_algo) { #ifdef HAVE_SELFTEST ret = wc_RsaPSS_CheckPadding( ssl->buffers.digest.buffer, diff --git a/src/ssl_load.c b/src/ssl_load.c index 7d1e7bbb965..7f581fb2d19 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -1860,9 +1860,13 @@ static int ProcessBufferCertPublicKey(WOLFSSL_CTX* ctx, WOLFSSL* ssl, #ifdef WC_RSA_PSS if (ssl) { ssl->useRsaPss = cert->keyOID == RSAPSSk; + ssl->rsaPssHash = cert->keyPssHash; + ssl->rsaPssSaltLen = cert->keyPssSaltLen; } if (ctx) { ctx->useRsaPss = cert->keyOID == RSAPSSk; + ctx->rsaPssHash = cert->keyPssHash; + ctx->rsaPssSaltLen = cert->keyPssSaltLen; } #endif break; diff --git a/src/tls13.c b/src/tls13.c index 68154208bcf..f6eeadc46ca 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -9603,26 +9603,19 @@ static WC_INLINE void EncodeSigAlg(const WOLFSSL * ssl, byte hashAlgo, break; #endif #ifndef NO_RSA - /* PSS signatures: 0x080[4-6] or 0x080[9-B] */ + /* PSS signatures with an rsaEncryption key: 0x080[4-6] */ case rsa_pss_sa_algo: output[0] = rsa_pss_sa_algo; + output[1] = hashAlgo; + break; #ifdef WC_RSA_PSS - /* If the private key uses the RSA-PSS OID, and the peer supports - * the rsa_pss_pss_* signature algorithm in use, then report - * rsa_pss_pss_* rather than rsa_pss_rsae_*. */ - if (ssl->useRsaPss && - ((ssl->pssAlgo & (1U << hashAlgo)) != 0U) && - (sha256_mac <= hashAlgo) && (hashAlgo <= sha512_mac)) - { - output[1] = PSS_RSAE_TO_PSS_PSS(hashAlgo); - } - else -#endif - { - output[1] = hashAlgo; - } + /* PSS signatures with an id-RSASSA-PSS key: 0x080[9-b] */ + case rsa_pss_pss_algo: + output[0] = rsa_pss_sa_algo; + output[1] = PSS_RSAE_TO_PSS_PSS(hashAlgo); break; #endif +#endif #ifdef HAVE_FALCON case falcon_level1_sa_algo: output[0] = FALCON_LEVEL1_SA_MAJOR; @@ -11644,8 +11637,15 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl) } /* Add signature algorithm. */ - if (ssl->hsType == DYNAMIC_TYPE_RSA) - args->sigAlgo = rsa_pss_sa_algo; + if (ssl->hsType == DYNAMIC_TYPE_RSA) { + #ifdef WC_RSA_PSS + /* id-RSASSA-PSS key: rsa_pss_pss_* (RFC 8446 4.2.3) */ + if (ssl->useRsaPss) + args->sigAlgo = rsa_pss_pss_algo; + else + #endif + args->sigAlgo = rsa_pss_sa_algo; + } #ifdef HAVE_ECC else if (ssl->hsType == DYNAMIC_TYPE_ECC) { #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) @@ -12692,6 +12692,12 @@ static int decodeRsaKey(WOLFSSL* ssl) return PEER_KEY_ERROR; ssl->peerRsaKeyPresent = 1; +#ifdef WC_RSA_PSS + /* Raw public key: OID not tracked, stay lenient. */ + ssl->peerRsaPss = 0; + ssl->peerRsaPssHash = 0; + ssl->peerRsaPssSaltLen = 0; +#endif keyRet = wc_RsaPublicKeyDecode(GetPeerCert(ssl)->sapkiDer, &tmpIdx, ssl->peerRsaKey, GetPeerCert(ssl)->sapkiLen); @@ -12928,6 +12934,22 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, if (ret < 0) goto exit_dcv; + #if !defined(NO_RSA) && defined(WC_RSA_PSS) + /* The PSS scheme must match the peer key's OID (RFC 8446 4.2.3). + * DecodeTls13SigAlg() folds both families together, so decode + * the bytes again to tell them apart. */ + if (ssl->peerRsaKeyPresent && input[args->idx] == NEW_SA_MAJOR) { + byte sa = invalid_sa_algo; + byte ha = 0; + + DecodeSigAlg(input + args->idx, &ha, &sa); + if (sa == rsa_pss_sa_algo || sa == rsa_pss_pss_algo) { + ret = CheckPeerRsaSigAlgo(ssl, sa, ha); + if (ret != 0) + goto exit_dcv; + } + } + #endif args->idx += OPAQUE16_LEN; /* Signature length. */ diff --git a/tests/api.c b/tests/api.c index be6253b80bd..030262b8075 100644 --- a/tests/api.c +++ b/tests/api.c @@ -39080,6 +39080,233 @@ static int test_wc_CryptoCb(void) return EXPECT_RESULT(); } +/* An id-RSASSA-PSS key signs with rsa_pss_pss_* only (RFC 8446 4.2.3), in + * TLS 1.2 too, with the hash its parameters declare (RFC 4055 1.2). The + * 2048-bit certs/rsapss keys declare SHA-256, the 3072-bit ones nothing. + * Mislabelled or PKCS#1 v1.5 signatures from such a key are refused. */ +#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(WC_RSA_PSS) && \ + defined(HAVE_ECC) && (!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) +#define TEST_TLS_RSA_PSS_PSS_KEY +#define RSAPSS_SVR_CERT "./certs/rsapss/server-rsapss.pem" +#define RSAPSS_SVR_KEY "./certs/rsapss/server-rsapss-priv.pem" +#define RSAPSS_CLI_CERT "./certs/rsapss/client-rsapss.pem" +#define RSAPSS_CLI_KEY "./certs/rsapss/client-rsapss-priv.pem" +#define RSAPSS_ROOT "./certs/rsapss/root-rsapss.pem" +#define RSAPSS3072_SVR_CERT "./certs/rsapss/server-3072-rsapss.pem" +#define RSAPSS3072_SVR_KEY "./certs/rsapss/server-3072-rsapss-priv.pem" + +/* Hash each side is expected to have signed with. */ +static int test_rsa_pss_pss_client_hash = sha256_mac; +static int test_rsa_pss_pss_server_hash = sha256_mac; + +typedef struct test_rsa_pss_pss_ver { + method_provider client; + method_provider server; + int tls13; +} test_rsa_pss_pss_ver; + +static const test_rsa_pss_pss_ver test_rsa_pss_pss_vers[] = { +#ifndef WOLFSSL_NO_TLS12 + { wolfTLSv1_2_client_method, wolfTLSv1_2_server_method, 0 }, +#endif +#ifdef WOLFSSL_TLS13 + { wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, 1 }, +#endif +}; + +/* Both sides hold id-RSASSA-PSS keys: both signed with rsa_pss_pss_* and + * with the expected hash. */ +static int test_rsa_pss_pss_on_result(WOLFSSL* ssl) +{ + EXPECT_DECLS; + ExpectIntEQ(ssl->useRsaPss, 1); + ExpectIntEQ(ssl->options.sigAlgo, rsa_pss_pss_algo); + ExpectIntEQ(ssl->options.hashAlgo, wolfSSL_is_server(ssl) ? + test_rsa_pss_pss_server_hash : test_rsa_pss_pss_client_hash); + ExpectIntEQ(ssl->peerRsaPss, 1); + if (wolfSSL_GetVersion(ssl) != WOLFSSL_TLSV1_3) { + /* TLS 1.2 keeps the two PSS scheme families apart when receiving. */ + ExpectIntEQ(ssl->options.peerSigAlgo, rsa_pss_pss_algo); + } + return EXPECT_RESULT(); +} + +/* Pick and label the scheme as if the key were rsaEncryption. */ +static int test_rsa_pss_pss_mislabel_ssl_ready(WOLFSSL* ssl) +{ + ssl->useRsaPss = 0; + return TEST_SUCCESS; +} + +#ifdef OPENSSL_EXTRA +/* Set on the session: the context setter allocates the suites before + * wolfSSL_new() derives them and leaves the cipher list empty. */ +static int test_rsa_pss_pss_only_ssl_ready(WOLFSSL* ssl) +{ + EXPECT_DECLS; + ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl, "RSA-PSS-PSS+SHA256"), + WOLFSSL_SUCCESS); + return EXPECT_RESULT(); +} + +static int test_rsa_pss_pss_sha384_ssl_ready(WOLFSSL* ssl) +{ + EXPECT_DECLS; + ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl, "RSA-PSS-PSS+SHA384"), + WOLFSSL_SUCCESS); + return EXPECT_RESULT(); +} + +/* rsa_pkcs1_sha256 for RSA keys; the ECDSA entry stops the server from + * replacing a list that leaves an advertised certificate type uncovered. */ +static int test_rsa_pss_pss_pkcs1_ssl_ready(WOLFSSL* ssl) +{ + EXPECT_DECLS; + ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl, "RSA+SHA256:ECDSA+SHA256"), + WOLFSSL_SUCCESS); + return EXPECT_RESULT(); +} + +/* Alert the client last received: after a refused ClientHello the harness's + * follow-up I/O overwrites the server's error, so check the client's view. */ +static int test_rsa_pss_pss_rx_alert = -1; + +static int test_rsa_pss_pss_client_on_cleanup(WOLFSSL* ssl) +{ + WOLFSSL_ALERT_HISTORY h; + + XMEMSET(&h, 0, sizeof(h)); + test_rsa_pss_pss_rx_alert = -1; + if (wolfSSL_get_alert_history(ssl, &h) == WOLFSSL_SUCCESS) + test_rsa_pss_pss_rx_alert = h.last_rx.code; + return TEST_SUCCESS; +} +#endif /* OPENSSL_EXTRA */ + +static void test_rsa_pss_pss_setup(test_ssl_cbf* c, test_ssl_cbf* s, + const test_rsa_pss_pss_ver* v) +{ + XMEMSET(c, 0, sizeof(*c)); + XMEMSET(s, 0, sizeof(*s)); + c->method = v->client; + s->method = v->server; + c->caPemFile = RSAPSS_ROOT; /* server-rsapss.pem chains to it */ + c->certPemFile = RSAPSS_CLI_CERT; + c->keyPemFile = RSAPSS_CLI_KEY; + s->caPemFile = RSAPSS_CLI_CERT; /* self-signed client certificate */ + s->certPemFile = RSAPSS_SVR_CERT; + s->keyPemFile = RSAPSS_SVR_KEY; +} +#endif /* TEST_TLS_RSA_PSS_PSS_KEY */ + +static int test_tls_rsa_pss_pss_key(void) +{ + EXPECT_DECLS; +#ifdef TEST_TLS_RSA_PSS_PSS_KEY + test_ssl_cbf client_cbf; + test_ssl_cbf server_cbf; + size_t i; + + for (i = 0; i < sizeof(test_rsa_pss_pss_vers) / + sizeof(test_rsa_pss_pss_vers[0]); i++) { + const test_rsa_pss_pss_ver* v = &test_rsa_pss_pss_vers[i]; + + /* Mutual authentication with id-RSASSA-PSS keys on both sides. The + * default lists offer SHA-512 first; the keys' parameters hold both + * sides to SHA-256. */ + test_rsa_pss_pss_client_hash = sha256_mac; + test_rsa_pss_pss_server_hash = sha256_mac; + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.on_result = test_rsa_pss_pss_on_result; + server_cbf.on_result = test_rsa_pss_pss_on_result; + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + + /* A client that labels its id-RSASSA-PSS signature rsa_pss_rsae_* is + * refused: that label requires an rsaEncryption key. */ + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.ssl_ready = test_rsa_pss_pss_mislabel_ssl_ready; + ExpectIntNE(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + ExpectIntEQ(server_cbf.last_err, WC_NO_ERR_TRACE(INVALID_PARAMETER)); + + #ifdef OPENSSL_EXTRA + /* Offered rsa_pss_pss_sha256 alone, the server selects it ... */ + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.ssl_ready = test_rsa_pss_pss_only_ssl_ready; + client_cbf.on_result = test_rsa_pss_pss_on_result; + server_cbf.on_result = test_rsa_pss_pss_on_result; + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + + /* ... a key whose parameters declare SHA-256 cannot take SHA-384: the + * server refuses the ClientHello with handshake_failure ... */ + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.ssl_ready = test_rsa_pss_pss_sha384_ssl_ready; + client_cbf.on_cleanup = test_rsa_pss_pss_client_on_cleanup; + ExpectIntNE(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + ExpectIntEQ(client_cbf.last_err, WC_NO_ERR_TRACE(FATAL_ERROR)); + ExpectIntEQ(test_rsa_pss_pss_rx_alert, handshake_failure); + + /* ... a key without parameters follows the offer ... */ + test_rsa_pss_pss_server_hash = sha384_mac; + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.ssl_ready = test_rsa_pss_pss_sha384_ssl_ready; + client_cbf.on_result = test_rsa_pss_pss_on_result; + server_cbf.on_result = test_rsa_pss_pss_on_result; + server_cbf.certPemFile = RSAPSS3072_SVR_CERT; + server_cbf.keyPemFile = RSAPSS3072_SVR_KEY; + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + test_rsa_pss_pss_server_hash = sha256_mac; + + /* ... and an rsaEncryption key cannot use rsa_pss_pss_*. */ + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.ssl_ready = test_rsa_pss_pss_only_ssl_ready; + client_cbf.on_cleanup = test_rsa_pss_pss_client_on_cleanup; + client_cbf.caPemFile = caCertFile; + server_cbf.certPemFile = svrCertFile; + server_cbf.keyPemFile = svrKeyFile; + ExpectIntNE(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + ExpectIntEQ(client_cbf.last_err, WC_NO_ERR_TRACE(FATAL_ERROR)); + ExpectIntEQ(test_rsa_pss_pss_rx_alert, handshake_failure); + + if (!v->tls13) { + /* The server (rsaEncryption key) asks for rsa_pkcs1_sha256 only: + * the id-RSASSA-PSS client key cannot sign that (RFC 4055 + * Section 1.2) ... */ + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.caPemFile = caCertFile; + server_cbf.certPemFile = svrCertFile; + server_cbf.keyPemFile = svrKeyFile; + server_cbf.ssl_ready = test_rsa_pss_pss_pkcs1_ssl_ready; + ExpectIntNE(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + ExpectIntEQ(client_cbf.last_err, + WC_NO_ERR_TRACE(INVALID_PARAMETER)); + + /* ... and a PKCS#1 v1.5 signature made with it is refused. */ + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.caPemFile = caCertFile; + client_cbf.ssl_ready = test_rsa_pss_pss_mislabel_ssl_ready; + server_cbf.certPemFile = svrCertFile; + server_cbf.keyPemFile = svrKeyFile; + server_cbf.ssl_ready = test_rsa_pss_pss_pkcs1_ssl_ready; + ExpectIntNE(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + ExpectIntEQ(server_cbf.last_err, + WC_NO_ERR_TRACE(INVALID_PARAMETER)); + } + #endif /* OPENSSL_EXTRA */ + } +#else + return TEST_SKIPPED; +#endif + return EXPECT_RESULT(); +} + /* Regression: a TLS 1.2 CBC MtE handshake whose record MAC is computed by a * callback that leaves the software hash state empty. Without routing * device-backed verify through update/final, TLS_hmac() reads that empty state @@ -47342,6 +47569,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wc_CryptoCb_nested_register), /* Unconditional shells (bodies self-guard on their feature macros). */ TEST_DECL(test_wc_CryptoCb_TLS_CBC_HMAC), + TEST_DECL(test_tls_rsa_pss_pss_key), TEST_DECL(test_wc_CryptoCb_Hmac_Find), TEST_DECL(test_wc_CryptoCb_AesGcm_SetKey), TEST_DECL(test_wc_CryptoCb_Aes_SetKey_Declined), diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 46090474893..9f5aad66de3 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -14537,6 +14537,9 @@ static int GetCertKey(DecodedCert* cert, const byte* source, word32* inOutIdx, WOLFSSL_MSG("RSA PSS: sig salt length too small"); return ASN_PARSE_E; } + /* Keep the key's parameters, binding per RFC 4055 1.2. */ + cert->keyPssHash = (byte)hash; + cert->keyPssSaltLen = (word16)saltLen; srcIdx += (word32)seqLen; } FALL_THROUGH; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 623df4f7569..30a4457c7cd 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4689,6 +4689,8 @@ struct WOLFSSL_CTX { short minRsaKeySz; /* minimum RSA key size */ #ifdef WC_RSA_PSS word8 useRsaPss; /* cert supports RSA-PSS */ + word8 rsaPssHash; /* hash declared by a PSS key, 0 if none */ + word16 rsaPssSaltLen; /* salt length declared by a PSS key */ #endif #endif #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) @@ -7159,6 +7161,11 @@ struct WOLFSSL { byte peerRsaKeyPresent; #ifdef WC_RSA_PSS word8 useRsaPss; /* cert supports RSA-PSS */ + word8 rsaPssHash; /* hash declared by own PSS key */ + word16 rsaPssSaltLen; /* salt length declared by own key */ + byte peerRsaPss; /* peer key has id-RSASSA-PSS OID */ + byte peerRsaPssHash; /* hash declared by peer PSS key */ + word16 peerRsaPssSaltLen; /* salt length declared by peer key */ #endif #endif #if defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE) @@ -8188,6 +8195,10 @@ WOLFSSL_LOCAL word32 LowResTimer(void); WOLFSSL_LOCAL int FindSuiteSSL(const WOLFSSL* ssl, byte* suite); WOLFSSL_LOCAL int FindSuite(const Suites* suites, byte first, byte second); +#if !defined(NO_RSA) && defined(WC_RSA_PSS) +WOLFSSL_LOCAL int CheckPeerRsaSigAlgo(const WOLFSSL* ssl, byte sigAlgo, + byte hashAlgo); +#endif WOLFSSL_LOCAL void DecodeSigAlg(const byte* input, byte* hashAlgo, byte* hsType); #ifdef WOLFSSL_HAVE_SLHDSA diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 29cd5d62759..4255f6f88dc 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1940,6 +1940,8 @@ struct DecodedCert { #ifdef WC_RSA_PSS word32 sigParamsIndex; /* start of signature parameters */ word32 sigParamsLength; /* length of signature parameters */ + byte keyPssHash; /* hash in PSS key params, 0 if none */ + word16 keyPssSaltLen; /* salt length in PSS key params */ #endif int version; /* cert version, 1 or 3 */ DNS_entry* altNames; /* alt names list of dns entries */ From 99a2b57c9396f738a96c4409cc360b5347c7ab28 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 5 Nov 2026 09:29:41 -0700 Subject: [PATCH 2/4] TLS: hold every RSA-PSS candidate to the key's parameters Check the hash, MGF and minimum salt length (RFC 4055 3.3) of each candidate scheme, not only the first match, through ConvertHashPss so TLS 1.3-only builds link. Read the scheme off the wire in rsapss.test and gate its SHA-384 and SHA-512 cases; trust the 3072-bit root in the unit test. --- scripts/rsapss.test | 121 ++++++++++++++++++++++++++++++---------- src/internal.c | 54 +++++++++++------- src/ssl_load.c | 2 + src/tls13.c | 1 + tests/api.c | 9 ++- wolfcrypt/src/asn.c | 3 +- wolfssl/internal.h | 3 + wolfssl/wolfcrypt/asn.h | 1 + 8 files changed, 144 insertions(+), 50 deletions(-) diff --git a/scripts/rsapss.test b/scripts/rsapss.test index 0965fc1a95e..ca1bbc81818 100755 --- a/scripts/rsapss.test +++ b/scripts/rsapss.test @@ -38,6 +38,10 @@ if [ "$HAVE_TLS12" = no ] && [ "$HAVE_TLS13" = no ]; then echo "skipping because neither TLS 1.2 nor TLS 1.3 is enabled in this build" exit 0 fi +HAVE_SHA384=no +HAVE_SHA512=no +grep -q -- '-DWOLFSSL_SHA384\b' config.log 2>/dev/null && HAVE_SHA384=yes +grep -q -- '-DWOLFSSL_SHA512\b' config.log 2>/dev/null && HAVE_SHA512=yes if ! grep -q -- -DWC_RSA_PSS config.log 2>/dev/null; then echo "skipping because WC_RSA_PSS not enabled in this build" exit 0 @@ -172,8 +176,61 @@ fail_case() { fails=$((fails+1)) } -# wolfSSL server, OpenSSL s_client: $1 name, $2 "Peer signature type" s_client -# must report or FAIL, $3 server arguments, $4 s_client arguments. +# SignatureScheme code point (RFC 8446 4.2.3) of a scheme name. +scheme_code() { + case "$1" in + rsa_pss_rsae_sha256) echo 0804 ;; + rsa_pss_rsae_sha384) echo 0805 ;; + rsa_pss_rsae_sha512) echo 0806 ;; + rsa_pss_pss_sha256) echo 0809 ;; + rsa_pss_pss_sha384) echo 080a ;; + rsa_pss_pss_sha512) echo 080b ;; + *) echo "unknown scheme $1" ;; + esac +} + +# SignatureScheme, as four hex digits, of the first $2 (CertificateVerify or +# ServerKeyExchange) received in the OpenSSL -msg dump $1, read off the wire +# rather than from the summary line, which names the scheme only from OpenSSL +# 3.2. Handshake messages are picked by their type byte: -msg does not name +# DTLS 1.2 messages, only their content type 22. The scheme follows the +# handshake header in a CertificateVerify and the ECDHE public key in a +# ServerKeyExchange (RFC 5246 7.4.8, RFC 8422 5.4); the DTLS handshake header +# is 12 bytes. +wire_sig_scheme() { + awk -v type="$([ "$2" = ServerKeyExchange ] && echo 0c || echo 0f)" ' + function hex(s, i, v) { + v = 0 + for (i = 1; i <= length(s); i++) + v = v * 16 + index("0123456789abcdef", substr(s, i, 1)) - 1 + return v + } + function found() { return grab && n > 0 && m[0] == type } + /^(<<<|>>>) / { + if (found()) exit + n = 0 + grab = ($1 == "<<<" && $0 ~ /Handshake|content_type=22/) + hdr = ($0 ~ /DTLS|version=6527[79]/) ? 12 : 4 + next + } + grab && /^ +[0-9a-f][0-9a-f]( [0-9a-f][0-9a-f])*$/ { + c = split($0, b) + for (i = 1; i <= c; i++) + m[n++] = b[i] + next + } + { if (found()) exit; grab = 0 } + END { + if (!found()) exit 1 + if (type == "0c") + hdr += 4 + hex(m[hdr + 3]) + print m[hdr] m[hdr + 1] + }' "$1" +} + +# wolfSSL server, OpenSSL s_client: $1 name, $2 scheme the server must sign +# with or FAIL, $3 server arguments, $4 s_client arguments. SIG_MSG names the +# message carrying the server's signature. wolfssl_server_case() { local name="$1" expect="$2" sargs="$3" cargs="$4" result cases=$((cases+1)) @@ -183,7 +240,7 @@ wolfssl_server_case() { server_pid=$! sleep 1 echo "test connection" | $TIMEOUT_KILL_2M $OPENSSL s_client \ - -connect 127.0.0.1:"$port" $cargs > rsapss.client.log 2>&1 + -connect 127.0.0.1:"$port" -msg $cargs > rsapss.client.log 2>&1 result=$? sleep 0.2 kill $server_pid 2>/dev/null @@ -194,16 +251,17 @@ wolfssl_server_case() { fi elif [ "$result" != 0 ]; then fail_case "$name: handshake failed" - elif ! grep -q "^Peer signature type: $expect\$" rsapss.client.log; then - fail_case "$name: expected peer signature type $expect" + elif [ "$(wire_sig_scheme rsapss.client.log "$SIG_MSG")" != \ + "$(scheme_code "$expect")" ]; then + fail_case "$name: server did not sign with $expect" fi rm -f rsapss.server.log rsapss.client.log } -# OpenSSL s_server with client auth, wolfSSL client: $1 name, $2 OK (expects an -# rsa_pss_pss_* CertificateVerify) or FAIL, $3 s_server arguments, $4 client -# arguments, $5 client certificate and key arguments, $6 that self-signed -# certificate as the s_server trust anchor. +# OpenSSL s_server with client auth, wolfSSL client: $1 name, $2 scheme the +# client must sign its CertificateVerify with or FAIL, $3 s_server arguments, +# $4 client arguments, $5 client certificate and key arguments, $6 that +# self-signed certificate as the s_server trust anchor. openssl_server_case() { local name="$1" expect="$2" sargs="$3" cargs="$4" ccert="$5" cca="$6" local result @@ -226,9 +284,9 @@ openssl_server_case() { fi elif [ "$result" != 0 ]; then fail_case "$name: handshake failed" - elif ! grep -A1 'CertificateVerify' rsapss.server.log | \ - grep -Eq '^ +0f 00 [0-9a-f]{2} [0-9a-f]{2} 08 (09|0a|0b) '; then - fail_case "$name: CertificateVerify is not rsa_pss_pss_*" + elif [ "$(wire_sig_scheme rsapss.server.log CertificateVerify)" != \ + "$(scheme_code "$expect")" ]; then + fail_case "$name: client did not sign with $expect" fi rm -f rsapss.server.log rsapss.client.log } @@ -239,13 +297,13 @@ openssl_server_case() { for ver in tls12 tls13 dtls12; do case $ver in tls12) [ "$HAVE_TLS12" = yes ] || continue - sv="-v 3"; cv="-tls1_2" ;; + sv="-v 3"; cv="-tls1_2"; SIG_MSG=ServerKeyExchange ;; tls13) [ "$HAVE_TLS13" = yes ] || continue - sv="-v 4"; cv="-tls1_3" ;; + sv="-v 4"; cv="-tls1_3"; SIG_MSG=CertificateVerify ;; dtls12) [ "$HAVE_DTLS12" = yes ] || continue # Explicit MTU: s_client cannot query the loopback path MTU on # every platform and would otherwise fragment the ClientHello. - sv="-v 3 -u"; cv="-dtls1_2 -mtu 1500" ;; + sv="-v 3 -u"; cv="-dtls1_2 -mtu 1500"; SIG_MSG=ServerKeyExchange ;; esac wolfssl_server_case "$ver PSS server, default list" rsa_pss_pss_sha256 \ "$sv -d $PSS_SVR" "$cv" @@ -253,12 +311,17 @@ for ver in tls12 tls13 dtls12; do "$sv -d $PSS_SVR" "$cv -sigalgs rsa_pss_pss_sha256" wolfssl_server_case "$ver PSS server, key parameters fix SHA-256" FAIL \ "$sv -d $PSS_SVR" "$cv -sigalgs rsa_pss_pss_sha384" - wolfssl_server_case "$ver PSS 3072 server, rsa_pss_pss_sha384 only" rsa_pss_pss_sha384 \ - "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha384" - wolfssl_server_case "$ver PSS 3072 server, rsa_pss_pss_sha512 only" rsa_pss_pss_sha512 \ - "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha512" - wolfssl_server_case "$ver PSS 3072 server, hash from the pss_pss offer" rsa_pss_pss_sha384 \ - "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha384:rsa_pss_rsae_sha256" + # The 3072-bit chain is SHA-384 signed. + if [ "$HAVE_SHA384" = yes ]; then + wolfssl_server_case "$ver PSS 3072 server, rsa_pss_pss_sha384 only" rsa_pss_pss_sha384 \ + "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha384" + wolfssl_server_case "$ver PSS 3072 server, hash from the pss_pss offer" rsa_pss_pss_sha384 \ + "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha384:rsa_pss_rsae_sha256" + if [ "$HAVE_SHA512" = yes ]; then + wolfssl_server_case "$ver PSS 3072 server, rsa_pss_pss_sha512 only" rsa_pss_pss_sha512 \ + "$sv -d $PSS3072_SVR" "$cv -sigalgs rsa_pss_pss_sha512" + fi + fi wolfssl_server_case "$ver PSS server, never rsa_pkcs1_*" rsa_pss_pss_sha256 \ "$sv -d $PSS_SVR" "$cv -sigalgs rsa_pss_pss_sha256:rsa_pkcs1_sha256" wolfssl_server_case "$ver PSS server, hash from the pss_pss offer" rsa_pss_pss_sha256 \ @@ -282,11 +345,11 @@ for ver in tls12 tls13; do esac cli="$PSS_CLI_WOLFSSL" cca="$CERT_DIR/rsapss/client-rsapss.pem" - openssl_server_case "$ver PSS client, default list" OK \ + openssl_server_case "$ver PSS client, default list" rsa_pss_pss_sha256 \ "$ov" "$wv" "$cli" "$cca" - openssl_server_case "$ver PSS client, rsa_pss_pss_sha256 only" OK \ + openssl_server_case "$ver PSS client, rsa_pss_pss_sha256 only" rsa_pss_pss_sha256 \ "$ov -client_sigalgs rsa_pss_pss_sha256" "$wv" "$cli" "$cca" - openssl_server_case "$ver PSS client, never rsa_pkcs1_*" OK \ + openssl_server_case "$ver PSS client, never rsa_pkcs1_*" rsa_pss_pss_sha256 \ "$ov -client_sigalgs rsa_pss_pss_sha256:rsa_pkcs1_sha256" "$wv" "$cli" "$cca" openssl_server_case "$ver PSS client, rsa_pss_rsae_* only offered" FAIL \ "$ov -client_sigalgs rsa_pss_rsae_sha256" "$wv" "$cli" "$cca" @@ -296,10 +359,12 @@ for ver in tls12 tls13; do openssl_server_case "$ver PSS client, rsa_pkcs1_* only offered" FAIL \ "$ov -client_sigalgs rsa_pkcs1_sha256" "$wv" "$cli" "$cca" fi - cli="$PSS3072_CLI_WOLFSSL" - cca="$CERT_DIR/rsapss/client-3072-rsapss.pem" - openssl_server_case "$ver PSS 3072 client, rsa_pss_pss_sha384 only" OK \ - "$ov -client_sigalgs rsa_pss_pss_sha384" "$wv" "$cli" "$cca" + if [ "$HAVE_SHA384" = yes ]; then + cli="$PSS3072_CLI_WOLFSSL" + cca="$CERT_DIR/rsapss/client-3072-rsapss.pem" + openssl_server_case "$ver PSS 3072 client, rsa_pss_pss_sha384 only" rsa_pss_pss_sha384 \ + "$ov -client_sigalgs rsa_pss_pss_sha384" "$wv" "$cli" "$cca" + fi done if [ "$fails" != 0 ]; then diff --git a/src/internal.c b/src/internal.c index d28b753335f..5e1158801a3 100644 --- a/src/internal.c +++ b/src/internal.c @@ -6500,10 +6500,28 @@ int RsaSign(WOLFSSL* ssl, const byte* in, word32 inSz, byte* out, #endif #if !defined(NO_RSA) && defined(WC_RSA_PSS) +/* Whether rsa_pss_*_ fits an id-RSASSA-PSS key's parameters: the + * declared hash and MGF, and a salt no shorter than declared (RFC 4055 3.3); + * the scheme's salt is the digest length (RFC 8446 4.2.3). */ +static int MatchRsaPssParams(byte keyHash, byte keyMgf, word16 keySaltLen, + byte hashAlgo) +{ + enum wc_HashType hashType = WC_HASH_TYPE_NONE; + int mgf = WC_MGF1NONE; + + if (ConvertHashPss(hashAlgo, &hashType, &mgf) != 0) + return 0; + if (keyHash != 0 && hashType != (enum wc_HashType)keyHash) + return 0; + if (keyMgf != 0 && mgf != (int)keyMgf) + return 0; + return (int)keySaltLen <= wc_HashGetDigestSize(hashType); +} + /* Check the peer's RSA signature scheme against its key: rsa_pss_rsae_* needs * an rsaEncryption key and rsa_pss_pss_* an id-RSASSA-PSS key (RFC 8446 4.2.3), - * used only with the hash and salt length its parameters declare (RFC 4055 - * 1.2). Returns 0 or INVALID_PARAMETER. */ + * used only as its parameters allow (RFC 4055 1.2). + * Returns 0 or INVALID_PARAMETER. */ int CheckPeerRsaSigAlgo(const WOLFSSL* ssl, byte sigAlgo, byte hashAlgo) { if (ssl->peerRsaPss) { @@ -6512,15 +6530,9 @@ int CheckPeerRsaSigAlgo(const WOLFSSL* ssl, byte sigAlgo, byte hashAlgo) WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } - if (ssl->peerRsaPssHash != 0 && HashAlgoToType(hashAlgo) != - (enum wc_HashType)ssl->peerRsaPssHash) { - WOLFSSL_MSG("Peer RSA-PSS hash differs from key parameters"); - WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); - return INVALID_PARAMETER; - } - if (ssl->peerRsaPssSaltLen != 0 && (int)ssl->peerRsaPssSaltLen != - wc_HashGetDigestSize(HashAlgoToType(hashAlgo))) { - WOLFSSL_MSG("Peer RSA-PSS salt length differs from key parameters"); + if (!MatchRsaPssParams(ssl->peerRsaPssHash, ssl->peerRsaPssMgf, + ssl->peerRsaPssSaltLen, hashAlgo)) { + WOLFSSL_MSG("Peer RSA-PSS scheme not allowed by key parameters"); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } @@ -8689,6 +8701,7 @@ int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) #ifdef WC_RSA_PSS ssl->useRsaPss = ctx->useRsaPss; ssl->rsaPssHash = ctx->rsaPssHash; + ssl->rsaPssMgf = ctx->rsaPssMgf; ssl->rsaPssSaltLen = ctx->rsaPssSaltLen; #endif #endif @@ -18858,6 +18871,7 @@ static int ProcessPeerCertDecodeKey(WOLFSSL* ssl, ProcPeerCertArgs* args, #ifdef WC_RSA_PSS ssl->peerRsaPss = (args->dCert->keyOID == RSAPSSk); ssl->peerRsaPssHash = args->dCert->keyPssHash; + ssl->peerRsaPssMgf = args->dCert->keyPssMgf; ssl->peerRsaPssSaltLen = args->dCert->keyPssSaltLen; #endif #if defined(WOLFSSL_RENESAS_TSIP_TLS) || \ @@ -35047,18 +35061,18 @@ static int MatchSigAlgo(WOLFSSL* ssl, int hashAlgo, int sigAlgo) } #endif /* WOLFSSL_HAVE_MLDSA */ #ifdef WC_RSA_PSS - /* RSA certificate and PSS sig alg. */ - if (ssl->options.sigAlgo == rsa_sa_algo) { + /* RSA certificate and PSS sig alg. Also entered once a candidate was + * chosen, so each later one is held to the key as well. */ + if (ssl->options.sigAlgo == rsa_sa_algo || + ssl->options.sigAlgo == rsa_pss_sa_algo || + ssl->options.sigAlgo == rsa_pss_pss_algo) { if (ssl->useRsaPss) { - /* id-RSASSA-PSS key: rsa_pss_pss_* only (RFC 8446 4.2.3), with - * the hash and salt length its parameters declare (RFC 4055 1.2). */ + /* id-RSASSA-PSS key: rsa_pss_pss_* only (RFC 8446 4.2.3), as its + * parameters allow (RFC 4055 1.2). */ if (sigAlgo != rsa_pss_pss_algo) return 0; - if (ssl->rsaPssHash != 0 && HashAlgoToType(hashAlgo) != - (enum wc_HashType)ssl->rsaPssHash) - return 0; - if (ssl->rsaPssSaltLen != 0 && (int)ssl->rsaPssSaltLen != - wc_HashGetDigestSize(HashAlgoToType(hashAlgo))) + if (!MatchRsaPssParams(ssl->rsaPssHash, ssl->rsaPssMgf, + ssl->rsaPssSaltLen, hashAlgo)) return 0; } else { diff --git a/src/ssl_load.c b/src/ssl_load.c index 7f581fb2d19..7803d390f44 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -1861,11 +1861,13 @@ static int ProcessBufferCertPublicKey(WOLFSSL_CTX* ctx, WOLFSSL* ssl, if (ssl) { ssl->useRsaPss = cert->keyOID == RSAPSSk; ssl->rsaPssHash = cert->keyPssHash; + ssl->rsaPssMgf = cert->keyPssMgf; ssl->rsaPssSaltLen = cert->keyPssSaltLen; } if (ctx) { ctx->useRsaPss = cert->keyOID == RSAPSSk; ctx->rsaPssHash = cert->keyPssHash; + ctx->rsaPssMgf = cert->keyPssMgf; ctx->rsaPssSaltLen = cert->keyPssSaltLen; } #endif diff --git a/src/tls13.c b/src/tls13.c index f6eeadc46ca..a070831ba92 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -12696,6 +12696,7 @@ static int decodeRsaKey(WOLFSSL* ssl) /* Raw public key: OID not tracked, stay lenient. */ ssl->peerRsaPss = 0; ssl->peerRsaPssHash = 0; + ssl->peerRsaPssMgf = 0; ssl->peerRsaPssSaltLen = 0; #endif keyRet = wc_RsaPublicKeyDecode(GetPeerCert(ssl)->sapkiDer, &tmpIdx, diff --git a/tests/api.c b/tests/api.c index 030262b8075..d09d7e8d05e 100644 --- a/tests/api.c +++ b/tests/api.c @@ -39094,6 +39094,7 @@ static int test_wc_CryptoCb(void) #define RSAPSS_ROOT "./certs/rsapss/root-rsapss.pem" #define RSAPSS3072_SVR_CERT "./certs/rsapss/server-3072-rsapss.pem" #define RSAPSS3072_SVR_KEY "./certs/rsapss/server-3072-rsapss-priv.pem" +#define RSAPSS3072_ROOT "./certs/rsapss/root-3072-rsapss.pem" /* Hash each side is expected to have signed with. */ static int test_rsa_pss_pss_client_hash = sha256_mac; @@ -39149,6 +39150,7 @@ static int test_rsa_pss_pss_only_ssl_ready(WOLFSSL* ssl) return EXPECT_RESULT(); } +#ifdef WOLFSSL_SHA384 static int test_rsa_pss_pss_sha384_ssl_ready(WOLFSSL* ssl) { EXPECT_DECLS; @@ -39156,6 +39158,7 @@ static int test_rsa_pss_pss_sha384_ssl_ready(WOLFSSL* ssl) WOLFSSL_SUCCESS); return EXPECT_RESULT(); } +#endif /* rsa_pkcs1_sha256 for RSA keys; the ECDSA entry stops the server from * replacing a list that leaves an advertised certificate type uncovered. */ @@ -39239,6 +39242,7 @@ static int test_tls_rsa_pss_pss_key(void) ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbf, &server_cbf, NULL), TEST_SUCCESS); + #ifdef WOLFSSL_SHA384 /* ... a key whose parameters declare SHA-256 cannot take SHA-384: the * server refuses the ClientHello with handshake_failure ... */ test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); @@ -39249,17 +39253,20 @@ static int test_tls_rsa_pss_pss_key(void) ExpectIntEQ(client_cbf.last_err, WC_NO_ERR_TRACE(FATAL_ERROR)); ExpectIntEQ(test_rsa_pss_pss_rx_alert, handshake_failure); - /* ... a key without parameters follows the offer ... */ + /* ... a key without parameters follows the offer (the 3072-bit chain + * is SHA-384 signed) ... */ test_rsa_pss_pss_server_hash = sha384_mac; test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); client_cbf.ssl_ready = test_rsa_pss_pss_sha384_ssl_ready; client_cbf.on_result = test_rsa_pss_pss_on_result; + client_cbf.caPemFile = RSAPSS3072_ROOT; server_cbf.on_result = test_rsa_pss_pss_on_result; server_cbf.certPemFile = RSAPSS3072_SVR_CERT; server_cbf.keyPemFile = RSAPSS3072_SVR_KEY; ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbf, &server_cbf, NULL), TEST_SUCCESS); test_rsa_pss_pss_server_hash = sha256_mac; + #endif /* WOLFSSL_SHA384 */ /* ... and an rsaEncryption key cannot use rsa_pss_pss_*. */ test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 9f5aad66de3..9f843f3580a 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -14538,7 +14538,8 @@ static int GetCertKey(DecodedCert* cert, const byte* source, word32* inOutIdx, return ASN_PARSE_E; } /* Keep the key's parameters, binding per RFC 4055 1.2. */ - cert->keyPssHash = (byte)hash; + cert->keyPssHash = (hash != WC_HASH_TYPE_NONE) ? (byte)hash : 0; + cert->keyPssMgf = (mgf != -1) ? (byte)mgf : 0; cert->keyPssSaltLen = (word16)saltLen; srcIdx += (word32)seqLen; } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 30a4457c7cd..c42aad6890e 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4690,6 +4690,7 @@ struct WOLFSSL_CTX { #ifdef WC_RSA_PSS word8 useRsaPss; /* cert supports RSA-PSS */ word8 rsaPssHash; /* hash declared by a PSS key, 0 if none */ + word8 rsaPssMgf; /* MGF declared by a PSS key, 0 if none */ word16 rsaPssSaltLen; /* salt length declared by a PSS key */ #endif #endif @@ -7162,9 +7163,11 @@ struct WOLFSSL { #ifdef WC_RSA_PSS word8 useRsaPss; /* cert supports RSA-PSS */ word8 rsaPssHash; /* hash declared by own PSS key */ + word8 rsaPssMgf; /* MGF declared by own PSS key */ word16 rsaPssSaltLen; /* salt length declared by own key */ byte peerRsaPss; /* peer key has id-RSASSA-PSS OID */ byte peerRsaPssHash; /* hash declared by peer PSS key */ + byte peerRsaPssMgf; /* MGF declared by peer PSS key */ word16 peerRsaPssSaltLen; /* salt length declared by peer key */ #endif #endif diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 4255f6f88dc..6e742bedfc8 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1941,6 +1941,7 @@ struct DecodedCert { word32 sigParamsIndex; /* start of signature parameters */ word32 sigParamsLength; /* length of signature parameters */ byte keyPssHash; /* hash in PSS key params, 0 if none */ + byte keyPssMgf; /* MGF in PSS key params, 0 if none */ word16 keyPssSaltLen; /* salt length in PSS key params */ #endif int version; /* cert version, 1 or 3 */ From 5286e642ad45df673b6173384a6e29573a51c0b8 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 5 Nov 2026 09:56:21 -0700 Subject: [PATCH 3/4] TLS: carry id-RSASSA-PSS key parameters through every install path Copy them on a context swap and when a certificate is installed as an X509 object, map the key to the RSA hybrid code points, list RSA-PSS schemes for SHA-256 to SHA-512 only, and refuse a TLS 1.2 ClientHello without signature_algorithms when the key must not sign rsa_pkcs1 (RFC 4055 1.2). --- src/internal.c | 48 ++++++++++++++++++++++++++-------- src/ssl.c | 6 +++++ src/ssl_load.c | 6 +++++ src/tls13.c | 8 ++++++ tests/api.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++ wolfssl/internal.h | 5 ++++ 6 files changed, 127 insertions(+), 11 deletions(-) diff --git a/src/internal.c b/src/internal.c index 5e1158801a3..a701b12384f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -4127,17 +4127,18 @@ static WC_INLINE void AddSuiteHashSigAlgo(byte* hashSigAlgo, byte macAlgo, #endif #endif /* WOLFSSL_HAVE_SLHDSA */ #ifdef WC_RSA_PSS - if (sigAlgo == rsa_pss_sa_algo) { - /* RSA PSS is sig then mac */ - ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, sigAlgo, macAlgo); - /* rsa_pss_pss_* too, also in TLS 1.2 (RFC 8446 1.3). */ - ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, sigAlgo, - PSS_RSAE_TO_PSS_PSS(macAlgo)); - } - else if (sigAlgo == rsa_pss_pss_algo) { - /* Only the id-RSASSA-PSS key scheme. */ - ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, rsa_pss_sa_algo, - PSS_RSAE_TO_PSS_PSS(macAlgo)); + /* RSA-PSS schemes exist for SHA-256, SHA-384 and SHA-512 only. */ + if (sigAlgo == rsa_pss_sa_algo || sigAlgo == rsa_pss_pss_algo) { + if (macAlgo >= sha256_mac && macAlgo <= sha512_mac) { + if (sigAlgo == rsa_pss_sa_algo) { + /* RSA PSS is sig then mac */ + ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, sigAlgo, + macAlgo); + } + /* rsa_pss_pss_* too, also in TLS 1.2 (RFC 8446 1.3). */ + ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, rsa_pss_sa_algo, + PSS_RSAE_TO_PSS_PSS(macAlgo)); + } } else #endif @@ -16235,6 +16236,11 @@ static int CopyDecodedPubKey(WOLFSSL_X509* x509, DecodedCert* dCert, int ret) dCert->pubKeySize, x509->heap, DYNAMIC_TYPE_PUBLIC_KEY); if (x509->pubKey.buffer != NULL) { x509->pubKeyOID = (int)dCert->keyOID; + #ifdef WC_RSA_PSS + x509->keyPssHash = dCert->keyPssHash; + x509->keyPssMgf = dCert->keyPssMgf; + x509->keyPssSaltLen = dCert->keyPssSaltLen; + #endif #ifdef OPENSSL_EXTRA_X509_SMALL /* Set now as the getters that cache objects made from the key * may run in parallel. */ @@ -34921,6 +34927,15 @@ int SetSuitesHashSigAlgo(Suites* suites, const char* list) ret = 0; break; } + #ifdef WC_RSA_PSS + /* No RSA-PSS scheme with any other hash. */ + if ((sig_alg == rsa_pss_sa_algo || + sig_alg == rsa_pss_pss_algo) && + (mac_alg < sha256_mac || mac_alg > sha512_mac)) { + ret = 0; + break; + } + #endif } { word32 needed = 2; @@ -35228,6 +35243,17 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz, } ssl->options.hashAlgo = minHash = MinHashAlgo(ssl); +#ifdef WC_RSA_PSS + /* Without a list a TLS 1.2 server signs rsa_pkcs1 (RFC 5246 7.4.1.4.1), + * which an id-RSASSA-PSS key must not do (RFC 4055 1.2). */ + if (hashSigAlgoSz == 0 && ssl->useRsaPss && + ssl->options.sigAlgo == rsa_sa_algo && + ssl->options.side == WOLFSSL_SERVER_END && + !IsAtLeastTLSv1_3(ssl->version)) { + WOLFSSL_ERROR_VERBOSE(MATCH_SUITE_ERROR); + return MATCH_SUITE_ERROR; + } +#endif /* No list means go with the defaults. */ if (hashSigAlgoSz == 0) return 0; diff --git a/src/ssl.c b/src/ssl.c index a1aed624e11..5f4d6ea7cfd 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -9134,6 +9134,12 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ssl->buffers.keyLabel = ctx->privateKeyLabel; ssl->buffers.keySz = ctx->privateKeySz; ssl->buffers.keyDevId = ctx->privateKeyDevId; +#if !defined(NO_RSA) && defined(WC_RSA_PSS) + ssl->useRsaPss = ctx->useRsaPss; + ssl->rsaPssHash = ctx->rsaPssHash; + ssl->rsaPssMgf = ctx->rsaPssMgf; + ssl->rsaPssSaltLen = ctx->rsaPssSaltLen; +#endif /* flags indicating what certs/keys are available */ ssl->options.haveRSA = ctx->haveRSA; ssl->options.haveDH = ctx->haveDH; diff --git a/src/ssl_load.c b/src/ssl_load.c index 7803d390f44..00e2546f01d 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -5454,6 +5454,12 @@ int wolfSSL_CTX_use_certificate(WOLFSSL_CTX *ctx, WOLFSSL_X509 *x) if (res == 1) { /* Set have options based on public key OID. */ wolfssl_set_have_from_key_oid(ctx, NULL, x->pubKeyOID); + #if !defined(NO_RSA) && defined(WC_RSA_PSS) + ctx->useRsaPss = (x->pubKeyOID == RSAPSSk); + ctx->rsaPssHash = x->keyPssHash; + ctx->rsaPssMgf = x->keyPssMgf; + ctx->rsaPssSaltLen = x->keyPssSaltLen; + #endif } return res; diff --git a/src/tls13.c b/src/tls13.c index a070831ba92..f2ea80e7717 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -9751,6 +9751,14 @@ static void EncodeDualSigAlg(byte sigAlg, byte altSigAlg, byte* output) output[0] = 0x0; output[1] = 0x0; +#ifdef WC_RSA_PSS + /* The hybrid code points do not tell the RSA key OIDs apart. */ + if (sigAlg == rsa_pss_pss_algo) + sigAlg = rsa_pss_sa_algo; + if (altSigAlg == rsa_pss_pss_algo) + altSigAlg = rsa_pss_sa_algo; +#endif + if (sigAlg == ecc_dsa_sa_algo && altSigAlg == mldsa_44_sa_algo) { output[1] = HYBRID_P256_MLDSA_44_SA_MINOR; } diff --git a/tests/api.c b/tests/api.c index d09d7e8d05e..8d860980d8d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -39314,6 +39314,70 @@ static int test_tls_rsa_pss_pss_key(void) return EXPECT_RESULT(); } +/* The id-RSASSA-PSS key's parameters follow the certificate through the + * other install paths too, and no RSA-PSS scheme is listed with a hash it + * does not exist for. */ +static int test_tls_rsa_pss_pss_key_install(void) +{ + EXPECT_DECLS; +#if defined(TEST_TLS_RSA_PSS_PSS_KEY) && defined(OPENSSL_EXTRA) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) + WOLFSSL_CTX* rsaCtx = NULL; + WOLFSSL_CTX* pssCtx = NULL; + WOLFSSL_CTX* x509Ctx = NULL; + WOLFSSL_X509* x509 = NULL; + WOLFSSL* ssl = NULL; + + ExpectNotNull(rsaCtx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + ExpectTrue(wolfSSL_CTX_use_certificate_file(rsaCtx, svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(rsaCtx, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(pssCtx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + ExpectTrue(wolfSSL_CTX_use_certificate_file(pssCtx, RSAPSS_SVR_CERT, + WOLFSSL_FILETYPE_PEM)); + ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(pssCtx, RSAPSS_SVR_KEY, + WOLFSSL_FILETYPE_PEM)); + + /* Swapping the context, as an SNI callback does, carries them over. */ + ExpectNotNull(ssl = wolfSSL_new(rsaCtx)); + ExpectIntEQ(ssl->useRsaPss, 0); + ExpectNotNull(wolfSSL_set_SSL_CTX(ssl, pssCtx)); + ExpectIntEQ(ssl->useRsaPss, 1); + ExpectIntEQ(ssl->rsaPssHash, WC_HASH_TYPE_SHA256); + ExpectIntEQ(ssl->rsaPssMgf, WC_MGF1SHA256); + ExpectIntEQ(ssl->rsaPssSaltLen, WC_SHA256_DIGEST_SIZE); + ExpectNotNull(wolfSSL_set_SSL_CTX(ssl, rsaCtx)); + ExpectIntEQ(ssl->useRsaPss, 0); + ExpectIntEQ(ssl->rsaPssHash, 0); + + /* So does installing the certificate as an X509 object. */ + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(RSAPSS_SVR_CERT, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(x509Ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate(x509Ctx, x509), 1); + ExpectIntEQ(x509Ctx->useRsaPss, 1); + ExpectIntEQ(x509Ctx->rsaPssHash, WC_HASH_TYPE_SHA256); + ExpectIntEQ(x509Ctx->rsaPssMgf, WC_MGF1SHA256); + ExpectIntEQ(x509Ctx->rsaPssSaltLen, WC_SHA256_DIGEST_SIZE); + + /* RSA-PSS schemes exist for SHA-256, SHA-384 and SHA-512 only. */ + ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl, "RSA-PSS-PSS+SHA1"), 0); + ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl, "RSA-PSS+SHA224"), 0); + ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl, "RSA-PSS-PSS+SHA256"), + WOLFSSL_SUCCESS); + + wolfSSL_X509_free(x509); + wolfSSL_free(ssl); + wolfSSL_CTX_free(x509Ctx); + wolfSSL_CTX_free(pssCtx); + wolfSSL_CTX_free(rsaCtx); +#else + return TEST_SKIPPED; +#endif + return EXPECT_RESULT(); +} + /* Regression: a TLS 1.2 CBC MtE handshake whose record MAC is computed by a * callback that leaves the software hash state empty. Without routing * device-backed verify through update/final, TLS_hmac() reads that empty state @@ -47577,6 +47641,7 @@ TEST_CASE testCases[] = { /* Unconditional shells (bodies self-guard on their feature macros). */ TEST_DECL(test_wc_CryptoCb_TLS_CBC_HMAC), TEST_DECL(test_tls_rsa_pss_pss_key), + TEST_DECL(test_tls_rsa_pss_pss_key_install), TEST_DECL(test_wc_CryptoCb_Hmac_Find), TEST_DECL(test_wc_CryptoCb_AesGcm_SetKey), TEST_DECL(test_wc_CryptoCb_Aes_SetKey_Declined), diff --git a/wolfssl/internal.h b/wolfssl/internal.h index c42aad6890e..46658b19d13 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6379,6 +6379,11 @@ struct WOLFSSL_X509 { #endif buffer pubKey; int pubKeyOID; +#ifdef WC_RSA_PSS + byte keyPssHash; /* PSS key parameters, */ + byte keyPssMgf; /* 0 if not declared */ + word16 keyPssSaltLen; +#endif DNS_entry* altNamesNext; /* hint for retrieval */ #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \ defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ From c652536d9a4cecac585d89a70973a17cb136b722 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 5 Nov 2026 10:39:36 -0700 Subject: [PATCH 4/4] TLS: no RSA key transport with an id-RSASSA-PSS key Such a key only signs (RFC 4055 1.2), so the server passes over the static RSA suites. Resolve the test certificates from the script's own directory for out-of-tree make check, fail cases that never connected, and keep the RSA-PSS checks out of NO_RSA builds. --- scripts/rsapss.test | 37 ++++++++++++++++++++++++++++++------- src/internal.c | 22 ++++++++++++++++++---- src/tls13.c | 2 +- tests/api.c | 27 +++++++++++++++++++++++++-- 4 files changed, 74 insertions(+), 14 deletions(-) diff --git a/scripts/rsapss.test b/scripts/rsapss.test index ca1bbc81818..175ce9b59a9 100755 --- a/scripts/rsapss.test +++ b/scripts/rsapss.test @@ -38,6 +38,15 @@ if [ "$HAVE_TLS12" = no ] && [ "$HAVE_TLS13" = no ]; then echo "skipping because neither TLS 1.2 nor TLS 1.3 is enabled in this build" exit 0 fi +# In sniffer builds the example server pins RSA key transport suites for +# TLS 1.2 and below (examples/server/server.c), which carry no server +# signature: only the TLS 1.3 server cases apply there. +SVR_TLS12=$HAVE_TLS12 +SVR_DTLS12=$HAVE_DTLS12 +if grep -q -- '-DWOLFSSL_SNIFFER\b' config.log 2>/dev/null; then + SVR_TLS12=no + SVR_DTLS12=no +fi HAVE_SHA384=no HAVE_SHA512=no grep -q -- '-DWOLFSSL_SHA384\b' config.log 2>/dev/null && HAVE_SHA384=yes @@ -55,7 +64,8 @@ if grep -q -- '-DNO_CODING' config.log 2>/dev/null; then exit 0 fi -CERT_DIR="$PWD/$(dirname "$0")/../certs" +# Resolved through cd: make check runs out of tree with an absolute $0. +CERT_DIR="$(cd "$(dirname "$0")/../certs" && pwd)" if [ "$OPENSSL" = "" ]; then OPENSSL=openssl fi @@ -176,6 +186,13 @@ fail_case() { fails=$((fails+1)) } +# A case whose client never reached a server proves nothing, whatever it +# expected. +no_connection() { + grep -q "connect:errno\|tcp connect failed" rsapss.client.log || + grep -q "can't load" rsapss.server.log +} + # SignatureScheme code point (RFC 8446 4.2.3) of a scheme name. scheme_code() { case "$1" in @@ -230,7 +247,8 @@ wire_sig_scheme() { # wolfSSL server, OpenSSL s_client: $1 name, $2 scheme the server must sign # with or FAIL, $3 server arguments, $4 s_client arguments. SIG_MSG names the -# message carrying the server's signature. +# message carrying the server's signature. Suites with RSA key transport carry +# no server signature, so s_client leaves them out. wolfssl_server_case() { local name="$1" expect="$2" sargs="$3" cargs="$4" result cases=$((cases+1)) @@ -240,12 +258,15 @@ wolfssl_server_case() { server_pid=$! sleep 1 echo "test connection" | $TIMEOUT_KILL_2M $OPENSSL s_client \ - -connect 127.0.0.1:"$port" -msg $cargs > rsapss.client.log 2>&1 + -connect 127.0.0.1:"$port" -msg -cipher DEFAULT:!kRSA $cargs \ + > rsapss.client.log 2>&1 result=$? sleep 0.2 kill $server_pid 2>/dev/null wait $server_pid 2>/dev/null - if [ "$expect" = FAIL ]; then + if no_connection; then + fail_case "$name: no connection" + elif [ "$expect" = FAIL ]; then if [ "$result" = 0 ]; then fail_case "$name: handshake completed but must fail" fi @@ -278,7 +299,9 @@ openssl_server_case() { result=$? kill $server_pid 2>/dev/null wait $server_pid 2>/dev/null - if [ "$expect" = FAIL ]; then + if no_connection; then + fail_case "$name: no connection" + elif [ "$expect" = FAIL ]; then if [ "$result" = 0 ]; then fail_case "$name: handshake completed but must fail" fi @@ -296,11 +319,11 @@ openssl_server_case() { # (D)TLS 1.2 as well (RFC 8446 1.3). for ver in tls12 tls13 dtls12; do case $ver in - tls12) [ "$HAVE_TLS12" = yes ] || continue + tls12) [ "$SVR_TLS12" = yes ] || continue sv="-v 3"; cv="-tls1_2"; SIG_MSG=ServerKeyExchange ;; tls13) [ "$HAVE_TLS13" = yes ] || continue sv="-v 4"; cv="-tls1_3"; SIG_MSG=CertificateVerify ;; - dtls12) [ "$HAVE_DTLS12" = yes ] || continue + dtls12) [ "$SVR_DTLS12" = yes ] || continue # Explicit MTU: s_client cannot query the loopback path MTU on # every platform and would otherwise fragment the ClientHello. sv="-v 3 -u"; cv="-dtls1_2 -mtu 1500"; SIG_MSG=ServerKeyExchange ;; diff --git a/src/internal.c b/src/internal.c index a701b12384f..075f639792e 100644 --- a/src/internal.c +++ b/src/internal.c @@ -34971,7 +34971,7 @@ int SetSuitesHashSigAlgo(Suites* suites, const char* list) #endif /* OPENSSL_EXTRA */ #if !defined(NO_TLS) && (!defined(NO_WOLFSSL_SERVER) || !defined(NO_CERTS)) -#ifdef WC_RSA_PSS +#if !defined(NO_RSA) && defined(WC_RSA_PSS) /* Smallest RSA key able to make an RSA-PSS signature with the given hash: * emLen >= 2*hLen + 2 (RFC 8017 9.1.1, salt length = hash length). * Returns 0 when the hash isn't one used with RSA-PSS - no restriction. */ @@ -35075,7 +35075,7 @@ static int MatchSigAlgo(WOLFSSL* ssl, int hashAlgo, int sigAlgo) return sigAlgo == mldsa_87_sa_algo; } #endif /* WOLFSSL_HAVE_MLDSA */ -#ifdef WC_RSA_PSS +#if !defined(NO_RSA) && defined(WC_RSA_PSS) /* RSA certificate and PSS sig alg. Also entered once a candidate was * chosen, so each later one is held to the key as well. */ if (ssl->options.sigAlgo == rsa_sa_algo || @@ -35243,7 +35243,7 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz, } ssl->options.hashAlgo = minHash = MinHashAlgo(ssl); -#ifdef WC_RSA_PSS +#if !defined(NO_RSA) && defined(WC_RSA_PSS) /* Without a list a TLS 1.2 server signs rsa_pkcs1 (RFC 5246 7.4.1.4.1), * which an id-RSASSA-PSS key must not do (RFC 4055 1.2). */ if (hashSigAlgoSz == 0 && ssl->useRsaPss && @@ -40612,7 +40612,7 @@ int SendCertificateVerify(WOLFSSL* ssl) } if (ssl->hsType == DYNAMIC_TYPE_RSA) { - #ifdef WC_RSA_PSS + #if !defined(NO_RSA) && defined(WC_RSA_PSS) if (IsAtLeastTLSv1_2(ssl) && ssl->useRsaPss) { /* id-RSASSA-PSS key: rsa_pss_pss_* (RFC 8446 4.2.3). */ args->sigAlgo = rsa_pss_pss_algo; @@ -43287,6 +43287,20 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) } } + #if !defined(NO_RSA) && defined(WC_RSA_PSS) + /* An id-RSASSA-PSS key only signs (RFC 4055 1.2): no RSA key + * transport, so none of the static RSA suites. */ + if (ssl->useRsaPss && CipherRequires(first, second, REQUIRES_RSA)) { + CipherSpecs specs; + + if (GetCipherSpec(ssl->options.side, first, second, &specs, + NULL) == 0 && specs.kea == rsa_kea) { + WOLFSSL_MSG("id-RSASSA-PSS key cannot do RSA key transport"); + return 0; + } + } + #endif + if (CipherRequires(first, second, REQUIRES_DHE)) { WOLFSSL_MSG("Requires DHE"); if (ssl->options.haveDH == 0) { diff --git a/src/tls13.c b/src/tls13.c index f2ea80e7717..fbfd477cfdc 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -11646,7 +11646,7 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl) /* Add signature algorithm. */ if (ssl->hsType == DYNAMIC_TYPE_RSA) { - #ifdef WC_RSA_PSS + #if !defined(NO_RSA) && defined(WC_RSA_PSS) /* id-RSASSA-PSS key: rsa_pss_pss_* (RFC 8446 4.2.3) */ if (ssl->useRsaPss) args->sigAlgo = rsa_pss_pss_algo; diff --git a/tests/api.c b/tests/api.c index 8d860980d8d..694fc017c0d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -39084,8 +39084,9 @@ static int test_wc_CryptoCb(void) * TLS 1.2 too, with the hash its parameters declare (RFC 4055 1.2). The * 2048-bit certs/rsapss keys declare SHA-256, the 3072-bit ones nothing. * Mislabelled or PKCS#1 v1.5 signatures from such a key are refused. */ -#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(WC_RSA_PSS) && \ - defined(HAVE_ECC) && (!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) +#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \ + defined(WC_RSA_PSS) && defined(HAVE_ECC) && \ + (!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) #define TEST_TLS_RSA_PSS_PSS_KEY #define RSAPSS_SVR_CERT "./certs/rsapss/server-rsapss.pem" #define RSAPSS_SVR_KEY "./certs/rsapss/server-rsapss-priv.pem" @@ -39139,6 +39140,16 @@ static int test_rsa_pss_pss_mislabel_ssl_ready(WOLFSSL* ssl) return TEST_SUCCESS; } +#if defined(WOLFSSL_STATIC_RSA) && defined(BUILD_TLS_RSA_WITH_AES_128_CBC_SHA) +/* Offer RSA key transport only. */ +static int test_rsa_pss_pss_static_rsa_ssl_ready(WOLFSSL* ssl) +{ + EXPECT_DECLS; + ExpectIntEQ(wolfSSL_set_cipher_list(ssl, "AES128-SHA"), WOLFSSL_SUCCESS); + return EXPECT_RESULT(); +} +#endif + #ifdef OPENSSL_EXTRA /* Set on the session: the context setter allocates the suites before * wolfSSL_new() derives them and leaves the cipher list empty. */ @@ -39233,6 +39244,18 @@ static int test_tls_rsa_pss_pss_key(void) &server_cbf, NULL), TEST_SUCCESS); ExpectIntEQ(server_cbf.last_err, WC_NO_ERR_TRACE(INVALID_PARAMETER)); + #if defined(WOLFSSL_STATIC_RSA) && \ + defined(BUILD_TLS_RSA_WITH_AES_128_CBC_SHA) + if (!v->tls13) { + /* The key cannot do RSA key transport either (RFC 4055 1.2): a + * client offering only a static RSA suite is refused. */ + test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v); + client_cbf.ssl_ready = test_rsa_pss_pss_static_rsa_ssl_ready; + ExpectIntNE(test_wolfSSL_client_server_nofail_memio(&client_cbf, + &server_cbf, NULL), TEST_SUCCESS); + } + #endif + #ifdef OPENSSL_EXTRA /* Offered rsa_pss_pss_sha256 alone, the server selects it ... */ test_rsa_pss_pss_setup(&client_cbf, &server_cbf, v);