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:
David Cooper 2020-01-28 11:15:03 -05:00 committed by GitHub
parent 3da67437f3
commit 7516c01315
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 101 additions and 20 deletions

View File

@ -10859,6 +10859,31 @@ hmac() {
return $ret 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 # arg1: hash function
# arg2: pseudorandom key (PRK) # arg2: pseudorandom key (PRK)
# arg2: info # arg2: info
@ -10890,7 +10915,7 @@ hkdf-expand() {
output+="$ti" output+="$ti"
tim1="$ti" tim1="$ti"
done done
out_len=2*$out_len out_len=$((2*out_len))
tm_out "${output:0:out_len}" tm_out "${output:0:out_len}"
return 0 return 0
} }
@ -10910,8 +10935,8 @@ hkdf-expand-label() {
local hkdflabel_length local hkdflabel_length
local -i len local -i len
hkdflabel_length="$(printf "%04X\n" $length)" hkdflabel_length="$(printf "%04X\n" "$length")"
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
# "544c5320312e332c20" = "TLS 1.3, " # "544c5320312e332c20" = "TLS 1.3, "
hkdflabel_label="544c5320312e332c20$label" hkdflabel_label="544c5320312e332c20$label"
else else
@ -10919,9 +10944,9 @@ hkdf-expand-label() {
hkdflabel_label="746c73313320$label" hkdflabel_label="746c73313320$label"
fi fi
len=${#hkdflabel_label}/2 len=${#hkdflabel_label}/2
hkdflabel_label="$(printf "%02X\n" $len)$hkdflabel_label" hkdflabel_label="$(printf "%02X\n" "$len")$hkdflabel_label"
len=${#context}/2 len=${#context}/2
hkdflabel_context="$(printf "%02X\n" $len)$context" hkdflabel_context="$(printf "%02X\n" "$len")$context"
hkdflabel="$hkdflabel_length$hkdflabel_label$hkdflabel_context" hkdflabel="$hkdflabel_length$hkdflabel_label$hkdflabel_context"
hkdf-expand "$hash_fn" "$secret" "$hkdflabel" "$length" hkdf-expand "$hash_fn" "$secret" "$hkdflabel" "$length"
@ -10977,7 +11002,7 @@ create-initial-transcript() {
local clienthello1="$2" hrr="$3" clienthello2="$4" serverhello="$5" local clienthello1="$2" hrr="$3" clienthello2="$4" serverhello="$5"
local hash_clienthello1 msg_transcript 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" msg_transcript="$clienthello1$hrr$clienthello2$serverhello"
elif [[ -n "$hrr" ]]; then elif [[ -n "$hrr" ]]; then
if [[ "$cipher" == *SHA256 ]]; then if [[ "$cipher" == *SHA256 ]]; then
@ -11036,7 +11061,7 @@ derive-handshake-secret() {
# early_secret="$(hmac "$hash_fn" "000...000" "000...000")" # early_secret="$(hmac "$hash_fn" "000...000" "000...000")"
case "$hash_fn" in case "$hash_fn" in
"-sha256") early_secret="33ad0a1c607ec03b09e6cd9893680ce210adf300aa1f2660e1b22e10f170f92a" "-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" # "6465726976656420736563726574" = "derived secret"
# derived_secret="$(derive-secret "$hash_fn" "$early_secret" "6465726976656420736563726574" "")" # derived_secret="$(derive-secret "$hash_fn" "$early_secret" "6465726976656420736563726574" "")"
derived_secret="c1c0c36bf8fb1d1afa949fbd360e71af69a6244a4c2eaef5bbbb6442a7277d2c" derived_secret="c1c0c36bf8fb1d1afa949fbd360e71af69a6244a4c2eaef5bbbb6442a7277d2c"
@ -11047,7 +11072,7 @@ derive-handshake-secret() {
fi fi
;; ;;
"-sha384") early_secret="7ee8206f5570023e6dc7519eb1073bc4e791ad37b5c382aa10ba18e2357e716971f9362f2c2fe2a76bfd78dfec4ea9b5" "-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" # "6465726976656420736563726574" = "derived secret"
# derived_secret="$(derive-secret "$hash_fn" "$early_secret" "6465726976656420736563726574" "")" # derived_secret="$(derive-secret "$hash_fn" "$early_secret" "6465726976656420736563726574" "")"
derived_secret="54c80fa05ee9e0532ce3db8ddeca37a0365683bcd3b27bdc88d2b9fdc115ca4ebc8edc1f0b72a6a0861e803fc34761ef" derived_secret="54c80fa05ee9e0532ce3db8ddeca37a0365683bcd3b27bdc88d2b9fdc115ca4ebc8edc1f0b72a6a0861e803fc34761ef"
@ -11060,7 +11085,7 @@ derive-handshake-secret() {
esac esac
shared_secret="$($OPENSSL pkeyutl -derive -inkey "$priv_file" -peerkey "$pub_file" 2>/dev/null | hexdump -v -e '16/1 "%02X"')" 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. # For draft 18 use $early_secret rather than $derived_secret.
if [[ "${TLS_SERVER_HELLO:8:4}" == "7F12" ]]; then if [[ "${TLS_SERVER_HELLO:8:4}" == "7F12" ]]; then
@ -11083,7 +11108,7 @@ derive-handshake-traffic-keys() {
local sender="$4" local sender="$4"
local hash_fn local hash_fn
local -i hash_len key_len 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 if [[ "$cipher" == *SHA256 ]]; then
hash_fn="-sha256" hash_fn="-sha256"
@ -11102,7 +11127,7 @@ derive-handshake-traffic-keys() {
return 1 return 1
fi 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 if [[ "$sender" == server ]]; then
# "7365727665722068616e647368616b65207472616666696320736563726574" = "server handshake traffic secret" # "7365727665722068616e647368616b65207472616666696320736563726574" = "server handshake traffic secret"
label="7365727665722068616e647368616b65207472616666696320736563726574" label="7365727665722068616e647368616b65207472616666696320736563726574"
@ -11126,7 +11151,12 @@ derive-handshake-traffic-keys() {
# "6976" = "iv" # "6976" = "iv"
iv="$(derive-traffic-key "$hash_fn" "$handshake_traffic_secret" "6976" "12")" iv="$(derive-traffic-key "$hash_fn" "$handshake_traffic_secret" "6976" "12")"
[[ $? -ne 0 ]] && return 1 [[ $? -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 # See RFC 8439, Section 2.1
@ -11364,7 +11394,7 @@ u64to8() {
local -i v="$1" local -i v="$1"
local p 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}" 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 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 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 msg_len remaining tls_serverhello_ascii_len sid_len
local -i j offset tls_extensions_len extension_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 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 -i seq_num=0 plaintext_len
local plaintext decrypted_response="" additional_data local plaintext decrypted_response="" additional_data
local include_headers=true local include_headers=true
@ -12174,7 +12204,7 @@ check_tls_serverhellodone() {
if [[ -n "$handshake_secret" ]]; then if [[ -n "$handshake_secret" ]]; then
handshake_traffic_keys="$(derive-handshake-traffic-keys "$cipher" "$handshake_secret" "$msg_transcript" "server")" 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 fi
if [[ -z "$tls_hello_ascii" ]]; then if [[ -z "$tls_hello_ascii" ]]; then
@ -12302,6 +12332,20 @@ check_tls_serverhellodone() {
# The ServerHello has already been added to $msg_transcript, # The ServerHello has already been added to $msg_transcript,
# but all other handshake messages need to be added. # but all other handshake messages need to be added.
if [[ -n "$key" ]] && [[ "$tls_msg_type" != 02 ]]; then 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}" msg_transcript+="$tls_msg_type${tls_handshake_ascii:$((i-6)):6}${tls_handshake_ascii:i:msg_len}"
fi fi
# For SSLv3 - TLS1.2 look for a ServerHelloDone message. # For SSLv3 - TLS1.2 look for a ServerHelloDone message.
@ -14294,10 +14338,11 @@ tls_sockets() {
local tls_hello_ascii next_packet local tls_hello_ascii next_packet
local clienthello1 original_clienthello hrr="" local clienthello1 original_clienthello hrr=""
local process_full="$3" offer_compression=false skip=false local process_full="$3" offer_compression=false skip=false
local close_connection=true local close_connection=true include_headers=true
local -i hello_done=0 local -i i len tag_len hello_done=0 plaintext_len
local cipher="" handshake_secret="" res local cipher="" tls_version handshake_secret="" res plaintext
local initial_msg_transcript msg_transcript local initial_msg_transcript msg_transcript finished_msg aad="" data=""
local handshake_traffic_keys key iv finished_key
[[ "$5" == true ]] && offer_compression=true [[ "$5" == true ]] && offer_compression=true
[[ "$6" == false ]] && close_connection=false [[ "$6" == false ]] && close_connection=false
@ -14431,6 +14476,42 @@ tls_sockets() {
tm_out " ($lines lines returned) " tm_out " ($lines lines returned) "
fi 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 # determine the return value for higher level, so that they can tell what the result is
if [[ $save -eq 1 ]] || [[ $lines -eq 1 ]]; then if [[ $save -eq 1 ]] || [[ $lines -eq 1 ]]; then
ret=1 # NOT available ret=1 # NOT available