I am the assigned Gen-ART reviewer for this draft. The General Area Review Team (Gen-ART) reviews all IETF documents being processed by the IESG for the IETF Chair. Please treat these comments just like any other last call comments. For more information, please see the FAQ at . Document: draft-ietf-tls-tls13-24 Reviewer: Dale R. Worley Review Date: 2018-03-02 IETF LC End Date: 2018-03-02 IESG Telechat date: 2018-03-08 Summary: This draft is basically ready for publication, but has nits that should be fixed before publication. This review only covers the general properties of the proposed protocol and the exposition, as I am unqualified to assess its security properties. There are various places where the exposition could be made clearer, especially to readers not immersed in security matters. In many places, it is mostly a matter of making clearer the connections between different points in the exposition. In a few places, there seems to be ambiguity regarding the specification that has technical significance. In particular: - There is inexactness about "transcript", "handshake context", and "context". - When a server receives ClientHello, is it obligated to promptly send not just the ServerHello, but all first-flight messages from ServerHello through Finished? (section 4.2.11.3) I ask this because the client is only obligated/permitted to send EndOfEarlyData when it receives the server's Finished. - It seems inconsistent that the client can send an empty Certificate message, but the server cannot, even though the server can omit sending the Certificate message. (section 4.4.2.4) - Comparing sections 4.2.10 and 4.6.1, when a PSK was established in an earlier connection, exactly what are the limitations on the cryptographic parameters that can be used when the PSK is used in a resumption connection? 4.2.10 suggests that the following must be the same in both connections: TLS version, cipher suite, ALPN. But 4.6.1 suggests that different cipher suites can be used, as long as they use the same hash algorithm. - In regard to section 4.6.1, it seems to require that a client MAY NOT resume a connection using a ticket issued during a connection in which the server did not present a certificate for itself, because in the handshake of the resumption connection, the client is required to verify that the SNI is compatible with the certificate the server presented in the original connection. But that constraint isn't stated in section 2.2, despite being a global constraint on the structure of sessions. - Presumably implementations MUST NOT send zero-length fragments of alert messages for the same reasons that they cannot send zero-length fragments of handshake messages (whatever those reasons are). - There are about 28 error codes but nearly 150 places where the text require the connection to be aborted with an error -- and hence, nearly 150 distinct constraints that can be violated. There are 19 alone for "illegal_parameter". I would like to see an "alert extension value" which assigns a distinct "minor" code to each statement in the text that requires an error response (with implementations being allowed to be a bit sloppy in providing the correct minor code). - I take it that there is no "close read side" operation. (If that existed, TLS could generate the "broken pipe" error.) There are a number of issues which span the whole text: The interaction of this draft with extensions defined for previous versions of TLS is not laid out clearly. It seems safe enough for this draft to import data structures from earlier extensions with only a reference to the earlier RFC, but if an extension defines behavior (e.g., a negotiation process), exactly what is the specification of that behavior in TLS 1.3, given that the referenced RFC only defines its use in TLS 1.2 or earlier? At the least, there should be an explicit statement that the behaviors are carried forward in the "obvious way". It's also not clear exactly which previously defined extensions are brought forward into TLS 1.3. I suspect that they are all listed in section 4.2, but is it clearly stated that those, and only those, are grandfathered in? Presumably, for any referenced extension, the placement of values in messages in TLS 1.2 has a "natural" analog in TLS 1.3 that at most involves moving the value from one field to another in certain messages. But it would be reassuring to have a clear statement of this, and an enumeration of any more complex cases. There are about 28 error codes but nearly 150 places where the text require the connection to be aborted with an error. There are 19 alone for "illegal_parameter". I would like to see an "alert extension value" which assigns a distinct "minor" code to each statement in the text that requires an error response. This code would make it a lot easier to diagnose what is going wrong with a handshake. To avoid making specifying and implementing these codes too difficult, implementations should be allowed to deviate to a degree from the specification regarding minor codes, and there should be a range of codes reserved for implementation-defined uses. Conversely, there are a couple of places where the implementation MUST NOT distinguish between different causes of an alert, but I believe that those are explicitly mentioned in the text. The terms "side", "server side", and "client side" are used in various places, despite that "endpoint" is the defined term. I suggest replacing these terms with "endpoint", "server", and "client". There are a number of places where "fatal alert" is used, but "error alert" seems to be the defined term. Detail items are: 1. Introduction TLS is application protocol independent; higher-level protocols can layer on top of TLS transparently. It might be informative to describe the nature of the currently-defined application protocol (a bidirectional, reliable byte stream) and similarly, to describe the requirements on the underlying transport (as far as I can tell, also a bidirectional, reliable byte stream). 1.1. Conventions and Terminology There are a number of terms which are frequently used in the text that don't seem to be defined. It's likely that they are only used in exposition, that if all sentences containing them were deleted, the document would still be complete and accurate. But it seems like it would be easier on the reader to define them. Terms that come to mind are: flight -- it seems to be thought that messages are grouped into "flights", where the messages of flight N from one sender cannot be sent until (more or less) it receives all messages of flights M (1 <= M <= N) from the peer. MAC and HMAC -- by a simple count, both of these terms are used exactly 11 times in the document. Is there any functional difference between them? ticket -- it's not clear what this means concretely. Abstractly, it seems to include the set of cryptographic parameters needed to send application data, but concretely I can't figure out if it is the entire block of parameters (struct NewSessionTicket), or whether it is (or can be) just a key that points to the parameters in some database (the ticket *field* of struct NewSessionTicket). A related issue is that there is a field "ticket" in the structure "NewSessionTicket", and, at least conceptually, that structure is also considered a "ticket". I strongly suggest you change the name of the field, and then revise uses of "ticket" to indicate whether they refer to the structure or to the field. Comparing to other parts of the text, I think you may have intended to call the field "identity", as the value in NewSessionTicket.ticket seems to be intended to be copied into PreSharedKeyExtension.OfferedPsks.PskIdentity. RTT, 0-RTT, 1-RTT -- Can these be defined more rigorously? server name, SNI -- These two terms seem to be used interchangeably and as if the reader already understands their meaning and use. Suggest you standardize on one (and list the other as a synonym in the glossary). Also include how "server name" interacts with the server's certificate. advertise -- I think this means when an endpoint sends a message containing an extension saying that it implements a feature or option, soliciting the peer to send a message containing the same extension (and thus agreeing to use the feature/option during this connection). transcript -- It might be useful to put the definition of this in this section. Also, define the "context" or "handshake context", which seems to be the sequence of messages included in the transcript hash (or the concatenation thereof). But doesn't seem clear what messages are in the "handshake context" for any single usage. handshake -- This has two meanings: (1) messages whose content type is "handshake" (see section 5), and (2) messages with content type "handshake" that are part of the initial exchange of such messages. The problem is that there are messages that are (1) but not (2), and so you get confusing language like the title of section 4.6. establish vs. provision -- The document mostly adheres to the convention that pre-shared keys are "provisioned" whereas keys that are set up using TLS (possibly in a previous session) are "established". In particular, "provisioned" is only used as an attribute of keys, whereas "establishing" is an action and "established" keys are ones created by that action. But there are places where the two terms aren't used distinctly. ALPN -- This is used sporadically throughout the document. "hash over" -- A personal gripe of mine is that I look at a hash as a function, and so its value is a "hash of (some string)". I don't mind "hash over X", "hash protects X", and "hash covers X"n when X is conceptualized as a substring of some larger string, that is, there's a Y that is explicitly being excluded. But in a lot of cases in this document, X is explicitly constructed from various fragments, so there's no Y. But maybe all these usages are conventional in cryptography. 2. Protocol Overview The cryptographic parameters used by the secure channel are produced by the TLS handshake protocol. This sub-protocol of TLS [...] I think you want to s/This sub-protocol/The TLS handshake protocol/, since a naive reader (me!) could consider "the secure channel" as a plausible antecedent of "this". 2.1. Incorrect DHE Share Note: The handshake transcript includes the initial ClientHello/ HelloRetryRequest exchange; it is not reset with the new ClientHello. "transcript" has not been defined yet. Perhaps: Note: The handshake transcript (the message input to the MAC in the Finished message) starts with ... 2.2. Resumption and Pre-Shared Key (PSK) Figure 3 shows a pair of handshakes in which the first establishes a PSK and the second uses it: The first handshake does not point out where the PSK is established. Better would be Figure 3 shows a pair of handshakes in which the first establishes a PSK and the second uses it. In the first, a PSK is carried from the server to the client in the NewSessionTicket message. In the second, the identity of the PSK is sent by the client to the server in the ClientHello. 3.1. Basic Block Size The representation of all data items is explicitly specified. The basic data block size is one byte (i.e., 8 bits). "byte" appears in the text 91 times, and "octet" appears 20 times. You probably want to change the uses of "octet" to "byte" for consistency. 3.3. Vectors You may want to move section 3.4 to before section 3.3, because section 3.3 implicitly depends on all numeric fields being unsigned, whereas the fact that all numeric fields are unsigned is only stated in section 3.4. 3.5. Enumerateds An additional sparse data type is available called enum. Each You probably want s/called enum/called enum or enumerated/. Future extensions or additions to the protocol may define new values. Add "... of a previously existing enumerated." 4.1.2. Client Hello In that case, the client MUST send the same ClientHello (without modification) except: s/(without modification) except:/without modification, except:/ For every TLS 1.3 ClientHello, this vector MUST contain exactly one byte set to zero, which corresponds to the "null" compression method in prior versions of TLS. There is an ambiguity in English, where this might mean "the number of bytes in this field which are zero is exactly one". It's a bit hard avoiding the ambiguity, but this seems to work: For every TLS 1.3 ClientHello, this vector MUST have exactly one member. The member MUST be zero, which corresponds to the "null" compression method in prior versions of TLS. -- The actual "Extension" format is defined in Section 4.2. Probably delete "actual". Most uses of "actual" in current writing (including mine) can be profitably deleted. 4.1.4. Hello Retry Request This allows the client to avoid having to compute partial hash transcripts for multiple hashes in the second ClientHello. This seems to be correct but I found it very hard to decode. This is clearer: This allows the client to avoid having to compute hashes of partial transcripts using multiple hash functions, to be used in binders in the second ClientHello. 4.2. Extensions In TLS 1.3, unlike TLS 1.2, extensions are negotiated for each handshake even when in resumption-PSK mode. However, 0-RTT parameters are those negotiated in the previous handshake; mismatches may require rejecting 0-RTT (see Section 4.2.10). I think what you mean is: Even for a session that is resumed using a PSK established in an earlier session, the applicable extensions are negotiated in its initial handshake and aren't carried over from the handshake of the session which established the PSK. However, the parameters applicable to 0-RTT data are those negotiated in the previous handshake; if those parameters are unacceptable to the server, it may reject use of 0-RTT in the session (see Section 4.2.10). -- Servers should be prepared to receive ClientHellos that include this extension but do not include 0x0304 in the list of versions. s/should/SHOULD/ 4.2.2. Cookie Clients MUST NOT use cookies in their initial ClientHello in subsequent connections. I think this becomes cleaner if you omit "in subsequent connections". 4.2.3. Signature Algorithms Items "RSASSA-PSS RSAE algorithms" and "RSASSA-PSS PSS algorithms" both contain: If the public key is carried in an X.509 certificate, it MUST use the rsaEncryption OID [RFC5280]. I think "it" references "certificate", so it would be clearer to say If the public key is carried in an X.509 certificate, the certificate MUST use the rsaEncryption OID [RFC5280]. -- If the corresponding public key's parameters present, [...] s/parameters present/parameters are present/ - Implementations that advertise support for RSASSA-PSS (which is mandatory in TLS 1.3), [...] The phrase "Implementations that advertise" makes me think of the label on the box. I think you mean "Endpoints that advertise". The "extension_data" field of these extension contains a SignatureSchemeList value: I think s/these extension/these extensions/ (rather than s/these extension/this extension/). 4.2.7. Negotiated Groups Items in named_group_list are ordered according to the client's preferences (most preferred choice first). This can be simplified to "Items in named_group_list are in descending order of the client's preferences." 4.2.8. Key Share group The named group for the key being exchanged. Finite Field Diffie-Hellman [DH] parameters are described in Section 4.2.8.1; Elliptic Curve Diffie-Hellman parameters are described in Section 4.2.8.2. I think this should be capitalized as "Finite field Diffie-Hellman ..." and "Elliptic curve Diffie-Hellman ...". key_exchange Key exchange information. The contents of this field are determined by the specified group and its corresponding definition. This could be better defined by rearranging the two items, since the parameters don't describe the group, they describe the key being exchanged: group The value designating the named group for the keys being exchanged, as defined in section 4.2.7. key_exchange The key exchange information. Finite field Diffie-Hellman [DH] parameters are described in Section 4.2.8.1; Elliptic curve Diffie-Hellman parameters are described in Section 4.2.8.2. -- Each KeyShareEntry value MUST correspond to a group offered in the "supported_groups" extension and MUST appear in the same order. I think you mean to require that for a given group there can be only one KeyShareEntry. So you could say Each KeyShareEntry value MUST correspond to a distinct group offered in the "supported_groups" extension, and the KeyShareEntrys MUST appear in the order their groups appear (possibly non-consecutively) in "supported_groups". 4.2.8.1. Diffie-Hellman Parameters This check ensures that the remote peer is properly behaved and isn't forcing the local system into a small subgroup. s/into a small subgroup/into insecure operation/? 4.2.8.2. ECDHE Parameters For the curves secp256r1, secp384r1 and secp521r1, peers MUST validate each other's public value Y by ensuring that the point is a valid point on the elliptic curve. The appropriate validation procedures are defined in Section 4.3.7 of [X962] and alternatively in Section 5.6.2.3 of [KEYAGREEMENT]. This process consists of three steps: (1) verify that Y is not the point at infinity (O), (2) verify that for Y = (x, y) both integers are in the correct interval, (3) ensure that (x, y) is a correct solution to the elliptic curve equation. For these curves, implementers do not need to verify membership in the correct subgroup. It seems that the language of this paragraph is a version behind, particularly in that this paragraph seems to use "Y" differently from the definition of UncompressedPointRepresentation. Comparing with [KEYAGREEMENT], it seems like it ought to read (with changes marked >>>...<<<): For the curves secp256r1, secp384r1 and secp521r1, peers MUST validate each other's public value >>>Q = (X, Y)<<< by ensuring that the point is a valid point on the elliptic curve. The appropriate validation procedures are defined in Section 4.3.7 of [X962] >>>or<<< alternatively in Section >>>5.6.2.3.3<<< of [KEYAGREEMENT]. This process consists of three steps: (1) verify that >>>Q<<< is not the point at infinity (O), (2) verify that >>>both integers X and Y <<< are in the correct interval, >>>and<<< (3) ensure that >>>Q<<< is a correct solution to the elliptic curve equation. For these curves, implementers do not need to verify membership in the correct subgroup. (You can s/or alternatively/and also/) In particular, 5.6.2.3.3 of [KEYAGREEMENT] is "validation without verifying subgroup membership", so it needs to be verified that this procedure expresses the author's intent. 4.2.9. Pre-Shared Key Exchange Modes psk_dhe_ke PSK with (EC)DHE key establishment. In this mode, the client and servers MUST supply "key_share" values as described in Section 4.2.8. s/servers/server/ 4.2.10. Early Data Indication For externally established PSKs, the associated values are those provisioned along with the key. Probably s/externally established/provisioned/. For PSKs provisioned via NewSessionTicket, a server MUST validate that the ticket age for the selected PSK identity (computed by subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age modulo 2^32) is within a small tolerance of the time since the ticket was issued (see Section 8). s/provisioned/established/. s/ticket_age_add/ticket_age_add in the ticket/. 0-RTT messages sent in the first flight have the same (encrypted) content types as their corresponding messages sent in other flights (handshake and application_data) but are protected under different keys. s/as their corresponding messages sent in/as messages of the same types sent in/ After receiving the server's Finished message, if the server has accepted early data, an EndOfEarlyData message will be sent to indicate the key change. This message will be encrypted with the 0-RTT traffic keys. This is awkward. Perhaps After receiving the server's Finished message, if the server has accepted early data, the client will send an EndOfEarlyData message indicate that following (non-early) application data uses the negotiated keys. The EndOfEarlyData message is be encrypted with the 0-RTT traffic keys. -- - Return its own extension in EncryptedExtensions, indicating that it intends to process the early data. s/its own extension/an early_data extension/ "pre_shared_key" extension. In addition, it MUST verify that the following values are consistent with those associated with the selected PSK: s/consistent with/the same as/ If the server chooses to accept the "early_data" extension, then it MUST comply with the same error handling requirements specified for all records when processing early data records. It seems like this could be misread by binding "when processing..." to "specified". This avoids that: If the server chooses to accept the "early_data" extension, then it MUST apply to early data records the same error handling requirements specified for other data records. -- Specifically, if the server fails to decrypt any 0-RTT record following an accepted "early_data" extension it MUST terminate the connection with a "bad_record_mac" alert as per Section 5.2. But probably better to s/any/an/. If the server rejects the "early_data" extension, the client application MAY opt to retransmit early data once the handshake has been completed. Better: [...] MAY opt to retransmit as non-early data the application data contained in the early data records -- Note that automatic re-transmission of early data could result in assumptions about the status of the connection being incorrect. This doesn't quite say what you want. Better Note that after connection establishment, the application may consider the status of the connection to be different than it was for early data, and so transmitting the same bytes as non-early application data may not have the same effect as transmitting them as early application data. -- Similarly, if early data assumes anything about the connection state, it might be sent in error after the handshake completes. A bit awkward. Perhaps Similarly, if early data assumes anything about the connection state, it might be erroneous to re-send the same data after the handshake completes. 4.2.11. Pre-Shared Key Extension The "pre_shared_key" extension is used to indicate the identity of the pre-shared key to be used with a given handshake in association with PSK key establishment. s/indicate/negotiate/ -- because more than one can be offered. selected_identity The server's chosen identity expressed as a (0-based) index into the identities in the client's list. I think this is intended as an index into the (abstract) vectors OfferedPsks.identities and OfferedPsks.binders, as opposed to an offset into the serialized data structures. You could be clearer with selected_identity The server's chosen identity expressed as a (0-based) index into the vector of identities in OfferedPsks. -- identity A label for a key. For instance, a ticket defined in Appendix B.3.4 or a label for a pre-shared key established externally. See issues regarding "ticket" in section 1.1. In TLS versions prior to TLS 1.3, the Server Name Identification (SNI) value was intended to be associated with the session (Section 3 of [RFC6066]), with the server being required to enforce that the SNI value associated with the session matches the one specified in the resumption handshake. However, in reality the implementations were not consistent on which of two supplied SNI values they would use, leading to the consistency requirement being de-facto enforced by the clients. In TLS 1.3, the SNI value is always explicitly specified in the resumption handshake, and there is no need for the server to associate an SNI value with the ticket. Clients, however, SHOULD store the SNI with the PSK to fulfill the requirements of Section 4.6.1. See issue regarding "SNI" in section 1.1. Implementor's note: the most straightforward way to implement the PSK/cipher suite matching requirements is to negotiate the cipher suite first and then exclude any incompatible PSKs. I think you mean: Implementor's note: the most straightforward way for the server to implement the PSK/cipher suite choice requirements is to choose the cipher suite first and then exclude any PSKs incompatible with the chosen cipher suite. since this doesn't seem to describe an interaction between the server and the client, but simply how the server responds to one message. In order to accept PSK key establishment, the server sends a "pre_shared_key" extension indicating the selected identity. I think this sentence would read better as a separate paragraph. This extension MUST be the last extension in the ClientHello. (This facilitates implementation as described below.) Given that the previous paragraph discussed the early_data extension, "This extension" isn't clear. So s/This extension/The "pre_shared_key" extension/. If this value is not present or does not validate, the server MUST abort the handshake. s/does not validate/is not valid/ (An actor validates a value; a value is validated.) 4.2.11.1. Ticket Age The "obfuscated_ticket_age" field of each PskIdentity contains an obfuscated version of the ticket age formed by taking the age in milliseconds and adding the "ticket_age_add" value that was included with the ticket, see Section 4.6.1 modulo 2^32. Clearer would be [...] "ticket_age_add" value that was in the NewSessionTicket for the ticket, modulo 2^32. -- Note that the "ticket_lifetime" field in the NewSessionTicket message is in seconds but the "obfuscated_ticket_age" is in milliseconds. Expand to Note that the "ticket_lifetime" field in the NewSessionTicket message is in seconds but the "obfuscated_ticket_age" and "ticket_age_add" fields are in milliseconds. 4.2.11.3. Processing Order Clients are permitted to "stream" 0-RTT data until they receive the server's Finished, only then sending the EndOfEarlyData message, followed by the rest of the handshake. In order to avoid deadlocks, when accepting "early_data", servers MUST process the client's ClientHello and then immediately send the ServerHello, rather than waiting for the client's EndOfEarlyData message. This is awkward, and omits the remainder of the servers' first flight messages. Better is Clients are permitted to "stream" 0-RTT data until they receive the server's Finished message, only then sending the EndOfEarlyData message, and the rest of the handshake. In order to avoid deadlocks, when accepting early data, servers MUST process the client's ClientHello immediately upon receipt, and immediately send all of its first flight messages from ServerHello through Finished, rather than waiting for the client's EndOfEarlyData message. 4.4. Authentication Messages Certificate The certificate to be used for authentication, and any supporting certificates in the chain. Note that certificate-based client authentication is not available in 0-RTT mode. Probably better to say s/in 0-RTT mode/for 0-RTT data/ -- or perhaps "early data". The following table defines the Handshake Context and MAC Base Key for each scenario: Eh, what? I think what you mean is that this table specifies what base keys are used for which messages. But "Mode" and "Handshake Context" don't seem to be defined terms. It seems to me that a better specification is the annotations in the state diagrams in Appendix A, which note for each message that is sent what key applies to it. Only after reading the text again do I realize that the "Handshake Context" column is listing what the handshake context *is* at various points in time. My confusion connects with the need for a more formal definition of "handshake context". 4.4.1. The Transcript Hash Many of the cryptographic computations in TLS make use of a transcript hash. This value is computed by hashing the concatenation of each included handshake message, including the handshake message header carrying the handshake message type and length fields, but not including record layer headers. I.e., There are a number of awkward spots in how this is phrased. Better: Many of the cryptographic computations in TLS make use of a transcript hash. This value is computed by hashing the concatenation of a sequence of messages in the handshake, with each message including the TLS message type and length fields, but not any headers of the underlying transport protocol. -- Transcript-Hash(M1, M2, ... MN) = Hash(M1 || M2 ... MN) Usually, symbol for "the n-th message" would use lower-case "n" as the index, and one usually puts the operator before and after "...". Also add verbal explanation: The transcript hash of messages M1 through Mn is: Transcript-Hash(M1, M2, ... Mn) = Hash(M1 || M2 || ... || Mn) Continue, As an exception to this general rule, when the server has responded to a ClientHello with a HelloRetryRequest, the first ClientHello is replaced with a special synthetic handshake message of message type "message_hash" whose data part is Hash(first ClientHello). I.e., Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) = Hash(message_hash || /* Handshake type (1 byte) */ 00 00 Hash.length || /* Handshake message length, in bytes (3 bytes) */ Hash(ClientHello1) || /* Hash of ClientHello1 */ HelloRetryRequest || ... || Mn) The reason for this construction is to allow the server not store state after sending HelloRetryRequest by storing just the hash of the first ClientHello in the cookie, rather than requiring it to store all of the ClientHello or the entire intermediate hash state (see Section 4.2.2). -- For concreteness, the transcript hash is always taken from the following sequence of handshake messages, starting at the first This is awkward. Perhaps the transcript hash is always of the following sequence of handshake messages, starting at the first ClientHello and including only those messages that we sent/received: -- In general, implementations can implement the transcript by keeping a running transcript hash value based on the negotiated hash. Probably s/negotiated hash/negotiated hash function/. Also, this needs to include the modification of truncating the last message if it is to include the transcript hash. I think you need something like: If the last message, Mn, is to include the transcript hash, then the transcript hash is always the last field of the message, and that message is first truncated by removing that field from the message. (The message length field of Mn is unmodified; it includes the length of the transcript hash.) Transcript-Hash(M1, M2, ... Mn) = Hash(M1 || M2 || ... || Truncate(Mn)) 4.4.2. Certificate If the corresponding certificate type extension ("server_certificate_type" or "client_certificate_type") was not negotiated in Encrypted Extensions, or the X.509 certificate type was negotiated, then each CertificateEntry contains a DER-encoded X.509 certificate. This needs a reference to RFC 7250 to define certificate type extension. Also, see the general issues regarding extensions. Each following certificate SHOULD directly certify one preceding it. The phrase "one preceding it" allows extraneous certificates in the list, as "one preceding it" doesn't usually require that it be immediately preceding. I think you mean "the one preceding it", which does require it to be immediately preceding, and thus does not allow extraneous certificates in the chain. 4.4.2.1. OCSP Status and SCT Extensions CertificateStatus message. In TLS 1.3, the server's OCSP information is carried in an extension in the CertificateEntry containing the associated certificate. Clearer to phrase it: [...] in the CertificateEntry containing the associated certificate in the Certificate message. -- CertificateRequest message. If the client opts to send an OCSP response, the body of its "status_request" extension MUST be a CertificateStatus structure as defined in [RFC6066]. s/its "status_request" extension"/the "status_request" extension in its Certificate message/. 4.4.2.2. Server Certificate Selection All certificates provided by the server MUST be signed by a signature algorithm advertised by the client, if they are able to provide such a chain (see Section 4.2.3). Probably better a/All certificates/Each certificate/. s/they are/the server is/ If the client cannot construct an acceptable chain using [...] The purpose of this paragraph is not clear. Was "server" meant? If so, it seems to be redundant. I think it is intended to discuss how the client processes the (alleged) certificate chain presented by the server, in which case, it's a sharp change of focus for this section. That could be aided by moving this paragraph to the end of the section and adding some words: If the client cannot construct an acceptable chain from the certificates provided by the server and decides to abort the handshake, then it MUST abort the handshake with an appropriate certificate-related alert (by default, "unsupported_certificate"; see Section 6.2 for more). But it would probably be better to integrate it into section 4.4.2.4. 4.4.2.3. Client Certificate Selection - If the CertificateRequest message contained a non-empty "oid_filters" extension, the end-entity certificate MUST match the extension OIDs recognized by the client, as described in Section 4.2.5. More exact would be "must match the extension OID/value pairs that are recognized by the client." 4.4.2.4. Receiving a Certificate Message It seems inconsistent that the client can send an empty Certificate message, but the server cannot, even though the server can omit sending the Certificate message. 4.4.3. Certificate Verify This message is used to provide explicit proof that an endpoint possesses the private key corresponding to its certificate. I'd prefer s/to its certificate/to the certificate it has presented/. The discussion of how "signature" is formed is awkward and I'm not sure I understand it. E.g., the digital signature is computed "over" a string, but one part of that string is "the content to be signed". I think it could be made clearer as: The algorithm field specifies the signature algorithm used (see Section 4.2.3 for the definition of this field). The signature is a digital signature using that algorithm. The string that is signed is the concatenation of: - A string that consists of octet 32 (0x20) repeated 64 times - The context string - A single 0 byte which serves as a separator - Transcript-Hash(Handshake Context, Certificate) But that leaves unclear what "context string" and "Handshake Context" are. I think you want to define those back in 4.4.1 (and probably also in 1.1) as both being the concatenation of the messages that are in the transcript to this point. I also assume that the Certificate Verify message is truncated when it is put into the Transcript-Hash and "context string", but that needs to be stated. 4.4.4. Finished The key used to compute the finished message is computed from the s/finished/Finished/ The key used to compute the finished message is computed from the Base key defined in Section 4.4 using HKDF (see Section 7.1). This is correct, but you have to read further to understand the key described here isn't the key that encrypts the finished message. It would be easier to understand if the text was rearranged: Structure of this message: struct { opaque verify_data[Hash.length]; } Finished; The verify_data value is computed as follows: finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length) verify_data = HMAC(finished_key, Transcript-Hash(Handshake Context, Certificate*, CertificateVerify*)) * Only included if present. And this is another instance where the poorly-defined "Handshake Context" appears. Any records following a 1-RTT Finished message MUST be encrypted under the appropriate application traffic key as described in Section 7.2. Are there any non-1-RTT Finished messages? And aren't all application data records encrypted under the "appropriate" key? Or is an "application traffic key" different from the keys used for application data early in the connection? This needs to be rephrased somehow, but I can't guess in what way. 4.6. Post-Handshake Messages This section name is awkward. Of course, there are messages after the handshake. I think the problem is that there are "handshake messages", messages with handshake types (or content type "handshake"), that are not part of "the handshake", the initial exchange of handshake-type messages. In the end, you need to decide what terminology to use so that the title of this section makes sense. the appropriate application traffic key. Is there a strict accounting of what messages are encrypted using which key? 4.6.1. New Session Ticket Message message, it MAY send a NewSessionTicket message. This message creates a unique association between the ticket value and a secret PSK derived from the resumption master secret. It would be useful to mention that the resumption_master_secret is defined/computed in section 7.1. Does "ticket value" mean the NewSessionTicket structure or the "ticket" field within it. See issues regarding "ticket" for section 1.1. (Section 4.2.11). Servers MAY send multiple tickets on a single Note the conflation here of "ticket" with "NewSessionTicket message". Any ticket MUST only be resumed with a cipher suite that has the same KDF hash algorithm as that used to establish the original connection. How is a ticket "resumed"? Also, since in a "resumption" connection, the ticket that is used is (or refers to) a PSK, the above statement corresponds to the statement "In addition, it MUST verify that the following values are consistent with those associated with the selected PSK:" in section 4.2.10. Clients MUST only resume if the new SNI value is valid for the server certificate presented in the original session, and SHOULD only resume if the SNI value matches the one used in the original session. What does it mean to say a client "resumes"? Here we suddenly descend into the usage of what seems to be an extension, server_name, which is presumably optional and logically added on to TLS rather than being an integral part of it. Also the logic isn't described very cleanly; I think it means "A client must abort resuming a connection if the ServerHello message does not contain a server_name extension whose value is a valid SNI for the server certificate presented in the original session ...". All of this seems to require that a client MAY NOT resume a connection using a ticket issued during a connection in which the server did not present a certificate for itself. But that constraint wasn't stated in section 2.2, despite being a global constraint on the structure of sessions. Or am I wrong in believing that the client chooses to resume the connection by placing the ticket in the ClientHello *before* it receives the ServerHello (which contains the SNI)? This paragraph seems to be written as if the client decides to resume after receiving ServerHello. ticket_lifetime Indicates the lifetime in seconds as a 32-bit unsigned integer in network byte order from the time of ticket issuance. Probably better to s/the time of ticket issuance/the time the NewSessionTicket was sent to the client/, unless "issuance"/"to issue" is explicitly defined somewhere. ticket The value of the ticket to be used as the PSK identity. The ticket itself is an opaque label. This shows the ambiguities around "ticket"; this specification says that "'ticket' is the value of the ticket to be used as the PSK identity". Is it intended that the "ticket" field of NewSessionTicket will become the "identity" field of PskIdentity.OfferedPsks.PreSharedKeyExtension? max_early_data_size The maximum amount of 0-RTT data that the client is allowed to send when using this ticket, in bytes. Only Application Data payload (i.e., plaintext but not padding or the inner content type byte) is counted. A server receiving more than max_early_data_size bytes of 0-RTT data SHOULD terminate the connection with an "unexpected_message" alert. Note that servers that reject early data due to lack of cryptographic material will be unable to differentiate padding from content, so clients SHOULD NOT depend on being able to send large quantities of padding in early data records. The last sentence assumes that a server that "reject early data due to lack of cryptographic material" will be strict and count all bytes in early data messages against the max_early_data_size quota. However, a server in such a situation could be liberal and not bother counting any bytes -- since it will be discarding early data messages (immediately after discovering that it can't decrypt them), it never has to buffer more than one of them. Unless I'm overlooking something, this advice isn't needed, since a server in this situation isn't buffering early data. 4.6.2. Post-Handshake Authentication All of the client's messages for a given response MUST appear consecutively on the wire with no intervening messages of other types. Better, "consecutively in the underlying transport stream". But that is a little vague given the message/fragment/record mechanism. More exactly, All of the client's messages for a given response MUST appear consecutively on the wire, that is, the records containing the fragments of the messages composing the client's response must appear consecutively in the underlying transport stream. 4.6.3. Key and IV Update I think you want to promote the first paragraph before the data structure definition. 5. Record Protocol and 5.1. Record Layer The text isn't very clear about the message/fragment/record mechanism. The text wants to consider the data for each content type to consist of a series of messages. The messages are cut into fragments. Adjacent fragments within one content type stream can be concatenated to form the content of TLSPlaintext structures. One problem is that despite this model, the boundary between messages isn't carried through the transport. For application data, message boundaries are lost entirely. For handshake and alert content types, there are some complex restrictions how their message boundaries show up as record boundaries, but the actual framing of messages is done "in band" by the message length fields. A closer description of the services TLS provides is that the data for each content type is a stream that can be broken arbitrarily into fragments that are the content of records, except: - The boundaries of alert messages must be boundaries of the records that carry them and no record boundary can be introduced into an alert message. - If two records contain fragments of the same handshake message, all records between them must contain only fragments of that handshake message. - If there is a key change between the sending of two handshake messages, there must be a record boundary between them. - Handshake messages MUST NOT span key changes. Implementations MUST verify that all messages immediately preceding a key change align with a record boundary; if not, then they MUST terminate the connection with an "unexpected_message" alert. Because the ClientHello, EndOfEarlyData, ServerHello, Finished, and KeyUpdate messages can immediately precede a key change, implementations MUST send these messages in alignment with a record boundary. Is this description correct? As written, it says "because a key change can happen after message X, there must be a record boundary after message X", which isn't exactly the same as "Handshake messages MUST NOT span key changes" -- unless there is always a key change following these message types, in which case s/can immediately precede/always precede/. I think the three points listed above give a clearer and more accurate version. Implementations MUST NOT send zero-length fragments of Handshake types, even if those fragments contain padding. Presumably implementations MUST NOT send zero-length fragments of alert messages also. When record protection has not yet been engaged, TLSPlaintext structures are written directly onto the wire. Better "... sent directly using the underlying transport protocol". 5.2. Record Payload Protection I *think* what's going on with record protection is: - The TLSPlaintext is turned into a TLSInnerPlaintext by moving the type field, removing the length field, and copying "fragment" to "content". (Why do the structures use different names for the data fragment?) - The encryption is done by: AEADEncrypted = AEAD-Encrypt(write_key, nonce, plaintext) where plaintext is TLSInnerPlaintext and AEADEncrypted becomes TLSCiphertext.encrypted_record. - TLSCiphertext is transmitted. But the text doesn't say this at all plainly. I would add before "AEAD algorithms take...": The TLSPlaintext is converted into a TLSInnerPlaintext by copying the type field, removing the length field, and copying the fragment field. (assuming we rename "content" to "fragment") Then replace the equation by: encrypted_record = AEAD-Encrypt(endpoint_write_key, nonce, TLSInnerPlaintext) -- The key is either the client_write_key or the server_write_key, the nonce is derived from the sequence number (see Section 5.3) and the client_write_iv or server_write_iv, and the additional data input is empty (zero length). It would be clearer to move the reference to read "the nonce is derived from the sequence number and the client_write_iv or server_write_iv (see Section 5.3)" as section 5.3 describes both the sequence number and the derivation. Similarly, the decryption algorithm is better expressed by TLSInnerPlaintext = AEAD-Decrypt(peer_write_key, nonce, encrypted_record) -- This limit is derived from the maximum TLSPlaintext length of 2^14 octets + 1 octet for ContentType + the maximum AEAD expansion of 255 octets. s/TLSPlaintext/TLSInnerPlaintext/ -- the maximum length of TLSPlaintext is 2^14 + 5. 5.3. Per-Record Nonce The appropriate sequence number is incremented by one after reading or writing each record. The first record transmitted under a particular traffic key MUST use sequence number 0. I think the first and second paragraphs could be profitably combined: A 64-bit sequence number is maintained separately for reading and writing records and is incremented by one after reading or writing each record. Each sequence number is set to zero at the beginning of a connection and whenever the key for that direction is changed; the first record transmitted under a particular traffic key uses sequence number 0. -- Because the size of sequence numbers is 64-bit, they should not wrap. The sense of "should" is not clear. I think what you want to say is Because the size of sequence numbers is 64 bits, there is no need to allow sequence numbers to wrap. -- Each AEAD algorithm will specify a range of possible lengths for the per-record nonce, from N_MIN bytes to N_MAX bytes of input I think this is clearer (as it makes clear where N_MIN comes from): Each AEAD algorithm specifies an N_MIN and N_MAX, which give the range of possible lengths in bytes of the per-record nonce. 5.4. Record Padding Padding is a string of zero-valued bytes appended to the ContentType field before encryption. More exact would be Padding is a string of zero-valued bytes following the type field in TLSInnerPlaintext. -- The presence of padding does not change the overall record size limitations - the full encoded TLSInnerPlaintext MUST NOT exceed 2^14 + 1 octets. If the maximum fragment length is reduced, as for example by the max_fragment_length extension from [RFC6066], then the reduced limit applies to the full plaintext, including the content type and padding. I think you want s/encoded TLSInnerPlaintext/TLSInnerPlaintext/ -- all data structures are defined with a concrete representation, so "encoded" is redundant, but "encoded" could be confused with "encrypted" and the encrypted plaintext can be longer than the plaintext. If the maximum fragment length is reduced, as for example by the max_fragment_length extension from [RFC6066], then the reduced limit applies to the full plaintext, including the content type and padding. This needs clarification. I think you mean that if the m.f.l. is reduced, then the limit on TLSInnerPlaintext is reduced to m.f.l.+1, but that's not what this says. OTOH, if this text is accurate, the maximum length of the fragment proper is m.f.l.-1. 6. Alert Protocol Alert messages convey a description of the alert and a legacy field that conveyed the severity of the message in previous versions of TLS. In TLS 1.3, the severity is implicit in the type of alert being sent, and the 'level' field can safely be ignored. I think at this point you want to insert "Alerts are divided into two classes: closure alerts and error alerts." Stateful implementations of tickets (as in many clients) SHOULD discard tickets associated with failed connections. What is a "ticket" here? And what are the "associations" in question? E.g., presumably it includes the ticket used when attempting to establish a connection if the attempt fails. But if a NewSessionTicket is received during a connection, and the connection is later aborted, does the client have to discard the remembered NewSessionTicket? All the alerts listed in Section 6.2 MUST be sent as fatal and MUST be treated as fatal regardless of the AlertLevel in the message. Unknown alert types MUST be treated as fatal. This was remarkably confusing until I figured out that the first "fatal" means "with AlertLevel "fatal"" and the second and third "fatal" mean "indicates abortive closure"! Better: All the alerts listed in Section 6.2 MUST be sent with AlertLevel "fatal" and when received MUST be treated as error alerts regardless of the AlertLevel in the message. Unknown alert types MUST be treated as error alerts. 6.1. Closure Alerts user_canceled This alert notifies the recipient that the sender is canceling the handshake for some reason unrelated to a protocol failure. If a user cancels an operation after the handshake is complete, just closing the connection by sending a "close_notify" is more appropriate. This alert SHOULD be followed by a "close_notify". This alert is generally a warning. What does "This alert is generally a warning." mean? What is it a warning of? Each party MUST send a "close_notify" alert before closing its write side of the connection, unless it has already sent some other fatal alert. This implies that close_notify is a "fatal alert" (properly, error alert). And "before closing the write side of the connection" is not clear, since sending close_notify *is* closing the write side of the connection. Better: Each party MUST send a "close_notify" alert before closing the write side of the underlying transport connection, unless it has already sent some other error alert. (Unless I'm mistaken regarding what is intended.) I take it that there is no "close read side" operation. (If that existed, TLS could generate the "broken pipe" error -- the sender wants to transmit more data but the receiver is unwilling to receive it.) If the application protocol using TLS provides that any data may be carried over the underlying transport after the TLS connection is closed, the TLS implementation MUST receive a "close_notify" alert before indicating end-of-data to the application-layer. No part of this standard should be taken to dictate the manner in which a usage profile for TLS manages its data transport, including when connections are opened or closed. This isn't clear too me. The second sentence seems to mean: A usage profile for TLS specified how it manages its data transport, including when connections are opened or closed. No part of this standard should be taken to dictate the manner in which a usage profile for TLS manages its data transport. But I can't figure out what the first sentence means. It seems to mean "If ... a TLS implementation MUST receive a "close_notify" alert before indicating end-of-data to its application-layer.", but that's obvious behavior, what else would cause it to signal EOD? Note: It is assumed that closing the write side of a connection reliably delivers pending data before destroying the transport. I think you mean Note: It is assumed that closing the write side of a connection will cause the peer TLS implementation to reliably deliver all transmitted data before [what?] 7. Cryptographic Computations The TLS handshake establishes one or more input secrets which are combined to create the actual working keying material, as detailed below. Probably delete "actual". Most uses of "actual" in current writing (including mine) can be profitably deleted. 7.1. Key Schedule Given the terminology in RFC 5869, struct HkdfLabel probably should be called HkdfInfo, as it is the "info" argument to HKDF-Expand. Messages are the concatenation of the indicated handshake messages, s/Messages are/Messages is/! Given a set of n InputSecrets, the final "master secret" is computed by iteratively invoking HKDF-Extract with InputSecret_1, [...] This is hard to follow. If I understand correctly, this is a general description of the mechanism that is diagrammed below. But, e.g., "secret" is used for at least two categories of quantities. It would be clearer to phrase this: Generally, we use a construction that takes as input a sequence of n InputSecrets and from them computes a sequence of derived secrets. The initial derived secret is simply a string of Hash.length bytes set to zeros. Each successive derived secret is computed by invoking HKDF-Extract with an InputSecret and the preceding derived secret as inputs. Concretely, for the present version of TLS 1.3, the construction proceeds as diagrammed below, with the InputSecrets on the left side and the derived secrets passing as shown by the downward arrows. The InputSecrets are added in the following order, where if a given InputSecret is not available, then the 0-value is used in its place: -- - HKDF-Extract is drawn as taking the Salt argument from the top and the IKM argument from the left. Append "with its output to the bottom and the name of the output at the right". 7.2. Updating Traffic Keys and IVs I think you want to remove "and IVs" here. IVs aren't mentioned in this section. Of course, changing the traffic key changes the IV, but it also changes the write key, and the write key isn't mentioned in the section title. 7.3. Traffic Key Calculation I think you want to title the section "Write Key and IV Calculation". And you want to (again) de-genericize the text: The traffic keying material (*_write_key and *_write_iv) is generated from the following input values: - A secret value, the applicable *_traffic_secret - A purpose value indicating the specific value being generated - The length of the key 7.4.1. Finite Field Diffie-Hellman For finite field groups, a conventional Diffie-Hellman computation is performed. I think you need a reference for this! 7.5. Exporters A separate interface for the early exporter is RECOMMENDED, especially on a server where a single interface can make the early exporter inaccessible. What does "where a single interface can make the early exporter inaccessible" mean? If you don't have a separate interface for the early exporter, how does "a single interface" make it inaccessible? 8.1. Single-Use Tickets If the tickets are not self-contained but rather are database keys, and the corresponding PSKs are deleted upon use, then connections established using one PSK enjoy forward secrecy. What does "one PSK" mean here? Do you mean "established using such a PSK" (equivalently "established using such a ticket")? Also, the question again arises as to what a "ticket" *is*. Because this mechanism requires sharing the session database between server nodes in environments with multiple distributed servers, it Probably more conventional to say "requires a shared database between all server instances". 8.2. Client Hello Recording The first paragraph is hard to follow. I think it could be clarified along these lines: An alternative form of anti-replay is to record a unique value derived from the ClientHello (generally either the random value or the PSK binder) and reject duplicates. Recording all ClientHellos causes state to grow without bound, but a server can instead retain ClientHellos only within a given time window and use the "obfuscated_ticket_age" to determine whether the ClientHello was generated by the client recently. Thus, the server can ensure that it only allows 0-RTT data in connections established by non-duplicate ClientHellos which were generated by the client within the recording window. -- The server MUST derive the storage key only from validated sections of the ClientHello. If the ClientHello contains multiple PSK identities, then an attacker can create multiple ClientHellos with different binder values for the less-preferred identity on the assumption that the server will not verify it, as recommended by Section 4.2.11. I.e., if the client sends PSKs A and B but the server prefers A, then the attacker can change the binder for B without affecting the binder for A. At this point, a conditional needs to be inserted; otherwise the argument you're making is only implicit. If the server uses the binder for B as part of the storage key, these variations on the ClientHello will not be detected by the server as duplicates of each other, and the server will accept all of them. Then continue with: This may cause side effects such as replay cache pollution, although any 0-RTT data will not be decryptable because it will use different keys. If the validated binder or the ClientHello.random are used as the storage key, then this attack is not possible. -- When implementations are freshly started, they SHOULD reject 0-RTT as long as any portion of their recording window overlaps the startup time. Otherwise, they run the risk of accepting replays which were originally sent during that period. I think this needs a couple of changes of phrasing: When an implementation is restarted with a cleared recording memory, it SHOULD reject 0-RTT as long as the startup time is within the recording window. Otherwise, it runs the risk of accepting replays of ClientHellos which were sent during the previous execution. 8.3. Freshness Checks Variations in client and server clock rates are likely to be minimal, though potentially with gross time corrections. What does "gross time corrections" mean? I think you mean "with moments of large change in the clock time", but that isn't a feature of the clock *rate*. I think this is more accurate: Differences between client and server clock times are likely to be minimal, though there will sometimes be gross differences due to uninitialized clocks and misconfigured time zones. -- After early data is accepted, records may continue to be streamed to the server over a longer time period. More clear as After the server accepts early data is accepted, the client may continue to send early data to the server over a longer time period than the freshness window for ClientHellos. 9.1. Mandatory-to-Implement Cipher Suites A TLS-compliant application MUST support digital signatures with rsa_pkcs1_sha256 (for certificates), rsa_pss_rsae_sha256 (for CertificateVerify and certificates), and ecdsa_secp256r1_sha256. It seems that ecdsa_secp256r1_sha256 is missing a statement of what uses it must be supported for. 9.2. Mandatory-to-Implement Extensions - If containing a "supported_groups" extension, it MUST also contain a "key_share" extension, and vice versa. An empty KeyShare.client_shares vector is permitted. I think this is a bit better expressed as: - If containing a "supported_groups" extension, it MUST also contain a "key_share" extension (which may contain an empty KeyShare.client_shares vector), and vice versa. -- Additionally, all implementations MUST support use of the "server_name" extension with applications capable of using it. I'm not clear what the test is for "applications capable of using SNI". I think you want to turn the conditional around: An application profile MAY require that the endpoint's TLS implementation supports use of the "server_name" extension. 9.3. Protocol Invariants - A server receiving a ClientHello MUST correctly ignore all unrecognized cipher suites, extensions, and other parameters. Otherwise, it may fail to interoperate with newer clients. In TLS 1.3, a client receiving a CertificateRequest or NewSessionTicket MUST also ignore all unrecognized extensions. This needs to be split, because the two parts are about different roles: - A server receiving a ClientHello MUST correctly ignore all unrecognized cipher suites, extensions, and other parameters. Otherwise, it may fail to interoperate with newer clients. - In TLS 1.3, a client receiving a CertificateRequest or NewSessionTicket MUST also ignore all unrecognized extensions. 11. IANA Considerations - TLS Alert Registry: Future values are allocated via Standards Action [RFC8126]. IANA [SHALL update/has updated] this registry to include values for "missing_extension" and "certificate_required". It would be nice to add a finer-grained "minor alert code" registry. Appendix A. State Machine It would be helpful if the state diagrams were extended to describe the activity on the "control channel" (handshake and alert content types) after CONNECTED, that is, what happens while the connection is established and how connections are shut down. Appendix B. Protocol Data Structures and Constant Values There is no listing of the value structure corresponding to each extension type. Extensions collectively are only defined as: struct { ExtensionType extension_type; opaque extension_data<0..2^16-1>; } Extension; This is a problem because the name of the value struct is not systematically derived from the name of the extension_type value! E.g., "signature_algorithms" and "signature_algorithms_cert" use SignatureSchemeList as a value, and you can only reliably discover that by searching through the whole of the text. B.4. Cipher Suites A symmetric cipher suite defines the pair of the AEAD algorithm and hash algorithm to be used with HKDF. Better phrased: A symmetric cipher suite is the pair of an AEAD algorithm and a hash algorithm to be used with HKDF. C.3. Implementation Pitfalls - As a server, do you send a HelloRetryRequest to clients which support a compatible (EC)DHE group but do not predict it in the "key_share" extension? This needs an additional condition: - As a server, if you select an (EC)DHE group which the client supports but for which the client did not provide a KeyShareEntry, do you send a HelloRetryRequest? Appendix D. Backward Compatibility Prior versions of TLS used the record layer version number for various purposes. (TLSPlaintext.legacy_record_version and TLSCiphertext.legacy_record_version) As of TLS 1.3, this field is [...] I think this was intended to be formatted thusly: Prior versions of TLS used the record layer version number (TLSPlaintext.legacy_record_version and TLSCiphertext.legacy_record_version) for various purposes. As of TLS 1.3, this field is [...] D.5. Backwards Compatibility Security Restrictions The security of SSL 3.0 [SSL3] is considered insufficient for the reasons enumerated in [RFC7568], and MUST NOT be negotiated for any reason. s/and MUST NOT/and it MUST NOT/, with "it" referring to "SSL 3.0", as otherwise the verb "MUST NOT" is parallel to the verb "is" and the subject of "MUST NOT" is "the security of SSL 3.0". E.1. Handshake - A set of "session keys" (the various secrets derived from the master secret) from which can be derived a set of working keys. Is this consistent with the usual meaning of "session key"? My understanding (which may be wrong) is that a "session key" is the key for a "session", i.e., an entire connection. Perhaps there is already a defined term in the text that covers the use you intend. Note that these properties are not necessarily independent, but reflect the protocol consumers' needs. The significance of "but" is not clear here, as it seems to be placing in opposition "reflect the ... needs" and "independent". I think this is probably closer to what you meant: Note that these properties are not necessarily independent, but together they cover the protocol consumers' needs. -- Uniqueness of the session keys: Any two distinct handshakes should produce distinct, unrelated session keys. Individual session keys produced by a handshake should also be distinct and unrelated. It's not clear how two session keys produced by a single handshake can be "unrelated". I suspect there's a known technical term for this, like "cryptographically independent" (to parallel "cryptographically random"). A similar term is needed in section E.1.4. If fresh (EC)DHE keys are used for each connection, then the output keys are forward secret. When it is used as an adjective, you hyphenate "forward-secret". E.1.3. 0-RTT See Section 4.2.10 for one mechanism to limit the exposure to replay. This discussion is now in section 8. [END]