-
Notifications
You must be signed in to change notification settings - Fork 340
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
Implementing Cryptography building block in .NET #1217
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #1217 +/- ##
==========================================
- Coverage 68.48% 67.17% -1.32%
==========================================
Files 172 174 +2
Lines 5848 5986 +138
Branches 648 667 +19
==========================================
+ Hits 4005 4021 +16
- Misses 1681 1798 +117
- Partials 162 167 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
/assign |
src/Dapr.Client/DaprClient.cs
Outdated
/// <returns>An array of encrypted bytes.</returns> | ||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] | ||
public abstract Task<byte[]> EncryptAsync(string vaultResourceName, byte[] plainTextBytes, | ||
KeyWrapAlgorithm algorithm, string keyName, string decryptionKeyName, DataEncryptionCipher dataEncryptionCipher = DataEncryptionCipher.AesGcm, |
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.
Should there be an explicit endeavour to make the default cipher consistent across SDKs? (Is this consistent with other SDKs today?)
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.
This is the default per the comments in the dapr.proto so I simply selected it, but in case there was a reason for users to use the alternative (right now just "chacha20-poly1305"), I wanted to leave users the option to select the other one. That said, this issue suggested that a third should be added and used as the default (perhaps completely replacing the alternatives) and by setting it as the default it would promote better security.
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.
This is awesome, thanks for writing this as I know it wasn't the simplest code :) I mentioned to you before that I'm really bad at C#, especially when streaming is involved, so please take my comments with a grain of salt!
src/Dapr.Client/DaprClientGrpc.cs
Outdated
public override async Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, | ||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions decryptionOptions, | ||
CancellationToken cancellationToken = default) => | ||
await DecryptAsync(vaultResourceName, new MemoryStream(ciphertextBytes.ToArray()), keyName, |
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.
ReadOnlyMemory<byte>
should by implicitly convertible to byte[]
, no? ToArray()
will make a copy which is probably not what we want.
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.
byte[]
is implicitly convertible to a ReadOnlyMemory<byte>
, but not vice versa. The best alternative I can find is using a MemoryMarshal to create a reference to the byte array and pass that in instead of copying it, but as this is exposed via TryGetArray
, it begs the new question of what the exception message should be if it fails for some reason. Is there any documented guidance on the shape of exceptions for the SDK?
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 see. Ach! The purpose of accepting ReadOnlyMemory<byte>
was to be more flexible for callers, but if we have to essentially restrict it to just array-like memory (or else make a copy), I'm wondering if that flexibility is really worth it. (Note that there is a performant Stream
implementation over ReadOnlyMemory<byte>
in the .NET "high efficiency" community toolkit. That said, I don't think we want to go down that path of taking on a new dependency or having our own implementation just for this purpose.)
I'm starting to feel like accepting (for now) byte[]
as the better approach for now. (And also returning byte[]
to keep the symmetry of the method.)
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.
It looks like their implementation to make a memory stream from a ReadOnlyMemory<byte>
is awfully similar to what I've got, using the MemoryMarshal to access the byte array.
I did copy over their argument exception text as it's richer than what I had.
As such, I vote leaving this as-is. It's about as high-performant as it'll probably get with this version of .NET and there's nothing that stops the developer from simply passing in a byte[]
to the method if that's what they have or a ReadOnlyMemory<byte>
if they happen to have that as well (as opposed to making this the developer's problem by reverting to only accepting byte[]
and leaving them having to do this same research to convert their own ReadOnlyMemory<byte>
accordingly).
@philliphoff Would appreciate your thoughts on this: Because of the method signature conflict, I renamed the encrypt/decrypt methods that took a stream and returned an Now that the Stream -> ReadOnlyMemory method is out of the picture, do you think it makes the API more accessible to keep these different names or instead expose just the EncryptAsync and DecryptAsync methods again with their various overloads? |
I would try to use just a single method name ( |
…e shorter variation in .NET 6 Signed-off-by: Whit Waldo <[email protected]>
… just EncryptAsync or DecryptAsync Signed-off-by: Whit Waldo <[email protected]>
…ely to the sidecar. Also simplified operation per suggestion in review. Signed-off-by: Whit Waldo <[email protected]>
Signed-off-by: Whit Waldo <[email protected]>
…ncEnumerable<ReadOnlyMemory<byte>> instead of IAsyncEnumerable<byte[]>. Signed-off-by: Whit Waldo <[email protected]>
…ncEnumerable<ReadOnlyMemory<byte>> Signed-off-by: Whit Waldo <[email protected]>
…reate MemoryStream from ReadOnlyMemory<byte>. Signed-off-by: Whit Waldo <[email protected]>
…minate unnecessary allocations. Signed-off-by: Whit Waldo <[email protected]>
…ertextStreamAsync methods Signed-off-by: Whit Waldo <[email protected]>
…e input value. Signed-off-by: Whit Waldo <[email protected]>
Signed-off-by: Whit Waldo <[email protected]>
Signed-off-by: Whit Waldo <[email protected]>
Signed-off-by: Whit Waldo <[email protected]>
Signed-off-by: Whit Waldo <[email protected]>
Signed-off-by: Whit Waldo <[email protected]>
Signed-off-by: Whit Waldo <[email protected]>
6b7be26
to
48bd541
Compare
If the terminal only supported multi-line commit messages, that would have been much easier to deal with. As it is, I believe I've properly merged this and the DCO check is validating as it should. @philliphoff |
@WhitWaldo Would you be willing to look at the differences in code coverage and see if there are some tests that could be added to bring it back to parity with the baseline? |
@philliphoff I can give it a look, but I don't know that there's a whole lot that can be added on the .NET side since it's largely just shuffling the bytes out and vice versa, as all the magic happens on the Dapr sidecar. Give me a few days and I'll see what I can add to it that I don't already have in there. What's the baseline we're aiming for? |
@WhitWaldo Ideally we'd end up close to the same test coverage as the baseline (i.e. prior to the changes). The coverage maps show a good portion of the new logic that isn't covered. This may just be a false-negative, but something to look at to see if that's the case or, if not, how easy it is to get it covered. |
Merging this to ensure it's in 1.13; more test coverage would be nice prior to the APIs being considered stable but can wait. |
* Added method to DaprClient and GRPC implementation to call cryptography proto endpoints Signed-off-by: Whit Waldo <[email protected]> * First pass at implementing all exposed Cryptography methods on Go interface Signed-off-by: Whit Waldo <[email protected]> * Added examples for Cryptography block Signed-off-by: Whit Waldo <[email protected]> * Added missing copyright statements Signed-off-by: Whit Waldo <[email protected]> * Updated to properly support Crypto API this time Signed-off-by: Whit Waldo <[email protected]> * Added copyright statements Signed-off-by: Whit Waldo <[email protected]> * Removed deprecated examples as the subtle APIs are presently disabled Signed-off-by: Whit Waldo <[email protected]> * Updated example to reflect new API shape Signed-off-by: Whit Waldo <[email protected]> * Updated example and readme Signed-off-by: Whit Waldo <[email protected]> * Added overloads for encrypting/decrypting streams instead of just fixed byte arrays. Added example demonstrating the same encrypting a file via a FileStream and decrypting from a MemoryStream. Signed-off-by: Whit Waldo <[email protected]> * Added some unit tests to pair with the implementation Signed-off-by: Whit Waldo <[email protected]> * Added null check for the stream argument Signed-off-by: Whit Waldo <[email protected]> * Changed case of the arguments as they should read "plaintext" and not "plainText" Signed-off-by: Whit Waldo <[email protected]> * Reduced number of encryption implementations by just wrapping byte array into memory stream Signed-off-by: Whit Waldo <[email protected]> * Constrainted returned member types per review suggestion Signed-off-by: Whit Waldo <[email protected]> * Updated methods to use ReadOnlyMemory<byte> instead of byte[] - updated implementations to use low-allocation spans where possible (though ToArray is necessary to wrap with MemoryStream). Signed-off-by: Whit Waldo <[email protected]> * Updated to use encryption/decryption options instead of lots of method overload variations. Simplified gRPC implementation to use fewer methods. Applied argument name updates applied previously (plainText -> plaintext). Signed-off-by: Whit Waldo <[email protected]> * Updated tests Signed-off-by: Whit Waldo <[email protected]> * Removed unused reference Signed-off-by: Whit Waldo <[email protected]> * Updated examples to reflect new method shapes. Downgraded package to .net 6 instead of .net 8 per review suggestion. Signed-off-by: Whit Waldo <[email protected]> * Updated to reflect non-aliased values per review suggestion Signed-off-by: Whit Waldo <[email protected]> * Update to ensure that both send/receive streams run at the same time instead of sequentially. Signed-off-by: Whit Waldo <[email protected]> * Updated to support streamed results in addition to fixed byte arrays. Refactored implementation to minimize duplicative code. Signed-off-by: Whit Waldo <[email protected]> * Updated example to fix compile issue Signed-off-by: Whit Waldo <[email protected]> * Removed encrypt/decrypt methods that accepted streams and returned ReadOnlyMemory<byte>. Marked implementations that use this on the gRPC class as private instead. Signed-off-by: Whit Waldo <[email protected]> * Added missing Obsolete attributes on Encrypt/Decrypt methods. Added overloads on decrypt methods that do not require a DecryptionOptions to be passed in. Signed-off-by: Whit Waldo <[email protected]> * Updated encrypt/decrypt options so the streaming block size no longer uses a uint. Added validation in its place to ensure the value provided is never less than or equal to 0. Signed-off-by: Whit Waldo <[email protected]> * Updated how validation works in the options to accommodate lack of the shorter variation in .NET 6 Signed-off-by: Whit Waldo <[email protected]> * Updated names of encrypt/decrypt streaming methods so everything uses just EncryptAsync or DecryptAsync Signed-off-by: Whit Waldo <[email protected]> * Fixed regression that would have prevented data from being sent entirely to the sidecar. Also simplified operation per suggestion in review. Signed-off-by: Whit Waldo <[email protected]> * Updated examples to reflect changed API Signed-off-by: Whit Waldo <[email protected]> * Updated so IAsyncEnumerable methods (encrypt and decrypt) return IAsyncEnumerable<ReadOnlyMemory<byte>> instead of IAsyncEnumerable<byte[]>. Signed-off-by: Whit Waldo <[email protected]> * Updated example to reflect change from IAsyncEnumerable<byte> to IAsyncEnumerable<ReadOnlyMemory<byte>> Signed-off-by: Whit Waldo <[email protected]> * Avoiding allocation by using MemoryMarshal instead of .ToArray() to create MemoryStream from ReadOnlyMemory<byte>. Signed-off-by: Whit Waldo <[email protected]> * Performance updates to minimize unnecessary byte array copies and eliminate unnecessary allocations. Signed-off-by: Whit Waldo <[email protected]> * Removed unnecessary return from SendPlaintextStreamAsync and SendCiphertextStreamAsync methods Signed-off-by: Whit Waldo <[email protected]> * Updated exception text to be more specific as to what's wrong with the input value. Signed-off-by: Whit Waldo <[email protected]> * Minor tweak to prefer using using a Memory Signed-off-by: Whit Waldo <[email protected]> * Deduplicated some of the Decrypt methods, simplifying the implementation Signed-off-by: Whit Waldo <[email protected]> * Eliminated duplicate encryption method, simplifying implementation Signed-off-by: Whit Waldo <[email protected]> * Updated to eliminate an unnecessary `await` and `async foreach`. Signed-off-by: Whit Waldo <[email protected]> * Updated stream example to reflect the changes to the API shape Signed-off-by: Whit Waldo <[email protected]> * Added notes about operations with stream-based data Signed-off-by: Whit Waldo <[email protected]> --------- Signed-off-by: Whit Waldo <[email protected]> Signed-off-by: James Croft <[email protected]>
* Added documentation detailing how serialization works using the DataContract serialization framework. (#1222) Signed-off-by: Whit Waldo <[email protected]> Signed-off-by: James Croft <[email protected]> * Weakly typed actor polymorphic and null responses (#1214) Signed-off-by: Remco Blok <[email protected]> Co-authored-by: Remco Blok <[email protected]> Co-authored-by: Phillip Hoff <[email protected]> Signed-off-by: James Croft <[email protected]> * Enable vault name mapping and error suppression Signed-off-by: Yash Nisar <[email protected]> Signed-off-by: James Croft <[email protected]> * Add additional secret descriptor constructor for required without key map Signed-off-by: James Croft <[email protected]> * Update configuration load exception rethrow to match rules Signed-off-by: James Croft <[email protected]> * Add tests for required/not required exception handling Signed-off-by: James Croft <[email protected]> * Implementing Cryptography building block in .NET (#1217) * Added method to DaprClient and GRPC implementation to call cryptography proto endpoints Signed-off-by: Whit Waldo <[email protected]> * First pass at implementing all exposed Cryptography methods on Go interface Signed-off-by: Whit Waldo <[email protected]> * Added examples for Cryptography block Signed-off-by: Whit Waldo <[email protected]> * Added missing copyright statements Signed-off-by: Whit Waldo <[email protected]> * Updated to properly support Crypto API this time Signed-off-by: Whit Waldo <[email protected]> * Added copyright statements Signed-off-by: Whit Waldo <[email protected]> * Removed deprecated examples as the subtle APIs are presently disabled Signed-off-by: Whit Waldo <[email protected]> * Updated example to reflect new API shape Signed-off-by: Whit Waldo <[email protected]> * Updated example and readme Signed-off-by: Whit Waldo <[email protected]> * Added overloads for encrypting/decrypting streams instead of just fixed byte arrays. Added example demonstrating the same encrypting a file via a FileStream and decrypting from a MemoryStream. Signed-off-by: Whit Waldo <[email protected]> * Added some unit tests to pair with the implementation Signed-off-by: Whit Waldo <[email protected]> * Added null check for the stream argument Signed-off-by: Whit Waldo <[email protected]> * Changed case of the arguments as they should read "plaintext" and not "plainText" Signed-off-by: Whit Waldo <[email protected]> * Reduced number of encryption implementations by just wrapping byte array into memory stream Signed-off-by: Whit Waldo <[email protected]> * Constrainted returned member types per review suggestion Signed-off-by: Whit Waldo <[email protected]> * Updated methods to use ReadOnlyMemory<byte> instead of byte[] - updated implementations to use low-allocation spans where possible (though ToArray is necessary to wrap with MemoryStream). Signed-off-by: Whit Waldo <[email protected]> * Updated to use encryption/decryption options instead of lots of method overload variations. Simplified gRPC implementation to use fewer methods. Applied argument name updates applied previously (plainText -> plaintext). Signed-off-by: Whit Waldo <[email protected]> * Updated tests Signed-off-by: Whit Waldo <[email protected]> * Removed unused reference Signed-off-by: Whit Waldo <[email protected]> * Updated examples to reflect new method shapes. Downgraded package to .net 6 instead of .net 8 per review suggestion. Signed-off-by: Whit Waldo <[email protected]> * Updated to reflect non-aliased values per review suggestion Signed-off-by: Whit Waldo <[email protected]> * Update to ensure that both send/receive streams run at the same time instead of sequentially. Signed-off-by: Whit Waldo <[email protected]> * Updated to support streamed results in addition to fixed byte arrays. Refactored implementation to minimize duplicative code. Signed-off-by: Whit Waldo <[email protected]> * Updated example to fix compile issue Signed-off-by: Whit Waldo <[email protected]> * Removed encrypt/decrypt methods that accepted streams and returned ReadOnlyMemory<byte>. Marked implementations that use this on the gRPC class as private instead. Signed-off-by: Whit Waldo <[email protected]> * Added missing Obsolete attributes on Encrypt/Decrypt methods. Added overloads on decrypt methods that do not require a DecryptionOptions to be passed in. Signed-off-by: Whit Waldo <[email protected]> * Updated encrypt/decrypt options so the streaming block size no longer uses a uint. Added validation in its place to ensure the value provided is never less than or equal to 0. Signed-off-by: Whit Waldo <[email protected]> * Updated how validation works in the options to accommodate lack of the shorter variation in .NET 6 Signed-off-by: Whit Waldo <[email protected]> * Updated names of encrypt/decrypt streaming methods so everything uses just EncryptAsync or DecryptAsync Signed-off-by: Whit Waldo <[email protected]> * Fixed regression that would have prevented data from being sent entirely to the sidecar. Also simplified operation per suggestion in review. Signed-off-by: Whit Waldo <[email protected]> * Updated examples to reflect changed API Signed-off-by: Whit Waldo <[email protected]> * Updated so IAsyncEnumerable methods (encrypt and decrypt) return IAsyncEnumerable<ReadOnlyMemory<byte>> instead of IAsyncEnumerable<byte[]>. Signed-off-by: Whit Waldo <[email protected]> * Updated example to reflect change from IAsyncEnumerable<byte> to IAsyncEnumerable<ReadOnlyMemory<byte>> Signed-off-by: Whit Waldo <[email protected]> * Avoiding allocation by using MemoryMarshal instead of .ToArray() to create MemoryStream from ReadOnlyMemory<byte>. Signed-off-by: Whit Waldo <[email protected]> * Performance updates to minimize unnecessary byte array copies and eliminate unnecessary allocations. Signed-off-by: Whit Waldo <[email protected]> * Removed unnecessary return from SendPlaintextStreamAsync and SendCiphertextStreamAsync methods Signed-off-by: Whit Waldo <[email protected]> * Updated exception text to be more specific as to what's wrong with the input value. Signed-off-by: Whit Waldo <[email protected]> * Minor tweak to prefer using using a Memory Signed-off-by: Whit Waldo <[email protected]> * Deduplicated some of the Decrypt methods, simplifying the implementation Signed-off-by: Whit Waldo <[email protected]> * Eliminated duplicate encryption method, simplifying implementation Signed-off-by: Whit Waldo <[email protected]> * Updated to eliminate an unnecessary `await` and `async foreach`. Signed-off-by: Whit Waldo <[email protected]> * Updated stream example to reflect the changes to the API shape Signed-off-by: Whit Waldo <[email protected]> * Added notes about operations with stream-based data Signed-off-by: Whit Waldo <[email protected]> --------- Signed-off-by: Whit Waldo <[email protected]> Signed-off-by: James Croft <[email protected]> * Update DaprSecretDescriptor constructors and documentation Signed-off-by: James Croft Signed-off-by: James Croft <[email protected]> * Remove DaprSecretStoreConfigurationProvider Console.WriteLine Signed-off-by: James Croft <[email protected]> --------- Signed-off-by: Whit Waldo <[email protected]> Signed-off-by: James Croft <[email protected]> Signed-off-by: Remco Blok <[email protected]> Signed-off-by: Yash Nisar <[email protected]> Signed-off-by: James Croft Co-authored-by: Whit Waldo <[email protected]> Co-authored-by: Remco Blok <[email protected]> Co-authored-by: Remco Blok <[email protected]> Co-authored-by: Phillip Hoff <[email protected]> Co-authored-by: Yash Nisar <[email protected]>
Description
This adds support for Cryptography building block (alpha) in .NET supporting both the encrypt and decrypt streaming APIs.
Examples work and I've updated the README to reflect use with Azure Key Vault (RBAC, environment variable install, etc.).
Issue reference
We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.
This closes #1072 - per recommendations in Discord, this streams the requests in configurable blocks (defaulting to 4 KB), sending only the request metadata payload in the first message and sending the plaintext/ciphertext blocks in subsequent messages. I manually updated the relevant sections of the dapr.proto pulling from the latest in dapr/dapr.
Submitted update for docs at dapr/docs#3928.
Also needs a Quickstart to show how this is used dapr/quickstarts#969
I still need to figure out how to write any tests for this, but I confirmed that my example (properly configured) runs without issue locally.
Checklist
Please make sure you've completed the relevant tasks for this PR, out of the following list: