Subject: [tcpinc] Eric Rescorla's Discuss on draft-ietf-tcpinc-tcpcrypt-09: (with DISCUSS and COMMENT)

From: Eric Rescorla <[email-masked]>
Date: Fri, 10 Nov 2017 19:12:07 -0800

Eric Rescorla has entered the following ballot position for
draft-ietf-tcpinc-tcpcrypt-09: Discuss

When responding, please keep the subject line intact and reply to all
email addresses included in the To and CC lines. (Feel free to cut this
introductory paragraph, however.)

Please refer to https://www.ietf.org/iesg/statement/discuss-criteria.html
for more information about IESG DISCUSS and COMMENT positions.

The document, along with other ballot positions, can be found here:
https://datatracker.ietf.org/doc/draft-ietf-tcpinc-tcpcrypt/

----------------------------------------------------------------------
DISCUSS:
----------------------------------------------------------------------

https://mozphab-ietf.devsvcdev.mozaws.net/D3970

   2^64 bytes in the underlying TCP datastream (which would cause the
   "offset" field to wrap) before re-keying.

In TLS and other WGs, we have adopted the practice of
salting the nonce with a secret per-connection value to avoid
large-scale surveillance attacks. Why did you opt to use a weaker
construction. See:
https://tlswg.github.io/tls13-spec/draft-ietf-tls-tls13.html#security-record-layer
and https://eprint.iacr.org/2016/564.

   FIN flag set, it MUST immediately send a frame (with empty
   application data if necessary) with "rekey = 1".

I don't think that the algorithm in this section
necessarily works properly, because you have to handle rekeys in
sequence:

Frame 1 [0:999]
Frame 2 [1000:1999, rekey=1]
Frame 3 [2000:2999, rekey=1]

Now what happens if the frames are re-ordered so you get Frame 3 and
then Frame 2. You will try to decrypt Frame 3 with generation 2 and
Frame 2 with generation 3, neither of which will work (though you
might be able to interpret the text loosely to have you try to decrypt
Frame 2 with generation 2). Note that if you were to resequence before
processing, this wouldn't happen.

At minimum I think some clarification is needed here.

Given that you are allowing P-256 and point reuse, you
should be requiring point validation. See:
https://tlswg.github.io/tls13-spec/draft-ietf-tls-tls13.html#rfc.section.4.2.8.2
https://tlswg.github.io/tls13-spec/draft-ietf-tls-tls13.html#elliptic-curve-diffie-hellman

You should probably also be requiring Curve25519 output validation.

You still seem to need to specify an MTI symmetric algorithm.

----------------------------------------------------------------------
COMMENT:
----------------------------------------------------------------------

The design of session resumption here essentially precludes doing
tcpcrypt resumption across servers (as one does with TLS) because you
need extremely tight control of ss[i] or you have catastrophic
results. Was this a deliberate choice by the WG?

   suboption containing the TEP identifier and "v = 0".  In order to
   propose session resumption (described further below) with a
   particular TEP, a host sends a variable-length suboption containing

It would be clearer if you explained resumption here.

          PRK = Extract(N_A, eno-transcript | Init1 | Init2 | ES)
What is the rationale for providing N_A as the salt to HKDF-Extract, given that
you also supply N_A in the Init1 message?

   session resumption such that a given pre-session key "ss[0]" is only
   used for either passive or active opens at the same host, not both.
This seems like it probably needs some more emphasis, as failure to follow this
instruction results in GCM compromise.

It would probably be useful to explain why you opted for this design rather
than having nonces, which would remove the need for such strict ss[i]
management. I assume the reason is that you want to save the round trip?

   connection; but if it aborts, the implementation MUST raise an error
   condition distinct from the end-of-file condition.

Note that this interacts badly with the rekey issue I raise below.

   format", and MUST accept values encoded in "compressed",
   "uncompressed" or "hybrid" formats.

Why are you allowing multiple formats here? Generally, if you're going to
encourage compressed, you want to just require it.

---

From: Kyle Rose <[email-masked]>
Date: Mon, 13 Nov 2017 09:56:10 +0800

I think I need some clarification on the point here. From the relevant
passage in the TLS 1.3 draft:

q( In order to prevent mass cryptanalysis when the same plaintext is
repeatedly encrypted by different users under the same key (as is
commonly the case for HTTP), the nonce is formed by mixing the
sequence number with a secret per-connection initialization vector
derived along with the traffic keys. See [BT16] for analysis of this
construction. )

What is the situation, commonly the case for HTTP, in which different
users would be using the same key for encryption?

> At minimum I think some clarification is needed here.

I'm confused. The byte stream will be delivered in-order via TCP
sequence semantics, so receiving frames (TLV within the TCP
bytestream) out-of-order should not be possible. If a MitM reordered
the frames, the receiver would either drop the frame or close the
connection following a failure to authenticate upon decryption:

q( In the latter case, the implementation MUST
   either drop the TCP segment(s) containing the frame or abort the
   connection; but if it aborts, the implementation MUST raise an error
   condition distinct from the end-of-file condition. )

> You should probably also be requiring Curve25519 output validation.

Agreed.

> You still seem to need to specify an MTI symmetric algorithm.

Daniel noted this in his October 25 email, but you are right: I can't
seem to find it in the latest draft, either. Did that change get lost?

