-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[API Proposal]: add Span<byte> key
parameter to SymmetricAlgorithm.EncryptCbc
and SymmetricAlgorithm.DecryptCbc
#111154
Comments
We definitely can't make it be static methods on SymmetricAlgorithm... we don't know what algorithm to use at that layer. It's even tricky making it static methods on Aes, et al, since we'd be picking a single provider implementation for how we're going to accomplish it. And then it's tricky as instance methods because for an Aes instance that's backed by a hardware key, it doesn't make sense to specify a key per-call. Looking at the comment in PR that suggested it, I wonder if we just want something like public partial class SymmetricAlgorithm
{
/// <summary>
/// Sets the key to use with this instance, but blocks it from being returned from the
/// <see cref="Key" /> property.
/// </summary>
public void SetOpaqueKey(ReadOnlySpan<byte> key);
protected virtual void SetOpaqueKeyCore(ReadOnlySpan<byte> key);
} It would probably have to default to throwing, but we'd wire it up on all of our types. (When overriding the member, a derived type should do whatever they need to do to make |
Are we able to even reasonably implement it? If I recall correctly, some platforms need the original key between each final transformation (CFB on macOS is ringing a bell) because the underlying OS-thing can't be reset. So in probably more than a few cases we are just going to behind the scenes convert a |
Levi's comment that inspired this proposal seems to simplify as "calling set_Key makes a copy you can't clear, and that's bad, mm'kay?". So even just |
Why do we need a new API for this? Couldn't we just override It does mean the |
Be....cause..... uh.... maybe.... he... also.... meant "and no one should be able to get it back?" If exportability isn't a concern, then, yeah, we could just change our set_Key on sealed/hidden types to avoid the KeyValue field, and our TryEncryptCbc (and friends) to do use the keybox instead of get_Key. |
It sounds like we could change our internals to work on the key box regardless. If we want a new API to prevent retrieval, that's fine, but I don't see a reason to not use the key box for the get / set. |
"I bet I know what Kevin's doing this weekend... oh, wait, today's Tuesday." |
10:30 PM, weekend, same thing.. We still might want a new API because
is more efficient than
Since the key box just wants contiguous memory, not having a new API would force an allocation. |
I'm not even remotely qualified to opine on any part of the implementation pieces, but observation on this part: public byte[] DecryptCbc(ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> iv, PaddingMode paddingMode = PaddingMode.PKCS7);
+ public static byte[] DecryptCbc(ReadOnlySpan<byte> key, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> iv, PaddingMode paddingMode = PaddingMode.PKCS7); If there is any possible mechanism to avoid that |
@vcsjones I openly confess my lack of familiarity with this API, but: can you clarify why we would still need a |
I think we first need to sort out “Can or do we want to even have an API that accepts the key as part of the one shot”. There are some edge cases that make that hard, like Jeremy suggested that the answer might be “no”, no I am actually not sure that makes the suggested idea disqualifying. The API could be: public partial class Aes {
public static int EncryptCbc(
ReadOnlySpan<byte> plaintext,
ReadOnlySpan<byte> key,
ReadOnlySpan<byte> iv,
Span<byte> destination,
PaddingMode paddingMode = PaddingMode.PKCS7);
// Repeat for byte[], int overloads, and Decrypt, and Cfb and Ecb, and other algorithms.
} That is at least implementable but we are talking about dozens of new APIs. Being a static though has the disadvantage that it isn’t really extendable. These algorithms are not sealed, and many other developers have derived from these types to add their own implementations. They could just shadow the static with
The one-shots as they exist today already have the usual “bool TryFoo” and “int Foo” overloads so if another one-shot is added we could certainly write to an existing buffer. |
Thanks for explaining @vcsjones, I dont have any expertise in a field, but I find runtime/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs Line 1320 in fba5430
But implementations of runtime/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HMACSHA512.cs Line 114 in fba5430
Maybe if the cases are similar we can follow the same approach? Or my guess is incorrect? |
Agree there's something funky there; any views on static abstract interface methods (C# 11+) limited to suitable runtimes? i.e. something like |
Background and motivation
Nowdays, aspnetcore DataProtection layer uses SymmetricAlgorithm.CreateDecryptor() and SymmetricAlgorithm.CreateEncryptor() for decryption and encryption operations.
While attempting to improve performance in this PR, I came across a limiting API in
SymmetricAlgorithm
class (and implementations): there is no way to pass inkey
of algorithm asSpan<byte>
orReadOnlySpan<byte>
(onlybyte[]
is available) toEncrypt
orDecrypt
methods.The only possibilities now are (below only showing encryption flow, but the same applies to decryption):
CryptoStream
to write a plain textthe problem with this API is that implementation was using
MemoryStream
asoutputStream
, which is not that performant.SymmetricAlgorithm.Key
propertythe problem with this API is that SymmetricAlgorithm.Key {set} is actually cloning a byte array. However, for aspnetcore DataProtection flow there is no need to do it - we could try to rent or
stackalloc
memory, fill it with key data, pass it in and after it is used clear it out.API Proposal
Basically the same kind of API exists for validationAlgorithms. In example HMACSHA256.HashData or HMACSHA512.HashData:
HMACSHAxxx
are implementations ofKeyedHashAlgorithm
, and I am able to determine the type and then call static methodHashData
, which accepts validation algorithm key asReadOnlySpan<byte>
:I think we could be able to have something similar on the SymmetricAlgorithm type (or on implementations):
note: I am proposing both Encrypt and Decrypt API here, and probably we should add it for all modes (cbc, cfb, ecb)
API Usage
Alternative Designs
No response
Risks
No response
The text was updated successfully, but these errors were encountered: