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

Add a key management simulator for data protection #55348

Merged
merged 3 commits into from May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions AspNetCore.sln
Expand Up @@ -1799,10 +1799,13 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MicroBenchmarks", "MicroBenchmarks", "{6469F11E-8CEE-4292-820B-324DFFC88EBC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.MicroBenchmarks", "src\Caching\perf\MicroBenchmarks\Microsoft.Extensions.Caching.MicroBenchmarks\Microsoft.Extensions.Caching.MicroBenchmarks.csproj", "{8D2CC6ED-5105-4F52-8757-C21F4DE78589}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{9DC6B242-457B-4767-A84B-C3D23B76C642}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OpenApi.Microbenchmarks", "src\OpenApi\perf\Microbenchmarks\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj", "{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeyManagementSimulator", "src\DataProtection\samples\KeyManagementSimulator\KeyManagementSimulator.csproj", "{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -10884,6 +10887,22 @@ Global
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|x64.Build.0 = Release|Any CPU
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|x86.ActiveCfg = Release|Any CPU
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|x86.Build.0 = Release|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Debug|arm64.ActiveCfg = Debug|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Debug|arm64.Build.0 = Debug|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Debug|x64.ActiveCfg = Debug|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Debug|x64.Build.0 = Debug|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Debug|x86.ActiveCfg = Debug|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Debug|x86.Build.0 = Debug|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Release|Any CPU.Build.0 = Release|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Release|arm64.ActiveCfg = Release|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Release|arm64.Build.0 = Release|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Release|x64.ActiveCfg = Release|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Release|x64.Build.0 = Release|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Release|x86.ActiveCfg = Release|Any CPU
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -11775,6 +11794,7 @@ Global
{8D2CC6ED-5105-4F52-8757-C21F4DE78589} = {6469F11E-8CEE-4292-820B-324DFFC88EBC}
{9DC6B242-457B-4767-A84B-C3D23B76C642} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B}
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734} = {9DC6B242-457B-4767-A84B-C3D23B76C642}
{5B5F86CC-3598-463C-9F9B-F78FBB6642F4} = {8275510E-0E6C-45A8-99DF-4F106BC7F075}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
Expand Down
1 change: 1 addition & 0 deletions src/DataProtection/DataProtection.slnf
Expand Up @@ -19,6 +19,7 @@
"src\\DataProtection\\samples\\CustomEncryptorSample\\CustomEncryptorSample.csproj",
"src\\DataProtection\\samples\\EntityFrameworkCoreSample\\EntityFrameworkCoreSample.csproj",
"src\\DataProtection\\samples\\KeyManagementSample\\KeyManagementSample.csproj",
"src\\DataProtection\\samples\\KeyManagementSimulator\\KeyManagementSimulator.csproj",
"src\\DataProtection\\samples\\NonDISample\\NonDISample.csproj",
"src\\DataProtection\\samples\\Redis\\Redis.csproj",
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
Expand Down
Expand Up @@ -47,6 +47,12 @@ public KeyRing(IKey defaultKey, IEnumerable<IKey> allKeys)

public Guid DefaultKeyId { get; }

// For testing
internal IReadOnlyCollection<Guid> GetAllKeyIds()
{
return _keyIdToKeyHolderMap.Keys;
}

public IAuthenticatedEncryptor? GetAuthenticatedEncryptorByKeyId(Guid keyId, out bool isRevoked)
{
isRevoked = false;
Expand Down
Expand Up @@ -59,8 +59,18 @@ internal sealed class KeyRingProvider : ICacheableKeyRingProvider, IKeyRingProvi
AutoRefreshWindowEnd = DateTime.UtcNow.AddMinutes(2);

AppContext.TryGetSwitch(DisableAsyncKeyRingUpdateSwitchKey, out _disableAsyncKeyRingUpdate);

// We use the Random class since we don't need a secure PRNG for this.
#if NET6_0_OR_GREATER
JitterRandom = Random.Shared;
#else
JitterRandom = new Random();
#endif
}

// Internal for testing
internal Random JitterRandom { get; set; }

// for testing
internal ICacheableKeyRingProvider CacheableKeyRingProvider { get; set; }

Expand Down Expand Up @@ -470,18 +480,12 @@ private IKeyRing GetCurrentKeyRingCoreNew(DateTime utcNow, bool forceRefresh)
}
}

private static TimeSpan GetRefreshPeriodWithJitter(TimeSpan refreshPeriod)
private TimeSpan GetRefreshPeriodWithJitter(TimeSpan refreshPeriod)
{
// We'll fudge the refresh period up to -20% so that multiple applications don't try to
// hit a single repository simultaneously. For instance, if the refresh period is 1 hour,
// we'll return a value in the vicinity of 48 - 60 minutes. We use the Random class since
// we don't need a secure PRNG for this.
#if NET6_0_OR_GREATER
var random = Random.Shared;
#else
var random = new Random();
#endif
return TimeSpan.FromTicks((long)(refreshPeriod.Ticks * (1.0d - (random.NextDouble() / 5))));
// we'll return a value in the vicinity of 48 - 60 minutes.
return TimeSpan.FromTicks((long)(refreshPeriod.Ticks * (1.0d - (JitterRandom.NextDouble() / 5))));
}

private static DateTimeOffset Min(DateTimeOffset a, DateTimeOffset b)
Expand Down
Expand Up @@ -137,12 +137,16 @@ public XmlKeyManager(IOptions<KeyManagementOptions> keyManagementOptions, IActiv

internal IXmlRepository KeyRepository { get; }

// Internal for testing
// Can't use TimeProvider since it's not available in framework
BrennanConroy marked this conversation as resolved.
Show resolved Hide resolved
internal Func<DateTimeOffset> GetUtcNow { get; set; } = () => DateTimeOffset.UtcNow;

/// <inheritdoc />
public IKey CreateNewKey(DateTimeOffset activationDate, DateTimeOffset expirationDate)
{
// For an immediately-activated key, the caller's Now may be slightly before ours,
// so we'll compensate to ensure that activation is never before creation.
var now = DateTimeOffset.UtcNow;
var now = GetUtcNow();
return _internalKeyManager.CreateNewKey(
keyId: Guid.NewGuid(),
creationDate: activationDate < now ? activationDate : now,
Expand Down Expand Up @@ -377,7 +381,7 @@ public void RevokeKey(Guid keyId, string? reason = null)
{
_internalKeyManager.RevokeSingleKey(
keyId: keyId,
revocationDate: DateTimeOffset.UtcNow,
revocationDate: GetUtcNow(),
reason: reason);
}

Expand Down
Expand Up @@ -52,5 +52,6 @@
<InternalsVisibleTo Include="Microsoft.AspNetCore.DataProtection.Extensions.Tests" />
<InternalsVisibleTo Include="Microsoft.AspNetCore.DataProtection.Tests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" Key="$(MoqPublicKey)" />
<InternalsVisibleTo Include="KeyManagementSimulator" />
</ItemGroup>
</Project>
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<OutputType>exe</OutputType>
</PropertyGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.DataProtection" />
<Reference Include="Microsoft.AspNetCore.DataProtection.Extensions" />
</ItemGroup>

</Project>