Skip to content

Commit 146af98

Browse files
committed
use TryEncrypt from Encrypt()
1 parent d4b1fc9 commit 146af98

File tree

6 files changed

+45
-241
lines changed

6 files changed

+45
-241
lines changed

src/DataProtection/DataProtection/src/Cng/CbcAuthenticatedEncryptor.cs

Lines changed: 14 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -405,92 +405,25 @@ public override bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte>
405405
}
406406
}
407407

408-
protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer)
408+
public override byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
409409
{
410-
// This buffer will be used to hold the symmetric encryption and HMAC subkeys
411-
// used in the generation of this payload.
412-
var cbTempSubkeys = checked(_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes);
413-
byte* pbTempSubkeys = stackalloc byte[checked((int)cbTempSubkeys)];
414-
415-
try
416-
{
417-
// Randomly generate the key modifier and IV.
418-
var cbKeyModifierAndIV = checked(KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes);
419-
byte* pbKeyModifierAndIV = stackalloc byte[checked((int)cbKeyModifierAndIV)];
420-
_genRandom.GenRandom(pbKeyModifierAndIV, cbKeyModifierAndIV);
421-
422-
// Calculate offsets
423-
byte* pbKeyModifier = pbKeyModifierAndIV;
424-
byte* pbIV = &pbKeyModifierAndIV[KEY_MODIFIER_SIZE_IN_BYTES];
410+
plaintext.Validate();
411+
additionalAuthenticatedData.Validate();
425412

426-
// Use the KDF to generate a new symmetric encryption and HMAC subkey
427-
_sp800_108_ctr_hmac_provider.DeriveKeyWithContextHeader(
428-
pbLabel: pbAdditionalAuthenticatedData,
429-
cbLabel: cbAdditionalAuthenticatedData,
430-
contextHeader: _contextHeader,
431-
pbContext: pbKeyModifier,
432-
cbContext: KEY_MODIFIER_SIZE_IN_BYTES,
433-
pbDerivedKey: pbTempSubkeys,
434-
cbDerivedKey: cbTempSubkeys);
413+
var size = GetEncryptedSize(plaintext.Count);
414+
var ciphertext = new byte[preBufferSize + size + postBufferSize];
435415

436-
// Calculate offsets
437-
byte* pbSymmetricEncryptionSubkey = pbTempSubkeys;
438-
byte* pbHmacSubkey = &pbTempSubkeys[_symmetricAlgorithmSubkeyLengthInBytes];
439-
440-
using (var symmetricKeyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes))
441-
{
442-
// We can't assume PKCS#7 padding (maybe the underlying provider is really using CTS),
443-
// so we need to query the padded output size before we can allocate the return value array.
444-
var cbOutputCiphertext = GetCbcEncryptedOutputSizeWithPadding(symmetricKeyHandle, pbPlaintext, cbPlaintext);
445-
446-
// Allocate return value array and start copying some data
447-
var retVal = new byte[checked(cbPreBuffer + KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + cbOutputCiphertext + _hmacAlgorithmDigestLengthInBytes + cbPostBuffer)];
448-
fixed (byte* pbRetVal = retVal)
449-
{
450-
// Calculate offsets
451-
byte* pbOutputKeyModifier = &pbRetVal[cbPreBuffer];
452-
byte* pbOutputIV = &pbOutputKeyModifier[KEY_MODIFIER_SIZE_IN_BYTES];
453-
byte* pbOutputCiphertext = &pbOutputIV[_symmetricAlgorithmBlockSizeInBytes];
454-
byte* pbOutputHmac = &pbOutputCiphertext[cbOutputCiphertext];
455-
456-
UnsafeBufferUtil.BlockCopy(from: pbKeyModifierAndIV, to: pbOutputKeyModifier, byteCount: cbKeyModifierAndIV);
457-
458-
// retVal will eventually contain { preBuffer | keyModifier | iv | encryptedData | HMAC(iv | encryptedData) | postBuffer }
459-
// At this point, retVal := { preBuffer | keyModifier | iv | _____ | _____ | postBuffer }
460-
461-
DoCbcEncrypt(
462-
symmetricKeyHandle: symmetricKeyHandle,
463-
pbIV: pbIV,
464-
pbInput: pbPlaintext,
465-
cbInput: cbPlaintext,
466-
pbOutput: pbOutputCiphertext,
467-
cbOutput: cbOutputCiphertext);
468-
469-
// At this point, retVal := { preBuffer | keyModifier | iv | encryptedData | _____ | postBuffer }
470-
471-
// Compute the HMAC over the IV and the ciphertext (prevents IV tampering).
472-
// The HMAC is already implicitly computed over the key modifier since the key
473-
// modifier is used as input to the KDF.
474-
using (var hashHandle = _hmacAlgorithmHandle.CreateHmac(pbHmacSubkey, _hmacAlgorithmSubkeyLengthInBytes))
475-
{
476-
hashHandle.HashData(
477-
pbInput: pbOutputIV,
478-
cbInput: checked(_symmetricAlgorithmBlockSizeInBytes + cbOutputCiphertext),
479-
pbHashDigest: pbOutputHmac,
480-
cbHashDigest: _hmacAlgorithmDigestLengthInBytes);
481-
}
482-
483-
// At this point, retVal := { preBuffer | keyModifier | iv | encryptedData | HMAC(iv | encryptedData) | postBuffer }
484-
// And we're done!
485-
return retVal;
486-
}
487-
}
488-
}
489-
finally
416+
if (!TryEncrypt(
417+
plaintext: plaintext,
418+
additionalAuthenticatedData: additionalAuthenticatedData,
419+
destination: ciphertext,
420+
out var bytesWritten))
490421
{
491-
// Buffer contains sensitive material; delete it.
492-
UnsafeBufferUtil.SecureZeroMemory(pbTempSubkeys, cbTempSubkeys);
422+
throw Error.CryptCommon_GenericError(new ArgumentException("Not enough space in destination array"));
493423
}
424+
425+
CryptoUtil.Assert(bytesWritten == size, "bytesWritten == size");
426+
return ciphertext;
494427
}
495428

