Skip to content

Fix TLS 1.3 record padding handling in _nx_secure_tls_process_record (RFC 8446 §5.4)#401

Open
EdouardMALOT wants to merge 1 commit into
eclipse-threadx:masterfrom
EdouardMALOT:fix/tls13-record-padding
Open

Fix TLS 1.3 record padding handling in _nx_secure_tls_process_record (RFC 8446 §5.4)#401
EdouardMALOT wants to merge 1 commit into
eclipse-threadx:masterfrom
EdouardMALOT:fix/tls13-record-padding

Conversation

@EdouardMALOT

Copy link
Copy Markdown
Contributor

Summary

A TLS 1.3 peer that pads its records cannot talk to NetX Duo: the very first padded record is rejected and the connection is torn down with a fatal alert.

Per RFC 8446 §5.4, the decrypted TLS 1.3 record is:

struct {
    opaque content[TLSPlaintext.length];
    ContentType type;
    uint8 zeros[length_of_padding];
} TLSInnerPlaintext;

The receiver must scan from the end of the plaintext, skipping zero bytes, to find the inner content type. _nx_secure_tls_process_record() instead reads the literal last byte as the content type and strips exactly one byte. For any padded record the extracted type is 0x00 (invalid, later rejected as an unrecognized message type), and the padding bytes would remain inside the message content.

Record padding is not exotic: the Java JDK (11+) TLS 1.3 implementation sends padded records (encountered in the field with a Java client), and OpenSSL produces them with -record_padding N.

Changes

  • nx_secure/src/nx_secure_tls_process_record.c — after decryption of a TLS 1.3 record, scan backward from the end of the plaintext skipping zero bytes; the first non-zero byte is the inner content type, and the message length is set to exclude the type byte and the padding. A plaintext consisting only of zeros is rejected (NX_SECURE_TLS_INVALID_PACKET), as required by §5.4 ("If a receiving implementation does not find a non-zero octet in the cleartext, it MUST terminate the connection").

Unpadded records take the same path as before: the first byte inspected (the last plaintext byte) is non-zero and the resulting length is unchanged.

Note: for the all-zeros case, RFC 8446 prescribes an unexpected_message alert; NX_SECURE_TLS_INVALID_PACKET currently maps to the internal-error alert group in _nx_secure_tls_map_error_to_alert(). The connection is still terminated with a fatal alert; happy to adjust the mapping if a stricter alert code is preferred.

Test plan

  • TLS 1.3 session against a peer sending padded records (Java 11+ HTTPS client): previously failed on the first encrypted record, now completes
  • OpenSSL with -record_padding N: handshake and application data exchange succeed
  • Unpadded TLS 1.3 traffic (OpenSSL/Mosquitto defaults): unchanged
  • TLS 1.2 record processing: not affected (code path is TLS 1.3-only)

…C 8446 §5.4)

The inner content type was read as the literal last byte of the
decrypted plaintext. Per RFC 8446 §5.4 that byte is followed by an
arbitrary-length zero padding, so the receiver must scan back from
the end skipping zeros to find it.
Without this, any client that pads its TLS 1.3 records gets a fatal
`unexpected_message` alert on its first record. Java JDK HttpClient
pads by default since 11 (traffic-analysis resistance), so every Java
HTTPS client tripped on this. Reproducible with OpenSSL using
`-record_padding N`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant