22// The .NET Foundation licenses this file to you under the MIT license. 
33
44using  System ; 
5+ using  System . Buffers ; 
56using  System . IO ; 
67using  System . Linq ; 
78using  System . Security . Cryptography ; 
@@ -153,15 +154,12 @@ private SymmetricAlgorithm CreateSymmetricAlgorithm(byte[]? key = null)
153154        return  retVal ; 
154155    } 
155156
156-     private  KeyedHashAlgorithm  CreateValidationAlgorithm ( byte [ ] ?  key   =   null ) 
157+     private  KeyedHashAlgorithm  CreateValidationAlgorithm ( byte [ ]  key ) 
157158    { 
158159        var  retVal  =  _validationAlgorithmFactory ( ) ; 
159160        CryptoUtil . Assert ( retVal  !=  null ,  "retVal != null" ) ; 
160161
161-         if  ( key  is  not null ) 
162-         { 
163-             retVal . Key  =  key ; 
164-         } 
162+         retVal . Key  =  key ; 
165163        return  retVal ; 
166164    } 
167165
@@ -298,29 +296,7 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
298296
299297        try 
300298        { 
301-             var  decryptedKdk  =  new  byte [ _keyDerivationKey . Length ] ; 
302-             var  encryptionSubkey  =  new  byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ; 
303-             var  validationSubkey  =  new  byte [ _validationAlgorithmSubkeyLengthInBytes ] ; 
304-             var  derivedKeysBuffer  =  new  byte [ checked ( encryptionSubkey . Length  +  validationSubkey . Length ) ] ; 
305- 
306-             fixed ( byte *  __unused__1  =  decryptedKdk ) 
307-             fixed ( byte *  __unused__2  =  encryptionSubkey ) 
308-             fixed ( byte *  __unused__3  =  validationSubkey ) 
309-             fixed ( byte *  __unused__4  =  derivedKeysBuffer ) 
310-             { 
311-                 try 
312-                 { 
313-                     return  EncryptImpl ( plaintext ,  additionalAuthenticatedData . AsSpan ( ) ,  decryptedKdk ,  encryptionSubkey ,  validationSubkey ,  derivedKeysBuffer ) ; 
314-                 } 
315-                 finally 
316-                 { 
317-                     // delete since these contain secret material 
318-                     Array . Clear ( decryptedKdk ,  0 ,  decryptedKdk . Length ) ; 
319-                     Array . Clear ( encryptionSubkey ,  0 ,  encryptionSubkey . Length ) ; 
320-                     Array . Clear ( validationSubkey ,  0 ,  validationSubkey . Length ) ; 
321-                     Array . Clear ( derivedKeysBuffer ,  0 ,  derivedKeysBuffer . Length ) ; 
322-                 } 
323-             } 
299+             return  EncryptImpl ( plaintext ,  additionalAuthenticatedData ) ; 
324300        } 
325301        catch  ( Exception  ex )  when  ( ex . RequiresHomogenization ( ) ) 
326302        { 
@@ -331,75 +307,79 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
331307
332308#if NET10_0_OR_GREATER 
333309    private  byte [ ]  EncryptImpl ( 
334-         ArraySegment < byte >  plaintextArraySegment , 
335-         ReadOnlySpan < byte >  additionalAuthenticatedData , 
336-         byte [ ]  decryptedKdk , 
337-         byte [ ]  encryptionSubkey , 
338-         byte [ ]  validationSubkey , 
339-         byte [ ]  derivedKeysBuffer ) 
310+         ReadOnlySpan < byte >  plainText , 
311+         ReadOnlySpan < byte >  additionalAuthenticatedData ) 
340312    { 
341-         var  plainText  =  plaintextArraySegment . AsSpan ( ) ; 
342- 
343-         // Step 4: Perform the encryption operation. 
344- 
345-         using  var  symmetricAlgorithm  =  CreateSymmetricAlgorithm ( key :  encryptionSubkey ) ; 
346-         using  var  validationAlgorithm  =  CreateValidationAlgorithm ( key :  null ! ) ;  // we dont care now, because we are only interested in the hash size property 
347- 
348-         var  cipherTextLength  =  symmetricAlgorithm . GetCiphertextLengthCbc ( plainText . Length ) ; 
349313        var  keyModifierLength  =  KEY_MODIFIER_SIZE_IN_BYTES ; 
350314        var  ivLength  =  _symmetricAlgorithmBlockSizeInBytes ; 
351-         var  macLength  =  validationAlgorithm . HashSize  *  8 ; 
352- 
353-         // allocating an array of a specific required length: 
354-         var  outputArray  =  new  byte [ keyModifierLength  +  ivLength  +  cipherTextLength  +  macLength ] ; 
355-         var  outputSpan  =  outputArray . AsSpan ( ) ; 
356- 
357-         // Step 1: Generate a random key modifier and IV for this operation. 
358-         // Both will be equal to the block size of the block cipher algorithm. 
359-         // note: we are generating it right into the result array 
360-         _genRandom . GenRandom ( outputSpan . Slice ( start :  0 ,  length :  keyModifierLength ) ) ; 
361-         _genRandom . GenRandom ( outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength ) ) ; 
362315
363-         var  keyModifier  =  outputSpan . Slice ( start :  0 ,  length :  keyModifierLength ) ; 
364-         var  iv  =  outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength ) ; 
365- 
366-         _keyDerivationKey . WriteSecretIntoBuffer ( new  ArraySegment < byte > ( decryptedKdk ) ) ; 
367-         ManagedSP800_108_CTR_HMACSHA512 . DeriveKeys ( 
368-             kdk :  decryptedKdk , 
369-             label :  additionalAuthenticatedData , 
370-             contextHeader :  _contextHeader , 
371-             contextData :  keyModifier , 
372-             prfFactory :  _kdkPrfFactory , 
373-             output :  new  ArraySegment < byte > ( derivedKeysBuffer ) ) ; 
374- 
375-         Buffer . BlockCopy ( derivedKeysBuffer ,  0 ,  encryptionSubkey ,  0 ,  encryptionSubkey . Length ) ; 
376-         Buffer . BlockCopy ( derivedKeysBuffer ,  encryptionSubkey . Length ,  validationSubkey ,  0 ,  validationSubkey . Length ) ; 
316+         // Step 1: Decrypt the KDK, and use it to generate new encryption and HMAC keys. 
317+         // We pin all unencrypted keys to limit their exposure via GC relocation. 
318+         var  decryptedKdk  =  new  byte [ _keyDerivationKey . Length ] ; 
319+         var  encryptionSubkey  =  new  byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ; 
320+         var  validationSubkey  =  new  byte [ _validationAlgorithmSubkeyLengthInBytes ] ; 
377321
378-         // encrypting plaintext into the target array directly 
379-         symmetricAlgorithm . EncryptCbc ( plainText ,  iv ,  outputSpan . Slice ( start :  KEY_MODIFIER_SIZE_IN_BYTES  +  _symmetricAlgorithmBlockSizeInBytes ,  length :  cipherTextLength ) ) ; 
322+         fixed ( byte *  __unused__1  =  decryptedKdk ) 
323+         fixed ( byte *  __unused__2  =  encryptionSubkey ) 
324+         fixed ( byte *  __unused__3  =  validationSubkey ) 
325+         { 
326+             var  keyModifier  =  ArrayPool < byte > . Shared . Rent ( keyModifierLength ) ; 
327+             _genRandom . GenRandom ( keyModifier ) ; 
328+ 
329+             _keyDerivationKey . WriteSecretIntoBuffer ( new  ArraySegment < byte > ( decryptedKdk ) ) ; 
330+             ManagedSP800_108_CTR_HMACSHA512 . DeriveKeys ( 
331+                 kdk :  decryptedKdk , 
332+                 label :  additionalAuthenticatedData , 
333+                 contextHeader :  _contextHeader , 
334+                 contextData :  keyModifier , 
335+                 prfFactory :  _kdkPrfFactory , 
336+                 operationSubKey :  encryptionSubkey , 
337+                 validationSubKey :  validationSubkey ) ; 
338+ 
339+             // idea of optimization here is firstly get all the types preset 
340+             // for calculating length of the output array and allocating it. 
341+             // then we are filling it with the data directly, without any additional copying 
342+             using  var  symmetricAlgorithm  =  CreateSymmetricAlgorithm ( key :  encryptionSubkey ) ; 
343+             using  var  validationAlgorithm  =  CreateValidationAlgorithm ( key :  validationSubkey ) ; 
344+ 
345+             var  cipherTextLength  =  symmetricAlgorithm . GetCiphertextLengthCbc ( plainText . Length ) ;  // CBC because symmetricAlgorithm is created with CBC mode 
346+             var  macLength  =  _validationAlgorithmDigestLengthInBytes ; 
347+ 
348+             // allocating an array of a specific required length 
349+             var  outputArray  =  new  byte [ keyModifierLength  +  ivLength  +  cipherTextLength  +  macLength ] ; 
350+             var  outputSpan  =  outputArray . AsSpan ( ) ; 
351+ 
352+             // Step 2: Copy the key modifier and the IV to the output stream since they'll act as a header. 
353+             keyModifier . CopyTo ( outputSpan . Slice ( start :  0 ,  length :  keyModifierLength ) ) ; 
354+ 
355+             // Step 3: Generate IV for this operation right into the result array (no allocation) 
356+             _genRandom . GenRandom ( outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength ) ) ; 
357+             var  iv  =  outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength ) ; 
358+ 
359+             // encrypting plaintext into the target array directly 
360+             symmetricAlgorithm . EncryptCbc ( plainText ,  iv ,  outputSpan . Slice ( start :  keyModifierLength  +  ivLength ,  length :  cipherTextLength ) ) ; 
380361
381-         // At this point, outputStream := { keyModifier || IV || ciphertext } 
362+              // At this point, outputStream := { keyModifier || IV || ciphertext } 
382363
383-         // Step 5 : Calculate the digest over the IV and ciphertext. 
384-         // We don't need to calculate the digest over the key modifier since that 
385-         // value has already been mixed into the KDF used to generate the MAC key. 
364+              // Step 4 : Calculate the digest over the IV and ciphertext. 
365+              // We don't need to calculate the digest over the key modifier since that 
366+              // value has already been mixed into the KDF used to generate the MAC key. 
386367
387-         validationAlgorithm . Key  =  validationSubkey ;  // crucial: finally set the key 
388-         validationAlgorithm . TryComputeHash ( source :  outputSpan ,  destination :  outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength  +  cipherTextLength ) ,  bytesWritten :  out  _ ) ; 
368+             var  ivAndCipherTextSpan  =  outputSpan . Slice ( start :  keyModifierLength ,  length :  ivLength  +  cipherTextLength ) ; 
369+             var  macDestinationSpan  =  outputSpan . Slice ( keyModifierLength  +  ivLength  +  cipherTextLength ,  macLength ) ; 
370+             validationAlgorithm . TryComputeHash ( source :  ivAndCipherTextSpan ,  destination :  macDestinationSpan ,  bytesWritten :  out  _ ) ; 
371+             // At this point, outputArray := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) } 
389372
390-         // At this point, outputArray := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) } 
391-         // And we're done! 
373+              // returning whatever was pooled back with clear (secret data to be cleaned) 
374+              ArrayPool < byte > . Shared . Return ( keyModifier ,   clearArray :   true ) ; 
392375
393-         return  outputArray ; 
376+             return  outputArray ; 
377+         } 
394378    } 
395379#else
396380    private  byte [ ]  EncryptImpl ( 
397381        ArraySegment < byte >  plaintext , 
398-         ReadOnlySpan < byte >  additionalAuthenticatedData , 
399-         byte [ ]  decryptedKdk , 
400-         byte [ ]  encryptionSubkey , 
401-         byte [ ]  validationSubkey , 
402-         byte [ ]  derivedKeysBuffer ) 
382+         ArraySegment < byte >  additionalAuthenticatedData ) 
403383    { 
404384        var  outputStream  =  new  MemoryStream ( ) ; 
405385
@@ -415,46 +395,70 @@ private byte[] EncryptImpl(
415395        outputStream . Write ( iv ,  0 ,  iv . Length ) ; 
416396
417397        // At this point, outputStream := { keyModifier || IV }. 
398+ 
418399        // Step 3: Decrypt the KDK, and use it to generate new encryption and HMAC keys. 
419400        // We pin all unencrypted keys to limit their exposure via GC relocation. 
420-         _keyDerivationKey . WriteSecretIntoBuffer ( new  ArraySegment < byte > ( decryptedKdk ) ) ; 
421-         ManagedSP800_108_CTR_HMACSHA512 . DeriveKeys ( 
422-             kdk :  decryptedKdk , 
423-             label :  additionalAuthenticatedData , 
424-             contextHeader :  _contextHeader , 
425-             contextData :  new  ArraySegment < byte > ( keyModifier ) , 
426-             prfFactory :  _kdkPrfFactory , 
427-             output :  new  ArraySegment < byte > ( derivedKeysBuffer ) ) ; 
428- 
429-         Buffer . BlockCopy ( derivedKeysBuffer ,  0 ,  encryptionSubkey ,  0 ,  encryptionSubkey . Length ) ; 
430-         Buffer . BlockCopy ( derivedKeysBuffer ,  encryptionSubkey . Length ,  validationSubkey ,  0 ,  validationSubkey . Length ) ; 
431401
432-         // Step 4: Perform the encryption operation. 
402+         var  decryptedKdk  =  new  byte [ _keyDerivationKey . Length ] ; 
403+         var  encryptionSubkey  =  new  byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ; 
404+         var  validationSubkey  =  new  byte [ _validationAlgorithmSubkeyLengthInBytes ] ; 
405+         var  derivedKeysBuffer  =  new  byte [ checked ( encryptionSubkey . Length  +  validationSubkey . Length ) ] ; 
433406
434-         using  ( var  symmetricAlgorithm  =  CreateSymmetricAlgorithm ( ) ) 
435-         using  ( var  cryptoTransform  =  symmetricAlgorithm . CreateEncryptor ( encryptionSubkey ,  iv ) ) 
436-         using  ( var  cryptoStream  =  new  CryptoStream ( outputStream ,  cryptoTransform ,  CryptoStreamMode . Write ) ) 
407+         fixed ( byte *  __unused__1  =  decryptedKdk ) 
408+         fixed ( byte *  __unused__2  =  encryptionSubkey ) 
409+         fixed ( byte *  __unused__3  =  validationSubkey ) 
410+         fixed ( byte *  __unused__4  =  derivedKeysBuffer ) 
437411        { 
438-             cryptoStream . Write ( plaintext . Array ! ,  plaintext . Offset ,  plaintext . Count ) ; 
439-             cryptoStream . FlushFinalBlock ( ) ; 
412+             try 
413+             { 
414+                 _keyDerivationKey . WriteSecretIntoBuffer ( new  ArraySegment < byte > ( decryptedKdk ) ) ; 
415+                 ManagedSP800_108_CTR_HMACSHA512 . DeriveKeysWithContextHeader ( 
416+                     kdk :  decryptedKdk , 
417+                     label :  additionalAuthenticatedData , 
418+                     contextHeader :  _contextHeader , 
419+                     context :  new  ArraySegment < byte > ( keyModifier ) , 
420+                     prfFactory :  _kdkPrfFactory , 
421+                     output :  new  ArraySegment < byte > ( derivedKeysBuffer ) ) ; 
422+ 
423+                 Buffer . BlockCopy ( derivedKeysBuffer ,  0 ,  encryptionSubkey ,  0 ,  encryptionSubkey . Length ) ; 
424+                 Buffer . BlockCopy ( derivedKeysBuffer ,  encryptionSubkey . Length ,  validationSubkey ,  0 ,  validationSubkey . Length ) ; 
425+ 
426+                 // Step 4: Perform the encryption operation. 
427+ 
428+                 using  ( var  symmetricAlgorithm  =  CreateSymmetricAlgorithm ( ) ) 
429+                 using  ( var  cryptoTransform  =  symmetricAlgorithm . CreateEncryptor ( encryptionSubkey ,  iv ) ) 
430+                 using  ( var  cryptoStream  =  new  CryptoStream ( outputStream ,  cryptoTransform ,  CryptoStreamMode . Write ) ) 
431+                 { 
432+                     cryptoStream . Write ( plaintext . Array ! ,  plaintext . Offset ,  plaintext . Count ) ; 
433+                     cryptoStream . FlushFinalBlock ( ) ; 
440434
441-             // At this point, outputStream := { keyModifier || IV || ciphertext } 
435+                      // At this point, outputStream := { keyModifier || IV || ciphertext } 
442436
443-             // Step 5: Calculate the digest over the IV and ciphertext. 
444-             // We don't need to calculate the digest over the key modifier since that 
445-             // value has already been mixed into the KDF used to generate the MAC key. 
437+                      // Step 5: Calculate the digest over the IV and ciphertext. 
438+                      // We don't need to calculate the digest over the key modifier since that 
439+                      // value has already been mixed into the KDF used to generate the MAC key. 
446440
447-             using  ( var  validationAlgorithm  =  CreateValidationAlgorithm ( validationSubkey ) ) 
448-             { 
449-                 // As an optimization, avoid duplicating the underlying buffer 
450-                 var  underlyingBuffer  =  outputStream . GetBuffer ( ) ; 
441+                      using  ( var  validationAlgorithm  =  CreateValidationAlgorithm ( validationSubkey ) ) 
442+                      { 
443+                          // As an optimization, avoid duplicating the underlying buffer 
444+                          var  underlyingBuffer  =  outputStream . GetBuffer ( ) ; 
451445
452-                 var  mac  =  validationAlgorithm . ComputeHash ( underlyingBuffer ,  KEY_MODIFIER_SIZE_IN_BYTES ,  checked ( ( int ) outputStream . Length  -  KEY_MODIFIER_SIZE_IN_BYTES ) ) ; 
453-                 outputStream . Write ( mac ,  0 ,  mac . Length ) ; 
446+                          var  mac  =  validationAlgorithm . ComputeHash ( underlyingBuffer ,  KEY_MODIFIER_SIZE_IN_BYTES ,  checked ( ( int ) outputStream . Length  -  KEY_MODIFIER_SIZE_IN_BYTES ) ) ; 
447+                          outputStream . Write ( mac ,  0 ,  mac . Length ) ; 
454448
455-                 // At this point, outputStream := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) } 
456-                 // And we're done! 
457-                 return  outputStream . ToArray ( ) ; 
449+                         // At this point, outputStream := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) } 
450+                         // And we're done! 
451+                         return  outputStream . ToArray ( ) ; 
452+                     } 
453+                 } 
454+             } 
455+             finally 
456+             { 
457+                 // delete since these contain secret material 
458+                 Array . Clear ( decryptedKdk ,  0 ,  decryptedKdk . Length ) ; 
459+                 Array . Clear ( encryptionSubkey ,  0 ,  encryptionSubkey . Length ) ; 
460+                 Array . Clear ( validationSubkey ,  0 ,  validationSubkey . Length ) ; 
461+                 Array . Clear ( derivedKeysBuffer ,  0 ,  derivedKeysBuffer . Length ) ; 
458462            } 
459463        } 
460464    } 
0 commit comments