Skip to content
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
22 changes: 22 additions & 0 deletions docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,27 @@ All restrictions and effects on the versions mentioned in [QUIC_VERSION_SETTINGS

Particularly, on server, these must be set **GLOBALLY** if you want them to take effect for servers.

The following settings are available via registry as well as via [QUIC_STATELESS_RETRY_CONFIG](./api/QUIC_STATELESS_RETRY_CONFIG.md):

| Setting | Type | Registry Name | Default | Description |
|---------|------|---------------|---------|-------------|
| Stateless Retry Key Rotation interval | uint32_t | RetryKeyRotationMs | 30000 | The interval stateless retry keys are rotated on. A token is valid for 2x this interval. |
| Stateless Retry Key Algorithm | uint32_t | RetryKeyAlgorithm | QUIC_AEAD_ALGORITHM_AES_256_GCM | The algorithm used to protect the stateless retry token. |
| Stateless Retry Key Secret | uint8_t[] | RetryKeySecret | Randomly Generated | The secret material used to generate the stateless retry keys. **MUST** be secure randomness! |

The `uint8_t[]` type is a `REG_BINARY` blob of the secret material, and must be the same length (in bytes) as the algorithm's key.

These settings only take effect in the global registry location.

When changing the stateless retry configuration via registry, admins **MUST** delete the existing RetryKeyRotationMs, RetryKeyAlgorithm, and RetryKeySecret registry values (if present) before writing the new values. This prevents a split state from occurring while applying settings.

For consistency when configuring Stateless Retry via the registry, values **MUST** be written in the following order:
1. RetryKeyRotationMs
2. RetryKeyAlgorithm
3. RetryKeySecret

See [QUIC_STATELESS_RETRY_CONFIG](./api/QUIC_STATELESS_RETRY_CONFIG.md) for more information.

## QUIC_SETTINGS

A [QUIC_SETTINGS](./api/QUIC_SETTINGS.md) struct is used to configure settings on a `Configuration` handle, `Connection` handle, or globally.
Expand Down Expand Up @@ -120,6 +141,7 @@ These parameters are accessed by calling [GetParam](./api/GetParam.md) or [SetPa
| `QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY`<br> 11 | uint8_t[] | Set-Only | Globally change the stateless reset key for all subsequent connections. |
| `QUIC_PARAM_GLOBAL_STATISTICS_V2_SIZES`<br> 12 | uint32_t[] | Get-only | Array of well-known sizes for each version of the QUIC_STATISTICS_V2 struct. The output array length is variable; pass a buffer of uint32_t and check BufferLength for the number of sizes returned. See GetParam documentation for usage details. |
| `QUIC_PARAM_GLOBAL_VERSION_NEGOTIATION_ENABLED`<br> (preview) | uint8_t (BOOLEAN) | Both | Globally enable the version negotiation extension for all client and server connections. |
| `QUIC_PARAM_GLOBAL_STATELESS_RETRY_CONFIG`<br> 13 | [QUIC_STATELESS_RETRY_CONFIG](./api/QUIC_STATELESS_RETRY_CONFIG.md) | Set-Only | Configure the stateless retry token secret, key algorithm, and key rotation interval. The secret length *must* match the AEAD algorithm key length. |

## Registration Parameters

Expand Down
50 changes: 50 additions & 0 deletions docs/api/QUIC_STATELESS_RETRY_CONFIG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
QUIC_STATELESS_RETRY_CONFIG structure
======

The structure used to configure the stateless retry feature.

# Syntax

```C
typedef struct QUIC_STATELESS_RETRY_CONFIG {
QUIC_AEAD_ALGORITHM_TYPE Algorithm;
uint32_t RotationMs;
uint32_t SecretLength;
_Field_size_bytes_(SecretLength)
const uint8_t* Secret;
} QUIC_STATELESS_RETRY_CONFIG;
```

# Members

`Algorithm`

The AEAD algorithm used for protecting the retry token. Must be one of the following constants:

Constant | Key Length (bytes)
---------|------------
**QUIC_AEAD_ALGORITHM_AES_128_GCM**<br> 0 | 16
**QUIC_AEAD_ALGORITHM_AES_256_GCM**<br>1<br> *The default* | 32

`RotationMs`

The interval to rotate the retry key. 30,000ms is the default. A token is valid for twice this interval. Zero is not allowed.

`SecretLength`

The length in bytes pointed to by Secret. Must match the key length of the chosen `Algorithm`.

`Secret`

A non-NULL pointer to a buffer containing `SecretLength` bytes of randomness. Used to generate the keys protecting the retry token.

# Remarks

`RotationMs` should be kept to a short interval, less than a minute, as retry tokens are returned immediately by clients.
Changing `RotationMs`, `Algorithm`, or `Secret` will invalidate all retry tokens issued prior to the change.
All servers deployed in a cluster and sharing the secret must have their clocks synchronized within `RotationMs` of UTC.
A server whose clock is ahead of UTC may produce a retry token that other servers in that deployment are unable to validate.

# See Also

[Settings](../Settings.md)<br>
198 changes: 195 additions & 3 deletions src/core/library.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,21 @@

uint8_t ResetHashKey[20];
CxPlatRandom(sizeof(ResetHashKey), ResetHashKey);
CxPlatRandom(sizeof(MsQuicLib.BaseRetrySecret), MsQuicLib.BaseRetrySecret);

uint8_t RetrySecret[CXPLAT_AEAD_AES_256_GCM_SIZE];
CxPlatRandom(sizeof(RetrySecret), RetrySecret);

QUIC_STATELESS_RETRY_CONFIG RetryConfig;
RetryConfig.SecretLength = sizeof(RetrySecret);
RetryConfig.Secret = RetrySecret;
RetryConfig.RotationMs = QUIC_STATELESS_RETRY_KEY_LIFETIME_MS;
RetryConfig.Algorithm = QUIC_AEAD_ALGORITHM_AES_256_GCM;

QUIC_STATUS Status = QuicLibrarySetRetryKeyConfig(&RetryConfig);
CXPLAT_FRE_ASSERT(QUIC_SUCCEEDED(Status));
CxPlatSecureZeroMemory(RetrySecret, sizeof(RetrySecret));

uint16_t i;
QUIC_STATUS Status;
for (i = 0; i < MsQuicLib.PartitionCount; ++i) {
Status =
QuicPartitionInitialize(
Expand Down Expand Up @@ -334,6 +345,81 @@
sizeof(PerfCounterSamples));
}

_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicLibraryLoadRetryConfig(
_In_ CXPLAT_STORAGE* Storage
)
{
QUIC_STATELESS_RETRY_CONFIG RetryConfig = { 0 };
uint8_t Secret[CXPLAT_AEAD_MAX_SIZE] = { 0 };
uint32_t SecretLength = sizeof(Secret);
uint32_t KeyRotationMs;
uint32_t RotationLength = sizeof(KeyRotationMs);
uint32_t KeyAlgorithm;
uint32_t AlgLength = sizeof(KeyAlgorithm);
BOOLEAN SettingChanged = FALSE;
BOOLEAN SecretChanged = FALSE;
BOOLEAN AlgorithmChanged = FALSE;

//
// Initialize RetryConfig with current settings. Secret is set to a
// sentinel value that won't get copied when only RotationMs changes.
//
CxPlatDispatchRwLockAcquireShared(&MsQuicLib.StatelessRetry.Lock, PrevIrql);
RetryConfig.Algorithm = (QUIC_AEAD_ALGORITHM_TYPE)MsQuicLib.StatelessRetry.AeadAlgorithm;
RetryConfig.RotationMs = MsQuicLib.StatelessRetry.KeyRotationMs;
RetryConfig.SecretLength = MsQuicLib.StatelessRetry.SecretLength;
RetryConfig.Secret = MsQuicLib.StatelessRetry.BaseSecret;
CxPlatDispatchRwLockReleaseShared(&MsQuicLib.StatelessRetry.Lock, PrevIrql);

if (QUIC_SUCCEEDED(
CxPlatStorageReadValue(
Storage,
QUIC_SETTING_RETRY_KEY_ROTATION_MS,
(uint8_t*)&KeyRotationMs,
&RotationLength))) {
RetryConfig.RotationMs = KeyRotationMs;
SettingChanged = TRUE;
}

if (QUIC_SUCCEEDED(
CxPlatStorageReadValue(
Storage,
QUIC_SETTING_RETRY_KEY_ALGORITHM,
(uint8_t*)&KeyAlgorithm,
&AlgLength))) {
AlgorithmChanged = TRUE;
}

if (QUIC_SUCCEEDED(
CxPlatStorageReadValue(
Storage,
QUIC_SETTING_RETRY_KEY_SECRET,
Secret,
&SecretLength))) {
SecretChanged = TRUE;
}

if (SecretChanged && AlgorithmChanged) {
//
// Both secret and algorithm must be present in the registry for
// either to take effect.
// We expect admins to delete the existing values when changing
// algorithm or secret, to prevent split state. See Settings.md.
//
RetryConfig.Algorithm = KeyAlgorithm;
RetryConfig.Secret = Secret;
RetryConfig.SecretLength = SecretLength;
SettingChanged = TRUE;
}

if (SettingChanged) {
QuicLibrarySetRetryKeyConfig(&RetryConfig);
}
CxPlatSecureZeroMemory(&Secret, sizeof(Secret));
}

_IRQL_requires_max_(PASSIVE_LEVEL)
void
MsQuicLibraryOnSettingsChanged(
Expand Down Expand Up @@ -377,6 +463,7 @@
QuicSettingsSetDefault(&MsQuicLib.Settings);
if (MsQuicLib.Storage != NULL) {
QuicSettingsLoad(&MsQuicLib.Settings, MsQuicLib.Storage);
QuicLibraryLoadRetryConfig(MsQuicLib.Storage);
}

QuicTraceLogInfo(
Expand All @@ -401,7 +488,6 @@
if (QUIC_FAILED(Status)) {
goto Error; // Cannot log anything if platform failed to initialize.
}
PlatformInitialized = TRUE;

CXPLAT_DBG_ASSERT(US_TO_MS(CxPlatGetTimerResolution()) + 1 <= UINT8_MAX);
MsQuicLib.TimerResolutionMs = (uint8_t)US_TO_MS(CxPlatGetTimerResolution()) + 1;
Expand All @@ -413,6 +499,9 @@
MsQuicLib.ToeplitzHash.InputSize = CXPLAT_TOEPLITZ_INPUT_SIZE_QUIC;
CxPlatToeplitzHashInitialize(&MsQuicLib.ToeplitzHash);

CxPlatDispatchRwLockInitialize(&MsQuicLib.StatelessRetry.Lock);
PlatformInitialized = TRUE;

CxPlatZeroMemory(&MsQuicLib.Settings, sizeof(MsQuicLib.Settings));
Status =
CxPlatStorageOpen(
Expand Down Expand Up @@ -499,6 +588,7 @@
MsQuicLib.DefaultCompatibilityList = NULL;
}
if (PlatformInitialized) {
CxPlatDispatchRwLockUninitialize(&MsQuicLib.StatelessRetry.Lock);
CxPlatUninitialize();
}
}
Expand Down Expand Up @@ -600,6 +690,8 @@
CXPLAT_FREE(MsQuicLib.DefaultCompatibilityList, QUIC_POOL_DEFAULT_COMPAT_VER_LIST);
MsQuicLib.DefaultCompatibilityList = NULL;

CxPlatDispatchRwLockUninitialize(&MsQuicLib.StatelessRetry.Lock);

if (MsQuicLib.ExecutionConfig != NULL) {
CXPLAT_FREE(MsQuicLib.ExecutionConfig, QUIC_POOL_EXECUTION_CONFIG);
MsQuicLib.ExecutionConfig = NULL;
Expand Down Expand Up @@ -1206,6 +1298,16 @@
}
break;

case QUIC_PARAM_GLOBAL_STATELESS_RETRY_CONFIG: {
if (Buffer == NULL || BufferLength < sizeof(QUIC_STATELESS_RETRY_CONFIG)) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}
const QUIC_STATELESS_RETRY_CONFIG* Config = (const QUIC_STATELESS_RETRY_CONFIG*)Buffer;
Status = QuicLibrarySetRetryKeyConfig(Config);
break;
}

default:
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
Expand All @@ -1228,303 +1330,339 @@

CXPLAT_DBG_ASSERT(MsQuicLib.Loaded);

switch (Param) {
case QUIC_PARAM_GLOBAL_RETRY_MEMORY_PERCENT:

if (*BufferLength < sizeof(MsQuicLib.Settings.RetryMemoryLimit)) {
*BufferLength = sizeof(MsQuicLib.Settings.RetryMemoryLimit);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = sizeof(MsQuicLib.Settings.RetryMemoryLimit);
*(uint16_t*)Buffer = MsQuicLib.Settings.RetryMemoryLimit;

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_SUPPORTED_VERSIONS:

if (*BufferLength < sizeof(QuicSupportedVersionList)) {
*BufferLength = sizeof(QuicSupportedVersionList);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = sizeof(QuicSupportedVersionList);
CxPlatCopyMemory(
Buffer,
QuicSupportedVersionList,
sizeof(QuicSupportedVersionList));

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_LOAD_BALACING_MODE:

if (*BufferLength < sizeof(uint16_t)) {
*BufferLength = sizeof(uint16_t);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = sizeof(uint16_t);
*(uint16_t*)Buffer = MsQuicLib.Settings.LoadBalancingMode;

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_PERF_COUNTERS: {

if (*BufferLength < sizeof(int64_t)) {
*BufferLength = sizeof(int64_t) * QUIC_PERF_COUNTER_MAX;
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

if (*BufferLength < QUIC_PERF_COUNTER_MAX * sizeof(int64_t)) {
//
// Copy as many counters will fit completely in the buffer.
//
*BufferLength = (*BufferLength / sizeof(int64_t)) * sizeof(int64_t);
} else {
*BufferLength = QUIC_PERF_COUNTER_MAX * sizeof(int64_t);
}

QuicLibrarySumPerfCounters(Buffer, *BufferLength);

Status = QUIC_STATUS_SUCCESS;
break;
}

case QUIC_PARAM_GLOBAL_SETTINGS:

Status = QuicSettingsGetSettings(&MsQuicLib.Settings, BufferLength, (QUIC_SETTINGS*)Buffer);
break;

case QUIC_PARAM_GLOBAL_VERSION_SETTINGS:

Status = QuicSettingsGetVersionSettings(&MsQuicLib.Settings, BufferLength, (QUIC_VERSION_SETTINGS*)Buffer);
break;

case QUIC_PARAM_GLOBAL_GLOBAL_SETTINGS:

Status = QuicSettingsGetGlobalSettings(&MsQuicLib.Settings, BufferLength, (QUIC_GLOBAL_SETTINGS*)Buffer);
break;

case QUIC_PARAM_GLOBAL_LIBRARY_VERSION:

if (*BufferLength < sizeof(MsQuicLib.Version)) {
*BufferLength = sizeof(MsQuicLib.Version);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = sizeof(MsQuicLib.Version);
CxPlatCopyMemory(Buffer, MsQuicLib.Version, sizeof(MsQuicLib.Version));

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_LIBRARY_GIT_HASH:

GitHashLength = (uint32_t)strlen(MsQuicLib.GitHash) + 1;

if (*BufferLength < GitHashLength) {
*BufferLength = GitHashLength;
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = GitHashLength;
CxPlatCopyMemory(Buffer, MsQuicLib.GitHash, GitHashLength);

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_EXECUTION_CONFIG: {
if (MsQuicLib.ExecutionConfig == NULL) {
*BufferLength = 0;
Status = QUIC_STATUS_SUCCESS;
break;
}

const uint32_t ConfigLength =
QUIC_GLOBAL_EXECUTION_CONFIG_MIN_SIZE +
sizeof(uint16_t) * MsQuicLib.ExecutionConfig->ProcessorCount;

if (*BufferLength < ConfigLength) {
*BufferLength = ConfigLength;
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = ConfigLength;
CxPlatCopyMemory(Buffer, MsQuicLib.ExecutionConfig, ConfigLength);
Status = QUIC_STATUS_SUCCESS;
break;
}

case QUIC_PARAM_GLOBAL_TLS_PROVIDER:

if (*BufferLength < sizeof(QUIC_TLS_PROVIDER)) {
*BufferLength = sizeof(QUIC_TLS_PROVIDER);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = sizeof(QUIC_TLS_PROVIDER);
*(QUIC_TLS_PROVIDER*)Buffer = CxPlatTlsGetProvider();

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_DATAPATH_FEATURES: {
if (*BufferLength < sizeof(uint32_t)) {
*BufferLength = sizeof(uint32_t);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

if (MsQuicLib.Datapath == NULL) {
Status = QUIC_STATUS_INVALID_STATE;
break;
}

*BufferLength = sizeof(uint32_t);
*(uint32_t*)Buffer = QuicLibraryGetDatapathFeatures();

Status = QUIC_STATUS_SUCCESS;
break;
}

case QUIC_PARAM_GLOBAL_VERSION_NEGOTIATION_ENABLED:

if (*BufferLength < sizeof(BOOLEAN)) {
*BufferLength = sizeof(BOOLEAN);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = sizeof(BOOLEAN);
*(BOOLEAN*)Buffer = MsQuicLib.Settings.VersionNegotiationExtEnabled;

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_IN_USE:

if (*BufferLength < sizeof(BOOLEAN)) {
*BufferLength = sizeof(BOOLEAN);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*BufferLength = sizeof(BOOLEAN);
*(BOOLEAN*)Buffer = MsQuicLib.InUse;

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_PLATFORM_WORKER_POOL:

if (*BufferLength != sizeof(CXPLAT_WORKER_POOL*)) {
*BufferLength = sizeof(CXPLAT_WORKER_POOL*);
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

*(CXPLAT_WORKER_POOL**)Buffer = MsQuicLib.WorkerPool;

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_GLOBAL_STATISTICS_V2_SIZES: {
static const uint32_t StatSizes[] = {
QUIC_STATISTICS_V2_SIZE_1,
QUIC_STATISTICS_V2_SIZE_2,
QUIC_STATISTICS_V2_SIZE_3,
QUIC_STATISTICS_V2_SIZE_4
};
static const uint32_t NumStatSizes = ARRAYSIZE(StatSizes);
uint32_t MaxSizes = *BufferLength / sizeof(uint32_t);
if (MaxSizes == 0) {
*BufferLength = NumStatSizes * sizeof(uint32_t); // Indicate the max size.
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
break;
}
if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}
const uint32_t ToCopy =
CXPLAT_MIN(MaxSizes, NumStatSizes) * sizeof(uint32_t);
CxPlatCopyMemory(Buffer, StatSizes, ToCopy);
*BufferLength = ToCopy;
Status = QUIC_STATUS_SUCCESS;
break;
}

case QUIC_PARAM_GLOBAL_STATELESS_RETRY_CONFIG: {
#ifdef DEBUG
//
// Only available in DEBUG builds for testing purposes.
// The application shouldn't need to read its own secret after setting.
//
CxPlatDispatchRwLockAcquireShared(&MsQuicLib.StatelessRetry.Lock, PrevIrql);
if (*BufferLength < sizeof(QUIC_STATELESS_RETRY_CONFIG) + MsQuicLib.StatelessRetry.SecretLength) {
*BufferLength = sizeof(QUIC_STATELESS_RETRY_CONFIG) + MsQuicLib.StatelessRetry.SecretLength;
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
CxPlatDispatchRwLockReleaseShared(&MsQuicLib.StatelessRetry.Lock, PrevIrql);
break;
}
if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
CxPlatDispatchRwLockReleaseShared(&MsQuicLib.StatelessRetry.Lock, PrevIrql);
break;

Check warning on line 1646 in src/core/library.c

View check run for this annotation

Codecov / codecov/patch

src/core/library.c#L1644-L1646

Added lines #L1644 - L1646 were not covered by tests
}
QUIC_STATELESS_RETRY_CONFIG* Config = (QUIC_STATELESS_RETRY_CONFIG*)Buffer;
Config->Algorithm = (QUIC_AEAD_ALGORITHM_TYPE)MsQuicLib.StatelessRetry.AeadAlgorithm;
Config->RotationMs = MsQuicLib.StatelessRetry.KeyRotationMs;
Config->SecretLength = MsQuicLib.StatelessRetry.SecretLength;
Config->Secret = (uint8_t*)(Config + 1);
CxPlatCopyMemory(
(uint8_t*)Config->Secret,
MsQuicLib.StatelessRetry.BaseSecret,
MsQuicLib.StatelessRetry.SecretLength);
CxPlatDispatchRwLockReleaseShared(&MsQuicLib.StatelessRetry.Lock, PrevIrql);
Status = QUIC_STATUS_SUCCESS;
break;
#else
Status = QUIC_STATUS_NOT_SUPPORTED;
break;
#endif // DEBUG
}

default:
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
Expand Down Expand Up @@ -2583,3 +2721,57 @@
}

#endif

_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
QuicLibrarySetRetryKeyConfig(
_In_ const QUIC_STATELESS_RETRY_CONFIG* Config
)
{
if (Config->Secret == NULL) {
QuicTraceLogError(
LibrarySetRetryKeySecretNull,
"[ lib] Invalid retry key secret: NULL.");
return QUIC_STATUS_INVALID_PARAMETER;
}
if (Config->Algorithm > QUIC_AEAD_ALGORITHM_AES_256_GCM ||
Config->Algorithm < QUIC_AEAD_ALGORITHM_AES_128_GCM) {
QuicTraceLogError(
LibrarySetRetryKeyAlgorithmInvalid,
"[ lib] Invalid retry key algorithm: %d.",
Config->Algorithm);
return QUIC_STATUS_INVALID_PARAMETER;
}
if (Config->RotationMs == 0) {
QuicTraceLogError(
LibrarySetRetryKeyRotationInvalid,
"[ lib] Invalid retry key rotation ms: %u.",
Config->RotationMs);
return QUIC_STATUS_INVALID_PARAMETER;
}
uint16_t AlgSecretLen = CxPlatKeyLength((CXPLAT_AEAD_TYPE)Config->Algorithm);
if (Config->SecretLength != AlgSecretLen) {
QuicTraceLogError(
LibrarySetRetryKeySecretLengthInvalid,
"[ lib] Invalid retry key secret length: %u. Expected %u.",
Config->SecretLength,
AlgSecretLen);
return QUIC_STATUS_INVALID_PARAMETER;
}
CXPLAT_DBG_ASSERT(AlgSecretLen <= sizeof(MsQuicLib.StatelessRetry.BaseSecret));

CxPlatDispatchRwLockAcquireExclusive(&MsQuicLib.StatelessRetry.Lock, PrevIrql);
if (Config->Secret != MsQuicLib.StatelessRetry.BaseSecret) {
CxPlatCopyMemory(MsQuicLib.StatelessRetry.BaseSecret, Config->Secret, AlgSecretLen);
MsQuicLib.StatelessRetry.SecretLength = AlgSecretLen;
}
MsQuicLib.StatelessRetry.AeadAlgorithm = (CXPLAT_AEAD_TYPE)Config->Algorithm;
MsQuicLib.StatelessRetry.KeyRotationMs = Config->RotationMs;
CxPlatDispatchRwLockReleaseExclusive(&MsQuicLib.StatelessRetry.Lock, PrevIrql);
QuicTraceLogInfo(
LibraryRetryKeyUpdated,
"[ lib] Stateless Retry Key updated. Algorithm: %d, RotationMs: %u",
Config->Algorithm,
Config->RotationMs);
return QUIC_STATUS_SUCCESS;
}
Loading
Loading