The idea of doing something more ticket-like was discussed, but David
pointed out (in the thread here:
https://www.ietf.org/mail-archive/web/tcpinc/current/msg00919.html)
that tickets might not meet the charter mandate for forward secrecy. I
don't recall discussing the implications of mismanaging ss[i], but

> instruction results in GCM compromise.
...

> encourage compressed, you want to just require it.

I'm not sure what the justification here was, so I'd be happy with
mandating compressed values if it simplifies the receiver's logic.

Kyle

---

From: Eric Rescorla <[email-masked]>
Date: Mon, 13 Nov 2017 10:09:25 +0800

That's not the assumption. It's a time-space tradeoff. Say you have a
128-bit
key. The attacker computes the value:

  Encrypt(K, "GET / HTTP/1.1") [or whatever]

For a feasible subset of the key space (e.g., 2^80 or so). It then captures
a large fraction of the traffic on the Internet and looks for matches for
one
of these strings. If it has a match, it now immediately knows the key. Given
the amount of traffic on the Internet, this is just maybe barely feasible
with
128-bit keys. However, with a randomized (secret) nonce, it becomes
infeasible.

>    condition distinct from the end-of-file condition. )

Maybe I'm misunderstanding tcpcrypt, so let me ask a preliminary
question:

Say you have two TCP segments protected using tcpcrypt, N and N+1
If the receiver receives N+1 before N, how does it behave?

> seem to find it in the latest draft, either. Did that change get lost?

I am not sure.

> I'll defer to Daniel on that point. Same goes for the following:

I don't think the issue here is tickets, but rather the lack of a nonce.
The point is that (say) the implementation fails to properly delete
ss[i] after encrypting some data. Then if an attacker can force
you to reuse ss[i], it can potentially recover data. This would not
be the case if you mixed in a nonce from both sides (the way that
TLS and IKE do).

-Ekr

---

From: Kyle Rose <[email-masked]>
Date: Mon, 13 Nov 2017 15:00:57 +0800

> 128-bit keys. However, with a randomized (secret) nonce, it becomes

Interesting. I don't have a strong opinion about this one, but if
there are no (or cheap) tradeoffs, it seems reasonable to use this
construction.

> If the receiver receives N+1 before N, how does it behave?

Tcpcrypt lives entirely within the ordered TCP data stream. If N+1
arrives before N, that payload will not be delivered to the tcpcrypt
engine until after N arrives, in accordance with the in-order
semantics of TCP payload delivery.

> be the case if you mixed in a nonce from both sides (the way that

I like the idea of added defense-in-depth against faulty
implementations, but it's not clear to me what tradeoffs this involves
re: (e.g.) SYN data on resumption, or on number of round trips, or on
the latency-to-first-payload-byte. I'd like Daniel to chime in here.

Kyle

---

From: Eric Rescorla <[email-masked]>
Date: Mon, 13 Nov 2017 07:21:26 +0000

> there are no (or cheap) tradeoffs, it seems reasonable to use this

The countermeasure that TLS uses seems pretty cheap.

> semantics of TCP payload delivery.

Right. I plead jet lag.

Now that you've reminded me of this, it's worth noting that the following
text
probably could use some expansion:

   When a frame is received, tcpcrypt reconstructs the associated data
   and frame nonce values (the former contains only data sent in the
   clear, and the latter is implicit in the TCP stream), and provides
   these and the ciphertext value to the the AEAD decryption operation.
   The output of this operation is either a plaintext value "P" or the
   special symbol FAIL.  In the latter case, the implementation MUST
   either drop the TCP segment(s) containing the frame or abort the
   connection; but if it aborts, the implementation MUST raise an error
   condition distinct from the end-of-file condition.

Presumably the theory here is that there is some sort of benign damage
to the packets that nevertheless passed the TCP checksum, and the retransmit
might succeed. However, in order for this to work properly, you have to
withhold
ACKs for those segments until you have verified them (or if you are using
SACK, renege on the segments once they become in-order and you determine
they are bogus). Otherwise, the sender will not retransmit, and you'll
never be
able to recover. This probably needs to be covered in this spec.

> the latency-to-first-payload-byte. I'd like Daniel to chime in here.

I suspect that latency is in fact the issue here, but there's probably
*some* way around this. Half-baked probably broken idea: what if each side
sent
a nonce before it started encrypting and mixed that into its *sending*
keys. Then at least you would have diversity for the keys, though no
anti-replay.

-Ekr

---

From: Kyle Rose <[email-masked]>
Date: Wed, 15 Nov 2017 09:47:11 +0800

> The countermeasure that TLS uses seems pretty cheap.

SGTM. Daniel can chime in here in case he strongly disagrees.

> they are bogus). Otherwise, the sender will not retransmit, and you'll never
> be

Good call. This is a good illustration that tcpcrypt actually operates
at the transport layer, or that there is an intrinsic/unavoidable
layer violation, if the intent of an implementation is to discard a
bad packet rather than abort the connection.

> keys. Then at least you would have diversity for the keys, though no

I like that this involves no tradeoffs other than some bytes for the
nonce. I don't think I'd want any changes at this point that
complicate the protocol or add round trips or latency, especially
since SYN data is not currently an issue for any TEP.

Do you think we need any additional language in security considerations?

Kyle

---

From: Daniel B Giffin <[email-masked]>
Date: Tue, 14 Nov 2017 18:23:25 -0800

> SGTM. Daniel can chime in here in case he strongly disagrees.

Yes, thanks to Ekr -- we weren't aware of this "multi-user"
problem and the countermeasure.  It looks worthwhile
(trivial performance impact), so we will do it and get an
okay from our local guru on the details.

daniel

---

From: Daniel B Giffin <[email-masked]>
Date: Fri, 17 Nov 2017 04:17:03 -0800

Thanks for the helpful review, Ekr!

