Extract server's signature algorithm

PR #1519 requested that testssl.sh show the signature algorithm that the server uses during the TLS handshake. In TLS 1.3, this appears in the CertificateVerify message. In TLS 1.2 it appears in the ServerKeyExchange message when the chosen cipher suite uses an ephemeral (DH or ECDH) key, except in the case of cipher suites that provide no authentication. This information is not present in TLS 1.1 and earlier, as the hash algorithm to use in these earlier versions of the protocol is hard coded into the specification.

This commit takes a first step towards being able to show the signature algorithm by extending parse_tls_serverhello() to extract the signature algorithm when it is present. Matching the output produced by OpenSSL, it output two separate lines, the "Peer signature type" (RSA, RSA-PSS, DSA, ECDSA, Ed25519, or Ed448) and the "Peer signing digest" (MD5, SHA1, SHA224, SHA256, SHA384, or SHA512). This will allow the same function to extract the signature algorithm and digest, whether the handshake was performed using "$OPENSSL s_client" or tls_sockets().
This commit is contained in:
David Cooper 2020-05-14 14:42:08 -04:00
parent 2022754b4a
commit df42eeb8b4

View File

@ -12800,6 +12800,7 @@ parse_tls_serverhello() {
local len1 len2 len3 key_bitstring="" pem_certificate
local dh_p dh_param ephemeral_param rfc7919_param
local -i dh_p_len dh_param_len
local peering_signing_digest=0 peer_signature_type=0
DETECTED_TLS_VERSION=""
[[ $DEBUG -ge 1 ]] && echo > $TMPFILE
@ -13033,6 +13034,14 @@ parse_tls_serverhello() {
fi
tls_serverkeyexchange_ascii="${tls_handshake_ascii:i:msg_len}"
tls_serverkeyexchange_ascii_len=$msg_len
elif [[ "$tls_msg_type" == 0F ]]; then
if [[ $msg_len -lt 4 ]]; then
debugme tmln_warning "Response contained malformed certificate_verify message."
return 1
fi
# Extract just the SignatureAndHashAlgorithm from the CertificateVerify message.
peering_signing_digest="${tls_handshake_ascii:i:2}"
peer_signature_type="${tls_handshake_ascii:$((i+2)):2}"
elif [[ "$process_full" =~ all ]] && [[ "$tls_msg_type" == 16 ]]; then
if [[ -n "$tls_certificate_status_ascii" ]]; then
debugme tmln_warning "Response contained more than one certificate_status handshake message."
@ -13811,12 +13820,24 @@ parse_tls_serverhello() {
29) dh_bits=253 ; named_curve_str="X25519" ;;
30) dh_bits=448 ; named_curve_str="X448" ;;
esac
if [[ "$DETECTED_TLS_VERSION" == 0303 ]]; then
# Skip over the public key to get to the SignatureAndHashAlgorithm
# This is TLS 1.2-only, as this field does not appear in earlier versions.
len1=2*$(hex2dec "${tls_serverkeyexchange_ascii:6:2}")
offset=$((len1+8))
if [[ $tls_serverkeyexchange_ascii_len -ge $((offset+4)) ]]; then
# The SignatureAndHashAlgorithm won't be present in an anonymous
# key exhange.
peering_signing_digest="${tls_serverkeyexchange_ascii:offset:2}"
peer_signature_type="${tls_serverkeyexchange_ascii:$((offset+2)):2}"
fi
fi
fi
if [[ $dh_bits -ne 0 ]] && [[ $named_curve -ne 29 ]] && [[ $named_curve -ne 30 ]]; then
[[ $DEBUG -ge 3 ]] && echo -e " dh_bits: ECDH, $named_curve_str, $dh_bits bits\n"
[[ $DEBUG -ge 3 ]] && echo -e " dh_bits: ECDH, $named_curve_str, $dh_bits bits"
echo "Server Temp Key: ECDH, $named_curve_str, $dh_bits bits" >> $TMPFILE
elif [[ $dh_bits -ne 0 ]]; then
[[ $DEBUG -ge 3 ]] && echo -e " dh_bits: $named_curve_str, $dh_bits bits\n"
[[ $DEBUG -ge 3 ]] && echo -e " dh_bits: $named_curve_str, $dh_bits bits"
echo "Server Temp Key: $named_curve_str, $dh_bits bits" >> $TMPFILE
fi
elif [[ $rfc_cipher_suite =~ TLS_DHE_ ]] || [[ $rfc_cipher_suite =~ TLS_DH_anon ]] || \
@ -13875,10 +13896,73 @@ parse_tls_serverhello() {
[[ "$ephemeral_param" != "$rfc7919_param" ]] && named_curve_str=""
fi
[[ $DEBUG -ge 3 ]] && [[ $dh_bits -ne 0 ]] && echo -e " dh_bits: DH,$named_curve_str $dh_bits bits\n"
[[ $DEBUG -ge 3 ]] && [[ $dh_bits -ne 0 ]] && echo -e " dh_bits: DH,$named_curve_str $dh_bits bits"
[[ $dh_bits -ne 0 ]] && echo "Server Temp Key: DH,$named_curve_str $dh_bits bits" >> $TMPFILE
if [[ "$DETECTED_TLS_VERSION" == 0303 ]]; then
# Skip over the public key (P, G, Y) to get to the SignatureAndHashAlgorithm
# This is TLS 1.2-only, as this field does not appear in earlier versions.
offset=$((dh_p_len+4))
if [[ $tls_serverkeyexchange_ascii_len -lt $((offset+4)) ]]; then
debugme echo "Malformed ServerKeyExchange Handshake message in ServerHello."
tmpfile_handle ${FUNCNAME[0]}.txt
return 1
fi
len1=2*$(hex2dec "${tls_serverkeyexchange_ascii:offset:4}")
offset+=$((len1+4))
if [[ $tls_serverkeyexchange_ascii_len -lt $((offset+4)) ]]; then
debugme echo "Malformed ServerKeyExchange Handshake message in ServerHello."
tmpfile_handle ${FUNCNAME[0]}.txt
return 1
fi
len1=2*$(hex2dec "${tls_serverkeyexchange_ascii:offset:4}")
offset+=$((len1+4))
if [[ $tls_serverkeyexchange_ascii_len -ge $((offset+4)) ]]; then
# The SignatureAndHashAlgorithm won't be present in an anonymous
# key exhange.
peering_signing_digest="${tls_serverkeyexchange_ascii:offset:2}"
peer_signature_type="${tls_serverkeyexchange_ascii:$((offset+2)):2}"
fi
fi
fi
fi
if [[ 0x$peering_signing_digest -eq 8 ]] && \
[[ 0x$peer_signature_type -ge 4 ]] && [[ 0x$peer_signature_type -le 11 ]]; then
case $peer_signature_type in
04) peering_signing_digest="SHA256"; peer_signature_type="RSA-PSS" ;;
05) peering_signing_digest="SHA384"; peer_signature_type="RSA-PSS" ;;
06) peering_signing_digest="SHA512"; peer_signature_type="RSA-PSS" ;;
07) peering_signing_digest=""; peer_signature_type="Ed25519" ;;
08) peering_signing_digest=""; peer_signature_type="Ed448" ;;
09) peering_signing_digest="SHA256"; peer_signature_type="RSA-PSS" ;;
0A) peering_signing_digest="SHA384"; peer_signature_type="RSA-PSS" ;;
0B) peering_signing_digest="SHA512"; peer_signature_type="RSA-PSS" ;;
esac
if [[ -n "$peering_signing_digest" ]]; then
echo "Peer signing digest: $peering_signing_digest" >> $TMPFILE
[[ $DEBUG -ge 3 ]] && echo -e " Peer signing digest: $peering_signing_digest"
fi
echo "Peer signature type: $peer_signature_type" >> $TMPFILE
[[ $DEBUG -ge 3 ]] && echo -e " Peer signature type: $peer_signature_type\n"
elif [[ 0x$peering_signing_digest -ge 1 ]] && [[ 0x$peering_signing_digest -le 6 ]] && \
[[ 0x$peer_signature_type -ge 1 ]] && [[ 0x$peer_signature_type -le 3 ]]; then
case $peering_signing_digest in
01) peering_signing_digest="MD5" ;;
02) peering_signing_digest="SHA1" ;;
03) peering_signing_digest="SHA224" ;;
04) peering_signing_digest="SHA256" ;;
05) peering_signing_digest="SHA384" ;;
06) peering_signing_digest="SHA512" ;;
esac
case $peer_signature_type in
01) peer_signature_type="RSA" ;;
02) peer_signature_type="DSA" ;;
03) peer_signature_type="ECDSA" ;;
esac
echo "Peer signing digest: $peering_signing_digest" >> $TMPFILE
[[ $DEBUG -ge 3 ]] && echo -e " Peer signing digest: $peering_signing_digest"
echo "Peer signature type: $peer_signature_type" >> $TMPFILE
[[ $DEBUG -ge 3 ]] && echo -e " Peer signature type: $peer_signature_type\n"
fi
tmpfile_handle ${FUNCNAME[0]}.txt
TLS_SERVER_HELLO="02$(printf "%06x" $(( tls_serverhello_ascii_len/2)) )${tls_serverhello_ascii}"