Skip to content

Commit afd344c

Browse files
committed
refactor
1 parent 4eb96e3 commit afd344c

File tree

8 files changed

+61
-24
lines changed

8 files changed

+61
-24
lines changed

src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorExtensions.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ public static bool TryEncrypt(
3838
ReadOnlySpan<byte> plaintext,
3939
ReadOnlySpan<byte> additionalAuthenticatedData,
4040
Span<byte> destination,
41-
uint preBufferSize,
42-
uint postBufferSize,
41+
int preBufferSize,
42+
int postBufferSize,
4343
out int bytesWritten)
4444
{
45-
var plaintextWithOffsets = plaintext.Slice((int)preBufferSize, plaintext.Length - (int)(preBufferSize + postBufferSize));
46-
return encryptor.TryEncrypt(plaintextWithOffsets, additionalAuthenticatedData, destination, out bytesWritten);
45+
var destinationBufferOffsets = destination.Slice(preBufferSize, destination.Length - (preBufferSize + postBufferSize));
46+
// var plaintextWithOffsets = plaintext.Slice((int)preBufferSize, plaintext.Length - (int)(preBufferSize + postBufferSize));
47+
return encryptor.TryEncrypt(plaintext, additionalAuthenticatedData, destinationBufferOffsets, out bytesWritten);
4748
}
4849
#endif
4950

src/DataProtection/DataProtection/src/AuthenticatedEncryption/IOptimizedAuthenticatedEncryptor.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,4 @@ internal interface IOptimizedAuthenticatedEncryptor : IAuthenticatedEncryptor
3838
/// All cryptography-related exceptions should be homogenized to CryptographicException.
3939
/// </remarks>
4040
byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize);
41-
42-
#if NET10_0_OR_GREATER
43-
int GetEncryptedSize(int plainTextLength, uint preBufferSize, uint postBufferSize);
44-
#endif
4541
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,10 @@ private void DoCbcEncrypt(BCryptKeyHandle symmetricKeyHandle, byte* pbIV, byte*
300300
}
301301

302302
#if NET10_0_OR_GREATER
303-
public override int GetEncryptedSize(int plainTextLength, uint preBufferSize, uint postBufferSize)
303+
public override int GetEncryptedSize(int plainTextLength)
304304
{
305305
uint paddedCiphertextLength = GetCbcEncryptedOutputSizeWithPadding((uint)plainTextLength);
306-
return checked((int)(preBufferSize + KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + paddedCiphertextLength + _hmacAlgorithmDigestLengthInBytes + postBufferSize));
306+
return checked((int)(KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + paddedCiphertextLength + _hmacAlgorithmDigestLengthInBytes));
307307
}
308308

309309
public override bool TryEncrypt(ReadOnlySpan<byte> plainText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,11 @@ private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaint
232232
}
233233

234234
#if NET10_0_OR_GREATER
235-
public override int GetEncryptedSize(int plainTextLength, uint preBufferSize, uint postBufferSize)
235+
public override int GetEncryptedSize(int plainTextLength)
236236
{
237237
// A buffer to hold the key modifier, nonce, encrypted data, and tag.
238238
// In GCM, the encrypted output will be the same length as the plaintext input.
239-
return checked((int)(preBufferSize + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plainTextLength + TAG_SIZE_IN_BYTES + postBufferSize));
239+
return checked((int)(KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plainTextLength + TAG_SIZE_IN_BYTES));
240240
}
241241

242242
public override bool TryEncrypt(ReadOnlySpan<byte> plainText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,7 @@ public byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additiona
8585
protected abstract byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer);
8686

8787
#if NET10_0_OR_GREATER
88-
public int GetEncryptedSize(int plainTextLength) => GetEncryptedSize(plainTextLength, 0, 0);
89-
public abstract int GetEncryptedSize(int plainTextLength, uint preBufferSize, uint postBufferSize);
88+
public abstract int GetEncryptedSize(int plainTextLength);
9089
public abstract bool TryEncrypt(ReadOnlySpan<byte> plainText, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten);
9190
#endif
9291
}

src/DataProtection/DataProtection/src/KeyManagement/KeyRingBasedDataProtector.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,24 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Buffers.Binary;
65
using System.Buffers;
6+
using System.Buffers.Binary;
7+
using System.Buffers.Text;
78
using System.Collections.Generic;
89
using System.Diagnostics;
910
using System.Diagnostics.CodeAnalysis;
1011
using System.IO;
1112
using System.Linq;
13+
using System.Reflection.PortableExecutable;
1214
using System.Runtime.CompilerServices;
1315
using System.Threading;
1416
using Microsoft.AspNetCore.Cryptography;
1517
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
18+
using Microsoft.AspNetCore.DataProtection.Internal;
1619
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
1720
using Microsoft.AspNetCore.Shared;
1821
using Microsoft.Extensions.Logging;
19-
using System.Buffers.Text;
20-
using Microsoft.AspNetCore.DataProtection.Internal;
22+
using static System.Runtime.InteropServices.JavaScript.JSType;
2123

2224
namespace Microsoft.AspNetCore.DataProtection.KeyManagement;
2325

@@ -34,6 +36,8 @@ internal sealed unsafe class KeyRingBasedDataProtector : IDataProtector, IPersis
3436
private readonly IKeyRingProvider _keyRingProvider;
3537
private readonly ILogger? _logger;
3638

