Re: [TLS] Application layer interactions and API guidance

Kyle Rose <> Wed, 12 October 2016 15:55 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 280DC129472 for <>; Wed, 12 Oct 2016 08:55:05 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -2.7
X-Spam-Status: No, score=-2.7 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_LOW=-0.7, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Authentication-Results: (amavisd-new); dkim=pass (1024-bit key)
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id jeTlltXP0ET3 for <>; Wed, 12 Oct 2016 08:55:01 -0700 (PDT)
Received: from ( [IPv6:2607:f8b0:400d:c09::233]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by (Postfix) with ESMTPS id 7A1B612943A for <>; Wed, 12 Oct 2016 08:55:01 -0700 (PDT)
Received: by with SMTP id z190so39170633qkc.2 for <>; Wed, 12 Oct 2016 08:55:01 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=google; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=5CbpLOZkZIKOYrRLU9To0NKpeAkX7V7aRc+o475/KiU=; b=aIyaZarYuh9YRz+ruHb1iL5U3D4EzvgjFf6Zigo/qFeAeyB+fs3k+yo2CgRLdcxagD O5TGLI0IgdIK5j6WvLpPhOgWGrM5JydRqPinMqd5rqPZYHdUxTQ3bN9T81fr2nHK+7Li AghFtjKSt8Rh0+bfJ3wu+CAvvQjZ+7lwFERRk=
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=5CbpLOZkZIKOYrRLU9To0NKpeAkX7V7aRc+o475/KiU=; b=Idqe6JlbsXnzWb+k9fKlQOsV3B0JaVdoTNuL6caux+gHHtUTHB7M8hZYG6eVGrVm+w Tcts51SbKSCNuGI8COgksLwvn02Eu/9/WvEHynDHnfqWlx3XmYPOjd1AFBLm2u7/0rqw fkD4QhnRI/c1gpHwh4E6u3+6Up0BeaPp2t3ZxCiwyE3hbb4iATjTjYZLXC2rD/9l6dFX q6cRYuy7I3ZvLCAr4QSz2VlygaTaPB1cXoxXsLNXTXMJqVpxN9ydYhQI7swQoxp5sKBr jV+n6Z8EFKW7K3DLi692yCd73LkcV3eBzNW9Xu9hxNMeJY59J99zCeu+YKm/Y1g1W65i M84A==
X-Gm-Message-State: AA6/9RnRsKkvt7CCA/u/imPgYfAf/fC33ObENR+qeyFHj+I68m3xq5zoCRP4G15EgUqxoRi+aNCfUR1Cwam2/g==
X-Received: by with SMTP id b3mr1812237qkg.77.1476287700621; Wed, 12 Oct 2016 08:55:00 -0700 (PDT)
MIME-Version: 1.0
Received: by with HTTP; Wed, 12 Oct 2016 08:54:59 -0700 (PDT)
X-Originating-IP: []
In-Reply-To: <>
References: <> <> <> <> <> <> <> <> <> <> <>
From: Kyle Rose <>
Date: Wed, 12 Oct 2016 11:54:59 -0400
Message-ID: <>
To: Ilari Liusvaara <>
Content-Type: multipart/alternative; boundary="94eb2c09743efb7631053ead03eb"
Archived-At: <>
Cc: "" <>
Subject: Re: [TLS] Application layer interactions and API guidance
X-Mailman-Version: 2.1.17
Precedence: list
List-Id: "This is the mailing list for the Transport Layer Security working group of the IETF." <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Wed, 12 Oct 2016 15:55:05 -0000

On Wed, Oct 12, 2016 at 4:11 AM, Ilari Liusvaara <>

> For when 0-RTT has become boring enough for me to implement, I would
> think the server-side interface I put in would be something like the
> following:
> - ServerSession::getReplayable0RttReader(alp_list) -> ZRttReader
> - ZRttReader::getAlpn() -> String
> - ZRttReader::dataAvailable() -> size
> - ZRttReader implements Read
> If there is no replayable 0RTT reader given when ClientHello is
> received, or if the ALP of the 0-RTT does not match any in alp_list,
> the 0-RTT is rejected. Otherwise it is accepted and the data stream is
> received via the ZRttReader. Then regular 1-RTT data will be returned by
> the usual Read interface of ServerSession.

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.

> On the client side, one option is for the early data to be specified prior
> > to or alongside handshake initiation, with some indication by the stack
> of
> > whether it was written or not. (I suggest all-or-none.) This precludes
> > question on the part of the client as to which data might have been sent
> > 0-RTT and which must have been sent 1-RTT.
> The interface I envision has client write all the 0-RTT data and
> explicitly signal the end of data, with a callback if server rejects,
> so the application can abort the flight early (the stack just black-
> holes the data after error).

Agreed on that, with s/a callback/some client application signaling
appropriate to the language or development model involved/.

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.

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.

> Another nice thing is that at the moment you receive a single byte from

> > read() you know a priori that every byte of early data you processed was
> > authentic.
> Well, one always knows that for any received data, one who sent it
> possesses the PSK secret and it can't be tampered with without the
> PSK secret.

Absolutely. But is it an authentic part of *this* connection? That's what
the client {Finished} tells you, later.

> 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:

> 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 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.