mirror of
https://github.com/drwetter/testssl.sh.git
synced 2025-01-03 23:39:45 +01:00
Merge pull request #940 from dcooper16/tls13_decrypt_response
Decrypt server's TLSv1.3 response
This commit is contained in:
commit
f53d601325
497
testssl.sh
497
testssl.sh
@ -286,6 +286,9 @@ HAS_PROXY=false
|
||||
HAS_XMPP=false
|
||||
HAS_POSTGRES=false
|
||||
HAS_MYSQL=false
|
||||
HAS_CHACHA20=false
|
||||
HAS_AES128_GCM=false
|
||||
HAS_AES256_GCM=false
|
||||
PORT=443 # unless otherwise auto-determined, see below
|
||||
NODE=""
|
||||
NODEIP=""
|
||||
@ -8742,27 +8745,375 @@ derive-handshake-traffic-keys() {
|
||||
return 0
|
||||
}
|
||||
|
||||
generate-ccm-gcm-keystream() {
|
||||
local icb="$1" icb_msb icb_lsb1
|
||||
local -i i icb_lsb n="$2"
|
||||
|
||||
icb_msb="${icb:0:24}"
|
||||
icb_lsb=0x${icb:24:8}
|
||||
|
||||
for (( i=0; i < n; i=i+1 )); do
|
||||
icb_lsb1="$(printf "%08X" $icb_lsb)"
|
||||
printf "\x${icb_msb:0:2}\x${icb_msb:2:2}\x${icb_msb:4:2}\x${icb_msb:6:2}\x${icb_msb:8:2}\x${icb_msb:10:2}\x${icb_msb:12:2}\x${icb_msb:14:2}\x${icb_msb:16:2}\x${icb_msb:18:2}\x${icb_msb:20:2}\x${icb_msb:22:2}\x${icb_lsb1:0:2}\x${icb_lsb1:2:2}\x${icb_lsb1:4:2}\x${icb_lsb1:6:2}"
|
||||
icb_lsb+=1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# arg1: an OpenSSL ecb cipher (e.g., -aes-128-ecb)
|
||||
# arg2: key
|
||||
# arg3: initial counter value (must be 128 bits)
|
||||
# arg4: ciphertext
|
||||
# See Sections 6.5 and 7.2 of SP 800-38D and Section 6.2 and Appendix A of SP 800-38C
|
||||
ccm-gcm-decrypt() {
|
||||
local cipher="$1"
|
||||
local key="$2"
|
||||
local icb="$3"
|
||||
local ciphertext="$4"
|
||||
local -i i i1 i2 i3 i4
|
||||
local -i ciphertext_len n mod_check
|
||||
local y plaintext=""
|
||||
|
||||
[[ ${#icb} -ne 32 ]] && return 7
|
||||
|
||||
ciphertext_len=${#ciphertext}
|
||||
n=$ciphertext_len/32
|
||||
mod_check=$ciphertext_len%32
|
||||
[[ $mod_check -ne 0 ]] && n+=1
|
||||
y="$(generate-ccm-gcm-keystream "$icb" "$n" | $OPENSSL enc "$cipher" -K "$key" -nopad 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
|
||||
|
||||
# XOR the ciphertext with the keystream ($y). For efficiency, work in blocks of 16 bytes at a time (but with each XOR operation working on
|
||||
# 32 bits.
|
||||
[[ $mod_check -ne 0 ]] && n=$n-1
|
||||
for (( i=0; i < n; i++ )); do
|
||||
i1=32*$i; i2=$i1+8; i3=$i1+16; i4=$i1+24
|
||||
plaintext+="$(printf "%08X%08X%08X%08X" "$((0x${ciphertext:i1:8} ^ 0x${y:i1:8}))" "$((0x${ciphertext:i2:8} ^ 0x${y:i2:8}))" "$((0x${ciphertext:i3:8} ^ 0x${y:i3:8}))" "$((0x${ciphertext:i4:8} ^ 0x${y:i4:8}))")"
|
||||
done
|
||||
# If the length of the ciphertext is not an even multiple of 16 bytes, then handle the final incomplete block.
|
||||
if [[ $mod_check -ne 0 ]]; then
|
||||
i1=32*$n
|
||||
for (( i=0; i < mod_check; i=i+2 )); do
|
||||
plaintext+="$(printf "%02X" "$((0x${ciphertext:i1:2} ^ 0x${y:i1:2}))")"
|
||||
i1+=2
|
||||
done
|
||||
fi
|
||||
tm_out "$plaintext"
|
||||
return 0
|
||||
}
|
||||
|
||||
# See RFC 7539, Section 2.1
|
||||
chacha20_Qround() {
|
||||
local -i a="0x$1"
|
||||
local -i b="0x$2"
|
||||
local -i c="0x$3"
|
||||
local -i d="0x$4"
|
||||
local -i x y
|
||||
|
||||
a=$(((a+b) & 0xffffffff))
|
||||
d=$((d^a))
|
||||
# rotate d left 16 bits
|
||||
x=$((d & 0xffff0000))
|
||||
x=$((x >> 16))
|
||||
y=$((d & 0x0000ffff))
|
||||
y=$((y << 16))
|
||||
d=$((x | y))
|
||||
|
||||
c=$(((c+d) & 0xffffffff))
|
||||
b=$((b^c))
|
||||
# rotate b left 12 bits
|
||||
x=$((b & 0xfff00000))
|
||||
x=$((x >> 20))
|
||||
y=$((b & 0x000fffff))
|
||||
y=$((y << 12))
|
||||
b=$((x | y))
|
||||
|
||||
a=$(((a+b) & 0xffffffff))
|
||||
d=$((d^a))
|
||||
# rotate d left 8 bits
|
||||
x=$((d & 0xff000000))
|
||||
x=$((x >> 24))
|
||||
y=$((d & 0x00ffffff))
|
||||
y=$((y << 8))
|
||||
d=$((x | y))
|
||||
|
||||
c=$(((c+d) & 0xffffffff))
|
||||
b=$((b^c))
|
||||
# rotate b left 7 bits
|
||||
x=$((b & 0xfe000000))
|
||||
x=$((x >> 25))
|
||||
y=$((b & 0x01ffffff))
|
||||
y=$((y << 7))
|
||||
b=$((x | y))
|
||||
|
||||
tm_out "$(printf "%x" $a) $(printf "%x" $b) $(printf "%x" $c) $(printf "%x" $d)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# See RFC 7539, Section 2.3.1
|
||||
chacha20_inner_block() {
|
||||
local s0="$1" s1="$2" s2="$3" s3="$4"
|
||||
local s4="$5" s5="$6" s6="$7" s7="$8"
|
||||
local s8="$9" s9="${10}" s10="${11}" s11="${12}"
|
||||
local s12="${13}" s13="${14}" s14="${15}" s15="${16}"
|
||||
local res
|
||||
|
||||
res="$(chacha20_Qround "$s0" "$s4" "$s8" "$s12")"
|
||||
read s0 s4 s8 s12 <<< "$res"
|
||||
res="$(chacha20_Qround "$s1" "$s5" "$s9" "$s13")"
|
||||
read s1 s5 s9 s13 <<< "$res"
|
||||
res="$(chacha20_Qround "$s2" "$s6" "$s10" "$s14")"
|
||||
read s2 s6 s10 s14 <<< "$res"
|
||||
res="$(chacha20_Qround "$s3" "$s7" "$s11" "$s15")"
|
||||
read s3 s7 s11 s15 <<< "$res"
|
||||
res="$(chacha20_Qround "$s0" "$s5" "$s10" "$s15")"
|
||||
read s0 s5 s10 s15 <<< "$res"
|
||||
res="$(chacha20_Qround "$s1" "$s6" "$s11" "$s12")"
|
||||
read s1 s6 s11 s12 <<< "$res"
|
||||
res="$(chacha20_Qround "$s2" "$s7" "$s8" "$s13")"
|
||||
read s2 s7 s8 s13 <<< "$res"
|
||||
res="$(chacha20_Qround "$s3" "$s4" "$s9" "$s14")"
|
||||
read s3 s4 s9 s14 <<< "$res"
|
||||
|
||||
tm_out "$s0 $s1 $s2 $s3 $s4 $s5 $s6 $s7 $s8 $s9 $s10 $s11 $s12 $s13 $s14 $s15"
|
||||
return 0
|
||||
}
|
||||
|
||||
# See RFC 7539, Sections 2.3 and 2.3.1
|
||||
chacha20_block() {
|
||||
local key="$1"
|
||||
local counter="$2"
|
||||
local nonce="$3"
|
||||
local s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15
|
||||
local ws0 ws1 ws2 ws3 ws4 ws5 ws6 ws7 ws8 ws9 ws10 ws11 ws12 ws13 ws14 ws15
|
||||
local working_state
|
||||
local -i i
|
||||
|
||||
# create the state variable
|
||||
s0="61707865"; s1="3320646e"; s2="79622d32"; s3="6b206574"
|
||||
s4="${key:6:2}${key:4:2}${key:2:2}${key:0:2}"
|
||||
s5="${key:14:2}${key:12:2}${key:10:2}${key:8:2}"
|
||||
s6="${key:22:2}${key:20:2}${key:18:2}${key:16:2}"
|
||||
s7="${key:30:2}${key:28:2}${key:26:2}${key:24:2}"
|
||||
s8="${key:38:2}${key:36:2}${key:34:2}${key:32:2}"
|
||||
s9="${key:46:2}${key:44:2}${key:42:2}${key:40:2}"
|
||||
s10="${key:54:2}${key:52:2}${key:50:2}${key:48:2}"
|
||||
s11="${key:62:2}${key:60:2}${key:58:2}${key:56:2}"
|
||||
s12="$counter"
|
||||
s13="${nonce:6:2}${nonce:4:2}${nonce:2:2}${nonce:0:2}"
|
||||
s14="${nonce:14:2}${nonce:12:2}${nonce:10:2}${nonce:8:2}"
|
||||
s15="${nonce:22:2}${nonce:20:2}${nonce:18:2}${nonce:16:2}"
|
||||
|
||||
# Initialize working_state to state
|
||||
working_state="$s0 $s1 $s2 $s3 $s4 $s5 $s6 $s7 $s8 $s9 $s10 $s11 $s12 $s13 $s14 $s15"
|
||||
|
||||
# compute the 20 rounds (10 calls to inner block function, each of which
|
||||
# performs 8 quarter rounds).
|
||||
for (( i=0 ; i < 10; i++ )); do
|
||||
working_state="$(chacha20_inner_block $working_state)"
|
||||
done
|
||||
read ws0 ws1 ws2 ws3 ws4 ws5 ws6 ws7 ws8 ws9 ws10 ws11 ws12 ws13 ws14 ws15 <<< "$working_state"
|
||||
|
||||
# Add working state to state
|
||||
s0="$(printf "%08X" $(((0x$s0+0x$ws0) & 0xffffffff)))"
|
||||
s1="$(printf "%08X" $(((0x$s1+0x$ws1) & 0xffffffff)))"
|
||||
s2="$(printf "%08X" $(((0x$s2+0x$ws2) & 0xffffffff)))"
|
||||
s3="$(printf "%08X" $(((0x$s3+0x$ws3) & 0xffffffff)))"
|
||||
s4="$(printf "%08X" $(((0x$s4+0x$ws4) & 0xffffffff)))"
|
||||
s5="$(printf "%08X" $(((0x$s5+0x$ws5) & 0xffffffff)))"
|
||||
s6="$(printf "%08X" $(((0x$s6+0x$ws6) & 0xffffffff)))"
|
||||
s7="$(printf "%08X" $(((0x$s7+0x$ws7) & 0xffffffff)))"
|
||||
s8="$(printf "%08X" $(((0x$s8+0x$ws8) & 0xffffffff)))"
|
||||
s9="$(printf "%08X" $(((0x$s9+0x$ws9) & 0xffffffff)))"
|
||||
s10="$(printf "%08X" $(((0x$s10+0x$ws10) & 0xffffffff)))"
|
||||
s11="$(printf "%08X" $(((0x$s11+0x$ws11) & 0xffffffff)))"
|
||||
s12="$(printf "%08X" $(((0x$s12+0x$ws12) & 0xffffffff)))"
|
||||
s13="$(printf "%08X" $(((0x$s13+0x$ws13) & 0xffffffff)))"
|
||||
s14="$(printf "%08X" $(((0x$s14+0x$ws14) & 0xffffffff)))"
|
||||
s15="$(printf "%08X" $(((0x$s15+0x$ws15) & 0xffffffff)))"
|
||||
|
||||
# serialize the state
|
||||
s0="${s0:6:2}${s0:4:2}${s0:2:2}${s0:0:2}"
|
||||
s1="${s1:6:2}${s1:4:2}${s1:2:2}${s1:0:2}"
|
||||
s2="${s2:6:2}${s2:4:2}${s2:2:2}${s2:0:2}"
|
||||
s3="${s3:6:2}${s3:4:2}${s3:2:2}${s3:0:2}"
|
||||
s4="${s4:6:2}${s4:4:2}${s4:2:2}${s4:0:2}"
|
||||
s5="${s5:6:2}${s5:4:2}${s5:2:2}${s5:0:2}"
|
||||
s6="${s6:6:2}${s6:4:2}${s6:2:2}${s6:0:2}"
|
||||
s7="${s7:6:2}${s7:4:2}${s7:2:2}${s7:0:2}"
|
||||
s8="${s8:6:2}${s8:4:2}${s8:2:2}${s8:0:2}"
|
||||
s9="${s9:6:2}${s9:4:2}${s9:2:2}${s9:0:2}"
|
||||
s10="${s10:6:2}${s10:4:2}${s10:2:2}${s10:0:2}"
|
||||
s11="${s11:6:2}${s11:4:2}${s11:2:2}${s11:0:2}"
|
||||
s12="${s12:6:2}${s12:4:2}${s12:2:2}${s12:0:2}"
|
||||
s13="${s13:6:2}${s13:4:2}${s13:2:2}${s13:0:2}"
|
||||
s14="${s14:6:2}${s14:4:2}${s14:2:2}${s14:0:2}"
|
||||
s15="${s15:6:2}${s15:4:2}${s15:2:2}${s15:0:2}"
|
||||
|
||||
tm_out "$s0$s1$s2$s3$s4$s5$s6$s7$s8$s9$s10$s11$s12$s13$s14$s15"
|
||||
return 0
|
||||
}
|
||||
|
||||
# See RFC 7539, Section 2.4
|
||||
chacha20() {
|
||||
local key="$1"
|
||||
local -i counter=1
|
||||
local nonce="$2"
|
||||
local ciphertext="$3"
|
||||
local -i i ciphertext_len num_blocks mod_check
|
||||
local -i i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16
|
||||
local keystream plaintext=""
|
||||
|
||||
ciphertext_len=${#ciphertext}
|
||||
num_blocks=$ciphertext_len/128
|
||||
|
||||
for (( i=0; i < num_blocks; i++)); do
|
||||
i1=128*$i; i2=$i1+8; i3=$i1+16; i4=$i1+24; i5=$i1+32; i6=$i1+40; i7=$i1+48; i8=$i1+56
|
||||
i9=$i1+64; i10=$i1+72; i11=$i1+80; i12=$i1+88; i13=$i1+96; i14=$i1+104; i15=$i1+112; i16=$i1+120
|
||||
keystream="$(chacha20_block "$key" "$(printf "%08X" $counter)" "$nonce")"
|
||||
plaintext+="$(printf "%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X" \
|
||||
"$((0x${ciphertext:i1:8} ^ 0x${keystream:0:8}))" \
|
||||
"$((0x${ciphertext:i2:8} ^ 0x${keystream:8:8}))" \
|
||||
"$((0x${ciphertext:i3:8} ^ 0x${keystream:16:8}))" \
|
||||
"$((0x${ciphertext:i4:8} ^ 0x${keystream:24:8}))" \
|
||||
"$((0x${ciphertext:i5:8} ^ 0x${keystream:32:8}))" \
|
||||
"$((0x${ciphertext:i6:8} ^ 0x${keystream:40:8}))" \
|
||||
"$((0x${ciphertext:i7:8} ^ 0x${keystream:48:8}))" \
|
||||
"$((0x${ciphertext:i8:8} ^ 0x${keystream:56:8}))" \
|
||||
"$((0x${ciphertext:i9:8} ^ 0x${keystream:64:8}))" \
|
||||
"$((0x${ciphertext:i10:8} ^ 0x${keystream:72:8}))" \
|
||||
"$((0x${ciphertext:i11:8} ^ 0x${keystream:80:8}))" \
|
||||
"$((0x${ciphertext:i12:8} ^ 0x${keystream:88:8}))" \
|
||||
"$((0x${ciphertext:i13:8} ^ 0x${keystream:96:8}))" \
|
||||
"$((0x${ciphertext:i14:8} ^ 0x${keystream:104:8}))" \
|
||||
"$((0x${ciphertext:i15:8} ^ 0x${keystream:112:8}))" \
|
||||
"$((0x${ciphertext:i16:8} ^ 0x${keystream:120:8}))")"
|
||||
counter+=1
|
||||
done
|
||||
|
||||
mod_check=$ciphertext_len%128
|
||||
if [[ $mod_check -ne 0 ]]; then
|
||||
keystream="$(chacha20_block "$key" "$(printf "%08X" $counter)" "$nonce")"
|
||||
i1=128*$num_blocks
|
||||
for (( i=0; i < mod_check; i=i+2 )); do
|
||||
plaintext+="$(printf "%02X" "$((0x${ciphertext:i1:2} ^ 0x${keystream:i:2}))")"
|
||||
i1+=2
|
||||
done
|
||||
fi
|
||||
tm_out "$plaintext"
|
||||
return 0
|
||||
}
|
||||
|
||||
# arg1: TLS cipher
|
||||
# arg2: key
|
||||
# arg3: nonce (must be 96 bits in length)
|
||||
# arg4: ciphertext
|
||||
sym-decrypt() {
|
||||
local cipher="$1"
|
||||
local key="$2" nonce="$3"
|
||||
local ciphertext="$4"
|
||||
local ossl_cipher
|
||||
local plaintext
|
||||
local -i ciphertext_len tag_len
|
||||
|
||||
case "$cipher" in
|
||||
*CCM_8*)
|
||||
tag_len=16 ;;
|
||||
*CCM*|*GCM*|*CHACHA20_POLY1305*)
|
||||
tag_len=32 ;;
|
||||
*)
|
||||
return 7 ;;
|
||||
esac
|
||||
|
||||
# The final $tag_len characters of the ciphertext are the authentication tag
|
||||
ciphertext_len=${#ciphertext}
|
||||
[[ $ciphertext_len -lt $tag_len ]] && return 7
|
||||
ciphertext_len=$ciphertext_len-$tag_len
|
||||
|
||||
if [[ "$cipher" =~ CHACHA20_POLY1305 ]]; then
|
||||
if "$HAS_CHACHA20"; then
|
||||
plaintext="$(asciihex_to_binary_file "${ciphertext:0:ciphertext_len}" "/dev/stdout" | \
|
||||
$OPENSSL enc -chacha20 -K "$key" -iv "01000000$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
|
||||
plaintext="$(strip_spaces "$plaintext")"
|
||||
else
|
||||
plaintext="$(chacha20 "$key" "$nonce" "${ciphertext:0:ciphertext_len}")"
|
||||
fi
|
||||
elif [[ "$cipher" == "TLS_AES_128_GCM_SHA256" ]] && "$HAS_AES128_GCM"; then
|
||||
plaintext="$(asciihex_to_binary_file "${ciphertext:0:ciphertext_len}" "/dev/stdout" | \
|
||||
$OPENSSL enc -aes-128-gcm -K "$key" -iv "$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
|
||||
plaintext="$(strip_spaces "$plaintext")"
|
||||
elif [[ "$cipher" == "TLS_AES_256_GCM_SHA384" ]] && "$HAS_AES256_GCM"; then
|
||||
plaintext="$(asciihex_to_binary_file "${ciphertext:0:ciphertext_len}" "/dev/stdout" | \
|
||||
$OPENSSL enc -aes-256-gcm -K "$key" -iv "$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
|
||||
plaintext="$(strip_spaces "$plaintext")"
|
||||
else
|
||||
if [[ "$cipher" =~ AES_128 ]]; then
|
||||
ossl_cipher="-aes-128-ecb"
|
||||
elif [[ "$cipher" =~ AES_256 ]]; then
|
||||
ossl_cipher="-aes-256-ecb"
|
||||
else
|
||||
return 7
|
||||
fi
|
||||
if [[ "$cipher" =~ CCM ]]; then
|
||||
plaintext="$(ccm-gcm-decrypt "$ossl_cipher" "$key" "02${nonce}000001" "${ciphertext:0:ciphertext_len}")"
|
||||
else # GCM
|
||||
plaintext="$(ccm-gcm-decrypt "$ossl_cipher" "$key" "${nonce}00000002" "${ciphertext:0:ciphertext_len}")"
|
||||
fi
|
||||
fi
|
||||
[[ $? -ne 0 ]] && return 7
|
||||
|
||||
tm_out "$plaintext"
|
||||
return 0
|
||||
}
|
||||
|
||||
# arg1: iv
|
||||
# arg2: sequence number
|
||||
get-nonce() {
|
||||
local iv="$1"
|
||||
local -i seq_num="$2"
|
||||
local -i len lsb
|
||||
local msb nonce
|
||||
|
||||
len=${#iv}
|
||||
[[ $len -lt 8 ]] && return 7
|
||||
i=$len-8
|
||||
msb="${iv:0:i}"
|
||||
lsb="0x${iv:i:8}"
|
||||
nonce="${msb}$(printf "%08X" "$(($lsb ^ $seq_num))")"
|
||||
tm_out "$nonce"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 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
|
||||
# (b) arg1 contains the entire ServerHello (and appears to contain the entire response);
|
||||
# (c) the entire response is supposed to be parsed; and
|
||||
# (d) the key and IV have not been provided to decrypt the response.
|
||||
# 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").
|
||||
# arg3: TLS cipher for decrypting TLSv1.3 response
|
||||
# arg4: key and IV for decrypting TLSv1.3 response
|
||||
check_tls_serverhellodone() {
|
||||
local tls_hello_ascii="$1"
|
||||
local process_full="$2"
|
||||
local cipher="$3"
|
||||
local key_and_iv="$4"
|
||||
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 tls_serverhello_ascii_len sid_len
|
||||
local -i j offset tls_extensions_len extension_len
|
||||
local tls_content_type tls_protocol tls_handshake_type tls_msg_type extension_type
|
||||
local tls_err_level
|
||||
local key iv
|
||||
local -i seq_num=0 plaintext_len
|
||||
local plaintext decrypted_response=""
|
||||
|
||||
DETECTED_TLS_VERSION=""
|
||||
|
||||
[[ -n "$key_and_iv" ]] && read key iv <<< "$key_and_iv"
|
||||
|
||||
if [[ -z "$tls_hello_ascii" ]]; then
|
||||
return 0 # no server hello received
|
||||
fi
|
||||
@ -8788,6 +9139,7 @@ check_tls_serverhellodone() {
|
||||
if [[ "$tls_content_type" == "16" ]]; then
|
||||
tls_handshake_ascii+="${tls_hello_ascii:i:msg_len}"
|
||||
tls_handshake_ascii_len=${#tls_handshake_ascii}
|
||||
decrypted_response+="$tls_content_type$tls_protocol$(printf "%04X" $(($msg_len/2)))${tls_hello_ascii:i:msg_len}"
|
||||
# the ServerHello MUST be the first handshake message
|
||||
[[ $tls_handshake_ascii_len -ge 2 ]] && [[ "${tls_handshake_ascii:0:2}" != "02" ]] && return 2
|
||||
if [[ $tls_handshake_ascii_len -ge 12 ]]; then
|
||||
@ -8825,12 +9177,35 @@ check_tls_serverhellodone() {
|
||||
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
|
||||
tm_out ""
|
||||
return 0 # The entire ServerHello message has been received (and the rest isn't needed)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
elif [[ "$tls_content_type" == "15" ]]; then # TLS ALERT
|
||||
tls_alert_ascii+="${tls_hello_ascii:i:msg_len}"
|
||||
decrypted_response+="$tls_content_type$tls_protocol$(printf "%04X" $(($msg_len/2)))${tls_hello_ascii:i:msg_len}"
|
||||
elif [[ "$tls_content_type" == "17" ]] && [[ -n "$key_and_iv" ]]; then # encrypted data
|
||||
nonce="$(get-nonce "$iv" "$seq_num")"
|
||||
[[ $? -ne 0 ]] && return 2
|
||||
plaintext="$(sym-decrypt "$cipher" "$key" "$nonce" "${tls_hello_ascii:i:msg_len}")"
|
||||
[[ $? -ne 0 ]] && return 2
|
||||
seq_num+=1
|
||||
|
||||
# Remove zeros from end of plaintext, if any
|
||||
plaintext_len=${#plaintext}-2
|
||||
while [[ "${plaintext:plaintext_len:2}" == "00" ]]; do
|
||||
plaintext_len=$plaintext_len-2
|
||||
done
|
||||
tls_content_type="${plaintext:plaintext_len:2}"
|
||||
decrypted_response+="${tls_content_type}0301$(printf "%04X" $(($plaintext_len/2)))${plaintext:0:plaintext_len}"
|
||||
if [[ "$tls_content_type" == "16" ]]; then
|
||||
tls_handshake_ascii+="${plaintext:0:plaintext_len}"
|
||||
elif [[ "$tls_content_type" == "15" ]]; then
|
||||
tls_alert_ascii+="${plaintext:0:plaintext_len}"
|
||||
else
|
||||
return 2
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
@ -8840,7 +9215,7 @@ check_tls_serverhellodone() {
|
||||
remaining=$tls_alert_ascii_len-$i
|
||||
[[ $remaining -lt 4 ]] && return 1
|
||||
tls_err_level=${tls_alert_ascii:i:2} # 1: warning, 2: fatal
|
||||
[[ $tls_err_level == "02" ]] && DETECTED_TLS_VERSION="" && return 0
|
||||
[[ $tls_err_level == "02" ]] && DETECTED_TLS_VERSION="" && tm_out "" && return 0
|
||||
done
|
||||
|
||||
# If there is a serverHelloDone or Finished, then we are done.
|
||||
@ -8857,14 +9232,15 @@ check_tls_serverhellodone() {
|
||||
|
||||
# For SSLv3 - TLS1.2 look for a ServerHelloDone message.
|
||||
# For TLS 1.3 look for a Finished message.
|
||||
[[ $tls_msg_type == "0E" ]] && return 0
|
||||
[[ $tls_msg_type == "14" ]] && return 0
|
||||
[[ $tls_msg_type == "0E" ]] && tm_out "" && return 0
|
||||
[[ $tls_msg_type == "14" ]] && tm_out "$decrypted_response" && 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 the response is TLSv1.3 and the full response is to be processed, but the
|
||||
# key and IV have not been provided to decrypt the response, 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
|
||||
[[ -z "$key_and_iv" ]] && [[ $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.
|
||||
@ -8931,14 +9307,17 @@ parse_tls_serverhello() {
|
||||
local -i tls_hello_ascii_len tls_handshake_ascii_len tls_alert_ascii_len msg_len
|
||||
local tls_serverhello_ascii="" tls_certificate_ascii=""
|
||||
local tls_serverkeyexchange_ascii="" tls_certificate_status_ascii=""
|
||||
local tls_encryptedextensions_ascii="" tls_revised_certificate_msg=""
|
||||
local -i tls_serverhello_ascii_len=0 tls_certificate_ascii_len=0
|
||||
local -i tls_serverkeyexchange_ascii_len=0 tls_certificate_status_ascii_len=0
|
||||
local -i tls_encryptedextensions_ascii_len=0
|
||||
local added_encrypted_extensions=false
|
||||
local tls_alert_descrip tls_sid_len_hex issuerDN subjectDN CAissuerDN CAsubjectDN
|
||||
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_no tls_cipher_suite rfc_cipher_suite tls_compression_method
|
||||
local tls_extensions="" extension_type named_curve_str="" named_curve_oid
|
||||
local -i i j extension_len tls_extensions_len ocsp_response_len=0 ocsp_response_list_len ocsp_resp_offset
|
||||
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
|
||||
local -i dh_bits=0 msb mask
|
||||
@ -9149,6 +9528,14 @@ parse_tls_serverhello() {
|
||||
fi
|
||||
tls_serverhello_ascii="${tls_handshake_ascii:i:msg_len}"
|
||||
tls_serverhello_ascii_len=$msg_len
|
||||
elif [[ "$process_full" == "all" ]] && [[ "$tls_msg_type" == "08" ]]; then
|
||||
# Add excrypted extensions (now decrypted) to end of extensions in SeverHello
|
||||
tls_encryptedextensions_ascii="${tls_handshake_ascii:i:msg_len}"
|
||||
tls_encryptedextensions_ascii_len=$msg_len
|
||||
if [[ $msg_len -lt 2 ]]; then
|
||||
debugme tmln_warning "Response contained a malformed encrypted extensions message"
|
||||
return 1
|
||||
fi
|
||||
elif [[ "$process_full" == "all" ]] && [[ "$tls_msg_type" == "0B" ]]; then
|
||||
if [[ -n "$tls_certificate_ascii" ]]; then
|
||||
debugme tmln_warning "Response contained more than one Certificate handshake message."
|
||||
@ -9450,6 +9837,74 @@ parse_tls_serverhello() {
|
||||
FF01) tls_extensions+="TLS server extension \"renegotiation info\" (id=65281), len=$extension_len\n" ;;
|
||||
*) tls_extensions+="TLS server extension \"unrecognized extension\" (id=$(printf "%d\n\n" "0x$extension_type")), len=$extension_len\n" ;;
|
||||
esac
|
||||
# After processing all of the extensions in the ServerHello message,
|
||||
# if it has been determined that the response is TLSv1.3 and the
|
||||
# response was decrypted, then modify $tls_serverhello_ascii by adding
|
||||
# the extensions from the EncryptedExtensions and Certificate messages
|
||||
# and then process them.
|
||||
if ! "$added_encrypted_extensions" && [[ "$DETECTED_TLS_VERSION" == "0304" ]] && \
|
||||
[[ $((i+8+extension_len)) -eq $tls_extensions_len ]]; then
|
||||
# Note that the encrypted extensions have been added so that
|
||||
# the aren't added a second time.
|
||||
added_encrypted_extensions=true
|
||||
if [[ -n "$tls_encryptedextensions_ascii" ]]; then
|
||||
tls_serverhello_ascii_len+=$tls_encryptedextensions_ascii_len-4
|
||||
tls_extensions_len+=$tls_encryptedextensions_ascii_len-4
|
||||
tls_encryptedextensions_ascii_len=$tls_encryptedextensions_ascii_len/2-2
|
||||
let offset=$extns_offset+4
|
||||
tls_serverhello_ascii="${tls_serverhello_ascii:0:extns_offset}$(printf "%04X" $((0x${tls_serverhello_ascii:extns_offset:4}+$tls_encryptedextensions_ascii_len)))${tls_serverhello_ascii:offset}${tls_encryptedextensions_ascii:4}"
|
||||
fi
|
||||
if [[ -n "$tls_certificate_ascii" ]]; then
|
||||
# In TLS 1.3, the Certificate message begins with a zero length certificate_request_context.
|
||||
# In addition, certificate_list is now a list of (certificate, extension) pairs rather than
|
||||
# just certificates. So, extract the extensions and add them to $tls_serverhello_ascii and
|
||||
# create a new $tls_certificate_ascii that only contains a list of certificates.
|
||||
if [[ -n "$tls_certificate_ascii" ]]; then
|
||||
if [[ "${tls_certificate_ascii:0:2}" != "00" ]]; then
|
||||
debugme tmln_warning "Malformed Certificate Handshake message in ServerHello."
|
||||
tmpfile_handle $FUNCNAME.txt
|
||||
return 1
|
||||
fi
|
||||
if [[ $tls_certificate_ascii_len -lt 8 ]]; then
|
||||
debugme tmln_warning "Malformed Certificate Handshake message in ServerHello."
|
||||
tmpfile_handle $FUNCNAME.txt
|
||||
return 1
|
||||
fi
|
||||
certificate_list_len=2*$(hex2dec "${tls_certificate_ascii:2:6}")
|
||||
if [[ $certificate_list_len -ne $tls_certificate_ascii_len-8 ]]; then
|
||||
debugme tmln_warning "Malformed Certificate Handshake message in ServerHello."
|
||||
tmpfile_handle $FUNCNAME.txt
|
||||
return 1
|
||||
fi
|
||||
|
||||
for (( j=8; j < tls_certificate_ascii_len; j=j+extn_len )); do
|
||||
if [[ $tls_certificate_ascii_len-$j -lt 6 ]]; then
|
||||
debugme tmln_warning "Malformed Certificate Handshake message in ServerHello."
|
||||
tmpfile_handle $FUNCNAME.txt
|
||||
return 1
|
||||
fi
|
||||
certificate_len=2*$(hex2dec "${tls_certificate_ascii:j:6}")
|
||||
if [[ $certificate_len -gt $tls_certificate_ascii_len-$j-6 ]]; then
|
||||
debugme tmln_warning "Malformed Certificate Handshake message in ServerHello."
|
||||
tmpfile_handle $FUNCNAME.txt
|
||||
return 1
|
||||
fi
|
||||
len1=$certificate_len+6
|
||||
tls_revised_certificate_msg+="${tls_certificate_ascii:j:len1}"
|
||||
j+=$len1
|
||||
extn_len=2*$(hex2dec "${tls_certificate_ascii:j:4}")
|
||||
j+=4
|
||||
# TODO: Should only the extensions associated with the EE certificate be added to $tls_serverhello_ascii?
|
||||
tls_serverhello_ascii_len+=$extn_len
|
||||
tls_extensions_len+=$extn_len
|
||||
let offset=$extns_offset+4
|
||||
tls_serverhello_ascii="${tls_serverhello_ascii:0:extns_offset}$(printf "%04X" $((0x${tls_serverhello_ascii:extns_offset:4}+$extn_len/2)))${tls_serverhello_ascii:offset}${tls_certificate_ascii:j:extn_len}"
|
||||
done
|
||||
tls_certificate_ascii_len=${#tls_revised_certificate_msg}+6
|
||||
tls_certificate_ascii="$(printf "%06X" $(($tls_certificate_ascii_len/2-3)))$tls_revised_certificate_msg"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
@ -10602,7 +11057,7 @@ tls_sockets() {
|
||||
local process_full="$3" offer_compression=false skip=false
|
||||
local close_connection=true
|
||||
local -i hello_done=0
|
||||
local cipher="" key_and_iv=""
|
||||
local cipher="" key_and_iv="" decrypted_response
|
||||
|
||||
[[ "$5" == "true" ]] && offer_compression=true
|
||||
[[ "$6" == "false" ]] && close_connection=false
|
||||
@ -10690,9 +11145,11 @@ tls_sockets() {
|
||||
fi
|
||||
skip=false
|
||||
if [[ $hello_done -eq 1 ]]; then
|
||||
check_tls_serverhellodone "$tls_hello_ascii" "$process_full"
|
||||
decrypted_response="$(check_tls_serverhellodone "$tls_hello_ascii" "$process_full" "$cipher" "$key_and_iv")"
|
||||
hello_done=$?
|
||||
[[ "$hello_done" -eq 0 ]] && [[ -n "$decrypted_response" ]] && tls_hello_ascii="$(toupper "$decrypted_response")"
|
||||
if [[ "$hello_done" -eq 3 ]]; then
|
||||
hello_done=1; skip=true
|
||||
debugme echo "reading server hello..."
|
||||
parse_tls_serverhello "$tls_hello_ascii" "ephemeralkey"
|
||||
ret=$?
|
||||
@ -10707,13 +11164,6 @@ tls_sockets() {
|
||||
else
|
||||
hello_done=2
|
||||
fi
|
||||
# The following three lines are temporary until the code
|
||||
# to decrypt TLSv1.3 responses has been added, at which point
|
||||
# parse_tls_serverhello() will be called with process_full="all"
|
||||
# and parse_tls_serverhello() will populate these files.
|
||||
process_full="ephemeralkey"
|
||||
[[ -e "$HOSTCERT" ]] && rm "$HOSTCERT"
|
||||
[[ -e "$TEMPDIR/intermediatecerts.pem" ]] && rm "$TEMPDIR/intermediatecerts.pem"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@ -13609,6 +14059,15 @@ find_openssl_binary() {
|
||||
grep -q 'mysql' $s_client_starttls_has && \
|
||||
HAS_MYSQL=true
|
||||
|
||||
$OPENSSL enc -chacha20 -K "12345678901234567890123456789012" -iv "01000000123456789012345678901234" > /dev/null 2> /dev/null <<< "test"
|
||||
[[ $? -eq 0 ]] && HAS_CHACHA20=true
|
||||
|
||||
$OPENSSL enc -aes-128-gcm -K 0123456789abcdef0123456789abcdef -iv 0123456789abcdef01234567 > /dev/null 2> /dev/null <<< "test"
|
||||
[[ $? -eq 0 ]] && HAS_AES128_GCM=true
|
||||
|
||||
$OPENSSL enc -aes-256-gcm -K 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef -iv 0123456789abcdef01234567 > /dev/null 2> /dev/null <<< "test"
|
||||
[[ $? -eq 0 ]] && HAS_AES256_GCM=true
|
||||
|
||||
if [[ "$OPENSSL_TIMEOUT" != "" ]]; then
|
||||
if type -p timeout 2>&1 >/dev/null ; then
|
||||
if ! "$do_mass_testing"; then
|
||||
|
Loading…
Reference in New Issue
Block a user