Skip to content

Commit 0e1dfd6

Browse files
committed
separate impl
1 parent 37a3277 commit 0e1dfd6

File tree

2 files changed

+83
-123
lines changed

2 files changed

+83
-123
lines changed

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

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

44
using System;
5-
using System.Buffers;
65
using System.Buffers.Binary;
6+
using System.Buffers;
77
using System.Collections.Generic;
88
using System.Diagnostics;
9+
using System.Diagnostics.CodeAnalysis;
10+
using System.IO;
911
using System.Linq;
1012
using System.Runtime.CompilerServices;
1113
using System.Threading;
1214
using Microsoft.AspNetCore.Cryptography;
1315
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
14-
using Microsoft.AspNetCore.DataProtection.Internal;
1516
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
1617
using Microsoft.AspNetCore.Shared;
1718
using Microsoft.Extensions.Logging;
19+
using System.Buffers.Text;
20+
using Microsoft.AspNetCore.DataProtection.Internal;
1821

1922
namespace Microsoft.AspNetCore.DataProtection.KeyManagement;
2023

@@ -26,13 +29,12 @@ internal unsafe class KeyRingBasedDataProtector : IDataProtector, IPersistedData
2629
// can never appear in a well-formed UTF8 sequence, so attempts to treat a protected payload as a
2730
// UTF8-encoded string will fail, and devs can catch the mistake early.
2831
protected const uint MAGIC_HEADER_V0 = 0x09F0C9F0;
32+
protected static readonly int _magicHeaderKeyIdSize = sizeof(uint) + sizeof(Guid);
2933

3034
protected AdditionalAuthenticatedDataTemplate _aadTemplate;
3135
protected readonly IKeyRingProvider _keyRingProvider;
3236
protected readonly ILogger? _logger;
3337

