TLS 1.3 Finished messages
This PR adds processing of the Finished messages in TLS 1.3 handshakes. It also addresses some shellcheck issues. If in debug mode, the HMAC of the transcript hash of the handshake context ($msg_transcript) is computed and compared against the Finished message sent by the server. If the full server response is parsed and the connection with the server is not to be closed when tls_sockets() completes, then the TLS 1.3 handshake is completed by creating the client Finished message and sending it to the server.
This commit is contained in:
parent
3da67437f3
commit
7516c01315
121
testssl.sh
121
testssl.sh
|
@ -10859,6 +10859,31 @@ hmac() {
|
|||
return $ret
|
||||
}
|
||||
|
||||
# arg1: hash function
|
||||
# arg2: key
|
||||
# arg3: transcript
|
||||
# Compute the HMAC of the hash of the transcript
|
||||
hmac-transcript() {
|
||||
local hash_fn="$1"
|
||||
local key="$2" transcript="$3" output
|
||||
local -i ret
|
||||
|
||||
if [[ ! "$OSSL_NAME" =~ LibreSSL ]] && [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]]; then
|
||||
output="$(asciihex_to_binary "$transcript" | \
|
||||
$OPENSSL dgst "$hash_fn" -binary 2>/dev/null | \
|
||||
$OPENSSL mac -macopt digest:"${hash_fn/-/}" -macopt hexkey:"$key" HMAC 2>/dev/null)"
|
||||
ret=$?
|
||||
tm_out "$(toupper "$(strip_lf "$output")")"
|
||||
else
|
||||
output="$(asciihex_to_binary "$transcript" | \
|
||||
$OPENSSL dgst "$hash_fn" -binary 2>/dev/null | \
|
||||
$OPENSSL dgst "$hash_fn" -mac HMAC -macopt hexkey:"$key" 2>/dev/null)"
|
||||
ret=$?
|
||||
tm_out "$(toupper "$(awk '/=/ { print $2 }' <<< "$output")")"
|
||||
fi
|
||||
return $ret
|
||||
}
|
||||
|
||||
# arg1: hash function
|
||||
# arg2: pseudorandom key (PRK)
|
||||
# arg2: info
|
||||
|
@ -10890,7 +10915,7 @@ hkdf-expand() {
|
|||
output+="$ti"
|
||||
tim1="$ti"
|
||||
done
|
||||
out_len=2*$out_len
|
||||
out_len=$((2*out_len))
|
||||
tm_out "${output:0:out_len}"
|
||||
return 0
|
||||
}
|
||||
|
@ -10910,8 +10935,8 @@ hkdf-expand-label() {
|
|||
local hkdflabel_length
|
||||
local -i len
|
||||
|
||||
hkdflabel_length="$(printf "%04X\n" $length)"
|
||||
if [[ "${TLS_SERVER_HELLO:8:2}" == "7F" ]] && [[ 0x${TLS_SERVER_HELLO:10:2} -lt 0x14 ]]; then
|
||||
hkdflabel_length="$(printf "%04X\n" "$length")"
|
||||
if [[ "${TLS_SERVER_HELLO:8:2}" == 7F ]] && [[ 0x${TLS_SERVER_HELLO:10:2} -lt 0x14 ]]; then
|
||||
# "544c5320312e332c20" = "TLS 1.3, "
|
||||
hkdflabel_label="544c5320312e332c20$label"
|
||||
else
|
||||
|
@ -10919,9 +10944,9 @@ hkdf-expand-label() {
|
|||
hkdflabel_label="746c73313320$label"
|
||||
fi
|
||||
len=${#hkdflabel_label}/2
|
||||
hkdflabel_label="$(printf "%02X\n" $len)$hkdflabel_label"
|
||||
hkdflabel_label="$(printf "%02X\n" "$len")$hkdflabel_label"
|
||||
len=${#context}/2
|
||||
hkdflabel_context="$(printf "%02X\n" $len)$context"
|
||||
hkdflabel_context="$(printf "%02X\n" "$len")$context"
|
||||
hkdflabel="$hkdflabel_length$hkdflabel_label$hkdflabel_context"
|
||||
|
||||
hkdf-expand "$hash_fn" "$secret" "$hkdflabel" "$length"
|
||||
|
@ -10977,7 +11002,7 @@ create-initial-transcript() {
|
|||
local clienthello1="$2" hrr="$3" clienthello2="$4" serverhello="$5"
|
||||
local hash_clienthello1 msg_transcript
|
||||
|
||||
if [[ -n "$hrr" ]] && [[ "${serverhello:8:4}" == "7F12" ]]; then
|
||||
if [[ -n "$hrr" ]] && [[ "${serverhello:8:4}" == 7F12 ]]; then
|
||||
msg_transcript="$clienthello1$hrr$clienthello2$serverhello"
|
||||
elif [[ -n "$hrr" ]]; then
|
||||
if [[ "$cipher" == *SHA256 ]]; then
|
||||
|
@ -11036,7 +11061,7 @@ derive-handshake-secret() {
|
|||
# early_secret="$(hmac "$hash_fn" "000...000" "000...000")"
|
||||
case "$hash_fn" in
|
||||
"-sha256") early_secret="33ad0a1c607ec03b09e6cd9893680ce210adf300aa1f2660e1b22e10f170f92a"
|
||||
if [[ "${TLS_SERVER_HELLO:8:2}" == "7F" ]] && [[ 0x${TLS_SERVER_HELLO:10:2} -lt 0x14 ]]; then
|
||||
if [[ "${TLS_SERVER_HELLO:8:2}" == 7F ]] && [[ 0x${TLS_SERVER_HELLO:10:2} -lt 0x14 ]]; then
|
||||
# "6465726976656420736563726574" = "derived secret"
|
||||
# derived_secret="$(derive-secret "$hash_fn" "$early_secret" "6465726976656420736563726574" "")"
|
||||
derived_secret="c1c0c36bf8fb1d1afa949fbd360e71af69a6244a4c2eaef5bbbb6442a7277d2c"
|
||||
|
@ -11047,7 +11072,7 @@ derive-handshake-secret() {
|
|||
fi
|
||||
;;
|
||||
"-sha384") early_secret="7ee8206f5570023e6dc7519eb1073bc4e791ad37b5c382aa10ba18e2357e716971f9362f2c2fe2a76bfd78dfec4ea9b5"
|
||||
if [[ "${TLS_SERVER_HELLO:8:2}" == "7F" ]] && [[ 0x${TLS_SERVER_HELLO:10:2} -lt 0x14 ]]; then
|
||||
if [[ "${TLS_SERVER_HELLO:8:2}" == 7F ]] && [[ 0x${TLS_SERVER_HELLO:10:2} -lt 0x14 ]]; then
|
||||
# "6465726976656420736563726574" = "derived secret"
|
||||
# derived_secret="$(derive-secret "$hash_fn" "$early_secret" "6465726976656420736563726574" "")"
|
||||
derived_secret="54c80fa05ee9e0532ce3db8ddeca37a0365683bcd3b27bdc88d2b9fdc115ca4ebc8edc1f0b72a6a0861e803fc34761ef"
|
||||
|
@ -11060,7 +11085,7 @@ derive-handshake-secret() {
|
|||
esac
|
||||
|
||||
shared_secret="$($OPENSSL pkeyutl -derive -inkey "$priv_file" -peerkey "$pub_file" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
|
||||
rm $pub_file $priv_file
|
||||
rm "$pub_file" "$priv_file"
|
||||
|
||||
# For draft 18 use $early_secret rather than $derived_secret.
|
||||
if [[ "${TLS_SERVER_HELLO:8:4}" == "7F12" ]]; then
|
||||
|
@ -11083,7 +11108,7 @@ derive-handshake-traffic-keys() {
|
|||
local sender="$4"
|
||||
local hash_fn
|
||||
local -i hash_len key_len
|
||||
local handshake_traffic_secret label key iv
|
||||
local handshake_traffic_secret label key iv finished="0000"
|
||||
|
||||
if [[ "$cipher" == *SHA256 ]]; then
|
||||
hash_fn="-sha256"
|
||||
|
@ -11102,7 +11127,7 @@ derive-handshake-traffic-keys() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
if [[ "${TLS_SERVER_HELLO:8:2}" == "7F" ]] && [[ 0x${TLS_SERVER_HELLO:10:2} -lt 0x14 ]]; then
|
||||
if [[ "${TLS_SERVER_HELLO:8:2}" == 7F ]] && [[ 0x${TLS_SERVER_HELLO:10:2} -lt 0x14 ]]; then
|
||||
if [[ "$sender" == server ]]; then
|
||||
# "7365727665722068616e647368616b65207472616666696320736563726574" = "server handshake traffic secret"
|
||||
label="7365727665722068616e647368616b65207472616666696320736563726574"
|
||||
|
@ -11126,7 +11151,12 @@ derive-handshake-traffic-keys() {
|
|||
# "6976" = "iv"
|
||||
iv="$(derive-traffic-key "$hash_fn" "$handshake_traffic_secret" "6976" "12")"
|
||||
[[ $? -ne 0 ]] && return 1
|
||||
tm_out "$key $iv"
|
||||
if [[ $DEBUG -ge 1 ]] || [[ "$sender" == client ]]; then
|
||||
# "66696e6973686564" = "finished"
|
||||
finished="$(derive-traffic-key "$hash_fn" "$handshake_traffic_secret" "66696e6973686564" "$hash_len")"
|
||||
[[ $? -ne 0 ]] && return 1
|
||||
fi
|
||||
tm_out "$key $iv $finished"
|
||||
}
|
||||
|
||||
# See RFC 8439, Section 2.1
|
||||
|
@ -11364,7 +11394,7 @@ u64to8() {
|
|||
local -i v="$1"
|
||||
local p
|
||||
|
||||
p="$(printf "%016X" $v)"
|
||||
p="$(printf "%016X" "$v")"
|
||||
tm_out "${p:14:2}${p:12:2}${p:10:2}${p:8:2}${p:6:2}${p:4:2}${p:2:2}${p:0:2}"
|
||||
return 0
|
||||
}
|
||||
|
@ -12163,9 +12193,9 @@ check_tls_serverhellodone() {
|
|||
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_content_type tls_protocol tls_msg_type extension_type
|
||||
local tls_err_level
|
||||
local hash_fn handshake_traffic_keys key="" iv=""
|
||||
local hash_fn handshake_traffic_keys key="" iv="" finished_key=""
|
||||
local -i seq_num=0 plaintext_len
|
||||
local plaintext decrypted_response="" additional_data
|
||||
local include_headers=true
|
||||
|
@ -12174,7 +12204,7 @@ check_tls_serverhellodone() {
|
|||
|
||||
if [[ -n "$handshake_secret" ]]; then
|
||||
handshake_traffic_keys="$(derive-handshake-traffic-keys "$cipher" "$handshake_secret" "$msg_transcript" "server")"
|
||||
read -r key iv <<< "$handshake_traffic_keys"
|
||||
read -r key iv finished_key <<< "$handshake_traffic_keys"
|
||||
fi
|
||||
|
||||
if [[ -z "$tls_hello_ascii" ]]; then
|
||||
|
@ -12302,6 +12332,20 @@ check_tls_serverhellodone() {
|
|||
# The ServerHello has already been added to $msg_transcript,
|
||||
# but all other handshake messages need to be added.
|
||||
if [[ -n "$key" ]] && [[ "$tls_msg_type" != 02 ]]; then
|
||||
if [[ $DEBUG -ge 1 ]] && [[ "$tls_msg_type" == 14 ]]; then
|
||||
# Check the Finished message
|
||||
if [[ "$cipher" == *SHA256 ]]; then
|
||||
hash_fn="-sha256"
|
||||
[[ $msg_len -eq 64 ]] || return 2
|
||||
elif [[ "$cipher" == *SHA384 ]]; then
|
||||
hash_fn="-sha384"
|
||||
[[ $msg_len -eq 96 ]] || return 2
|
||||
else
|
||||
return 2
|
||||
fi
|
||||
[[ "${tls_handshake_ascii:i:msg_len}" != $(hmac-transcript "$hash_fn" "$finished_key" "$msg_transcript") ]] && \
|
||||
return 2
|
||||
fi
|
||||
msg_transcript+="$tls_msg_type${tls_handshake_ascii:$((i-6)):6}${tls_handshake_ascii:i:msg_len}"
|
||||
fi
|
||||
# For SSLv3 - TLS1.2 look for a ServerHelloDone message.
|
||||
|
@ -14294,10 +14338,11 @@ tls_sockets() {
|
|||
local tls_hello_ascii next_packet
|
||||
local clienthello1 original_clienthello hrr=""
|
||||
local process_full="$3" offer_compression=false skip=false
|
||||
local close_connection=true
|
||||
local -i hello_done=0
|
||||
local cipher="" handshake_secret="" res
|
||||
local initial_msg_transcript msg_transcript
|
||||
local close_connection=true include_headers=true
|
||||
local -i i len tag_len hello_done=0 plaintext_len
|
||||
local cipher="" tls_version handshake_secret="" res plaintext
|
||||
local initial_msg_transcript msg_transcript finished_msg aad="" data=""
|
||||
local handshake_traffic_keys key iv finished_key
|
||||
|
||||
[[ "$5" == true ]] && offer_compression=true
|
||||
[[ "$6" == false ]] && close_connection=false
|
||||
|
@ -14431,6 +14476,42 @@ tls_sockets() {
|
|||
tm_out " ($lines lines returned) "
|
||||
fi
|
||||
|
||||
if ! "$close_connection" && [[ $save == 0 ]] && \
|
||||
[[ -n "$handshake_secret" ]] && [[ "$process_full" == all+ ]]; then
|
||||
tls_version="$DETECTED_TLS_VERSION"
|
||||
if [[ "${TLS_SERVER_HELLO:8:3}" == 7F1 ]]; then
|
||||
tls_version="${TLS_SERVER_HELLO:8:4}"
|
||||
elif [[ "$TLS_SERVER_HELLO" =~ 002B00027F1[0-9A-F] ]]; then
|
||||
tls_version="${BASH_REMATCH:8:4}"
|
||||
fi
|
||||
[[ "${tls_version:0:2}" == 7F ]] && [[ 0x${tls_version:2:2} -lt 25 ]] && include_headers=false
|
||||
|
||||
handshake_traffic_keys="$(derive-handshake-traffic-keys "$cipher" "$handshake_secret" "$initial_msg_transcript" "client")"
|
||||
read -r key iv finished_key <<< "$handshake_traffic_keys"
|
||||
if [[ "$cipher" == *SHA256 ]]; then
|
||||
finished_msg="14000020$(hmac-transcript "-sha256" "$finished_key" "$msg_transcript")"
|
||||
else
|
||||
finished_msg="14000030$(hmac-transcript "-sha384" "$finished_key" "$msg_transcript")"
|
||||
fi
|
||||
[[ "$cipher" =~ CCM_8 ]] && tag_len=8 || tag_len=16
|
||||
aad="170303$(printf "%04X" "$(( ${#finished_msg}/2 + tag_len + 1 ))")"
|
||||
if "$include_headers"; then
|
||||
# The header information was added to additional data in TLSv1.3 draft 25.
|
||||
finished_msg="$(sym-encrypt "$cipher" "$key" "$(get-nonce "$iv" 0)" "${finished_msg}16" "$aad")"
|
||||
else
|
||||
finished_msg="$(sym-encrypt "$cipher" "$key" "$(get-nonce "$iv" 0)" "${finished_msg}16" "")"
|
||||
fi
|
||||
finished_msg="$aad$finished_msg"
|
||||
|
||||
len=${#finished_msg}
|
||||
for (( i=0; i < len; i=i+2 )); do
|
||||
data+=", ${finished_msg:i:2}"
|
||||
done
|
||||
debugme echo -e "\nsending finished..."
|
||||
socksend_clienthello "${data}"
|
||||
sleep $USLEEP_SND
|
||||
fi
|
||||
|
||||
# determine the return value for higher level, so that they can tell what the result is
|
||||
if [[ $save -eq 1 ]] || [[ $lines -eq 1 ]]; then
|
||||
ret=1 # NOT available
|
||||
|
|
Loading…
Reference in New Issue