Initial support for TLSv1.3

This PR adds initial support for TLSv1.3 to tls_sockets() and for run_client_simulation(). It does not change any of the other functions test TLSv1.3. So, with the exception of run_client_simulation(), the functionality added by this PR can only be tested using the --devel option.

This PR does not include the ability to decrypt the encrypted portions of the server's response. So, it does not support functions that need to see such things as the server's certificate, status information, or extensions (other than key share).
This commit is contained in:
David Cooper 2017-10-03 16:10:09 -04:00
parent 833f724689
commit 968b37e5fe

View File

@ -3644,8 +3644,21 @@ client_simulation_sockets() {
tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE")
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
# Check if the response is a HelloRetryRequest.
resend_if_hello_retry_request "$tls_hello_ascii" "$cipher_list_2send" "$4" "$process_full"
ret=$?
if [[ $ret -eq 2 ]]; then
tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE")
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
elif [[ $ret -eq 1 ]] || [[ $ret -eq 6 ]]; then
close_socket
TMPFILE=$SOCK_REPLY_FILE
tmpfile_handle $FUNCNAME.dd
return $ret
fi
if [[ "${tls_hello_ascii:0:1}" != "8" ]]; then
check_tls_serverhellodone "$tls_hello_ascii"
check_tls_serverhellodone "$tls_hello_ascii" "ephemeralkey"
hello_done=$?
fi
@ -3674,7 +3687,7 @@ client_simulation_sockets() {
cat "$sock_reply_file3" >> "$SOCK_REPLY_FILE"
rm "$sock_reply_file3"
check_tls_serverhellodone "$tls_hello_ascii"
check_tls_serverhellodone "$tls_hello_ascii" "ephemeralkey"
hello_done=$?
fi
done
@ -7915,15 +7928,21 @@ parse_sslv2_serverhello() {
return $ret
}
# Return 0 if arg1 contains the entire server response, 1 if it does not, and 2 if the response is malformed.
# Return 3 if the response version is TLS 1.3 and the entire ServerHello has been received, since any remaining
# portion of the response will be encrypted.
# Return:
# 0 if arg1 contains the entire server response.
# 1 if arg1 does not contain the entire server response.
# 2 if the response is malformed.
# 3 if (a) the response version is TLSv1.3;
# (b) arg1 contains the entire ServerHello (and appears to contain the entire response); and
# (c) the entire response is supposed to be parsed
# arg1: ASCII-HEX encoded reply
# arg2: whether to process the full request ("all") or just the basic request plus the ephemeral key if any ("ephemeralkey").
check_tls_serverhellodone() {
local tls_hello_ascii="$1"
local process_full="$2"
local tls_handshake_ascii="" tls_alert_ascii=""
local -i i tls_hello_ascii_len tls_handshake_ascii_len tls_alert_ascii_len
local -i msg_len remaining
local -i msg_len remaining tls_serverhello_ascii_len
local tls_content_type tls_protocol tls_handshake_type tls_msg_type
local tls_err_level
@ -7943,7 +7962,7 @@ check_tls_serverhellodone() {
[[ "$tls_content_type" != "17" ]] && return 2
i=$i+2
tls_protocol="${tls_hello_ascii:i:4}"
[[ -z "$DETECTED_TLS_VERSION" ]] && DETECTED_TLS_VERSION=$tls_protocol
[[ -z "$DETECTED_TLS_VERSION" ]] && DETECTED_TLS_VERSION="$tls_protocol"
[[ "${tls_protocol:0:2}" != "03" ]] && return 2
i=$i+4
msg_len=2*$(hex2dec "${tls_hello_ascii:i:4}")
@ -7958,12 +7977,12 @@ check_tls_serverhellodone() {
[[ $tls_handshake_ascii_len -ge 2 ]] && [[ "${tls_handshake_ascii:0:2}" != "02" ]] && return 2
if [[ $tls_handshake_ascii_len -ge 12 ]]; then
DETECTED_TLS_VERSION="${tls_handshake_ascii:8:4}"
if [[ 0x"$DETECTED_TLS_VERSION" -ge "0x0304" ]]; then
tls_handshake_ascii_len=2*$(hex2dec "${tls_handshake_ascii:2:6}")
if [[ $tls_handshake_ascii_len+8 -gt $remaining ]]; then
return 1 # Not all of the ServerHello message has been received
else
return 3
# A version of {0x7F, xx} represents an implementation of a draft version of TLS 1.3
[[ "${DETECTED_TLS_VERSION:0:2}" == "7F" ]] && DETECTED_TLS_VERSION="0304"
if [[ 0x$DETECTED_TLS_VERSION -ge 0x0304 ]] && [[ "$process_full" == "ephemeralkey" ]]; then
tls_serverhello_ascii_len=2*$(hex2dec "${tls_handshake_ascii:2:6}")
if [[ $tls_handshake_ascii_len -ge $tls_serverhello_ascii_len+8 ]]; then
return 0 # The entire ServerHello message has been received (and the rest isn't needed)
fi
fi
fi
@ -7998,7 +8017,12 @@ check_tls_serverhellodone() {
[[ $tls_msg_type == "0E" ]] && return 0
[[ $tls_msg_type == "14" ]] && return 0
done
# If the response is TLSv1.3 and the full response is to be processed,
# then return 3 if the entire ServerHello has been received.
if [[ "$DETECTED_TLS_VERSION" == "0304" ]] && [[ "$process_full" == "all" ]] && \
[[ $tls_handshake_ascii_len -gt 0 ]]; then
return 3
fi
# If we haven't encoountered a fatal alert or a server hello done,
# then there must be more data to retrieve.
return 1
@ -8024,15 +8048,15 @@ parse_tls_serverhello() {
local -i tls_sid_len offset extns_offset nr_certs=0
local tls_msg_type tls_content_type tls_protocol tls_protocol2 tls_hello_time
local tls_err_level tls_err_descr tls_cipher_suite rfc_cipher_suite tls_compression_method
local tls_extensions="" extension_type named_curve_str=""
local tls_extensions="" extension_type named_curve_str="" named_curve_oid
local -i i j extension_len tls_extensions_len ocsp_response_len ocsp_response_list_len
local -i certificate_list_len certificate_len cipherlist_len
local -i curve_type named_curve
local -i dh_bits=0 msb mask
local tmp_der_certfile tmp_pem_certfile hostcert_issuer="" ocsp_response=""
local key_bitstring=""
local dh_p ephemeral_param rfc7919_param
local -i dh_p_len
local len1 len2 len3 key_bitstring="" tmp_der_key_file
local dh_p dh_param ephemeral_param rfc7919_param
local -i dh_p_len dh_param_len
TLS_TIME=""
DETECTED_TLS_VERSION=""
@ -8125,7 +8149,6 @@ parse_tls_serverhello() {
debugme tm_out " tls_err_descr: 0x${tls_err_descr} / = $(hex2dec ${tls_err_descr})"
case $tls_err_descr in
00) tls_alert_descrip="close notify" ;;
01) tls_alert_descrip="end of early data" ;;
0A) tls_alert_descrip="unexpected message" ;;
14) tls_alert_descrip="bad record mac" ;;
15) tls_alert_descrip="decryption failed" ;;
@ -8215,7 +8238,8 @@ parse_tls_serverhello() {
01) tmln_out " (client_hello)" ;;
02) tmln_out " (server_hello)" ;;
03) tmln_out " (hello_verify_request)" ;;
04) tmln_out " (NewSessionTicket)" ;;
04) tmln_out " (new_session_ticket)" ;;
05) tmln_out " (end_of_early_data)" ;;
06) tmln_out " (hello_retry_request)" ;;
08) tmln_out " (encrypted_extensions)" ;;
0B) tmln_out " (certificate)" ;;
@ -8229,6 +8253,7 @@ parse_tls_serverhello() {
16) tmln_out " (certificate_status)" ;;
17) tmln_out " (supplemental_data)" ;;
18) tmln_out " (key_update)" ;;
FE) tmln_out " (message_hash)" ;;
*) tmln_out ;;
esac
echo " msg_len: $((msg_len/2))"
@ -8302,6 +8327,7 @@ parse_tls_serverhello() {
# byte 37+sid-len: compression method: 00: none, 01: deflate, 64: LZS
# byte 38+39+sid-len: extension length
tls_protocol2="${tls_serverhello_ascii:0:4}"
[[ "${tls_protocol2:0:2}" == "7F" ]] && tls_protocol2="0304"
if [[ "${tls_protocol2:0:2}" != "03" ]]; then
debugme tmln_warning "server_version.major in ServerHello is not 03."
return 1
@ -8406,13 +8432,81 @@ parse_tls_serverhello() {
0018) tls_extensions+="TLS server extension \"token binding\" (id=24), len=$extension_len\n" ;;
0019) tls_extensions+="TLS server extension \"cached info\" (id=25), len=$extension_len\n" ;;
0023) tls_extensions+="TLS server extension \"session ticket\" (id=35), len=$extension_len\n" ;;
0028) tls_extensions+="TLS server extension \"key share\" (id=40), len=$extension_len\n" ;;
0028) tls_extensions+="TLS server extension \"key share\" (id=40), len=$extension_len\n"
if [[ $extension_len -lt 4 ]]; then
debugme tmln_warning "Malformed key share extension."
return 1
fi
let offset=$extns_offset+12+$i
named_curve=$(hex2dec "${tls_serverhello_ascii:offset:4}")
let offset=$extns_offset+16+$i
msg_len=2*"$(hex2dec "${tls_serverhello_ascii:offset:4}")"
if [[ $msg_len -ne $extension_len-8 ]]; then
debugme tmln_warning "Malformed key share extension."
return 1
fi
case $named_curve in
23) dh_bits=256 ; named_curve_str="P-256" ; named_curve_oid="06082a8648ce3d030107" ;;
24) dh_bits=384 ; named_curve_str="P-384" ; named_curve_oid="06052b81040022" ;;
25) dh_bits=521 ; named_curve_str="P-521" ; named_curve_oid="06052b81040023" ;;
29) dh_bits=253 ; named_curve_str="X25519" ;;
30) dh_bits=448 ; named_curve_str="X448" ;;
256) dh_bits=2048 ; named_curve_str="ffdhe2048" ;;
257) dh_bits=3072 ; named_curve_str="ffdhe3072" ;;
258) dh_bits=4096 ; named_curve_str="ffdhe4096" ;;
259) dh_bits=6144 ; named_curve_str="ffdhe6144" ;;
260) dh_bits=8192 ; named_curve_str="ffdhe8192" ;;
*) named_curve_str="" ; named_curve_oid="" ;;
esac
let offset=$extns_offset+20+$i
if [[ $named_curve -eq 29 ]]; then
key_bitstring="302a300506032b656e032100${tls_serverhello_ascii:offset:msg_len}"
elif [[ $named_curve -eq 30 ]]; then
key_bitstring="3042300506032b656f033900${tls_serverhello_ascii:offset:msg_len}"
elif [[ $named_curve -lt 256 ]] && [[ -n "$named_curve_oid" ]]; then
len1="$(printf "%02x" $(($msg_len/2+1)))"
[[ "0x${len1}" -ge "0x80" ]] && len1="81${len1}"
key_bitstring="03${len1}00${tls_serverhello_ascii:offset:msg_len}"
len2="$(printf "%02x" $((${#named_curve_oid}/2+9)))"
len3="$(printf "%02x" $((${#named_curve_oid}/2+${#key_bitstring}/2+11)))"
[[ "0x${len3}" -ge "0x80" ]] && len3="81${len3}"
key_bitstring="30${len3}30${len2}06072a8648ce3d0201${named_curve_oid}${key_bitstring}"
elif [[ "$named_curve_str" =~ "ffdhe" ]] && [[ "${TLS13_KEY_SHARES[named_curve]}" =~ "BEGIN" ]]; then
dh_param="$($OPENSSL pkey -pubout -outform DER <<< "${TLS13_KEY_SHARES[named_curve]}" | hexdump -v -e '16/1 "%02X"')"
# First is the length of the public-key SEQUENCE, and it is always encoded in four bytes (3082xxxx)
# Next is the length of the parameters SEQUENCE, and it is also always encoded in four bytes (3082xxxx)
dh_param_len=8+2*"$(hex2dec "${dh_param:12:4}")"
dh_param="${dh_param:8:dh_param_len}"
if [[ "0x${tls_serverhello_ascii:offset:2}" -ge 0x80 ]]; then
key_bitstring="00${tls_serverhello_ascii:offset:msg_len}"
msg_len+=2
else
key_bitstring="${tls_serverhello_ascii:offset:msg_len}"
fi
len1="$(printf "%04x" $(($msg_len/2)))"
key_bitstring="0282${len1}$key_bitstring"
len1="$(printf "%04x" $((${#key_bitstring}/2+1)))"
key_bitstring="${dh_param}0382${len1}00$key_bitstring"
len1="$(printf "%04x" $((${#key_bitstring}/2)))"
key_bitstring="3082${len1}$key_bitstring"
fi
if [[ -n "$key_bitstring" ]]; then
tmp_der_key_file=$(mktemp $TEMPDIR/pub_key_der.XXXXXX) || return 1
asciihex_to_binary_file "$key_bitstring" "$tmp_der_key_file"
key_bitstring="$($OPENSSL pkey -pubin -in $tmp_der_key_file -inform DER 2>$ERRFILE)"
rm $tmp_der_key_file
fi
;;
0029) tls_extensions+="TLS server extension \"pre-shared key\" (id=41), len=$extension_len\n" ;;
002A) tls_extensions+="TLS server extension \"early data\" (id=42), len=$extension_len\n" ;;
002B) tls_extensions+="TLS server extension \"supported versions\" (id=43), len=$extension_len\n" ;;
002C) tls_extensions+="TLS server extension \"cookie\" (id=44), len=$extension_len\n" ;;
002D) tls_extensions+="TLS server extension \"psk key exchange modes\" (id=45), len=$extension_len\n" ;;
002E) tls_extensions+="TLS server extension \"ticket early data info\" (id=46), len=$extension_len\n" ;;
002F) tls_extensions+="TLS server extension \"certificate authorities\" (id=47), len=$extension_len\n" ;;
0030) tls_extensions+="TLS server extension \"oid filters\" (id=48), len=$extension_len\n" ;;
0031) tls_extensions+="TLS server extension \"post handshake auth\" (id=49), len=$extension_len\n" ;;
3374) tls_extensions+="TLS server extension \"next protocol\" (id=13172), len=$extension_len\n"
local -i protocol_len
echo -n "Protocols advertised by server: " >> $TMPFILE
@ -8457,6 +8551,21 @@ parse_tls_serverhello() {
rfc_cipher_suite="$($OPENSSL ciphers -V 'ALL:COMPLEMENTOFALL' | grep -i " 0x${tls_cipher_suite:0:2},0x${tls_cipher_suite:2:2} " | awk '{ print $3 }')"
fi
echo "Cipher : $rfc_cipher_suite" >> $TMPFILE
if [[ $dh_bits -ne 0 ]]; then
if [[ "$named_curve_str" =~ "ffdhe" ]]; then
echo "Server Temp Key: DH, $named_curve_str, $dh_bits bits" >> $TMPFILE
elif [[ "$named_curve_str" == "X25519" ]] || [[ "$named_curve_str" == "X448" ]]; then
echo "Server Temp Key: $named_curve_str, $dh_bits bits" >> $TMPFILE
else
echo "Server Temp Key: ECDH, $named_curve_str, $dh_bits bits" >> $TMPFILE
fi
fi
if [[ -n "$key_bitstring" ]]; then
echo "$key_bitstring" >> $TMPFILE
[[ "${TLS13_KEY_SHARES[named_curve]}" =~ "BEGIN" ]] && \
echo "${TLS13_KEY_SHARES[named_curve]}" >> $TMPFILE
fi
echo "===============================================================================" >> $TMPFILE
if [[ "0x${tls_protocol2:2:2}" -le "0x03" ]]; then
case $tls_compression_method in
00) echo "Compression: NONE" >> $TMPFILE ;;
@ -8478,7 +8587,21 @@ parse_tls_serverhello() {
echo -n " tls_hello_time: 0x$tls_hello_time "
parse_date "$TLS_TIME" "+%Y-%m-%d %r" "%s"
fi
echo " tls_cipher_suite: 0x$tls_cipher_suite"
echo -n " tls_cipher_suite: 0x$tls_cipher_suite"
if [[ -n "$rfc_cipher_suite" ]]; then
echo " ($rfc_cipher_suite)"
else
echo ""
fi
if [[ $dh_bits -ne 0 ]]; then
if [[ "$named_curve_str" =~ "ffdhe" ]]; then
echo " dh_bits: DH, $named_curve_str, $dh_bits bits"
elif [[ "$named_curve_str" == "X25519" ]] || [[ "$named_curve_str" == "X448" ]]; then
echo " dh_bits: $named_curve_str, $dh_bits bits"
else
echo " dh_bits: ECDH, $named_curve_str, $dh_bits bits"
fi
fi
if [[ "0x${tls_protocol2:2:2}" -le "0x03" ]]; then
echo -n " tls_compression_method: 0x$tls_compression_method "
case $tls_compression_method in
@ -8869,13 +8992,71 @@ sslv2_sockets() {
return $ret
}
# arg1: supported groups extension
# arg2: "all" - process full response (including Certificate and certificate_status handshake messages)
# "ephemeralkey" - extract the server's ephemeral key (if any)
# Given the supported groups extension, create a key_share extension that includes a key share for
# each group listed in the supported groups extension.
generate_key_share_extension() {
local supported_groups
local -i i len supported_groups_len group
local extn_len list_len
local key_share key_shares=""
supported_groups="${1//\\x/}"
[[ "${supported_groups:0:4}" != "000a" ]] && return 1
supported_groups_len=${#supported_groups}
[[ $supported_groups_len -lt 16 ]] && return 1
len=2*$(hex2dec "${supported_groups:4:4}")
[[ $len+8 -ne $supported_groups_len ]] && return 1
len=2*$(hex2dec "${supported_groups:8:4}")
[[ $len+12 -ne $supported_groups_len ]] && return 1
for (( i=12; i<supported_groups_len; i=i+4 )); do
group=$(hex2dec "${supported_groups:i:4}")
# If the Supported groups extensions lists more than one group,
# then don't include the larger key shares in the extension.
[[ $i -gt 12 ]] && [[ $group -gt 256 ]] && continue
# Versions of OpenSSL prior to 1.1.0 cannot perform operations
# with X25519 keys, so don't include the X25519 key share
# if the server's response needs to be decrypted and an
# older version of OpenSSL is being used.
[[ $i -gt 12 ]] && [[ $group -eq 29 ]] && [[ "$2" == "all" ]] && \
[[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != "1.1.0"* ]] && \
[[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != "1.1.1"* ]] && \
continue
# NOTE: The public keys could be extracted from the private keys
# (TLS13_KEY_SHARES) using $OPENSSL, but only OpenSSL 1.1.0 can
# extract the public key from an X25519 private key.
key_share="${TLS13_PUBLIC_KEY_SHARES[group]}"
if [[ ${#key_share} -gt 4 ]]; then
key_shares+=",$key_share"
fi
done
[[ -z "$key_shares" ]] && tm_out "" && return 0
len=${#key_shares}/3
list_len="$(printf "%04x" "$len")"
len+=2
extn_len="$(printf "%04x" "$len")"
tm_out "00,28,${extn_len:0:2},${extn_len:2:2},${list_len:0:2},${list_len:2:2}$key_shares"
return 0
}
# ARG1: TLS version low byte (00: SSLv3, 01: TLS 1.0, 02: TLS 1.1, 03: TLS 1.2)
# ARG2: CIPHER_SUITES string (lowercase, and in the format output by code2network())
# ARG3: (optional) additional request extensions
# ARG4: (optional): "true" if ClientHello should advertise compression methods other than "NULL"
# ARG3: "all" - process full response (including Certificate and certificate_status handshake messages)
# "ephemeralkey" - extract the server's ephemeral key (if any)
# ARG4: (optional) additional request extensions
# ARG5: (optional): "true" if ClientHello should advertise compression methods other than "NULL"
socksend_tls_clienthello() {
local tls_low_byte="$1"
local tls_low_byte="$1" tls_legacy_version="$1"
local process_full="$3"
local tls_word_reclayer="03, 01" # the first TLS version number is the record layer and always 0301 -- except: SSLv3
local servername_hexstr len_servername len_servername_hex
local hexdump_format_str part1 part2
@ -8888,11 +9069,12 @@ socksend_tls_clienthello() {
local extension_signature_algorithms extension_heartbeat
local extension_session_ticket extension_next_protocol extension_padding
local extension_supported_groups="" extension_supported_point_formats=""
local extra_extensions extra_extensions_list=""
local extensions_key_share="" extn_type supported_groups_c2n=""
local extra_extensions extra_extensions_list="" extension_supported_versions=""
local offer_compression=false compression_methods
# TLSv1.3 ClientHello messages MUST specify only the NULL compression method.
[[ "$4" == "true" ]] && [[ "0x$tls_low_byte" -le "0x03" ]] && offer_compression=true
[[ "$5" == "true" ]] && [[ "0x$tls_low_byte" -le "0x03" ]] && offer_compression=true
cipher_suites="$2" # we don't have the leading \x here so string length is two byte less, see next
len_ciph_suites_byte=${#cipher_suites}
@ -8908,34 +9090,39 @@ socksend_tls_clienthello() {
# Add extensions
# Check to see if any ECC cipher suites are included in cipher_suites
for (( i=0; i<len_ciph_suites_byte; i=i+8 )); do
j=$i+4
part1="0x${cipher_suites:$i:2}"
part2="0x${cipher_suites:$j:2}"
if [[ "$part1" == "0xc0" ]]; then
if [[ "$part2" -ge "0x01" ]] && [[ "$part2" -le "0x19" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x23" ]] && [[ "$part2" -le "0x3b" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x48" ]] && [[ "$part2" -le "0x4f" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x5c" ]] && [[ "$part2" -le "0x63" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x70" ]] && [[ "$part2" -le "0x79" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x86" ]] && [[ "$part2" -le "0x8d" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x9a" ]] && [[ "$part2" -le "0x9b" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0xac" ]] && [[ "$part2" -le "0xaf" ]]; then
ecc_cipher_suite_found=true && break
# (not needed for TLSv1.3)
if [[ "0x$tls_low_byte" -le "0x03" ]]; then
for (( i=0; i<len_ciph_suites_byte; i=i+8 )); do
j=$i+4
part1="0x${cipher_suites:$i:2}"
part2="0x${cipher_suites:$j:2}"
if [[ "$part1" == "0xc0" ]]; then
if [[ "$part2" -ge "0x01" ]] && [[ "$part2" -le "0x19" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x23" ]] && [[ "$part2" -le "0x3b" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x48" ]] && [[ "$part2" -le "0x4f" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x5c" ]] && [[ "$part2" -le "0x63" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x70" ]] && [[ "$part2" -le "0x79" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x86" ]] && [[ "$part2" -le "0x8d" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0x9a" ]] && [[ "$part2" -le "0x9b" ]]; then
ecc_cipher_suite_found=true && break
elif [[ "$part2" -ge "0xac" ]] && [[ "$part2" -le "0xaf" ]]; then
ecc_cipher_suite_found=true && break
fi
elif [[ "$part1" == "0xcc" ]]; then
if [[ "$part2" == "0xa8" ]] || [[ "$part2" == "0xa9" ]] || \
[[ "$part2" == "0xac" ]] || [[ "$part2" == "0x13" ]] || \
[[ "$part2" == "0x14" ]]; then
ecc_cipher_suite_found=true && break
fi
fi
elif [[ "$part1" == "0xcc" ]]; then
if [[ "$part2" == "0xa8" ]] || [[ "$part2" == "0xa9" ]] || [[ "$part2" == "0xac" ]] || [[ "$part2" == "0x13" ]] || [[ "$part2" == "0x14" ]]; then
ecc_cipher_suite_found=true && break
fi
fi
done
done
fi
if [[ -n "$SNI" ]]; then
#formatted example for SNI
@ -8954,11 +9141,19 @@ socksend_tls_clienthello() {
len_sni_ext=$(printf "%02x\n" $((len_servername+5)))
fi
extension_signature_algorithms="
00, 0d, # Type: signature_algorithms , see RFC 5246
00, 20, # len
00,1e, 06,01, 06,02, 06,03, 05,01, 05,02, 05,03,
04,01, 04,02, 04,03, 03,01, 03,02, 03,03, 02,01, 02,02, 02,03"
if [[ 0x$tls_low_byte -le 0x03 ]]; then
extension_signature_algorithms="
00, 0d, # Type: signature_algorithms , see RFC 5246
00, 20, 00,1e, # lengths
06,01, 06,02, 06,03, 05,01, 05,02, 05,03, 04,01, 04,02, 04,03,
03,01, 03,02, 03,03, 02,01, 02,02, 02,03"
else
extension_signature_algorithms="
00, 0d, # Type: signature_algorithms , see draft-ietf-tls-tls13
00, 1c, 00, 1a, # lengths
04,03, 05,03, 06,03, 08,04, 08,05, 08,06,
04,01, 05,01, 06,01, 08,07, 08,08, 02,01, 02,03"
fi
extension_heartbeat="
00, 0f, 00, 01, 01"
@ -8978,7 +9173,32 @@ socksend_tls_clienthello() {
00, 18, 00, 09, 00, 0a, 00, 1a, 00, 16, 00, 17, 00, 1d, 00, 08,
00, 06, 00, 07, 00, 14, 00, 15, 00, 04, 00, 05, 00, 12, 00, 13,
00, 01, 00, 02, 00, 03, 00, 0f, 00, 10, 00, 11"
# Supported Point Formats Extension
elif [[ 0x$tls_low_byte -gt 0x03 ]]; then
# Supported Groups Extension
if [[ "$process_full" != "all" ]] || \
[[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == "1.1.0"* ]] || \
[[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == "1.1.1"* ]]; then
extension_supported_groups="
00,0a, # Type: Supported Groups, see draft-ietf-tls-tls13
00,0e, 00,0c, # lengths
00,1d, 00,17, 00,18, 00,19,
01,00, 01,01"
else
# OpenSSL prior to 1.1.0 does not support X25519, so list it as the least
# preferred option if the response needs to be decrypted.
extension_supported_groups="
00,0a, # Type: Supported Groups, see draft-ietf-tls-tls13
00,0e, 00,0c, # lengths
00,17, 00,18, 00,19,
01,00, 01,01, 00,1d"
fi
code2network "$extension_supported_groups"
supported_groups_c2n="$NW_STR"
fi
if "$ecc_cipher_suite_found" || [[ 0x$tls_low_byte -gt 0x03 ]]; then
# Supported Point Formats Extension.
extension_supported_point_formats="
00, 0b, # Type: Supported Point Formats , see RFC 4492
00, 02, # len
@ -8988,16 +9208,25 @@ socksend_tls_clienthello() {
# Each extension should appear in the ClientHello at most once. So,
# find out what extensions were provided as an argument and only use
# the provided values for those extensions.
extra_extensions="$(tolower "$3")"
extra_extensions="$(tolower "$4")"
code2network "$extra_extensions"
len_all=${#extra_extensions}
for (( i=0; i < len_all; i=i+16+4*0x$len_extension_hex )); do
part2=$i+4
extra_extensions_list+=" ${NW_STR:i:2}${NW_STR:part2:2} "
extn_type="${NW_STR:i:2}${NW_STR:part2:2}"
extra_extensions_list+=" $extn_type "
j=$i+8
part2=$j+4
len_extension_hex="${NW_STR:j:2}${NW_STR:part2:2}"
if [[ "$extn_type" == "000a" ]] && [[ 0x$tls_low_byte -gt 0x03 ]]; then
j=14+4*0x$len_extension_hex
supported_groups_c2n="${NW_STR:i:j}"
fi
done
if [[ 0x$tls_low_byte -gt 0x03 ]]; then
extensions_key_share="$(generate_key_share_extension "$supported_groups_c2n" "$process_full")"
[[ $? -ne 0 ]] && return 1
fi
if [[ -n "$SNI" ]] && [[ ! "$extra_extensions_list" =~ " 0000 " ]]; then
all_extensions="
@ -9008,6 +9237,24 @@ socksend_tls_clienthello() {
,00, $len_servername_hex # server_name length. We assume len(hostname) < FF - 9
,$servername_hexstr" # server_name target
fi
if [[ 0x$tls_low_byte -ge 0x04 ]] && [[ ! "$extra_extensions_list" =~ " 002b " ]]; then
# Add supported_versions extension listing all TLS/SSL versions
# from the one specified in $tls_low_byte to SSLv3.
for (( i=0x$tls_low_byte; i >=0; i=i-1 )); do
if [[ 0x$i -eq 4 ]]; then
# FIXME: The ClientHello currently indicates support
# for drafts 18, 19, 20, and 21 of TLSv1.3 in addition
# to the final version of TLSv1.3. In the future, the
# draft versions should be removed.
extension_supported_versions+=", 03, 04, 7f, 15, 7f, 14, 7f, 13, 7f, 12"
else
extension_supported_versions+=", 03, $(printf "%02x" $i)"
fi
done
[[ -n "$all_extensions" ]] && all_extensions+=","
# FIXME: Adjust the lengths ("+11" and "+9") when the draft versions of TLSv1.3 are removed.
all_extensions+="00, 2b, 00, $(printf "%02x" $((2*0x$tls_low_byte+11))), $(printf "%02x" $((2*0x$tls_low_byte+10)))$extension_supported_versions"
fi
if [[ ! "$extra_extensions_list" =~ " 0023 " ]]; then
[[ -n "$all_extensions" ]] && all_extensions+=","
@ -9031,6 +9278,12 @@ socksend_tls_clienthello() {
[[ -n "$all_extensions" ]] && all_extensions+=","
all_extensions+="$extension_supported_groups"
fi
if [[ -n "$extensions_key_share" ]] && [[ ! "$extra_extensions_list" =~ " 0028 " ]]; then
[[ -n "$all_extensions" ]] && all_extensions+=","
all_extensions+="$extensions_key_share"
fi
if [[ -n "$extension_supported_point_formats" ]] && [[ ! "$extra_extensions_list" =~ " 000b " ]]; then
[[ -n "$all_extensions" ]] && all_extensions+=","
all_extensions+="$extension_supported_point_formats"
@ -9105,6 +9358,8 @@ socksend_tls_clienthello() {
# if we have SSLv3, the first occurence of TLS protocol -- record layer -- is SSLv3, otherwise TLS 1.0
[[ $tls_low_byte == "00" ]] && tls_word_reclayer="03, 00"
[[ 0x$tls_legacy_version -ge 0x04 ]] && tls_legacy_version="03"
if "$offer_compression"; then
# See http://www.iana.org/assignments/comp-meth-ids/comp-meth-ids.xhtml#comp-meth-ids-2
compression_methods="03,01,40,00" # Offer NULL, DEFLATE, and LZS compression
@ -9119,7 +9374,7 @@ socksend_tls_clienthello() {
# Handshake header:
,01 # Type (x01 for ClientHello)
,00, $len_client_hello_word # Length ClientHello
,03, $tls_low_byte # TLS version ClientHello
,03, $tls_legacy_version # TLS version ClientHello
,54, 51, 1e, 7a # Unix time since see www.moserware.com/2009/06/first-few-milliseconds-of-https.html
,de, ad, be, ef # Random 28 bytes
,31, 33, 07, 00, 00, 00, 00, 00
@ -9141,6 +9396,172 @@ socksend_tls_clienthello() {
return 0
}
# arg1: The server's response
# arg2: CIPHER_SUITES string (lowercase, and in the format output by code2network())
# arg3: (optional) additional request extensions
# arg4: "all" - process full response (including Certificate and certificate_status handshake messages)
# "ephemeralkey" - extract the server's ephemeral key (if any)
# Return 0 if the response is not a HelloRetryRequest.
# Return 1 if the response is a malformed HelloRetryRequest or if a new ClientHello cannot be sent.
# Return 2 if the response is a HelloRetryRequest, and sending a new ClientHello succeeded.
# Return 6 if the response is a HelloRetryRequest, and sending a new ClientHello failed.
resend_if_hello_retry_request() {
local tls_hello_ascii="$1"
local cipher_list_2send="$2"
local process_full="$4"
local tls_low_byte server_version cipher_suite rfc_cipher_suite
local -i i j msg_len tls_hello_ascii_len
local -i extns_offset hrr_extns_len extra_extensions_len len_extn
local extra_extensions extn_type part2 new_extra_extns="" new_key_share temp
tls_hello_ascii_len=${#tls_hello_ascii}
# A HelloRetryRequest is at least 13 bytes long
[[ $tls_hello_ascii_len -lt 26 ]] && return 0
# A HelloRetryRequest is a handshake message (16) with a major record version of 03.
[[ "${tls_hello_ascii:0:4}" != "1603" ]] && return 0
# The handshake type for hello_retry_request is 06.
[[ "${tls_hello_ascii:10:2}" != "06" ]] && return 0
# This appears to be a HelloRetryRequest messsage.
debugme echo "reading hello retry request... "
if [[ "$DEBUG" -ge 4 ]]; then
hexdump -C $SOCK_REPLY_FILE | head -6
echo
fi
# Check the length of the handshake message
msg_len=2*$(hex2dec "${tls_hello_ascii:6:4}")
if [[ $msg_len -ne $tls_hello_ascii_len-10 ]]; then
debugme echo "malformed HelloRetryRequest"
return 1
fi
# Check the length of the HelloRetryRequest message.
msg_len=2*$(hex2dec "${tls_hello_ascii:12:6}")
if [[ $msg_len -ne $tls_hello_ascii_len-18 ]]; then
debugme echo "malformed HelloRetryRequest"
return 1
fi
server_version="${tls_hello_ascii:18:4}"
if [[ "$server_version" == "0304" ]] || [[ 0x$server_version -ge 0x7f13 ]]; then
# Starting with TLSv1.3 draft 19, a HelloRetryRequest is at least 15 bytes long
[[ $tls_hello_ascii_len -lt 30 ]] && return 0
cipher_suite="${tls_hello_ascii:22:2},${tls_hello_ascii:24:2}"
extns_offset=26
else
extns_offset=22
fi
# Check the length of the extensions.
hrr_extns_len=2*$(hex2dec "${tls_hello_ascii:extns_offset:4}")
if [[ $hrr_extns_len -ne $tls_hello_ascii_len-$extns_offset-4 ]]; then
debugme echo "malformed HelloRetryRequest"
return 1
fi
if [[ "${server_version:0:2}" == "7F" ]]; then
tls_low_byte="04"
else
tls_low_byte="${server_version:2:2}"
fi
if [[ $DEBUG -ge 3 ]]; then
echo "TLS message fragments:"
echo " tls_protocol (reclyr): 0x${tls_hello_ascii:2:4}"
echo " tls_content_type: 0x16 (handshake)"
echo " msg_len: $(hex2dec "${tls_hello_ascii:6:4}")"
echo
echo "TLS handshake message:"
echo " handshake type: 0x06 (hello_retry_request)"
echo " msg_len: $(hex2dec "${tls_hello_ascii:12:6}")"
echo
echo "TLS hello retry request message:"
echo " server version: $server_version"
if [[ "$server_version" == "0304" ]] || [[ 0x$server_version -ge 0x7f13 ]]; then
echo -n " cipher suite: $cipher_suite"
if [[ $TLS_NR_CIPHERS -ne 0 ]]; then
if [[ "${cipher_suite:0:2}" == "00" ]]; then
rfc_cipher_suite="$(show_rfc_style "x${cipher_suite:3:2}")"
else
rfc_cipher_suite="$(show_rfc_style "x${cipher_suite:0:2}${cipher_suite:3:2}")"
fi
else
rfc_cipher_suite="$($OPENSSL ciphers -V 'ALL:COMPLEMENTOFALL' | grep -i " 0x${cipher_suite:0:2},0x${cipher_suite:3:2} " | awk '{ print $3 }')"
fi
if [[ -n "$rfc_cipher_suite" ]]; then
echo " ($rfc_cipher_suite)"
else
echo ""
fi
fi
fi
# Parse HelloRetryRequest extensions
for (( i=extns_offset+4; i < tls_hello_ascii_len; i=i+8+$len_extn )); do
extn_type="${tls_hello_ascii:i:4}"
j=$i+4
len_extn=2*$(hex2dec "${tls_hello_ascii:j:4}")
j+=4
if [[ $len_extn -gt $tls_hello_ascii_len-$j ]]; then
debugme echo "malformed HelloRetryRequest"
return 1
fi
# If the HRR includes a cookie extension, then it needs to be
# included in the next ClientHello.
if [[ "$extn_type" == "002C" ]]; then
j=8+$len_extn
new_extra_extns+="${tls_hello_ascii:i:j}"
fi
# If the HRR includes a key_share extension, then it specifies the
# group to be used in the next ClientHello. So, create a key_share
# extension that specifies this group.
if [[ "$extn_type" == "0028" ]]; then
if [[ $len_extn -ne 4 ]]; then
debugme echo "malformed key share extension in HelloRetryRequest"
return 1
fi
[[ $DEBUG -ge 3 ]] && echo " key share: 0x${tls_hello_ascii:j:4}"
new_key_share="$(generate_key_share_extension "000a00040002${tls_hello_ascii:j:4}" "$process_full")"
new_extra_extns+="${new_key_share//,/}"
fi
done
debugme echo ""
if [[ -n "$new_extra_extns" ]]; then
temp="$new_extra_extns"
extra_extensions_len=${#temp}
new_extra_extns=""
for (( i=0 ; i < extra_extensions_len; i=i+2 )); do
new_extra_extns+=",${temp:i:2}"
done
new_extra_extns="${new_extra_extns:1}"
fi
# Include any extra extensions that were included in the first ClientHello,
# except key_share and cookie.
extra_extensions="$(strip_spaces "$(tolower "$3")")"
extra_extensions_len=${#extra_extensions}
for (( i=0; i < extra_extensions_len; i=i+12+$len_extn )); do
part2=$i+3
extn_type="${extra_extensions:i:2}${extra_extensions:part2:2}"
j=$i+6
part2=$j+3
len_extn=3*$(hex2dec "${extra_extensions:j:2}${extra_extensions:part2:2}")
if [[ "$extn_type" != "0028" ]] && [[ "$extn_type" != "002c" ]]; then
j=12+$len_extn
new_extra_extns+=",${extra_extensions:i:j}"
fi
done
debugme echo -en "\nsending second client hello... "
socksend_tls_clienthello "$tls_low_byte" "$cipher_list_2send" "$process_full" "$new_extra_extns"
if [[ $? -ne 0 ]]; then
debugme echo "stuck on sending: $ret"
return 6
fi
sockread_serverhello 32768
return 2
}
# arg1: TLS version low byte
# (00: SSLv3, 01: TLS 1.0, 02: TLS 1.1, 03: TLS 1.2)
# arg2: (optional) list of cipher suites
@ -9157,8 +9578,9 @@ tls_sockets() {
local tls_low_byte
local cipher_list_2send
local sock_reply_file2 sock_reply_file3
local tls_hello_ascii next_packet hello_done=0
local process_full="$3" offer_compression=false
local tls_hello_ascii next_packet
local process_full="$3" offer_compression=false skip=false
local -i hello_done=0
[[ "$5" == "true" ]] && offer_compression=true
tls_low_byte="$1"
@ -9175,7 +9597,7 @@ tls_sockets() {
cipher_list_2send="$NW_STR"
debugme echo -en "\nsending client hello... "
socksend_tls_clienthello "$tls_low_byte" "$cipher_list_2send" "$4" "$offer_compression"
socksend_tls_clienthello "$tls_low_byte" "$cipher_list_2send" "$process_full" "$4" "$offer_compression"
ret=$? # 6 means opening socket didn't succeed, e.g. timeout
# if sending didn't succeed we don't bother
@ -9186,41 +9608,60 @@ tls_sockets() {
tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE")
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
# The server's response may span more than one packet. So,
# check if response appears to be complete, and if it isn't
# then try to get another packet from the server.
# Check if the response is a HelloRetryRequest.
resend_if_hello_retry_request "$tls_hello_ascii" "$cipher_list_2send" "$4" "$process_full"
ret=$?
if [[ $ret -eq 2 ]]; then
tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE")
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
elif [[ $ret -eq 1 ]] || [[ $ret -eq 6 ]]; then
close_socket
TMPFILE=$SOCK_REPLY_FILE
tmpfile_handle $FUNCNAME.dd
return $ret
fi
# The server's response may span more than one packet. If only the
# first part of the response needs to be processed, this isn't an
# issue. However, if the entire response needs to be processed or
# if the ephemeral key is needed (which comes last for TLS 1.2 and
# below), then we need to check if response appears to be complete,
# and if it isn't then try to get another packet from the server.
if [[ "$process_full" == "all" ]] || [[ "$process_full" == "ephemeralkey" ]]; then
check_tls_serverhellodone "$tls_hello_ascii"
hello_done=$?
[[ "$hello_done" -eq 3 ]] && process_full="ephemeralkey"
hello_done=1; skip=true
fi
for (( 1 ; hello_done==1; 1 )); do
sock_reply_file2=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7
mv "$SOCK_REPLY_FILE" "$sock_reply_file2"
if ! "$skip"; then
sock_reply_file2=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7
mv "$SOCK_REPLY_FILE" "$sock_reply_file2"
debugme echo -n "requesting more server hello data... "
socksend "" $USLEEP_SND
sockread_serverhello 32768
debugme echo -n "requesting more server hello data... "
socksend "" $USLEEP_SND
sockread_serverhello 32768
next_packet=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE")
next_packet="${next_packet%%[!0-9A-F]*}"
if [[ ${#next_packet} -eq 0 ]]; then
# This shouldn't be necessary. However, it protects against
# getting into an infinite loop if the server has nothing
# left to send and check_tls_serverhellodone doesn't
# correctly catch it.
mv "$sock_reply_file2" "$SOCK_REPLY_FILE"
hello_done=0
else
tls_hello_ascii+="$next_packet"
next_packet=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE")
next_packet="${next_packet%%[!0-9A-F]*}"
sock_reply_file3=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7
mv "$SOCK_REPLY_FILE" "$sock_reply_file3"
mv "$sock_reply_file2" "$SOCK_REPLY_FILE"
cat "$sock_reply_file3" >> "$SOCK_REPLY_FILE"
rm "$sock_reply_file3"
if [[ ${#next_packet} -eq 0 ]]; then
# This shouldn't be necessary. However, it protects against
# getting into an infinite loop if the server has nothing
# left to send and check_tls_serverhellodone doesn't
# correctly catch it.
mv "$sock_reply_file2" "$SOCK_REPLY_FILE"
hello_done=0
else
tls_hello_ascii+="$next_packet"
check_tls_serverhellodone "$tls_hello_ascii"
sock_reply_file3=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7
mv "$SOCK_REPLY_FILE" "$sock_reply_file3"
mv "$sock_reply_file2" "$SOCK_REPLY_FILE"
cat "$sock_reply_file3" >> "$SOCK_REPLY_FILE"
rm "$sock_reply_file3"
fi
fi
skip=false
if [[ $hello_done -eq 1 ]]; then
check_tls_serverhellodone "$tls_hello_ascii" "$process_full"
hello_done=$?
[[ "$hello_done" -eq 3 ]] && process_full="ephemeralkey"
fi