Skip to content

export_keying_material() API doesn't conform with TLS 1.3 recommendations #945

@briansmith

Description

@briansmith

The TLS 1.3 spec says in https://datatracker.ietf.org/doc/html/rfc8446#appendix-E.1.4:

[B]ecause these secrets can be used to compute any
exporter value, they SHOULD be erased as soon as possible. If the
total set of exporter labels is known, then implementations SHOULD
pre-compute the inner Derive-Secret stage of the exporter computation
for all those labels, then erase the [early_]exporter_master_secret,
followed by each inner value as soon as it is known that it will not
be needed again.

However, the design of the export_keying_material() API makes this erasure recommendation impossible to implement, because we currently support calling export_keying_material() at any time after the handshake completes, and so we have to retain the exporter key.

Note that TLS doesn't provide any way to rotate the exporter key; the KeyUpdate mechanism only applies to traffic keys. This design pretty much forces us to implement the erasure recommendation above, if we want to have long-lived connections that minimize their ability to expose secrets.

I propose:

  • We remove export_keying_material() from the public API.
  • Add a "handshake" callback. (Note that this suggested interface is intended to be easily extensible in a way that maintains SemVer compatibility):
pub trait HandshakeComplete {
     /// Called just after the the peer's handshake messages and identity (if any) have been successfully validated.
     ///
     ///
      pub fn handshake_complete(&self, handshake_data: HandshakeData) -> Result<(), Box<dyn Debug>>
}

pub struct HandshakeData {
     /// <The current export_keying_material API>
     fn export_keying_material(&mut self, ....) -> ....;
}
  • When the handshake completes, call this callback.
  • Move current_exporter_secret out of KeyScheduleTraffic to the implementation(s) of HandshakeData. (It should probably be lazily constructed the first time export_keying_material is called, since most applications don't need it.)
  • The new export_keying_material() API should implement additional validation of its parameters as suggested in the TLS 1.3 specification and the and common sense (e.g. that the label and other parameters that should be non-empty aren't empty)..
  • Since HandshakeData is moved into the callback, its contents (in particular, the exporter key) will be destroyed automatically, implementing the semantics that the TLS 1.3 spec recommends.

Besides being safer, this would also:

  • Slightly reduce the memory requirements for each connection
  • Make it easier to split the KeySchedule into read and write parts, which I think is probably useful for full-duplex mode.

Metadata

Metadata

Assignees

Labels

next-major-releasebreaking changes put off until next major release

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions