On Mon, Jul 06, 2026 at 07:23:41PM -0400, Nick Sullivan wrote:
> Dear TLS,
>
> I'm sharing a draft for the group's consideration.
> draft-sullivan-tls-xof-ciphers-00 runs the entire TLS 1.3 key schedule
> on a single Keccak permutation, instead of HKDF built on HMAC built on
> the cipher suite's hash, which today is always SHA-2. This is newly
> practical because deployments using SHA-3, ML-KEM, or ML-DSA already
> carry a Keccak permutation, so the primitive is already in the stack.
This looks pretty complicated.
I came up with the following design for high-performance TLS 1.3 key
schedule based on (Turbo)Keccak:
- Start with a XOF. XOF(input,len) invokes XOF on input and returns
len bytes of output. XOFid is identifier for the XOF. For performance,
it should be 8 bytes. I think XOF should be (Turbo)Shake256.
- XOFs do not have natural output length. So let L be some fixed length.
For performance, L should be multiple of 8 at most 48. This is also
used as Hash.length. I think L should be 48.
- But L might not be suitable length for binders. So let Lb be length of
binder. I think Lb should be 32.
- For performance, extra derives for rachet should be avoided, and XOF
can cheaply give 2L byte output, so let state consist of two L byte
subkeys. Left(x) gives the first subkey, and Right(x) gives the
second.
- For compatibility with things like QUIC, Right(x) can also be applied
to L byte string, yielding x.
- Let Rachet(x,input) = XOF(XOFid||Left(x)||input, 2*L).
This operation mixes in input and irreversibly mixes it, acting like
combination of HKDF-Extract and Derive-Secret("derived").
- Transcript hash is XOF(messages,L).
- Let Output(x,label,context,len) = XOF(Right(x)||HkdfLabel,len).
HkdfLabel is as in section 7.1 of RFC8446. This acts like
HKDF-Expand-Label in RFC 8446.
- The key schedule from RFC8446 is modified as follows:
* All HKDF-Extract are replaced by Rachet.
* All "derived" operations are removed (since Rachet already
does the equivalent).
* All HKDF-Expand-Label are replaced by Output.
* All transcript hashes of "" are replaced by empty context.
(binder_key, TLS-Exporter labels)
* All traffic secrets have length 2*L (since those are states, the
early/handshake ones never use the first L bytes, but that is
~free).
* verify_data is computed as Output(BaseKey,"finished",transcript,Lb).
(Directly in one step, instead of expand+MAC).
* Traffic secrets are racheted by using Rachet with empty input
(instead of HKDF-Expand-Label. EKU could use non-empty input).
* AEAD key material is computed as
write_key||write_iv = Output(traffic_secret,"key",len).
(generating the whole thing in one go, instead of separate
"key"/"iv" derivations).
- Extracting traffic secret (e.g. for QUIC) gives Right(traffic_secret).
Because Output works with L byte keys, the application can use Output.
Ignoring the transcript and assuming no PSK, I count that this needs
15 decks on both sides (3 for rachets, 8 for encryption keys, 2 for
finished, 1 for exporters and 1 for resumption).
Even if this is closer to the existing key schedule than
draft-sullivan-tls-xof-ciphers, it still could be very problematic to
implement (I have not even tried to do that) and of course needs a new
security proof.
-Ilari
_______________________________________________
TLS mailing list -- [email protected]
To unsubscribe send an email to [email protected]