@@ -16,17 +16,14 @@ namespace Microsoft.AspNetCore.DataProtection;
1616/// Wraps an existing <see cref="IDataProtector"/> and appends a purpose that allows 
1717/// protecting data with a finite lifetime. 
1818/// </summary> 
19- internal  sealed  class  TimeLimitedDataProtector  :  ITimeLimitedDataProtector 
20- #if NET10_0_OR_GREATER
21-     ,  ISpanDataProtector 
22- #endif
19+ internal  class  TimeLimitedDataProtector  :  ITimeLimitedDataProtector 
2320{ 
2421    private  const  string  MyPurposeString  =  "Microsoft.AspNetCore.DataProtection.TimeLimitedDataProtector.v1" ; 
2522
26-     private  readonly  IDataProtector  _innerProtector ; 
2723    private  IDataProtector ?  _innerProtectorWithTimeLimitedPurpose ;  // created on-demand 
24+     protected  readonly  IDataProtector  _innerProtector ; 
2825
29-     private  const  int  ExpirationTimeHeaderSize  =  8 ;  // size of the expiration time header in bytes (64-bit UTC tick count) 
26+     protected  const  int  ExpirationTimeHeaderSize  =  8 ;  // size of the expiration time header in bytes (64-bit UTC tick count) 
3027
3128    public  TimeLimitedDataProtector ( IDataProtector  innerProtector ) 
3229    { 
@@ -37,10 +34,16 @@ public ITimeLimitedDataProtector CreateProtector(string purpose)
3734    { 
3835        ArgumentNullThrowHelper . ThrowIfNull ( purpose ) ; 
3936
40-         return  new  TimeLimitedDataProtector ( _innerProtector . CreateProtector ( purpose ) ) ; 
37+         var  protector  =  _innerProtector . CreateProtector ( purpose ) ; 
38+         if  ( protector  is  ISpanDataProtector  spanDataProtector ) 
39+         { 
40+             return  new  TimeLimitedSpanDataProtector ( spanDataProtector ) ; 
41+         } 
42+ 
43+         return  new  TimeLimitedDataProtector ( protector ) ; 
4144    } 
4245
43-     private  IDataProtector  GetInnerProtectorWithTimeLimitedPurpose ( ) 
46+     protected  IDataProtector  GetInnerProtectorWithTimeLimitedPurpose ( ) 
4447    { 
4548        // thread-safe lazy init pattern with multi-execution and single publication 
4649        var  retVal  =  Volatile . Read ( ref  _innerProtectorWithTimeLimitedPurpose ) ; 
@@ -132,57 +135,4 @@ byte[] IDataProtector.Unprotect(byte[] protectedData)
132135
133136        return  Unprotect ( protectedData ,  out  _ ) ; 
134137    } 
135- 
136- #if NET10_0_OR_GREATER 
137-     public  bool  TryGetProtectedSize ( ReadOnlySpan < byte >  plainText ,  out  int  cipherTextLength ) 
138-     { 
139-         var  dataProtector  =  GetInnerProtectorWithTimeLimitedPurpose ( ) ; 
140-         if  ( dataProtector  is  ISpanDataProtector  optimizedDataProtector ) 
141-         { 
142-             var  result  =  optimizedDataProtector . TryGetProtectedSize ( plainText ,  out  cipherTextLength ) ; 
143- 
144-             // prepended the expiration time as a 64-bit UTC tick count takes ExpirationTimeHeaderSize bytes; 
145-             // see Protect(byte[] plaintext, DateTimeOffset expiration) for details 
146-             cipherTextLength  +=  ExpirationTimeHeaderSize ; 
147-             return  result ; 
148-         } 
149- 
150-         cipherTextLength  =  default ; 
151-         return  false ; 
152-     } 
153- 
154-     public  bool  TryProtect ( ReadOnlySpan < byte >  plaintext ,  Span < byte >  destination ,  out  int  bytesWritten ) 
155-         =>  TryProtect ( plaintext ,  destination ,  DateTimeOffset . MaxValue ,  out  bytesWritten ) ; 
156- 
157-     public  bool  TryProtect ( ReadOnlySpan < byte >  plaintext ,  Span < byte >  destination ,  DateTimeOffset  expiration ,  out  int  bytesWritten ) 
158-     { 
159-         if  ( _innerProtector  is  not ISpanDataProtector  optimizedDataProtector ) 
160-         { 
161-             throw  new  NotSupportedException ( "The inner protector does not support optimized data protection." ) ; 
162-         } 
163- 
164-         // we need to prepend the expiration time, so we need to allocate a buffer for the plaintext with header 
165-         byte [ ] ?  plainTextWithHeader  =  null ; 
166-         try 
167-         { 
168-             plainTextWithHeader  =  ArrayPool < byte > . Shared . Rent ( plaintext . Length  +  ExpirationTimeHeaderSize ) ; 
169-             var  plainTextWithHeaderSpan  =  plainTextWithHeader . AsSpan ( 0 ,  plaintext . Length  +  ExpirationTimeHeaderSize ) ; 
170- 
171-             // We prepend the expiration time (as a 64-bit UTC tick count) to the unprotected data. 
172-             BitHelpers . WriteUInt64 ( plainTextWithHeaderSpan ,  0 ,  ( ulong ) expiration . UtcTicks ) ; 
173- 
174-             // and copy the plaintext into the buffer 
175-             plaintext . CopyTo ( plainTextWithHeaderSpan . Slice ( ExpirationTimeHeaderSize ) ) ; 
176- 
177-             return  optimizedDataProtector . TryProtect ( plainTextWithHeaderSpan ,  destination ,  out  bytesWritten ) ; 
178-         } 
179-         finally 
180-         { 
181-             if  ( plainTextWithHeader  is  not null ) 
182-             { 
183-                 ArrayPool < byte > . Shared . Return ( plainTextWithHeader ) ; 
184-             } 
185-         } 
186-     } 
187- #endif
188138} 
0 commit comments