33
44using System ;
55using System . IO ;
6+ using System . Linq ;
67using System . Security . Cryptography ;
78using Microsoft . AspNetCore . Cryptography ;
89using Microsoft . AspNetCore . DataProtection . AuthenticatedEncryption ;
@@ -152,12 +153,15 @@ private SymmetricAlgorithm CreateSymmetricAlgorithm(byte[]? key = null)
152153 return retVal ;
153154 }
154155
155- private KeyedHashAlgorithm CreateValidationAlgorithm ( byte [ ] key )
156+ private KeyedHashAlgorithm CreateValidationAlgorithm ( byte [ ] ? key = null )
156157 {
157158 var retVal = _validationAlgorithmFactory ( ) ;
158159 CryptoUtil . Assert ( retVal != null , "retVal != null" ) ;
159160
160- retVal . Key = key ;
161+ if ( key is not null )
162+ {
163+ retVal . Key = key ;
164+ }
161165 return retVal ;
162166 }
163167
@@ -250,7 +254,7 @@ public byte[] Decrypt(ArraySegment<byte> protectedPayload, ArraySegment<byte> ad
250254 var ciphertext = protectedPayload . Array . AsSpan ( ) . Slice ( ciphertextOffset , macOffset - ciphertextOffset ) ;
251255 var iv = protectedPayload . Array . AsSpan ( ) . Slice ( ivOffset , _symmetricAlgorithmBlockSizeInBytes ) ;
252256
253- return symmetricAlgorithm . DecryptCbc ( ciphertext , iv ) ;
257+ return symmetricAlgorithm . DecryptCbc ( ciphertext , iv ) ; // symmetricAlgorithm is created with CBC mode
254258#else
255259 var iv = new byte [ _symmetricAlgorithmBlockSizeInBytes ] ;
256260 Buffer . BlockCopy ( protectedPayload . Array ! , ivOffset , iv , 0 , iv . Length ) ;
@@ -294,24 +298,6 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
294298
295299 try
296300 {
297- var outputStream = new MemoryStream ( ) ;
298-
299- // Step 1: Generate a random key modifier and IV for this operation.
300- // Both will be equal to the block size of the block cipher algorithm.
301-
302- var keyModifier = _genRandom . GenRandom ( KEY_MODIFIER_SIZE_IN_BYTES ) ;
303- var iv = _genRandom . GenRandom ( _symmetricAlgorithmBlockSizeInBytes ) ;
304-
305- // Step 2: Copy the key modifier and the IV to the output stream since they'll act as a header.
306-
307- outputStream . Write ( keyModifier , 0 , keyModifier . Length ) ;
308- outputStream . Write ( iv , 0 , iv . Length ) ;
309-
310- // At this point, outputStream := { keyModifier || IV }.
311-
312- // Step 3: Decrypt the KDK, and use it to generate new encryption and HMAC keys.
313- // We pin all unencrypted keys to limit their exposure via GC relocation.
314-
315301 var decryptedKdk = new byte [ _keyDerivationKey . Length ] ;
316302 var encryptionSubkey = new byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ;
317303 var validationSubkey = new byte [ _validationAlgorithmSubkeyLengthInBytes ] ;
@@ -324,46 +310,7 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
324310 {
325311 try
326312 {
327- _keyDerivationKey . WriteSecretIntoBuffer ( new ArraySegment < byte > ( decryptedKdk ) ) ;
328- ManagedSP800_108_CTR_HMACSHA512 . DeriveKeysWithContextHeader (
329- kdk : decryptedKdk ,
330- label : additionalAuthenticatedData ,
331- contextHeader : _contextHeader ,
332- context : new ArraySegment < byte > ( keyModifier ) ,
333- prfFactory : _kdkPrfFactory ,
334- output : new ArraySegment < byte > ( derivedKeysBuffer ) ) ;
335-
336- Buffer . BlockCopy ( derivedKeysBuffer , 0 , encryptionSubkey , 0 , encryptionSubkey . Length ) ;
337- Buffer . BlockCopy ( derivedKeysBuffer , encryptionSubkey . Length , validationSubkey , 0 , validationSubkey . Length ) ;
338-
339- // Step 4: Perform the encryption operation.
340-
341- using ( var symmetricAlgorithm = CreateSymmetricAlgorithm ( ) )
342- using ( var cryptoTransform = symmetricAlgorithm . CreateEncryptor ( encryptionSubkey , iv ) )
343- using ( var cryptoStream = new CryptoStream ( outputStream , cryptoTransform , CryptoStreamMode . Write ) )
344- {
345- cryptoStream . Write ( plaintext . Array ! , plaintext . Offset , plaintext . Count ) ;
346- cryptoStream . FlushFinalBlock ( ) ;
347-
348- // At this point, outputStream := { keyModifier || IV || ciphertext }
349-
350- // Step 5: Calculate the digest over the IV and ciphertext.
351- // We don't need to calculate the digest over the key modifier since that
352- // value has already been mixed into the KDF used to generate the MAC key.
353-
354- using ( var validationAlgorithm = CreateValidationAlgorithm ( validationSubkey ) )
355- {
356- // As an optimization, avoid duplicating the underlying buffer
357- var underlyingBuffer = outputStream . GetBuffer ( ) ;
358-
359- var mac = validationAlgorithm . ComputeHash ( underlyingBuffer , KEY_MODIFIER_SIZE_IN_BYTES , checked ( ( int ) outputStream . Length - KEY_MODIFIER_SIZE_IN_BYTES ) ) ;
360- outputStream . Write ( mac , 0 , mac . Length ) ;
361-
362- // At this point, outputStream := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) }
363- // And we're done!
364- return outputStream . ToArray ( ) ;
365- }
366- }
313+ return EncryptImpl ( plaintext , additionalAuthenticatedData . AsSpan ( ) , decryptedKdk , encryptionSubkey , validationSubkey , derivedKeysBuffer ) ;
367314 }
368315 finally
369316 {
@@ -381,4 +328,135 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
381328 throw Error . CryptCommon_GenericError ( ex ) ;
382329 }
383330 }
331+
332+ #if NET10_0_OR_GREATER
333+ private byte [ ] EncryptImpl (
334+ ArraySegment < byte > plaintextArraySegment ,
335+ ReadOnlySpan < byte > additionalAuthenticatedData ,
336+ byte [ ] decryptedKdk ,
337+ byte [ ] encryptionSubkey ,
338+ byte [ ] validationSubkey ,
339+ byte [ ] derivedKeysBuffer )
340+ {
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 ) ;
349+ var keyModifierLength = KEY_MODIFIER_SIZE_IN_BYTES ;
350+ 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 ) ) ;
362+
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 ) ;
377+
378+ // encrypting plaintext into the target array directly
379+ symmetricAlgorithm . EncryptCbc ( plainText , iv , outputSpan . Slice ( start : KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes , length : cipherTextLength ) ) ;
380+
381+ // At this point, outputStream := { keyModifier || IV || ciphertext }
382+
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.
386+
387+ validationAlgorithm . Key = validationSubkey ; // crucial: finally set the key
388+ validationAlgorithm . TryComputeHash ( source : outputSpan , destination : outputSpan . Slice ( start : keyModifierLength , length : ivLength + cipherTextLength ) , bytesWritten : out _ ) ;
389+
390+ // At this point, outputArray := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) }
391+ // And we're done!
392+
393+ return outputArray ;
394+ }
395+ #else
396+ private byte [ ] EncryptImpl (
397+ ArraySegment < byte > plaintext ,
398+ ReadOnlySpan < byte > additionalAuthenticatedData ,
399+ byte [ ] decryptedKdk ,
400+ byte [ ] encryptionSubkey ,
401+ byte [ ] validationSubkey ,
402+ byte [ ] derivedKeysBuffer )
403+ {
404+ var outputStream = new MemoryStream ( ) ;
405+
406+ // Step 1: Generate a random key modifier and IV for this operation.
407+ // Both will be equal to the block size of the block cipher algorithm.
408+
409+ var keyModifier = _genRandom . GenRandom ( KEY_MODIFIER_SIZE_IN_BYTES ) ;
410+ var iv = _genRandom . GenRandom ( _symmetricAlgorithmBlockSizeInBytes ) ;
411+
412+ // Step 2: Copy the key modifier and the IV to the output stream since they'll act as a header.
413+
414+ outputStream . Write ( keyModifier , 0 , keyModifier . Length ) ;
415+ outputStream . Write ( iv , 0 , iv . Length ) ;
416+
417+ // At this point, outputStream := { keyModifier || IV }.
418+ // Step 3: Decrypt the KDK, and use it to generate new encryption and HMAC keys.
419+ // 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 ) ;
431+
432+ // Step 4: Perform the encryption operation.
433+
434+ using ( var symmetricAlgorithm = CreateSymmetricAlgorithm ( ) )
435+ using ( var cryptoTransform = symmetricAlgorithm . CreateEncryptor ( encryptionSubkey , iv ) )
436+ using ( var cryptoStream = new CryptoStream ( outputStream , cryptoTransform , CryptoStreamMode . Write ) )
437+ {
438+ cryptoStream . Write ( plaintext . Array ! , plaintext . Offset , plaintext . Count ) ;
439+ cryptoStream . FlushFinalBlock ( ) ;
440+
441+ // At this point, outputStream := { keyModifier || IV || ciphertext }
442+
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.
446+
447+ using ( var validationAlgorithm = CreateValidationAlgorithm ( validationSubkey ) )
448+ {
449+ // As an optimization, avoid duplicating the underlying buffer
450+ var underlyingBuffer = outputStream . GetBuffer ( ) ;
451+
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 ) ;
454+
455+ // At this point, outputStream := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) }
456+ // And we're done!
457+ return outputStream . ToArray ( ) ;
458+ }
459+ }
460+ }
461+ #endif
384462}
0 commit comments