From fa1ccdb56519c1878f0c4f5db9cdcb86bea60b9b Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 4 Aug 2021 14:39:12 -0400 Subject: [PATCH] Check for RFC 8879 certificate compression This commit adds a check for whether the server supports certificate compression (RFC 8879). If it does, then the list of supprted compression methods is output in the server's preference order. --- CHANGELOG.md | 1 + CREDITS.md | 3 +- testssl.sh | 94 ++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e0d9f2..01aded2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ * Headerflag X-XSS-Protection is now labeled as INFO * Client simulation runs in wide mode which is even better readable * Added --reqheader to support custom headers in HTTP requests +* Test for support for RFC 8879 certificate compression ### Features implemented / improvements in 3.0 diff --git a/CREDITS.md b/CREDITS.md index de826d5..2553003 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -23,12 +23,13 @@ Full contribution, see git log. - experimental "eTLS" detection - parallel mass testing! - RFC <--> OpenSSL cipher name space switches for the command line - - better error msg suppression (not fully installed openssl + - better error msg suppression (not fully installed openssl) - GREASE support - Bleichenbacher / ROBOT vulnerability test - several protocol preferences improvements - pwnedkeys.com support - CT support + - RFC 8879, certificate compression - Lots of fixes and improvements ##### Further credits (in alphabetical order) diff --git a/testssl.sh b/testssl.sh index f130d98..19ca2d5 100755 --- a/testssl.sh +++ b/testssl.sh @@ -7510,6 +7510,49 @@ determine_tls_extensions() { return $success } +# Return a list of the certificate compression methods supported (RFC 8879) +determine_cert_compression() { + # 1=zlib, 2=brotli, 3=zstd + local -a supported_compression_methods=("" "false" "false" "false") + local -i i len nr_compression_methods=3 + local len1 len2 methods_to_test method_found method_nr methods_found="" + + # Certificate compression is only supported by TLS 1.3. + [[ $(has_server_protocol "tls1_3") -eq 1 ]] && tm_out "" && return 1 + while true; do + methods_to_test="" + for (( i=1; i <= nr_compression_methods; i++ )); do + ! "${supported_compression_methods[i]}" && methods_to_test+=" ,00,$(printf "%02x" $i)" + done + len=$((2*${#methods_to_test}/7)) + # If there are no more compression methods remaining to be tested, then quit. + [[ $len -eq 0 ]] && break + len1=$(printf "%02x" "$len") + len2=$(printf "%02x" "$((len+1))") + tls_sockets "04" "$TLS13_CIPHER" "all+" "00,1b, 00,$len2, $len1$methods_to_test" + if [[ $? -ne 0 ]]; then + add_proto_offered tls1_3 no + tm_out "" + return 1 + fi + add_proto_offered tls1_3 yes + method_found="$(awk '/Certificate Compression Algorithm: / { print $4 $5 }' "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" + [[ -z "$method_found" ]] && break + [[ -z "$methods_found" ]] && tmpfile_handle ${FUNCNAME[0]}.txt "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" + method_found="${method_found//(//}" + method_found="${method_found//)/}" + method_nr="${method_found%%/*}" + supported_compression_methods[method_nr]=true + methods_found+=" $method_found" + done + if [[ -n "$methods_found" ]]; then + methods_found="${methods_found:1}" + else + methods_found="none" + fi + tm_out "$methods_found" + return 0 +} extract_certificates() { local version="$1" @@ -9500,6 +9543,7 @@ run_server_defaults() { local -a ciphers_to_test certificate_type local -a -i success local cn_nosni cn_sni sans_nosni sans_sni san tls_extensions client_auth_ca + local cert_compression_methods local using_sockets=true "$SSL_NATIVE" && using_sockets=false @@ -9707,6 +9751,9 @@ run_server_defaults() { done determine_tls_extensions + cert_compression_methods="$(determine_cert_compression)" + [[ -n "$cert_compression_methods" ]] && [[ "$cert_compression_methods" != "none" ]] && \ + extract_new_tls_extensions "$TEMPDIR/$NODEIP.determine_cert_compression.txt" if [[ $? -eq 0 ]] && [[ "$OPTIMAL_PROTO" != -ssl2 ]]; then cp "$TEMPDIR/$NODEIP.determine_tls_extensions.txt" $TMPFILE @@ -9847,6 +9894,16 @@ run_server_defaults() { tls_time + jsonID="cert_compression" + if [[ $(has_server_protocol "tls1_3") -eq 0 ]]; then + jsonID="certificate_compression" + pr_bold " Certificate Compression " + outln "$cert_compression_methods" + fileout "$jsonID" "INFO" "$cert_compression_methods" + else + fileout "$jsonID" "INFO" "N/A" + fi + jsonID="clientAuth" pr_bold " Client Authentication " outln "$CLIENT_AUTH" @@ -13137,6 +13194,7 @@ parse_tls_serverhello() { local tls_msg_type tls_content_type tls_protocol tls_protocol2 tls_hello_time local tls_err_level tls_err_descr_no tls_cipher_suite rfc_cipher_suite tls_compression_method local tls_extensions="" extension_type named_curve_str="" named_curve_oid + local cert_compression_method="" cert_compression_method_str="" local -i i j extension_len extn_len tls_extensions_len ocsp_response_len=0 ocsp_response_list_len ocsp_resp_offset local -i certificate_list_len certificate_len cipherlist_len local -i curve_type named_curve @@ -13401,18 +13459,20 @@ parse_tls_serverhello() { [[ $DEBUG -ge 1 ]] && tmpfile_handle ${FUNCNAME[0]}.txt return 1 fi + cert_compression_method="${tls_handshake_ascii:i:4}" + case $cert_compression_method in + 0001) cert_compression_method_str="ZLIB" ;; + 0002) cert_compression_method_str="Brotli" ;; + 0003) cert_compression_method_str="Zstandard" ;; + *) cert_compression_method_str="unrecognized" ;; + esac if [[ $DEBUG -ge 3 ]]; then - tm_out " Certificate Compression Algorithm: ${tls_handshake_ascii:i:4}" - case ${tls_handshake_ascii:i:4} in - 0001) tmln_out " (ZLIB)" ;; - 0002) tmln_out " (Brotli)" ;; - 0003) tmln_out " (Zstandard)" ;; - *) tmln_out ;; - esac + tmln_out " Certificate Compression Algorithm: $cert_compression_method ($cert_compression_method_str)" offset=$((i+4)) tmln_out " Uncompressed certificate length: $(printf "%d" 0x${tls_handshake_ascii:offset:6})" tmln_out fi + tls_extensions+="TLS server extension \"compress_certificate\" (id=27), len=0\n" if [[ "$process_full" =~ all ]] && "$HAS_ZLIB" && [[ "${tls_handshake_ascii:i:4}" == 0001 ]]; then offset=$((i+4)) tls_certificate_ascii_len=2*0x${tls_handshake_ascii:offset:6} @@ -13872,6 +13932,9 @@ parse_tls_serverhello() { esac echo "===============================================================================" >> $TMPFILE fi + if [[ -n "$cert_compression_method" ]]; then + echo "Certificate Compression Algorithm: $cert_compression_method ($cert_compression_method_str)" >> $TMPFILE + fi [[ -n "$tls_extensions" ]] && echo -e "$tls_extensions" >> $TMPFILE if [[ $DEBUG -ge 3 ]]; then @@ -14827,6 +14890,23 @@ prepare_tls_clienthello() { all_extensions+="$extension_supported_point_formats" fi + if [[ "0x$tls_low_byte" -ge 0x04 ]] && [[ ! "$extra_extensions_list" =~ " 001b " ]]; then + # If the response needs to be decrypted, then indicate support + # for ZLIB certificate compression if $OPENSSL can decompress + # the result. If the response does not need to be decrypted, + # then indicate support for all certificate compression methods, + # as the response does not need to be decompressed. + if [[ "$process_full" =~ all ]]; then + if "$HAS_ZLIB"; then + [[ -n "$all_extensions" ]] && all_extensions+="," + all_extensions+="00,1b,00,03,02,00,01" + fi + else + [[ -n "$all_extensions" ]] && all_extensions+="," + all_extensions+="00,1b,00,07,06,00,01,00,02,00,03" + fi + fi + if [[ -n "$extra_extensions" ]]; then [[ -n "$all_extensions" ]] && all_extensions+="," all_extensions+="$extra_extensions"