Skip to content

perf: improve ManagedAuthenticatedEncryptor Decrypt() and Encrypt() flow #59424

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

Merged
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e47b41d
a bit of improvement
DeagleGross Dec 9, 2024
bb6d3c0
another buffer.blockCopy removal
DeagleGross Dec 10, 2024
384c3d5
use `DecryptCbc` instead?
DeagleGross Dec 10, 2024
90bf754
tests and remove another array
DeagleGross Dec 10, 2024
a37b0d5
fix the slicing of IV
DeagleGross Dec 11, 2024
268ee44
slice correctly!
DeagleGross Dec 12, 2024
eb6cb1b
try with encrypt
DeagleGross Dec 16, 2024
d090882
finish encrypt
DeagleGross Dec 17, 2024
6ec76d3
use same overload in decrypt()
DeagleGross Dec 17, 2024
49eab48
dont allocate for prf-output as well
DeagleGross Dec 17, 2024
4ba9cc2
address PR comments
DeagleGross Dec 17, 2024
919d003
remove spaces
DeagleGross Dec 17, 2024
87d118b
prfInput and prfOutput rented
DeagleGross Dec 18, 2024
2524a15
correctHash as rented
DeagleGross Dec 18, 2024
ec70b6e
more details
DeagleGross Dec 18, 2024
0d30a5b
use static `TryHashData` for specific hash implementation
DeagleGross Dec 18, 2024
bab196a
prettify
DeagleGross Dec 19, 2024
e96ad0c
dont rent (only stackalloc or allocate new byte array) + address othe…
DeagleGross Jan 7, 2025
cfbf7a5
Merge branch 'main' into dmkorolev/dataprotection/lin-perf-2
DeagleGross Jan 22, 2025
c9aa2e3
address PR comment
DeagleGross Jan 24, 2025
560c79e
use ReadOnlySpan only in CryptoUtils
DeagleGross Jan 26, 2025
8e3df1a
move to "manual" section
DeagleGross Jan 26, 2025
4434822
reduce to single implementation ManagedSP800_108_CTR_HMACSHA512.Deriv…
DeagleGross Jan 27, 2025
aed8e9d
adjust a comment
DeagleGross Jan 27, 2025
a846056
change decrypt to single implementation
DeagleGross Jan 27, 2025
e72f716
single impl for encrypt()
DeagleGross Jan 27, 2025
cf48cc8
no pool at all
DeagleGross Jan 27, 2025
0f3d1aa
remove leftover
DeagleGross Jan 27, 2025
51bfe77
oops stackoverflow
DeagleGross Jan 28, 2025
ab25e0f
merge main
DeagleGross Jan 28, 2025
6f035d4
correct merge
DeagleGross Jan 28, 2025
22224dc
merge main
DeagleGross Mar 7, 2025
c8019fb
no-alloc setKey for algorithm
DeagleGross Mar 10, 2025
d87aeaf
raise stackalloc to 256
DeagleGross Mar 10, 2025
489c5cd
Revert "raise stackalloc to 256"
DeagleGross Mar 10, 2025
26ee6ad
address ManagedSP800_108_CTR_HMACSHA512
DeagleGross Mar 10, 2025
9ce2b17
Merge branch 'main' into dmkorolev/dataprotection/lin-perf-2
DeagleGross Mar 17, 2025
f50c7fb
merga main
DeagleGross Mar 18, 2025
f45a948
address PR comments
DeagleGross Mar 18, 2025
66b2170
merge main
DeagleGross Mar 18, 2025
66cd115
remove test + address nits
DeagleGross Mar 18, 2025
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
1 change: 1 addition & 0 deletions eng/Dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ and are generated based on the last package release.
<LatestPackageReference Include="System.DirectoryServices.Protocols" />
<LatestPackageReference Include="System.IdentityModel.Tokens.Jwt" />
<LatestPackageReference Include="System.IO.Pipelines" />
<LatestPackageReference Include="System.Memory" />
<LatestPackageReference Include="System.Net.Http" />
<LatestPackageReference Include="System.Net.Http.Json" />
<LatestPackageReference Include="System.Net.Sockets" />
Expand Down
4 changes: 4 additions & 0 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>c3d95b4305e17ebe8dbda8efde3144a1c693499c</Sha>
</Dependency>
<Dependency Name="System.Memory" Version="4.6.0">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>000000</Sha>
</Dependency>
<Dependency Name="System.Net.Http.Json" Version="10.0.0-preview.3.25163.6">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>c3d95b4305e17ebe8dbda8efde3144a1c693499c</Sha>
Expand Down
1 change: 1 addition & 0 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@
<SystemCommandlineExperimentalVersion>0.3.0-alpha.19317.1</SystemCommandlineExperimentalVersion>
<SystemComponentModelVersion>4.3.0</SystemComponentModelVersion>
<SystemNetHttpVersion>4.3.4</SystemNetHttpVersion>
<SystemMemoryVersion>4.6.0</SystemMemoryVersion>
<SystemNetSocketsVersion>4.3.0</SystemNetSocketsVersion>
<SystemSecurityCryptographyX509CertificatesVersion>4.3.2</SystemSecurityCryptographyX509CertificatesVersion>
<SystemRuntimeInteropServicesRuntimeInformationVersion>4.3.0</SystemRuntimeInteropServicesRuntimeInformationVersion>
Expand Down
18 changes: 6 additions & 12 deletions src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,24 +92,18 @@ public static bool TimeConstantBuffersAreEqual(byte* bufA, byte* bufB, uint coun
}

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static bool TimeConstantBuffersAreEqual(byte[] bufA, int offsetA, int countA, byte[] bufB, int offsetB, int countB)
public static bool TimeConstantBuffersAreEqual(ReadOnlySpan<byte> bufA, ReadOnlySpan<byte> bufB)
{
// Technically this is an early exit scenario, but it means that the caller did something bizarre.
// An error at the call site isn't usable for timing attacks.
Assert(countA == countB, "countA == countB");
// This early exit handles unexpected input without introducing timing vulnerabilities.
Assert(bufA.Length == bufB.Length, "countA == countB");

#if NETCOREAPP
unsafe
{
return CryptographicOperations.FixedTimeEquals(
bufA.AsSpan(start: offsetA, length: countA),
bufB.AsSpan(start: offsetB, length: countB));
}
return CryptographicOperations.FixedTimeEquals(bufA, bufB);
#else
bool areEqual = true;
for (int i = 0; i < countA; i++)
for (int i = 0; i < bufA.Length; i++)
{
areEqual &= (bufA[offsetA + i] == bufB[offsetB + i]);
areEqual &= (bufA[i] == bufB[i]);
}
return areEqual;
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@
<InternalsVisibleTo Include="Microsoft.AspNetCore.DataProtection.Extensions.Tests" />
<InternalsVisibleTo Include="Microsoft.AspNetCore.DataProtection.Tests" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != '$(DefaultNetCoreTargetFramework)'">
<Reference Include="System.Memory" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Xunit;

namespace Microsoft.AspNetCore.Cryptography;
Expand All @@ -15,7 +16,7 @@ public void TimeConstantBuffersAreEqual_Array_Equal()
byte[] b = new byte[] { 0xAB, 0xCD, 0x23, 0x45, 0x67, 0xEF };

// Act & assert
Assert.True(CryptoUtil.TimeConstantBuffersAreEqual(a, 1, 3, b, 2, 3));
Assert.True(CryptoUtil.TimeConstantBuffersAreEqual(a.AsSpan(1, 3), b.AsSpan(2, 3)));
}

[Fact]
Expand All @@ -25,7 +26,7 @@ public void TimeConstantBuffersAreEqual_Array_Unequal()
byte[] b = new byte[] { 0xAB, 0xCD, 0x23, 0xFF, 0x67, 0xEF };

// Act & assert
Assert.False(CryptoUtil.TimeConstantBuffersAreEqual(a, 1, 3, b, 2, 3));
Assert.False(CryptoUtil.TimeConstantBuffersAreEqual(a.AsSpan(1, 3), b.AsSpan(2, 3)));
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ internal sealed unsafe class AesGcmAuthenticatedEncryptor : IOptimizedAuthentica
// 256 00-01-00-00-00-20-00-00-00-0C-00-00-00-10-00-00-00-10-E7-DC-CE-66-DF-85-5A-32-3A-6B-B7-BD-7A-59-BE-45
private static readonly byte[] AES_256_GCM_Header = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0xE7, 0xDC, 0xCE, 0x66, 0xDF, 0x85, 0x5A, 0x32, 0x3A, 0x6B, 0xB7, 0xBD, 0x7A, 0x59, 0xBE, 0x45 };

private static readonly Func<byte[], HashAlgorithm> _kdkPrfFactory = key => new HMACSHA512(key); // currently hardcoded to SHA512

private readonly byte[] _contextHeader;

private readonly Secret _keyDerivationKey;
Expand Down Expand Up @@ -112,13 +110,13 @@ public byte[] Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte> addition
try
{
_keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment<byte>(decryptedKdk));
ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader(
ManagedSP800_108_CTR_HMACSHA512.DeriveKeys(
kdk: decryptedKdk,
label: additionalAuthenticatedData,
contextHeader: _contextHeader,
context: keyModifier,
prfFactory: _kdkPrfFactory,
output: new ArraySegment<byte>(derivedKey));
contextData: keyModifier,
operationSubkey: derivedKey,
validationSubkey: Span<byte>.Empty /* filling in derivedKey only */ );

// Perform the decryption operation
var nonce = new Span<byte>(ciphertext.Array, nonceOffset, NONCE_SIZE_IN_BYTES);
Expand Down Expand Up @@ -185,13 +183,13 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
try
{
_keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment<byte>(decryptedKdk));
ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader(
ManagedSP800_108_CTR_HMACSHA512.DeriveKeys(
kdk: decryptedKdk,
label: additionalAuthenticatedData,
contextHeader: _contextHeader,
context: keyModifier,
prfFactory: _kdkPrfFactory,
output: new ArraySegment<byte>(derivedKey));
contextData: keyModifier,
operationSubkey: derivedKey,
validationSubkey: Span<byte>.Empty /* filling in derivedKey only */ );

// do gcm
var nonce = new Span<byte>(retVal, nonceOffset, NONCE_SIZE_IN_BYTES);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.AspNetCore.DataProtection.Managed;

internal interface IManagedGenRandom
{
byte[] GenRandom(int numBytes);

#if NET10_0_OR_GREATER
void GenRandom(Span<byte> target);
#endif
}
Loading
Loading