Why You Can't Afford to Ignore the TLS Version Field
Luke Hebert
- 3 minutes read - 613 wordsThe version field in a TLS ClientHello message is easy to overlook, but it plays a critical role in protocol negotiation. It appears in three separate places, each with a different purpose, and misreading any one of them can lead to confusing handshake failures. Here is a breakdown of what each version field does and how to handle them correctly.
Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 105
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 101
Version: TLS 1.2 (0x0303)Version fields occur in three places:
- as part of the header for each record that the client and the server send;
- as part of the
ClientHellomessage from the client; - as part of the
ServerHellomessage from the server.
Protocol Version Negotiation
The version field in the ClientHello is the maximum version supported by the client implementation. For instance, when the client puts 0x0302 in this field (that’s the conventional value meaning “TLS 1.1”), the client tells the server: “I am ready to handle all protocol versions up to TLS 1.1.” The version field in the ServerHello message from the server specifies which protocol version will be used for this connection. The server should use the highest protocol version that both client and server support.
The client should not announce support for a protocol version that it does not actually support, lest a server chooses such a version, mistakenly believing that the client indeed supports it.
About Records
The ClientHello from the client is sent wrapped into one or several records, and each record contains the protocol version as well. The records are like the envelopes around letters. It is safe to use version 0x0300 (SSLv3) for these records, regardless of the maximum supported version indicated in the ClientHello; that’s like sending a letter in an SSLv3 envelope, but the letter says “by the way, I also support TLS 1.0 and TLS 1.1.” Using SSLv3 records maximizes interoperability with old and buggy implementations that know only of SSLv3 and would reject records with a higher version.
The response from the server states the protocol version which will be used, and should come as records bearing that version. For example, if the server says “TLS 1.1” in its ServerHello, then that ServerHello should come wrapped into a record also tagged as “TLS 1.1”; and all subsequent records from both client and server should use that version.
Interoperability
Theoretically, a server should accept any value greater than or equal to 0x0300 in a version field, and should not complain if it contains, e.g., 0xA7C0 (meaning “TLS 165.193,” a fictitious version which will probably never be defined). This holds for both the ClientHello message and the record headers. The protocol version impacts the encoding of records, but the first records are in cleartext (no cryptography), and for them the version can be ignored because cleartext is cleartext (SSL 3.0, TLS 1.0, TLS 1.1, and TLS 1.2 cleartext records differ only by the version specified in the header, but are otherwise identical).
In practice, there are reports of widely deployed implementations which do not tolerate “version” fields where the first byte is not 0x03. There even are implementations which do not support a ClientHello which specifies a version higher than 0x0301 (aka TLS 1.0).
To minimize issues, a client:
- shall use SSL 3.0 in the records for the
ClientHello; - should specify its highest supported version in the
ClientHello; - may fallback, on failure, to trying the
ClientHelloagain, this time claiming a lower maximum supported version (to accommodate old servers which get a stroke when they see “TLS 1.1” or “TLS 1.2”).