88using System . Security . Cryptography ;
99using Microsoft . AspNetCore . Cryptography ;
1010using Microsoft . AspNetCore . DataProtection . AuthenticatedEncryption ;
11+ using Microsoft . AspNetCore . DataProtection . Internal ;
1112using Microsoft . AspNetCore . DataProtection . SP800_108 ;
1213
1314namespace Microsoft . AspNetCore . DataProtection . Managed ;
@@ -195,8 +196,10 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad
195196 ReadOnlySpan < byte > keyModifier = protectedPayload . Array ! . AsSpan ( keyModifierOffset , ivOffset - keyModifierOffset ) ;
196197
197198 // Step 2: Decrypt the KDK and use it to restore the original encryption and MAC keys.
198- // We pin all unencrypted keys to limit their exposure via GC relocation.
199199
200+ // The best optimization is to stackalloc. If the size is too big, we would want to rent from the pool,
201+ // but we can't due to the HashAlgorithm, ValidationAlgorithm and SymmetricAlgorithm requiring a byte[] instead of a Span<byte>
202+ // in the constructor / Key property.
200203 var decryptedKdk = new byte [ _keyDerivationKey . Length ] ;
201204 var decryptionSubkey = new byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ;
202205 var validationSubkey = new byte [ _validationAlgorithmSubkeyLengthInBytes ] ;
@@ -219,16 +222,44 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad
219222
220223 // Step 3: Calculate the correct MAC for this payload.
221224 // correctHash := MAC(IV || ciphertext)
222- byte [ ] correctHash ;
225+ checked
226+ {
227+ eofOffset = protectedPayload . Offset + protectedPayload . Count ;
228+ macOffset = eofOffset - _validationAlgorithmDigestLengthInBytes ;
229+ }
230+ #if NET10_0_OR_GREATER
231+ using var hashAlgorithm = CreateValidationAlgorithm ( validationSubkey ) ;
232+ var hashSize = hashAlgorithm . GetDigestSizeInBytes ( ) ;
233+
234+ byte [ ] ? correctHashLease = null ;
235+ Span < byte > correctHash = hashSize <= 128
236+ ? stackalloc byte [ hashSize ]
237+ : ( correctHashLease = DataProtectionPool . Rent ( hashSize ) ) . AsSpan ( 0 , hashSize ) ;
238+ try
239+ {
240+ var hashSource = protectedPayload . Array ! . AsSpan ( ivOffset , macOffset - ivOffset ) ;
241+ hashAlgorithm . TryComputeHash ( hashSource , correctHash , out _ ) ;
223242
224- using ( var hashAlgorithm = CreateValidationAlgorithm ( validationSubkey ) )
243+ // Step 4: Validate the MAC provided as part of the payload.
244+ var payloadMacPart = protectedPayload . Array ! . AsSpan ( macOffset , eofOffset - macOffset ) ;
245+ if ( ! CryptoUtil . TimeConstantBuffersAreEqual ( correctHash , payloadMacPart ) )
246+ {
247+ throw Error . CryptCommon_PayloadInvalid ( ) ; // integrity check failure
248+ }
249+ }
250+ finally
225251 {
226- checked
252+ if ( correctHashLease is not null )
227253 {
228- eofOffset = protectedPayload . Offset + protectedPayload . Count ;
229- macOffset = eofOffset - _validationAlgorithmDigestLengthInBytes ;
254+ // it was not cleaned in previous implementation (var correctHash = new byte[])
255+ DataProtectionPool . Return ( correctHashLease , clearArray : false ) ;
230256 }
257+ }
258+ #else
259+ byte [ ] correctHash ;
231260
261+ using ( var hashAlgorithm = CreateValidationAlgorithm ( validationSubkey ) )
262+ {
232263 correctHash = hashAlgorithm . ComputeHash ( protectedPayload . Array ! , ivOffset , macOffset - ivOffset ) ;
233264 }
234265
@@ -237,12 +268,13 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad
237268 {
238269 throw Error . CryptCommon_PayloadInvalid ( ) ; // integrity check failure
239270 }
271+ #endif
240272
241273 // Step 5: Decipher the ciphertext and return it to the caller.
242274#if NET10_0_OR_GREATER
243275 using var symmetricAlgorithm = CreateSymmetricAlgorithm ( key : decryptionSubkey ) ;
244276
245- // note: here protectedPayload.Array is taken without an offset (cant use AsSpan() on ArraySegment)
277+ // note: here protectedPayload.Array is taken without an offset (can't use AsSpan() on ArraySegment)
246278 var ciphertext = protectedPayload . Array . AsSpan ( ciphertextOffset , macOffset - ciphertextOffset ) ;
247279 var iv = protectedPayload . Array . AsSpan ( ivOffset , _symmetricAlgorithmBlockSizeInBytes ) ;
248280
0 commit comments