Skip to content

Conversation

@sm-msft
Copy link
Contributor

@sm-msft sm-msft commented Aug 20, 2025

Description

SChannel in Windows 2025 and newer releases allows

  • Server app to generate/manage resumption tickets that can include custom Server application state
  • Client app to manage the resumption ticket blobs it receives from the server

These features help implement custom 1-RTT resumption tickets on SChannel server applications and custom resumption ticket handling on SChannel clients.

This PR adds support for these features and integrates it into existing MsQuic code that utilizes similar features supported in OpenSSL and quictls libraries. Bulk of these changes are in tls_schannel.c wrapper.

Where available (and when not explicitly disabled), MsQuic has been using the automatic SChannel 1-RTT resumption handling thus far and it would remain the default mechanism in the library.

This change introduces a new QUIC_CREDENTIAL_FLAG for SChannel use

  • for a server app to opt into exclusively managing custom application state tickets/resumption ticket issuance
  • for a client app to opt into exclusively managing resumption ticket reception and use.

An additional QUIC_CREDENTIAL_FLAG allows an app to turn disable the use of 1-RTT/resumption in SChannel.
These flags are available only under the preview features macro.

Methods added to tls_schannel.c and other wrappers help enable the new functionality on Server2025 and newer releases and also to target 1-RTT tests on the appropriate platforms.

This change is linked to these issues: #5307 and #5162

Testing

Existing 1-RTT unit tests, BVTs have been enabled for SChannel on Server2025+ and new unit tests are added for additional coverage in SChannel/OpenSSL/quictls.

Existing automatic resumption tests cover the default scenario and older Windows OS scenarios.

Documentation

Documentation has been added to the newly introduced credential flags.

sm-msft added 21 commits July 18, 2025 01:01
…SChannel

- Code to push/retrieve client resumption ticket over SChannel
- Few corresponding test changes
…g explicit ticket management for SChannel

- Refactored tls_schannel code to rx/tx resumption tickets/ session state only when the credential flag is enabled
- If the app receiving ticket indicates error, it is logged but the TLS connection will proceed
- Added further checks and fixed up code in tls_schannel to ensure all TLS tests pass
- Refactored tls unit tests to include the new credential flag and enabled all unit tests for SChannel
- Other minor refactoring
- Added comments
- Fixed TLS resumption unit tests (ignored scnearios/bugs)
- Added many unit tests to cover the various resumption scenarios
- Minor refactoring
- Minor refactoring
- Update some traces and CLOG sidecar
…propriate init path

- Clear buffers/buffer length at initialization
- Clear buffer length after a ticket/app state is delivered
- Add new series of e2e tests for non-zero length app resumption states for the new Schannel scenarios
…o missing library support

- Unit tests that rely on empty tickets being indicated are moved back into OpenSSL-only category
@sm-msft sm-msft requested a review from a team as a code owner August 20, 2025 17:31
@sm-msft
Copy link
Contributor Author

sm-msft commented Aug 20, 2025

This draft PR was used to develop this change: #5311

@sm-msft sm-msft requested a review from anrossi August 20, 2025 17:35
@codecov
Copy link

codecov bot commented Aug 20, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.59%. Comparing base (591b7db) to head (93cd305).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5371      +/-   ##
==========================================
- Coverage   86.71%   85.59%   -1.12%     
==========================================
  Files          59       59              
  Lines       18328    18328              
==========================================
- Hits        15893    15688     -205     
- Misses       2435     2640     +205     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.


