Skip to content

Commit c029655

Browse files
committed
init decrypt
1 parent f223b45 commit c029655

File tree

5 files changed

+134
-134
lines changed

5 files changed

+134
-134
lines changed

src/DataProtection/DataProtection/src/AuthenticatedEncryption/ISpanAuthenticatedEncryptor.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ public interface ISpanAuthenticatedEncryptor : IAuthenticatedEncryptor
2121
/// <returns>The length of the encrypted data</returns>
2222
int GetEncryptedSize(int plainTextLength);
2323

24+
/// <summary>
25+
/// Returns the size of the decrypted data for a given ciphertext length.
26+
/// </summary>
27+
/// <param name="cipherTextLength">Length of the cipher text that will be decrypted later</param>
28+
/// <returns>The length of the decrypted data</returns>
29+
int GetDecryptedSize(int cipherTextLength);
30+
2431
/// <summary>
2532
/// Attempts to encrypt and tamper-proof a piece of data.
2633
/// </summary>
@@ -34,4 +41,6 @@ public interface ISpanAuthenticatedEncryptor : IAuthenticatedEncryptor
3441
/// <param name="bytesWritten">When this method returns, the total number of bytes written into destination</param>
3542
/// <returns>true if destination is long enough to receive the encrypted data; otherwise, false.</returns>
3643
bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten);
44+
45+
bool TryDecrypt(ReadOnlySpan<byte> cipherText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten);
3746
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@
77
using Microsoft.AspNetCore.Cryptography.Cng;
88
using Microsoft.AspNetCore.Cryptography.SafeHandles;
99
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
10-
using Microsoft.AspNetCore.DataProtection.Cng.Internal;
1110
using Microsoft.AspNetCore.DataProtection.SP800_108;
1211

1312
namespace Microsoft.AspNetCore.DataProtection.Cng;
1413

1514
// An encryptor which does Encrypt(CBC) + HMAC using the Windows CNG (BCrypt*) APIs.
1615
// The payloads produced by this encryptor should be compatible with the payloads
1716
// produced by the managed Encrypt(CBC) + HMAC encryptor.
18-
internal sealed unsafe class CbcAuthenticatedEncryptor : CngAuthenticatedEncryptorBase
17+
internal sealed unsafe class CbcAuthenticatedEncryptor : IOptimizedAuthenticatedEncryptor, ISpanAuthenticatedEncryptor, IDisposable
1918
{
2019
// Even when IVs are chosen randomly, CBC is susceptible to IV collisions within a single
2120
// key. For a 64-bit block cipher (like 3DES), we'd expect a collision after 2^32 block

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

Lines changed: 90 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.AspNetCore.Cryptography.Cng;
77
using Microsoft.AspNetCore.Cryptography.SafeHandles;
88
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
9-
using Microsoft.AspNetCore.DataProtection.Cng.Internal;
109
using Microsoft.AspNetCore.DataProtection.SP800_108;
1110

1211
namespace Microsoft.AspNetCore.DataProtection.Cng;
@@ -21,7 +20,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng;
2120
// going to the IV. This means that we'll only hit the 2^-32 probability limit after 2^96 encryption
2221
// operations, which will realistically never happen. (At the absurd rate of one encryption operation
2322
// per nanosecond, it would still take 180 times the age of the universe to hit 2^96 operations.)
24-
internal sealed unsafe class CngGcmAuthenticatedEncryptor : CngAuthenticatedEncryptorBase
23+
internal sealed unsafe class CngGcmAuthenticatedEncryptor : IOptimizedAuthenticatedEncryptor, ISpanAuthenticatedEncryptor, IDisposable
2524
{
2625
// Having a key modifier ensures with overwhelming probability that no two encryption operations
2726
// will ever derive the same (encryption subkey, MAC subkey) pair. This limits an attacker's
@@ -51,71 +50,22 @@ public CngGcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHand
5150
_contextHeader = CreateContextHeader();
5251
}
5352

54-
private byte[] CreateContextHeader()
53+
public int GetDecryptedSize(int cipherTextLength)
5554
{
56-
var retVal = new byte[checked(
57-
1 /* KDF alg */
58-
+ 1 /* chaining mode */
59-
+ sizeof(uint) /* sym alg key size */
60-
+ sizeof(uint) /* GCM nonce size */
61-
+ sizeof(uint) /* sym alg block size */
62-
+ sizeof(uint) /* GCM tag size */
63-
+ TAG_SIZE_IN_BYTES /* tag of GCM-encrypted empty string */)];
64-
65-
fixed (byte* pbRetVal = retVal)
66-
{
67-
byte* ptr = pbRetVal;
68-
69-
// First is the two-byte header
70-
*(ptr++) = 0; // 0x00 = SP800-108 CTR KDF w/ HMACSHA512 PRF
71-
*(ptr++) = 1; // 0x01 = GCM encryption + authentication
72-
73-
// Next is information about the symmetric algorithm (key size, nonce size, block size, tag size)
74-
BitHelpers.WriteTo(ref ptr, _symmetricAlgorithmSubkeyLengthInBytes);
75-
BitHelpers.WriteTo(ref ptr, NONCE_SIZE_IN_BYTES);
76-
BitHelpers.WriteTo(ref ptr, TAG_SIZE_IN_BYTES); // block size = tag size
77-
BitHelpers.WriteTo(ref ptr, TAG_SIZE_IN_BYTES);
78-
79-
// See the design document for an explanation of the following code.
80-
var tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
81-
fixed (byte* pbTempKeys = tempKeys)
82-
{
83-
byte dummy;
84-
85-
// Derive temporary key for encryption.
86-
using (var provider = SP800_108_CTR_HMACSHA512Util.CreateEmptyProvider())
87-
{
88-
provider.DeriveKey(
89-
pbLabel: &dummy,
90-
cbLabel: 0,
91-
pbContext: &dummy,
92-
cbContext: 0,
93-
pbDerivedKey: pbTempKeys,
94-
cbDerivedKey: (uint)tempKeys.Length);
95-
}
96-
97-
// Encrypt a zero-length input string with an all-zero nonce and copy the tag to the return buffer.
98-
byte* pbNonce = stackalloc byte[(int)NONCE_SIZE_IN_BYTES];
99-
UnsafeBufferUtil.SecureZeroMemory(pbNonce, NONCE_SIZE_IN_BYTES);
100-
DoGcmEncrypt(
101-
pbKey: pbTempKeys,
102-
cbKey: _symmetricAlgorithmSubkeyLengthInBytes,
103-
pbNonce: pbNonce,
104-
pbPlaintextData: &dummy,
105-
cbPlaintextData: 0,
106-
pbEncryptedData: &dummy,
107-
pbTag: ptr);
108-
}
55+
throw new NotImplementedException();
56+
}
10957

110-
ptr += TAG_SIZE_IN_BYTES;
111-
CryptoUtil.Assert(ptr - pbRetVal == retVal.Length, "ptr - pbRetVal == retVal.Length");
112-
}
58+
public bool TryDecrypt(ReadOnlySpan<byte> cipherText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)
59+
{
60+
throw new NotImplementedException();
61+
}
11362

114-
// retVal := { version || chainingMode || symAlgKeySize || nonceSize || symAlgBlockSize || symAlgTagSize || TAG-of-E("") }.
115-
return retVal;
63+
public byte[] Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte> additionalAuthenticatedData)
64+
{
65+
throw new NotImplementedException();
11666
}
11767

