Skip to content

Commit e0401b6

Browse files
committed
TLS 1.3 early data / 0-RTT
This PR implements a check for TLS early data. It needs a compatible OpenSSL or LibreSSL version. For modern OS versionis it should automagically pick the right, modern binary for the check. Mitigations like Defer processing or HTTP 425 are not yet tested. To clarify is * whether to penalize SSLlabs rating (@magnuslarsen). testssl.net has it enabled but the Web UI claims it's not a/v, see https://www.ssllabs.com/ssltest/analyze.html?d=testssl.net&s=172.67.205.231&hideResults=on&latest * Man pages To be in line with other HAS2_* global vars (HAS2_QUIC, HAS2_UDS), the following vars were renamed from their OPENSSL2_HAS_* counter parts: - HAS2_TLS13 - HAS2_CHACHA20=false - HAS2_AES128_GCM=false - HAS2_AES256_GCM=false
1 parent 3ece1e4 commit e0401b6

File tree

1 file changed

+113
-22
lines changed

1 file changed

+113
-22
lines changed

testssl.sh

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,10 @@ CIPHERS_BY_STRENGTH_FILE=""
247247
TLS_DATA_FILE="" # mandatory file for socket-based handshakes
248248
OPENSSL="" # ~/bin/openssl.$(uname).$(uname -m) if you run this from GitHub. Linux otherwise probably /usr/bin/openssl
249249
OPENSSL2=${OPENSSL2:-/usr/bin/openssl} # This will be openssl version >=1.1.1 (auto determined) as opposed to openssl-bad (OPENSSL)
250-
OPENSSL2_HAS_TLS_1_3=false # If we run with supplied binary AND $OPENSSL2 supports TLS 1.3 this will be set to true
251-
OPENSSL2_HAS_CHACHA20=false
252-
OPENSSL2_HAS_AES128_GCM=false
253-
OPENSSL2_HAS_AES256_GCM=false
250+
HAS2_TLS13=false # If we run with supplied binary AND $OPENSSL2 supports TLS 1.3 this will be set to true
251+
HAS2_CHACHA20=false
252+
HAS2_AES128_GCM=false
253+
HAS2_AES256_GCM=false
254254
OSSL_SHORTCUT=${OSSL_SHORTCUT:-true} # If you don't want automagically switch from $OPENSSL to $OPENSSL2 for TLS 1.3-only hosts, set this to false
255255
OPENSSL_LOCATION=""
256256
OPENSSL_NOTIMEOUT="" # Needed for renegotiation tests
@@ -5560,6 +5560,7 @@ run_prototest_openssl() {
55605560
#
55615561
# arg1: protocol
55625562
# arg2: available (yes) or not (no)
5563+
#
55635564
add_proto_offered() {
55645565
# the ":" is mandatory here (and @ other places), otherwise e.g. tls1 will match tls1_2
55655566
if [[ "$2" == yes ]] && [[ "$PROTOS_OFFERED" =~ $1:no ]]; then
@@ -5571,7 +5572,7 @@ add_proto_offered() {
55715572
fi
55725573
}
55735574

5574-
# function which checks whether SSLv2 - TLS 1.2 is being offered, see add_proto_offered()
5575+
# function which checks whether the supplied protocol was tested to be offered; see also add_proto_offered()
55755576
# arg1: protocol string or hex code for TLS protocol
55765577
# echos: 0 if proto known being offered, 1: known not being offered, 2: we don't know yet whether proto is being offered
55775578
# return value is always zero
@@ -7130,6 +7131,59 @@ sub_session_resumption() {
71307131
return $ret
71317132
}
71327133

7134+
7135+
# Tests for TSL 1.3 early data / 0-RTT (see RFC 8470). Defer processing or HTTP 425 is not yet tested.
7136+
# Returns:
7137+
# - 0: Early Data was accepted
7138+
# - 1: not
7139+
# - 2: no TLS 1.3
7140+
# - 3: STARTTLS
7141+
# - 4/5: problem with openssl binary
7142+
# - 6: Client Auth not possible
7143+
# - 7: no session data
7144+
#
7145+
sub_early_data() {
7146+
local sess_data=$TEMPDIR/session_data.log
7147+
local early_data=$TEMPDIR/early_data.log
7148+
local openssl_bin=""
7149+
7150+
[[ "$CLIENT_AUTH" == required ]] && [[ -z "$MTLS" ]] && return 6
7151+
[[ $(has_server_protocol 04) -eq 1 ]] && return 2
7152+
[[ -n "$STARTTLS" ]] && return 3
7153+
7154+
if "$HAS_TLS13"; then
7155+
openssl_bin=$OPENSSL
7156+
elif "$HAS2_TLS13"; then
7157+
openssl_bin="$OPENSSL2"
7158+
else
7159+
return 4
7160+
fi
7161+
7162+
if "$HAS_EARLYDATA"; then
7163+
# OpenSSL also has early data, LibreSSL won't succeeded here
7164+
openssl_bin=$OPENSSL
7165+
elif "$HAS2_EARLYDATA"; then
7166+
openssl_bin="$OPENSSL2"
7167+
else
7168+
return 5
7169+
fi
7170+
7171+
safe_echo "HEAD / HTTP/1.1\r\nHost: $NODE\r\nConnection: close\r\nEarly-Data: 1\r\n\r\n" > $early_data
7172+
$openssl_bin s_client $(s_client_options "$STARTTLS $BUGS -tls1_3 -connect $NODEIP:$PORT $PROXY $SNI") -sess_out $sess_data -ign_eof \
7173+
< $early_data >/dev/null 2>$ERRFILE
7174+
if [[ ! -s "$sess_data" ]]; then
7175+
exit 7
7176+
fi
7177+
7178+
$openssl_bin s_client $(s_client_options "$STARTTLS $BUGS -tls1_3 -connect $NODEIP:$PORT $PROXY $SNI") -sess_in $sess_data \
7179+
-early_data $early_data </dev/null 2>$ERRFILE | grep -qi '^Early Data was accepted'
7180+
if [[ $? -eq 0 ]]; then
7181+
return 0
7182+
else
7183+
return 1
7184+
fi
7185+
}
7186+
71337187
run_server_preference() {
71347188
local cipher1="" cipher2="" tls13_cipher1="" tls13_cipher2="" default_proto=""
71357189
local default_cipher="" ciph
@@ -10665,20 +10719,20 @@ run_server_defaults() {
1066510719
jsonID="sessionresumption_ticket"
1066610720
sub_session_resumption "$sessticket_proto"
1066710721
case $? in
10668-
0) out "Tickets: yes, "
10722+
0) out "tickets: yes, "
1066910723
fileout "$jsonID" "INFO" "supported"
1067010724
;;
10671-
1) out "Tickets no, "
10725+
1) out "tickets no, "
1067210726
fileout "$jsonID" "INFO" "not supported"
1067310727
;;
10674-
5) pr_warning "Ticket resumption test failed, pls report / "
10728+
5) pr_warning "ticket resumption test failed, pls report / "
1067510729
fileout "$jsonID" "WARN" "check failed, pls report"
1067610730
((ret++))
1067710731
;;
1067810732
6) pr_warning "Client Auth: Ticket resumption test not supported / "
1067910733
fileout "$jsonID" "WARN" "check couldn't be performed because of client authentication"
1068010734
;;
10681-
7) pr_warning "Connect problem: Ticket resumption test not possible / "
10735+
7) pr_warning "connect problem: Ticket resumption test not possible / "
1068210736
fileout "$jsonID" "WARN" "check failed because of connect problem"
1068310737
((ret++))
1068410738
;;
@@ -10711,6 +10765,43 @@ run_server_defaults() {
1071110765
esac
1071210766
fi
1071310767

10768+
pr_bold " TLS 1.3 early data support "
10769+
jsonID="early_data"
10770+
if "$NO_SSL_SESSIONID"; then
10771+
pr_svrty_good "no early data"; outln " (no SSL session ID)"
10772+
fileout "$jsonID" "OK" "No early data: no session SSL ID"
10773+
else
10774+
sub_early_data
10775+
case $? in
10776+
0) out "offered, potentially " ; pr_svrty_high "NOT ok"; outln " (check context, see e.g. RFC 8446 E.5)"
10777+
fileout "$jsonID" "HIGH" "supported"
10778+
# https://www.rfc-editor.org/rfc/rfc8446#appendix-E.5
10779+
;;
10780+
1) prln_svrty_good "no early data offered"
10781+
fileout "$jsonID" "OK" "No early data"
10782+
;;
10783+
2) outln "not offered, as no TLS 1.3 offered"
10784+
fileout "$jsonID" "INFO" "No TLS 1.3 offered"
10785+
;;
10786+
3) outln "not tested, as STARTTLS doesn't offer that"
10787+
fileout "$jsonID" "OK" "not tested, as STARTTLS doesn't offer that"
10788+
;;
10789+
4) prln_warning "couldn't test it, no OpenSSL TLS 1.3 support"
10790+
fileout "$jsonID" "WARN" "no OpenSSL support TLS 1.3 support"
10791+
;;
10792+
5) prln_warning "couldn't test it, no OpenSSL early_data support"
10793+
fileout "$jsonID" "INFO" "no OpenSSL early_data support"
10794+
;;
10795+
6) prln_warning "Client Auth: early data check not supported"
10796+
fileout "$jsonID" "WARN" "check couldn't be performed because of client authentication"
10797+
;;
10798+
7) prln_warning "check failed (no session data"
10799+
fileout "$jsonID" "WARN" "check failed (no session data)"
10800+
((ret++))
10801+
;;
10802+
esac
10803+
fi
10804+
1071410805
tls_time
1071510806