Thanks again for pointing this out.  The next draft
implements a nonce-randomizing countermeasure.  I'll excerpt
the relevant changes.  First, what was before the frame
nonce is now called the frame ID:

   Lastly, a "frame ID" (used to construct the nonce for the AEAD
   algorithm) has this format:

                     byte
                        +------+------+------+------+
                      0 |       FRAME_ID_MAGIC      |
                        +------+------+------+------+
                      4 |                           |
                        +           offset          +
                      8 |                           |
                        +------+------+------+------+

Next, the traffic keys are extended by 12 bytes to provide
bits for nonce randomization.  This bit shows where the keys
come from:

   Given a session secret "ss[i]", the two sides compute a series of
   master keys as follows:

                 mk[0] = CPRF(ss[i], CONST_REKEY, K_LEN)
                 mk[j] = CPRF(mk[j-1], CONST_REKEY, K_LEN)

   The process of advancing through the series of master keys is
   described in Section 3.8.

   Finally, each master key "mk[j]" is used to generate traffic keys for
   protecting application data using authenticated encryption:

            k_ab[j] = CPRF(mk[j], CONST_KEY_A, ae_keylen + 12)
            k_ba[j] = CPRF(mk[j], CONST_KEY_B, ae_keylen + 12)

As before, "ae_keylen" depends on the negotiated AEAD
algorithm, e.g. 16 bytes for AES128-GCM.

The encryption operation is detailed in Section 3.6.  After
explaining the construction of the "plaintext" and
"associated data" values, it explains how the frame nonce is
formed:

   A "frame ID" value (see Section 4.2.3) is also constructed for the
   frame but not explicitly transmitted.  It contains an "offset" field
   whose integer value is the zero-indexed byte offset of the beginning
   of the current encryption frame in the underlying TCP datastream.
   (That is, the offset in the framing stream, not the plaintext
   application stream.)  Because it is strictly necessary for the
   security of the AEAD algorithms specified in this document, an
   implementation MUST NOT ever transmit distinct frames with the same
   frame ID value under the same encryption key.  In particular, a
   retransmitted TCP segment MUST contain the same payload bytes for the
   same TCP sequence numbers, and a host MUST NOT transmit more than
   2^64 bytes in the underlying TCP datastream (which would cause the
   "offset" field to wrap) before re-keying.

   With reference to the "AEAD Interface" described in Section 2 of
   [RFC5116], tcpcrypt invokes the AEAD algorithm with values taken from
   the traffic key "k_ab[j]" or "k_ba[j]" for some "j", according to the
   host's role as described in Section 3.3.

   First, the traffic key is divided into two parts:

        byte   0                        ae_keylen    ae_keylen + 11
               |                           |            |
               v                           v            v
             +----+----+--...--+----+----+----+--...--+----+
             |             K             |        NR       |
             +----+----+--...--+----+----+----+--...--+----+

             \_____________________________________________/
                              traffic key

   The first "ae_keylen" bytes of the traffic key provide the AEAD key
   "K", while the remaining 12 bytes provide a "nonce randomizer" value
   "NR".  The frame ID is then combined via bitwise exclusive-or with
   the nonce randomizer to yield "N", the AEAD nonce for the frame:

                            N = frame_ID xor NR

   The plaintext value serves as "P", and the associated data as "A".
   The output of the encryption operation, "C", is transmitted in the
   frame's "ciphertext" field.

This packaging of the key and nonce bits into one value is a
little bit awkward in the above excerpts (versus say the TLS
approach of doing two separate extractions to get the two
distinct values), but elsewhere in the spec it saves a lot
of complexity because the handling of a traffic key can be
explained without reference to the nonce randomizer.

Still, I'm open to other ways of describing this.

> At minimum I think some clarification is needed here.

As Kyle explained, it works because tcpcrypt is now
contained by the TCP datastream.  But you may be remembering
the good old days when it was simply a segment rewriter!

> https://tlswg.github.io/tls13-spec/draft-ietf-tls-tls13.html#elliptic-curve-diffie-hellman

Yes, thank you.  For the NIST curves, I have added:

	Implementations MUST validate these "pubkey" values according to the
  algorithm in [ieee1363] Section A.16.10.

But I see that TLS refers to ANSI X9.62 for validation, even
though it refers to IEEE1363 for DH computation.  Is there a
good reason not to stick with the one source?

Also, I guess there's no need to check on-the-curve if we
allow only compressed format, and it's not perfectly clear
to me whether we need to check group membership, but I'd
really rather not have all these details in this document if
there's a good way to cite it out.

Lastly, I've added this to section 3.3:

   If a host receives an ephemeral public key from its peer and a
   required key-validation step fails (see Section 5), it MUST abort the
   connection and raise an error condition distinct from the end-of-file
   condition.
 
> You should probably also be requiring Curve25519 output validation.

I think you're saying we should check that the DH result is
not zero?

No harm, I suppose, but I'm going to try to get guidance on
whether it's necessary.

> You still seem to need to specify an MTI symmetric algorithm.

There's a table of the requirements in "6. AEAD Algorithms."
But you're not the only one who had trouble finding it, so I
wonder what's going on ...

I think it's a choice to keep key material in the kernel and
to have control over forward secrecy.

> you also supply N_A in the Init1 message?

I'm not qualified to say.  I agree it seems "redundant", but
it appears to achieve what it needs to.  Perhaps we can find
a more satisfying answer for you ...

> instruction results in GCM compromise.

