On Fri, Jul 24, 2026 at 03:30:55PM +0200, Nick Sullivan wrote:
> TLSWG,
>
> TLS 1.3 runs its entire key schedule on HKDF over SHA-2. This draft defines
> an extension that replaces that schedule with one built on an
> extendable-output function, so no SHA-2 remains in the key schedule: the
> negotiated KDF governs every derivation, the Finished and binder MACs, and
> the transcript hash.
>
> This is draft-sullivan-tls-xof-ciphers-00, reworked and renamed. The old
> name came from a design that negotiated the schedule through cipher suites.
> The KDF now has its own extension, so the old name was wrong.
>
> https://datatracker.ietf.org/doc/draft-sullivan-tls-xof-schedule/
>
> What it does not change: the cipher suites, the AEAD algorithms, the state
> machine, and the record layer. A connection whose ClientHello does not
> carry the extension uses HKDF exactly as today; absence is the default, and
> the default is TLS 1.3. The KDF is negotiated in its own extension carrying
> a TLS KDF Identifier, so it is not bound to a cipher suite, and there is no
> new TLS version. The draft updates RFC 9258.
Some comments:
- This seems to be rather long for document like this.
There seems to be a lot of redundant stuff that could be written much
more concisely and clearly.
- Getting rid of the MAC might be useful. Complicates the analysis, but
simplifies the protocol.
- Might be useful to define frame(NULL)=epsilon.
- TLS 1.3 currently uses 48 byte transcripts at 256-bit security level.
Using 64-byte transcripts might add extra sponge calls.
- Postfix notation could be clearer than the prefix notation used.
E.g., finished_key = Init(base_key).Derive("finished")
- The exporter construction with only one derive does not have the same
properties as standard TLS 1.3 extractor.
No idea if anything uses the subkeying property of the TLS 1.3
extractor.
- ctag should presumably be parameter for Derive. In section 7.3
it is not at all clear what the values should be.
- That "out" stuff looks useless.
- Might be better to define Ratchet as in-place operation so it
inherently overwrites the old state.
- Might make sense to define Destroy operation that destroys state.
- # once per key-exchange component
This is a nasty surprise for implementations that treat the
key exchange as a black box.
- SHAKE is not supposed to be contextualized. If you need
contextualization, use cSHAKE instead.
- Some rewritten stuff:
ctx = Init(NULL)
ctx.absorb("psk", PSK) #PSK empty if no PSK.
c_e_traffic = ctx.derive("c e traffic", TH_CH)
c_exp_master = ctx.derive("e exp master", TH_CH)
binder_key = ctx.Derive(ext_or_res_label, NULL)
ctx.ratchet()
ctx.absorb("dh", DHE); #Omitted if no DH.
c_hs_traffic = ctx.derive("c hs traffic", TH_SH)
s_hs_traffic = ctx.derive("s hs traffic", TH_SH)
ctx.ratchet()
c_ap_traffic = ctx.derive("c ap traffic", TH_SF)
s_ap_traffic = ctx.derive("s ap traffic", TH_SF)
exp_master = ctx.derive("exp master", TH_SF)
res_master = ctx.derive("res master", TH_CF)
ctx.destroy()
TLS-Exporter(label, context_value, key_length) =
Init(Init(Secret).derive(label, NULL)).
derive("exporter", context_value, key_length)
app_traffic_secret_{N+1} =
Init(app_traffic_secret_N).derive("traffic upd", NULL)
- The handshake secret stuff looks like it can break blocks in
random places.
- The 64 byte hashlen looks like it causes bad blockbreaking in
traffic secret stuff.
-Ilari
_______________________________________________
TLS mailing list -- [email protected]
To unsubscribe send an email to [email protected]