Skip to content

New TLS 1.3 record format

The macro TMP_IS_TLS_1_3() triggers TLS 1.3 related code. I've set this to (type == GNUTLS_APPLICATION_DATA) purely for my convenience, as it's easier to work with application records in the debugger.

When this code gets merged, it should probably be changed to something like:

#define IS_TLS_1_3(session) (get_version(session)->tls13_sem)

where tls13_sem should be activated when negociating a TLS 1.3 session. I mark this MR as WIP because of this, and because I haven't yet implemented the new per-record nonce, as specified in section 5.3.

I've put a great deal of effort in making this code self-contained. It can be merged as-is, and it won't break older TLS communications. This is (should be) true even if you set TMP_IS_TLS_1_3 to (true) or (false) or if you keep it at its current value of (type == GNUTLS_APPLICATION_DATA).

Here goes a summary of the changes.

First commit ("Receive new record format")

This commit removes the arbitrary padding from the plaintext as required in section 5.4. It first decrypts the ciphertext, and then scans the plaintext from bottom to top until the first non-zero byte. This byte is the content type. If it does not match the expected content type my implementations fails with GNUTLS_E_TLS_PACKET_DECODING_ERROR.

There's some ambiguity here as the draft says implementations MAY add arbitrary padding (composed of an arbitrary number of zeros) but also says they MUST fail if they don't find any zero byte in the plaintext.

If a receiving implementation does not find a non-zero octet in the cleartext, it MUST terminate the connection with an "unexpected_message" alert.

This leads me to question what happens if I receive an unpadded plaintext which has, well, any zeros in it by itself? As per the draft I would have to discard it, but the sender explicitly decided not to add padding, and it turned out the plaintext itself had no zeros in it. Is this feasible to happen in the first place?

Second commit ("Send new record format")

This commit adds more responsibilities to the _gnutls_send_tlen_int() function. It leverages the min_pad parameter to let the caller influence on TLS 1.3 arbitrary padding. If min_pad is zero no padding is added at all if we're using an AEAD cipher (mandatory in TLS 1.3). If we're using a non-AEAD cipher then the previous behaviour is preserved regardless of the value of min_pad: that value is passed to encrypt_packet(), and it will decide what to do. This way we don't break _gnutls_send_tlen_int(). The function is kept self-contained and client code doesn't have to be changed. Client code doesn't have to know which version of TLS we're in - _gnutls_send_tlen_int() will do the right thing.

Now if min_pad is non-zero and we're in TLS 1.3 and we're using an AEAD cipher (implicit), then padding is added to the plaintext before encrypting as explained in section 5.4 (same as before). I've decided to take min_pad as a hint and add at least that much padding, but maybe some more. Padding is added until we reach the next 16-byte boundary, or the maximum send size (max_send_size), whichever comes first.

The macro TMP_IS_TLS_1_3() is used to determine whether we're in TLS 1.3 or not. As I said, it should work even if we sett that macro to (true), because no padding is added if min_pad is 0, and if it's not, the padding is removed automatically at the receiver thus recovering the original contents.

Finally, we also add a new function, _gnutls_zeroize_datum(), which allocates a new gnutls_datum_t as long as the data to send, plus the padding, plus one byte for the content type. It then fills it with zeros and then rewrites the first bytes with the data to send.

Third commit ("New revived high-level record range API")

We make some minor modifications in gnutls_record_send_range() and we add a new function gnutls_tls13_record_send_padding(). This function generates a TLSInnerPlaintext with padding only, no content of any type (just one byte for the content type). It has similar semantics as gnutls_record_send_range() but the caller only specifies the minimum amount of padding, not the maximum. Padding is added starting at the specified minimum, until the next 16-byte boundary.

Merge request reports