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

From: Eric Rescorla <[email-masked]>
Date: Thu, 1 Nov 2018 14:09:45 -0700

I have reviewed the latest version of this document.

This appears to address most of the points in my discuss. A few more
comments below.

   When a frame is received, tcpcrypt reconstructs the associated data
   and frame ID values (the former contains only data sent in the clear,
   and the latter is implicit in the TCP stream), computes the nonce "N"
   as above, and provides these and the ciphertext value to 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 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 (and "renege" if they
   have been SACKed, according to [RFC2018] Section 8).

Looking through this again, why is this the right guidance? It seems
like:

1. You shouldn't ACK data that you can't verify, because it's confusing
to the other side. This seems like one of the arguments for tcpcrypt
rather than protocols that run over top of TCP.
2. You would get better resistance to connection failure if you instead
decrypted packets and then ACKed.

Is the situation that sometimes you have to ACK? I haven't thought
this through. Perhaps you should recommend against ACKing if you
don't have to.

I also notice you don't require checking that the result of the
X25519 function is nonzero. We hve been requiring this in other
IETF protocols.

   the number of past connections that are vulnerable.  Of course,
   placing private keys in persistent storage introduces severe risks
   that they will not be destroyed reliably and in a timely fashion, and
   SHOULD be avoided at all costs.

This seems a bit over the top. Either it's this serious and you need
a MUST, or it's not this serious, and you should tone it down.

   To meet that ideal, it might appear natural to also mandate ECDHE
   using P-256, as this scheme is well-studied, widely implemented, and
   sufficiently different from the Curve25519-based scheme that it is
   unlikely they will both suffer from a single (non-quantum)
   cryptanalytic advance.

I'm not sure how persuasive this argument is. Wouldn't an efficient
discrete log algorithm in generic groups affect both of these functions.
As far as I know, it's not known that no such algorithm exists.

-Ekr

On Tue, Nov 28, 2017 at 2:55 PM Kyle Rose <[email-masked]> wrote:

> On Tue, Nov 28, 2017 at 5:38 PM, Daniel B Giffin <[email-masked]>
> wrote:
> > 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.
>
> 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
> >> 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?
> >
> > 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.
>
> 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
> the protocol already has this property, as you pointed out:
>
> > 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).
>
> 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
>

---

From: David Mazieres <[email-masked]>
Date: Thu, 01 Nov 2018 21:57:54 -0700

> rather than protocols that run over top of TCP.

This obviously isn't how the original tcpcrypt worked, but the working
group decided that for middlebox robustness, tcpcrypt should not protect
the integrity of the TCP header (including ACK bits).  As it is, nothing
requires TCP segments to align with tcpcrypt frames.  If the recipient
lucks out and hasn't ACKed any of the corrupt data, then maybe hope a
retransmission is better, although middleboxes might prevent sequence
numbers from being overwritten anyway.  Since that's a rather fringe
case, if you only do one thing it needs to be aborting the connection.

> decrypted packets and then ACKed.

Not especially, because tcpcrypt doesn't protect the header, so an
active attacker can just inject bad packets and desynchronize the TCP
connection.  There does exist a protocol that does better--namely the
original version of tcpcrypt, but for various reasons the working group
ultimately rejected this design (over my objections), and it's too late
to revisit the decision at this point.

> this through. Perhaps you should recommend against ACKing if you

The ACKing is effectively dictated by RFC793 (+SACK).

> X25519 function is nonzero. We hve been requiring this in other

Seems like a vanishingly low-probability event, but I see no problem to
adding such a requirement.  Do you have some language to suggest?  Maybe
we could cut and paste out of another RFC with already approved
language?

> a MUST, or it's not this serious, and you should tone it down.

It is serious, especially with SSDs.  The reason not to make it a MUST
is that some implementations may be unable to avoid it--like a virtual
machine that gets transparently paged to disk.  That could still be okay
if, say, the swap partition is encrypted with an ephemeral key.

> As far as I know, it's not known that no such algorithm exists.

