Skip to content

Commit 37a3277

Browse files
committed
inheritance!
1 parent 948052c commit 37a3277

File tree

2 files changed

+108
-475
lines changed

2 files changed

+108
-475
lines changed

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

Lines changed: 95 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@
44
using System;
55
using System.Buffers;
66
using System.Buffers.Binary;
7-
using System.Buffers.Text;
87
using System.Collections.Generic;
98
using System.Diagnostics;
10-
using System.Diagnostics.CodeAnalysis;
11-
using System.IO;
129
using System.Linq;
1310
using System.Runtime.CompilerServices;
1411
using System.Threading;
@@ -28,11 +25,13 @@ internal unsafe class KeyRingBasedDataProtector : IDataProtector, IPersistedData
2825
// The last nibble reserved for version information. There's also the nice property that "F0 C9"
2926
// can never appear in a well-formed UTF8 sequence, so attempts to treat a protected payload as a
3027
// UTF8-encoded string will fail, and devs can catch the mistake early.
31-
private const uint MAGIC_HEADER_V0 = 0x09F0C9F0;
28+
protected const uint MAGIC_HEADER_V0 = 0x09F0C9F0;
3229

33-
private AdditionalAuthenticatedDataTemplate _aadTemplate;
34-
private readonly IKeyRingProvider _keyRingProvider;
35-
private readonly ILogger? _logger;
30+
protected AdditionalAuthenticatedDataTemplate _aadTemplate;
31+
protected readonly IKeyRingProvider _keyRingProvider;
32+
protected readonly ILogger? _logger;
33+
34+
protected static readonly int _magicHeaderKeyIdSize = sizeof(uint) + sizeof(Guid);
3635

