Skip to content

Fix session ticket key rotation when hint exceeds key lifetime#10822

Merged
dgarske merged 3 commits into
wolfSSL:masterfrom
mattia-moffa:20260629-ticket-hint-window-check
Jul 9, 2026
Merged

Fix session ticket key rotation when hint exceeds key lifetime#10822
dgarske merged 3 commits into
wolfSSL:masterfrom
mattia-moffa:20260629-ticket-hint-window-check

Conversation

@mattia-moffa

@mattia-moffa mattia-moffa commented Jun 30, 2026

Copy link
Copy Markdown
Member

Description

Disallows a ticketHint > WOLFSSL_TICKET_KEY_LIFETIME / 2.

The default ticket encryption callback rotates two keys and can only honor a hint below half the ticket key lifetime; a larger hint previously left no key available for encryption, failing every handshake after the first on a reused server CTX. Now these values are rejected.

Fixes #10796

Testing

./configure --enable-dtls13 --enable-session-ticket && make && make check

Checklist

  • added tests

@mattia-moffa mattia-moffa self-assigned this Jun 30, 2026
Copilot AI review requested due to automatic review settings June 30, 2026 21:38

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.

Pull request overview

Fixes a session-ticket encryption key rotation edge case where ticketHint can exceed the default ticket key lifetime, previously causing TicketEncCbCtx_ChooseKey() to return BAD_STATE_E and break subsequent handshakes when reusing a server WOLFSSL_CTX.

Changes:

  • Update default session ticket key selection/rotation logic to avoid returning BAD_STATE_E when both keys are decrypt-valid but neither covers ticketHint.
  • Add a new API regression test that reuses server/client contexts across multiple handshakes with an oversized ticket hint.
  • Register the new test in the ssl extension test list.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/internal.c Adjusts default ticket key selection behavior when neither key covers the configured hint window.
tests/api/test_ssl_ext.c Adds a regression test covering repeated handshakes with a reused server context and oversized ticket hint.
tests/api/test_ssl_ext.h Exposes/registers the new regression test in the ssl_ext group.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/internal.c Outdated
Comment thread tests/api/test_ssl_ext.c Outdated
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

gcc-arm-cortex-m0plus

  • FLASH: .text +572 B (+0.9%, 64,155 B / 262,144 B, total: 24% used)

gcc-arm-cortex-m3

  • FLASH: .text +916 B (+0.8%, 122,449 B / 262,144 B, total: 47% used)

gcc-arm-cortex-m4

  • FLASH: .rodata.CSWTCH.1 +4 B, .rodata.str1.1 +58 B, .rodata.wolfSSL_ERR_reason_error_string.str1.1 +18 B, .text +960 B (+0.5%, 200,284 B / 262,144 B, total: 76% used)

gcc-arm-cortex-m4-baremetal

  • FLASH: .text +576 B (+0.9%, 66,763 B / 262,144 B, total: 25% used)

gcc-arm-cortex-m4-crypto-only

  • FLASH: .rodata.CSWTCH.1 +4 B, .rodata.str1.1 +58 B, .text +576 B (+0.4%, 174,440 B / 262,144 B, total: 67% used)

gcc-arm-cortex-m4-dtls13

  • FLASH: .text +896 B (+0.5%, 180,824 B / 1,048,576 B, total: 17% used)

gcc-arm-cortex-m4-min-ecc

  • FLASH: .text +576 B (+0.9%, 61,741 B / 262,144 B, total: 24% used)

gcc-arm-cortex-m4-openssl-compat

  • FLASH: .rodata +264 B, .text +2,240 B (+0.3%, 770,828 B / 1,048,576 B, total: 74% used)

gcc-arm-cortex-m4-pkcs7

  • FLASH: .rodata.CSWTCH.1 +4 B, .rodata.str1.1 +58 B, .text +640 B (+0.3%, 212,268 B / 262,144 B, total: 81% used)

gcc-arm-cortex-m4-pq

  • FLASH: .rodata +84 B, .text +1,152 B (+0.4%, 279,300 B / 1,048,576 B, total: 27% used)

gcc-arm-cortex-m4-rsa-only

  • FLASH: .rodata +80 B, .text +1,216 B (+0.4%, 324,960 B / 1,048,576 B, total: 31% used)

gcc-arm-cortex-m4-sp-math

  • FLASH: .text +576 B (+0.9%, 61,741 B / 262,144 B, total: 24% used)

gcc-arm-cortex-m4-tls12

  • FLASH: .text +896 B (+0.7%, 123,213 B / 262,144 B, total: 47% used)

gcc-arm-cortex-m4-tls13

  • FLASH: .rodata.CSWTCH.1 +4 B, .rodata.str1.1 +58 B, .rodata.wolfSSL_ERR_reason_error_string.str1.1 +18 B, .text +1,024 B (+0.5%, 235,982 B / 262,144 B, total: 90% used)

