Subject: Re: [TLS] Application layer interactions and API guidance
From: Ilari Liusvaara <[email-masked]>
To: Kyle Rose <[email-masked]>
Cc: "[email-masked]" <[email-masked]>
Date: Wed, 12 Oct 2016 20:02:37 +0300
List-Id: "This is the mailing list for the Transport Layer Security working group of the IETF." <tls.ietf.org>

On Wed, Oct 12, 2016 at 11:54:59AM -0400, Kyle Rose wrote:
> On Wed, Oct 12, 2016 at 4:11 AM, Ilari Liusvaara <[email-masked]>
> wrote:
> 
> Ok, I see where you're going with this. I'm not sure whether I would put
> the ALP filtering logic in the API or do something more like:
> 
> early_data = get_early_data()
> is_early_data_good = <application inspects the early data>
> start_server_handshake(reject_early_data = !is_early_data_good)
> 
> This allows the server to decide whether or not to reject early data on any
> basis, not just ALP. But maybe a shortcut is good for the common case.

You need ALP if multiple are supported, since some applications might
support 0-RTT and some not.

Also, TLS 1.3 0-RTT assumes that 0-RTT transport is all-or-none.
 
> The reason for waiting for application to ACK the 0-RTT error is that
> > the thing is MT-safe, so one has to prepare for races..
> >
> 
> I'm frankly skeptical of the utility of (most) thread safety at the socket
> level. (Safety against crashes, yes; safety against arbitrary interleaving
> of stream data, no.) IMO, semantic guarantees around multiplexing belong in
> a higher layer. Otherwise, the API must impose additional constraints
> (e.g., max write size) that apply across the board, even to applications
> that will never write from multiple threads, and must require the stack
> provide additional guarantees (e.g., all-or-none writes) that the developer
> has to know about.

That was actually mostly artifact of the programming language (easiest
to make things that aren't thread-locked fully thread-safe, even if
there is an intermediate step).
 
> Given how much TLS libraries try to look like Berkeley sockets to
> applications, you'll probably see fewer usage errors if that multiplexing
> logic is implemented at the application layer with an upward-facing
> interface that either looks nothing like sockets or that presents distinct
> logical sockets for distinct streams, intended for use by one thread at a
> time, multiplexed over the same connection.

Well, this lib looks nothing like Berkeley sockets. :-)
 
> > However, receiving any 1-RTT data after 0-RTT data does not imply that
> > the 0-RTT data was not replayed!
> >
> 
> I think I may have been imprecise. By "received" I mean "received by the
> application", i.e., "delivered by the stack". By the time the stack
> delivers one byte of 1-RTT data to the application, we know:
> 
> (1) Client {Finished} has been received by the server
> (2) which authenticates ClientHello
> (3) which incorporates early_data
> (4) Client {Finished} is protected by client_handshake_traffic_secret
> (5) which incorporates ServerHello
> (6) which incorporates the server random
> (7) which means that secret is fresh (i.e., not subject to replay)
>
> By this point in the connection, there is proof that early_data has not
> been replayed. The application doesn't necessarily know this when the early
> data is first delivered, but it can find out later, which may be all that
> some applications want. Clearly not all, as you point out:

This is actually only useful if the application can cancel out effects
of 0-RTT if handshake fails... Which tends to be fraught with peril to
implement.

Because the transport delay from client starting sending CH to server
first receiving first byte past some point is the same if the data
before the point is sent as 0-RTT or 1-RTT!

And the dominant component in transport delay tends to be round-trip
delay.

> > Do we want to support the case in which 0-RTT failure means the client can
> > > send entirely different data? If so, then the above isn't general enough,
> > > but the client API could offer an option to say "don't resend this data
> > if
> > > 0-RTT fails" with some flag set on this condition or (for event systems)
> > a
> > > callback registered to do something more interesting.
> >
> > There's the case where ALP mismatches (and unfortunately, due to how
> > ALPN and 0-RTT interact, mismatches can happen in cases other than
> > just that 0-RTT is fundamentially impossible).
> >
> > In that case, the data is obviously different. Then there are also
> > things like the planned 0-RTT tokbind, where the data sent on 0-RTT
> > failure is different from the original data.
> >
> 
> Agreed that this is a perfect example.
> 
> Since the early_data has to be received by the stack and be authenticated
> as a single chunk as part of the ClientHello, an API that returns it as a
> monolithic blob (like get_early_data, as above) solves the problem of
> associating requests/tokens to channel bindings. If some middleware wants
> to offer a stream interface to this buffer because that's the idiom for the
> particular language, that probably makes sense, but I suspect the low-level
> interface for a C implementation will be something more like "Here's a
> pointer and a length; go nuts."

The 0-RTT data is not part of ClientHello. It is sent in streaming
manner (with handshake blocking if it hans't been completely sent by
the time ServerFinished is received.

ClientFinished does _not_ MAC 0-RTT data, even in case of successful
transport.

Also, the tokbind case would require interface that lets the application
get to the connection in state where ClientHello has been sent but no
0-RTT data yet, since some of the parameters in 0-RTT data do actually
depend on the exact ClientHello sent.

> > The above API also doesn't support the case in which the server wants to
> > > treat 0-RTT data entirely different from 1-RTT data, which would suggest
> > a
> > > side-band/two channel API. Is this interesting to anyone? I'm not sure I
> > > can see any use for purposely ignoring post-hoc authentication of early
> > > data.
> >
> > I regard post-hoc authentication as quite dangerous. At least in
> > some applications it is a deadly security mistake.
> >
>
> How so? There's certainly some danger in mixing the two data streams for
> applications that really care about the state of forward secrecy for each
> byte. But I'm having a tough time understanding why finding out later that
> "No, that early_data was in fact not replayed" is a bad thing. IANAC:
> genuinely curious here.

Well, I was referring to the general problem that finding out that 'the
peer is X' and applying it to the data sent in the same TLS connection
before that point can result in very nasty surprises.

And also, receiving 1-RTT data does not imply that the 0-RTT data
itself was not replayed (just that any replay it is of didn't
complete, assuming PSK keys are secret).

-Ilari