-
Notifications
You must be signed in to change notification settings - Fork 915
Description
Contact Details
No response
Version
5.8.2
Description
A WolfSSL TLS 1.3 client receiving a HelloRetryRequest followed by a ServerHello with another cipher (eg. using TLS_AES_256_GCM_SHA384 in HRR and TLS_AES_128_GCM_SHA256 in ServerHello), will continue the handshake while it should reject the ServerHello with an appropriate error.
According to the RFC 8446 section 4.1.4 : A client which receives a cipher suite that was not offered MUST abort the handshake. Servers MUST ensure that they negotiate the same cipher suite when receiving a conformant updated ClientHello (if the server selects the cipher suite as the first step in the negotiation, then this will happen automatically). Upon receiving the ServerHello, clients MUST check that the cipher suite supplied in the ServerHello is the same as that in the HelloRetryRequest and otherwise abort the handshake with an “illegal_parameter” alert. So the client should reject the ServerHello with the other cipher with an "illegal_parameter" alert.
Impact
Unknown, but we noticed that the transcript hash computed by WolfSSL is not using only the first (from HRR) or second (from SH) provided hash function, and we assume that the begining of the transcript (before SH) is computed using the first hash function and the rest is computed using the second hash function.
Expected behavior
WolfSSL client should reject the ServerHello, send an Alert message and abort the connection.
Reproduction steps
Here is an example of a TLS 1.3 handshake that triggers the described behavior :
- Wait for ClientHello
- Send the following HRR
- Record Layer:
- ContentType: Handshake
- Version: TLS 1.2 (legacy marker)
- Length: 56
- Handshake:
- Type: ServerHello
- Length: 52
- Version: 0x0303
- Random: cf21ad74e59a6111be1d8c021e65b891c2a211167abb8c5e079e09e2c8a8339c (HelloRetryRequest magic value)
- SessionID: empty
- CipherSuite: TLS_AES_256_GCM_SHA384 (0x1301)
- Compression: null
- Extensions:
- supported_versions = TLS 1.3 (0x0304)
- KeyShare = secp384r1
in raw hex :1603030038020000340303cf21ad74e59a6111be1d8c021e65b891c2a211167abb8c5e079e09e2c8a8339c00130200000c002b00020304003300020018
- Record Layer:
- Wait for second ClientHello
- Send the following ServerHello :
- Record Layer:
- ContentType: Handshake
- Version: TLS 1.2 (legacy marker)
- Length: 155
- Handshake:
- Type: ServerHello
- Length: 151
- Version: 0x0303
- Random: 0101010101010101010101010101010101010101010101010101010101010101
- SessionID: empty
- CipherSuite: TLS_AES_128_GCM_SHA256 (0x1303)
- Compression: null
- Extensions:
- KeyShare (len 101)
- supported_versions = TLS 1.3 (0x0304)
In raw hex :160303009b020000970303010101010101010101010101010101010101010101010101010101010101010100130300006f003300650018006104533ee5bf40ec2d67988b77f317489bb6df952925c709fc0381111a5956f2d758110e59d3d7c1729e2c0d70eaf773e6120116426de2436a2f5fdd7fe54faf952b04fd13f516ce627f89d2019d4c8796959e4333c7065b496ca634d5dc63bde91f002b00020304
- Record Layer:
Start the following Python TCP server :
import socket
HOST = "0.0.0.0"
PORT = 3000
payload1 = bytes.fromhex(
"1603030038020000340303cf21ad74e59a6111be1d8c021e65b891c2a211167abb8c5e079e09e2c8a8339c00130200000c002b00020304003300020018"
)
payload2 = bytes.fromhex(
"160303009b020000970303010101010101010101010101010101010101010101010101010101010101010100130300006f003300650018006104533ee5bf40ec2d67988b77f317489bb6df952925c709fc0381111a5956f2d758110e59d3d7c1729e2c0d70eaf773e6120116426de2436a2f5fdd7fe54faf952b04fd13f516ce627f89d2019d4c8796959e4333c7065b496ca634d5dc63bde91f002b00020304"
)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen(1)
print(f"[*] Listening on {HOST}:{PORT} ...")
# Accept client connection
conn, addr = server_socket.accept()
with conn:
print(f"[+] Connection from {addr}")
# Wait for ClientHello
data = conn.recv(1024)
print(f"[>] Received: {data.hex()}")
# Send HRR
conn.sendall(payload1)
print(f"[<] Sent: {payload1.hex()}")
# Wait for second ClientHello
data = conn.recv(1024)
print(f"[>] Received: {data.hex()}")
# Send ServerHello
conn.sendall(payload2)
print(f"[<] Sent: {payload2.hex()}")
while True:
data = conn.recv(1024)
print(f"[>] Received: {data.hex()}")Then start TLS 1.3 handshake with WolfSSL client :
./build/examples/client/client -p 3000 -v 4You should see the WolfSSL client waiting for the server's EncryptedExtensions instead of aborting the connection.
Acknowledgements
This bug was found thanks to the tlspuffin fuzzer designed and developed by the tlspuffin team:
- Max Ammann
- Olivier Demengeon - Loria, Inria
- Tom Gouville - Loria, Inria
- Lucca Hirschi - Loria, Inria
- Steve Kremer - Loria, Inria
- Michael Mera - Loria, Inria