66using Microsoft . AspNetCore . Cryptography . Cng ;
77using Microsoft . AspNetCore . Cryptography . SafeHandles ;
88using Microsoft . AspNetCore . DataProtection . AuthenticatedEncryption ;
9- using Microsoft . AspNetCore . DataProtection . Cng . Internal ;
109using Microsoft . AspNetCore . DataProtection . SP800_108 ;
1110
1211namespace Microsoft . AspNetCore . DataProtection . Cng ;
@@ -21,7 +20,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng;
2120// going to the IV. This means that we'll only hit the 2^-32 probability limit after 2^96 encryption
2221// operations, which will realistically never happen. (At the absurd rate of one encryption operation
2322// per nanosecond, it would still take 180 times the age of the universe to hit 2^96 operations.)
24- internal sealed unsafe class CngGcmAuthenticatedEncryptor : CngAuthenticatedEncryptorBase
23+ internal sealed unsafe class CngGcmAuthenticatedEncryptor : IOptimizedAuthenticatedEncryptor , ISpanAuthenticatedEncryptor , IDisposable
2524{
2625 // Having a key modifier ensures with overwhelming probability that no two encryption operations
2726 // will ever derive the same (encryption subkey, MAC subkey) pair. This limits an attacker's
@@ -51,71 +50,22 @@ public CngGcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHand
5150 _contextHeader = CreateContextHeader ( ) ;
5251 }
5352
54- private byte [ ] CreateContextHeader ( )
53+ public int GetDecryptedSize ( int cipherTextLength )
5554 {
56- var retVal = new byte [ checked (
57- 1 /* KDF alg */
58- + 1 /* chaining mode */
59- + sizeof ( uint ) /* sym alg key size */
60- + sizeof ( uint ) /* GCM nonce size */
61- + sizeof ( uint ) /* sym alg block size */
62- + sizeof ( uint ) /* GCM tag size */
63- + TAG_SIZE_IN_BYTES /* tag of GCM-encrypted empty string */ ) ] ;
64-
65- fixed ( byte * pbRetVal = retVal )
66- {
67- byte * ptr = pbRetVal ;
68-
69- // First is the two-byte header
70- * ( ptr ++ ) = 0 ; // 0x00 = SP800-108 CTR KDF w/ HMACSHA512 PRF
71- * ( ptr ++ ) = 1 ; // 0x01 = GCM encryption + authentication
72-
73- // Next is information about the symmetric algorithm (key size, nonce size, block size, tag size)
74- BitHelpers . WriteTo ( ref ptr , _symmetricAlgorithmSubkeyLengthInBytes ) ;
75- BitHelpers. WriteTo ( ref ptr , NONCE_SIZE_IN_BYTES ) ;
76- BitHelpers. WriteTo ( ref ptr , TAG_SIZE_IN_BYTES ) ; // block size = tag size
77- BitHelpers. WriteTo ( ref ptr , TAG_SIZE_IN_BYTES ) ;
78-
79- // See the design document for an explanation of the following code.
80- var tempKeys = new byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ;
81- fixed ( byte * pbTempKeys = tempKeys)
82- {
83- byte dummy;
84-
85- // Derive temporary key for encryption.
86- using ( var provider = SP800_108_CTR_HMACSHA512Util. CreateEmptyProvider ( ) )
87- {
88- provider. DeriveKey (
89- pbLabel : & dummy ,
90- cbLabel : 0 ,
91- pbContext : & dummy ,
92- cbContext : 0 ,
93- pbDerivedKey : pbTempKeys ,
94- cbDerivedKey : ( uint ) tempKeys . Length ) ;
95- }
96-
97- // Encrypt a zero-length input string with an all-zero nonce and copy the tag to the return buffer.
98- byte * pbNonce = stackalloc byte [ ( int ) NONCE_SIZE_IN_BYTES ] ;
99- UnsafeBufferUtil. SecureZeroMemory ( pbNonce , NONCE_SIZE_IN_BYTES ) ;
100- DoGcmEncrypt(
101- pbKey : pbTempKeys ,
102- cbKey : _symmetricAlgorithmSubkeyLengthInBytes ,
103- pbNonce : pbNonce ,
104- pbPlaintextData : & dummy ,
105- cbPlaintextData : 0 ,
106- pbEncryptedData : & dummy ,
107- pbTag : ptr ) ;
108- }
55+ throw new NotImplementedException ( ) ;
56+ }
10957
110- ptr += TAG_SIZE_IN_BYTES;
111- CryptoUtil. Assert ( ptr - pbRetVal == retVal . Length , "ptr - pbRetVal == retVal.Length" ) ;
112- }
58+ public bool TryDecrypt ( ReadOnlySpan < byte > cipherText , ReadOnlySpan < byte > additionalAuthenticatedData , Span < byte > destination , out int bytesWritten )
59+ {
60+ throw new NotImplementedException ( ) ;
61+ }
11362
114- // retVal := { version || chainingMode || symAlgKeySize || nonceSize || symAlgBlockSize || symAlgTagSize || TAG-of-E("") }.
115- return retVal;
63+ public byte [ ] Decrypt ( ArraySegment < byte > ciphertext , ArraySegment < byte > additionalAuthenticatedData )
64+ {
65+ throw new NotImplementedException ( ) ;
11666 }
11767
118- protected override byte [ ] DecryptImpl ( byte * pbCiphertext , uint cbCiphertext , byte * pbAdditionalAuthenticatedData , uint cbAdditionalAuthenticatedData )
68+ protected byte [ ] DecryptImpl ( byte * pbCiphertext , uint cbCiphertext , byte * pbAdditionalAuthenticatedData , uint cbAdditionalAuthenticatedData )
11969 {
12070 // Argument checking: input must at the absolute minimum contain a key modifier, nonce, and tag
12171 if ( cbCiphertext < KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES )
@@ -192,14 +142,6 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt
192142 }
193143 }
194144
195- public override void Dispose( )
196- {
197- _sp800_108_ctr_hmac_provider. Dispose( ) ;
198-
199- // We don't want to dispose of the underlying algorithm instances because they
200- // might be reused.
201- }
202-
203145 // 'pbNonce' must point to a 96-bit buffer.
204146 // 'pbTag' must point to a 128-bit buffer.
205147 // 'pbEncryptedData' must point to a buffer the same length as 'pbPlaintextData'.
@@ -231,14 +173,14 @@ private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaint
231173 }
232174 }
233175
234- public override int GetEncryptedSize( int plainTextLength )
176+ public int GetEncryptedSize ( int plainTextLength )
235177 {
236178 // A buffer to hold the key modifier, nonce, encrypted data, and tag.
237179 // In GCM, the encrypted output will be the same length as the plaintext input.
238180 return checked ( ( int ) ( KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plainTextLength + TAG_SIZE_IN_BYTES ) ) ;
239181 }
240182
241- public override bool TryEncrypt( ReadOnlySpan < byte > plaintext , ReadOnlySpan < byte > additionalAuthenticatedData , Span < byte > destination , out int bytesWritten)
183+ public bool TryEncrypt ( ReadOnlySpan < byte > plaintext , ReadOnlySpan < byte > additionalAuthenticatedData , Span < byte > destination , out int bytesWritten )
242184 {
243185 bytesWritten = 0 ;
244186
@@ -309,7 +251,10 @@ public override bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte>
309251 }
310252 }
311253
312- public override byte [ ] Encrypt( ArraySegment< byte > plaintext, ArraySegment < byte > additionalAuthenticatedData , uint preBufferSize , uint postBufferSize )
254+ public byte [ ] Encrypt ( ArraySegment < byte > plaintext , ArraySegment < byte > additionalAuthenticatedData )
255+ => Encrypt ( plaintext , additionalAuthenticatedData , 0 , 0 ) ;
256+
257+ public byte [ ] Encrypt ( ArraySegment < byte > plaintext , ArraySegment < byte > additionalAuthenticatedData , uint preBufferSize , uint postBufferSize )
313258 {
314259 plaintext . Validate ( ) ;
315260 additionalAuthenticatedData . Validate ( ) ;
@@ -330,4 +275,76 @@ public override byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte>
330275 CryptoUtil . Assert ( bytesWritten == size , "bytesWritten == size" ) ;
331276 return ciphertext ;
332277 }
278+
279+ private byte [ ] CreateContextHeader ( )
280+ {
281+ var retVal = new byte [ checked (
282+ 1 /* KDF alg */
283+ + 1 /* chaining mode */
284+ + sizeof ( uint ) /* sym alg key size */
285+ + sizeof ( uint ) /* GCM nonce size */
286+ + sizeof ( uint ) /* sym alg block size */
287+ + sizeof ( uint ) /* GCM tag size */
288+ + TAG_SIZE_IN_BYTES /* tag of GCM-encrypted empty string */ ) ] ;
289+
290+ fixed ( byte * pbRetVal = retVal )
291+ {
292+ byte * ptr = pbRetVal ;
293+
294+ // First is the two-byte header
295+ * ( ptr ++ ) = 0 ; // 0x00 = SP800-108 CTR KDF w/ HMACSHA512 PRF
296+ * ( ptr ++ ) = 1 ; // 0x01 = GCM encryption + authentication
297+
298+ // Next is information about the symmetric algorithm (key size, nonce size, block size, tag size)
299+ BitHelpers . WriteTo ( ref ptr , _symmetricAlgorithmSubkeyLengthInBytes ) ;
300+ BitHelpers. WriteTo ( ref ptr , NONCE_SIZE_IN_BYTES ) ;
301+ BitHelpers. WriteTo ( ref ptr , TAG_SIZE_IN_BYTES ) ; // block size = tag size
302+ BitHelpers. WriteTo ( ref ptr , TAG_SIZE_IN_BYTES ) ;
303+
304+ // See the design document for an explanation of the following code.
305+ var tempKeys = new byte [ _symmetricAlgorithmSubkeyLengthInBytes ] ;
306+ fixed ( byte * pbTempKeys = tempKeys)
307+ {
308+ byte dummy;
309+
310+ // Derive temporary key for encryption.
311+ using ( var provider = SP800_108_CTR_HMACSHA512Util. CreateEmptyProvider ( ) )
312+ {
313+ provider. DeriveKey (
314+ pbLabel : & dummy ,
315+ cbLabel : 0 ,
316+ pbContext : & dummy ,
317+ cbContext : 0 ,
318+ pbDerivedKey : pbTempKeys ,
319+ cbDerivedKey : ( uint ) tempKeys . Length ) ;
320+ }
321+
322+ // Encrypt a zero-length input string with an all-zero nonce and copy the tag to the return buffer.
323+ byte * pbNonce = stackalloc byte [ ( int ) NONCE_SIZE_IN_BYTES ] ;
324+ UnsafeBufferUtil. SecureZeroMemory ( pbNonce , NONCE_SIZE_IN_BYTES ) ;
325+ DoGcmEncrypt(
326+ pbKey : pbTempKeys ,
327+ cbKey : _symmetricAlgorithmSubkeyLengthInBytes ,
328+ pbNonce : pbNonce ,
329+ pbPlaintextData : & dummy ,
330+ cbPlaintextData : 0 ,
331+ pbEncryptedData : & dummy ,
332+ pbTag : ptr ) ;
333+ }
334+
335+ ptr += TAG_SIZE_IN_BYTES;
336+ CryptoUtil. Assert ( ptr - pbRetVal == retVal . Length , "ptr - pbRetVal == retVal.Length" ) ;
337+ }
338+
339+ // retVal := { version || chainingMode || symAlgKeySize || nonceSize || symAlgBlockSize || symAlgTagSize || TAG-of-E("") }.
340+ return retVal;
341+ }
342+
343+ public void Dispose( )
344+ {
345+ _sp800_108_ctr_hmac_provider. Dispose ( ) ;
346+
347+ // We don't want to dispose of the underlying algorithm instances because they
348+ // might be reused.
349+ }
333350}
0 commit comments