On Thu, Jun 25, 2026 at 10:23 PM Martin Thomson <[email protected]> wrote:

>
> On Fri, Jun 26, 2026, at 07:39, Nick Sullivan wrote:
> > Quick clarification of my last email. The points, briefly:
> > - Publish -08 as-is: ok
> > - Do not merge #25: it reroutes a failed Section 7.2 encapsulation-key
> > check from illegal_parameter to internal_error, away from -08 (and the
> > approved draft-ietf-tls-ecdhe-mlkem).
> > - Minor, non-blocking: probabilistic wording in Section 4.3, the DTLS
> > freshness clarification, and a note that implicit rejection is not an
> > error.
>
> Nick, that original mail was tl;dr.  I tried.  Are you suggesting that the
> draft is correct without changes?  Just because it works doesn't make it
> good.  A lot depends on how you intend to use this API, but I would greatly
> prefer that it be at least possible to only have to deal with APIs that
> take byte sequences.  (Unlike other uses of the same thing, there is no
> value to obtaining a checked value for multiple uses, because these are
> single-use items.)
>
> With that API, failing the checks in FIPS 203 will be indistinguishable
> from failing to gather entropy or actual cryptographic problems (which we
> generally want to hide; fault injection, etc...).
>
> The part where this gets weird for me is this (which both you and Bas say
> in different ways, I think):
>
> > First, the client need not repeat the decapsulation-key checks on every
> operation [...]
>
> I was not suggesting that the client check the key it made at all (unless
> I'm completely losing it).
>
> #25 says:
>
> > Prior to encapsulation, the server MUST perform the encapsulation key
> check
> > from Section 7.2 of {{FIPS203}}.
> > Prior to decapsulation, the client MUST perform the decapsulation input
> check
> > from Section 7.3 of {{FIPS203}}.
>
> Paraphrasing: "check the stuff you received from the other side before
> using it".  It says nothing of what each peer itself generates.
>
> What am I missing?
>

The paraphrase for the second sentence is not right. You need to go look at
the text you're citing there. FIPS 203, Section 7.3's gives the decap input
check as:

> 1. (Ciphertext type check) If 𝑐 is not a byte array of length
32(𝑑𝑢𝑘+𝑑𝑣) for the values of 𝑑𝑢,
> 𝑑𝑣, and 𝑘 specified by the relevant parameter set, then input checking
has failed.

This is checking the ciphertext length is correct. That is indeed checking
what you received.

> 2. (Decapsulation key type check) If dk is not a byte array of length
768𝑘+96 for the value of
> 𝑘 specified by the relevant parameter set, then input checking has
failed.

This is the *length of the private key*. That is (hehe) checking what you
generated. It is *not* what you received from the other side.

> 3. (Hash check) Perform the computation [...] If test ≠ dk[768𝑘+32 ∶
768𝑘+64], then input checking has failed.

This is *part of the private key*. Likewise, that is (here) checking what
you generated. It is *not* what you received from the other side.

Remember that "input" in FIPS 203 does not mean "what happens in TLS". It
is the input to the decap function, and the private key is an input to the
decap function. All this is because of the funny way that FIPS 203 models
things. It models the decap function as taking the private key as a byte
string, in the funky "semi-expanded" form, which has some redundancies[*].

Now, obviously, you don't want to revaluate the private key every time you
do an operation. You want to encapsulate (hah) it in some MLKEMPrivateKey
object which captures these invariants. That's why that same section of
FIPS 203 goes on to talk about how you don't *actually* need to check it
each time because "assurance that this check has been performed can be
acquired through other means", and it then it cites SP 800-227.

That ends up being a very roundabout way of saying that you might know the
private key is valid because you generated or you imported it yourself and
did the checks once at import time. In TLS's case, it's because you
generated it.

Now, the problem is: if we go in TLS and say "you must perform these
checks", it reads like we're saying "well, FIPS 203 said this is OK to use
other assurance, but it's important to TLS that you recheck this anyway.
That's not the message we want to communicate.

David

[*] This form has turned out to be more-or-less irrelevant. Other than some
unforced errors in the PKCS#8 format, we've all moved on from this one. It
turns out the semi-expanded form is neither a good interchange format (too
much redundant stuff; use seeds) nor a good in-memory format (too little
redundant stuff; you should cache the matrix). All it is at this point is a
very weird model of the correct behavior.
_______________________________________________
TLS mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to