Skip to content

Commit 6142309

Browse files
committed
introduce IOptimizedDataProtector
1 parent efd4f79 commit 6142309

File tree

5 files changed

+80
-25
lines changed

5 files changed

+80
-25
lines changed

src/DataProtection/Abstractions/src/IDataProtector.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,4 @@ public interface IDataProtector : IDataProtectionProvider
2727
/// Thrown if the protected data is invalid or malformed.
2828
/// </exception>
2929
byte[] Unprotect(byte[] protectedData);
30-
31-
#if NET10_0_OR_GREATER
32-
/// <summary>
33-
/// Returns the size of the encrypted data for a given plaintext length.
34-
/// </summary>
35-
/// <param name="plainText">The plain text that will be encrypted later</param>
36-
/// <returns>The length of the encrypted data</returns>
37-
int GetProtectedSize(ReadOnlySpan<byte> plainText);
38-
39-
/// <summary>
40-
/// Attempts to encrypt and tamper-proof a piece of data.
41-
/// </summary>
42-
/// <param name="plainText">The input to encrypt.</param>
43-
/// <param name="destination">The ciphertext blob, including authentication tag.</param>
44-
/// <param name="bytesWritten">When this method returns, the total number of bytes written into destination</param>
45-
/// <returns>true if destination is long enough to receive the encrypted data; otherwise, false.</returns>
46-
bool TryProtect(ReadOnlySpan<byte> plainText, Span<byte> destination, out int bytesWritten);
47-
#endif
4830
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace Microsoft.AspNetCore.DataProtection;
11+
12+
#if NET10_0_OR_GREATER
13+
14+
/// <summary>
15+
/// An interface that can provide data protection services.
16+
/// Is an optimized version of <see cref="IDataProtector"/>.
17+
/// </summary>
18+
public interface IOptimizedDataProtector : IDataProtector
19+
{
20+
/// <summary>
21+
/// Returns the size of the encrypted data for a given plaintext length.
22+
/// </summary>
23+
/// <param name="plainText">The plain text that will be encrypted later</param>
24+
/// <returns>The length of the encrypted data</returns>
25+
int GetProtectedSize(ReadOnlySpan<byte> plainText);
26+
27+
/// <summary>
28+
/// Attempts to encrypt and tamper-proof a piece of data.
29+
/// </summary>
30+
/// <param name="plainText">The input to encrypt.</param>
31+
/// <param name="destination">The ciphertext blob, including authentication tag.</param>
32+
/// <param name="bytesWritten">When this method returns, the total number of bytes written into destination</param>
33+
/// <returns>true if destination is long enough to receive the encrypted data; otherwise, false.</returns>
34+
bool TryProtect(ReadOnlySpan<byte> plainText, Span<byte> destination, out int bytesWritten);
35+
}
36+
37+
#endif

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
namespace Microsoft.AspNetCore.DataProtection.KeyManagement;
2323