3736
public KeyRingBasedDataProtector(IKeyRingProvider keyRingProvider, ILogger? logger, string[]? originalPurposes, string newPurpose)
3837
{
@@ -46,6 +45,9 @@ public KeyRingBasedDataProtector(IKeyRingProvider keyRingProvider, ILogger? logg
4645

4746
internal string[] Purposes { get; }
4847

48+
protected IKeyRingProvider KeyRingProvider => _keyRingProvider;
49+
protected ILogger? Logger => _logger;
50+
4951
private static string[] ConcatPurposes(string[]? originalPurposes, string newPurpose)
5052
{
5153
if (originalPurposes != null && originalPurposes.Length > 0)
@@ -61,55 +63,47 @@ private static string[] ConcatPurposes(string[]? originalPurposes, string newPur
6163
}
6264
}
6365

64-
public IDataProtector CreateProtector(string purpose)
66+
public virtual IDataProtector CreateProtector(string purpose)
6567
{
6668
ArgumentNullThrowHelper.ThrowIfNull(purpose);
6769

6870
return new KeyRingBasedDataProtector(
69-
logger: _logger,
70-
keyRingProvider: _keyRingProvider,
71+
logger: Logger,
72+
keyRingProvider: KeyRingProvider,
7173
originalPurposes: Purposes,
7274
newPurpose: purpose);
7375
}
7476

75-
private static string JoinPurposesForLog(IEnumerable<string> purposes)
77+
protected static string JoinPurposesForLog(IEnumerable<string> purposes)
7678
{
7779
return "(" + String.Join(", ", purposes.Select(p => "'" + p + "'")) + ")";
7880
}
7981

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

93-
public byte[] Protect(byte[] plaintext)
87+
protected byte[] ProtectCore(byte[] plaintext)
9488
{
9589
ArgumentNullThrowHelper.ThrowIfNull(plaintext);
9690

9791
try
9892
{
9993
// Perform the encryption operation using the current default encryptor.
100-
var currentKeyRing = _keyRingProvider.GetCurrentKeyRing();
94+
var currentKeyRing = KeyRingProvider.GetCurrentKeyRing();
10195
var defaultKeyId = currentKeyRing.DefaultKeyId;
10296
var defaultEncryptorInstance = currentKeyRing.DefaultAuthenticatedEncryptor;
10397
CryptoUtil.Assert(defaultEncryptorInstance != null, "defaultEncryptorInstance != null");
10498

105-
if (_logger.IsDebugLevelEnabled())
99+
if (Logger.IsDebugLevelEnabled())
106100
{
107-
_logger.PerformingProtectOperationToKeyWithPurposes(defaultKeyId, JoinPurposesForLog(Purposes));
101+
Logger.PerformingProtectOperationToKeyWithPurposes(defaultKeyId, JoinPurposesForLog(Purposes));
108102
}
109103

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

114108
// We allocate a 20-byte pre-buffer so that we can inject the magic header and key id into the return value.
115109
var retVal = defaultEncryptorInstance.Encrypt(
@@ -140,54 +134,29 @@ public byte[] Protect(byte[] plaintext)
140134
}
141135
}
142136

143-
private static Guid ReadGuid(void* ptr)
137+
protected byte[] UnprotectCore(byte[] protectedData)
144138
{
145-
#if NETCOREAPP
146-
// Performs appropriate endianness fixups
147-
return new Guid(new ReadOnlySpan<byte>(ptr, sizeof(Guid)));
148-
#elif NETSTANDARD2_0 || NETFRAMEWORK
149-
Debug.Assert(BitConverter.IsLittleEndian);
150-
return Unsafe.ReadUnaligned<Guid>(ptr);
151-
#else
152-
#error Update target frameworks
153-
#endif
154-
}
155-
156-
private static uint ReadBigEndian32BitInteger(byte* ptr)
157-
{
158-
return ((uint)ptr[0] << 24)
159-
| ((uint)ptr[1] << 16)
160-
| ((uint)ptr[2] << 8)
161-
| ((uint)ptr[3]);
162-
}
139+
ArgumentNullThrowHelper.ThrowIfNull(protectedData);
163140

164-
private static bool TryGetVersionFromMagicHeader(uint magicHeader, out int version)
165-
{
166-
const uint MAGIC_HEADER_VERSION_MASK = 0xFU;
167-
if ((magicHeader & ~MAGIC_HEADER_VERSION_MASK) == MAGIC_HEADER_V0)
168-
{
169-
version = (int)(magicHeader & MAGIC_HEADER_VERSION_MASK);
170-
return true;
171-
}
172-
else
173-
{
174-
version = default(int);
175-
return false;
176-
}
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;
177145
}
178146

179-
public byte[] Unprotect(byte[] protectedData)
147+
protected byte[] DangerousUnprotectCore(byte[] protectedData, bool ignoreRevocationErrors, out bool requiresMigration, out bool wasRevoked)
180148
{
149+
// argument & state checking
181150
ArgumentNullThrowHelper.ThrowIfNull(protectedData);
182151

183-
// Argument checking will be done by the callee
184-
return DangerousUnprotect(protectedData,
185-
ignoreRevocationErrors: false,
186-
requiresMigration: out _,
187-
wasRevoked: out _);
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;
188157
}
189158

190-
private byte[] UnprotectCore(byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
159+
protected byte[] UnprotectCoreInternal(byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
191160
{
192161
Debug.Assert(protectedData != null);
193162

@@ -293,7 +262,59 @@ private byte[] UnprotectCore(byte[] protectedData, bool allowOperationsOnRevoked
293262
}
294263
}
295264

296-
private static void WriteGuid(void* ptr, Guid value)
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+
317+
protected static void WriteGuid(void* ptr, Guid value)
297318
{
298319
#if NETCOREAPP
299320
var span = new Span<byte>(ptr, sizeof(Guid));
@@ -309,14 +330,21 @@ private static void WriteGuid(void* ptr, Guid value)
309330
#endif
310331
}
311332

312-
private static void WriteBigEndianInteger(byte* ptr, uint value)
333+
protected static void WriteBigEndianInteger(byte* ptr, uint value)
313334
{
314335
ptr[0] = (byte)(value >> 24);
315336
ptr[1] = (byte)(value >> 16);
316337
ptr[2] = (byte)(value >> 8);
317338
ptr[3] = (byte)(value);
318339
}
319340

341+
protected enum UnprotectStatus
342+
{
343+
Ok,
344+
DefaultEncryptionKeyChanged,
345+
DecryptionKeyWasRevoked
346+
}
347+
320348
internal struct AdditionalAuthenticatedDataTemplate
321349
{
322350
private byte[] _aadTemplate;
@@ -412,11 +440,4 @@ internal static byte[] BuildAadTemplateBytes(string[] purposes)
412440
return targetArr;
413441
}
414442
}
415-
416-
private enum UnprotectStatus
417-
{
418-
Ok,
419-
DefaultEncryptionKeyChanged,
420-
DecryptionKeyWasRevoked
421-
}
422443
}

0 commit comments

Comments
 (0)