Skip to content

Commit eb6cb1b

Browse files
committed
try with encrypt
1 parent 268ee44 commit eb6cb1b

File tree

4 files changed

+159
-61
lines changed

4 files changed

+159
-61
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
5+
46
namespace Microsoft.AspNetCore.DataProtection.Managed;
57

68
internal interface IManagedGenRandom
79
{
810
byte[] GenRandom(int numBytes);
11+
12+
#if NET10_0_OR_GREATER
13+
void GenRandom(Span<byte> target);
14+
#endif
915
}

src/DataProtection/DataProtection/src/Managed/ManagedAuthenticatedEncryptor.cs

Lines changed: 139 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.IO;
6+
using System.Linq;
67
using System.Security.Cryptography;
78
using Microsoft.AspNetCore.Cryptography;
89
using 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
}

src/DataProtection/DataProtection/src/Managed/ManagedGenRandomImpl.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using System.Security.Cryptography;
56

67
namespace Microsoft.AspNetCore.DataProtection.Managed;
@@ -16,6 +17,10 @@ private ManagedGenRandomImpl()
1617
{
1718
}
1819

20+
#if NET10_0_OR_GREATER
21+
public void GenRandom(Span<byte> target) => RandomNumberGenerator.Fill(target);
22+
#endif
23+
1924
public byte[] GenRandom(int numBytes)
2025
{
2126
var bytes = new byte[numBytes];

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/SequentialGenRandom.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using Microsoft.AspNetCore.DataProtection.Cng;
56
using Microsoft.AspNetCore.DataProtection.Managed;
67

@@ -27,4 +28,12 @@ public void GenRandom(byte* pbBuffer, uint cbBuffer)
2728
pbBuffer[i] = _value++;
2829
}
2930
}
31+
32+
public void GenRandom(Span<byte> target)
33+
{
34+
for (var i = 0; i < target.Length; i++)
35+
{
36+
target[i] = _value++;
37+
}
38+
}
3039
}

0 commit comments

Comments
 (0)