diff --git a/SCRIPTS-LIST b/SCRIPTS-LIST index 03f5cf6a8a..753bbf90e9 100644 --- a/SCRIPTS-LIST +++ b/SCRIPTS-LIST @@ -35,6 +35,7 @@ scripts/ google.test - example client test against google, part of tests resume.test - example sessoin resume test, part of tests ocsp-stapling.test - example client test against globalsign, part of tests + ocsp-stapling1_tls13.text - example client test against example server, part of tests ocsp-stapling2.test - example client test against example server, part of tests sniffer-testsuite.test - runs snifftest on a pcap of testsuite, part of tests in sniffer mode diff --git a/configure.ac b/configure.ac index 562ed1616d..04de035488 100644 --- a/configure.ac +++ b/configure.ac @@ -5968,10 +5968,27 @@ fi # Certificate Status Request : a.k.a. OCSP Stapling AC_ARG_ENABLE([ocspstapling], - [AS_HELP_STRING([--enable-ocspstapling],[Enable OCSP Stapling (default: disabled)])], + [AS_HELP_STRING([--enable-ocspstapling],[Enable OCSP Stapling ((options: yes, no-multi, no, disabled default: disabled)])], [ ENABLED_CERTIFICATE_STATUS_REQUEST=$enableval ], [ ENABLED_CERTIFICATE_STATUS_REQUEST=no ] ) +AS_CASE([$ENABLED_CERTIFICATE_STATUS_REQUEST], + [no],[ + ENABLED_CERTIFICATE_STATUS_REQUEST="no" + ENABLED_TLS_OCSP_MULTI="no" + ], + [disabled],[ + ENABLED_CERTIFICATE_STATUS_REQUEST="no" + ENABLED_TLS_OCSP_MULTI="no" + ], + [yes],[ + ENABLED_CERTIFICATE_STATUS_REQUEST="yes" + ENABLED_TLS_OCSP_MULTI="yes" + ], + [no-multi],[ + ENABLED_CERTIFICATE_STATUS_REQUEST="yes" + ENABLED_TLS_OCSP_MULTI="no" + ]) if test "x$ENABLED_NGINX" = "xyes" || test "x$ENABLED_WPAS" = "xyes" || \ test "x$ENABLED_HAPROXY" = "xyes" || test "x$ENABLED_LIGHTY" = "xyes" || \ @@ -9547,7 +9564,17 @@ if test "$ENABLED_DH" != "no" && test "$ENABLED_DH" != "const"; then LT_LIB_M fi - +# mulitple OCSP stapling for TLS 1.3 Certificate extension +if test "$ENABLED_CERTIFICATE_STATUS_REQUEST" = "yes" +then + if test "$ENABLED_TLS13" = "yes" + then + if test "$ENABLED_TLS_OCSP_MULTI" = "yes" + then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_TLS_OCSP_MULTI" + fi + fi +fi ################################################################################ # USER SETTINGS @@ -9798,6 +9825,7 @@ AM_CONDITIONAL([BUILD_SM4],[test "x$ENABLED_SM4" != "xno" || test "x$ENABLED_USE AM_CONDITIONAL([BUILD_INLINE],[test "x$ENABLED_INLINE" = "xyes"]) AM_CONDITIONAL([BUILD_OCSP],[test "x$ENABLED_OCSP" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"]) AM_CONDITIONAL([BUILD_OCSP_STAPLING],[test "x$ENABLED_CERTIFICATE_STATUS_REQUEST" = "xyes"]) +AM_CONDITIONAL([BUILD_OCSP_STAPLING_MULTI],[test "x$ENABLED_CERTIFICATE_STATUS_REQUEST" = "xyes" && test "x$ENABLED_TLS13" = "xyes" && test "x$ENABLED_TLS_OCSP_MULTI" = "xyes"]) AM_CONDITIONAL([BUILD_OCSP_STAPLING_V2],[test "x$ENABLED_CERTIFICATE_STATUS_REQUEST_V2" = "xyes"]) AM_CONDITIONAL([BUILD_CRL],[test "x$ENABLED_CRL" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"]) AM_CONDITIONAL([BUILD_CRL_MONITOR],[test "x$ENABLED_CRL_MONITOR" = "xyes"]) diff --git a/examples/client/client.c b/examples/client/client.c index e6724081b9..ca2719bd14 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -1221,66 +1221,70 @@ static const char* client_usage_msg[][78] = { || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) "-W Use OCSP Stapling (1 v1, 2 v2, 3 v2 multi)\n", /* 41 */ " With 'm' at end indicates MUST staple\n", /* 42 */ +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS_OCSP_MULTI) + " -W 1 -v 4, Perform multi OCSP stapling for TLS13\n", + /* 43 */ +#endif #endif #if defined(ATOMIC_USER) && !defined(WOLFSSL_AEAD_ONLY) - "-U Atomic User Record Layer Callbacks\n", /* 43 */ + "-U Atomic User Record Layer Callbacks\n", /* 45 */ #endif #ifdef HAVE_PK_CALLBACKS - "-P Public Key Callbacks\n", /* 44 */ + "-P Public Key Callbacks\n", /* 45 */ #endif #ifdef HAVE_ANON - "-a Anonymous client\n", /* 45 */ + "-a Anonymous client\n", /* 46 */ #endif #ifdef HAVE_CRL - "-C Disable CRL\n", /* 46 */ + "-C Disable CRL\n", /* 47 */ #endif #ifdef WOLFSSL_TRUST_PEER_CERT - "-E Path to load trusted peer cert\n", /* 47 */ + "-E Path to load trusted peer cert\n", /* 48 */ #endif #ifdef HAVE_WNR - "-q Whitewood config file, defaults\n", /* 48 */ + "-q Whitewood config file, defaults\n", /* 49 */ #endif "-H Internal tests" - " [defCipherList, exitWithRet, verifyFail, useSupCurve,\n", /* 49 */ - " loadSSL, disallowETM]\n", /* 50 */ + " [defCipherList, exitWithRet, verifyFail, useSupCurve,\n", /* 50 */ + " loadSSL, disallowETM]\n", /* 51 */ #ifdef WOLFSSL_TLS13 - "-J Use HelloRetryRequest to choose group for KE\n", /* 51 */ - "-K Key Exchange for PSK not using (EC)DHE\n", /* 52 */ - "-I Update keys and IVs before sending data\n", /* 53 */ + "-J Use HelloRetryRequest to choose group for KE\n", /* 52 */ + "-K Key Exchange for PSK not using (EC)DHE\n", /* 53 */ + "-I Update keys and IVs before sending data\n", /* 54 */ #ifndef NO_DH - "-y Key Share with FFDHE named groups only\n", /* 54 */ + "-y Key Share with FFDHE named groups only\n", /* 55 */ #endif #ifdef HAVE_ECC - "-Y Key Share with ECC named groups only\n", /* 55 */ + "-Y Key Share with ECC named groups only\n", /* 56 */ #endif #endif /* WOLFSSL_TLS13 */ #ifdef HAVE_CURVE25519 - "-t Use X25519 for key exchange\n", /* 56 */ + "-t Use X25519 for key exchange\n", /* 57 */ #endif #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) - "-Q Support requesting certificate post-handshake\n", /* 57 */ + "-Q Support requesting certificate post-handshake\n", /* 58 */ #endif #ifdef WOLFSSL_EARLY_DATA - "-0 Early data sent to server (0-RTT handshake)\n", /* 58 */ + "-0 Early data sent to server (0-RTT handshake)\n", /* 59 */ #endif #ifdef WOLFSSL_MULTICAST - "-3 Multicast, grpid < 256\n", /* 59 */ + "-3 Multicast, grpid < 256\n", /* 60 */ #endif "-1 Display a result by specified language.\n" - " 0: English, 1: Japanese\n", /* 60 */ + " 0: English, 1: Japanese\n", /* 61 */ #if !defined(NO_DH) && !defined(HAVE_FIPS) && \ !defined(HAVE_SELFTEST) && !defined(WOLFSSL_OLD_PRIME_CHECK) - "-2 Disable DH Prime check\n", /* 61 */ + "-2 Disable DH Prime check\n", /* 62 */ #endif #ifdef HAVE_SECURE_RENEGOTIATION - "-4 Use resumption for renegotiation\n", /* 62 */ + "-4 Use resumption for renegotiation\n", /* 63 */ #endif #ifdef HAVE_TRUSTED_CA - "-5 Use Trusted CA Key Indication\n", /* 63 */ + "-5 Use Trusted CA Key Indication\n", /* 64 */ #endif "-6 Simulate WANT_WRITE errors on every other IO send\n", #ifdef HAVE_CURVE448 - "-8 Use X448 for key exchange\n", /* 66 */ + "-8 Use X448 for key exchange\n", /* 65 */ #endif #if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN) && \ (defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_EXT)) && \ @@ -1448,69 +1452,73 @@ static const char* client_usage_msg[][78] = { || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) "-W OCSP Staplingを使用する" " (1 v1, 2 v2, 3 v2 multi)\n", /* 41 */ - " 'm' を最後に指定すると必ず staple を使用する\n" /* 42 */ + " 'm' を最後に指定すると必ず staple を使用する\n" /* 42 */ +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS_OCSP_MULTI) + " -W 1 -v 4, " + "TLS13 使用時に複数(Multi)の OCSP を実施します\n" /* 43 */ +#endif #endif #if defined(ATOMIC_USER) && !defined(WOLFSSL_AEAD_ONLY) "-U アトミック・ユーザー記録の" - "コールバックを利用する\n", /* 43 */ + "コールバックを利用する\n", /* 44 */ #endif #ifdef HAVE_PK_CALLBACKS - "-P 公開鍵コールバック\n", /* 44 */ + "-P 公開鍵コールバック\n", /* 45 */ #endif #ifdef HAVE_ANON - "-a 匿名クライアント\n", /* 45 */ + "-a 匿名クライアント\n", /* 46 */ #endif #ifdef HAVE_CRL - "-C CRLを無効\n", /* 46 */ + "-C CRLを無効\n", /* 47 */ #endif #ifdef WOLFSSL_TRUST_PEER_CERT - "-E 信頼出来るピアの証明書ロードの為のパス\n", /* 47 */ + "-E 信頼出来るピアの証明書ロードの為のパス\n", /* 48 */ #endif #ifdef HAVE_WNR - "-q Whitewood コンフィグファイル, 既定値\n", /* 48 */ + "-q Whitewood コンフィグファイル, 既定値\n", /* 49 */ #endif "-H 内部テスト" - " [defCipherList, exitWithRet, verifyFail, useSupCurve,\n", /* 49 */ - " loadSSL, disallowETM]\n", /* 50 */ + " [defCipherList, exitWithRet, verifyFail, useSupCurve,\n", /* 50 */ + " loadSSL, disallowETM]\n", /* 51 */ #ifdef WOLFSSL_TLS13 - "-J HelloRetryRequestをKEのグループ選択に使用する\n", /* 51 */ - "-K 鍵交換にPSKを使用、(EC)DHEは使用しない\n", /* 52 */ - "-I データ送信前に、鍵とIVを更新する\n", /* 53 */ + "-J HelloRetryRequestをKEのグループ選択に使用する\n", /* 52 */ + "-K 鍵交換にPSKを使用、(EC)DHEは使用しない\n", /* 53 */ + "-I データ送信前に、鍵とIVを更新する\n", /* 54 */ #ifndef NO_DH - "-y FFDHE名前付きグループとの鍵共有のみ\n", /* 54 */ + "-y FFDHE名前付きグループとの鍵共有のみ\n", /* 55 */ #endif #ifdef HAVE_ECC - "-Y ECC名前付きグループとの鍵共有のみ\n", /* 55 */ + "-Y ECC名前付きグループとの鍵共有のみ\n", /* 56 */ #endif #endif /* WOLFSSL_TLS13 */ #ifdef HAVE_CURVE25519 - "-t X25519を鍵交換に使用する\n", /* 56 */ + "-t X25519を鍵交換に使用する\n", /* 57 */ #endif #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) - "-Q ポストハンドシェークの証明要求をサポートする\n", /* 57 */ + "-Q ポストハンドシェークの証明要求をサポートする\n", /* 58 */ #endif #ifdef WOLFSSL_EARLY_DATA "-0 Early data をサーバーへ送信する" - "(0-RTTハンドシェイク)\n", /* 58 */ + "(0-RTTハンドシェイク)\n", /* 59 */ #endif #ifdef WOLFSSL_MULTICAST - "-3 マルチキャスト, grpid < 256\n", /* 59 */ + "-3 マルチキャスト, grpid < 256\n", /* 60 */ #endif "-1 指定された言語で結果を表示します。\n" - " 0: 英語、 1: 日本語\n", /* 60 */ + " 0: 英語、 1: 日本語\n", /* 61 */ #if !defined(NO_DH) && !defined(HAVE_FIPS) && \ !defined(HAVE_SELFTEST) && !defined(WOLFSSL_OLD_PRIME_CHECK) - "-2 DHプライム番号チェックを無効にする\n", /* 61 */ + "-2 DHプライム番号チェックを無効にする\n", /* 62 */ #endif #ifdef HAVE_SECURE_RENEGOTIATION - "-4 再交渉に再開を使用\n", /* 62 */ + "-4 再交渉に再開を使用\n", /* 63 */ #endif #ifdef HAVE_TRUSTED_CA - "-5 信頼できる認証局の鍵表示を使用する\n", /* 63 */ + "-5 信頼できる認証局の鍵表示を使用する\n", /* 64 */ #endif "-6 WANT_WRITE エラーを全てのIO 送信でシミュレートします\n", #ifdef HAVE_CURVE448 - "-8 鍵交換に X448 を使用する\n", /* 66 */ + "-8 鍵交換に X448 を使用する\n", /* 65 */ #endif #if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN) && \ (defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_EXT)) && \ diff --git a/scripts/include.am b/scripts/include.am index f4f925a080..c42fce2a7e 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -27,6 +27,9 @@ endif if BUILD_OCSP_STAPLING dist_noinst_SCRIPTS+= scripts/ocsp-stapling.test +if BUILD_OCSP_STAPLING_MULTI +dist_noinst_SCRIPTS+= scripts/ocsp-stapling_tls13multi.test +endif if !BUILD_OCSP_STAPLING_V2 testsuite/testsuite.log: scripts/ocsp-stapling.log scripts/ocsp-stapling-with-ca-as-responder.log endif @@ -34,6 +37,9 @@ scripts/ocsp-stapling.log: scripts/ocsp.log dist_noinst_SCRIPTS+= scripts/ocsp-stapling-with-ca-as-responder.test scripts/ocsp-stapling-with-ca-as-responder.log: scripts/ocsp.log scripts/ocsp-stapling-with-ca-as-responder.log: scripts/ocsp-stapling.log +if BUILD_OCSP_STAPLING_MULTI +scripts/ocsp-stapling_tls13multi.log: scripts/ocsp-stapling-with-ca-as-responder.log +endif endif if BUILD_OCSP_STAPLING_V2 diff --git a/scripts/ocsp-stapling.test b/scripts/ocsp-stapling.test index 90b7332a9a..a6f8948b58 100755 --- a/scripts/ocsp-stapling.test +++ b/scripts/ocsp-stapling.test @@ -21,6 +21,20 @@ if ./examples/client/client '-#' | fgrep -q -e ' -DWOLFSSL_SNIFFER '; then exit 77 fi +if ./examples/client/client -V | grep -q 4; then + tls13=yes +fi +if ./examples/client/client -? 2>&1 | grep -q 'DTLSv1.3'; then + dtls13=yes +fi +./examples/client/client '-?' 2>&1 | grep -- 'Perform multi OCSP stapling for TLS13' +if [ $? -eq 0 ]; then + tls13multi=yes +else + tls13multi=no +fi + + if openssl s_server -help 2>&1 | fgrep -q -i ipv6 && nc -h 2>&1 | fgrep -q -i ipv6; then IPV6_SUPPORTED=yes else @@ -340,7 +354,7 @@ RESULT=$? printf '%s\n\n' "Test successfully REVOKED!" -if ./examples/client/client -V | grep -q 4; then + if [[ ("$tls13" == "yes") && ("$tls13multi" == "no") ]]; then printf '%s\n\n' "------------- TEST CASE 3 SHOULD PASS --------------------" # client test against our own server - GOOD CERT remove_single_rF "$ready_file2" @@ -406,7 +420,7 @@ if ./examples/client/client -? 2>&1 | grep -q 'DTLSv1.2'; then printf '%s\n\n' "Test PASSED!" fi -if ./examples/client/client -? 2>&1 | grep -q 'DTLSv1.3'; then + if [[ ("$dtls13" == "yes") && ("$tls13multi" == "no") ]]; then printf '%s\n\n' "------------- TEST CASE DTLS-2 SHOULD PASS -------------------" # client test against our own server, must staple - GOOD CERT ./examples/server/server -c certs/ocsp/server1-cert.pem -R "$ready_file2" \ diff --git a/scripts/ocsp-stapling2.test b/scripts/ocsp-stapling2.test index f18ee1a7c4..dea1af61b5 100755 --- a/scripts/ocsp-stapling2.test +++ b/scripts/ocsp-stapling2.test @@ -43,7 +43,7 @@ fi if ./examples/client/client '-#' | fgrep -q -e ' -DTEST_IPV6 '; then if [[ "$IPV6_SUPPORTED" == "no" ]]; then echo 'Skipping IPV6 test in environment lacking IPV6 support.' - exit 0 + exit 77 fi LOCALHOST='[::1]' LOCALHOST_FOR_NC='-6 ::1' diff --git a/scripts/ocsp-stapling_tls13multi.test b/scripts/ocsp-stapling_tls13multi.test new file mode 100755 index 0000000000..27ef90031f --- /dev/null +++ b/scripts/ocsp-stapling_tls13multi.test @@ -0,0 +1,522 @@ +#!/bin/bash + +# ocsp-stapling_tls13multi.test +# Test requires HAVE_OCSP and HAVE_CERTIFICATE_STATUS_REQUEST_V2 + +SCRIPT_DIR="$(dirname "$0")" + +# if we can, isolate the network namespace to eliminate port collisions. +if [[ -n "$NETWORK_UNSHARE_HELPER" ]]; then + if [[ -z "$NETWORK_UNSHARE_HELPER_CALLED" ]]; then + export NETWORK_UNSHARE_HELPER_CALLED=yes + exec "$NETWORK_UNSHARE_HELPER" "$0" "$@" || exit $? + fi +elif [ "${AM_BWRAPPED-}" != "yes" ]; then + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + export AM_BWRAPPED=yes + exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" + fi + unset AM_BWRAPPED +fi + +if [[ -z "${RETRIES_REMAINING-}" ]]; then + export RETRIES_REMAINING=2 +fi + +if ! ./examples/client/client -V | grep -q 4; then + tls13=no + +else + tls13=yes +fi + +if ! ./examples/client/client -? 2>&1 | grep -q 'DTLSv1.3'; then + dtls13=no +else + dtls13=yes +fi + +if [[ ("$tls13" == "no") && ("$dtls13" == "no") ]]; then + echo 'skipping ocsp-stapling_tls13multi.test because TLS1.3 is not available.' 1>&2 + exit 77 +fi + +if ! ./examples/client/client -V | grep -q 4; then + tls13=no + echo 'skipping ocsp-stapling_tls13multi.test because TLS1.3 is not available.' 1>&2 + exit 77 +else + tls13=yes +fi + +if openssl s_server -help 2>&1 | fgrep -q -i ipv6 && nc -h 2>&1 | fgrep -q -i ipv6; then + IPV6_SUPPORTED=yes +else + IPV6_SUPPORTED=no +fi + +if ./examples/client/client '-#' | fgrep -q -e ' -DTEST_IPV6 '; then + if [[ "$IPV6_SUPPORTED" == "no" ]]; then + echo 'Skipping IPV6 test in environment lacking IPV6 support.' + exit 77 + fi + LOCALHOST='[::1]' + LOCALHOST_FOR_NC='-6 ::1' +else + LOCALHOST='127.0.0.1' + LOCALHOST_FOR_NC='127.0.0.1' +fi + +PARENTDIR="$PWD" + +# create a unique workspace directory ending in PID for the script instance ($$) +# to make this instance orthogonal to any others running, even on same repo. +# TCP ports are also carefully formed below from the PID, to minimize conflicts. + +#WORKSPACE="${PARENTDIR}/workspace.pid$$" +#mkdir "${WORKSPACE}" || exit $? + +WORKSPACE="$(mktemp -d -p ${PARENTDIR})" + +cp -pR ${SCRIPT_DIR}/../certs "${WORKSPACE}"/ || exit $? +cd "$WORKSPACE" || exit $? +ln -s ../examples + +CERT_DIR="certs/ocsp" + + +ready_file1="$WORKSPACE"/wolf_ocsp_s2_readyF1$$ +ready_file2="$WORKSPACE"/wolf_ocsp_s2_readyF2$$ +ready_file3="$WORKSPACE"/wolf_ocsp_s2_readyF3$$ +ready_file4="$WORKSPACE"/wolf_ocsp_s2_readyF4$$ +ready_file5="$WORKSPACE"/wolf_ocsp_s2_readyF5$$ +printf '%s\n' "ready file 1: $ready_file1" +printf '%s\n' "ready file 2: $ready_file2" +printf '%s\n' "ready file 3: $ready_file3" +printf '%s\n' "ready file 4: $ready_file4" +printf '%s\n' "ready file 5: $ready_file5" + +test_cnf="ocsp_s2.cnf" + +wait_for_readyFile(){ + + counter=0 + + while [ ! -s $1 -a "$counter" -lt 20 ]; do + if [[ -n "${2-}" ]]; then + if ! kill -0 $2 2>&-; then + echo "pid $2 for port ${3-} exited before creating ready file. bailing..." + exit 1 + fi + fi + echo -e "waiting for ready file..." + sleep 0.1 + counter=$((counter+ 1)) + done + + if test -e $1; then + echo -e "found ready file, starting client..." + else + echo -e "NO ready file at $1 -- ending test..." + exit 1 + fi + +} + +remove_single_rF(){ + if test -e $1; then + printf '%s\n' "removing ready file: $1" + rm $1 + fi +} +#create a configure file for cert generation with the port 0 solution +create_new_cnf() { + echo "Random Ports Selected: $1 $2 $3 $4" + + cat <<- EOF > $test_cnf + # + # openssl configuration file for OCSP certificates + # + + # Extensions to add to a certificate request (intermediate1-ca) + [ v3_req1 ] + basicConstraints = CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + keyUsage = nonRepudiation, digitalSignature, keyEncipherment + authorityInfoAccess = OCSP;URI:http://127.0.0.1:$1 + + # Extensions to add to a certificate request (intermediate2-ca) + [ v3_req2 ] + basicConstraints = CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + keyUsage = nonRepudiation, digitalSignature, keyEncipherment + authorityInfoAccess = OCSP;URI:http://127.0.0.1:$2 + + # Extensions to add to a certificate request (intermediate3-ca) + [ v3_req3 ] + basicConstraints = CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + keyUsage = nonRepudiation, digitalSignature, keyEncipherment + authorityInfoAccess = OCSP;URI:http://127.0.0.1:$3 + + # Extensions for a typical CA + [ v3_ca ] + basicConstraints = CA:true + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + keyUsage = keyCertSign, cRLSign + authorityInfoAccess = OCSP;URI:http://127.0.0.1:$4 + + # OCSP extensions. + [ v3_ocsp ] + basicConstraints = CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + extendedKeyUsage = OCSPSigning +EOF + + mv $test_cnf $CERT_DIR/$test_cnf + cd $CERT_DIR + CURR_LOC="$PWD" + printf '%s\n' "echo now in $CURR_LOC" + ./renewcerts-for-test.sh $test_cnf + cd $WORKSPACE +} + +remove_ready_file(){ + if test -e $ready_file1; then + printf '%s\n' "removing ready file: $ready_file1" + rm $ready_file1 + fi + if test -e $ready_file2; then + printf '%s\n' "removing ready file: $ready_file2" + rm $ready_file2 + fi + if test -e $ready_file3; then + printf '%s\n' "removing ready file: $ready_file3" + rm $ready_file3 + fi + if test -e $ready_file4; then + printf '%s\n' "removing ready file: $ready_file4" + rm $ready_file4 + fi + if test -e $ready_file5; then + printf '%s\n' "removing ready file: $ready_file5" + rm $ready_file5 + fi +} + +cleanup() +{ + exit_status=$? + for i in $(jobs -pr) + do + kill -s KILL "$i" + done + remove_ready_file + rm $CERT_DIR/$test_cnf + cd "$PARENTDIR" || return 1 + rm -r "$WORKSPACE" || return 1 + + if [[ ("$exit_status" == 1) && ($RETRIES_REMAINING -gt 0) ]]; then + echo "retrying..." + RETRIES_REMAINING=$((RETRIES_REMAINING - 1)) + exec $0 "$@" + fi +} +trap cleanup EXIT INT TERM HUP + +[ ! -x ./examples/client/client ] && echo -e "\n\nClient doesn't exist" && exit 1 + +# check if supported key size is large enough to handle 4096 bit RSA +size="$(./examples/client/client '-?' | grep "Max RSA key")" +size="${size//[^0-9]/}" +if [ ! -z "$size" ]; then + printf 'check on max key size of %d ...' $size + if [ $size -lt 4096 ]; then + printf '%s\n' "4096 bit RSA keys not supported" + exit 0 + fi + printf 'OK\n' +fi + +#get four unique ports + +# choose consecutive ports based on the PID, skipping any that are +# already bound, to avoid the birthday problem in case other +# instances are sharing this host. + +get_first_free_port() { + local ret="$1" + while :; do + if [[ "$ret" -ge 65536 ]]; then + ret=1024 + fi + if ! nc -z ${LOCALHOST_FOR_NC} "$ret"; then + break + fi + ret=$((ret+1)) + done + echo "$ret" + return 0 +} + +base_port=$((((($$ + $RETRIES_REMAINING) * 5) % (65536 - 2048)) + 1024)) +port1=$(get_first_free_port $base_port) +port2=$(get_first_free_port $((port1 + 1))) +port3=$(get_first_free_port $((port2 + 1))) +port4=$(get_first_free_port $((port3 + 1))) +port5=$(get_first_free_port $((port4 + 1))) + +# 1: +./examples/server/server -R $ready_file1 -p $port1 & +server_pid1=$! +wait_for_readyFile $ready_file1 $server_pid1 $port1 +if [ ! -f $ready_file1 ]; then + printf '%s\n' "Failed to create ready file1: \"$ready_file1\"" + exit 1 +fi +# 2: +./examples/server/server -R $ready_file2 -p $port2 & +server_pid2=$! +wait_for_readyFile $ready_file2 $server_pid2 $port2 +if [ ! -f $ready_file2 ]; then + printf '%s\n' "Failed to create ready file2: \"$ready_file2\"" + exit 1 +fi +# 3: +./examples/server/server -R $ready_file3 -p $port3 & +server_pid3=$! +wait_for_readyFile $ready_file3 $server_pid3 $port3 +if [ ! -f $ready_file3 ]; then + printf '%s\n' "Failed to create ready file3: \"$ready_file3\"" + exit 1 +fi +# 4: +./examples/server/server -R $ready_file4 -p $port4 & +server_pid4=$! +wait_for_readyFile $ready_file4 $server_pid4 $port4 +if [ ! -f $ready_file4 ]; then + printf '%s\n' "Failed to create ready file4: \"$ready_file4\"" + exit 1 +fi + +printf '%s\n' "------------- PORTS ---------------" +printf '%s' "Random ports selected: $port1 $port2" +printf '%s\n' " $port3 $port4" +printf '%s\n' "-----------------------------------" +# Use client connections to cleanly shutdown the servers +./examples/client/client -p $port1 +./examples/client/client -p $port2 +./examples/client/client -p $port3 +./examples/client/client -p $port4 +create_new_cnf $port1 $port2 $port3 \ + $port4 + +sleep 0.1 + +# setup ocsp responders +# OLD: ./certs/ocsp/ocspd-root-ca-and-intermediate-cas.sh & +# NEW: openssl isn't being cleaned up, invoke directly in script for cleanup +# purposes! +openssl ocsp -port $port1 -nmin 1 \ + -index certs/ocsp/index-ca-and-intermediate-cas.txt \ + -rsigner certs/ocsp/ocsp-responder-cert.pem \ + -rkey certs/ocsp/ocsp-responder-key.pem \ + -CA certs/ocsp/root-ca-cert.pem \ + "$@" \ + & + +# OLD: ./certs/ocsp/ocspd-intermediate2-ca-issued-certs.sh & +# NEW: openssl isn't being cleaned up, invoke directly in script for cleanup +# purposes! +openssl ocsp -port $port2 -nmin 1 \ + -index certs/ocsp/index-intermediate2-ca-issued-certs.txt \ + -rsigner certs/ocsp/ocsp-responder-cert.pem \ + -rkey certs/ocsp/ocsp-responder-key.pem \ + -CA certs/ocsp/intermediate2-ca-cert.pem \ + "$@" \ + & + +# OLD: ./certs/ocsp/ocspd-intermediate3-ca-issued-certs.sh & +# NEW: openssl isn't being cleaned up, invoke directly in script for cleanup +# purposes! +openssl ocsp -port $port3 -nmin 1 \ + -index certs/ocsp/index-intermediate3-ca-issued-certs.txt \ + -rsigner certs/ocsp/ocsp-responder-cert.pem \ + -rkey certs/ocsp/ocsp-responder-key.pem \ + -CA certs/ocsp/intermediate3-ca-cert.pem \ + "$@" \ + & + +# NEW: openssl isn't being cleaned up, invoke directly in script for cleanup +# purposes! +openssl ocsp -port $port4 -nmin 1 \ + -index certs/ocsp/index-ca-and-intermediate-cas.txt \ + -rsigner certs/ocsp/ocsp-responder-cert.pem \ + -rkey certs/ocsp/ocsp-responder-key.pem \ + -CA certs/ocsp/root-ca-cert.pem \ + "$@" \ + & + +sleep 0.1 +# "jobs" is not portable for posix. Must use bash interpreter! +[ $(jobs -r | wc -l) -ne 4 ] && printf '\n\n%s\n' "Setup ocsp responder failed, skipping" && exit 0 + +printf '\n\n%s\n\n' "All OCSP responders started successfully!" + +if [ "$tls13" == "yes" ]; then + printf '%s\n\n' "------------- TEST CASE 1 SHOULD PASS ------------------------" + # client test against our own server - GOOD CERTS + ./examples/server/server -c certs/ocsp/server3-cert.pem \ + -k certs/ocsp/server3-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection 1 failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE 2 SHOULD REVOKE ----------------------" + # client test against our own server - REVOKED SERVER CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server4-cert.pem \ + -k certs/ocsp/server4-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + + printf '%s\n\n' "------------- TEST CASE 3 SHOULD REVOKE ----------------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server4-cert.pem \ + -k certs/ocsp/server4-key.pem -R $ready_file5 \ + -p $port5 & + sleep 0.1 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + + printf '%s\n\n' "------------- TEST CASE 4 SHOULD REVOKE ------------------------" + # client test against our own server - REVOKED INTERMEDIATE CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server5-cert.pem \ + -k certs/ocsp/server5-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + + printf '%s\n\n' "------------- TEST CASE 5 SHOULD REVOKE ----------------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server5-cert.pem \ + -k certs/ocsp/server5-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + + printf '%s\n\n' "------------- TEST CASE 6 LOAD CERT IN SSL -------------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server1-cert.pem \ + -k certs/ocsp/server1-key.pem -R $ready_file5 -v 4 \ + -p $port5 -H loadSSL & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + echo "test connection" | openssl s_client -status -legacy_renegotiation -connect ${LOCALHOST}:$port5 -cert ./certs/client-cert.pem -key ./certs/client-key.pem -CAfile ./certs/ocsp/root-ca-cert.pem + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection failed $RESULT" && exit 1 + wait $server_pid5 + if [ $? -ne 0 ]; then + printf '%s\n' "Unexpected server result" + exit 1 + fi + printf '%s\n\n' "Test successful" + printf '%s\n\n' "------------- TEST CASE 7 SHOULD REVOKE ----------------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server4-cert.pem \ + -k certs/ocsp/server4-key.pem -R $ready_file5 \ + -p $port5 -H loadSSL -v 4 & + server_pid5=$! + sleep 0.1 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection succeeded $RESULT" && exit 1 + wait $server_pid5 + if [ $? -ne 1 ]; then + printf '%s\n' "Unexpected server result" + exit 1 + fi + printf '%s\n\n' "Test successfully REVOKED!" +fi + +if [ "$dtls13" == "yes" ]; then + printf '%s\n\n' "------------- TEST CASE DTLS-1 SHOULD PASS ---------------" + # client test against our own server - GOOD CERTS + ./examples/server/server -c certs/ocsp/server3-cert.pem \ + -k certs/ocsp/server3-key.pem -R $ \ + -p $port5 -u -v 4 & + server_pid5=$! + sleep 0.2 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -u -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection 1 failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE DTLS-2 SHOULD REVOKE --------------" + # client test against our own server - REVOKED SERVER CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server4-cert.pem \ + -k certs/ocsp/server4-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + sleep 0.2 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + +fi + +# need a unique port since may run the same time as testsuite +generate_port() { + #-------------------------------------------------------------------------# + # Generate a random port number + #-------------------------------------------------------------------------# + + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49512) + 49512)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49512) + 49512)) + else + echo "Unknown OS TYPE" + exit 1 + fi +} + +printf '%s\n\n' "------------------- TESTS COMPLETE ---------------------------" + +exit 0 diff --git a/src/internal.c b/src/internal.c index 4ba6013fe7..eb361d7e85 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7719,6 +7719,11 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) ssl->sigSpec = ctx->sigSpec; ssl->sigSpecSz = ctx->sigSpecSz; #endif /* WOLFSSL_DUAL_ALG_CERTS */ +#ifdef HAVE_OCSP +#if defined(WOLFSSL_TLS13) && defined(HAVE_CERTIFICATE_STATUS_REQUEST) + ssl->response_idx = 0; +#endif +#endif /* Returns 0 on success, not WOLFSSL_SUCCESS (1) */ WOLFSSL_MSG_EX("InitSSL done. return 0 (success)"); return 0; @@ -13218,12 +13223,17 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ (defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(WOLFSSL_NO_TLS12)) -static int ProcessCSR(WOLFSSL* ssl, byte* input, word32* inOutIdx, - word32 status_length) +static int ProcessCSR_ex(WOLFSSL* ssl, byte* input, word32* inOutIdx, + word32 status_length, int idx) { int ret = 0; OcspRequest* request; - +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + TLSX* ext = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); + CertificateStatusRequest* csr; +#else + (void)idx; +#endif #ifdef WOLFSSL_SMALL_STACK CertStatus* status; OcspEntry* single; @@ -13235,11 +13245,19 @@ static int ProcessCSR(WOLFSSL* ssl, byte* input, word32* inOutIdx, #endif WOLFSSL_ENTER("ProcessCSR"); - +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + if (ext) { + /* status request */ + csr = (CertificateStatusRequest*)ext->data; + if (csr && !csr->ssl) + csr->ssl = ssl; + } +#endif do { #ifdef HAVE_CERTIFICATE_STATUS_REQUEST if (ssl->status_request) { - request = (OcspRequest*)TLSX_CSR_GetRequest(ssl->extensions); + request = (OcspRequest*)TLSX_CSR_GetRequest_ex(ssl->extensions, + idx); ssl->status_request = 0; break; } @@ -13308,6 +13326,12 @@ static int ProcessCSR(WOLFSSL* ssl, byte* input, word32* inOutIdx, WOLFSSL_LEAVE("ProcessCSR", ret); return ret; } + +static int ProcessCSR(WOLFSSL* ssl, byte* input, word32* inOutIdx, + word32 status_length) +{ + return ProcessCSR_ex(ssl, input, inOutIdx, status_length, 0); +} #endif @@ -14363,6 +14387,52 @@ static int ProcessPeerCertCheckKey(WOLFSSL* ssl, ProcPeerCertArgs* args) return ret; } +#if defined(HAVE_OCSP) && defined(WOLFSSL_TLS13) \ + && defined(HAVE_CERTIFICATE_STATUS_REQUEST) +static int ProcessPeerCertsChainOCSPStatusCheck(WOLFSSL* ssl) +{ + int ret = 0; + word32 i; + word32 idx = 0; + TLSX* ext = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); + CertificateStatusRequest* csr; + + if (ext) { + csr = (CertificateStatusRequest*)ext->data; + if (csr == NULL) { + return 0; + } + } else + return 0; + + /* error when leaf cert doesn't have certificate status */ + if (csr->requests < 1 || csr->responses[0].length == 0) { + WOLFSSL_MSG("Leaf cert doesn't have certificate status."); + return BAD_CERTIFICATE_STATUS_ERROR; + } + + for (i = 0; i < csr->requests; i++) { + if (csr->responses[i].length != 0) { + ssl->status_request = 1; + idx = 0; + ret = ProcessCSR_ex(ssl, + csr->responses[i].buffer, + &idx, csr->responses[i].length, i); + if (ret < 0) { + WOLFSSL_ERROR_VERBOSE(ret); + break; + } + } + else { + WOLFSSL_MSG("Intermediate cert doesn't have certificate status."); + } + } + + return ret; +} + +#endif + #ifdef HAVE_CRL static int ProcessPeerCertsChainCRLCheck(WOLFSSL* ssl, ProcPeerCertArgs* args) { @@ -14645,8 +14715,11 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, args->idx += extSz; listSz -= extSz + OPAQUE16_LEN; WOLFSSL_MSG_EX("\tParsing %d bytes of cert extensions", - args->exts[args->totalCerts].length); + args->exts[args->totalCerts].length); #if !defined(NO_TLS) + #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + ssl->response_idx = args->totalCerts; + #endif ret = TLSX_Parse(ssl, args->exts[args->totalCerts].buffer, (word16)args->exts[args->totalCerts].length, certificate, NULL); @@ -14841,6 +14914,13 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, } else /* skips OCSP and force CRL check */ #endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ + #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + if (IsAtLeastTLSv1_3(ssl->version)) { + ret = TLSX_CSR_InitRequest_ex(ssl->extensions, + args->dCert, ssl->heap, args->certIdx); + } + else + #endif if (SSL_CM(ssl)->ocspEnabled && SSL_CM(ssl)->ocspCheckAll) { WOLFSSL_MSG("Doing Non Leaf OCSP check"); @@ -15321,24 +15401,17 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (ssl->options.side == WOLFSSL_CLIENT_END) { #ifdef HAVE_CERTIFICATE_STATUS_REQUEST if (ssl->status_request) { - args->fatal = (TLSX_CSR_InitRequest(ssl->extensions, - args->dCert, ssl->heap) != 0); + args->fatal = (TLSX_CSR_InitRequest_ex( + ssl->extensions, args->dCert, + ssl->heap, args->certIdx) != 0); doLookup = 0; WOLFSSL_MSG("\tHave status request"); #if defined(WOLFSSL_TLS13) if (ssl->options.tls1_3) { - TLSX* ext = TLSX_Find(ssl->extensions, - TLSX_STATUS_REQUEST); - if (ext != NULL) { - word32 idx = 0; - CertificateStatusRequest* csr = - (CertificateStatusRequest*)ext->data; - ret = ProcessCSR(ssl, csr->response.buffer, - &idx, csr->response.length); - if (ret < 0) { - WOLFSSL_ERROR_VERBOSE(ret); - goto exit_ppc; - } + ret = ProcessPeerCertsChainOCSPStatusCheck(ssl); + if (ret < 0) { + WOLFSSL_ERROR_VERBOSE(ret); + goto exit_ppc; } } #endif @@ -23307,7 +23380,7 @@ int SendFinished(WOLFSSL* ssl) * * Returns 0 on success */ -static int CreateOcspRequest(WOLFSSL* ssl, OcspRequest* request, +int CreateOcspRequest(WOLFSSL* ssl, OcspRequest* request, DecodedCert* cert, byte* certData, word32 length, byte *ctxOwnsRequest) { @@ -24182,7 +24255,6 @@ int SendCertificateStatus(WOLFSSL* ssl) if (idx > chain->length) break; - ret = CreateOcspRequest(ssl, request, cert, der.buffer, der.length, &ctxOwnsRequest); if (ret == 0) { @@ -25359,6 +25431,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e) case MAX_CHAIN_ERROR: return "Maximum Chain Depth Exceeded"; + case MAX_CERT_EXTENSIONS_ERR: + return "Maximum Cert Extension Exceeded"; + case COOKIE_ERROR: return "DTLS Cookie Error"; diff --git a/src/ssl.c b/src/ssl.c index 594cc0ceee..0a80a69569 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -13100,7 +13100,11 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, #ifdef WOLFSSL_QUIC wolfSSL_quic_clear(ssl); #endif - +#ifdef HAVE_OCSP +#if defined(WOLFSSL_TLS13) && defined(HAVE_CERTIFICATE_STATUS_REQUEST) + ssl->response_idx = 0; +#endif +#endif return WOLFSSL_SUCCESS; } diff --git a/src/tls.c b/src/tls.c index a2f3705cf0..129c6b5484 100644 --- a/src/tls.c +++ b/src/tls.c @@ -3116,51 +3116,64 @@ int TLSX_UseTruncatedHMAC(TLSX** extensions, void* heap) static void TLSX_CSR_Free(CertificateStatusRequest* csr, void* heap) { + int i; + switch (csr->status_type) { case WOLFSSL_CSR_OCSP: - FreeOcspRequest(&csr->request.ocsp); + for (i = 0; i <= csr->requests; i++) { + FreeOcspRequest(&csr->request.ocsp[i]); + } break; } - #ifdef WOLFSSL_TLS13 - if (csr->response.buffer != NULL) { - XFREE(csr->response.buffer, csr->ssl->heap, + for (i = 0; i < MAX_CERT_EXTENSIONS; i++) { + if (csr->responses[i].buffer != NULL) { + XFREE(csr->responses[i].buffer, heap, DYNAMIC_TYPE_TMP_BUFFER); + } } #endif XFREE(csr, heap, DYNAMIC_TYPE_TLSX); (void)heap; } -static word16 TLSX_CSR_GetSize(CertificateStatusRequest* csr, byte isRequest) +word16 TLSX_CSR_GetSize_ex(CertificateStatusRequest* csr, byte isRequest, + int idx) { word16 size = 0; /* shut up compiler warnings */ (void) csr; (void) isRequest; - #ifndef NO_WOLFSSL_CLIENT if (isRequest) { switch (csr->status_type) { case WOLFSSL_CSR_OCSP: size += ENUM_LEN + 2 * OPAQUE16_LEN; - if (csr->request.ocsp.nonceSz) + if (csr->request.ocsp[0].nonceSz) size += OCSP_NONCE_EXT_SZ; break; } } #endif #if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) - if (!isRequest && csr->ssl->options.tls1_3) - return OPAQUE8_LEN + OPAQUE24_LEN + csr->response.length; + if (!isRequest && IsAtLeastTLSv1_3(csr->ssl->version)) { + return (word16)(OPAQUE8_LEN + OPAQUE24_LEN + + csr->responses[idx].length); + } +#else + (void)idx; #endif - return size; } -static int TLSX_CSR_Write(CertificateStatusRequest* csr, byte* output, - byte isRequest) +static word16 TLSX_CSR_GetSize(CertificateStatusRequest* csr, byte isRequest) +{ + return TLSX_CSR_GetSize_ex(csr, isRequest, 0); +} + +int TLSX_CSR_Write_ex(CertificateStatusRequest* csr, byte* output, + byte isRequest, int idx) { /* shut up compiler warnings */ (void) csr; (void) output; (void) isRequest; @@ -3181,8 +3194,8 @@ static int TLSX_CSR_Write(CertificateStatusRequest* csr, byte* output, offset += OPAQUE16_LEN; /* request extensions */ - if (csr->request.ocsp.nonceSz) { - ret = (int)EncodeOcspRequestExtensions(&csr->request.ocsp, + if (csr->request.ocsp[0].nonceSz) { + ret = (int)EncodeOcspRequestExtensions(&csr->request.ocsp[0], output + offset + OPAQUE16_LEN, OCSP_NONCE_EXT_SZ); @@ -3204,20 +3217,112 @@ static int TLSX_CSR_Write(CertificateStatusRequest* csr, byte* output, } #endif #if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) - if (!isRequest && csr->ssl->options.tls1_3) { + if (!isRequest && IsAtLeastTLSv1_3(csr->ssl->version)) { word16 offset = 0; output[offset++] = csr->status_type; - c32to24(csr->response.length, output + offset); + c32to24(csr->responses[idx].length, output + offset); offset += OPAQUE24_LEN; - XMEMCPY(output + offset, csr->response.buffer, csr->response.length); - offset += csr->response.length; + XMEMCPY(output + offset, csr->responses[idx].buffer, + csr->responses[idx].length); + offset += (word16)csr->responses[idx].length; return offset; } +#else + (void)idx; #endif return 0; } +static int TLSX_CSR_Write(CertificateStatusRequest* csr, byte* output, + byte isRequest) +{ + return TLSX_CSR_Write_ex(csr, output, isRequest, 0); +} + +#if !defined(NO_WOLFSSL_SERVER) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_TLS_OCSP_MULTI) +/* Process OCSP request certificate chain + * + * ssl SSL/TLS object. + * returns 0 on success, otherwise failure. + */ +static int ProcessChainOCSPRequest(WOLFSSL* ssl) +{ + DecodedCert* cert; + OcspRequest* request; + TLSX* extension; + CertificateStatusRequest* csr; + DerBuffer* chain; + word32 pos = 0; + buffer der; + int i = 1; + int ret = 0; + byte ctxOwnsRequest = 0; + + /* use certChain if available, otherwise use peer certificate */ + chain = ssl->buffers.certChain; + if (chain == NULL) { + chain = ssl->buffers.certificate; + } + + extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); + csr = extension ? + (CertificateStatusRequest*)extension->data : NULL; + if (csr == NULL) + return MEMORY_ERROR; + + cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), ssl->heap, + DYNAMIC_TYPE_DCERT); + if (cert == NULL) { + return MEMORY_E; + } + + if (chain && chain->buffer) { + while (ret == 0 && pos + OPAQUE24_LEN < chain->length) { + c24to32(chain->buffer + pos, &der.length); + pos += OPAQUE24_LEN; + der.buffer = chain->buffer + pos; + pos += der.length; + + if (pos > chain->length) + break; + request = &csr->request.ocsp[i]; + if (ret == 0) { + ret = CreateOcspRequest(ssl, request, cert, + der.buffer, der.length, &ctxOwnsRequest); + if (ctxOwnsRequest) { + wolfSSL_Mutex* ocspLock = + &SSL_CM(ssl)->ocsp_stapling->ocspLock; + if (wc_LockMutex(ocspLock) == 0) { + /* the request is ours */ + ssl->ctx->certOcspRequest = NULL; + } + wc_UnLockMutex(ocspLock); + } + } + + if (ret == 0) { + request->ssl = ssl; + ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling, + request, &csr->responses[i], ssl->heap); + /* Suppressing, not critical */ + if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED) || + ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) || + ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) { + ret = 0; + } + i++; + csr->requests++; + } + } + } + XFREE(cert, ssl->heap, DYNAMIC_TYPE_DCERT); + + return ret; +} +#endif + static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte isRequest) { @@ -3272,14 +3377,14 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, switch (csr->status_type) { case WOLFSSL_CSR_OCSP: /* propagate nonce */ - if (csr->request.ocsp.nonceSz) { + if (csr->request.ocsp[0].nonceSz) { request = (OcspRequest*)TLSX_CSR_GetRequest(ssl->extensions); if (request) { - XMEMCPY(request->nonce, csr->request.ocsp.nonce, - csr->request.ocsp.nonceSz); - request->nonceSz = csr->request.ocsp.nonceSz; + XMEMCPY(request->nonce, csr->request.ocsp[0].nonce, + csr->request.ocsp[0].nonceSz); + request->nonceSz = csr->request.ocsp[0].nonceSz; } } break; @@ -3310,14 +3415,21 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, ret = BUFFER_ERROR; } if (ret == 0) { - csr->response.buffer = (byte*)XMALLOC(resp_length, ssl->heap, + if (ssl->response_idx < (1 + MAX_CHAIN_DEPTH)) + csr->responses[ssl->response_idx].buffer = + (byte*)XMALLOC(resp_length, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); - if (csr->response.buffer == NULL) + else + ret = BAD_FUNC_ARG; + + if (ret == 0 && + csr->responses[ssl->response_idx].buffer == NULL) ret = MEMORY_ERROR; } if (ret == 0) { - XMEMCPY(csr->response.buffer, input + offset, resp_length); - csr->response.length = resp_length; + XMEMCPY(csr->responses[ssl->response_idx].buffer, + input + offset, resp_length); + csr->responses[ssl->response_idx].length = resp_length; } return ret; @@ -3382,6 +3494,7 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, #if defined(WOLFSSL_TLS13) if (ssl->options.tls1_3) { + if (ssl->buffers.certificate == NULL) { WOLFSSL_MSG("Certificate buffer not set!"); return BUFFER_ERROR; @@ -3412,19 +3525,33 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, } FreeDecodedCert(cert); XFREE(cert, ssl->heap, DYNAMIC_TYPE_DCERT); - extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); csr = extension ? (CertificateStatusRequest*)extension->data : NULL; if (csr == NULL) return MEMORY_ERROR; - request = &csr->request.ocsp; - ret = CreateOcspResponse(ssl, &request, &csr->response); + request = &csr->request.ocsp[0]; + ret = CreateOcspResponse(ssl, &request, &csr->responses[0]); + if (request != &csr->request.ocsp[0] && + ssl->buffers.weOwnCert) { + /* request will be allocated in CreateOcspResponse() */ + FreeOcspRequest(request); + XFREE(request, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST); + } if (ret != 0) return ret; - if (csr->response.buffer) + + if (csr->responses[0].buffer) TLSX_SetResponse(ssl, TLSX_STATUS_REQUEST); + #if defined(WOLFSSL_TLS_OCSP_MULTI) + /* process OCSP request in certificate chain */ + if ((ret = ProcessChainOCSPRequest(ssl)) != 0) { + WOLFSSL_MSG("Process Cert Chain OCSP request failed"); + WOLFSSL_ERROR_VERBOSE(ret); + return ret; + } + #endif } else #endif @@ -3436,9 +3563,10 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, return 0; } -int TLSX_CSR_InitRequest(TLSX* extensions, DecodedCert* cert, void* heap) +int TLSX_CSR_InitRequest_ex(TLSX* extensions, DecodedCert* cert, + void* heap, int idx) { - TLSX* extension = TLSX_Find(extensions, TLSX_STATUS_REQUEST); + TLSX* extension = TLSX_Find(extensions, TLSX_STATUS_REQUEST); CertificateStatusRequest* csr = extension ? (CertificateStatusRequest*)extension->data : NULL; int ret = 0; @@ -3447,18 +3575,31 @@ int TLSX_CSR_InitRequest(TLSX* extensions, DecodedCert* cert, void* heap) switch (csr->status_type) { case WOLFSSL_CSR_OCSP: { byte nonce[MAX_OCSP_NONCE_SZ]; - int nonceSz = csr->request.ocsp.nonceSz; - + int req_cnt = idx == -1 ? csr->requests : idx; + int nonceSz = csr->request.ocsp[0].nonceSz; + OcspRequest* request; + + request = &csr->request.ocsp[req_cnt]; + if (request->serial != NULL) { + /* clear request contents before re-use */ + FreeOcspRequest(request); + } /* preserve nonce */ - XMEMCPY(nonce, csr->request.ocsp.nonce, nonceSz); + XMEMCPY(nonce, request->nonce, nonceSz); - if ((ret = InitOcspRequest(&csr->request.ocsp, cert, 0, heap)) - != 0) - return ret; + if (req_cnt < MAX_CERT_EXTENSIONS) { + if ((ret = InitOcspRequest(request, cert, 0, heap)) != 0) + return ret; - /* restore nonce */ - XMEMCPY(csr->request.ocsp.nonce, nonce, nonceSz); - csr->request.ocsp.nonceSz = nonceSz; + /* restore nonce */ + XMEMCPY(request->nonce, nonce, nonceSz); + request->nonceSz = nonceSz; + csr->requests++; + } + else { + WOLFSSL_ERROR_VERBOSE(MAX_CERT_EXTENSIONS_ERR); + return MAX_CERT_EXTENSIONS_ERR; + } } break; } @@ -3467,22 +3608,37 @@ int TLSX_CSR_InitRequest(TLSX* extensions, DecodedCert* cert, void* heap) return ret; } -void* TLSX_CSR_GetRequest(TLSX* extensions) +int TLSX_CSR_InitRequest(TLSX* extensions, DecodedCert* cert, void* heap) +{ + return TLSX_CSR_InitRequest_ex(extensions, cert, heap, -1); +} + +void* TLSX_CSR_GetRequest_ex(TLSX* extensions, int idx) { TLSX* extension = TLSX_Find(extensions, TLSX_STATUS_REQUEST); CertificateStatusRequest* csr = extension ? (CertificateStatusRequest*)extension->data : NULL; - if (csr) { + if (csr && csr->ssl) { switch (csr->status_type) { case WOLFSSL_CSR_OCSP: - return &csr->request.ocsp; + if (IsAtLeastTLSv1_3(csr->ssl->version)) { + return idx < csr->requests ? &csr->request.ocsp[idx] : NULL; + } + else { + return idx == 0 ? &csr->request.ocsp[0] : NULL; + } } } return NULL; } +void* TLSX_CSR_GetRequest(TLSX* extensions) +{ + return TLSX_CSR_GetRequest_ex(extensions, 0); +} + int TLSX_CSR_ForceRequest(WOLFSSL* ssl) { TLSX* extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); @@ -3493,9 +3649,9 @@ int TLSX_CSR_ForceRequest(WOLFSSL* ssl) switch (csr->status_type) { case WOLFSSL_CSR_OCSP: if (SSL_CM(ssl)->ocspEnabled) { - csr->request.ocsp.ssl = ssl; + csr->request.ocsp[0].ssl = ssl; return CheckOcspRequest(SSL_CM(ssl)->ocsp, - &csr->request.ocsp, NULL, NULL); + &csr->request.ocsp[0], NULL, NULL); } else { WOLFSSL_ERROR_VERBOSE(OCSP_LOOKUP_FAIL); @@ -3523,7 +3679,9 @@ int TLSX_UseCertificateStatusRequest(TLSX** extensions, byte status_type, return MEMORY_E; ForceZero(csr, sizeof(CertificateStatusRequest)); - +#if defined(WOLFSSL_TLS13) + XMEMSET(csr->responses, 0, sizeof(csr->responses)); +#endif csr->status_type = status_type; csr->options = options; csr->ssl = ssl; @@ -3540,9 +3698,9 @@ int TLSX_UseCertificateStatusRequest(TLSX** extensions, byte status_type, (void)devId; #endif if (ret == 0) { - if (wc_RNG_GenerateBlock(&rng, csr->request.ocsp.nonce, + if (wc_RNG_GenerateBlock(&rng, csr->request.ocsp[0].nonce, MAX_OCSP_NONCE_SZ) == 0) - csr->request.ocsp.nonceSz = MAX_OCSP_NONCE_SZ; + csr->request.ocsp[0].nonceSz = MAX_OCSP_NONCE_SZ; wc_FreeRng(&rng); } diff --git a/src/tls13.c b/src/tls13.c index 55c9fabe53..ca6980b04d 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -8403,6 +8403,75 @@ static word32 NextCert(byte* data, word32 length, word32* idx) return len; } +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) +/* Write certificate status request into certificate to buffer. + * + * ssl SSL/TLS object. + * certExts DerBuffer array. buffers written + * extSz word32 array. + * Length of the certificate status request data for the certificate. + * extSz_num number of the CSR written + * extIdx The index number of certificate status request data + * for the certificate. + * offset index offset + * returns Total number of bytes written. + */ +static word32 WriteCSRToBuffer(WOLFSSL* ssl, DerBuffer** certExts, + word16* extSz, word16 extSz_num) +{ + int ret = 0; + TLSX* ext; + CertificateStatusRequest* csr; + word32 ex_offset = HELLO_EXT_TYPE_SZ + OPAQUE16_LEN /* extension type */ + + OPAQUE16_LEN /* extension length */; + word32 totalSz = 0; + word32 tmpSz; + word32 extIdx; + DerBuffer* der; + + ext = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); + csr = ext ? (CertificateStatusRequest*)ext->data : NULL; + + if (csr) { + for (extIdx = 0; extIdx < (word16)(extSz_num); extIdx++) { + tmpSz = TLSX_CSR_GetSize_ex(csr, 0, extIdx); + + if (tmpSz > (OPAQUE8_LEN + OPAQUE24_LEN) && + certExts[extIdx] == NULL) { + /* csr extension is not zero */ + extSz[extIdx] = tmpSz; + + ret = AllocDer(&certExts[extIdx], extSz[extIdx] + ex_offset, + CERT_TYPE, ssl->heap); + if (ret < 0) + return ret; + der = certExts[extIdx]; + + /* write extension type */ + c16toa(ext->type, der->buffer + + OPAQUE16_LEN); + /* writes extension data length. */ + c16toa(extSz[extIdx], der->buffer + + HELLO_EXT_TYPE_SZ + OPAQUE16_LEN); + /* write extension data */ + extSz[extIdx] = (word16)TLSX_CSR_Write_ex(csr, + der->buffer + ex_offset, 0, extIdx); + /* add extension offset */ + extSz[extIdx] += (word16)ex_offset; + /* extension length */ + c16toa(extSz[extIdx] - OPAQUE16_LEN, + der->buffer); + } + totalSz += extSz[extIdx]; + } + } + else { + /* chain cert empty extension size */ + totalSz += OPAQUE16_LEN * extSz_num; + } + return totalSz; +} +#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */ /* Add certificate data and empty extension to output up to the fragment size. * * ssl SSL/TLS object. @@ -8412,10 +8481,11 @@ static word32 NextCert(byte* data, word32 length, word32* idx) * idx The start of the certificate data to write out. * fragSz The maximum size of this fragment. * output The buffer to write to. + * extIdx The index number of the extension data with the certificate * returns the number of bytes written. */ static word32 AddCertExt(WOLFSSL* ssl, byte* cert, word32 len, word16 extSz, - word32 idx, word32 fragSz, byte* output) + word32 idx, word32 fragSz, byte* output, word16 extIdx) { word32 i = 0; word32 copySz = min(len - idx, fragSz); @@ -8436,7 +8506,7 @@ static word32 AddCertExt(WOLFSSL* ssl, byte* cert, word32 len, word16 extSz, } } else { - byte* certExts = ssl->buffers.certExts->buffer + idx + i - len; + byte* certExts = ssl->buffers.certExts[extIdx]->buffer + idx + i - len; /* Put out as much of the extensions' data as will fit in fragment. */ if (copySz > fragSz - i) copySz = fragSz - i; @@ -8458,13 +8528,16 @@ static int SendTls13Certificate(WOLFSSL* ssl) { int ret = 0; word32 certSz, certChainSz, headerSz, listSz, payloadSz; - word16 extSz = 0; + word16 extSz[1 + MAX_CERT_EXTENSIONS]; + word16 extIdx = 0; word32 length, maxFragment; + word32 totalextSz = 0; word32 len = 0; word32 idx = 0; word32 offset = OPAQUE16_LEN; byte* p = NULL; byte certReqCtxLen = 0; + #ifdef WOLFSSL_POST_HANDSHAKE_AUTH byte* certReqCtx = NULL; #endif @@ -8519,35 +8592,45 @@ static int SendTls13Certificate(WOLFSSL* ssl) /* Cert Req Ctx Len | Cert Req Ctx | Cert List Len | Cert Data Len */ headerSz = OPAQUE8_LEN + certReqCtxLen + CERT_HEADER_SZ + CERT_HEADER_SZ; + /* set empty extension as default */ + for (extIdx = 0; extIdx < XELEM_CNT(extSz); extIdx++) + extSz[extIdx] = OPAQUE16_LEN; - ret = TLSX_GetResponseSize(ssl, certificate, &extSz); - if (ret < 0) - return ret; - - /* Create extensions' data if none already present. */ - if (extSz > OPAQUE16_LEN && ssl->buffers.certExts == NULL) { - ret = AllocDer(&ssl->buffers.certExts, extSz, CERT_TYPE, ssl->heap); - if (ret < 0) - return ret; + length = 0; + listSz = 0; - extSz = 0; - ret = TLSX_WriteResponse(ssl, ssl->buffers.certExts->buffer, - certificate, &extSz); + #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + /* We only send CSR on the server side. On client side, the CSR data + * is populated with the server response. We would be sending the server + * its own stapling data. */ + if (ssl->options.side == WOLFSSL_SERVER_END) { + ret = WriteCSRToBuffer(ssl, &ssl->buffers.certExts[0], &extSz[0], + 1 /* +1 for leaf */ + ssl->buffers.certChainCnt); if (ret < 0) return ret; + totalextSz += ret; + ret = 0; /* Clear to signal no error */ + } + else + #endif + { + /* Leaf cert empty extension size */ + totalextSz += OPAQUE16_LEN; + /* chain cert empty extension size */ + totalextSz += OPAQUE16_LEN * ssl->buffers.certChainCnt; } /* Length of message data with one certificate and extensions. */ - length = headerSz + certSz + extSz; + length += headerSz + certSz + totalextSz; /* Length of list data with one certificate and extensions. */ - listSz = CERT_HEADER_SZ + certSz + extSz; + listSz += CERT_HEADER_SZ + certSz + totalextSz; /* Send rest of chain if sending cert (chain has leading size/s). */ if (certSz > 0 && ssl->buffers.certChainCnt > 0) { p = ssl->buffers.certChain->buffer; /* Chain length including extensions. */ - certChainSz = ssl->buffers.certChain->length + - OPAQUE16_LEN * ssl->buffers.certChainCnt; + certChainSz = ssl->buffers.certChain->length; + length += certChainSz; listSz += certChainSz; } @@ -8562,6 +8645,8 @@ static int SendTls13Certificate(WOLFSSL* ssl) maxFragment = (word32)wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE); + extIdx = 0; + while (length > 0 && ret == 0) { byte* output = NULL; word32 fragSz = 0; @@ -8576,15 +8661,15 @@ static int SendTls13Certificate(WOLFSSL* ssl) #endif /* WOLFSSL_DTLS13 */ if (ssl->fragOffset == 0) { - if (headerSz + certSz + extSz + certChainSz <= + if (headerSz + certSz + totalextSz + certChainSz <= maxFragment - HANDSHAKE_HEADER_SZ) { - fragSz = headerSz + certSz + extSz + certChainSz; + fragSz = headerSz + certSz + totalextSz + certChainSz; } #ifdef WOLFSSL_DTLS13 else if (ssl->options.dtls){ /* short-circuit the fragmentation logic here. DTLS fragmentation will be done in dtls13HandshakeSend() */ - fragSz = headerSz + certSz + extSz + certChainSz; + fragSz = headerSz + certSz + totalextSz + certChainSz; } #endif /* WOLFSSL_DTLS13 */ else { @@ -8643,20 +8728,23 @@ static int SendTls13Certificate(WOLFSSL* ssl) else AddTls13RecordHeader(output, fragSz, handshake, ssl); - if (certSz > 0 && ssl->fragOffset < certSz + extSz) { - /* Put in the leaf certificate with extensions. */ - word32 copySz = AddCertExt(ssl, ssl->buffers.certificate->buffer, - certSz, extSz, ssl->fragOffset, fragSz, output + i); - i += copySz; - ssl->fragOffset += copySz; - length -= copySz; - fragSz -= copySz; - if (ssl->fragOffset == certSz + extSz) - FreeDer(&ssl->buffers.certExts); + if (extIdx == 0) { + if (certSz > 0 && ssl->fragOffset < certSz + extSz[0]) { + /* Put in the leaf certificate with extensions. */ + word32 copySz = AddCertExt(ssl, ssl->buffers.certificate->buffer, + certSz, extSz[extIdx], ssl->fragOffset, fragSz, + output + i, extIdx); + i += copySz; + ssl->fragOffset += copySz; + length -= copySz; + fragSz -= copySz; + if (ssl->fragOffset == certSz + extSz[extIdx]) + FreeDer(&ssl->buffers.certExts[extIdx]); + } } if (certChainSz > 0 && fragSz > 0) { - /* Put in the CA certificates with empty extensions. */ - while (fragSz > 0) { + /* Put in the CA certificates with extensions. */ + while (fragSz > 0) { word32 l; if (offset == len + OPAQUE16_LEN) { @@ -8665,19 +8753,27 @@ static int SendTls13Certificate(WOLFSSL* ssl) /* Point to the start of current cert in chain buffer. */ p = ssl->buffers.certChain->buffer + idx; len = NextCert(ssl->buffers.certChain->buffer, - ssl->buffers.certChain->length, &idx); + ssl->buffers.certChain->length, &idx); if (len == 0) break; + if (MAX_CERT_EXTENSIONS > extIdx) + extIdx++; } - - /* Write out certificate and empty extension. */ - l = AddCertExt(ssl, p, len, OPAQUE16_LEN, offset, fragSz, - output + i); + /* Write out certificate and extension. */ + l = AddCertExt(ssl, p, len, extSz[extIdx], offset, fragSz, + output + i, extIdx); i += l; ssl->fragOffset += l; length -= l; fragSz -= l; offset += l; + + if (extIdx != 0 && extIdx < MAX_CERT_EXTENSIONS && + ssl->buffers.certExts[extIdx] != NULL && + offset == len + extSz[extIdx]) + FreeDer(&ssl->buffers.certExts[extIdx]); + /* for next chain cert */ + len += extSz[extIdx] - OPAQUE16_LEN; } } diff --git a/wolfssl/error-ssl.h b/wolfssl/error-ssl.h index 6354f635a8..539ead3a64 100644 --- a/wolfssl/error-ssl.h +++ b/wolfssl/error-ssl.h @@ -100,7 +100,7 @@ enum wolfSSL_ErrorCodes { COOKIE_ERROR = -369, /* dtls cookie error */ SEQUENCE_ERROR = -370, /* dtls sequence error */ SUITES_ERROR = -371, /* suites pointer error */ - + MAX_CERT_EXTENSIONS_ERR = -372, /* max cert extension exceeded */ OUT_OF_ORDER_E = -373, /* out of order message */ BAD_KEA_TYPE_E = -374, /* bad KEA type found */ SANITY_CIPHER_E = -375, /* sanity check on cipher error */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 88188bd1d1..5f60e960cd 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1989,6 +1989,15 @@ enum Misc { #define MAX_CHAIN_DEPTH 9 #endif +/* Max certificate extensions in TLS1.3 */ +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + /* Number of extensions to set each OCSP response */ + #define MAX_CERT_EXTENSIONS (1 + MAX_CHAIN_DEPTH) +#else + /* Only empty extensions */ + #define MAX_CERT_EXTENSIONS 1 +#endif + /* max size of a certificate message payload */ /* assumes MAX_CHAIN_DEPTH number of certificates at 2kb per certificate */ #ifndef MAX_CERTIFICATE_SZ @@ -3231,10 +3240,11 @@ typedef struct { byte options; WOLFSSL* ssl; union { - OcspRequest ocsp; + OcspRequest ocsp[MAX_CERT_EXTENSIONS]; } request; + word16 requests; #ifdef WOLFSSL_TLS13 - buffer response; + buffer responses[MAX_CERT_EXTENSIONS]; #endif } CertificateStatusRequest; @@ -3243,12 +3253,25 @@ WOLFSSL_LOCAL int TLSX_UseCertificateStatusRequest(TLSX** extensions, #ifndef NO_CERTS WOLFSSL_LOCAL int TLSX_CSR_InitRequest(TLSX* extensions, DecodedCert* cert, void* heap); +WOLFSSL_LOCAL int TLSX_CSR_InitRequest_ex(TLSX* extensions, DecodedCert* cert, + void* heap, int idx); #endif WOLFSSL_LOCAL void* TLSX_CSR_GetRequest(TLSX* extensions); WOLFSSL_LOCAL int TLSX_CSR_ForceRequest(WOLFSSL* ssl); +WOLFSSL_LOCAL word16 TLSX_CSR_GetSize_ex(CertificateStatusRequest* csr, + byte isRequest, + int idx); +WOLFSSL_LOCAL int TLSX_CSR_Write_ex(CertificateStatusRequest* csr, byte* output, + byte isRequest, int idx); +WOLFSSL_LOCAL void* TLSX_CSR_GetRequest_ex(TLSX* extensions, int idx); #endif - +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) +WOLFSSL_LOCAL int CreateOcspRequest(WOLFSSL* ssl, OcspRequest* request, + DecodedCert* cert, byte* certData, word32 length, + byte *ctxOwnsRequest); +#endif /** Certificate Status Request v2 - RFC 6961 */ #ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2 @@ -4723,7 +4746,7 @@ typedef struct Buffers { /* chain after self, in DER, with leading size for each cert */ #ifdef WOLFSSL_TLS13 int certChainCnt; - DerBuffer* certExts; + DerBuffer* certExts[MAX_CERT_EXTENSIONS]; #endif #endif #ifdef WOLFSSL_SEND_HRR_COOKIE @@ -5952,6 +5975,9 @@ struct WOLFSSL { char* url; #endif #endif +#if defined(WOLFSSL_TLS13) && defined(HAVE_CERTIFICATE_STATUS_REQUEST) + word32 response_idx; +#endif #endif #ifdef HAVE_NETX NetX_Ctx nxCtx; /* NetX IO Context */