Subject: [TLS] Fully encrypted and authenticated headers (was Re: Encrypting record headers: practical for TLS 1.3 after all?)
From: Bryan A Ford <[email-masked]>
To: "[email-masked]" <[email-masked]>
Date: Tue, 1 Dec 2015 17:22:13 +0100
List-Id: "This is the mailing list for the Transport Layer Security working group of the IETF." <tls.ietf.org>

Some of the parallel discussion on the SSH list - especially comments by
Simon Tatham and Niels Möller - made me think of an alternative design
that would not only hide TLS headers but also ensure that they are
integrity-checked by the receiver *before* the receiver attempts to
interpret the record header in any way.  This alternative approach
already feels to me like an improvement over my last one, and perhaps
even those who are skeptical of the need to encrypt TLS headers might
appreciate the desirability of closing vulnerabilities to
length-fiddling attacks: e.g., where an attacker sets the high bit of
the length field in the last TLS record so that the receiver will likely
hang on a long read before detecting the change.

Proposal:

The basic idea is in some respects even simpler than the last one:

1. We move the TLS header from the "additional data" field to the start
of the "plaintext" field.  That is, the sender encrypts the header along
with the payload, and the receiver decrypts and integrity-checks the
header along with the payload, using the standard AEAD interface.  The
"additional data" field now contains *only* the sequence number, which
both sender and receiver track and compute implicitly.

2. The 2-byte length field in each record's header no longer indicates
the length of the *current* record but instead indicates the length of
the *next* record.  The length of the first record might be defined in a
new field we add to the handshake/key-exchange protocol, or it might
simply be set to some well-known standard first record size.

That's basically it.  The key advantage is that this way the receiver
always "knows" the correct size of each record before having to read or
interpret any part of it in any way.  In fact, because the record is
fully encrypted, the receiver implementation is basically forced to use
the AEAD to decrypt and integrity-check the record before doing anything
at all with it, which closes down not only the well-known
length-fiddling attack that has dogged SSH, but a whole class of
possible potential implementation bugs regarding the (delicate) handling
of cleartext TLS headers before they've been integrity-checked.
Basically, this ensures that if the receiver have any bugs in the
handling of the TLS header, they can be exploited only by its
communication partner, and not by an attacker in the middle of the path.
 To me this seems like a useful robustness benefit completely
independent of the (also desirable) benefits with respect to traffic
analysis.

Choosing next-record lengths:

Of course this approach presents the minor challenge that the sender
must always "commit" to the length of the next record before sending the
current one.  But this challenge is pretty easy to deal with.  The
easiest way to handle it, of course, is if the sender simply pads all
packets to the same record length - a solution I would generally be
quite happy with due to its complementary traffic analysis resistance
benefits, and I would even favor explicitly encouraging that solution in
the TLS RFC.  I would love it if the next TLS RFC ended up containing a
clause of the form:

	The TLS sender SHOULD always transmit TLS records of
	length exactly X, unless this is deemed impractical
	due to overriding performance or bandwidth-efficiency
	considerations.

...where "X" is some reasonable value we discuss and decide on.

But my (new) proposal does not require all TLS implementations to
maintain a fixed record size.  If the sender implementation wants to use
variable-size records, it can still process batches of records written
together normally up until the last record in the batch.  But when
processing the last record in a write - or in general the last record
before "going idle" where it does not yet have a next record to send -
the sender simply "picks something."  That something might for example
be a minimum record size, meaning the next record will be big enough to
hold a "next record length" indicating a useful length, but will
otherwise be basically a no-op record, so we'll pay the cost of an extra
small record and AEAD encryption/decryption each time transmission
resumes after being idle.

Alternatively the sender of variable-length records might pick a
"typical record size" as the next-record-size to transmit in the last
record in a burst; that way once transmission resumes the next record
can probably carry useful data, but might need padding if its committed
size is larger than the amount of data to be transmitted.  The
(important) existence of the padding mechanism already added in TLS 1.3
makes this alternative possible.

DTLS:

Now there's still the important question of whether this (new) proposal
could be made to work in the context of DTLS.  For the DTLS case, my
current thinking is that some elements of my earlier proposal is
probably more suitable: namely using a stream cipher (or AEAD used as a
stream cipher) to encrypt and recognize the explicitly-transmitted
sequence numbers that DTLS needs.  This could operate basically the same
as I described in my earlier E-mail on this topic.  Note that the length
field is no longer a problem in DTLS as it is in TLS, because the
receiver already gets the length of the datagram from UDP.

Comments/discussion?  Is the above more (or less) palatable than my
previous proposal?

Cheers
Bryan