-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Adding interfaces for storage client side encryption to Azure.Core #7601
Conversation
schaabs
commented
Sep 13, 2019
- Adding IKeyEncryptionKey and IKeyEncryptionKeyResolver to Azure.Core.Cryptography
- Updated CryptographyClient to implement IKeyEncryptionKey explicitly
- Updated key vault algorithm enums to follow the extensible enum pattern used throughout the key vault client libraries
sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/EncryptionAlgorithm.cs
Outdated
Show resolved
Hide resolved
sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/SignatureAlgorithm.cs
Outdated
Show resolved
Hide resolved
/azp run net - keyvault - ci |
Azure Pipelines successfully started running 1 pipeline(s). |
@danieljurek why did the pipeline fail so you had to restart it? |
I'm not yet sure what's causing this. I'm having a look locally and tracking this in #7604 |
I'm trying to get us to the point where we track all the issues that make builds flaky so if possible when restarting the pipeline reference the issues that's tracking the failure you are seeing (flaky test/build flakiness etc.) for we can prioritize fixing them in the right order. |
/// <summary> | ||
/// A key which is used to encrypt, or wrap, another key | ||
/// </summary> | ||
public interface IKeyEncryptionKey |
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.
Wouldn't a IKeyEncryptor
be a better name?
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.
That's what I originally was going to call it. However, when this is fed to storage it's referred to as keyEncryptionKey so I felt this would more explicitly link the two together.
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.
IKeyEncryptionKey
does look strange. I think storage should still call the parameter whatever it think is best, but the type name could be what we think is best. BTW, is the interface specific to wrapping and unwrapping key? Or would it work on any byte array? If the later, we should call it without reffering to "key" at all.
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 interface is specific to encrypting keys, in that it is only able to encrypt a single block as it doesn't use a nonce or IV. Also the algorithm specified is expected to be a key wrap algorithm, not just any encryption algorithm. I personally think that IKeyEncryptionKey is an appropriate name as it's an abstraction of a key which is able to wrap another key. While the name might seem odd without prior knowledge of cryptography concepts, it is a well known term in cryptography and key hierarchy management. https://csrc.nist.gov/glossary/term/Key_Encryption_Key
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 looks awesome and is much nicer than another package. Thanks Scott!
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Azure.Core.Cryptography |
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.
So we'll use this from Azure.Storage.Blobs.Cryptography and you'll implement it in Azure.Security.KeyVault.Cryptography? That's a good namespace choice then.
/// <summary> | ||
/// A key which is used to encrypt, or wrap, another key | ||
/// </summary> | ||
public interface IKeyEncryptionKey |
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'm not an expert in this area by any stretch so I find this name a little jarring. It's not really a key but a thing that encrypts keys, right? Would IKeyEncryptionClient
make sense since it follows most of our other client patterns? Disregard if this is a well known concept/pattern that should keep using this name.
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 don't think that this should contain the word "Client" as quite often implementations of this would not call a service.
sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/KeyWrapAlgorithm.cs
Show resolved
Hide resolved
/// <param name="keyId">The key idenitifier of the key encryption key to retrieve</param> | ||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | ||
/// <returns>The key encryption key corresponding to the specified keyId</returns> | ||
IKeyEncryptionKey Resolve(string keyId, CancellationToken cancellationToken); |
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.
Nit - what happens when it's not resolved? Do we return null or throw something? It might be helpful to document that here.
/// <param name="key">The key to be encrypted</param> | ||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | ||
/// <returns>The encrypted key bytes</returns> | ||
byte[] WrapKey(string algorithm, byte[] key, CancellationToken cancellationToken); |
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.
If clients will be interacting with implementations via the interface, cancellationToken
should be defaulted so the compiler emits the default
if otherwise unspecified.
/// <returns>The encrypted key</returns> | ||
async Task<byte[]> IKeyEncryptionKey.WrapKeyAsync(string algorithm, byte[] key, CancellationToken cancellationToken) | ||
{ | ||
return (await WrapKeyAsync(new KeyWrapAlgorithm(algorithm), key, cancellationToken).ConfigureAwait(false)).EncryptedKey; |
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.
Nit: breaking apart complex statements makes it easier to debug later, i.e. make this two separate statements. IMO, makes it more readable as well.
/azp run net - keyvault - ci |
Azure Pipelines successfully started running 1 pipeline(s). |
restarted pipeline for issue #7490 |
/// <summary> | ||
/// A key which is used to encrypt, or wrap, another key | ||
/// </summary> | ||
public interface IKeyEncryptionKey |
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.
IKeyEncryptionKey
does look strange. I think storage should still call the parameter whatever it think is best, but the type name could be what we think is best. BTW, is the interface specific to wrapping and unwrapping key? Or would it work on any byte array? If the later, we should call it without reffering to "key" at all.
/// <param name="key">The key to be encrypted</param> | ||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | ||
/// <returns>The encrypted key bytes</returns> | ||
byte[] WrapKey(string algorithm, byte[] key, CancellationToken cancellationToken = default); |
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 take ReadOnlyMemory instead of byte[]?
public static implicit operator string(EncryptionAlgorithm value) => value._value; | ||
|
||
/// <inheritdoc/> | ||
public override bool Equals(object obj) => obj is EncryptionAlgorithm other && Equals(other); |
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 should have EditorBrowsable false
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 added this to Equals, GetHashCode and ToString for the added enum like structs.