Skip to content

Fix TLS 1.3 CertificateVerify transcript hash length (use ciphersuite hash) - #417

Open
EdouardMALOT wants to merge 2 commits into
eclipse-threadx:devfrom
EdouardMALOT:fix/tls13-certverify-transcript-hash-length
Open

Fix TLS 1.3 CertificateVerify transcript hash length (use ciphersuite hash)#417
EdouardMALOT wants to merge 2 commits into
eclipse-threadx:devfrom
EdouardMALOT:fix/tls13-certverify-transcript-hash-length

Conversation

@EdouardMALOT

Copy link
Copy Markdown
Contributor

Summary

Follow-up to the #377 review discussion (#377 (comment)):
the dynamic transcript-hash length introduced there is keyed to the signature scheme's
hash, but per RFC 8446 §4.4.3 the
CertificateVerify content embeds Transcript-Hash(Handshake Context), whose length is fixed
by the negotiated ciphersuite's hash. The two differ whenever a peer signs with a hash
other than the suite's.

Concrete case with the only TLS 1.3 suite currently enabled (TLS_AES_128_GCM_SHA256):
an ECDSA P-384 certificate signs ecdsa_secp384r1_sha384, so 48 bytes are copied from a
32-byte transcript-hash slot (nx_secure_tls_transcript_hashes[..][NX_SECURE_TLS_MAX_HASH_SIZE]
with NX_SECURE_TLS_MAX_HASH_SIZE == 32). This corrupts the signed content on the send side
and rejects valid peer signatures on the verify side — TLS 1.3 no longer works with ECDSA
P-384/P-521 certificates (it did before #377, when the copy length was a constant 32,
which was correct for every configuration shipped at the time).

The concern raised in the review remains fully addressed: the length is not hardcoded, and
SHA-384/512 ciphersuites will produce 48/64-byte transcript hashes when they are enabled.

Changes

  • nx_secure/src/nx_secure_tls_send_certificate_verify.c — key the transcript-hash copy
    length to nx_secure_tls_session_ciphersuite -> nx_secure_tls_hash (SHA-256 fallback,
    mirroring _nx_secure_tls_1_3_transcript_hash_save). The signature scheme's hash still
    digests the assembled content and parameterizes RSA-PSS.
  • nx_secure/src/nx_secure_tls_process_certificate_verify.c — same fix on the verify side.
  • test/regression/nx_secure_test/nx_secure_tls_1_3_certverify_transcript_hash_test.c
    regression test calling both functions directly with a signature-scheme hash (SHA-384) that
    differs from the ciphersuite hash (SHA-256), capturing via a spy hash method the exact byte
    count copied into the CertificateVerify content. Asserts 32 bytes (ciphersuite SHA-256,
    correct), not 48 (signature-scheme SHA-384, what the bug copied).

Test plan

  • Regression test added, exercising both the send and process paths directly
  • Verified against real hardware: a NetX Duo TLS 1.3 client repeatedly and reliably
    completes the handshake against a broker presenting EC P-384 and P-521 server
    certificates (ecdsa_secp384r1_sha384 / ecdsa_secp521r1_sha512), where it previously
    rejected the broker's valid CertificateVerify signature. A P-256 control case (unaffected
    by the bug, same hash on both sides) continues to pass throughout.
  • Regression check: P-256 / RSA (sha256 schemes) handshakes unchanged

Related: #377, #399, #161.

… hash)

Per RFC 8446 §4.4.3 the CertificateVerify content embeds
Transcript-Hash(Handshake Context), whose length is fixed by the
negotiated ciphersuite's hash — not by the signature scheme's hash.
The dynamic length introduced during the eclipse-threadx#377 review keyed it to the
signature scheme's hash; the two differ whenever the peer signs with
a hash other than the suite's, e.g. an ECDSA P-384 certificate
(ecdsa_secp384r1_sha384) with TLS_AES_128_GCM_SHA256 — the only TLS 1.3
suite currently enabled. In that case 48 bytes were copied from a
32-byte transcript hash slot, corrupting the signed content on the send
side and rejecting valid peer signatures on the verify side.

Key the transcript length to the session ciphersuite hash (with the
same SHA-256 fallback as _nx_secure_tls_1_3_transcript_hash_save); the
signature scheme's hash still digests the assembled content and
parameterizes RSA-PSS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@EdouardMALOT
EdouardMALOT force-pushed the fix/tls13-certverify-transcript-hash-length branch 2 times, most recently from 0016490 to ced5aae Compare August 6, 2026 12:19
…sh length

Calls _nx_secure_tls_send_certificate_verify and
_nx_secure_tls_process_certificate_verify directly with a signature-scheme
hash (SHA-384) that differs from the ciphersuite hash (SHA-256), and
captures via a spy hash method the exact byte count copied into the
CertificateVerify content. Asserts 32 bytes (the ciphersuite's SHA-256,
correct per RFC 8446 §4.4.3), not 48 (the signature scheme's SHA-384,
what the bug copied).
@EdouardMALOT
EdouardMALOT force-pushed the fix/tls13-certverify-transcript-hash-length branch from ced5aae to 9de2760 Compare August 6, 2026 12:40
@EdouardMALOT
EdouardMALOT changed the base branch from master to dev August 6, 2026 14:32
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