Skip to content

Commit 9caa72e

Browse files
committed
aes
1 parent ae6fb76 commit 9caa72e

File tree

2 files changed

+74
-11
lines changed

2 files changed

+74
-11
lines changed

src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,80 @@ public int GetEncryptedSize(int plainTextLength)
145145
=> GetEncryptedSize(plainTextLength, 0, 0);
146146

147147
public int GetEncryptedSize(int plainTextLength, uint preBufferSize, uint postBufferSize)
148-
=> checked((int)(preBufferSize + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plainTextLength + TAG_SIZE_IN_BYTES + postBufferSize));
148+
{
149+
// A buffer to hold the key modifier, nonce, encrypted data, and tag.
150+
// In GCM, the encrypted output will be the same length as the plaintext input.
151+
return checked((int)(preBufferSize + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plainTextLength + TAG_SIZE_IN_BYTES + postBufferSize));
152+
}
149153

150-
public bool TryEncrypt(ReadOnlySpan<byte> plainText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)
154+
public bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)
151155
{
152-
throw new NotImplementedException();
156+
bytesWritten = 0;
157+
158+
try
159+
{
160+
// Generate random key modifier and nonce
161+
var keyModifier = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES);
162+
var nonceBytes = _genRandom.GenRandom(NONCE_SIZE_IN_BYTES);
163+
164+
// KeyModifier and nonce to destination
165+
keyModifier.CopyTo(destination.Slice(bytesWritten, KEY_MODIFIER_SIZE_IN_BYTES));
166+
bytesWritten += KEY_MODIFIER_SIZE_IN_BYTES;
167+
nonceBytes.CopyTo(destination.Slice(bytesWritten, NONCE_SIZE_IN_BYTES));
168+
bytesWritten += NONCE_SIZE_IN_BYTES;
169+
170+
// At this point, destination := { keyModifier | nonce | _____ | _____ }
171+
172+
// Use the KDF to generate a new symmetric block cipher key
173+
// We'll need a temporary buffer to hold the symmetric encryption subkey
174+
Span<byte> decryptedKdk = _keyDerivationKey.Length <= 256
175+
? stackalloc byte[256].Slice(0, _keyDerivationKey.Length)
176+
: new byte[_keyDerivationKey.Length];
177+
var derivedKey = _derivedkeySizeInBytes <= 256
178+
? stackalloc byte[256].Slice(0, _derivedkeySizeInBytes)
179+
: new byte[_derivedkeySizeInBytes];
180+
181+
fixed (byte* decryptedKdkUnsafe = decryptedKdk)
182+
fixed (byte* __unused__2 = derivedKey)
183+
{
184+
try
185+
{
186+
_keyDerivationKey.WriteSecretIntoBuffer(decryptedKdkUnsafe, decryptedKdk.Length);
187+
ManagedSP800_108_CTR_HMACSHA512.DeriveKeys(
188+
kdk: decryptedKdk,
189+
label: additionalAuthenticatedData,
190+
contextHeader: _contextHeader,
191+
contextData: keyModifier,
192+
operationSubkey: derivedKey,
193+
validationSubkey: Span<byte>.Empty /* filling in derivedKey only */ );
194+
195+
// Perform GCM encryption. Destination buffer expected structure:
196+
// { keyModifier | nonce | encryptedData | authenticationTag }
197+
var nonce = destination.Slice(KEY_MODIFIER_SIZE_IN_BYTES, NONCE_SIZE_IN_BYTES);
198+
var encrypted = destination.Slice(bytesWritten, plaintext.Length);
199+
var tag = destination.Slice(bytesWritten + plaintext.Length, TAG_SIZE_IN_BYTES);
200+
201+
using var aes = new AesGcm(derivedKey, TAG_SIZE_IN_BYTES);
202+
aes.Encrypt(nonce, plaintext, encrypted, tag);
203+
204+
// At this point, destination := { keyModifier | nonce | encryptedData | authenticationTag }
205+
// And we're done!
206+
bytesWritten += plaintext.Length + TAG_SIZE_IN_BYTES;
207+
return true;
208+
}
209+
finally
210+
{
211+
// delete since these contain secret material
212+
decryptedKdk.Clear();
213+
derivedKey.Clear();
214+
}
215+
}
216+
}
217+
catch (Exception ex) when (ex.RequiresHomogenization())
218+
{
219+
// Homogenize all exceptions to CryptographicException.
220+
throw Error.CryptCommon_GenericError(ex);
221+
}
153222
}
154223

155224
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Aes/AesAuthenticatedEncryptorTests.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Text;
77
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
88
using Microsoft.AspNetCore.DataProtection.Managed;
9+
using Microsoft.AspNetCore.DataProtection.Tests.Internal;
910

1011
namespace Microsoft.AspNetCore.DataProtection.Tests.Aes;
1112
public class AesAuthenticatedEncryptorTests
@@ -21,13 +22,6 @@ public void Roundtrip_AesGcm_TryEncryptDecrypt_CorrectlyEstimatesDataLength(int
2122
ArraySegment<byte> plaintext = new ArraySegment<byte>(Encoding.UTF8.GetBytes("plaintext"));
2223
ArraySegment<byte> aad = new ArraySegment<byte>(Encoding.UTF8.GetBytes("aad"));
2324

24-
var expectedSize = encryptor.GetEncryptedSize(plaintext.Count);
25-
26-
byte[] ciphertext = encryptor.Encrypt(plaintext, aad);
27-
Assert.Equal(expectedSize, ciphertext.Length);
28-
29-
byte[] decipheredtext = encryptor.Decrypt(new ArraySegment<byte>(ciphertext), aad);
30-
31-
Assert.Equal(plaintext.AsSpan(), decipheredtext.AsSpan());
25+
RoundtripEncryptionHelpers.AssertTryEncryptTryDecryptParity(encryptor, plaintext, aad);
3226
}
3327
}

0 commit comments

Comments
 (0)