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..175ce9b59a9 100755 --- a/scripts/rsapss.test +++ b/scripts/rsapss.test @@ -26,10 +26,31 @@ 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 +# 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 +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 @@ -43,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 @@ -121,37 +143,256 @@ 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)) +} + +# 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 + 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 } -# -# 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 +# 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. 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)) + 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" -msg -cipher DEFAULT:!kRSA $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 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 + elif [ "$result" != 0 ]; then + fail_case "$name: handshake failed" + elif [ "$(wire_sig_scheme rsapss.client.log "$SIG_MSG")" != \ + "$(scheme_code "$expect")" ]; then + fail_case "$name: server did not sign with $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 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 + 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 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 + elif [ "$result" != 0 ]; then + fail_case "$name: handshake failed" + 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 } -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) [ "$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) [ "$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 ;; + 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" + # 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 \ + "$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" rsa_pss_pss_sha256 \ + "$ov" "$wv" "$cli" "$cca" + 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_*" 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" + 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 + 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 + 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..075f639792e 100644 --- a/src/internal.c +++ b/src/internal.c @@ -4127,14 +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); - #ifdef WOLFSSL_TLS13 - /* Add the certificate algorithm as well */ - ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, sigAlgo, - PSS_RSAE_TO_PSS_PSS(macAlgo)); - #endif + /* 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 @@ -6259,11 +6263,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 +6447,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 +6500,53 @@ 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 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) { + 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 (!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; + } + } + 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 +6669,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 +8701,9 @@ 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->rsaPssMgf = ctx->rsaPssMgf; + ssl->rsaPssSaltLen = ctx->rsaPssSaltLen; #endif #endif #ifdef HAVE_ECC @@ -16177,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. */ @@ -18810,6 +18874,12 @@ 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->peerRsaPssMgf = args->dCert->keyPssMgf; + 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 +34841,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 @@ -34855,10 +34927,19 @@ 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; -#if defined(WC_RSA_PSS) && defined(WOLFSSL_TLS13) +#if defined(WC_RSA_PSS) if (sig_alg == rsa_pss_sa_algo) needed = 4; #endif @@ -34890,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. */ @@ -34994,17 +35075,34 @@ static int MatchSigAlgo(WOLFSSL* ssl, int hashAlgo, int sigAlgo) return sigAlgo == mldsa_87_sa_algo; } #endif /* WOLFSSL_HAVE_MLDSA */ -#ifdef WC_RSA_PSS - /* RSA certificate and PSS sig alg. */ - if (ssl->options.sigAlgo == rsa_sa_algo) { +#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 || + 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), as its + * parameters allow (RFC 4055 1.2). */ + if (sigAlgo != rsa_pss_pss_algo) + return 0; + if (!MatchRsaPssParams(ssl->rsaPssHash, ssl->rsaPssMgf, + ssl->rsaPssSaltLen, 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 && @@ -35145,6 +35243,17 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz, } ssl->options.hashAlgo = minHash = MinHashAlgo(ssl); +#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 && + 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; @@ -38683,6 +38792,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) { @@ -40494,8 +40612,12 @@ int SendCertificateVerify(WOLFSSL* ssl) } if (ssl->hsType == DYNAMIC_TYPE_RSA) { - #ifdef WC_RSA_PSS - if (IsAtLeastTLSv1_2(ssl) && + #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; + } + else if (IsAtLeastTLSv1_2(ssl) && (ssl->pssAlgo & (1 << ssl->options.hashAlgo))) { args->sigAlgo = rsa_pss_sa_algo; } @@ -40549,7 +40671,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 +41200,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 +41943,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 +42608,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 +42785,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 +42902,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 +42981,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: { @@ -43158,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) { @@ -44834,12 +44977,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 +45237,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.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 7d1e7bbb965..00e2546f01d 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -1860,9 +1860,15 @@ static int ProcessBufferCertPublicKey(WOLFSSL_CTX* ctx, WOLFSSL* ssl, #ifdef WC_RSA_PSS 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 break; @@ -5448,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 68154208bcf..fbfd477cfdc 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; @@ -9758,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; } @@ -11644,8 +11645,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) { + #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; + 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 +12700,13 @@ 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->peerRsaPssMgf = 0; + ssl->peerRsaPssSaltLen = 0; +#endif keyRet = wc_RsaPublicKeyDecode(GetPeerCert(ssl)->sapkiDer, &tmpIdx, ssl->peerRsaKey, GetPeerCert(ssl)->sapkiLen); @@ -12928,6 +12943,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..694fc017c0d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -39080,6 +39080,327 @@ 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(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" +#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" +#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; +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; +} + +#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. */ +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(); +} + +#ifdef WOLFSSL_SHA384 +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(); +} +#endif + +/* 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)); + + #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); + 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); + + #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); + 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 (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); + 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(); +} + +/* 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 @@ -47342,6 +47663,8 @@ 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_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/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 46090474893..9f843f3580a 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -14537,6 +14537,10 @@ 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 = (hash != WC_HASH_TYPE_NONE) ? (byte)hash : 0; + cert->keyPssMgf = (mgf != -1) ? (byte)mgf : 0; + cert->keyPssSaltLen = (word16)saltLen; srcIdx += (word32)seqLen; } FALL_THROUGH; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 623df4f7569..46658b19d13 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4689,6 +4689,9 @@ 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 */ + word8 rsaPssMgf; /* MGF 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) @@ -6376,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) || \ @@ -7159,6 +7167,13 @@ struct WOLFSSL { byte peerRsaKeyPresent; #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 #if defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE) @@ -8188,6 +8203,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..6e742bedfc8 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1940,6 +1940,9 @@ 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 */ + 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 */ DNS_entry* altNames; /* alt names list of dns entries */