if (TLSExtensionAllocated && AppSessionStateAllocated && SessionTicketAllocated) {
break;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this check still needed when you have the check below

        if (!(Result & CXPLAT_TLS_RESULT_ERROR) &&
            (TLSExtensionAllocated || AppSessionStateAllocated || SessionTicketAllocated)) {
            break;
        }

// Inline 1-RTT app state validation is not supported on Windows
//
return FALSE;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

What does "Inline 1-RTT app state validation" mean?

Copy link
Collaborator

@guhetier guhetier left a comment

Choose a reason for hiding this comment

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

Review in progress


extern "C"
BOOLEAN
CxPlatSupportsTicketManagement();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Test should not use internal functions.
To skip tests, please use the pattern used below (QUIC_DISABLE_0RTT_TESTS) so it is controled as a test configuration.

Comment on lines +822 to +823
}
else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
}
else {
} else {

_In_ MsQuicConfiguration& ClientConfiguration,
_Out_ QUIC_BUFFER** ResumptionTicket
_Out_ QUIC_BUFFER** ResumptionTicket,
_In_ bool TrueResume
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think a better name is needed. TrueResume is very vague. I think it means "send a non-empty resume ticket"? A name capturing this would be better.

//
// Received application session state on the server, typically notified to the server app.
//
PSEC_APP_SESSION_STATE RxAppSessionState;
Copy link
Collaborator

@csujedihy csujedihy Aug 27, 2025

Choose a reason for hiding this comment

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

Can we have "client" and "server" in the name for these variables? It's a little difficult to follow when looking at how they are used.

Maybe we can match the bit flags defined above?

Copy link
Collaborator

@guhetier guhetier left a comment

Choose a reason for hiding this comment

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

Review in progress


#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES

QUIC_CREDENTIAL_FLAG_DISABLE_RESUMPTION = 0x00400000, // TODO: Combine this with CXPLAT_TLS_CREDENTIAL_FLAG_DISABLE_RESUMPTION
Copy link
Collaborator

Choose a reason for hiding this comment

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

TODO to be solved before merging.

memcpy(&ServerSelfSignedCredConfigSChannelResumption, SelfSignedCertParams, sizeof(QUIC_CREDENTIAL_CONFIG));
memcpy(&ServerSelfSignedCredConfigClientAuth, SelfSignedCertParams, sizeof(QUIC_CREDENTIAL_CONFIG));

#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
Copy link
Collaborator

Choose a reason for hiding this comment

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

Question: Do we actually need this ifdef?
As far as I know:

  • test code is always compiled with QUIC_API_ENABLE_PREVIEW_FEATURES
  • preview features implementation is always compiled in MsQuic, QUIC_API_ENABLE_PREVIEW_FEATURES only control the visibility of definitions in the header

These uses of QUIC_API_ENABLE_PREVIEW_FEATURES in test code might be a remain from the past and not have any effect.

}

#ifndef QUIC_API_ENABLE_PREVIEW_FEATURES
GTEST_SKIP_("Resumption ticket management is not supported in this build.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

That skip reason is misleading, the build always support preview features.

return CxPlatSupportsTicketManagement();
}

TEST_P(WithHandshakeArgs1, TrueResume) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please adapt the test name to describe what is tested here. If needed, feel free to change the Resume test name above to make it clear what the difference is.

struct PrimeResumption {
CxPlatEvent ShutdownEvent;
MsQuicConnection* Connection {nullptr};
bool TrueResume;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a C++ struct, please intialize this

//
// If the input length is zero, then we are initializing the client
// side, and have a few special differences in this code path.
// Call to encode ticket data has different ordering of inputs and different inputs as well.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand this comment. Different from what? It would be more helpfult to explain the ordering needs in for the different scenarios


if (ManagedResumptionTickets) {
//
// Explicit session management is in force for this context.
Copy link
Collaborator

Choose a reason for hiding this comment

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

"in force": is this a typo? Did you mean "enforced"?

}
#endif

if (AppSessionState != NULL) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

:O that function is so long, I had to scroll forever to find this cleanup.
We should break it down somehow (not for this PR)

!AreResumptionTicketsManaged(TlsContext->SecConfig->Flags)){
//
// Schannel currently sends the NST after receiving client finished.
// Older client behavior: Schannel sends the NST after receiving client finished.
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is "older" in this context?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This isn't client behavior. Sending NST after receiving client finished is a server behavior. Its still the case, I believe, because NSTs can only be sent from the server after handshake but now the server with active management can defer sending the ticket. This piece of code fakes a ticket received event for schannel.

//
// Client session state ticket is delivered only once.
//
TlsContext->RxSessionTicket->SessionTicketSize = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we free the memory too in this case?

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.

4 participants