Original text:
1.2.  draft-05 Prohibit SSL negotiation for backwards compatibility. The higher-level protocol used to process the enclosed fragment. <del> The version of the protocol being employed.  This document describes TLS Version 1.3, which uses the version { 3, 4 }.  The version value 3.4 is historical, deriving from the use of {3, 1} for TLS 1.0.  (See record-layer-1.)  Note that a client that supports multiple versions of TLS may not know what version will be employed before it receives the ServerHello.  See backward- compatibility for discussion about what record layer version number should be employed for ClientHello. </del>  The length (in bytes) of the following TLSPlaintext.fragment.  The independent block to be dealt with by the higher-level protocol specified by the type field.  Implementations MUST NOT send zero-length fragments of Handshake or Alert types.  Zero-length fragments of Application data MAY be sent as they are potentially useful as a traffic analysis countermeasure. described in Section 2.1 of RFC5116.  The key is either the client_write_key or the server_write_key. <del> %%% Record Layer struct { ContentType type; ProtocolVersion version; uint16 length; opaque </del>  nonce_explicit[SecurityParameters.record_iv_length]; aead-ciphered struct { opaque content[TLSPlaintext.length]; } fragment; } TLSCiphertext; The type field is identical to TLSPlaintext.type. <del> The version field is identical to TLSPlaintext.version. </del>  The length (in bytes) of the following TLSCiphertext.fragment. The version of the TLS protocol by which the client wishes to communicate during this session.  This SHOULD be the latest (highest valued) version supported by the client.  For this <del> version of the specification, the version will be 3.4 (see backward-compatibility for details about backward compatibility). </del>  A client-generated random structure.

