-
Notifications
You must be signed in to change notification settings - Fork 10.4k
feat: "Spanify" DataProtector #62903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds new non-allocating "Span-based" APIs to the DataProtection interfaces, specifically implementing the Protect/Encrypt functionality without requiring heap allocations. The new APIs allow callers to get the size of protected data upfront and then encrypt directly into a provided buffer.
Key changes:
- Added
GetProtectedSize()
andTryProtect()
methods toIDataProtector
interface - Added
GetEncryptedSize()
andTryEncrypt()
methods toIAuthenticatedEncryptor
interface - Implemented these methods across all encryptor types (AES-GCM, CBC, CNG variants, Managed implementations)
Reviewed Changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
src/DataProtection/Abstractions/src/IDataProtector.cs | Added new span-based APIs to the main data protector interface |
src/DataProtection/DataProtection/src/AuthenticatedEncryption/IAuthenticatedEncryptor.cs | Added new span-based APIs to the authenticated encryptor interface |
src/DataProtection/DataProtection/src/KeyManagement/KeyRingBasedDataProtector.cs | Core implementation of new APIs with proper header management |
src/DataProtection/DataProtection/src/Managed/ManagedAuthenticatedEncryptor.cs | Implementation for managed (non-CNG) encryption algorithms |
src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs | Implementation for AES-GCM encryption |
src/DataProtection/DataProtection/src/Cng/CbcAuthenticatedEncryptor.cs | Implementation for CNG-based CBC encryption |
src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs | Implementation for CNG-based GCM encryption |
src/DataProtection/Extensions/src/TimeLimitedDataProtector.cs | Updated time-limited protector to support new APIs |
Multiple test files | Comprehensive test coverage for the new functionality |
src/DataProtection/DataProtection/src/Managed/ManagedAuthenticatedEncryptor.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should update our existing code that calls into IDataProtecter to use the new methods to get good test coverage of the new methods.
@martincostello that was the initial idea - to improve Antiforgery and other places where DataProtection is used under the hood - for example Blazor. I have some starters for Antiforgery perf improvements, but this one was the biggest blocker to make an alloc-free flow and I hope this and follow-up changes will help there |
Please, review with caution! This is cryptography and failures here can be pretty dramatic...
This PR introduces 2 new interfaces which allow usage of DataProtection via
Span<byte>
:ISpanDataProtector
andIAuthenticatedEncryptor
.Details
Current PR proposes extra interfaces
ISpanDataProtector
andISpanAuthenticatedEncryptor
(Inspiration taken from ISpanFormattable : IFormattable). The APIs are:and
From the implementation standpoint, these APIs are basically doing what existing
Protect() / Unprotect()
andEncrypt() / Decrypt()
do, but without allocating a result array and instead filling in theSpan<byte> destination
.For
IAuthenticatedEncryptor
s I tried to reuse theTryEncrypt
/TryDecrypt
fromEncrypt
/Decrypt
where possible to reduce code duplication.Registrations
IDataProtector
also should implementIDataProtectionProvider
which provides the protector instance:And in default DI (
AddDataProtection
)IDataProtector
is created in such a manner:https://github.com/dotnet/aspnetcore/blob/b52c80fef2b8bf5239c7643d9c79d1029bb6c988/src/DataProtection/DataProtection/src/DataProtectionServiceCollectionExtensions.cs#L89C13-L95C14
I dont want to introduce more APIs and interfaces for providing
ISpanDataProtector
and instead when providing I am doing a lookup to determine whether it is possible to returnISpanDataProtector
orIDataProtector
. Users will then be able to differentiate based on their needs and registrations.Usage
Having
IDataProtector
:and the same goes for
IAuthenticatedEncryptor
, but I doubt majority of users implement their ownIAuthenticatedEncryptor
.Testing
In order to verify correctness of the changes I've created
RoundtripEncryptionHelpers.AssertTryEncryptTryDecryptParity
where I try to call Encrypt/Decrypt in different order forIDataProtector
andIAuthenticatedEncryptor
to ensure any order of operations give a correct roundtrip result.I will also change any other place in aspnetcore to use this API to make sure new APIs behave correctly and in the same way as existing APIs
Fixes #44758