Last Call Review of draft-ietf-tls-tls13-24
review-ietf-tls-tls13-24-genart-lc-worley-2018-03-02-00

Request Review of draft-ietf-tls-tls13
Requested rev. no specific revision (document currently at 28)
Type Last Call Review
Team General Area Review Team (Gen-ART) (genart)
Deadline 2018-03-02
Requested 2018-02-16
Other Reviews Secdir Last Call review of -24 by Rich Salz (diff)
Review State Completed
Reviewer Dale Worley
Review review-ietf-tls-tls13-24-genart-lc-worley-2018-03-02
Posted at https://mailarchive.ietf.org/arch/msg/gen-art/mzpZ7CjPmUCfDE-iFCiJtEogHKo
Reviewed rev. 24 (document currently at 28)
Review result Ready with Nits
Draft last updated 2018-03-02
Review completed: 2018-03-02

Review
review-ietf-tls-tls13-24-genart-lc-worley-2018-03-02

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
<https://wiki.tools.ietf.org/area/gen/wiki/GenArtfaq>.

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]