496429
/// <summary>

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

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -309,57 +309,24 @@ public override bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte>
309309
}
310310
}
311311

312-
protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer)
312+
public override byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
313313
{
314-
// Allocate a buffer to hold the key modifier, nonce, encrypted data, and tag.
315-
// In GCM, the encrypted output will be the same length as the plaintext input.
316-
var retVal = new byte[checked(cbPreBuffer + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + cbPlaintext + TAG_SIZE_IN_BYTES + cbPostBuffer)];
317-
fixed (byte* pbRetVal = retVal)
318-
{
319-
// Calculate offsets
320-
byte* pbKeyModifier = &pbRetVal[cbPreBuffer];
321-
byte* pbNonce = &pbKeyModifier[KEY_MODIFIER_SIZE_IN_BYTES];
322-
byte* pbEncryptedData = &pbNonce[NONCE_SIZE_IN_BYTES];
323-
byte* pbAuthTag = &pbEncryptedData[cbPlaintext];
324-
325-
// Randomly generate the key modifier and nonce
326-
_genRandom.GenRandom(pbKeyModifier, KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES);
314+
plaintext.Validate();
315+
additionalAuthenticatedData.Validate();
327316

328-
// At this point, retVal := { preBuffer | keyModifier | nonce | _____ | _____ | postBuffer }
317+
var size = GetEncryptedSize(plaintext.Count);
318+
var ciphertext = new byte[preBufferSize + size + postBufferSize];
329319

330-
// Use the KDF to generate a new symmetric block cipher key
331-
// We'll need a temporary buffer to hold the symmetric encryption subkey
332-
byte* pbSymmetricEncryptionSubkey = stackalloc byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)];
333-
try
334-
{
335-
_sp800_108_ctr_hmac_provider.DeriveKeyWithContextHeader(
336-
pbLabel: pbAdditionalAuthenticatedData,
337-
cbLabel: cbAdditionalAuthenticatedData,
338-
contextHeader: _contextHeader,
339-
pbContext: pbKeyModifier,
340-
cbContext: KEY_MODIFIER_SIZE_IN_BYTES,
341-
pbDerivedKey: pbSymmetricEncryptionSubkey,
342-
cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes);
343-
344-
// Perform the encryption operation
345-
DoGcmEncrypt(
346-
pbKey: pbSymmetricEncryptionSubkey,
347-
cbKey: _symmetricAlgorithmSubkeyLengthInBytes,
348-
pbNonce: pbNonce,
349-
pbPlaintextData: pbPlaintext,
350-
cbPlaintextData: cbPlaintext,
351-
pbEncryptedData: pbEncryptedData,
352-
pbTag: pbAuthTag);
353-
354-
// At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer }
355-
// And we're done!
356-
return retVal;
357-
}
358-
finally
359-
{
360-
// The buffer contains key material, so delete it.
361-
UnsafeBufferUtil.SecureZeroMemory(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes);
362-
}
320+
if (!TryEncrypt(
321+
plaintext: plaintext,
322+
additionalAuthenticatedData: additionalAuthenticatedData,
323+
destination: ciphertext,
324+
out var bytesWritten))
325+
{
326+
throw Error.CryptCommon_GenericError(new ArgumentException("Not enough space in destination array"));
363327
}
328+
329+
CryptoUtil.Assert(bytesWritten == size, "bytesWritten == size");
330+
return ciphertext;
364331
}
365332
}