2424
internal sealed unsafe class KeyRingBasedDataProtector : IDataProtector, IPersistedDataProtector
25+
#if NET10_0_OR_GREATER
26+
, IOptimizedDataProtector
27+
#endif
2528
{
2629
// This magic header identifies a v0 protected data blob. It's the high 28 bits of the SHA1 hash of
2730
// "Microsoft.AspNet.DataProtection.KeyManagement.KeyRingBasedDataProtector" [US-ASCII], big-endian.

src/DataProtection/Extensions/src/DataProtectionAdvancedExtensions.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ public static string Unprotect(this ITimeLimitedDataProtector protector, string
9797
}
9898

9999
private sealed class TimeLimitedWrappingProtector : IDataProtector
100+
#if NET10_0_OR_GREATER
101+
, IOptimizedDataProtector
102+
#endif
100103
{
101104
public DateTimeOffset Expiration;
102105
private readonly ITimeLimitedDataProtector _innerProtector;
@@ -128,8 +131,25 @@ public byte[] Unprotect(byte[] protectedData)
128131
}
129132

130133
#if NET10_0_OR_GREATER
131-
public int GetProtectedSize(ReadOnlySpan<byte> plainText) => _innerProtector.GetProtectedSize(plainText);
132-
public bool TryProtect(ReadOnlySpan<byte> plainText, Span<byte> destination, out int bytesWritten) => _innerProtector.TryProtect(plainText, destination, out bytesWritten);
134+
public int GetProtectedSize(ReadOnlySpan<byte> plainText)
135+
{
136+
if (_innerProtector is IOptimizedDataProtector optimizedDataProtector)
137+
{
138+
return optimizedDataProtector.GetProtectedSize(plainText);
139+
}
140+
141+
throw new NotSupportedException("The inner protector does not support optimized data protection.");
142+
}
143+
144+
public bool TryProtect(ReadOnlySpan<byte> plainText, Span<byte> destination, out int bytesWritten)
145+
{
146+
if (_innerProtector is IOptimizedDataProtector optimizedDataProtector)
147+
{
148+
return optimizedDataProtector.TryProtect(plainText, destination, out bytesWritten);
149+
}
150+
151+
throw new NotSupportedException("The inner protector does not support optimized data protection.");
152+
}
133153
#endif
134154
}
135155
}

src/DataProtection/Extensions/src/TimeLimitedDataProtector.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ namespace Microsoft.AspNetCore.DataProtection;
1717
/// protecting data with a finite lifetime.
1818
/// </summary>
1919
internal sealed class TimeLimitedDataProtector : ITimeLimitedDataProtector
20+
#if NET10_0_OR_GREATER
21+
, IOptimizedDataProtector
22+
#endif
2023
{
2124
private const string MyPurposeString = "Microsoft.AspNetCore.DataProtection.TimeLimitedDataProtector.v1";
2225

@@ -134,18 +137,28 @@ byte[] IDataProtector.Unprotect(byte[] protectedData)
134137
public int GetProtectedSize(ReadOnlySpan<byte> plainText)
135138
{
136139
var dataProtector = GetInnerProtectorWithTimeLimitedPurpose();
137-
var size = dataProtector.GetProtectedSize(plainText);
140+
if (dataProtector is IOptimizedDataProtector optimizedDataProtector)
141+
{
142+
var size = optimizedDataProtector.GetProtectedSize(plainText);
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+
return size + ExpirationTimeHeaderSize;
147+
}
138148

139-
// prepended the expiration time as a 64-bit UTC tick count takes ExpirationTimeHeaderSize bytes;
140-
// see Protect(byte[] plaintext, DateTimeOffset expiration) for details
141-
return size + ExpirationTimeHeaderSize;
149+
throw new NotSupportedException("The inner protector does not support optimized data protection.");
142150
}
143151

144152
public bool TryProtect(ReadOnlySpan<byte> plaintext, Span<byte> destination, out int bytesWritten)
145153
=> TryProtect(plaintext, destination, DateTimeOffset.MaxValue, out bytesWritten);
146154

147155
public bool TryProtect(ReadOnlySpan<byte> plaintext, Span<byte> destination, DateTimeOffset expiration, out int bytesWritten)
148156
{
157+
if (_innerProtector is not IOptimizedDataProtector optimizedDataProtector)
158+
{
159+
throw new NotSupportedException("The inner protector does not support optimized data protection.");
160+
}
161+
149162
// we need to prepend the expiration time, so we need to allocate a buffer for the plaintext with header
150163
byte[]? plainTextWithHeader = null;
151164
try
@@ -159,7 +172,7 @@ public bool TryProtect(ReadOnlySpan<byte> plaintext, Span<byte> destination, Dat
159172
// and copy the plaintext into the buffer
160173
plaintext.CopyTo(plainTextWithHeaderSpan.Slice(ExpirationTimeHeaderSize));
161174

162-
return _innerProtector.TryProtect(plainTextWithHeaderSpan, destination, out bytesWritten);
175+
return optimizedDataProtector.TryProtect(plainTextWithHeaderSpan, destination, out bytesWritten);
163176
}
164177
finally
165178
{

0 commit comments

Comments
 (0)