For our reference, here's the paragraph in question:

   A session secret may not be used to secure more than one TCP
   connection.  To prevent this, a host MUST NOT resume with a session
   secret if it has ever enabled encryption in the past with the same
   secret, in either role.  In the event that two hosts simultaneously
   send SYN segments to each other that propose resumption with the same
   session secret but the two segments are not part of a simultaneous
   open, both connections will have to revert to fresh key-exchange.  To
   avoid this limitation, implementations MAY choose to implement
   session resumption such that a given pre-session key "ss[0]" is only
   used for either passive or active opens at the same host, not both.

It seems like the straightforward implementation is to mark
a session secret "used" as soon as a resume succeeds.  The
only downside is that you could be forced to do fresh key
exchange in the case of bad timing (if you try to connect to
the host at the same time it connects to you), so the last
sentence here gives you a way to avoid that ever happening
(with I guess the downside of having to store two session
secrets for the same host).  Does it seem that this
paragraph is somehow risking a dangerous implementation?

> management. I assume the reason is that you want to save the round trip?

If I understand the question, I think the choice here is
mostly to prioritize forward secrecy.  Perhaps we can find a
way to make this more clear in Security Considerations.

> Note that this interacts badly with the rekey issue I raise below.

It's not clear exactly what you mean, but perhaps this is
another concern that is moot because of the reliable-ordered
receipt of frames?  In any case (and with relevance to some
other discussion in this thread), due to other comments we
have improved the guidance on what to do in the case of
encryption failure:

                     [...] The output of this operation is either a
   plaintext value "P" or the special symbol FAIL.  In the latter case,
   the implementation SHOULD abort the connection and raise an error
   condition distinct from the end-of-file condition.  But if none of
   the TCP segment(s) containing the frame have been acknowledged and
   retransmission could potentially result in a valid frame, an
   implementation MAY instead drop these segments.

> encourage compressed, you want to just require it.

That makes sense.  I'm changing this to:

   Implementations MUST encode these "pubkey" values in "compressed
   format".

Thanks again for all the useful comments.

daniel

---

From: Eric Rescorla <[email-masked]>
Date: Sun, 19 Nov 2017 07:04:56 +0800

Hi Daniel.

I won't have a chance to review this document until Monday, but I
wanted to respond not to the point about the reuse of ss[i], because I
think you (and others) may have understood me and thought this was
primarily about tickets versus session ids, which it is not.

At a high level, tcpcrypt establishes a master secret which is then
used to create a chain of resumption secrets ss[i]. Each ss[i] value
is intended only to be used for resumption once, in which case things
are fine. However, because the only input to the traffic keys is
ss[i], if you ever reuse ss[i], the result is catastrophic
(potentially leading to a complete loss of integrity if the cipher is
AES-GCM).

By contrast, TLS 1.3 has a similar design (the secrets are a tree, not
a chain) but because there is always a nonce exchange, and the nonces
are mixed into the traffic keys, so if you *do* reuse a secret, the
main impact is that the two connections in which you use the secret
are linkable (by the ticket ID), and of course that you don't have
FS. IKE PSK has a similar design, and I think it's clear that it's
less brittle than the design in tcpcrypt.

This is an orthogonal question to whether the IDs used for resumption
are lookup keys (like in tcpcrypt) or can be self-contained (TLS 1.3
allows both lookup keys and self-contained IDs). Although designs
where one side dictates the identifier are compatible with
self-encrypted tickets, the need not be used that way, and are
compatible with FS. See, for instance:
https://tools.ietf.org/html/draft-ietf-tls-tls13-21#section-8.1.

Assuming I understand the design of tcpcrypt correctly, it seems like
the reason for the choice not to have a nonce is that you don't have
room in the SYN, and you don't want to take another round trip in the
resumption case. However, I think you can improve on the situation
somewhat even within these constraints. Here's your Figure 9 from
ENO modified to include tcpcrypt as I understand it:

             (1) A -> B:  SYN      ENO<TCPCRYPT sid[i] part 1>
             (2) B -> A:  SYN-ACK  ENO<TCPCRYPT sid[i] part 2>
             (3) A -> B:  ACK      ENO<>

A can start sending encrypted traffic after sending (3) and B can
start sending encrypted traffic after receiving (3). Am I correct
so far?

Assuming I am correct, then it seems like (B) could send a nonce
in message (2) and A could send a nonce in message (3), like so:

             (1) A -> B:  SYN      ENO<TCPCRYPT sid[i] part 1>
             (2) B -> A:  SYN-ACK  ENO<TCPCRYPT sid[i] part 2, N_a>
             (3) A -> B:  ACK      ENO<TCPCRYPT N_b>

You could then derive the keys from ss[i], N_a, and N_b, and avoid
brittleness associated with ss[i] reuse. Of course, you should still
require that people not reuse ss[i], because that gives you FS, but
the consequences if people fail to do so would not be so dire.

-Ekr

---

From: Eric Rescorla <[email-masked]>
Date: Sun, 19 Nov 2017 07:32:46 +0800

As it happens, I was able to take a look. Several notes from my review:

- Not all algorithms take a 12 byte nonce, so you probably want to allow
some generality
here.
- I'm not sure you still need the NONCE_MAGIC value.
- Do you want to cover SACK in the section about how you handle
deprotection failures.

wrote
> good reason not to stick with the one source?

TBH, not sure. Filed an issue on TLS for that.

> there's a good way to cite it out.

I think your text is fine.

> No harm, I suppose, but I'm going to try to get guidance on

It's the way we're going in the security area. Is there some reason not to.

> to have control over forward secrecy.

See my separate note on this topoic.

> a more satisfying answer for you ...

I agree it's harmless, but I'd be interested.

-Ekr

---

From: "Black, David" <[email-masked]>
Date: Mon, 20 Nov 2017 22:11:41 +0000

