Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Interface for App to Set Stateless Reset Key #3879

Merged
merged 18 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ These parameters are accessed by calling [GetParam](./api/GetParam.md) or [SetPa
| `QUIC_PARAM_GLOBAL_GLOBAL_SETTINGS`<br> 6 | QUIC_GLOBAL_SETTINGS | Both | Globally change global only settings. |
| `QUIC_PARAM_GLOBAL_VERSION_SETTINGS`<br> 7 | QUIC_VERSIONS_SETTINGS | Both | Globally change version settings for all subsequent connections. |
| `QUIC_PARAM_GLOBAL_LIBRARY_GIT_HASH`<br> 8 | char[64] | Get-only | Git hash used to build MsQuic (null terminated string) |
| `QUIC_PARAM_GLOBAL_EXECUTION_CONFIG`<br> 9 | QUIC_EXECUTION_CONFIG | Both | Globally configure the execution model used for QUIC. Must be set before opening registration. |
| `QUIC_PARAM_GLOBAL_EXECUTION_CONFIG`<br> 9 | QUIC_EXECUTION_CONFIG | Both | Globally configure the execution model used for QUIC. Must be set before opening registration. |
| `QUIC_PARAM_GLOBAL_TLS_PROVIDER`<br> 10 | QUIC_TLS_PROVIDER | Get-Only | The TLS provider being used by MsQuic for the TLS handshake. |
| `QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY`<br> 11 | uint8_t[] | Set-Only | Globally change the stateless reset key for all subsequent connections. |

## Registration Parameters

Expand Down
31 changes: 31 additions & 0 deletions src/core/library.c
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,37 @@ QuicLibrarySetGlobalParam(
Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY:
if (!MsQuicLib.LazyInitComplete) {
Status = QUIC_STATUS_INVALID_STATE;
break;
}
if (BufferLength != QUIC_STATELESS_RESET_KEY_LENGTH * sizeof(uint8_t)) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

Status = QUIC_STATUS_SUCCESS;
for (uint16_t i = 0; i < MsQuicLib.ProcessorCount; ++i) {
CXPLAT_HASH* TokenHash = NULL;
Status =
CxPlatHashCreate(
CXPLAT_HASH_SHA256,
(uint8_t*)Buffer,
QUIC_STATELESS_RESET_KEY_LENGTH * sizeof(uint8_t),
&TokenHash);
if (QUIC_FAILED(Status)) {
break;
}

QUIC_LIBRARY_PP* PerProc = &MsQuicLib.PerProc[i];
CxPlatLockAcquire(&PerProc->ResetTokenLock);
CxPlatHashFree(PerProc->ResetTokenHash);
PerProc->ResetTokenHash = TokenHash;
CxPlatLockRelease(&PerProc->ResetTokenLock);
}
break;

default:
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
Expand Down
6 changes: 6 additions & 0 deletions src/cs/lib/msquic_generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3077,6 +3077,9 @@ internal static unsafe partial class MsQuic
[NativeTypeName("#define QUIC_MAX_RESUMPTION_APP_DATA_LENGTH 1000")]
internal const uint QUIC_MAX_RESUMPTION_APP_DATA_LENGTH = 1000;

[NativeTypeName("#define QUIC_STATELESS_RESET_KEY_LENGTH 32")]
internal const uint QUIC_STATELESS_RESET_KEY_LENGTH = 32;

[NativeTypeName("#define QUIC_EXECUTION_CONFIG_MIN_SIZE (uint32_t)FIELD_OFFSET(QUIC_EXECUTION_CONFIG, ProcessorList)")]
internal static readonly uint QUIC_EXECUTION_CONFIG_MIN_SIZE = unchecked((uint)((int)(Marshal.OffsetOf<QUIC_EXECUTION_CONFIG>("ProcessorList"))));

Expand Down Expand Up @@ -3143,6 +3146,9 @@ internal static unsafe partial class MsQuic
[NativeTypeName("#define QUIC_PARAM_GLOBAL_TLS_PROVIDER 0x0100000A")]
internal const uint QUIC_PARAM_GLOBAL_TLS_PROVIDER = 0x0100000A;

[NativeTypeName("#define QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY 0x0100000B")]
internal const uint QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY = 0x0100000B;

[NativeTypeName("#define QUIC_PARAM_CONFIGURATION_SETTINGS 0x03000000")]
internal const uint QUIC_PARAM_CONFIGURATION_SETTINGS = 0x03000000;

Expand Down
7 changes: 6 additions & 1 deletion src/inc/msquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ typedef _In_range_(0, QUIC_UINT62_MAX) uint64_t QUIC_UINT62;
//
#define QUIC_MAX_RESUMPTION_APP_DATA_LENGTH 1000

//
// The number of bytes of stateless reset key.
//
#define QUIC_STATELESS_RESET_KEY_LENGTH 32

typedef enum QUIC_TLS_PROVIDER {
QUIC_TLS_PROVIDER_SCHANNEL = 0x0000,
QUIC_TLS_PROVIDER_OPENSSL = 0x0001,
Expand Down Expand Up @@ -832,7 +837,7 @@ void
#define QUIC_PARAM_GLOBAL_EXECUTION_CONFIG 0x01000009 // QUIC_EXECUTION_CONFIG
#endif
#define QUIC_PARAM_GLOBAL_TLS_PROVIDER 0x0100000A // QUIC_TLS_PROVIDER

#define QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY 0x0100000B // uint8_t[] - Array size is QUIC_STATELESS_RESET_KEY_LENGTH
//
// Parameters for Registration.
//
Expand Down
10 changes: 9 additions & 1 deletion src/test/MsQuicTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,11 @@ QuicTestClientDisconnect(
bool StopListenerFirst
);

void
QuicTestStatelessResetKey(
void
);

void
QuicTestKeyUpdate(
_In_ int Family,
Expand Down Expand Up @@ -1210,4 +1215,7 @@ typedef struct {
QUIC_CTL_CODE(112, METHOD_BUFFERED, FILE_WRITE_DATA)
// QUIC_RUN_FEATURE_NEGOTIATION

#define QUIC_MAX_IOCTL_FUNC_CODE 112
#define IOCTL_QUIC_RUN_STATELESS_RESET_KEY \
QUIC_CTL_CODE(113, METHOD_BUFFERED, FILE_WRITE_DATA)

#define QUIC_MAX_IOCTL_FUNC_CODE 113
9 changes: 9 additions & 0 deletions src/test/bin/quic_gtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,15 @@ TEST(Misc, ClientDisconnect) {
}
}

TEST(Misc, StatelessResetKey) {
TestLogger Logger("QuicTestStatelessResetKey");
if (TestingKernelMode) {
ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_STATELESS_RESET_KEY));
} else {
QuicTestStatelessResetKey();
}
}

TEST_P(WithKeyUpdateArgs1, KeyUpdate) {
TestLoggerT<ParamType> Logger("QuicTestKeyUpdate", GetParam());
if (TestingKernelMode) {
Expand Down
6 changes: 6 additions & 0 deletions src/test/bin/winkernel/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ size_t QUIC_IOCTL_BUFFER_SIZES[] =
sizeof(QUIC_RUN_CUSTOM_CERT_VALIDATION),
sizeof(QUIC_RUN_FEATURE_NEGOTIATION),
sizeof(QUIC_RUN_FEATURE_NEGOTIATION),
0,
};

CXPLAT_STATIC_ASSERT(
Expand Down Expand Up @@ -1362,6 +1363,11 @@ QuicTestCtlEvtIoDeviceControl(
Params->FeatureNegotiationParams.ClientSupport));
break;
#endif

case IOCTL_QUIC_RUN_STATELESS_RESET_KEY:
QuicTestCtlRun(QuicTestStatelessResetKey());
break;

default:
Status = STATUS_NOT_IMPLEMENTED;
break;
Expand Down
33 changes: 33 additions & 0 deletions src/test/lib/ApiTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2654,6 +2654,39 @@ void QuicTestGlobalParam()
}
#endif

//
// QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY
//
{
TestScopeLogger LogScope0("QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY");
{
TestScopeLogger LogScope1("SetParam");
uint8_t StatelessResetkey[QUIC_STATELESS_RESET_KEY_LENGTH - 1];
CxPlatRandom(sizeof(StatelessResetkey), StatelessResetkey);
{
TestScopeLogger LogScope2("StatelessResetkey fail with invalid state");
TEST_QUIC_STATUS(
QUIC_STATUS_INVALID_STATE,
MsQuic->SetParam(
nullptr,
QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY,
sizeof(StatelessResetkey),
StatelessResetkey));
}
{
TestScopeLogger LogScope2("StatelessResetkey fail with invalid parameter");
MsQuicRegistration Registration;
TEST_QUIC_STATUS(
QUIC_STATUS_INVALID_PARAMETER,
MsQuic->SetParam(
nullptr,
QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY,
sizeof(StatelessResetkey),
StatelessResetkey));
}
}
}

//
// Invalid parameter
//
Expand Down
102 changes: 102 additions & 0 deletions src/test/lib/DataTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,108 @@ QuicTestClientDisconnect(
}
}

void
QuicTestStatelessResetKey(
)
{
//
// By changing the stateless reset key, the stateless reset packets the client
// receives after the server side is shut down no longer match, eventually resulting
// in a timeout on the client instead of an abort.
//

PingStats ClientStats(UINT64_MAX - 1, 1, 1, TRUE, TRUE, FALSE, FALSE, TRUE, QUIC_STATUS_CONNECTION_TIMEOUT);

CxPlatEvent EventClientDeleted(true);

MsQuicRegistration Registration;
TEST_TRUE(Registration.IsValid());

MsQuicAlpn Alpn("MsQuicTest");

MsQuicSettings Settings;
Settings.SetIdleTimeoutMs(10000);
Settings.SetPeerUnidiStreamCount(1);

MsQuicConfiguration ServerConfiguration(Registration, Alpn, Settings, ServerSelfSignedCredConfig);
TEST_TRUE(ServerConfiguration.IsValid());

MsQuicCredentialConfig ClientCredConfig;
MsQuicConfiguration ClientConfiguration(Registration, Alpn, Settings, ClientCredConfig);
TEST_TRUE(ClientConfiguration.IsValid());

{
TestListener Listener(Registration, ListenerAcceptConnectionAndStreams, ServerConfiguration);
TEST_TRUE(Listener.IsValid());
TEST_QUIC_SUCCEEDED(Listener.Start(Alpn));

QuicAddr ServerLocalAddr;
TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr));

{
UniquePtr<TestConnection> Server;
ServerAcceptContext ServerAcceptCtx((TestConnection**)&Server);
Listener.Context = &ServerAcceptCtx;

TestConnection* Client =
NewPingConnection(
Registration,
&ClientStats,
false);
if (Client == nullptr) {
return;
}

Client->SetDeletedEvent(&EventClientDeleted.Handle);

Client->SetExpectedTransportCloseStatus(ClientStats.ExpectedCloseStatus);
TEST_QUIC_SUCCEEDED(Client->SetDisconnectTimeout(1000)); // ms

if (!SendPingBurst(
Client,
ClientStats.StreamCount,
ClientStats.PayloadLength)) {
return;
}

TEST_QUIC_SUCCEEDED(
Client->Start(
ClientConfiguration,
QUIC_ADDRESS_FAMILY_INET,
QUIC_TEST_LOOPBACK_FOR_AF(QUIC_ADDRESS_FAMILY_INET),
ServerLocalAddr.GetPort()));

if (!Client->WaitForConnectionComplete()) {
return;
}
TEST_TRUE(Client->GetIsConnected());

TEST_NOT_EQUAL(nullptr, Server);
if (!Server->WaitForConnectionComplete()) {
return;
}
TEST_TRUE(Server->GetIsConnected());

CxPlatSleep(15); // Sleep for just a bit.

uint8_t StatelessResetKey[QUIC_STATELESS_RESET_KEY_LENGTH];
CxPlatRandom(sizeof(StatelessResetKey), StatelessResetKey);
TEST_QUIC_SUCCEEDED(
MsQuic->SetParam(
nullptr,
QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY,
sizeof(StatelessResetKey),
StatelessResetKey));

Server->Shutdown(QUIC_CONNECTION_SHUTDOWN_FLAG_SILENT, 0);
}

if (!CxPlatEventWaitWithTimeout(EventClientDeleted.Handle, TestWaitTimeout)) {
TEST_FAILURE("Wait for EventClientDeleted timed out after %u ms.", TestWaitTimeout);
}
}
}

struct AbortiveTestContext {
AbortiveTestContext(
_In_ HQUIC ServerConfiguration,
Expand Down
12 changes: 12 additions & 0 deletions src/tools/spin/spinquic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,18 @@ CXPLAT_THREAD_CALLBACK(RunThread, Context)
}
}

if (0 == GetRandom(4)) {
uint8_t StatelessResetKey[QUIC_STATELESS_RESET_KEY_LENGTH];
CxPlatRandom(sizeof(StatelessResetKey), StatelessResetKey);
if (!QUIC_SUCCEEDED(MsQuic.SetParam(
nullptr,
QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY,
sizeof(StatelessResetKey),
StatelessResetKey))) {
break;
}
}

QUIC_REGISTRATION_CONFIG RegConfig;
RegConfig.AppName = "spinquic";
RegConfig.ExecutionProfile = FuzzData ? QUIC_EXECUTION_PROFILE_TYPE_SCAVENGER : (QUIC_EXECUTION_PROFILE)GetRandom(4);
Expand Down