118-
protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData)
68+
protected byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData)
11969
{
12070
// Argument checking: input must at the absolute minimum contain a key modifier, nonce, and tag
12171
if (cbCiphertext < KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES)
@@ -192,14 +142,6 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt
192142
}
193143
}
194144

195-
public override void Dispose()
196-
{
197-
_sp800_108_ctr_hmac_provider.Dispose();
198-
199-
// We don't want to dispose of the underlying algorithm instances because they
200-
// might be reused.
201-
}
202-
203145
// 'pbNonce' must point to a 96-bit buffer.
204146
// 'pbTag' must point to a 128-bit buffer.
205147
// 'pbEncryptedData' must point to a buffer the same length as 'pbPlaintextData'.
@@ -231,14 +173,14 @@ private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaint
231173
}
232174
}
233175

234-
public override int GetEncryptedSize(int plainTextLength)
176+
public int GetEncryptedSize(int plainTextLength)
235177
{
236178
// A buffer to hold the key modifier, nonce, encrypted data, and tag.
237179
// In GCM, the encrypted output will be the same length as the plaintext input.
238180
return checked((int)(KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plainTextLength + TAG_SIZE_IN_BYTES));
239181
}
240182

241-
public override bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)
183+
public bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)
242184
{
243185
bytesWritten = 0;
244186

@@ -309,7 +251,10 @@ public override bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte>
309251
}
310252
}
311253