Hmm - while I'm not sure whether this resumption crypto change should be made, I'm concerned by what may be "scope creep" here, courtesy of a use case difference from TLS that weakens reasoning by analogy across the two protocols.

TLS is predominantly a user-space library protocol that is used with load-balanced server clusters, including protocol offload engines. That creates opportunities to move TLS state around, e.g., across TCP connections.

In contrast, tcpcrypt is predominantly a kernel-space protocol whose state is intended to be bound to a single TCP connection. It's much harder and less common to extract the relevant kernel protocol state, although it's possible in principle.

If this change is not made, then at a minimum, some serious warnings about avoiding resumption key reuse are in order,  as beyond GCM, that is a serious threat to all stream ciphers.

Thanks, --David ... Sent from my Android not-so-smartphone.

-------- Original message --------
From: Eric Rescorla <[email-masked]>
Date: 11/18/17 5:05 PM (GMT-06:00)
To: Daniel B Giffin <[email-masked]>
Cc: tcpinc <[email-masked]>, Kyle Rose <[email-masked]>, [email-masked], The IESG <[email-masked]>, [email-masked]
Subject: Re: [tcpinc] Eric Rescorla's Discuss on draft-ietf-tcpinc-tcpcrypt-09: (with DISCUSS and COMMENT)

Hi Daniel.

-Ekr

On Fri, Nov 17, 2017 at 8:17 PM, Daniel B Giffin <[email-masked]<mailto:[email-masked]>> wrote:
Thanks for the helpful review, Ekr!

                     byte

                            N = frame_ID xor NR

daniel

---

From: Daniel B Giffin <[email-masked]>
Date: Thu, 23 Nov 2017 00:02:42 -0800

> - I'm not sure you still need the NONCE_MAGIC value.

Yes, I agree on the above.  I will change this to do the
same as TLS: zero-pad the record number (frame offset for
tcpcrypt) to the nonce size.

> - Do you want to cover SACK in the section about how you handle

I suppose that makes sense.  I'll try to propose new
language there shortly.

> I think your text is fine.

Great.

> It's the way we're going in the security area. Is there some reason not to.

Okay, having done some more reading about this, it sounds
like a zero check on the output is not at all sufficient to
ensure "contributory" behavior from the X25519 function:

  https://moderncrypto.org/mail-archive/curves/2017/000896.html

And because our key-derivation function gets fresh
randomness from each host's nonce, I don't believe we need
to get this contribution from the DH function.  (I mostly
have a confirmation from Dan Boneh on this, but have asked
for more clarity to be sure we can make this claim
confidently; I'll report back as soon as I can.)

In RFC7748, which we're referencing for how to perform
X25519, it says in Section 6.1:

	 Both now share K = X25519(a, X25519(b, 9)) = X25519(b, X25519(a, 9))
   as a shared secret.  Both MAY check, without leaking extra
   information about the value of K, whether K is the all-zero value and
   abort if so (see below).  Alice and Bob can then use a key-derivation
   function that includes K, K_A, and K_B to derive a symmetric key.

   The check for the all-zero value results from the fact that the
   X25519 function produces that value if it operates on an input
   corresponding to a point with small order, where the order divides
   the cofactor of the curve (see Section 7).  The check may be
   performed by ORing all the bytes together and checking whether the
   result is zero, as this eliminates standard side-channels in software
   implementations.

So I guess tcpcrypt's instruction is "MAY abort" due to this
reference.  I believe this does not break anything
(good-faith implementations will not cause each other to
abort), but it doesn't sound like it solves anything either.

Even if we wanted to "validate" X25519 pubkeys, I am not
sure how to do that.  The only instruction I can find is to
compare the received pubkeys against this list of about a
dozen bad values:

  https://cr.yp.to/ecdh.html#validate

If there is some IETF discussion of best practice, can you
point me to it?  The closest I could find is in CFRG from
2015:

	https://www.ietf.org/mail-archive/web/cfrg/current/msg06558.html

For now, I think my tentative proposal is to make no
changes: that is, continue to reference RFC7748 and make no
mention of public-key validation for X25519.

[skipped for now]

---

From: Daniel B Giffin <[email-masked]>
Date: Fri, 24 Nov 2017 10:28:42 -0800

Ekr: Thanks for explaining the point about session-secret
reuse, and for proposing a reasonable coutermeasure.

I agree adding nonces to the resumption handshake could be a
useful safeguard, but there is the cost of 12 (or more)
bytes of option-space on the first ACK in each direction.

And as David Black suggests, the expected setting for
implementation of tcpcrypt ought to make safe managment of
session secrets straightforward.  (Famous last words?)

Although I'm happy to entertain more discussion about this
tradeoff, I'm inclined to David Black's suggestion to add
"serious warnings about avoiding resumption key reuse" to
Security Considerations.

daniel

---

From: Eric Rescorla <[email-masked]>
Date: Fri, 24 Nov 2017 10:33:54 -0800

> bytes of option-space on the first ACK in each direction.

Well, there are (at least) two other options:

1. You could simply accept the extra round trip and send nonces in both
directions, as you do for the non-resumption handshake. That would be
the most straighforward approach, and arguably the best one.

2. As I mentioned in my original note, you could have implementations
send a nonce in their sending direction before the first byte of ciphertext.
This isn't as good because you don't get anti-replay, but I *believe*
that it would provide strong defense against keying material reuse,
which seems like the most immediate threat.

-Ekr

> > Hmm - while I'm not sure whether this resumption crypto change should be
> made, I'm concerned by what may be "scope creep" here, courtesy of a use
> case difference from TLS that weakens reasoning by analogy across the two
> protocols.
> >
> > TLS is predominantly a user-space library protocol that is used with
> load-balanced server clusters, including protocol offload engines. That
> creates opportunities to move TLS state around, e.g., across TCP
> connections.
> >
> > In contrast, tcpcrypt is predominantly a kernel-space protocol whose
> state is intended to be bound to a single TCP connection. It's much harder
> and less common to extract the relevant kernel protocol state, although
> it's possible in principle.
> >
> > If this change is not made, then at a minimum, some serious warnings
> about avoiding resumption key reuse are in order,  as beyond GCM, that is a
> serious threat to all stream ciphers.
> >
> > Cc: tcpinc <[email-masked]>, Kyle Rose <[email-masked]>,
> [email-masked], The IESG <[email-masked]>,
> [email-masked]
> > Subject: Re: [tcpinc] Eric Rescorla's Discuss on
> draft-ietf-tcpinc-tcpcrypt-09: (with DISCUSS and COMMENT)
> >
> > Hi Daniel.
> >

---

From: Kyle Rose <[email-masked]>
Date: Mon, 27 Nov 2017 08:36:34 -0500

> the most straighforward approach, and arguably the best one.

Whether this is acceptable to mandate or not really depends on the
goals of session resumption. If the goal is simply to save compute
resources for key exchange, then this seems like a reasonable change
to make; but if the goal is to drive down time-to-first-byte-delivered
to no more than a normal TCP connection, then this will negate that
advantage, significant especially for lengthy paths.

If tcpinc were primarily about confidentiality against active
attackers, I think I'd come down on the side of safety against bad
implementations; but, as the primary goal of tcpinc is to prevent
pervasive passive surveillance, I think the balance tips the other
way: to favor performance over misuse tolerance.

I also agree with David Black that I feel like there's a lot of scope
creep here: frankly, I'm not comfortable making substantive changes to
the core protocol without doing another WGLC.

> which seems like the most immediate threat.

This may be the best compromise: it provides a measure of misuse
tolerance within the existing performance constraints.

Kyle

---

From: Eric Rescorla <[email-masked]>
Date: Mon, 27 Nov 2017 07:07:49 -0800

> way: to favor performance over misuse tolerance.

Hmm.... This seems like kind of an odd argument given that the design of
tcpcrypt explicitly eschews the kind of techniques that have been found
to be important to high resumption rates in protocols such as TLS.

> the core protocol without doing another WGLC.

I agree that you should do another WGLC if you make this change (or either
change, for that matter.) But I don't think that's a reason not to make
changes.

-Ekr

---

From: "Black, David" <[email-masked]>
Date: Mon, 27 Nov 2017 16:30:42 +0000

Inline ...
 
> way: to favor performance over misuse tolerance.

[David>]  I agree with Kyle here.  In addition, a new key exchange is cheaper for tcpcrypt by comparison to TLS (e.g., because tcpcrypt does not authenticate), so if the properties of a new tcpcrypt key exchange are wanted, that's what should be done, IMHO.

> tolerance within the existing performance constraints.

[David>] Hmm - this doesn’t do much for confidentiality with stream ciphers like AES GCM (the single "MUST implement" cipher for tcpcrypt) , as if reuse happens, then the attacker XORs the two matching ciphertext streams yielding an XOR of the plaintext streams in which the first n bytes of XOR'd nonces can safely be ignored.

If the intent was that the nonces participate in key derivation, then the keys would need to be re-derived after the first message following resumption is received - it's a better idea to move that key derivation into setting up the TCP connection for resumption.   One consequence of nonce participation in key derivation is that pre-calculation of resumption keying material prior to the TCP handshake is no longer possible.  I would hope that a one-byte nonce in each direction will suffice, as the goal is reuse prevention, not injection of significant new entropy.  If significant new entropy is wanted, start over with a new key exchange, IMHO.  

Here's a sketch of one way in which this could be done.  First, reduce the resumption identifier size to 16 bytes from the current 18, and then send 8 bytes of resumption ID + 1 byte of nonce in each direction for resumption in place of the current 9 bytes of resumption ID.  Those nonces then need to be folded into the existing tcpcrypt CPRF key generation framework that takes a 1-byte value in addition to the key.   One possibility (feels like a bit of a hack) is to take the upper half of the current 1-byte CONST value space for this purpose by forcing the most significant bit of each nonce to 1, and then running CPRF twice to include both nonces:
	ss_a[i] = CPRF(ss[i], nonce_a | 0x80, K_LEN)
	ss_ab[i] = CPRF(ss_a[i], nonce_b |0x80, K_LEN)
After this, the master keys, mk[i], are generated from ss_ab[i] instead of ss[i].

The result is 7 bits of nonce in each direction, so 14 bits of reuse resistance.  That seems ok, since reuse is not supposed to happen in the first place.  OTOH, there's no useful anti-replay resistance here, as one would need much larger nonces for that, so it's good that anti-replay is a non-goal.  However, Kyle's concern about changes to the core protocol applies to this sort of design, even though it doesn't add a new round-trip.

---

From: Eric Rescorla <[email-masked]>
Date: Mon, 27 Nov 2017 08:43:47 -0800

On Mon, Nov 27, 2017 at 8:30 AM, Black, David <[email-masked]> wrote:

> [David>]  I agree with Kyle here.  In addition, a new key exchange is
> cheaper for tcpcrypt by comparison to TLS (e.g., because tcpcrypt does not
> authenticate), so if the properties of a new tcpcrypt key exchange are
> wanted, that's what should be done, IMHO.
>

I'm not sure why you call this the properties of a new key exchange.
Non-reuse of keys is a basic property of key establishment schemes.

> [David>] Hmm - this doesn’t do much for confidentiality with stream
> ciphers like AES GCM (the single "MUST implement" cipher for tcpcrypt) , as
> if reuse happens, then the attacker XORs the two matching ciphertext
> streams yielding an XOR of the plaintext streams in which the first n bytes
> of XOR'd nonces can safely be ignored.
>

> If the intent was that the nonces participate in key derivation, then the
> keys would need to be re-derived after the first message following
> resumption is received

Huh? I think we're talking past each other here.

My suggestion was to send a nonce before you send any ciphertext and then
derive the traffic keys from ss[i] and the nonce. That's why this only
works properly asymmetrically.

- it's a better idea to move that key derivation into setting up the TCP
> connection for resumption.   One consequence of nonce participation in key
> derivation is that pre-calculation of resumption keying material prior to
> the TCP handshake is no longer possible.  I would hope that a one-byte
> nonce in each direction will suffice, as the goal is reuse prevention, not
> injection of significant new entropy.  If significant new entropy is
> wanted, start over with a new key exchange, IMHO.
>

To have strong reuse prevention you need a long enough nonce that you have
a low probability of collision.

One concern here is that an implementation might have a defect where it
improperly erases ss[i] after use, in which case an attacker might
repeatedly exercise that defect and thus get key reuse.

-Ekr

Here's a sketch of one way in which this could be done.  First, reduce the
> resumption identifier size to 16 bytes from the current 18, and then send 8
> bytes of resumption ID + 1 byte of nonce in each direction for resumption
> in place of the current 9 bytes of resumption ID.  Those nonces then need
> to be folded into the existing tcpcrypt CPRF key generation framework that
> takes a 1-byte value in addition to the key.   One possibility (feels like
> a bit of a hack) is to take the upper half of the current 1-byte CONST
> value space for this purpose by forcing the most significant bit of each
> nonce to 1, and then running CPRF twice to include both nonces:
> The result is 7 bits of nonce in each direction, so 14 bits of reuse
> resistance.  That seems ok, since reuse is not supposed to happen in the
> first place.  OTOH, there's no useful anti-replay resistance here, as one
> would need much larger nonces for that, so it's good that anti-replay is a
> non-goal.  However, Kyle's concern about changes to the core protocol
> applies to this sort of design, even though it doesn't add a new round-trip.
>
> > Kyle
>

---

From: Kyle Rose <[email-masked]>
Date: Mon, 27 Nov 2017 12:24:15 -0500

> to be important to high resumption rates in protocols such as TLS.

Were you thinking of stateless resumption (e.g., tickets) or something
else here? My recollection from a mailing list discussion on tickets
was that ENO and tcpcrypt deliberately make some tradeoffs in session
resumption for performance given the constraints of TCP, specifically
in terms of option space: there simply isn't enough option space to
encode sufficient state, and the realities of middlebox ossification
probably dead-end enhancements like EDO on the public internet.

Speaking personally (not as chair), I also feel like tcpinc sits in a
niche in which stateless resumption adds more complexity than is
justified by the probable use cases. That's probably begging the
question some, as stateless resumption might alter that distribution,
but it's not clear the extra round trip for repeated connections to
the same machine would be tolerable for many WAN use cases in legacy
protocols, where tcpinc is focused.

> change, for that matter.) But I don't think that's a reason not to make

