Transcript¶
The Transcript Hash is a running sha2 of the handshake messages
exchanged when establishing the secure connection. The digest is used
many times, at various stages of the exchange, by
TLSCipherSuite. The digest,
together with the key exchanged via
key_share or
pre_shared_key, is used to
generate the many secrets key used by the AEAD algorithms to encrypt
exchanges.
There are shenanigans regarding
ClientHello and
HelloRetryRequest that
make the implementation unfortunately not trivial.
About Client Hello
The client can offer multiple cipher suites in its first
ClientHello, the
different offered cipher suites can come with different hashing
algorithm, e.g. TLS_AES_128_GCM_SHA256
vs TLS_AES_256_GCM_SHA384 (SHA256 vs
SHA386). The actual hashing algorithm to use for this connection will
only be known with the
ServerHello /
HelloRetryRequest
hanshake of the server.
This means that either (1) we have to keep the
ClientHello message
around until we know what hash algorithm the server decides to use and
only then compute the transcript hash, either (2) we save multiple
hashes and then discard those we won’t use. The current implementation
uses (2) as several passages of the TLS RFC hint at this choice and that
it seems to be the common method used by many TLS implementations.
In cases multiple differents hashing algorithms are offered, that until
receiving ServerHello
/ HelloRetryRequest we
do not know which one will be used, it is forbidden to request a digest
(via the digest() and hexdigest() methods) before calling
post_init().
This has an important implication regarding
TLSCipherSuite and Early Data:
the transcript hash of
ClientHello is used
when generating the “early exported master” and “client early traffic”
secrets. Those secrets are generated and used before receiving
ServerHello /
HelloRetryRequest. All
the pre-shared-keys all must share the same hashing algorithm othersise
it would be impossible to generate the secrets.
About Hello Retry Request
TLS 1.3 claims that it is possible to do a stateless
HelloRetryRequest
using a clever (but not smart) hack. We still don’t know how to do a
stateless
HelloRetryRequest but
we still have to implement the hack otherwise the transcript wouldn’t be
right.
- class siotls.transcript.Transcript¶
- __init__(digestmods)¶
Prepare a new empty transcript for a new connection.
- Parameters:
digestmods (Iterable[siotls.crypto.HashFunction]) – the list of hash function that are supported for this connection, usually SHA256, SHA384, or both.
- Raises:
ValueError – When
digestmodsis empty.
- copy()¶
Create an independent copy of the current transcript hash.
- digest()¶
Digest the current transcript hash.
- Raises:
ValueError – When this method is called before
post_init()was called to finilize the initialization.- Return type:
bytes
- do_hrr_dance()¶
Implemented as follow:
Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) = Hash(message_hash || /* Handshake type */ 00 00 Hash.length || /* Handshake message length (bytes) */ Hash(ClientHello1) || /* Hash of ClientHello1 */ HelloRetryRequest || ... || Mn)
- hexdigest()¶
Digest and hexlify the current transcript hash.
- Raises:
ValueError – When this method is called before
post_init()was called to finilize the initialization.- Return type:
str
- post_init(digestmod)¶
Finilize the initialization with a definitive hash function.
- Parameters:
digestmod (siotls.crypto.HashFunction) – The definite hash function.
- Raises:
ValueError – When
digestmodis not one of the hash functions this transcript was initialized with.- Return type:
None
- update(handshake_data, side, handshake_type)¶
Update the underlying hash with new data.
The side and handshake type are used as sanity check, and also to save the ClientHello’s transcript digest for
do_hrr_dance().- Parameters:
handshake_data (bytes) – The plain handshake, as extracted and decrypted from TLS record layer.
side ('client' | 'server') – The side of this connection.
handshake_type (HandshakeType | HandshakeType_) – The type of the handshake.