src/DataProtection/DataProtection/src/Cng/Internal/CngAuthenticatedEncryptorBase.cs

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -45,44 +45,8 @@ public byte[] Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte> addition
4545

4646
public abstract void Dispose();
4747

48-
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData)
49-
{
50-
return Encrypt(plaintext, additionalAuthenticatedData, 0, 0);
51-
}
52-
53-
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
54-
{
55-
// This wrapper simply converts ArraySegment<byte> to byte* and calls the impl method.
56-
57-
// Input validation
58-
plaintext.Validate();
59-
additionalAuthenticatedData.Validate();
60-
61-
byte dummy; // used only if plaintext or AAD is empty, since otherwise 'fixed' returns null pointer
62-
fixed (byte* pbPlaintextArray = plaintext.Array)
63-
{
64-
fixed (byte* pbAdditionalAuthenticatedDataArray = additionalAuthenticatedData.Array)
65-
{
66-
try
67-
{
68-
return EncryptImpl(
69-
pbPlaintext: (pbPlaintextArray != null) ? &pbPlaintextArray[plaintext.Offset] : &dummy,
70-
cbPlaintext: (uint)plaintext.Count,
71-
pbAdditionalAuthenticatedData: (pbAdditionalAuthenticatedDataArray != null) ? &pbAdditionalAuthenticatedDataArray[additionalAuthenticatedData.Offset] : &dummy,
72-
cbAdditionalAuthenticatedData: (uint)additionalAuthenticatedData.Count,
73-
cbPreBuffer: preBufferSize,
74-
cbPostBuffer: postBufferSize);
75-
}
76-
catch (Exception ex) when (ex.RequiresHomogenization())
77-
{
78-
// Homogenize to CryptographicException.
79-
throw Error.CryptCommon_GenericError(ex);
80-
}
81-
}
82-
}
83-
}
84-
85-
protected abstract byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer);
48+
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData) => Encrypt(plaintext, additionalAuthenticatedData, 0, 0);
49+
public abstract byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize);
8650

8751
public abstract int GetEncryptedSize(int plainTextLength);
8852
public abstract bool TryEncrypt(ReadOnlySpan<byte> plainText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten);

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

Lines changed: 12 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -146,75 +146,20 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
146146
plaintext.Validate();
147147
additionalAuthenticatedData.Validate();
148148

