HKDF-Extract consists of a function that uses HMAC, which in itself relies on a hash. So what we need to do is to work backwards from that.
We'll assume SHA-256 with a 64 byte block size as SHA-512 has a bigger block size of 128 and is therefore unlikely to be the limiting factor, even though it uses 17 bytes for padding and length encoding instead of the 9 for SHA-256. Note that SHA-3 is not mentioned in the RFC.
Let's first look at the algorithms used:
The capacity of a single run of the inner SHA-256 on the final block including padding is 64 - 8 - 1 = 55 bytes. The 8 bytes are required to encode the size of the message in bits, and one byte is required for the bit padding. All input before that is processed block by block, which is important for the HMAC function.
SHA-256 is used in the HMAC function where the key $K'$ is used, padded with $opad$ which is the block size. That means we can safely ignore the HMAC function here to calculate the minimum, as the key simply takes a full block before the final one.
The way HMAC is used in PBKDF-Expand removes one byte from the total for a counter, leaving us with 54 bytes. We only need one block as output, otherwise the output of the previous hash would also be included, skewing the results.
So we have 54 bytes to work with in the final block which is going to contain the label.
The label is not used directly in the definition of TLS. We can see that:
struct {
uint16 length = Length;
opaque label<7..255> = "tls13 " + Label;
opaque context<0..255> = Context;
} HkdfLabel;
Here the size of the label
and context
fields are prefixed as a single byte to the byte arrays (or vectors in TLS parlor that they represent. Note that the 7 in <7..255>
assumes that the label is at least one character.
These are the components of the structure in bytes, except for the label:
- length: 2 bytes (uint16)
- label length field: 1 byte (since the label length ranges from 7 to 255, it fits in 1 byte)
- "tls13 " string: 6 bytes
- label: the max size of which we try to calculate
- context length field: 1 byte (since the context length ranges from 0 to 255, it fits in 1 byte)
- context: 32 bytes (given)
if we calculate the final tally of the known bytes sizes we have:
$$2 + 1 + 6 + 1 + 32 = 42$$
Now we can do the final calculation: $54 - 42 = 12$ bytes left in the block for the label.
Alternatively the calculation can also be found on the TLS 1.3 mailing list.