Agreed (though I am not sure Mirja will be happy to hear that). My
comment about scope creep is simply that the WG came to a consensus on
this topic (resumption tradeoffs) long ago, so I'm not sure we should
be second guessing that judgment at IETF LC.

Kyle

---

From: Eric Rescorla <[email-masked]>
Date: Mon, 27 Nov 2017 09:36:53 -0800

On Mon, Nov 27, 2017 at 9:24 AM, Kyle Rose <[email-masked]> wrote:

> else here?

Yes.

> My recollection from a mailing list discussion on tickets

> protocols, where tcpinc is focused.

I don't really think it's really worth relitigating this; I'm merely
observing
that if you want high resumption rates then you want stateless resumption.
As for the question of option space, I'm not persuaded it wouldn't
be possible to have stateless resumption with the same number of
round trip times if you were willing to be a bit more careful about
how you handled failure.

> be second guessing that judgment at IETF LC.

I'm not sure what you think is being second guessed. Taking a step back, the
way we got here is that I observed that the specific resumption design here
is extremely brittle (and far more brittle than in other protocols that we
design)
and that the warnings about how you had to handle the keys were insufficient
to allow the users to know. This then turned into a discussion about how
to make the protocol less brittle. It's certainly within the WG's
discretion to
not address this (though I personally think you ought to) but if so, it's
imperative
that it be adequately documented what the risks are, which the current text
does not do.

With that said, it's not clear to me that the WG actually did have consensus
that this brittleness was a good tradeoff. Was this property in fact widely
understood? Speaking only for myself, it really only came into focus for me
when I did my review.

-Ekr

---

From: Kyle Rose <[email-masked]>
Date: Mon, 27 Nov 2017 12:57:39 -0500

> that if you want high resumption rates then you want stateless resumption.

For a server pool, I completely agree.

> round trip times if you were willing to be a bit more careful about

I would be interested in an (OOB) conversation on your thoughts here.

> I'm not sure what you think is being second guessed.

I was referring specifically to the resumption tradeoffs (rate vs.
performance). That said, all I can find is this:

https://www.ietf.org/mail-archive/web/tcpinc/current/msg00919.html

which doesn't actually talk about that tradeoff, but rather about the
charter's requirement for forward secrecy.