34-
protected static readonly int _magicHeaderKeyIdSize = sizeof(uint) + sizeof(Guid);
35-
3638
public KeyRingBasedDataProtector(IKeyRingProvider keyRingProvider, ILogger? logger, string[]? originalPurposes, string newPurpose)
3739
{
3840
Debug.Assert(keyRingProvider != null);
@@ -45,9 +47,6 @@ public KeyRingBasedDataProtector(IKeyRingProvider keyRingProvider, ILogger? logg
4547

4648
internal string[] Purposes { get; }
4749

48-
protected IKeyRingProvider KeyRingProvider => _keyRingProvider;
49-
protected ILogger? Logger => _logger;
50-
5150
private static string[] ConcatPurposes(string[]? originalPurposes, string newPurpose)
5251
{
5352
if (originalPurposes != null && originalPurposes.Length > 0)
@@ -68,8 +67,8 @@ public virtual IDataProtector CreateProtector(string purpose)
6867
ArgumentNullThrowHelper.ThrowIfNull(purpose);
6968

7069
return new KeyRingBasedDataProtector(
71-
logger: Logger,
72-
keyRingProvider: KeyRingProvider,
70+
logger: _logger,
71+
keyRingProvider: _keyRingProvider,
7372
originalPurposes: Purposes,
7473
newPurpose: purpose);
7574
}
@@ -79,31 +78,39 @@ protected static string JoinPurposesForLog(IEnumerable<string> purposes)
7978
return "(" + String.Join(", ", purposes.Select(p => "'" + p + "'")) + ")";
8079
}
8180

82-
protected byte[] GetAadForKey(Guid keyId, bool isProtecting)
81+
// allows decrypting payloads whose keys have been revoked
82+
public byte[] DangerousUnprotect(byte[] protectedData, bool ignoreRevocationErrors, out bool requiresMigration, out bool wasRevoked)
8383
{
84-
return _aadTemplate.GetAadForKey(keyId, isProtecting);
84+
// argument & state checking
85+
ArgumentNullThrowHelper.ThrowIfNull(protectedData);
86+
87+
UnprotectStatus status;
88+
var retVal = UnprotectCore(protectedData, ignoreRevocationErrors, status: out status);
89+
requiresMigration = (status != UnprotectStatus.Ok);
90+
wasRevoked = (status == UnprotectStatus.DecryptionKeyWasRevoked);
91+
return retVal;
8592
}
8693

87-
protected byte[] ProtectCore(byte[] plaintext)
94+
public byte[] Protect(byte[] plaintext)
8895
{
8996
ArgumentNullThrowHelper.ThrowIfNull(plaintext);
9097

9198
try
9299
{
93100
// Perform the encryption operation using the current default encryptor.
94-
var currentKeyRing = KeyRingProvider.GetCurrentKeyRing();
101+
var currentKeyRing = _keyRingProvider.GetCurrentKeyRing();
95102
var defaultKeyId = currentKeyRing.DefaultKeyId;
96103
var defaultEncryptorInstance = currentKeyRing.DefaultAuthenticatedEncryptor;
97104
CryptoUtil.Assert(defaultEncryptorInstance != null, "defaultEncryptorInstance != null");
98105

99-
if (Logger.IsDebugLevelEnabled())
106+
if (_logger.IsDebugLevelEnabled())
100107
{
101-
Logger.PerformingProtectOperationToKeyWithPurposes(defaultKeyId, JoinPurposesForLog(Purposes));
108+
_logger.PerformingProtectOperationToKeyWithPurposes(defaultKeyId, JoinPurposesForLog(Purposes));
102109
}
103110

104111
// We'll need to apply the default key id to the template if it hasn't already been applied.
105112
// If the default key id has been updated since the last call to Protect, also write back the updated template.
106-
var aad = GetAadForKey(defaultKeyId, isProtecting: true);
113+
var aad = _aadTemplate.GetAadForKey(defaultKeyId, isProtecting: true);
107114

108115
// We allocate a 20-byte pre-buffer so that we can inject the magic header and key id into the return value.
109116
var retVal = defaultEncryptorInstance.Encrypt(
@@ -134,29 +141,54 @@ protected byte[] ProtectCore(byte[] plaintext)
134141
}
135142
}
136143

137-
protected byte[] UnprotectCore(byte[] protectedData)
144+
private static Guid ReadGuid(void* ptr)
138145
{
139-
ArgumentNullThrowHelper.ThrowIfNull(protectedData);
146+
#if NETCOREAPP
147+
// Performs appropriate endianness fixups
148+
return new Guid(new ReadOnlySpan<byte>(ptr, sizeof(Guid)));
149+
#elif NETSTANDARD2_0 || NETFRAMEWORK
150+
Debug.Assert(BitConverter.IsLittleEndian);
151+
return Unsafe.ReadUnaligned<Guid>(ptr);
152+
#else
153+
#error Update target frameworks
154+
#endif
155+
}
140156

141-
// Argument checking will be done by the callee
142-
UnprotectStatus status;
143-
var retVal = UnprotectCoreInternal(protectedData, allowOperationsOnRevokedKeys: false, status: out status);
144-
return retVal;
157+
private static uint ReadBigEndian32BitInteger(byte* ptr)
158+
{
159+
return ((uint)ptr[0] << 24)
160+
| ((uint)ptr[1] << 16)
161+
| ((uint)ptr[2] << 8)
162+
| ((uint)ptr[3]);
145163
}
146164

147-
protected byte[] DangerousUnprotectCore(byte[] protectedData, bool ignoreRevocationErrors, out bool requiresMigration, out bool wasRevoked)
165+
private static bool TryGetVersionFromMagicHeader(uint magicHeader, out int version)
166+
{
167+
const uint MAGIC_HEADER_VERSION_MASK = 0xFU;
168+
if ((magicHeader & ~MAGIC_HEADER_VERSION_MASK) == MAGIC_HEADER_V0)
169+
{
170+
version = (int)(magicHeader & MAGIC_HEADER_VERSION_MASK);
171+
return true;
172+
}
173+
else
174+
{
175+
version = default(int);
176+
return false;
177+
}
178+
}
179+
180+
public byte[] Unprotect(byte[] protectedData)
148181
{
149-
// argument & state checking
150182
ArgumentNullThrowHelper.ThrowIfNull(protectedData);
151183

152-
UnprotectStatus status;
153-
var retVal = UnprotectCoreInternal(protectedData, ignoreRevocationErrors, status: out status);
154-
requiresMigration = (status != UnprotectStatus.Ok);
155-
wasRevoked = (status == UnprotectStatus.DecryptionKeyWasRevoked);
156-
return retVal;
184+
// Argument checking will be done by the callee
185+
return DangerousUnprotect(protectedData,
186+
ignoreRevocationErrors: false,
187+
requiresMigration: out _,
188+
wasRevoked: out _);
157189
}
158190

159-
protected byte[] UnprotectCoreInternal(byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
191+
private byte[] UnprotectCore(byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
160192
{
161193
Debug.Assert(protectedData != null);
162194

@@ -262,58 +294,6 @@ protected byte[] UnprotectCoreInternal(byte[] protectedData, bool allowOperation
262294
}
263295
}
264296

265-
// allows decrypting payloads whose keys have been revoked
266-
public byte[] DangerousUnprotect(byte[] protectedData, bool ignoreRevocationErrors, out bool requiresMigration, out bool wasRevoked)
267-
{
268-
return DangerousUnprotectCore(protectedData, ignoreRevocationErrors, out requiresMigration, out wasRevoked);
269-
}
270-
271-
public byte[] Protect(byte[] plaintext)
272-
{
273-
return ProtectCore(plaintext);
274-
}
275-
276-
public byte[] Unprotect(byte[] protectedData)
277-
{
278-
return UnprotectCore(protectedData);
279-
}
280-
281-
protected static Guid ReadGuid(void* ptr)
282-
{
283-
#if NETCOREAPP
284-
// Performs appropriate endianness fixups
285-
return new Guid(new ReadOnlySpan<byte>(ptr, sizeof(Guid)));
286-
#elif NETSTANDARD2_0 || NETFRAMEWORK
287-
Debug.Assert(BitConverter.IsLittleEndian);
288-
return Unsafe.ReadUnaligned<Guid>(ptr);
289-
#else
290-
#error Update target frameworks
291-
#endif
292-
}
293-
294-
protected static uint ReadBigEndian32BitInteger(byte* ptr)
295-
{
296-
return ((uint)ptr[0] << 24)
297-
| ((uint)ptr[1] << 16)
298-
| ((uint)ptr[2] << 8)
299-
| ((uint)ptr[3]);
300-
}
301-
302-
protected static bool TryGetVersionFromMagicHeader(uint magicHeader, out int version)
303-
{
304-
const uint MAGIC_HEADER_VERSION_MASK = 0xFU;
305-
if ((magicHeader & ~MAGIC_HEADER_VERSION_MASK) == MAGIC_HEADER_V0)
306-
{
307-
version = (int)(magicHeader & MAGIC_HEADER_VERSION_MASK);
308-
return true;
309-
}
310-
else
311-
{
312-
version = default(int);
313-
return false;
314-
}
315-
}
316-
317297
protected static void WriteGuid(void* ptr, Guid value)
318298
{
319299
#if NETCOREAPP
@@ -338,13 +318,6 @@ protected static void WriteBigEndianInteger(byte* ptr, uint value)
338318
ptr[3] = (byte)(value);
339319
}
340320

341-
protected enum UnprotectStatus
342-
{
343-
Ok,
344-
DefaultEncryptionKeyChanged,
345-
DecryptionKeyWasRevoked
346-
}
347-
348321
internal struct AdditionalAuthenticatedDataTemplate
349322
{
350323
private byte[] _aadTemplate;
@@ -440,4 +413,11 @@ internal static byte[] BuildAadTemplateBytes(string[] purposes)
440413
return targetArr;
441414
}
442415
}
416+
417+
private enum UnprotectStatus
418+
{
419+
Ok,
420+
DefaultEncryptionKeyChanged,
421+
DecryptionKeyWasRevoked
422+
}
443423
}

src/DataProtection/DataProtection/src/KeyManagement/KeyRingBasedSpanDataProtector.cs

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24,58 +24,47 @@ public override IDataProtector CreateProtector(string purpose)
2424
ArgumentNullThrowHelper.ThrowIfNull(purpose);
2525

2626
return new KeyRingBasedDataProtector(
27-
logger: Logger,
28-
keyRingProvider: KeyRingProvider,
27+
logger: _logger,
28+
keyRingProvider: _keyRingProvider,
2929
originalPurposes: Purposes,
3030
newPurpose: purpose);
3131
}
3232

33-
public bool TryGetProtectedSize(ReadOnlySpan<byte> plainText, out int cipherTextLength)
33+
public int GetProtectedSize(ReadOnlySpan<byte> plainText)
3434
{
35-
cipherTextLength = default;
36-
3735
// Get the current key ring to access the encryptor
38-
var currentKeyRing = KeyRingProvider.GetCurrentKeyRing();
39-
var defaultEncryptor = currentKeyRing.DefaultAuthenticatedEncryptor;
40-
if (defaultEncryptor is not ISpanAuthenticatedEncryptor optimizedAuthenticatedEncryptor)
41-
{
42-
return false;
43-
}
44-
CryptoUtil.Assert(optimizedAuthenticatedEncryptor != null, "optimizedAuthenticatedEncryptor != null");
36+
var currentKeyRing = _keyRingProvider.GetCurrentKeyRing();
37+
var defaultEncryptor = (ISpanAuthenticatedEncryptor)currentKeyRing.DefaultAuthenticatedEncryptor!;
38+
CryptoUtil.Assert(defaultEncryptor != null, "DefaultAuthenticatedEncryptor != null");
4539

4640
// We allocate a 20-byte pre-buffer so that we can inject the magic header and key id into the return value.
4741
// See Protect() / TryProtect() for details
48-
cipherTextLength = _magicHeaderKeyIdSize + optimizedAuthenticatedEncryptor.GetEncryptedSize(plainText.Length);
49-
return true;
42+
return _magicHeaderKeyIdSize + defaultEncryptor.GetEncryptedSize(plainText.Length);
5043
}
5144

5245
public bool TryProtect(ReadOnlySpan<byte> plaintext, Span<byte> destination, out int bytesWritten)
5346
{
5447
try
5548
{
5649
// Perform the encryption operation using the current default encryptor.
57-
var currentKeyRing = KeyRingProvider.GetCurrentKeyRing();
50+
var currentKeyRing = _keyRingProvider.GetCurrentKeyRing();
5851
var defaultKeyId = currentKeyRing.DefaultKeyId;
59-
var defaultEncryptor = currentKeyRing.DefaultAuthenticatedEncryptor;
60-
if (defaultEncryptor is not ISpanAuthenticatedEncryptor spanEncryptor)
61-
{
62-
throw new NotSupportedException("The current default encryptor does not support optimized protection.");
63-
}
64-
CryptoUtil.Assert(spanEncryptor != null, "optimizedAuthenticatedEncryptor != null");
52+
var defaultEncryptor = (ISpanAuthenticatedEncryptor)currentKeyRing.DefaultAuthenticatedEncryptor!;
53+
CryptoUtil.Assert(defaultEncryptor != null, "DefaultAuthenticatedEncryptor != null");
6554

66-
if (Logger.IsDebugLevelEnabled())
55+
if (_logger.IsDebugLevelEnabled())
6756
{
68-
Logger.PerformingProtectOperationToKeyWithPurposes(defaultKeyId, JoinPurposesForLog(Purposes));
57+
_logger.PerformingProtectOperationToKeyWithPurposes(defaultKeyId, JoinPurposesForLog(Purposes));
6958
}
7059

7160
// We'll need to apply the default key id to the template if it hasn't already been applied.
7261
// If the default key id has been updated since the last call to Protect, also write back the updated template.
73-
var aad = GetAadForKey(defaultKeyId, isProtecting: true);
62+
var aad = _aadTemplate.GetAadForKey(defaultKeyId, isProtecting: true);
7463

7564
var preBufferSize = _magicHeaderKeyIdSize;
7665
var postBufferSize = 0;
7766
var destinationBufferOffsets = destination.Slice(preBufferSize, destination.Length - (preBufferSize + postBufferSize));
78-
var success = spanEncryptor.TryEncrypt(plaintext, aad, destinationBufferOffsets, out bytesWritten);
67+
var success = defaultEncryptor.TryEncrypt(plaintext, aad, destinationBufferOffsets, out bytesWritten);
7968

8069
// At this point: destination := { 000..000 || encryptorSpecificProtectedPayload },
8170
// where 000..000 is a placeholder for our magic header and key id.
@@ -105,13 +94,4 @@ public bool TryProtect(ReadOnlySpan<byte> plaintext, Span<byte> destination, out
10594
throw Error.Common_EncryptionFailed(ex);
10695
}
10796
}
108-
109-
public int GetProtectedSize(ReadOnlySpan<byte> plainText)
110-
{
111-
if (!TryGetProtectedSize(plainText, out int cipherTextLength))
112-
{
113-
throw new NotSupportedException("The current default encryptor does not support optimized protection.");
114-
}
115-
return cipherTextLength;
116-
}
11797
}

0 commit comments

Comments
 (0)