Skip to content

Commit

Permalink
Expose Interface for App to Set Stateless Reset Key (#3879)
Browse files Browse the repository at this point in the history
* Allow stateless reset token key to be set #1719

* Update src/core/library.c

How about we use the same way as the random initial reset key, we call  MsQuicLibraryFreePartitions() to set all PerProc->ResetTokenHash to NULL

Co-authored-by: Nick Banks <[email protected]>

* Address the code review feedback

* Revert clog.sidecar changes

* Add event for stateless reset key

* Revert the file clog.sidecar

* Remove DatapathTcpAuxBinding changes from clog.sidecar

* Remove parameter from function QuicTestStatelessResetKey and address other comments

* Update src/test/MsQuicTests.h

Co-authored-by: Nick Banks <[email protected]>

* Update src/core/library.c

Co-authored-by: Nick Banks <[email protected]>

* Update src/test/lib/DataTest.cpp

Co-authored-by: Nick Banks <[email protected]>

* Address code review comments

* Move stateless reset key after the other global param

* Address CheckDotnet test and update document

* Address the failure at CIFuzz

* Address the failure in CIFuzz

* Add one test case when MsQuic lib has not been initialized yet

---------

Co-authored-by: Nick Banks <[email protected]>
  • Loading branch information
ukingliu and nibanks authored Sep 28, 2023
1 parent 0950840 commit 649f971
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 3 deletions.
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 @@ -418,6 +418,11 @@ QuicTestClientDisconnect(
bool StopListenerFirst
);

void
QuicTestStatelessResetKey(
void
);

void
QuicTestKeyUpdate(
_In_ int Family,
Expand Down Expand Up @@ -1211,4 +1216,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 @@ -1896,6 +1896,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 @@ -1363,6 +1364,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

0 comments on commit 649f971

Please sign in to comment.