> understood? Speaking only for myself, it really only came into focus for me

No, I think you're right that this wasn't widely understood, or at
least not discussed. That wasn't what I was referring to, however: I
do think the brittleness is worth at least discussing on the list
(check) and seeing if there's consensus to make a change to the
protocol or to add a crystal clear warning to the draft explaining the
critical importance of not reusing SIDs.

Kyle

---

From: "Black, David" <[email-masked]>
Date: Mon, 27 Nov 2017 19:15:27 +0000

> critical importance of not reusing SIDs.

Thanks, --David

> -----Original Message-----
> From: Kyle Rose [mailto:[email-masked]]
> Sent: Monday, November 27, 2017 12:58 PM
> To: Eric Rescorla <[email-masked]>
> Cc: Daniel B Giffin <[email-masked]>; Black, David
> <[email-masked]>; tcpinc <[email-masked]>; [email-masked];
> The IESG <[email-masked]>; [email-masked]

---

From: Daniel B Giffin <[email-masked]>
Date: Mon, 27 Nov 2017 20:11:24 -0800

Whether or not we decide to go back to the WG to discuss the
mechanics of session resumption, we could make lightweight
changes to the current protocol in this last call that would
facilitate any "upgrades" that appear necessary after
deployment has begun.

To wit, we propose:

  - Replace the first byte of INIT1_MAGIC and INIT2_MAGIC
    with a "version" byte.  Implementations of the current
    protocol must send a zero version-byte, and must ignore
    what they receive.

  - When receiving ENO suboptions for tcpcrypt TEPs with
    v=1, which are "resumption suboptions": if the
    resumption identifier-half is not the expected length
    (9 bytes), treat this suboption as a bare TEP byte with
    v=0; that is, an invitation to fresh key-exchange with
    that TEP.  This *almost* goes without saying; the
    current draft doesn't say what to do in this case of
    "malformatted" resumption identifiers.

The version byte is intended to allow implementations of
future protocol versions to identify each other (by sending
the highest version they support), which then allows them to
perform "advanced" resumption signaling later.

And specifying how "advanced" resumption suboptions collapse
into simple TEP advertizements (instead of being ignored
altogether) ensures backwards compatibility with the current
protocol, even without the version mechanism.

These changes would permit several backwards-compatible
resumption mechanisms to be added later:

  - Ekr's idea to send nonces on the first ACK in each
    direction
  - David Black's idea to combine some nonce-bytes with the
    resumption identifiers
  - Ekr's idea to send nonces in an extra handshake in the
    TCP datastream

Again, we don't intend to reject the possibility of more
agressive changes, but if we want to go ahead with something
now then the above minor changes provide an upgrade path if
it becomes necessary.

If this sounds attractive, the above changes can be added to
the new draft (ready in the next 24 hours) that addresses
recent reviews.

d

---

From: Kyle Rose <[email-masked]>
Date: Tue, 28 Nov 2017 15:00:45 -0500

>     "malformatted" resumption identifiers.

Upon reflection, isn't this an oversight? It seems the draft should
address this regardless of how we decide to deal with misuse
tolerance.

> perform "advanced" resumption signaling later.

So is the idea that the format of INIT*_MAGIC would be fixed, and that
the version can only affect subsequent messages? I suppose the
alternative is that we burn a new set of TEPs whenever anything about
the protocol changes, so putting that extensibility directly into
tcpcrypt (where a much less constrained number of signaling bits are
available) makes sense for future proofing, maybe even irrespective of
this particular issue. I'm thinking of some analogy to ClientHello
extensions in TLS, where you can send stuff that the server might not
parse, and that's okay. Do we want an extra frame in/after the INIT
messages for sending version-specific data on the first flight that
old receiver versions will simply ignore (beyond including it in the
handshake transcript), to avoid an extra RTT for version negotiation?

Kyle

---

From: Daniel B Giffin <[email-masked]>
Date: Tue, 28 Nov 2017 14:38:55 -0800

> address this regardless of how we decide to deal with misuse

Yes.  It makes sense that whenever you send a TEP identifier
(by itself or for resumption, even if you use some
yet-unspecified resumption-request format), you are willing
to do fresh key exchange with that TEP.  Thus I have added
this to section 3.5. Session Resumption:

   If a passive opener receives an ENO suboption with a TEP identifier
   and "v = 1", but the suboption data does not have length 9 bytes, it
   MUST behave as if the same TEP had been sent with "v = 0".  That is,
   the suboption MUST be interpreted as an offer to negotiate fresh key
   exchange with that TEP.

> handshake transcript), to avoid an extra RTT for version negotiation?

Because the new "version" byte would be the first byte sent
in each direction, it can be used to negotiate any future
protocol behavior to follow that byte.

Even without the version byte, the Init1/Init2 messages
already reserve trailing space for future uses, which
implementations of the current protocol are instructed to
ignore (although it is always part of the transcript).

daniel

---

From: Kyle Rose <[email-masked]>
Date: Tue, 28 Nov 2017 17:55:15 -0500

>    the suboption MUST be interpreted as an offer to negotiate fresh key

Right, I was just pointing out that we want to keep this change even
if we decide not to make further changes to resumption at this time.

>> Do we want an extra frame in/after the INIT

You need both because you need to know that the INIT message you send
will be accepted by the receiver before you know which version(s) it
supports (i.e., on the first flight). That said, I had forgotten that

> ignore (although it is always part of the transcript).

If we decide to make use of that space in a future version, we'll need
to come up with a structure with intrinsic backward compatibility for
version fallback... but that's a conversation for another time. :-)

Kyle