Skip to content
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

Merged
merged 5 commits into from
Sep 17, 2019

Conversation

schaabs
Copy link
Member

@schaabs 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

@danieljurek
Copy link
Member

/azp run net - keyvault - ci

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@pakrym
Copy link
Contributor

pakrym commented Sep 13, 2019

@danieljurek why did the pipeline fail so you had to restart it?

@danieljurek
Copy link
Member

@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

@pakrym
Copy link
Contributor

pakrym commented Sep 13, 2019

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
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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

Copy link
Member

@tg-msft tg-msft left a 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
Copy link
Member

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
Copy link
Member

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.

Copy link
Member Author

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.

/// <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);
Copy link
Member

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);
Copy link
Member

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;
Copy link
Member

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.

@schaabs
Copy link
Member Author

schaabs commented Sep 16, 2019

/azp run net - keyvault - ci

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@schaabs
Copy link
Member Author

schaabs commented Sep 16, 2019

restarted pipeline for issue #7490

/// <summary>
/// A key which is used to encrypt, or wrap, another key
/// </summary>
public interface IKeyEncryptionKey
Copy link
Member

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);
Copy link
Member

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);
Copy link
Member

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

Copy link
Member Author

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.

@schaabs schaabs merged commit 7d13263 into Azure:master Sep 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants