44using System ;
55using System . Buffers ;
66using System . Buffers . Binary ;
7- using System . Buffers . Text ;
87using System . Collections . Generic ;
98using System . Diagnostics ;
10- using System . Diagnostics . CodeAnalysis ;
11- using System . IO ;
129using System . Linq ;
1310using System . Runtime . CompilerServices ;
1411using 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