-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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
BrennanConroy
merged 41 commits into
dotnet:main
from
DeagleGross:dmkorolev/dataprotection/lin-perf-2
Mar 18, 2025
Merged
Changes from 6 commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
e47b41d
a bit of improvement
DeagleGross bb6d3c0
another buffer.blockCopy removal
DeagleGross 384c3d5
use `DecryptCbc` instead?
DeagleGross 90bf754
tests and remove another array
DeagleGross a37b0d5
fix the slicing of IV
DeagleGross 268ee44
slice correctly!
DeagleGross eb6cb1b
try with encrypt
DeagleGross d090882
finish encrypt
DeagleGross 6ec76d3
use same overload in decrypt()
DeagleGross 49eab48
dont allocate for prf-output as well
DeagleGross 4ba9cc2
address PR comments
DeagleGross 919d003
remove spaces
DeagleGross 87d118b
prfInput and prfOutput rented
DeagleGross 2524a15
correctHash as rented
DeagleGross ec70b6e
more details
DeagleGross 0d30a5b
use static `TryHashData` for specific hash implementation
DeagleGross bab196a
prettify
DeagleGross e96ad0c
dont rent (only stackalloc or allocate new byte array) + address othe…
DeagleGross cfbf7a5
Merge branch 'main' into dmkorolev/dataprotection/lin-perf-2
DeagleGross c9aa2e3
address PR comment
DeagleGross 560c79e
use ReadOnlySpan only in CryptoUtils
DeagleGross 8e3df1a
move to "manual" section
DeagleGross 4434822
reduce to single implementation ManagedSP800_108_CTR_HMACSHA512.Deriv…
DeagleGross aed8e9d
adjust a comment
DeagleGross a846056
change decrypt to single implementation
DeagleGross e72f716
single impl for encrypt()
DeagleGross cf48cc8
no pool at all
DeagleGross 0f3d1aa
remove leftover
DeagleGross 51bfe77
oops stackoverflow
DeagleGross ab25e0f
merge main
DeagleGross 6f035d4
correct merge
DeagleGross 22224dc
merge main
DeagleGross c8019fb
no-alloc setKey for algorithm
DeagleGross d87aeaf
raise stackalloc to 256
DeagleGross 489c5cd
Revert "raise stackalloc to 256"
DeagleGross 26ee6ad
address ManagedSP800_108_CTR_HMACSHA512
DeagleGross 9ce2b17
Merge branch 'main' into dmkorolev/dataprotection/lin-perf-2
DeagleGross f50c7fb
merga main
DeagleGross f45a948
address PR comments
DeagleGross 66b2170
merge main
DeagleGross 66cd115
remove test + address nits
DeagleGross File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -137,13 +137,18 @@ private byte[] CreateContextHeader() | |
return retVal; | ||
} | ||
|
||
private SymmetricAlgorithm CreateSymmetricAlgorithm() | ||
private SymmetricAlgorithm CreateSymmetricAlgorithm(byte[]? key = null) | ||
{ | ||
var retVal = _symmetricAlgorithmFactory(); | ||
CryptoUtil.Assert(retVal != null, "retVal != null"); | ||
|
||
retVal.Mode = CipherMode.CBC; | ||
retVal.Padding = PaddingMode.PKCS7; | ||
if (key is not null) | ||
{ | ||
retVal.Key = key; | ||
} | ||
|
||
return retVal; | ||
} | ||
|
||
|
@@ -161,6 +166,8 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad | |
protectedPayload.Validate(); | ||
additionalAuthenticatedData.Validate(); | ||
|
||
var protectedPayloadSpan = protectedPayload.AsSpan(); | ||
DeagleGross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
// Argument checking - input must at the absolute minimum contain a key modifier, IV, and MAC | ||
if (protectedPayload.Count < checked(KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + _validationAlgorithmDigestLengthInBytes)) | ||
{ | ||
|
@@ -172,7 +179,6 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad | |
try | ||
{ | ||
// Step 1: Extract the key modifier and IV from the payload. | ||
|
||
int keyModifierOffset; // position in protectedPayload.Array where key modifier begins | ||
int ivOffset; // position in protectedPayload.Array where key modifier ends / IV begins | ||
int ciphertextOffset; // position in protectedPayload.Array where IV ends / ciphertext begins | ||
|
@@ -187,8 +193,6 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad | |
} | ||
|
||
ArraySegment<byte> keyModifier = new ArraySegment<byte>(protectedPayload.Array!, keyModifierOffset, ivOffset - keyModifierOffset); | ||
var iv = new byte[_symmetricAlgorithmBlockSizeInBytes]; | ||
DeagleGross marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Buffer.BlockCopy(protectedPayload.Array!, ivOffset, iv, 0, iv.Length); | ||
|
||
// Step 2: Decrypt the KDK and use it to restore the original encryption and MAC keys. | ||
// We pin all unencrypted keys to limit their exposure via GC relocation. | ||
|
@@ -206,16 +210,16 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad | |
try | ||
{ | ||
_keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment<byte>(decryptedKdk)); | ||
ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader( | ||
DeagleGross marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ManagedSP800_108_CTR_HMACSHA512.DeriveKeys( | ||
kdk: decryptedKdk, | ||
label: additionalAuthenticatedData, | ||
contextHeader: _contextHeader, | ||
context: keyModifier, | ||
contextData: keyModifier, | ||
prfFactory: _kdkPrfFactory, | ||
output: new ArraySegment<byte>(derivedKeysBuffer)); | ||
|
||
Buffer.BlockCopy(derivedKeysBuffer, 0, decryptionSubkey, 0, decryptionSubkey.Length); | ||
DeagleGross marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Buffer.BlockCopy(derivedKeysBuffer, decryptionSubkey.Length, validationSubkey, 0, validationSubkey.Length); | ||
derivedKeysBuffer.AsSpan().Slice(start: 0, length: decryptionSubkey.Length).CopyTo(decryptionSubkey); | ||
derivedKeysBuffer.AsSpan().Slice(start: decryptionSubkey.Length, length: validationSubkey.Length).CopyTo(validationSubkey); | ||
|
||
// Step 3: Calculate the correct MAC for this payload. | ||
// correctHash := MAC(IV || ciphertext) | ||
|
@@ -233,27 +237,33 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad | |
} | ||
|
||
// Step 4: Validate the MAC provided as part of the payload. | ||
|
||
if (!CryptoUtil.TimeConstantBuffersAreEqual(correctHash, 0, correctHash.Length, protectedPayload.Array!, macOffset, eofOffset - macOffset)) | ||
{ | ||
throw Error.CryptCommon_PayloadInvalid(); // integrity check failure | ||
} | ||
|
||
// Step 5: Decipher the ciphertext and return it to the caller. | ||
#if NET10_0_OR_GREATER | ||
DeagleGross marked this conversation as resolved.
Show resolved
Hide resolved
|
||
using var symmetricAlgorithm = CreateSymmetricAlgorithm(key: decryptionSubkey); | ||
|
||
using (var symmetricAlgorithm = CreateSymmetricAlgorithm()) | ||
using (var cryptoTransform = symmetricAlgorithm.CreateDecryptor(decryptionSubkey, iv)) | ||
{ | ||
var outputStream = new MemoryStream(); | ||
using (var cryptoStream = new CryptoStream(outputStream, cryptoTransform, CryptoStreamMode.Write)) | ||
{ | ||
cryptoStream.Write(protectedPayload.Array!, ciphertextOffset, macOffset - ciphertextOffset); | ||
cryptoStream.FlushFinalBlock(); | ||
// note: here protectedPayload.Array is taken without an offset (cant use AsSpan() on ArraySegment) | ||
var ciphertext = protectedPayload.Array.AsSpan().Slice(ciphertextOffset, macOffset - ciphertextOffset); | ||
DeagleGross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
var iv = protectedPayload.Array.AsSpan().Slice(ivOffset, _symmetricAlgorithmBlockSizeInBytes); | ||
|
||
// At this point, outputStream := { plaintext }, and we're done! | ||
return outputStream.ToArray(); | ||
} | ||
return symmetricAlgorithm.DecryptCbc(ciphertext, iv); | ||
DeagleGross marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#else | ||
var iv = new byte[_symmetricAlgorithmBlockSizeInBytes]; | ||
Buffer.BlockCopy(protectedPayload.Array!, ivOffset, iv, 0, iv.Length); | ||
DeagleGross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
using var symmetricAlgorithm = CreateSymmetricAlgorithm(); | ||
using (var cryptoTransform = symmetricAlgorithm.CreateDecryptor(decryptionSubkey, iv)) | ||
{ | ||
var length = macOffset - ciphertextOffset; | ||
var result = new byte[length]; | ||
_ = cryptoTransform.TransformBlock(protectedPayload.Array!, ciphertextOffset, length, result, 0); | ||
|
||
return result; | ||
} | ||
#endif | ||
} | ||
finally | ||
{ | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.