1071610807
jsonID="cert_compression"
@@ -13434,7 +13525,7 @@ chacha20() {
1343413525
if "$HAS_CHACHA20"; then
1343513526
plaintext="$(hex2binary "$ciphertext" | $OPENSSL enc -chacha20 -K "$key" -iv "01000000$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
1343613527
enc_chacha_used=true
13437-
elif "$OPENSSL2_HAS_CHACHA20"; then
13528+
elif "$HAS2_CHACHA20"; then
1343813529
# empty OPENSSL_CONF temporarily as it might cause problems, see #2780
1343913530
plaintext="$(hex2binary "$ciphertext" | OPENSSL_CONF='' $OPENSSL2 enc -chacha20 -K "$key" -iv "01000000$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
1344013531
enc_chacha_used=true
@@ -14135,7 +14226,7 @@ gcm-decrypt() {
1413514226
if "$HAS_AES128_GCM"; then
1413614227
plaintext="$(hex2binary "$ciphertext" | $OPENSSL enc -aes-128-gcm -K "$key" -iv "$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
1413714228
enc_aesgcm_used=true
14138-
elif "$OPENSSL2_HAS_AES128_GCM"; then
14229+
elif "$HAS2_AES128_GCM"; then
1413914230
# empty OPENSSL_CONF temporarily as it might cause problems, see #2780
1414014231
plaintext="$(hex2binary "$ciphertext" | OPENSSL_CONF='' $OPENSSL2 enc -aes-128-gcm -K "$key" -iv "$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
1414114232
enc_aesgcm_used=true
@@ -14144,7 +14235,7 @@ gcm-decrypt() {
1414414235
if "$HAS_AES256_GCM"; then
1414514236
plaintext="$(hex2binary "$ciphertext" | $OPENSSL enc -aes-256-gcm -K "$key" -iv "$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
1414614237
aesgcm_used=true
14147-
elif "$OPENSSL2_HAS_AES256_GCM"; then
14238+
elif "$HAS2_AES256_GCM"; then
1414814239
# empty OPENSSL_CONF temporarily as it might cause problems, see #2780
1414914240
plaintext="$(hex2binary "$ciphertext" | OPENSSL_CONF='' $OPENSSL2 enc -aes-256-gcm -K "$key" -iv "$nonce" 2>/dev/null | hexdump -v -e '16/1 "%02X"')"
1415014241
enc_aesgcm_used=true
@@ -21137,7 +21228,7 @@ find_openssl_binary() {
2113721228
# Kind of fine this way as openssl 1.1.1 supports early_data, came with tls 1.3
2113821229
if $OPENSSL s_client -help 2>&1 | grep -q early_data ; then
2113921230
HAS_EARLYDATA=true
21140-
elif OPENSSL_CONF='' $OPENSS2 s_client --help 2>&1 | grep -q early_data ; then
21231+
elif OPENSSL_CONF='' $OPENSSL2 s_client --help 2>&1 | grep -q early_data ; then
2114121232
HAS2_EARLYDATA=true
2114221233
fi
2114321234

@@ -21234,15 +21325,15 @@ find_openssl_binary() {
2123421325
if [[ $OPENSSL2 != $OPENSSL ]] && [[ -x $OPENSSL2 ]]; then
2123521326
if ! "$HAS_CHACHA20"; then
2123621327
OPENSSL_CONF='' $OPENSSL2 enc -chacha20 -K 12345678901234567890123456789012 -iv 01000000123456789012345678901234 >/dev/null 2>/dev/null <<< "test"
21237-
[[ $? -eq 0 ]] && OPENSSL2_HAS_CHACHA20=true
21328+
[[ $? -eq 0 ]] && HAS2_CHACHA20=true
2123821329
fi
2123921330
if ! "$HAS_AES128_GCM"; then
2124021331
OPENSSL_CONF='' $OPENSSL2 enc -aes-128-gcm -K 0123456789abcdef0123456789abcdef -iv 0123456789abcdef01234567 >/dev/null 2>/dev/null <<< "test"
21241-
[[ $? -eq 0 ]] && OPENSSL2_HAS_AES128_GCM=true
21332+
[[ $? -eq 0 ]] && HAS2_AES128_GCM=true
2124221333
fi
2124321334
if ! "$HAS_AES256_GCM"; then
2124421335
OPENSSL_CONF='' $OPENSSL2 enc -aes-256-gcm -K 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef -iv 0123456789abcdef01234567 >/dev/null 2>/dev/null <<< "test"
21245-
[[ $? -eq 0 ]] && OPENSSL2_HAS_AES256_GCM=true
21336+
[[ $? -eq 0 ]] && HAS2_AES256_GCM=true
2124621337
fi
2124721338

2124821339
# Now check whether the standard $OPENSSL has Unix-domain socket and xmpp-server support. If
@@ -21256,7 +21347,7 @@ find_openssl_binary() {
2125621347
grep -q 'xmpp-server' $s_client2_starttls_has && HAS_XMPP_SERVER2=true
2125721348
# Likely we don't need the following second check here, see 6 lines above
2125821349
if grep -wq 'tls1_3' $s_client_has2; then
21259-
OPENSSL_CONF='' OPENSSL2_HAS_TLS_1_3=true
21350+
OPENSSL_CONF='' HAS2_TLS13=true
2126021351
fi
2126121352
fi
2126221353
fi
@@ -21598,10 +21689,10 @@ HAS_CURVES: $HAS_CURVES
2159821689
OSSL_SUPPORTED_CURVES: $OSSL_SUPPORTED_CURVES
2159921690

2160021691
OPENSSL2: $OPENSSL2 ($($OPENSSL2 version -v 2>/dev/null))
21601-
OPENSSL2_HAS_TLS_1_3: $OPENSSL2_HAS_TLS_1_3
21602-
OPENSSL2_HAS_CHACHA20: $OPENSSL2_HAS_CHACHA20
21603-
OPENSSL2_HAS_AES128_GCM: $OPENSSL2_HAS_AES128_GCM
21604-
OPENSSL2_HAS_AES256_GCM: $OPENSSL2_HAS_AES256_GCM
21692+
HAS2_TLS13: $HAS2_TLS13
21693+
HAS2_CHACHA20: $HAS2_CHACHA20
21694+
HAS2_AES128_GCM: $HAS2_AES128_GCM
21695+
HAS2_AES256_GCM: $HAS2_AES256_GCM
2160521696

2160621697
HAS_SSL2: $HAS_SSL2
2160721698
HAS_SSL3: $HAS_SSL3
@@ -23104,7 +23195,7 @@ determine_optimal_proto() {
2310423195
[[ $? -ne 0 ]] && exit $ERR_CLUELESS
2310523196
elif "$all_failed" && ! "$ALL_FAILED_SOCKETS"; then
2310623197
if ! "$HAS_TLS13" && "$TLS13_ONLY"; then
23107-
if "$OPENSSL2_HAS_TLS_1_3"; then
23198+
if "$HAS2_TLS13"; then
2310823199
if "$OSSL_SHORTCUT" || [[ "$WARNINGS" == batch ]]; then
2310923200
# switch w/o asking
2311023201
OPEN_MSG=" $NODE:$PORT appeared to support TLS 1.3 ONLY. Thus switched automagically from\n \"$OPENSSL\" to \"$OPENSSL2\"."

0 commit comments

Comments
 (0)