diff --git a/SCRIPTS-LIST b/SCRIPTS-LIST index 03f5cf6a8a..e69f99fa7f 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 - exmaple 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/scripts/include.am b/scripts/include.am index f4f925a080..95bd93c8c4 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -27,10 +27,12 @@ endif if BUILD_OCSP_STAPLING dist_noinst_SCRIPTS+= scripts/ocsp-stapling.test +dist_noinst_SCRIPTS+= scripts/ocsp-stapling_tls13.test if !BUILD_OCSP_STAPLING_V2 -testsuite/testsuite.log: scripts/ocsp-stapling.log scripts/ocsp-stapling-with-ca-as-responder.log +testsuite/testsuite.log: scripts/ocsp-stapling.log scripts/ocsp-stapling_tls13.log scripts/ocsp-stapling-with-ca-as-responder.log endif scripts/ocsp-stapling.log: scripts/ocsp.log +scripts/ocsp-stapling_tls13.log: scripts/ocsp-stapling.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 diff --git a/scripts/ocsp-stapling_tls13.test b/scripts/ocsp-stapling_tls13.test new file mode 100755 index 0000000000..5edd7309d4 --- /dev/null +++ b/scripts/ocsp-stapling_tls13.test @@ -0,0 +1,482 @@ +#!/bin/bash + +# ocsp-stapling_tls13.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 + echo 'skipping ocsp-stapling_tls13.test because TLS1.3 is not available.' 1>&2 + exit 77 +fi + +if ./examples/client/client '-#' | fgrep -q -e ' -DWOLFSSL_SNIFFER '; then + echo 'skipping oscp-stapling2.test because WOLFSSL_SNIFFER defined.' + exit 77 +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 0 + 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 $? +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() { + printf '%s\n' "Random Ports Selected: $1 $2 $3 $4" + + printf '%s\n' "#" > $test_cnf + printf '%s\n' "# openssl configuration file for OCSP certificates" >> $test_cnf + printf '%s\n' "#" >> $test_cnf + printf '%s\n' "" >> $test_cnf + printf '%s\n' "# Extensions to add to a certificate request (intermediate1-ca)" >> $test_cnf + printf '%s\n' "[ v3_req1 ]" >> $test_cnf + printf '%s\n' "basicConstraints = CA:false" >> $test_cnf + printf '%s\n' "subjectKeyIdentifier = hash" >> $test_cnf + printf '%s\n' "authorityKeyIdentifier = keyid:always,issuer:always" >> $test_cnf + printf '%s\n' "keyUsage = nonRepudiation, digitalSignature, keyEncipherment" >> $test_cnf + printf '%s\n' "authorityInfoAccess = OCSP;URI:http://127.0.0.1:$1" >> $test_cnf + printf '%s\n' "" >> $test_cnf + printf '%s\n' "# Extensions to add to a certificate request (intermediate2-ca)" >> $test_cnf + printf '%s\n' "[ v3_req2 ]" >> $test_cnf + printf '%s\n' "basicConstraints = CA:false" >> $test_cnf + printf '%s\n' "subjectKeyIdentifier = hash" >> $test_cnf + printf '%s\n' "authorityKeyIdentifier = keyid:always,issuer:always" >> $test_cnf + printf '%s\n' "keyUsage = nonRepudiation, digitalSignature, keyEncipherment" >> $test_cnf + printf '%s\n' "authorityInfoAccess = OCSP;URI:http://127.0.0.1:$2" >> $test_cnf + printf '%s\n' "" >> $test_cnf + printf '%s\n' "# Extensions to add to a certificate request (intermediate3-ca)" >> $test_cnf + printf '%s\n' "[ v3_req3 ]" >> $test_cnf + printf '%s\n' "basicConstraints = CA:false" >> $test_cnf + printf '%s\n' "subjectKeyIdentifier = hash" >> $test_cnf + printf '%s\n' "authorityKeyIdentifier = keyid:always,issuer:always" >> $test_cnf + printf '%s\n' "keyUsage = nonRepudiation, digitalSignature, keyEncipherment" >> $test_cnf + printf '%s\n' "authorityInfoAccess = OCSP;URI:http://127.0.0.1:$3" >> $test_cnf + printf '%s\n' "" >> $test_cnf + printf '%s\n' "# Extensions for a typical CA" >> $test_cnf + printf '%s\n' "[ v3_ca ]" >> $test_cnf + printf '%s\n' "basicConstraints = CA:true" >> $test_cnf + printf '%s\n' "subjectKeyIdentifier = hash" >> $test_cnf + printf '%s\n' "authorityKeyIdentifier = keyid:always,issuer:always" >> $test_cnf + printf '%s\n' "keyUsage = keyCertSign, cRLSign" >> $test_cnf + printf '%s\n' "authorityInfoAccess = OCSP;URI:http://127.0.0.1:$4" >> $test_cnf + printf '%s\n' "" >> $test_cnf + printf '%s\n' "# OCSP extensions." >> $test_cnf + printf '%s\n' "[ v3_ocsp ]" >> $test_cnf + printf '%s\n' "basicConstraints = CA:false" >> $test_cnf + printf '%s\n' "subjectKeyIdentifier = hash" >> $test_cnf + printf '%s\n' "authorityKeyIdentifier = keyid:always,issuer:always" >> $test_cnf + printf '%s\n' "extendedKeyUsage = OCSPSigning" >> $test_cnf + + 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!" +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 PASS ------------------------" +remove_single_rF $ready_file5 +./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 2 failed" && exit 1 +printf '%s\n\n' "Test PASSED!" + +printf '%s\n\n' "------------- TEST CASE 3 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 4 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 5 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 6 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 7 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 8 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!" + +# 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 324ec932cd..57608cf454 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7707,6 +7707,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; @@ -13212,8 +13217,8 @@ 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; @@ -13233,7 +13238,8 @@ static int ProcessCSR(WOLFSSL* ssl, byte* input, word32* inOutIdx, 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; } @@ -13305,6 +13311,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 @@ -14367,6 +14379,40 @@ static int ProcessPeerCertCheckKey(WOLFSSL* ssl, ProcPeerCertArgs* args) return ret; } +#if defined(HAVE_OCSP) && defined(WOLFSSL_TLS13) \ + && defined(HAVE_CERTIFICATE_STATUS_REQUEST) +static int ProcessPeerCersChainOCSPStausCheck(WOLFSSL* ssl) +{ + int ret = 0; + word32 i; + word32 idx = 0; + TLSX* ext = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); + CertificateStatusRequest* csr; + + if (ext == NULL) { + return 0; + } + csr = (CertificateStatusRequest*)ext->data; + + 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; + } + } + } + + return ret; +} + +#endif + #ifdef HAVE_CRL static int ProcessPeerCertsChainCRLCheck(WOLFSSL* ssl, ProcPeerCertArgs* args) { @@ -14649,8 +14695,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); @@ -14843,6 +14892,13 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, } else /* skips OCSP and force CRL check */ #endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ + #if defined(WOLFSSL_TLS13) && defined(HAVE_CERTIFICATE_STATUS_REQUEST) + if (ssl->options.tls1_3) { + ret = TLSX_CSR_InitRequest(ssl->extensions, + args->dCert, ssl->heap); + } + else + #endif if (SSL_CM(ssl)->ocspEnabled && SSL_CM(ssl)->ocspCheckAll) { WOLFSSL_MSG("Doing Non Leaf OCSP check"); @@ -15330,18 +15386,10 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, 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 = ProcessPeerCersChainOCSPStausCheck(ssl); + if (ret < 0) { + WOLFSSL_ERROR_VERBOSE(ret); + goto exit_ppc; } } #endif @@ -23309,7 +23357,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) { int ret; @@ -24175,7 +24223,6 @@ int SendCertificateStatus(WOLFSSL* ssl) if (idx > chain->length) break; - ret = CreateOcspRequest(ssl, request, cert, der.buffer, der.length); if (ret == 0) { diff --git a/src/tls.c b/src/tls.c index 472c6519f3..7a9a1737a3 100644 --- a/src/tls.c +++ b/src/tls.c @@ -3117,51 +3117,74 @@ int TLSX_UseTruncatedHMAC(TLSX** extensions, void* heap) static void TLSX_CSR_Free(CertificateStatusRequest* csr, void* heap) { +#ifdef WOLFSSL_TLS13 + int i; +#endif switch (csr->status_type) { case WOLFSSL_CSR_OCSP: - FreeOcspRequest(&csr->request.ocsp); + #ifndef WOLFSSL_TLS13 + FreeOcspRequest(&csr->request.ocsp[0]); + #else + for(i = 0; i < csr->requests;i++) { + FreeOcspRequest(&csr->request.ocsp[i]); + } + #endif break; } #ifdef WOLFSSL_TLS13 - if (csr->response.buffer != NULL) { - XFREE(csr->response.buffer, csr->ssl->heap, + for(i = 0; i < (1 + MAX_CHAIN_DEPTH);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 && csr->ssl->options.tls1_3) { + if (csr->responses[idx].length != 0) + size = (word16)(OPAQUE8_LEN + OPAQUE24_LEN + + csr->responses[idx].length); + else + size = (word16)OPAQUE16_LEN; + return size; + } +#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; @@ -3182,8 +3205,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); @@ -3208,17 +3231,26 @@ static int TLSX_CSR_Write(CertificateStatusRequest* csr, byte* output, if (!isRequest && csr->ssl->options.tls1_3) { 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); +} + static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte isRequest) { @@ -3273,14 +3305,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; @@ -3311,14 +3343,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; @@ -3383,6 +3422,8 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, #if defined(WOLFSSL_TLS13) if (ssl->options.tls1_3) { + int i = 0; + (void)i; if (ssl->buffers.certificate == NULL) { WOLFSSL_MSG("Certificate buffer not set!"); return BUFFER_ERROR; @@ -3412,20 +3453,71 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, return ret; } 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); + + { + buffer der; + word32 pos = 0; + DerBuffer* chain; + /* use certChain if available, otherwise use peer certificate */ + chain = ssl->buffers.certChain; + if (chain == NULL) { + chain = ssl->buffers.certificate; + } + + if (chain && chain->buffer) { + while (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 + 1]; + if (ret == 0) { + ret = CreateOcspRequest(ssl, request, cert, + der.buffer, der.length); + if (ret == 0 && + request == ssl->ctx->certOcspRequest) { + ssl->ctx->certOcspRequest = NULL; + } + } + if (ret == 0) { + request->ssl = ssl; + ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling, + request, &csr->responses[i + 1], 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); + } } else #endif @@ -3448,18 +3540,30 @@ 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; + #if defined(WOLFSSL_TLS13) + int req_cnt = csr->requests; + #else + int req_cnt = 0; + #endif + int nonceSz = csr->request.ocsp[0].nonceSz; /* preserve nonce */ - XMEMCPY(nonce, csr->request.ocsp.nonce, nonceSz); + XMEMCPY(nonce, csr->request.ocsp[0].nonce, nonceSz); - if ((ret = InitOcspRequest(&csr->request.ocsp, cert, 0, heap)) - != 0) + #if defined(WOLFSSL_TLS13) + if (req_cnt < 1 + MAX_CHAIN_DEPTH) { + #endif + if ((ret = InitOcspRequest(&csr->request.ocsp[req_cnt], + cert, 0, heap)) != 0) return ret; /* restore nonce */ - XMEMCPY(csr->request.ocsp.nonce, nonce, nonceSz); - csr->request.ocsp.nonceSz = nonceSz; + XMEMCPY(csr->request.ocsp[req_cnt].nonce, nonce, nonceSz); + csr->request.ocsp[req_cnt].nonceSz = nonceSz; + #if defined(WOLFSSL_TLS13) + csr->requests++; + } + #endif } break; } @@ -3468,7 +3572,7 @@ int TLSX_CSR_InitRequest(TLSX* extensions, DecodedCert* cert, void* heap) return ret; } -void* TLSX_CSR_GetRequest(TLSX* extensions) +void* TLSX_CSR_GetRequest_ex(TLSX* extensions, int idx) { TLSX* extension = TLSX_Find(extensions, TLSX_STATUS_REQUEST); CertificateStatusRequest* csr = extension ? @@ -3477,13 +3581,24 @@ void* TLSX_CSR_GetRequest(TLSX* extensions) if (csr) { switch (csr->status_type) { case WOLFSSL_CSR_OCSP: - return &csr->request.ocsp; + #if defined(WOLFSSL_TLS13) + return idx < csr->requests ? + &csr->request.ocsp[csr->requests - idx - 1] : NULL; + #else + return idx < 1 ? + &csr->request.ocsp[idx] : NULL; + #endif } } 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); @@ -3494,9 +3609,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); @@ -3514,6 +3629,9 @@ int TLSX_UseCertificateStatusRequest(TLSX** extensions, byte status_type, { CertificateStatusRequest* csr = NULL; int ret = 0; +#if defined(WOLFSSL_TLS13) + int i; +#endif if (!extensions || status_type != WOLFSSL_CSR_OCSP) return BAD_FUNC_ARG; @@ -3524,7 +3642,12 @@ int TLSX_UseCertificateStatusRequest(TLSX** extensions, byte status_type, return MEMORY_E; ForceZero(csr, sizeof(CertificateStatusRequest)); - +#if defined(WOLFSSL_TLS13) + for(i = 0; i < (1 + MAX_CHAIN_DEPTH); i++) { + csr->responses[i].length = 0; + csr->responses[i].buffer = NULL; + } +#endif csr->status_type = status_type; csr->options = options; csr->ssl = ssl; @@ -3541,9 +3664,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 aa2ab160de..dc4271922c 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -8412,10 +8412,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 +8437,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 +8459,23 @@ static int SendTls13Certificate(WOLFSSL* ssl) { int ret = 0; word32 certSz, certChainSz, headerSz, listSz, payloadSz; - word16 extSz = 0; +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + TLSX* ext; + CertificateStatusRequest* csr; + #define MAX_CERT_EXTENSIONS MAX_CHAIN_DEPTH +#else + #define MAX_CERT_EXTENSIONS 1 +#endif + word16 extSz[1 + MAX_CERT_EXTENSIONS] = { OPAQUE16_LEN }; + 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 +8530,101 @@ 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; + XMEMSET(extSz, 0, sizeof(extSz)); + + length = 0; + listSz = 0; - ret = TLSX_GetResponseSize(ssl, certificate, &extSz); + ret = TLSX_GetResponseSize(ssl, certificate, &extSz[0]); 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 (extSz[0] > OPAQUE16_LEN && ssl->buffers.certExts[0] == NULL) { + ret = AllocDer(&ssl->buffers.certExts[0], extSz[0], + CERT_TYPE, ssl->heap); if (ret < 0) return ret; - extSz = 0; - ret = TLSX_WriteResponse(ssl, ssl->buffers.certExts->buffer, - certificate, &extSz); + extSz[0] = 0; + ret = TLSX_WriteResponse(ssl, ssl->buffers.certExts[0]->buffer, + certificate, &extSz[0]); if (ret < 0) return ret; } + /* set empty extension as default */ + for (extIdx = 1; extIdx < (1 + MAX_CERT_EXTENSIONS); extIdx++) + extSz[extIdx] = OPAQUE16_LEN; + + /* certificate extension size */ + totalextSz += extSz[0]; + +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + if (ssl->options.side == WOLFSSL_SERVER_END && + ssl->buffers.certChainCnt > 0) { + ext = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST); + csr = ext ? (CertificateStatusRequest*)ext->data : NULL; + + if (csr) { + word32 ex_offset; + + ex_offset = HELLO_EXT_TYPE_SZ + OPAQUE16_LEN /* extesion type */ + + OPAQUE16_LEN /* extension length */; + for (extIdx = 1; extIdx <(word16) + (1 + ssl->buffers.certChainCnt); extIdx++) { + + extSz[extIdx] = TLSX_CSR_GetSize_ex(csr, 0, extIdx); + if (extSz[extIdx] > OPAQUE16_LEN && + ssl->buffers.certExts[extIdx] == NULL) { + + ret = AllocDer(&ssl->buffers.certExts[extIdx], + extSz[extIdx] + ex_offset, + CERT_TYPE, ssl->heap); + if (ret < 0) + return ret; + + /* write extension type */ + c16toa(ext->type, ssl->buffers.certExts[extIdx]->buffer + + OPAQUE16_LEN); + /* writes extension data length. */ + c16toa(extSz[extIdx], ssl->buffers.certExts[extIdx]->buffer + + HELLO_EXT_TYPE_SZ + OPAQUE16_LEN); + /* write extension data */ + extSz[extIdx] = (word16)TLSX_CSR_Write_ex(csr, + ssl->buffers.certExts[extIdx]->buffer + ex_offset, 0, + extIdx); + /* add extension offset */ + extSz[extIdx] += (word16)ex_offset; + /* extension length */ + c16toa(extSz[extIdx] - OPAQUE16_LEN, + ssl->buffers.certExts[extIdx]->buffer); + } + totalextSz += extSz[extIdx]; + } + } + else { + /* chain cert empty extension size */ + totalextSz += OPAQUE16_LEN * ssl->buffers.certChainCnt; + } + } else if (ssl->options.side == WOLFSSL_CLIENT_END && + ssl->buffers.certChainCnt > 0) +#endif + { + /* 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 +8639,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 +8655,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 +8722,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 +8747,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/internal.h b/wolfssl/internal.h index a119007679..0af3083d84 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3231,11 +3231,16 @@ typedef struct { byte status_type; byte options; WOLFSSL* ssl; +#ifdef WOLFSSL_TLS13 union { - OcspRequest ocsp; + OcspRequest ocsp[1 + MAX_CHAIN_DEPTH]; + } request; + buffer responses[1 + MAX_CHAIN_DEPTH]; + word16 requests; +#else + union { + OcspRequest ocsp[1]; } request; -#ifdef WOLFSSL_TLS13 - buffer response; #endif } CertificateStatusRequest; @@ -3247,7 +3252,15 @@ WOLFSSL_LOCAL int TLSX_CSR_InitRequest(TLSX* extensions, DecodedCert* cert, #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); +WOLFSSL_LOCAL int CreateOcspRequest(WOLFSSL* ssl, OcspRequest* request, + DecodedCert* cert, byte* certData, word32 length); #endif /** Certificate Status Request v2 - RFC 6961 */ @@ -4724,7 +4737,11 @@ typedef struct Buffers { /* chain after self, in DER, with leading size for each cert */ #ifdef WOLFSSL_TLS13 int certChainCnt; - DerBuffer* certExts; + #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) + DerBuffer* certExts[1 + MAX_CHAIN_DEPTH]; + #else + DerBuffer* certExts[1]; + #endif #endif #endif #ifdef WOLFSSL_SEND_HRR_COOKIE @@ -5957,6 +5974,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 */ diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 625618e1a7..e0ee659133 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2630,6 +2630,8 @@ struct OcspRequest { void* ssl; }; +struct WOLFSSL; + WOLFSSL_LOCAL void InitOcspResponse(OcspResponse* resp, OcspEntry* single, CertStatus* status, byte* source, word32 inSz, void* heap); WOLFSSL_LOCAL void FreeOcspResponse(OcspResponse* resp);