Reviewer comments:
This does the following: 1) Reorganize the backwards compatibility section into subsections for easier readability 2) Freezes the ClientHello record layer version to TLS 1.0 (its effective compatible version) 3) Adds explicit requirement that all other record layer versions match the negotiated version This plus PR should be sufficient to close issue . The record layer requirements were proposed by Brian Smith on the list. URL (also mentioned by him in other discussions)
I don't generally use capitals and periods for sentence fragments in parentheticals. Full sentences generally shouldn't be in parentheticals. Other instances of "(see _)" prefer a lowercase 's' and no period, but it's not consistent throughout the whole document. I'd lean towards that, however upon inspection I don't think this cross reference is useful at all. I'll just remove it. I'll also correct the embarrassingly stupid typo. (spellchecker doesn't work in web interface) I'm going to refactor this slightly to move more info down to the end of the section rather than cramming everything in here.
Can you think of a good external reference to cite for the buggy server blurb? If it's a "complex topic beyond the scope of this document" then it ideally should reference more information elsewhere.
Re: other vendors' ClientHello version strategies: It occurs to me that we might prefer to make the ClientHello record version a SHOULD rather than a MUST to allow for some flexibility in implementation. If anyone is sending different versions in different instances to cope with specific server bugs, that may not need to be completely prohibited. (if not needed, then a MUST is probably appropriate)
RE: "This document URL SSL 3.0." I disagree that it is unnecessary. Whilst redundant, I think the reason why TLS uses weird version numbers needs to be stated somewhere in this document. Moving it to the backwards compatibility section is an option if you think it's in the way up here. RE: second section of record layer version text I have that set aside due to formatting concerns. The "version" section is indented in a list and putting lots of text in there risks cluttering things up a bit. I'd prefer to have the minimum definition and requirements under that heading and additional issues separately, in order to maximize readability.
Well crud. I rebased this and GitHub dropped the comments from NAME They haven't vanished entirely into the aether, just not shown above in the threading here. For reference, imagine there are proper "commented on an outdated diff" lines above and that I didn't confuse GitHub and am not talking to myself. I shall endeavor to be less of a GitHub noob in the future. URL URL URL URL URL URL
It might be simpler to just change to: TLS clients MUST set this version to { 3, 1 } for the initial ClientHello and all alert messages. For the ServerHello and all other TLS records this MUST be equal to the negotiated version. Additionally, dropping the version field from TLSCipherText entirely might be worth considering, but that's outside the scope of this here.
(I assume you mean s/clients/implementations/? Otherwise there's still an unspecified hole.) All alerts period, or just those before version negotiation? OpenSSL, after version negotiation, will currently reject any record (of whatever type) whose version does not match the negotiated version. Go's crypto/tls does the same thing. I haven't done a survey of other implementations yet. It can certainly change for TLS 1.3. But, as an implementer, I would prefer that field be hammered down in a way that can advise older versions, whether or not 1.3 additionally clamps it post-negotiation. If just those before version negotiation, I think a cleaner statement of that rule is: Prior to version negotiation, this version MUST by {3, 1}. This includes the initial ClientHello and any alerts sent during version negotiation. After version negotiation, this MUST be equal to the negotiated version. This includes the ServerHello and all following records. This is fairly straightforward and clean. The record-layer initializes in "proto TLS" which uses {3, 1}. Then it switches one to a concrete version. The one ambiguity left is a client's alert. That one is sent when one side has done negotiation, but not the other. Which is where the trickiness I mentioned comes up with existing servers[]. Whether the complexity is worth bothering with is another matter. (For context, I'm coming at this from having recently rewritten BoringSSL's version negotiation logic. 1.2's failure to clearly specify version negotiation was a huge pain point.) [] For completeness, OpenSSL client has its own quirks. If the client has a minimum version > TLS 1.0 AND the server sends some alert with record-layer {3, 1}, the client will fail to read it. OpenSSL client negotiates based on record-layer version, not ServerHello. But that implementation is sufficiently weird that I don't think it's worth bothering about. See links in .
I was proposing {3,1} for all alerts, yes. (even old versions should be able to parse new; an unknown error could just be generically acted on) However, if there are implementations that would find this inconsistent with current behavior, then I agree it's not worth it. I like the language you suggested. It deals with the error cases and is quite clear. (modulo the typo ;) To deal with the protocol_version case, this might cover everything: Prior to version negotiation, this value MUST be { 3, 1 }. This includes the ClientHello, records sent during negotiation, and errors aborting the negotiation. After version negotiation, this MUST be equal to the negotiated version. This includes the ServerHello and all following records.
"records sent during negotiation" would count EarlyDataExtension records as well. Arguably this should be { 3, 4 }, but I don't think it's worth the complexity.
Version negotiation is completed by the first packet that the server sends (ie typically ServerHello), and the client typically only sends one packet if it cares about compatibility with versions prior to TLS 1.3, so forcing the packet version to {3, 1} for the ClientHello should be sufficient.
Per proposed resolution at interim, we can now simplify things down to one frozen and deprecated value. This is the ideal solution, IMO. In addition to updates for this, I've also renamed the field to "record_version" instead of just "version" as people easily mix up the various version fields. It now has a different expected behavior, so a different name is appropriate.
I'm fine with freezing it going forward (it means that someday in the far far future perhaps we can forget about this craziness altogether). But note that implementations supporting TLS < 1.3 will still need to do a swap when negotiating older versions. Existing clients and servers (including anything derived from OpenSSL) will reject post-version-negotiation records that don't have the right record-layer version. I don't know if advice on that front belongs in this draft or elsewhere or nowhere, but it is a concern implementations will have for a while.
Good point. I've updated backwards compatibility section for that.
More fixes and rebasing.
Note that I expect a new version negotiation mechanism implemented via an extension will be desired in order to deal with TLS 1.3 intolerance, and thus parts of this will be changed again. ClientHello.client_version would most likely be frozen to {3,3} in such a mechanism. That, however, is outside the scope of this PR which is to revise the existing backwards compatibility and freeze the record layer version.
(Apologies if this is the wrong medium. I wasn't sure if issues here or emails to the list were preferred since these aren't huge changes.) How version negotiation interacts with session resumption isn't said explicitly. Two little things that probably should be fixed: On the server, clarify that you MUST NOT resume a session at a version other than the original one. Instead, ignore the offered session and do a full handshake. This is easy to justify: every client (I tested NSS, OpenSSL, SecureTransport, and SChannel) will fail the handshake if the server tries to mismatch. It's also weird. (Say a future TLS version changed the master secret size...) Still, OpenSSL happily did so until . On the client, RFC 5246, E.1 has this little paragraph that seems to have carried over into 1.3. This is terrible advice. The parenthetical bit is wrong, but I would push for dropping it completely. The server may have changed, there may be multiple differently-configured servers behind that host, the session may have been established under fallback conditions, etc. OpenSSL takes it to an and . (I ended up completely rewriting this bit for BoringSSL.) NSS in something less catastrophic but still has the second "sticky fallback" failure mode.
I support removing that text. See also earlier discussion: URL URL Note that when a client does not consistently use the same ClientHello.clientversion, it should probably avoid attempting to resume a session X if it used a different ClientHello.clientversion for the connection that created session X than it is attempting to use in the current ClientHello. The consequence of this is that these clients should store ClientHello.clientversion in the session cache entry. However, it is better for them to always consistently set ClientHello.clientversion to TLS 1.2.
Having generic text to take care when mixing session caches between contexts with different parameters is probably worthwhile yeah. Sharing a session cache between a pile of sockets that, say, turn off AEADs and a pile that don't is also iffy. The clientversion seems rather specific requirement to put in, and requiring TLS impls to store the entire ClientHello in the session cache seems a bit much. I actually don't see what doing just the clientversion buys for the extra state, at least in contexts where a browser does it. Are you thinking to do that to account for the NSS clientversion clamping behavior, the old OpenSSL server one when combined with fallbacks, or something else? If the NSS side, that would mean NSS never resumes sessions on any but the highest version. It should just remove that logic; neither SChannel nor Secure Transport did it when I tested. Chrome + BoringSSL doesn't either and we haven't had problems. (It's hit stable on Mac and beta on Windows.) If the old OpenSSL server behavior (and without FALLBACKSCSV) and you should have negotiated a stronger TLS-1.2-only cipher, an attacker could just trigger the fallback again anyway, so we're not gaining much. That seems best fixed on the server (and has been now). In fact, if the server wants to be extra fancy, it could simulate picking a new cipher and only accept the session if that matched. But probably expiring old sessions when changing server config works just as well there. (That may also be worth some text: servers should take care when considering old sessions across a server config change.)
I mean this (acting as the client): Decide what number (e.g. TLS 1.2) you are going to put in ClientHello.clientversion and what cipher suites you are going to send in the ClientHello. Search the session cache for a matching entry, where the session was established on a connection where ClientHello.clientversion matches the ClientHello.clientversion you picked in the previous step, and where the cipher suite for the cached session matches a cipher suite in the set of cipher suites you've enabled for the current ClientHello. Note that we're matching ClientHello.clientversion to ClientHello.clientversion, NOT to the version that was negotiated (ServerHello.serverversion). If the server attempts to resume the session with a different serverversion or a different cipher suite, then something's gone very wrong; bail! The idea here is to avoid cases where the server may do strange things like use a different ServerHello.serverversion or a different cipher suite than what was originally negotiated. As you mentioned, the logic I described isn't enough to prevent all silliness, but I think it would avoid most cases.
We removed resumption entirely as of draft-07 so I think this is OBE.
It's renegotiation that was removed. This might still be relevant.
Well, we removed explicit resumption as well, and at least technically you could do PSK-resumption with different cipher suites and/or versions. I agree. But, my conclusion is that the TLS specification is wrong, and that that advice should be dropped from it and ignored by clients, because it enables this scenerio. I also think that the spec should be changed to require servers to never resume a session for a version lower than min(ClientHello.version, ), for similar reasons. In fact, your draft would be a good place to tell clients that they SHOULD NOT follow that advice in the TLS specifications. The current logic in your draft says that if the client doesn't downgrade ClientHello.clientversion to equal the version in the resumed session, then it must send the TLSDOWNGRADESCSV if ClientHello.clientversion is less than the highest version that it supports. That is the correct logic. The way it is written has made people think that TLSDOWNGRADESCSV is never supposed to be sent during resumption handshakes, which is why it is good to clarify it. OK. Cheers, Brian
, Bodo Moeller wrote: Right. In general, a resumption ClientHello SHOULD be just like a non-resumption ClientHello, except for the presence of the session ID and/or session ticket, because a resumption ClientHello is potentially the start of a full handshake. The TLS specs (TLS 1.2, in this case) say: Whenever a client already knows the highest protocol version known to a server (for example, when resuming a session), it SHOULD initiate the connection in that native protocol. However, actually the client does not ever know the highest protocol version the server supports, because the server may have expanded its version support since the last time the client contacted it. But, the consequence of that sentence is that at least NSS sends the session version as clientversion instead of its actual max supported version. I think that is wrong. However, it isn't clear if there are compatibility issues with changing that. I had thought that NSS was changed to use the session version for ClientHello.clientversion during resumption for compatibility reasons. However, looking at the change history, it appears to be the case that that change was made coincidentally with a compatibility fix for renegotiation, not initial resumption handshakes. So, it may be the case that it is already safe to always send the max supported version as client_version in an initial resumption handshake, without any indication from the server, even in the non-secure fallback case. It would require some implementation to make that change and test the compatibility to know for sure. Cheers, Brian
Dave Garrett wrote: It would be best to have your change do exactly that, by shortening the remaining text to: Implementations MUST NOT send or accept an SSL version 2.0 compatible CLIENT-HELLO. Implementations MUST NOT send or accept TLS records with a version less than { 3, 0 }. Note the suggestion to use the term "records" instead of "messages". I think further changes can be made to tighten up the text regarding allowed record versions, but those changes are more open for debate. For example, we might say that the ClientHello's record version MUST be { 3, 1 } and that the version field in all other records MUST equal the negotiated version. But, again, this can be done on top of your changes. Cheers, Brian

Revised text:
1.2. <ins> draft-06 Prohibit RC4 negotiation for backwards compatibility. Freeze & deprecate record layer version field. </ins> draft-05 Prohibit SSL negotiation for backwards compatibility. The higher-level protocol used to process the enclosed fragment.  <ins> The protocol version the current record is compatible with.  This value MUST be set to { 3, 1 } for all records.  This field is deprecated and MUST be ignored for all purposes. </ins> The length (in bytes) of the following TLSPlaintext.fragment.  The independent block to be dealt with by the higher-level protocol specified by the type field. <ins> This document describes TLS Version 1.3, which uses the version { 3, 4 }.  The version value 3.4 is historical, deriving from the use of { 3, 1 } for TLS 1.0 and { 3, 0 } for SSL 3.0.  In order to maximize backwards compatibility, the record layer version identifies as simply TLS 1.0.  Endpoints supporting other versions negotiate the version to use by following the procedure and requirements in backward-compatibility. </ins> Implementations MUST NOT send zero-length fragments of Handshake or Alert types.  Zero-length fragments of Application data MAY be sent as they are potentially useful as a traffic analysis countermeasure. described in Section 2.1 of RFC5116.  The key is either the client_write_key or the server_write_key.  <ins> %%% Record Layer struct { ContentType type; ProtocolVersion record_version = { 3, 1 }; /* TLS v1.x */ uint16 length; opaque </ins> nonce_explicit[SecurityParameters.record_iv_length]; aead-ciphered struct { opaque content[TLSPlaintext.length]; } fragment; } TLSCiphertext; The type field is identical to TLSPlaintext.type.  <ins> The record_version field is identical to TLSPlaintext.record_version and is always { 3, 1 }. </ins> The length (in bytes) of the following TLSCiphertext.fragment. The version of the TLS protocol by which the client wishes to communicate during this session.  This SHOULD be the latest (highest valued) version supported by the client.  For this  <ins> version of the specification, the version will be 3.4.  (See backward-compatibility for details about backward compatibility.) </ins> A client-generated random structure.