312-
public override byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
254+
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData)
255+
=> Encrypt(plaintext, additionalAuthenticatedData, 0, 0);
256+
257+
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
313258
{
314259
plaintext.Validate();
315260
additionalAuthenticatedData.Validate();
@@ -330,4 +275,76 @@ public override byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte>
330275
CryptoUtil.Assert(bytesWritten == size, "bytesWritten == size");
331276
return ciphertext;
332277
}
278+
279+
private byte[] CreateContextHeader()
280+
{
281+
var retVal = new byte[checked(
282+
1 /* KDF alg */
283+
+ 1 /* chaining mode */
284+
+ sizeof(uint) /* sym alg key size */
285+
+ sizeof(uint) /* GCM nonce size */
286+
+ sizeof(uint) /* sym alg block size */
287+
+ sizeof(uint) /* GCM tag size */
288+
+ TAG_SIZE_IN_BYTES /* tag of GCM-encrypted empty string */)];
289+
290+
fixed (byte* pbRetVal = retVal)
291+
{
292+
byte* ptr = pbRetVal;
293+
294+
// First is the two-byte header
295+
*(ptr++) = 0; // 0x00 = SP800-108 CTR KDF w/ HMACSHA512 PRF
296+
*(ptr++) = 1; // 0x01 = GCM encryption + authentication
297+
298+
// Next is information about the symmetric algorithm (key size, nonce size, block size, tag size)
299+
BitHelpers.WriteTo(ref ptr, _symmetricAlgorithmSubkeyLengthInBytes);
300+
BitHelpers.WriteTo(ref ptr, NONCE_SIZE_IN_BYTES);
301+
BitHelpers.WriteTo(ref ptr, TAG_SIZE_IN_BYTES); // block size = tag size
302+
BitHelpers.WriteTo(ref ptr, TAG_SIZE_IN_BYTES);
303+
304+
// See the design document for an explanation of the following code.
305+
var tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
306+
fixed (byte* pbTempKeys = tempKeys)
307+
{
308+
byte dummy;
309+
310+
// Derive temporary key for encryption.
311+
using (var provider = SP800_108_CTR_HMACSHA512Util.CreateEmptyProvider())
312+
{
313+
provider.DeriveKey(
314+
pbLabel: &dummy,
315+
cbLabel: 0,
316+
pbContext: &dummy,
317+
cbContext: 0,
318+
pbDerivedKey: pbTempKeys,
319+
cbDerivedKey: (uint)tempKeys.Length);
320+
}
321+
322+
// Encrypt a zero-length input string with an all-zero nonce and copy the tag to the return buffer.
323+
byte* pbNonce = stackalloc byte[(int)NONCE_SIZE_IN_BYTES];
324+
UnsafeBufferUtil.SecureZeroMemory(pbNonce, NONCE_SIZE_IN_BYTES);
325+
DoGcmEncrypt(
326+
pbKey: pbTempKeys,
327+
cbKey: _symmetricAlgorithmSubkeyLengthInBytes,
328+
pbNonce: pbNonce,
329+
pbPlaintextData: &dummy,
330+
cbPlaintextData: 0,
331+
pbEncryptedData: &dummy,
332+
pbTag: ptr);
333+
}
334+
335+
ptr += TAG_SIZE_IN_BYTES;
336+
CryptoUtil.Assert(ptr - pbRetVal == retVal.Length, "ptr - pbRetVal == retVal.Length");
337+
}
338+
339+
// retVal := { version || chainingMode || symAlgKeySize || nonceSize || symAlgBlockSize || symAlgTagSize || TAG-of-E("") }.
340+
return retVal;
341+
}
342+
343+
public void Dispose()
344+
{
345+
_sp800_108_ctr_hmac_provider.Dispose();
346+
347+
// We don't want to dispose of the underlying algorithm instances because they
348+
// might be reused.
349+
}
333350
}

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

Lines changed: 0 additions & 53 deletions
This file was deleted.

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

Lines changed: 34 additions & 6 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 Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
45
using Moq;
56

67
namespace Microsoft.AspNetCore.DataProtection.Cng.Internal;
@@ -82,19 +83,46 @@ public void Decrypt_HandlesEmptyCiphertextPointerFixup()
8283
Assert.Equal(new byte[] { 0x20, 0x21, 0x22 }, retVal);
8384
}
8485

85-
internal abstract class MockableEncryptor : CngAuthenticatedEncryptorBase
86+
internal abstract class MockableEncryptor : IOptimizedAuthenticatedEncryptor, ISpanAuthenticatedEncryptor, IDisposable
8687
{
87-
public override void Dispose()
88+
public abstract byte[] DecryptHook(IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData);
89+
public abstract byte[] EncryptHook(IntPtr pbPlaintext, uint cbPlaintext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer);
90+
91+
public int GetEncryptedSize(int plainTextLength)
8892
{
93+
throw new NotImplementedException();
8994
}
9095

91-
public abstract byte[] DecryptHook(IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData);
96+
public int GetDecryptedSize(int cipherTextLength)
97+
{
98+
throw new NotImplementedException();
99+
}
92100

93-
protected sealed override unsafe byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData)
101+
public bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)
94102
{
95-
return DecryptHook((IntPtr)pbCiphertext, cbCiphertext, (IntPtr)pbAdditionalAuthenticatedData, cbAdditionalAuthenticatedData);
103+
throw new NotImplementedException();
96104
}
97105

98-
public abstract byte[] EncryptHook(IntPtr pbPlaintext, uint cbPlaintext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer);
106+
public bool TryDecrypt(ReadOnlySpan<byte> cipherText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)
107+
{
108+
throw new NotImplementedException();
109+
}
110+
111+
public byte[] Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte> additionalAuthenticatedData)
112+
{
113+
throw new NotImplementedException();
114+
}
115+
116+
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData)
117+
{
118+
throw new NotImplementedException();
119+
}
120+
121+
public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
122+
{
123+
throw new NotImplementedException();
124+
}
125+
126+
public void Dispose() { }
99127
}
100128
}

0 commit comments

Comments
 (0)