gcc-arm-cortex-m7

  • FLASH: .rodata.CSWTCH.1 +4 B, .rodata.str1.1 +58 B, .rodata.wolfSSL_ERR_reason_error_string.str1.1 +18 B, .text +960 B (+0.5%, 200,220 B / 262,144 B, total: 76% used)

gcc-arm-cortex-m7-pq

  • FLASH: .rodata +84 B, .text +1,152 B (+0.4%, 279,876 B / 1,048,576 B, total: 27% used)

gcc-arm-cortex-m7-tls13

  • FLASH: .rodata.CSWTCH.1 +4 B, .rodata.str1.1 +58 B, .rodata.wolfSSL_ERR_reason_error_string.str1.1 +18 B, .text +1,088 B (+0.5%, 236,046 B / 262,144 B, total: 90% used)

linuxkm-pie

  • Data: __patchable_function_entries +256 B (+1.0%, 25,456 B)

linuxkm-standard

  • Data: __patchable_function_entries +320 B (+0.7%, 48,120 B)

stm32-sim-stm32h753

  • FLASH: .text +832 B (+0.5%, 183,396 B / 2,097,152 B, total: 9% used)

The default ticket encryption callback rotates two keys and can only
honor a hint below WOLFSSL_TICKET_KEY_LIFETIME/2; a larger hint
previously left no key available for encryption, failing every handshake
after the first on a reused server CTX. Now these values are rejected.
@mattia-moffa
mattia-moffa force-pushed the 20260629-ticket-hint-window-check branch from 3125631 to 9514b21 Compare July 1, 2026 19:48
@mattia-moffa

Copy link
Copy Markdown
Member Author

Jenkins retest this please

@dgarske
dgarske requested a review from SparkiDev July 7, 2026 22:13

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Skoll Multi-Scan Review

Modes: review + review-securityOverall recommendation: REQUEST_CHANGES
Findings: 5 total — 4 posted, 1 skipped
2 finding(s) posted as inline comments (see file-level comments below)
2 finding(s) not tied to a diff line (full detail below)

Posted findings

  • [Medium] [review+review-security] Hint rejection also fires with a custom TicketEncCb -- macro gate reflects macro presence, not the default callbacksrc/ssl_api_ext.c:1073-1080
  • [Low] [review] Compile-time and runtime hint checks disagree by one for odd WOLFSSL_TICKET_KEY_LIFETIMEwolfssl/internal.h:2050-2052

Findings not tied to a diff line

New hint cap regresses OpenSSL-compat SSL_CTX_set_timeout for common timeouts (= 30 min)

File: src/ssl_api_ext.c:1073-1080 (manifests via src/ssl_sess.c:958-968)
Function: wolfSSL_CTX_set_TicketHint / wolfSSL_CTX_set_timeout
Severity: High
Category: Regression

wolfSSL_CTX_set_TicketHint is also called internally by the OpenSSL-compat wolfSSL_CTX_set_timeout (SSL_CTX_set_timeout). Under OPENSSL_EXTRA && HAVE_SESSION_TICKET && !NO_WOLFSSL_SERVER, wolfSSL_CTX_set_timeout(ctx, to) forwards the session timeout straight into wolfSSL_CTX_set_TicketHint(ctx, (int)to) (ssl_sess.c:968). The new check rejects any hint >= WOLFSSL_TICKET_KEY_LIFETIME / 2, i.e. any value >= 1800 seconds (30 min) with the default 3600s key lifetime. Session timeouts of 30+ minutes are the norm (OpenSSL's own default is 7200s; values like 3600/7200/86400 are routine), so SSL_CTX_set_timeout(ctx, 7200) -- which previously succeeded (only >604800 was rejected) -- now returns BAD_FUNC_ARG. This is a backward-incompatible break of a widely-used public compat API. Worse, ctx->timeout is already assigned at ssl_sess.c:958 BEFORE the ticket-hint call fails, so the session-cache timeout is silently applied while the function reports failure, leaving inconsistent state for callers that check the return code. Severity views differ (review: High/BLOCK; review-security: Medium) -- the stricter High is kept because this breaks a public API for common inputs; the session-cache timeout is conceptually independent of ticket-key rotation. Not covered by CI: test_wolfSSL_CTX_set_timeout only uses values below 1800.

Recommendation: Decouple the session-cache timeout from the ticket-hint constraint. In wolfSSL_CTX_set_timeout, clamp the value passed to wolfSSL_CTX_set_TicketHint (e.g. min(to, WOLFSSL_TICKET_KEY_LIFETIME/2 - 1)) or ignore a ticket-hint rejection when setting the session timeout, so a large session timeout still succeeds and ctx state stays consistent. Keep the hard rejection only for the direct public setter if desired. Add a test_wolfSSL_CTX_set_timeout case with a value >= WOLFSSL_TICKET_KEY_LIFETIME/2 (e.g. 3600) to lock in the intended behavior.

Referenced code: src/ssl_api_ext.c:1073-1080 (manifests via src/ssl_sess.c:958-968) (10 lines)


No test covers the wolfSSL_CTX_set_timeout - set_TicketHint regression path

File: tests/api.c:27884-27913
Function: test_wolfSSL_CTX_set_timeout
Severity: Medium
Category: test

The new tests in test_ssl_ext.c exercise the direct setter, but the highest-impact behavior change is via the internal caller wolfSSL_CTX_set_timeout. The existing test_wolfSSL_CTX_set_timeout only uses small values (20, 30, 100), all below the new 1800s cap, so it will neither catch the regression nor lock in whatever the intended behavior becomes. Since the change silently alters what SSL_CTX_set_timeout returns for common values, this path needs explicit coverage.

Recommendation: Add a case under OPENSSL_EXTRA + HAVE_SESSION_TICKET + server that calls wolfSSL_CTX_set_timeout with a value >= WOLFSSL_TICKET_KEY_LIFETIME/2 (e.g. 3600) and asserts the intended outcome (success with clamped hint per the fix above), and verify ctx state is consistent.


Skipped findings

  • [Info] Stricter compile-time #error may break previously-valid custom lifetime/hint configurations

Review generated by Skoll

Comment thread src/ssl_api_ext.c Outdated
Comment thread wolfssl/internal.h Outdated

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SparkiDev
SparkiDev previously approved these changes Jul 7, 2026
@dgarske

dgarske commented Jul 8, 2026

Copy link
Copy Markdown
Member

Jenkins retest this please: "Error cloning remote repo 'origin'". Timeout

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Skoll Multi-Scan Review

Modes: review + review-securityOverall recommendation: COMMENT
Findings: 4 total — 4 posted, 0 skipped
3 finding(s) posted as inline comments (see file-level comments below)
1 finding(s) not tied to a diff line (full detail below)

Posted findings

  • [Medium] [review+review-security] Oversized ticket hint returns FATAL and tears down the whole handshake instead of degrading to a ticket-less connectionsrc/internal.c:41749
  • [Low] [review] New failure uses WOLFSSL_TICKET_RET_FATAL while other encrypt-side returns in this function use REJECTsrc/internal.c:41751
  • [Info] [review] Trivial custom ticket-encryption callback duplicated across two test filestests/api/test_ssl_ext.c:203

Findings not tied to a diff line

Compile-time ticket lifetime guard does not cover the new runtime 2*hint constraint

File: wolfssl/internal.h:2050
Function: DefTicketEncCb
Severity: Low
Category: Convention

internal.h statically enforces WOLFSSL_TICKET_KEY_LIFETIME > SESSION_TICKET_HINT_DEFAULT, but the new runtime rule requires 2 * ticketHint < WOLFSSL_TICKET_KEY_LIFETIME. With the defaults (lifetime 3600, default hint 300) there is no problem (600 < 3600). However, a user who overrides WOLFSSL_TICKET_KEY_LIFETIME to a value in the range (SESSION_TICKET_HINT_DEFAULT, 2*SESSION_TICKET_HINT_DEFAULT] (e.g. 400-600) passes the compile-time #error check yet now has every default-hint ticket handshake fail at runtime, with no build-time warning. This is a narrow, already-degenerate configuration, but the failure surfaces only at runtime.

Recommendation: Consider tightening the static assert to #if WOLFSSL_TICKET_KEY_LIFETIME <= 2 * SESSION_TICKET_HINT_DEFAULT (or documenting the 2*hint requirement near WOLFSSL_TICKET_KEY_LIFETIME) so users who shrink the key lifetime are warned at build time rather than seeing runtime handshake failures.

Referenced code: wolfssl/internal.h:2050-2052 (3 lines)


Review generated by Skoll

Comment thread src/internal.c Outdated
Comment thread src/internal.c Outdated
Comment thread tests/api/test_ssl_ext.c
@mattia-moffa

Copy link
Copy Markdown
Member Author

Jenkins retest this please

@mattia-moffa
mattia-moffa requested a review from dgarske July 9, 2026 01:50
@dgarske
dgarske merged commit f2fc9b6 into wolfSSL:master Jul 9, 2026
320 checks passed
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.

[Bug]: DefTicketEncCb: BAD_STATE_E on second handshake when ticket hint > WOLFSSL_TICKET_KEY_LIFETIME / 2

5 participants