Skip to content

Commit ae6fb76

Browse files
committed
cnggcm
1 parent 8d49973 commit ae6fb76

File tree

2 files changed

+68
-9
lines changed

2 files changed

+68
-9
lines changed

src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,12 +234,77 @@ private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaint
234234
#if NET10_0_OR_GREATER
235235
public override int GetEncryptedSize(int plainTextLength, uint preBufferSize, uint postBufferSize)
236236
{
237+
// A buffer to hold the key modifier, nonce, encrypted data, and tag.
238+
// In GCM, the encrypted output will be the same length as the plaintext input.
237239
return checked((int)(preBufferSize + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plainTextLength + TAG_SIZE_IN_BYTES + postBufferSize));
238240
}
239241

240242
public override bool TryEncrypt(ReadOnlySpan<byte> plainText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)
241243
{
242-
throw new NotImplementedException();
244+
bytesWritten = 0;
245+
246+
try
247+
{
248+
fixed (byte* pbDestination = destination)
249+
{
250+
// Calculate offsets
251+
byte* pbKeyModifier = pbDestination;
252+
byte* pbNonce = &pbKeyModifier[KEY_MODIFIER_SIZE_IN_BYTES];
253+
byte* pbEncryptedData = &pbNonce[NONCE_SIZE_IN_BYTES];
254+
byte* pbAuthTag = &pbEncryptedData[plainText.Length];
255+
256+
// Randomly generate the key modifier and nonce
257+
_genRandom.GenRandom(pbKeyModifier, KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES);
258+
bytesWritten += checked((int)(KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES));
259+
260+
// At this point, retVal := { preBuffer | keyModifier | nonce | _____ | _____ | postBuffer }
261+
262+
// Use the KDF to generate a new symmetric block cipher key
263+
// We'll need a temporary buffer to hold the symmetric encryption subkey
264+
byte* pbSymmetricEncryptionSubkey = stackalloc byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)];
265+
try
266+
{
267+
fixed (byte* pbAdditionalAuthenticatedData = additionalAuthenticatedData)
268+
{
269+
_sp800_108_ctr_hmac_provider.DeriveKeyWithContextHeader(
270+
pbLabel: pbAdditionalAuthenticatedData,
271+
cbLabel: (uint)additionalAuthenticatedData.Length,
272+
contextHeader: _contextHeader,
273+
pbContext: pbKeyModifier,
274+
cbContext: KEY_MODIFIER_SIZE_IN_BYTES,
275+
pbDerivedKey: pbSymmetricEncryptionSubkey,
276+
cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes);
277+
}
278+
279+
// Perform the encryption operation
280+
fixed (byte* pbPlainText = plainText)
281+
{
282+
DoGcmEncrypt(
283+
pbKey: pbSymmetricEncryptionSubkey,
284+
cbKey: _symmetricAlgorithmSubkeyLengthInBytes,
285+
pbNonce: pbNonce,
286+
pbPlaintextData: pbPlainText,
287+
cbPlaintextData: (uint)plainText.Length,
288+
pbEncryptedData: pbEncryptedData,
289+
pbTag: pbAuthTag);
290+
}
291+
292+
// At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer }
293+
// And we're done!
294+
bytesWritten += plainText.Length + checked((int)TAG_SIZE_IN_BYTES);
295+
return true;
296+
}
297+
finally
298+
{
299+
// The buffer contains key material, so delete it.
300+
UnsafeBufferUtil.SecureZeroMemory(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes);
301+
}
302+
}
303+
}
304+
catch (Exception ex) when (ex.RequiresHomogenization())
305+
{
306+
throw Error.CryptCommon_GenericError(ex);
307+
}
243308
}
244309
#endif
245310

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Cng/GcmAuthenticatedEncryptorTests.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
88
using Microsoft.AspNetCore.DataProtection.Managed;
99
using Microsoft.AspNetCore.DataProtection.Test.Shared;
10+
using Microsoft.AspNetCore.DataProtection.Tests.Internal;
1011
using Microsoft.AspNetCore.InternalTesting;
1112

1213
namespace Microsoft.AspNetCore.DataProtection.Cng;
@@ -119,13 +120,6 @@ public void Roundtrip_CngGcm_TryEncryptDecrypt_CorrectlyEstimatesDataLength(int
119120
ArraySegment<byte> plaintext = new ArraySegment<byte>(Encoding.UTF8.GetBytes("plaintext"));
120121
ArraySegment<byte> aad = new ArraySegment<byte>(Encoding.UTF8.GetBytes("aad"));
121122

122-
var expectedSize = encryptor.GetEncryptedSize(plaintext.Count);
123-
124-
byte[] ciphertext = encryptor.Encrypt(plaintext, aad);
125-
Assert.Equal(expectedSize, ciphertext.Length);
126-
127-
byte[] decipheredtext = encryptor.Decrypt(new ArraySegment<byte>(ciphertext), aad);
128-
129-
Assert.Equal(plaintext.AsSpan(), decipheredtext.AsSpan());
123+
RoundtripEncryptionHelpers.AssertTryEncryptTryDecryptParity(encryptor, plaintext, aad);
130124
}
131125
}

0 commit comments

Comments
 (0)