In fact, there are lower bounds on generic-group discrete-log attacks.
See, for example, this recent Eurocrypt paper:

        https://www.henrycg.com/files/academic/papers/eurocrypt18discrete.pdf

But it sounds like you actually agree with us that we don't need to
mandate P-256, so I'm not sure what you are suggesting here.

David

---

From: Eric Rescorla <[email-masked]>
Date: Thu, 1 Nov 2018 23:13:54 -0700

> The ACKing is effectively dictated by RFC793 (+SACK).

OK, I'm persuaded I'm wrong about this.

> we could cut and paste out of another RFC with already approved

It's a lot probability event with a correctly-behaving counterparty, but
it's easy for
the other side to force the output to 0. Here is some text:

https://tools.ietf.org/rfcmarkup?doc=8446#section-7.4.2

"

   For these curves, implementations SHOULD use the approach specified
   in [RFC7748 <https://tools.ietf.org/rfcmarkup?rfc=7748>] to
calculate the Diffie-Hellman shared secret.
   Implementations MUST check whether the computed Diffie-Hellman shared
   secret is the all-zero value and abort if so, as described in
   Section 6 of [RFC7748]
<https://tools.ietf.org/rfcmarkup?rfc=7748#section-6>.  If
implementors use an alternative
   implementation of these elliptic curves, they SHOULD perform the
   additional checks specified in Section 7 of [RFC7748]
<https://tools.ietf.org/rfcmarkup?rfc=7748#section-7>.

"

> if, say, the swap partition is encrypted with an ephemeral key.

Well, then the text should say this. But "SHOULD at all costs" is silly.

> mandate P-256, so I'm not sure what you are suggesting here.

I'm suggesting you remove the justificatory text. Other WGs have just
mandated one group without justification

-Ekr

---

From: David Mazieres <[email-masked]>
Date: Sun, 04 Nov 2018 05:29:52 -0800

I've posted a new draft in the usual place:

        https://datatracker.ietf.org/doc/draft-ietf-tcpinc-tcpcrypt/

Please let us know if the diffs satisfy your concerns:

        https://www.ietf.org/rfcdiff?url1=draft-ietf-tcpinc-tcpcrypt-13&url2=draft-ietf-tcpinc-tcpcrypt-14&difftype=--html

Thanks,
David

---

From: Eric Rescorla <[email-masked]>
Date: Mon, 5 Nov 2018 03:21:46 -0800

> https://www.ietf.org/rfcdiff?url1=draft-ietf-tcpinc-tcpcrypt-13&url2=draft-ietf-tcpinc-tcpcrypt-14&difftype=--html

I am not an EC expert, but my impression based on the discussion in TLS was
that checking for the zero value for X25519 was not sufficient defense
against malicious peers if you didn't use the 7748 computations, hence the
language in 8446. Do you believe otherwise?

-Ekr

---

From: David Mazieres <[email-masked]>
Date: Mon, 05 Nov 2018 06:02:25 -0800

> language in 8446. Do you believe otherwise?

I don't understand what you are asking for here.  Section 7 of RFC7748
is mostly advice applicable to tcpcrypt's authors, which we are
following--for instance by including the entire transcript with nonces
in the session key computation so as not to depend on "contributory
behavior" of the Diffie-Hellman parameters.  What is it that you want us
to specify in our RFC for tcpcrypt implementers?

The only think tcpcrypt cares about for malicious peers is preventing
session ID reuse, which the design indeed appropriately prevents.
Otherwise, if a malicious peer causes a connection to be aborted or
leaks the session key--well, that's something they could have done
anyway.

Can you be more concrete about what you are worried about and what you
want us to do about it?

David

---

From: Daniel B Giffin <[email-masked]>
Date: Thu, 8 Nov 2018 13:19:36 -0800

Eric Rescorla wrote:
[..]
> language in 8446. Do you believe otherwise?

The draft currently says:

   Key-agreement schemes ECDHE-Curve25519 and ECDHE-Curve448 use the
   functions X25519 and X448, respectively, to perform the Diffie-Helman
   protocol as described in [RFC7748].  Implementations MUST check
   whether the computed Diffie-Hellman shared secret is the all-zero
   value and abort if so, as described in Section 6 of [RFC7748].  [...]

This requires, perhaps too gently, the (Montgomery ladder)
algorithm in RFC 7748.

In contrast, RFC 8446 allows other, (non-Montgomery)
algorithms to be used to compute X25519 and X448 in TLS:

   For these curves, implementations SHOULD use the approach specified
   in [RFC7748] to calculate the Diffie-Hellman shared secret.
   Implementations MUST check whether the computed Diffie-Hellman shared
   secret is the all-zero value and abort if so, as described in
   Section 6 of [RFC7748].  If implementors use an alternative
   implementation of these elliptic curves, they SHOULD perform the
   additional checks specified in Section 7 of [RFC7748].

But it is not clear exactly what "additional checks" are
required (reject all invalid keys?), and for what purpose
(ensure some "contributory behavior" property?)

It seems the only observable difference would be that other
algorithms might abort on some invalid DH inputs that the
RFC 7748 algorithm accepts.

So there are two orthogonal questions here:

- Should tcpcrypt allow implementations that behave
  differently from RFC 7748?

- Given that tcpcrypt does not depend on any "contributory
  behavior" property, is there any use in requiring DH input
  validation?

If other algorithms are allowed *and* input validation is
required, it seems we will have to explain more clearly what
validation entails.

d

---

From: Eric Rescorla <[email-masked]>
Date: Wed, 21 Nov 2018 13:55:50 -0800

> to specify in our RFC for tcpcrypt implementers?

I provided the text I recommend in my note of November 1.

"   For these curves, implementations SHOULD use the approach specified

I agree that this isn't maximally clear in 7748, but it does involve
checking that you
are on-curve.

> session ID reuse, which the design indeed appropriately prevents.

Generally, we've decided to adopt a belt and suspenders approach here,
namely:

1. Trying to ensure that the DH function can't be forced into a small set
of values
by one side.
2. Merging in the transcript so even if the DH function isn't contributive,
you can't
force the output keys into a small range.

I agree that tcpcrypt does (2). What I am looking for is also (1).

-Ekr

> want us to do about it?-

---

From: Eric Rescorla <[email-masked]>
Date: Wed, 21 Nov 2018 13:59:04 -0800

> This requires, perhaps too gently, the (Montgomery ladder)

OK, I did not read it that way. It would be fine with me if you made this
sharper. I.e., "Implementations MUST use the Montgomery ladder as specified
in [RFC7748] and MUST check whether..."

> required (reject all invalid keys?),

Yeah, I agree that this isn't maximally clear.

and for what purpose
> (ensure some "contributory behavior" property?)

Yes.

>   behavior" property, is there any use in requiring DH input

See my response to David. Elsewhere we have been opting for belt and
suspenders.

-Ekr

---

From: Daniel B Giffin <[email-masked]>
Date: Thu, 29 Nov 2018 10:41:22 -0800

Okay, how about this language that harmonizes with the SSL
approach:

  Key-agreement schemes ECDHE-Curve25519 and ECDHE-Curve448
  perform the Diffie-Helman protocol using the functions
  X25519 and X448, respectively.  Implementations SHOULD
  compute these functions using the algorithms described in
  RFC7748.  When they do so, implementations MUST check
  whether the computed Diffie-Hellman shared secret is the
  all-zero value and abort if so, as described in Section 6
  of RFC7748.  Alternative implementations of these
  functions SHOULD abort when either input would force the
  output to one of a small set of values, as discussed in
  Section 7 of RFC7748.

That last sentence is explicit (or as explicit as practical
in the scope of this document) because I really can't find
any *instruction* in Section 7 about input checking.

d

---

From: Eric Rescorla <[email-masked]>
Date: Thu, 29 Nov 2018 16:56:24 -0800

SGTM.

-Ekr