Skip to content

prevent sequence wrap for tls13 - #11767

Open
sebastian-carpenter wants to merge 2 commits into
wolfSSL:masterfrom
sebastian-carpenter:tls-sequence
Open

sebastian-carpenter wants to merge 2 commits into
wolfSSL:masterfrom
sebastian-carpenter:tls-sequence

Conversation

@sebastian-carpenter

@sebastian-carpenter sebastian-carpenter commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Description

chacha20 or any AEAD with WOLFSSL_TLS13_IGNORE_AEAD_LIMITS defined would wrap the sequence number at 2^64.

Added a reserved section of sequence numbers: between 2^64 - 2^16 - 1 and 2^64.

  • Once the lower limit is reached tls13 will send out a KeyUpdate
  • If 2^64 - 1 is reached then the connection will fail with SEQUENCE_NUMBER_E

This reserved section is mainly for post-handshake authentication, where a series of records would have to use the same key.

GH #11129

Testing

  • Added check for chacha at the imposed sequence limit
  • AES with WOLFSSL_TLS13_IGNORE_AEAD_LIMITS defined does a keyupdate at the imposed sequence limit
  • Reaching 2^64 - 1 causes an error

There was also an issue with asynchronous crypto. When the sequence number lies on the limit boundary the KeyUpdate would be queued before the call completed.

Fixed issue and modified test_tls13_cryptocb_async to run with the sender at the AEAD limit.


DTLS:

  • DTLS 1.3 now hard fails with SEQUENCE_NUMBER_E when an epoch's sequence number reaches 2^64 - 1
  • The DTLS 1.3 handshake message_seq fails with the new DTLS_MSG_SEQUENCE_E instead of wrapping.

Testing

  • DTLS 1.3 does a KeyUpdate at the AEAD limit for each AES-GCM/CCM suite
  • DTLS 1.3 does a KeyUpdate at the sequence limit for ChaCha20, SHA256-SHA256, and AES with WOLFSSL_TLS13_IGNORE_AEAD_LIMITS defined
  • TLS 1.3 does a KeyUpdate at the sequence limit for SHA256-SHA256
  • In DTLS 1.3, reaching 2^64 - 1 or the message_seq maximum causes an error

Checklist

  • added tests
  • updated/added doxygen
  • updated appropriate READMEs
  • Updated manual and documentation

@sebastian-carpenter sebastian-carpenter self-assigned this Oct 15, 2026
Copilot AI balanced review requested due to automatic review settings October 15, 2026 17:48
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report · Explore what changed →

gcc-arm-cortex-m4

  • FLASH: .rodata.wolfSSL_ERR_reason_error_string.str1.1 +28 B (+0.0%, 209,320 B / 262,144 B, total: 80% used)

gcc-arm-cortex-m4-dtls13

  • FLASH: .text +128 B (+0.1%, 196,376 B / 1,048,576 B, total: 19% used)

gcc-arm-cortex-m4-openssl-compat

  • FLASH: .rodata +32 B, .text +64 B (+0.0%, 801,988 B / 1,048,576 B, total: 76% used)

gcc-arm-cortex-m4-pq

  • FLASH: .rodata +24 B, .text +64 B (+0.0%, 313,192 B / 1,048,576 B, total: 30% used)

gcc-arm-cortex-m4-rsa-only

  • FLASH: .rodata +32 B (+0.0%, 343,792 B / 1,048,576 B, total: 33% used)

gcc-arm-cortex-m4-tls13

  • FLASH: .rodata.wolfSSL_ERR_reason_error_string.str1.1 +28 B, .text +64 B (+0.0%, 249,982 B / 262,144 B, total: 95% used)

gcc-arm-cortex-m7

  • FLASH: .rodata.wolfSSL_ERR_reason_error_string.str1.1 +28 B (+0.0%, 209,384 B / 262,144 B, total: 80% used)

gcc-arm-cortex-m7-pq

  • FLASH: .rodata +24 B (+0.0%, 314,024 B / 1,048,576 B, total: 30% used)

gcc-arm-cortex-m7-tls13

  • FLASH: .rodata.wolfSSL_ERR_reason_error_string.str1.1 +28 B, .text +64 B (+0.0%, 249,982 B / 262,144 B, total: 95% used)

zephyr-frdm_rw612

  • FLASH: datas +16 B, log_const_area -8 B, rodata +20 B, text +10,016 B (+3.6%, 292,508 B / 67,108,864 B, total: 0% used)
  • RAM: bss +49 B, datas +16 B (+0.1%, 58,423 B / 983,040 B, total: 6% used)

No memory changes detected for:


Catch memory regressions in CI with MemBrowse. Free for open source.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new rekey trigger can overwrite a suspended asynchronous record before encryption resumes.

1 open finding
What changed in this PR

Prevents TLS 1.3 send-sequence wrap by rekeying before exhaustion and rejecting records at the final sequence number.

Changes:

  • Adds a rekey threshold independent of AEAD usage-limit enforcement.
  • Adds a hard stop before sequence rollover.
  • Tests rekey boundaries and exhaustion behavior.
File Description
wolfssl/​internal.h Defines rekey thresholds and helper declaration.
tests/​api/​test_tls13.h Registers sequence-wrap tests.
tests/​api/​test_tls13.c Adds boundary tests and strengthens KeyUpdate checks.
src/​tls13.c Detects rekey threshold and blocks sequence wrap.
src/​internal.c Triggers rekeying near sequence exhaustion.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/internal.c Resolved
@sebastian-carpenter

sebastian-carpenter commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Jenkins retest this please.

@rizlik rizlik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good work, sorry about the late review, I do think the following points will improve the contribution:

  1. Extend the same automatic re-keying mechanic to DTLSv1.3, using the same trigger point looks ok to me.
  2. Fix the ignored return value of Dtls13GetSeq in WriteSEQTls13, so that DTLSv1.3 has a failclose guard against wrapping (the guard is inside Dtls13GetSeq but WriteSEQTls13 and BuildTls13Nonce are both void, so the error never reaches EncryptTls13/BuildTls13Message). Either change the return type of WriteSEQTls13 and BuildTls13Nonce to bubble the error up through EncryptTls13, or add a new specific DTLS guard on dtls13EncryptEpoch->nextSeqNumber next to the TLS hard stop in BuildTls13Message (probably better, it also fires before the seq is incremented rather than after).
  3. Add a wrapping guard in DTLSv1.3 Handshake message_seq as well (16 bits), incremented by post-hand auth, keyUpdates and NewSessionTicket. Actually, this is probably the only wrapping scenario that can really happen in real life.
  4. Evaluate if consolidating limits check (both AEAD specific ones and general seq ones) in a single function.

@sebastian-carpenter

Copy link
Copy Markdown
Contributor Author
  1. DTLSv13 will now rekey automatically.
  2. Added a guard for the DTLS code based on dtls13EncryptEpoch->nextSeqNumber. This is next to the TLS hard stop in BuildTls13Message.
  3. Added a wrap check to msg_sequence. If it would wrap then instead error with DTLS_MSG_SEQUENCE_E.
  4. Pulled checks into CheckTLS13AEADSendLimit. When AEAD limits are ignored the switch now collapses to TLS13_SEQ_REKEY_LIMIT.

Extras:

  • Ended up adding rekey for the null cipher
  • memio testing for DTLS AEAD limits. (There is a socket-based one but it doesn't necessarily exercise all of the types)

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.

TLS 1.3 ChaCha Sequence Wrap Is Not Enforced in wolfSSL

4 participants