@@ -51,95 +51,123 @@ public CngGcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHand
5151 }
5252
5353 public int GetDecryptedSize ( int cipherTextLength )
54- {
55- throw new NotImplementedException ( ) ;
56- }
57-
58- public bool TryDecrypt ( ReadOnlySpan < byte > cipherText , ReadOnlySpan < byte > additionalAuthenticatedData , Span < byte > destination , out int bytesWritten )
59- {
60- throw new NotImplementedException ( ) ;
61- }
62-
63- public byte [ ] Decrypt ( ArraySegment < byte > ciphertext , ArraySegment < byte > additionalAuthenticatedData )
64- {
65- throw new NotImplementedException ( ) ;
66- }
67-
68- protected byte [ ] DecryptImpl ( byte * pbCiphertext , uint cbCiphertext , byte * pbAdditionalAuthenticatedData , uint cbAdditionalAuthenticatedData )
6954 {
7055 // Argument checking: input must at the absolute minimum contain a key modifier, nonce, and tag
71- if ( cbCiphertext < KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES )
56+ if ( cipherTextLength < KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES )
7257 {
7358 throw Error . CryptCommon_PayloadInvalid ( ) ;
7459 }
7560
76- // Assumption: pbCipherText := { keyModifier || nonce || encryptedData || authenticationTag }
61+ return checked ( cipherTextLength - ( int ) ( KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES ) ) ;
62+ }
7763
78- var cbPlaintext = checked ( cbCiphertext - ( KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES ) ) ;
64+ public bool TryDecrypt ( ReadOnlySpan < byte > cipherText , ReadOnlySpan < byte > additionalAuthenticatedData , Span < byte > destination , out int bytesWritten )
65+ {
66+ bytesWritten = 0 ;
7967
80- var retVal = new byte [ cbPlaintext ] ;
81- fixed ( byte * pbRetVal = retVal )
68+ try
8269 {
83- // Calculate offsets
84- byte * pbKeyModifier = pbCiphertext ;
85- byte * pbNonce = & pbKeyModifier [ KEY_MODIFIER_SIZE_IN_BYTES ] ;
86- byte * pbEncryptedData = & pbNonce [ NONCE_SIZE_IN_BYTES ] ;
87- byte * pbAuthTag = & pbEncryptedData [ cbPlaintext ] ;
88-
89- // Use the KDF to recreate the symmetric block cipher key
90- // We'll need a temporary buffer to hold the symmetric encryption subkey
91- byte * pbSymmetricDecryptionSubkey = stackalloc byte [ checked ( ( int ) _symmetricAlgorithmSubkeyLengthInBytes ) ] ;
92- try
70+ var plaintextLength = GetDecryptedSize ( cipherText . Length ) ;
71+
72+ // Check if destination is large enough
73+ if ( destination . Length < plaintextLength )
9374 {
94- _sp800_108_ctr_hmac_provider . DeriveKeyWithContextHeader (
95- pbLabel : pbAdditionalAuthenticatedData ,
96- cbLabel : cbAdditionalAuthenticatedData ,
97- contextHeader : _contextHeader ,
98- pbContext : pbKeyModifier ,
99- cbContext : KEY_MODIFIER_SIZE_IN_BYTES ,
100- pbDerivedKey : pbSymmetricDecryptionSubkey ,
101- cbDerivedKey : _symmetricAlgorithmSubkeyLengthInBytes ) ;
102-
103- // Perform the decryption operation
104- using ( var decryptionSubkeyHandle = _symmetricAlgorithmHandle . GenerateSymmetricKey ( pbSymmetricDecryptionSubkey , _symmetricAlgorithmSubkeyLengthInBytes ) )
105- {
106- byte dummy ;
107- byte * pbPlaintext = ( pbRetVal != null ) ? pbRetVal : & dummy ; // CLR doesn't like pinning empty buffers
108-
109- BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo ;
110- BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO . Init ( out authInfo ) ;
111- authInfo . pbNonce = pbNonce ;
112- authInfo . cbNonce = NONCE_SIZE_IN_BYTES ;
113- authInfo . pbTag = pbAuthTag ;
114- authInfo . cbTag = TAG_SIZE_IN_BYTES ;
115-
116- // The call to BCryptDecrypt will also validate the authentication tag
117- uint cbDecryptedBytesWritten ;
118- var ntstatus = UnsafeNativeMethods . BCryptDecrypt (
119- hKey : decryptionSubkeyHandle ,
120- pbInput : pbEncryptedData ,
121- cbInput : cbPlaintext ,
122- pPaddingInfo : & authInfo ,
123- pbIV : null , // IV not used; nonce provided in pPaddingInfo
124- cbIV : 0 ,
125- pbOutput : pbPlaintext ,
126- cbOutput : cbPlaintext ,
127- pcbResult : out cbDecryptedBytesWritten ,
128- dwFlags : 0 ) ;
129- UnsafeNativeMethods . ThrowExceptionForBCryptStatus ( ntstatus ) ;
130- CryptoUtil . Assert ( cbDecryptedBytesWritten == cbPlaintext , "cbDecryptedBytesWritten == cbPlaintext" ) ;
131-
132- // At this point, retVal := { decryptedPayload }
133- // And we're done!
134- return retVal ;
135- }
75+ return false ;
13676 }
137- finally
77+
78+ // Assumption: cipherText := { keyModifier || nonce || encryptedData || authenticationTag }
79+ fixed ( byte * pbCiphertext = cipherText )
80+ fixed ( byte * pbAdditionalAuthenticatedData = additionalAuthenticatedData )
81+ fixed ( byte * pbDestination = destination )
13882 {
139- // The buffer contains key material, so delete it.
140- UnsafeBufferUtil . SecureZeroMemory ( pbSymmetricDecryptionSubkey , _symmetricAlgorithmSubkeyLengthInBytes ) ;
83+ // Calculate offsets
84+ byte * pbKeyModifier = pbCiphertext ;
85+ byte * pbNonce = & pbKeyModifier [ KEY_MODIFIER_SIZE_IN_BYTES ] ;
86+ byte * pbEncryptedData = & pbNonce [ NONCE_SIZE_IN_BYTES ] ;
87+ byte * pbAuthTag = & pbEncryptedData [ plaintextLength ] ;
88+
89+ // Use the KDF to recreate the symmetric block cipher key
90+ // We'll need a temporary buffer to hold the symmetric encryption subkey
91+ byte * pbSymmetricDecryptionSubkey = stackalloc byte [ checked ( ( int ) _symmetricAlgorithmSubkeyLengthInBytes ) ] ;
92+ try
93+ {
94+ _sp800_108_ctr_hmac_provider . DeriveKeyWithContextHeader (
95+ pbLabel : pbAdditionalAuthenticatedData ,
96+ cbLabel : ( uint ) additionalAuthenticatedData . Length ,
97+ contextHeader : _contextHeader ,
98+ pbContext : pbKeyModifier ,
99+ cbContext : KEY_MODIFIER_SIZE_IN_BYTES ,
100+ pbDerivedKey : pbSymmetricDecryptionSubkey ,
101+ cbDerivedKey : _symmetricAlgorithmSubkeyLengthInBytes ) ;
102+
103+ // Perform the decryption operation
104+ using ( var decryptionSubkeyHandle = _symmetricAlgorithmHandle . GenerateSymmetricKey ( pbSymmetricDecryptionSubkey , _symmetricAlgorithmSubkeyLengthInBytes ) )
105+ {
106+ byte dummy ;
107+ byte * pbPlaintext = ( plaintextLength > 0 ) ? pbDestination : & dummy ; // CLR doesn't like pinning empty buffers
108+
109+ BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo ;
110+ BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO . Init ( out authInfo ) ;
111+ authInfo . pbNonce = pbNonce ;
112+ authInfo . cbNonce = NONCE_SIZE_IN_BYTES ;
113+ authInfo . pbTag = pbAuthTag ;
114+ authInfo . cbTag = TAG_SIZE_IN_BYTES ;
115+
116+ // The call to BCryptDecrypt will also validate the authentication tag
117+ uint cbDecryptedBytesWritten ;
118+ var ntstatus = UnsafeNativeMethods . BCryptDecrypt (
119+ hKey : decryptionSubkeyHandle ,
120+ pbInput : pbEncryptedData ,
121+ cbInput : ( uint ) plaintextLength ,
122+ pPaddingInfo : & authInfo ,
123+ pbIV : null , // IV not used; nonce provided in pPaddingInfo
124+ cbIV : 0 ,
125+ pbOutput : pbPlaintext ,
126+ cbOutput : ( uint ) plaintextLength ,
127+ pcbResult : out cbDecryptedBytesWritten ,
128+ dwFlags : 0 ) ;
129+ UnsafeNativeMethods . ThrowExceptionForBCryptStatus ( ntstatus ) ;
130+ CryptoUtil . Assert ( cbDecryptedBytesWritten == plaintextLength , "cbDecryptedBytesWritten == plaintextLength" ) ;
131+
132+ // At this point, retVal := { decryptedPayload }
133+ // And we're done!
134+ bytesWritten = ( int ) cbDecryptedBytesWritten ;
135+ return true ;
136+ }
137+ }
138+ finally
139+ {
140+ // The buffer contains key material, so delete it.
141+ UnsafeBufferUtil . SecureZeroMemory ( pbSymmetricDecryptionSubkey , _symmetricAlgorithmSubkeyLengthInBytes ) ;
142+ }
141143 }
142144 }
145+ catch ( Exception ex ) when ( ex . RequiresHomogenization ( ) )
146+ {
147+ throw Error . CryptCommon_GenericError ( ex ) ;
148+ }
149+ }
150+
151+ public byte [ ] Decrypt ( ArraySegment < byte > ciphertext , ArraySegment < byte > additionalAuthenticatedData )
152+ {
153+ ciphertext . Validate ( ) ;
154+ additionalAuthenticatedData . Validate ( ) ;
155+
156+ var size = GetDecryptedSize ( ciphertext . Count ) ;
157+ var plaintext = new byte [ size ] ;
158+ var destination = plaintext . AsSpan ( ) ;
159+
160+ if ( ! TryDecrypt (
161+ cipherText : ciphertext ,
162+ additionalAuthenticatedData : additionalAuthenticatedData ,
163+ destination : destination ,
164+ out var bytesWritten ) )
165+ {
166+ throw Error . CryptCommon_GenericError ( new ArgumentException ( "Not enough space in destination array" ) ) ;
167+ }
168+
169+ CryptoUtil . Assert ( bytesWritten == size , "bytesWritten == size" ) ;
170+ return plaintext ;
143171 }
144172
145173 // 'pbNonce' must point to a 96-bit buffer.
0 commit comments