149-
try
149+
var size = GetEncryptedSize(plaintext.Count);
150+
var ciphertext = new byte[preBufferSize + size + postBufferSize];
151+
152+
if (!TryEncrypt(
153+
plaintext: plaintext,
154+
additionalAuthenticatedData: additionalAuthenticatedData,
155+
destination: ciphertext,
156+
out var bytesWritten))
150157
{
151-
// Allocate a buffer to hold the key modifier, nonce, encrypted data, and tag.
152-
// In GCM, the encrypted output will be the same length as the plaintext input.
153-
var retVal = new byte[checked(preBufferSize + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plaintext.Count + TAG_SIZE_IN_BYTES + postBufferSize)];
154-
int keyModifierOffset; // position in ciphertext.Array where key modifier begins
155-
int nonceOffset; // position in ciphertext.Array where key modifier ends / nonce begins
156-
int encryptedDataOffset; // position in ciphertext.Array where nonce ends / encryptedData begins
157-
int tagOffset; // position in ciphertext.Array where encrypted data ends
158-
159-
checked
160-
{
161-
keyModifierOffset = plaintext.Offset + (int)preBufferSize;
162-
nonceOffset = keyModifierOffset + KEY_MODIFIER_SIZE_IN_BYTES;
163-
encryptedDataOffset = nonceOffset + NONCE_SIZE_IN_BYTES;
164-
tagOffset = encryptedDataOffset + plaintext.Count;
165-
}
166-
167-
// Randomly generate the key modifier and nonce
168-
var keyModifier = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES);
169-
var nonceBytes = _genRandom.GenRandom(NONCE_SIZE_IN_BYTES);
170-
171-
Buffer.BlockCopy(keyModifier, 0, retVal, (int)preBufferSize, keyModifier.Length);
172-
Buffer.BlockCopy(nonceBytes, 0, retVal, (int)preBufferSize + keyModifier.Length, nonceBytes.Length);
173-
174-
// At this point, retVal := { preBuffer | keyModifier | nonce | _____ | _____ | postBuffer }
175-
176-
// Use the KDF to generate a new symmetric block cipher key
177-
// We'll need a temporary buffer to hold the symmetric encryption subkey
178-
var decryptedKdk = new byte[_keyDerivationKey.Length];
179-
var derivedKey = new byte[_derivedkeySizeInBytes];
180-
fixed (byte* __unused__1 = decryptedKdk)
181-
fixed (byte* __unused__2 = derivedKey)
182-
{
183-
try
184-
{
185-
_keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment<byte>(decryptedKdk));
186-
ManagedSP800_108_CTR_HMACSHA512.DeriveKeys(
187-
kdk: decryptedKdk,
188-
label: additionalAuthenticatedData,
189-
contextHeader: _contextHeader,
190-
contextData: keyModifier,
191-
operationSubkey: derivedKey,
192-
validationSubkey: Span<byte>.Empty /* filling in derivedKey only */ );
193-
194-
// do gcm
195-
var nonce = new Span<byte>(retVal, nonceOffset, NONCE_SIZE_IN_BYTES);
196-
var tag = new Span<byte>(retVal, tagOffset, TAG_SIZE_IN_BYTES);
197-
var encrypted = new Span<byte>(retVal, encryptedDataOffset, plaintext.Count);
198-
using var aes = new AesGcm(derivedKey, TAG_SIZE_IN_BYTES);
199-
aes.Encrypt(nonce, plaintext, encrypted, tag);
200-
201-
// At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer }
202-
// And we're done!
203-
return retVal;
204-
}
205-
finally
206-
{
207-
// delete since these contain secret material
208-
Array.Clear(decryptedKdk, 0, decryptedKdk.Length);
209-
Array.Clear(derivedKey, 0, derivedKey.Length);
210-
}
211-
}
212-
}
213-
catch (Exception ex) when (ex.RequiresHomogenization())
214-
{
215-
// Homogenize all exceptions to CryptographicException.
216-
throw Error.CryptCommon_GenericError(ex);
158+
throw Error.CryptCommon_GenericError(new ArgumentException("Not enough space in destination array"));
217159
}
160+
161+
CryptoUtil.Assert(bytesWritten == size, "bytesWritten == size");
162+
return ciphertext;
218163
}
219164

220165
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,9 +423,9 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
423423
var size = GetEncryptedSize(plainTextSpan.Length);
424424
var retVal = new byte[size];
425425

426-
if (!TryEncrypt(plainTextSpan, additionalAuthenticatedData.AsSpan(), retVal, out var bytesWritten))
426+
if (!TryEncrypt(plainTextSpan, additionalAuthenticatedData, retVal, out var bytesWritten))
427427
{
428-
throw Error.CryptCommon_PayloadInvalid();
428+
throw Error.CryptCommon_GenericError(new ArgumentException("Not enough space in destination array"));
429429
}
430430

431431
return retVal;

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,5 @@ protected sealed override unsafe byte[] DecryptImpl(byte* pbCiphertext, uint cbC
9696
}
9797

9898
public abstract byte[] EncryptHook(IntPtr pbPlaintext, uint cbPlaintext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer);
99-
100-
protected sealed override unsafe byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer)
101-
{
102-
return EncryptHook((IntPtr)pbPlaintext, cbPlaintext, (IntPtr)pbAdditionalAuthenticatedData, cbAdditionalAuthenticatedData, cbPreBuffer, cbPostBuffer);
103-
}
10499
}
105100
}

0 commit comments

Comments
 (0)