22// The .NET Foundation licenses this file to you under the MIT license.
33
44using System ;
5- using System . Buffers ;
65using System . Buffers . Binary ;
6+ using System . Buffers ;
77using System . Collections . Generic ;
88using System . Diagnostics ;
9+ using System . Diagnostics . CodeAnalysis ;
10+ using System . IO ;
911using System . Linq ;
1012using System . Runtime . CompilerServices ;
1113using System . Threading ;
1214using Microsoft . AspNetCore . Cryptography ;
1315using Microsoft . AspNetCore . DataProtection . AuthenticatedEncryption ;
14- using Microsoft . AspNetCore . DataProtection . Internal ;
1516using Microsoft . AspNetCore . DataProtection . KeyManagement . Internal ;
1617using Microsoft . AspNetCore . Shared ;
1718using Microsoft . Extensions . Logging ;
19+ using System . Buffers . Text ;
20+ using Microsoft . AspNetCore . DataProtection . Internal ;
1821
1922namespace 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}
0 commit comments