39+
private static readonly int _magicHeaderKeyIdSize = (sizeof(uint) + sizeof(Guid));
40+
3741
public KeyRingBasedDataProtector(IKeyRingProvider keyRingProvider, ILogger? logger, string[]? originalPurposes, string newPurpose)
3842
{
3943
Debug.Assert(keyRingProvider != null);
@@ -98,7 +102,9 @@ public int GetProtectedSize(ReadOnlySpan<byte> plainText)
98102
var defaultEncryptor = currentKeyRing.DefaultAuthenticatedEncryptor;
99103
CryptoUtil.Assert(defaultEncryptor != null, "defaultEncryptorInstance != null");
100104

101-
return defaultEncryptor.GetEncryptedSize(plainText.Length);
105+
// We allocate a 20-byte pre-buffer so that we can inject the magic header and key id into the return value.
106+
// See Protect() / TryProtect() for details
107+
return _magicHeaderKeyIdSize + defaultEncryptor.GetEncryptedSize(plainText.Length);
102108
}
103109

104110
public bool TryProtect(ReadOnlySpan<byte> plaintext, Span<byte> destination, out int bytesWritten)
@@ -125,7 +131,7 @@ public bool TryProtect(ReadOnlySpan<byte> plaintext, Span<byte> destination, out
125131
plaintext: plaintext,
126132
additionalAuthenticatedData: aad,
127133
destination: destination,
128-
preBufferSize: (uint)(sizeof(uint) + sizeof(Guid)),
134+
preBufferSize: _magicHeaderKeyIdSize,
129135
postBufferSize: 0,
130136
out bytesWritten);
131137

@@ -136,6 +142,7 @@ public bool TryProtect(ReadOnlySpan<byte> plaintext, Span<byte> destination, out
136142
BinaryPrimitives.WriteUInt32BigEndian(destination.Slice(0, sizeof(uint)), MAGIC_HEADER_V0);
137143
var writeKeyIdResult = defaultKeyId.TryWriteBytes(destination.Slice(sizeof(uint), sizeof(Guid)));
138144
Debug.Assert(writeKeyIdResult, "Failed to write Guid to destination.");
145+
bytesWritten += _magicHeaderKeyIdSize;
139146

140147
// At this point, destination := { magicHeader || keyId || encryptorSpecificProtectedPayload }
141148
// And we're done!

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,10 @@ public byte[] Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte> addition
142142
}
143143

144144
public int GetEncryptedSize(int plainTextLength)
145-
=> GetEncryptedSize(plainTextLength, 0, 0);
146-
147-
public int GetEncryptedSize(int plainTextLength, uint preBufferSize, uint postBufferSize)
148145
{
149146
// A buffer to hold the key modifier, nonce, encrypted data, and tag.
150147
// 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));
148+
return checked((int)(KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plainTextLength + TAG_SIZE_IN_BYTES));
152149
}
153150

154151
public bool TryEncrypt(ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> additionalAuthenticatedData, Span<byte> destination, out int bytesWritten)

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/KeyManagement/KeyRingBasedDataProtectorTests.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,44 @@ public void CreateProtector_ChainsPurposes()
623623
[Fact]
624624
public void GetProtectedSize_TryProtect_CorrectlyEstimatesDataLength()
625625
{
626-
// TODO!
626+
// Arrange
627+
byte[] plaintext = new byte[] { 0x10, 0x20, 0x30, 0x40, 0x50 };
628+
var encryptorFactory = new AuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
629+
Key key = new Key(Guid.NewGuid(), DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, new AuthenticatedEncryptorConfiguration().CreateNewDescriptor(), new[] { encryptorFactory });
630+
var keyRing = new KeyRing(key, new[] { key });
631+
var mockKeyRingProvider = new Mock<IKeyRingProvider>();
632+
mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(keyRing);
633+
634+
var protector = new KeyRingBasedDataProtector(
635+
keyRingProvider: mockKeyRingProvider.Object,
636+
logger: GetLogger(),
637+
originalPurposes: null,
638+
newPurpose: "purpose");
639+
640+
// Act - get estimated size
641+
int estimatedSize = protector.GetProtectedSize(plaintext);
642+
643+
// Act - allocate buffer and try protect
644+
byte[] destination = new byte[estimatedSize];
645+
bool success = protector.TryProtect(plaintext, destination, out int bytesWritten);
646+
647+
// Assert
648+
Assert.True(success, "TryProtect should succeed with estimated buffer size");
649+
Assert.Equal(estimatedSize, bytesWritten);
650+
Assert.True(bytesWritten > 0, "Should write some bytes");
651+
Assert.True(bytesWritten >= plaintext.Length, "Protected data should be at least as large as plaintext");
652+
653+
// Verify the protected data can be unprotected to get original plaintext
654+
byte[] actualDestination = new byte[bytesWritten];
655+
Array.Copy(destination, actualDestination, bytesWritten);
656+
byte[] unprotectedData = protector.Unprotect(actualDestination);
657+
Assert.Equal(plaintext, unprotectedData);
658+
659+
// Test with buffer that's too small
660+
byte[] smallBuffer = new byte[estimatedSize - 1];
661+
bool smallBufferSuccess = protector.TryProtect(plaintext, smallBuffer, out int smallBufferBytesWritten);
662+
Assert.False(smallBufferSuccess, "TryProtect should fail with buffer that's too small");
663+
Assert.Equal(0, smallBufferBytesWritten);
627664
}
628665

629666
private static byte[] BuildAadFromPurposeStrings(Guid keyId, params string[] purposes)

0 commit comments

Comments
 (0)