@@ -200,17 +200,34 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad
200200            // The best optimization is to stackalloc. If the size is too big, we would want to rent from the pool, 
201201            // but we can't due to the HashAlgorithm, ValidationAlgorithm and SymmetricAlgorithm requiring a byte[] instead of a Span<byte> 
202202            // in the constructor / Key property. 
203+ #if NET10_0_OR_GREATER 
204+             byte [ ] ?  decryptedKdkLease  =  null ; 
205+             Span < byte >  decryptedKdk  =  _keyDerivationKey . Length  <=  128 
206+                 ?  stackalloc  byte [ _keyDerivationKey . Length ] 
207+                 :  ( decryptedKdkLease  =  DataProtectionPool . Rent ( _keyDerivationKey . Length ) ) . AsSpan ( 0 ,  _keyDerivationKey . Length ) ; 
208+ #else
203209            var  decryptedKdk  =  new  byte [ _keyDerivationKey . Length ] ; 
210+ #endif
211+ 
204212            var  decryptionSubkey  =  new  byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ; 
205213            var  validationSubkey  =  new  byte [ _validationAlgorithmSubkeyLengthInBytes ] ; 
206214
207-             fixed ( byte *  __unused__1  =  decryptedKdk ) 
215+             fixed ( byte *  decryptedKdkUnsafe  =  decryptedKdk ) 
208216            fixed ( byte *  __unused__2  =  decryptionSubkey ) 
209217            fixed ( byte *  __unused__3  =  validationSubkey ) 
210218            { 
211219                try 
212220                { 
213-                     _keyDerivationKey . WriteSecretIntoBuffer ( new  ArraySegment < byte > ( decryptedKdk ) ) ; 
221+                     _keyDerivationKey . WriteSecretIntoBuffer ( decryptedKdkUnsafe ,  decryptedKdk . Length ) ; 
222+ #if NET10_0_OR_GREATER 
223+                     ManagedSP800_108_CTR_HMACSHA512 . DeriveKeysHMACSHA512 ( 
224+                         kdk :  decryptedKdk , 
225+                         label :  additionalAuthenticatedData , 
226+                         contextHeader :  _contextHeader , 
227+                         contextData :  keyModifier , 
228+                         operationSubKey :  decryptionSubkey , 
229+                         validationSubKey :  validationSubkey ) ; 
230+ #else
214231                    ManagedSP800_108_CTR_HMACSHA512 . DeriveKeys ( 
215232                        kdk :  decryptedKdk , 
216233                        label :  additionalAuthenticatedData , 
@@ -219,6 +236,7 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad
219236                        prfFactory :  _kdkPrfFactory , 
220237                        operationSubKey :  decryptionSubkey , 
221238                        validationSubKey :  validationSubkey ) ; 
239+ #endif
222240
223241                    // Step 3: Calculate the correct MAC for this payload. 
224242                    // correctHash := MAC(IV || ciphertext) 
@@ -296,7 +314,19 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad
296314                finally 
297315                { 
298316                    // delete since these contain secret material 
317+ #if NET10_0_OR_GREATER 
318+                     if  ( decryptedKdkLease  is  not null ) 
319+                     { 
320+                         DataProtectionPool . Return ( decryptedKdkLease ,  clearArray :  true ) ; 
321+                     } 
322+                     else 
323+                     { 
324+                         decryptedKdk . Clear ( ) ; 
325+                     } 
326+ #else
299327                    Array . Clear ( decryptedKdk ,  0 ,  decryptedKdk . Length ) ; 
328+ #endif
329+ 
300330                    Array . Clear ( decryptionSubkey ,  0 ,  decryptionSubkey . Length ) ; 
301331                    Array . Clear ( validationSubkey ,  0 ,  validationSubkey . Length ) ; 
302332                } 
@@ -340,65 +370,83 @@ private byte[] EncryptImpl(
340370
341371        // Step 1: Decrypt the KDK, and use it to generate new encryption and HMAC keys. 
342372        // We pin all unencrypted keys to limit their exposure via GC relocation. 
343-         var  decryptedKdk  =  new  byte [ _keyDerivationKey . Length ] ; 
373+         byte [ ] ?  decryptedKdkLease  =  null ; 
374+         Span < byte >  decryptedKdk  =  _keyDerivationKey . Length  <=  128 
375+             ?  stackalloc  byte [ _keyDerivationKey . Length ] 
376+             :  ( decryptedKdkLease  =  DataProtectionPool . Rent ( _keyDerivationKey . Length ) ) . AsSpan ( 0 ,  _keyDerivationKey . Length ) ; 
377+ 
344378        var  encryptionSubkey  =  new  byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ; 
345379        var  validationSubkey  =  new  byte [ _validationAlgorithmSubkeyLengthInBytes ] ; 
346380
347-         fixed ( byte *  __unused__1  =  decryptedKdk ) 
381+         fixed ( byte *  decryptedKdkUnsafe  =  decryptedKdk ) 
348382        fixed ( byte *  __unused__2  =  encryptionSubkey ) 
349383        fixed ( byte *  __unused__3  =  validationSubkey ) 
350384        { 
351-             var  keyModifier  =  ArrayPool < byte > . Shared . Rent ( keyModifierLength ) ; 
352-             _genRandom . GenRandom ( keyModifier ) ; 
385+             var  keyModifier  =  DataProtectionPool . Rent ( keyModifierLength ) ; 
353386
354-             _keyDerivationKey . WriteSecretIntoBuffer ( new  ArraySegment < byte > ( decryptedKdk ) ) ; 
355-             ManagedSP800_108_CTR_HMACSHA512 . DeriveKeys ( 
356-                 kdk :  decryptedKdk , 
357-                 label :  additionalAuthenticatedData , 
358-                 contextHeader :  _contextHeader , 
359-                 contextData :  keyModifier , 
360-                 prfFactory :  _kdkPrfFactory , 
361-                 operationSubKey :  encryptionSubkey , 
362-                 validationSubKey :  validationSubkey ) ; 
387+             try 
388+             { 
389+                 _genRandom . GenRandom ( keyModifier ) ; 
363390
364-             // idea of optimization here is firstly get all the types preset 
365-             // for calculating length of the output array and allocating it. 
366-             // then we are filling it with the data directly, without any additional copying 
367-             using  var  symmetricAlgorithm  =  CreateSymmetricAlgorithm ( key :  encryptionSubkey ) ; 
368-             using  var  validationAlgorithm  =  CreateValidationAlgorithm ( key :  validationSubkey ) ; 
391+                 _keyDerivationKey . WriteSecretIntoBuffer ( decryptedKdkUnsafe ,  decryptedKdk . Length ) ; 
392+                 ManagedSP800_108_CTR_HMACSHA512 . DeriveKeysHMACSHA512 ( 
393+                     kdk :  decryptedKdk , 
394+                     label :  additionalAuthenticatedData , 
395+                     contextHeader :  _contextHeader , 
396+                     contextData :  keyModifier , 
397+                     operationSubKey :  encryptionSubkey , 
398+                     validationSubKey :  validationSubkey ) ; 
369399
370-             var  cipherTextLength  =  symmetricAlgorithm . GetCiphertextLengthCbc ( plainText . Length ) ;  // CBC because symmetricAlgorithm is created with CBC mode 
371-             var  macLength  =  _validationAlgorithmDigestLengthInBytes ; 
400+                 // idea of optimization here is firstly get all the types preset 
401+                 // for calculating length of the output array and allocating it. 
402+                 // then we are filling it with the data directly, without any additional copying 
403+                 using  var  symmetricAlgorithm  =  CreateSymmetricAlgorithm ( key :  encryptionSubkey ) ; 
404+                 using  var  validationAlgorithm  =  CreateValidationAlgorithm ( key :  validationSubkey ) ; 
372405
373-             // allocating an array of a specific required length 
374-             var  outputArray  =  new  byte [ keyModifierLength  +  ivLength  +  cipherTextLength  +  macLength ] ; 
375-             var  outputSpan  =  outputArray . AsSpan ( ) ; 
406+                 var  cipherTextLength  =  symmetricAlgorithm . GetCiphertextLengthCbc ( plainText . Length ) ;  // CBC because symmetricAlgorithm is created with CBC mode 
407+                 var  macLength  =  _validationAlgorithmDigestLengthInBytes ; 
376408
377-             // Step 2: Copy the key modifier and the IV to the output stream since they'll act as a header. 
378-             keyModifier . CopyTo ( outputSpan . Slice ( start :  0 ,  length :  keyModifierLength ) ) ; 
409+                 // allocating an array of a specific required length 
410+                 var  outputArray  =  new  byte [ keyModifierLength  +  ivLength  +  cipherTextLength  +  macLength ] ; 
411+                 var  outputSpan  =  outputArray . AsSpan ( ) ; 
379412
380-             // Step 3: Generate IV for this operation right into the result array (no allocation) 
381-             _genRandom . GenRandom ( outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength ) ) ; 
382-             var  iv  =  outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength ) ; 
413+                 // Step 2: Copy the key modifier and the IV to the output stream since they'll act as a header. 
414+                 keyModifier . CopyTo ( outputSpan . Slice ( start :  0 ,  length :  keyModifierLength ) ) ; 
383415
384-             // encrypting plaintext into the target array directly 
385-             symmetricAlgorithm . EncryptCbc ( plainText ,  iv ,  outputSpan . Slice ( start :  keyModifierLength  +  ivLength ,  length :  cipherTextLength ) ) ; 
416+                 // Step 3: Generate IV for this operation right into the result array (no allocation) 
417+                 _genRandom . GenRandom ( outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength ) ) ; 
418+                 var  iv  =  outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength ) ; 
386419
387-             // At this point, outputStream := { keyModifier || IV || ciphertext } 
420+                 // encrypting plaintext into the target array directly 
421+                 symmetricAlgorithm . EncryptCbc ( plainText ,  iv ,  outputSpan . Slice ( start :  keyModifierLength  +  ivLength ,  length :  cipherTextLength ) ) ; 
388422
389-             // Step 4: Calculate the digest over the IV and ciphertext. 
390-             // We don't need to calculate the digest over the key modifier since that 
391-             // value has already been mixed into the KDF used to generate the MAC key. 
423+                 // At this point, outputStream := { keyModifier || IV || ciphertext } 
392424
393-             var  ivAndCipherTextSpan  =  outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength  +  cipherTextLength ) ; 
394-             var  macDestinationSpan  =  outputSpan . Slice ( keyModifierLength  +  ivLength  +  cipherTextLength ,  macLength ) ; 
395-             validationAlgorithm . TryComputeHash ( source :  ivAndCipherTextSpan ,  destination :  macDestinationSpan ,  bytesWritten :  out  _ ) ; 
396-             // At this point, outputArray := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) } 
425+                 // Step 4: Calculate the digest over the IV and ciphertext. 
426+                 // We don't need to calculate the digest over the key modifier since that 
427+                 // value has already been mixed into the KDF used to generate the MAC key. 
397428
398-             // returning whatever was pooled back with clear (secret data to be cleaned) 
399-             ArrayPool < byte > . Shared . Return ( keyModifier ,  clearArray :  true ) ; 
429+                 var  ivAndCipherTextSpan  =  outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength  +  cipherTextLength ) ; 
430+                 var  macDestinationSpan  =  outputSpan . Slice ( keyModifierLength  +  ivLength  +  cipherTextLength ,  macLength ) ; 
431+                 validationAlgorithm . TryComputeHash ( source :  ivAndCipherTextSpan ,  destination :  macDestinationSpan ,  bytesWritten :  out  _ ) ; 
432+                 // At this point, outputArray := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) } 
400433
401-             return  outputArray ; 
434+                 return  outputArray ; 
435+             } 
436+             finally 
437+             { 
438+                 // returning whatever was pooled back with clear (secret data to be cleaned) 
439+                 DataProtectionPool . Return ( keyModifier ,  clearArray :  true ) ; 
440+ 
441+                 if  ( decryptedKdkLease  is  not null ) 
442+                 { 
443+                     DataProtectionPool . Return ( keyModifier ,  clearArray :  true ) ; 
444+                 } 
445+                 else 
446+                 { 
447+                     decryptedKdk . Clear ( ) ; 
448+                 } 
449+             } 
402450        } 
403451    } 
404452#else
0 commit comments