-
Notifications
You must be signed in to change notification settings - Fork 122
Defining the KMS Keyring #147
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
Conversation
*Description of changes:* Defining the KMS Keyring. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. # Check any applicable: - [ ] Were any files moved? Moving files changes their URL, which breaks all hyperlinks to the files.
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.
Couple of questions, but otherwise the KMS keyring logic LGTM. Note that awslabs/aws-encryption-sdk-specification#50 is still open (specifies behavior with client supplier when a region can't be grabbed from the arn), and could impact this PR if more changes are suggested.
You should also get this reviewed by someone with more Java experience, and more familiar with the current MKP behavior.
result.getPlaintextDataKey(), | ||
edk.getEncryptedDataKey(), | ||
edk.getProviderInformation(), this); | ||
} catch (final AwsCryptoException | MismatchedDataKeyException ex) { |
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.
Do we have a unit test that ensures this behavior is maintained? (I can't seem to find one, or other unit tests for KMSMasterKey :()
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.
KMSMasterKey is covered by LegacyKMSMasterKeyProviderTests. I've added a separate KmsMasterKeyTest to cover the case of a MismatchedDataKeyException.
} | ||
|
||
private static boolean isKeyAlias(String arn) { | ||
return arn.startsWith(ALIAS_PREFIX) && !arn.contains(":"); |
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.
Where does the !arn.contains(":")
requirement come from?
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 was thinking I wanted to return false for an alias ARN, that still contains the ALIAS_PREFIX (though not as a prefix). But since an alias ARN would not have that as a prefix, checking for : shouldn't be necessary.
encryptionMaterials.getPlaintextDataKey(), encryptionMaterials.getEncryptionContext()); | ||
|
||
encryptionMaterials.addEncryptedDataKey(encryptedDataKey, | ||
new KeyringTraceEntry(KMS_PROVIDER_ID, keyId, KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT)); |
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.
Did we determine whether this should be the keyId the user inputs, or the keyId returned from the KMS result (and is it in the spec/do we have an issue cut to update the spec)?
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 were leaning toward the keyId the user inputs, which is what I went with, but it wasn't settled. Either way, the spec still just says "generator" which is wrong, I've created an issue: awslabs/aws-encryption-sdk-specification#60
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 did not review the tests. Minor issues found, but nothing major.
* This exception is thrown when the key used by KMS to decrypt a data key does not | ||
* match the provider information contained within the encrypted data key. | ||
*/ | ||
public class MismatchedDataKeyException extends IllegalStateException { |
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 is technically an IllegalStateException
. According to the documentation.
Signals that a method has been invoked at an illegal or inappropriate time. In other words, the Java environment or Java application is not in an appropriate state for the requested operation.
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.
Originally I wanted this to not be an AwsCryptoException so I could handle it differently, and I was thinking it indicated KMS was in a bad state, so IllegalStateException seemed ok. Now looking over the code again, I can just make it extend AwsCryptoException. I added another unit test for KmsKeyring to ensure the behavior I wanted is maintained for this exception.
.filter(edk -> edk.getProviderId().equals(KMS_PROVIDER_ID)) | ||
.map(edk -> new String(edk.getProviderInformation(), PROVIDER_ENCODING)) | ||
.allMatch(KmsUtils::isArnWellFormed)) { | ||
throw new MalformedArnException("encryptedDataKeys contains a malformed ARN"); |
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.
Why should a single malformed ARN cause decryption failure? So long as we can find a single valid ARN and decrypt it, we should succeed.
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.
My thinking was that since decryption failures are ignored, as a user of this method I would like the method to fail fast if I am passing in bad inputs. Otherwise I would not understand why a particular key was not being used for decryption.
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.
We should be flexible in what we accept (when it doesn't conflict with security). What if a newer version of the ESDK is delivered which supports a new form of ARNs? Older versions should still be able to decrypt data associated with the old-style of understood ARNs.
If no data keys can be decrypted, then this error should be propagated up. That's useful.
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 disagree. The question is, how did such a value get in the the EDK in the first place?
I guess the question is "what is bad input"? if someone were to create a new KMS Keyring, that is not quite right and sometimes puts the wrong thing in the ProviderID, is this an example of a bad input?
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.
@seebees which point are you disagreeing with?
throw new MalformedArnException("encryptedDataKeys contains a malformed ARN"); | ||
} | ||
|
||
for (EncryptedDataKey encryptedDataKey : encryptedDataKeys) { |
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 a potential O(n^2) operation. We iterate over each EDK and then in okToDecrypt
iterate over every keyId we have configured. This seems like a potentially inefficient way to do this.
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'll put all the configured keys into a HashSet to improve performance
* @return The proxy | ||
*/ | ||
private AWSKMS newCachingProxy(AWSKMS client, String regionId) { | ||
return (AWSKMS) Proxy.newProxyInstance( |
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's worth keeping an eye on this. Proxies can cause performance problems sometimes as they depend on Java reflection APIs.
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.
Sure, though I hope any proxy overhead would be insignificant compared to the actual service call to KMS.
Will address after decision is made on supporting raw key IDs
Description of changes:
This change contains the initial implementation of the KMS Keyring, as well as a refactoring of the existing KMS logic into a DataKeyEncryptionDao that is shared between the KMS Keyring and the KMS Master Key.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
Check any applicable: