Skip to content

Add RFC8773(bis) cert_with_extern_psk support#10085

Open
Frauschi wants to merge 1 commit intowolfSSL:masterfrom
Frauschi:rfc8773bis-cert-with-extern-psk
Open

Add RFC8773(bis) cert_with_extern_psk support#10085
Frauschi wants to merge 1 commit intowolfSSL:masterfrom
Frauschi:rfc8773bis-cert-with-extern-psk

Conversation

@Frauschi
Copy link
Copy Markdown
Contributor

Implement RFC 8773(bis) cert_with_extern_psk for TLS 1.3.

Includes unit tests for API and handshake behavior as well as tests in the testsuite using extended examples.

Copy link
Copy Markdown

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #10085

Scan targets checked: src, src-bugs, src-compliance
Findings: 3

Medium (2)

DoPreSharedKeys returns fatal error instead of skipping session ticket identity when cert_with_extern_psk is offered

File: src/tls13.c:6193-6198
Function: DoPreSharedKeys
Category: Logic errors

When the server processes PSK identities in DoPreSharedKeys, it iterates through all client-offered identities trying each as a session ticket first, then as an external PSK via FindPsk. The new code returns PSK_KEY_ERROR immediately when a session ticket identity matches and certWithExternOffered is true. This terminates the entire function, preventing the server from trying subsequent identities in the list. A legitimate client could offer a session ticket identity (for resumption with servers that don't support cert_with_extern_psk) followed by an external PSK identity (for cert_with_extern_psk). The fatal return prevents the server from ever reaching the external PSK identity, causing a handshake failure that could be avoided by simply skipping the ticket identity.

#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(HAVE_SESSION_TICKET)
            if (certWithExternOffered) {
                WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR);
                return PSK_KEY_ERROR;
            }
#endif

Recommendation: Replace return PSK_KEY_ERROR with continue so that the server skips the session ticket identity and proceeds to try the next identity in the list, which may be a valid external PSK identity suitable for cert_with_extern_psk.


DoPreSharedKeys fatally rejects session ticket match when cert_with_extern_psk is offered, violating RFC 8773bis fallback behavior

File: src/tls13.c:6196-6200
Function: DoPreSharedKeys
Category: Session resumption violations

When the client offers the tls_cert_with_extern_psk extension and also includes a session ticket identity in pre_shared_key, the server returns PSK_KEY_ERROR if the session ticket successfully decrypts. This is a hard return, not a continue, so it aborts the entire PSK identity search loop — even if a valid external PSK identity appears later in the list.

RFC 8773bis Section 3 explicitly permits the server to select a resumption PSK when the client offers cert_with_extern_psk: "If the client includes the 'tls_cert_with_extern_psk' extension in the ClientHello, and the server selects a resumption PSK, the server MUST NOT echo the extension." The spec allows normal ticket resumption as a fallback — the server simply omits the extension from ServerHello.

The current code prevents two valid scenarios:

  1. A client offering both a session ticket and an external PSK identity (ticket listed first) — the server should skip the ticket and try the external PSK, or fall back to normal resumption.
  2. A client offering cert_with_extern_psk with only a session ticket that matches — the server should fall back to normal ticket resumption without echoing the extension.
#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(HAVE_SESSION_TICKET)
            if (certWithExternOffered) {
                WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR);
                return PSK_KEY_ERROR;
            }
#endif

            /* SERVER: using secret in session ticket for peer auth. */
            ssl->options.peerAuthGood = 1;

Recommendation: Replace return PSK_KEY_ERROR with continue so the server skips the session ticket identity and continues searching for an external PSK identity. If no external PSK matches after the loop completes, the server can fall back to normal behavior (either retry with the ticket without echoing cert_with_extern_psk, or proceed without PSK).


Low (1)

TLSX_Cert_With_Extern_Psk_Parse overwrites actual error code with MEMORY_E

File: src/tls.c:12389-12390
Function: TLSX_Cert_With_Extern_Psk_Parse
Category: Incorrect error handling

In the client_hello handler of TLSX_Cert_With_Extern_Psk_Parse, the return value of TLSX_Cert_With_Extern_Psk_Use(ssl) is checked for non-zero but then hardcoded MEMORY_E is returned instead of the actual error code. TLSX_Cert_With_Extern_Psk_Use internally calls TLSX_Push, which may return various error codes. The actual error is lost and replaced with MEMORY_E, making debugging harder and potentially reporting the wrong error to the caller.

if (msgType == client_hello) {
        if (!ssl->options.certWithExternPsk)
            return 0;
        if (TLSX_Cert_With_Extern_Psk_Use(ssl) != 0)
            return MEMORY_E;

Recommendation: Propagate the actual error code: int ret = TLSX_Cert_With_Extern_Psk_Use(ssl); if (ret != 0) return ret;


This review was generated automatically by Fenrir. Findings are non-blocking.

@Frauschi Frauschi force-pushed the rfc8773bis-cert-with-extern-psk branch from 2ee0302 to 7a7ba81 Compare March 26, 2026 21:29
@Frauschi
Copy link
Copy Markdown
Contributor Author

@wolfSSL-Fenrir-bot Findings 1 and 2 identify a real bug,
but the recommended fix (continue instead of return) would be a spec
violation.

RFC 8773bis §5.1 (draft-ietf-tls-8773bis-13) is unambiguous:

▎ "All of the listed PSKs MUST be external PSKs. If a resumption PSK is
listed along with the tls_cert_with_extern_psk extension, the server
MUST abort the handshake with an illegal_parameter alert."

There is no "skip and continue" option — the presence of any resumption
PSK identity alongside tls_cert_with_extern_psk is a protocol
violation requiring a hard abort. Changing to continue would allow the
server to accept an external PSK identity from a list that also
contained a forbidden resumption PSK, violating the spec. The return
PSK_KEY_ERROR is correct because PSK_KEY_ERROR maps to
illegal_parameter in TranslateErrorToAlert (src/internal.c).

The actual bug was that the check fired too late: only after
DoClientTicketCheck succeeded. If a ticket successfully decrypted
(WOLFSSL_TICKET_RET_OK) but then failed DoClientTicketCheck (e.g.
cipher suite mismatch, expired), the early continue at the
check-failure path bypassed the certWithExternOffered guard entirely. A
client could therefore offer cert_with_extern_psk alongside a
resumption PSK identity that happened to fail the suite/expiry check,
and the server would proceed to evaluate subsequent external PSK
identities without aborting — a spec violation.

The fix moves the certWithExternOffered abort to fire immediately upon
successful ticket decryption, before DoClientTicketCheck and
DoClientTicketFinalize. This ensures any recognized session ticket
identity triggers the mandatory abort regardless of further validity
checks. The sess_free_cb cleanup is included in the early-exit path,
and the move also avoids DoClientTicketFinalize polluting ssl->session
with ticket state that will never be used.

Copy link
Copy Markdown

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #10085

Scan targets checked: src, src-bugs, src-compliance

No new issues found in the changed files. ✅

@Frauschi
Copy link
Copy Markdown
Contributor Author

Jenkins retest this please

Implement RFC8773bis (draft-ietf-tls-8773bis-13)
cert_with_extern_psk for TLS 1.3, including protocol checks
and API support.

Includes unit tests for API and handshake behavior as well
as tests in the testsuite using extended examples.
@Frauschi Frauschi force-pushed the rfc8773bis-cert-with-extern-psk branch from 7a7ba81 to 9976db0 Compare March 27, 2026 08:40
@Frauschi Frauschi added the Not For This Release Not for release 5.9.1 label Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Not For This Release Not for release 5.9.1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants