0.7.0
- 0.7.0
- 0.6.0
- Introduce configurable storage options
- 0.5.0
- Introduce KMS Configuration of MRDiscovery
- 0.4.0
- Introduce KMS Configuration of Discovery
- 0.3.0
- Introduce MRK Compatibility via KMS Configuration
- 0.2.0
- Update keystore structure and add encryption context option
- 0.1.0
- Initial record
Language | Confirmed Compatible with Spec Version | Minimum Version Confirmed | Implementation |
---|---|---|---|
Dafny | 0.5.0 | 1.4.0 | AwsCryptographyKeyStoreOperations.dfy |
Java | 0.5.0 | 1.4.0 | KeyStore.java |
.NET | 0.5.0 | 1.4.0 | KeyStore.cs |
A Keystore persists hierarchical data that allows customers to call AWS KMS less often. The Keystore persists branch keys that wrap multiple data keys. This creates a hierarchy where a branch key wraps multiple data keys and facilitates caching. These branch keys are only generated using the AWS KMS API GenerateDataKeyWithoutPlaintext.
By creating and persisting a data key to an accessible medium, such as a DynamoDb table, distributed cryptographic agents can use common, coordinated, cryptographic materials.
This prevents distributed cryptographic agents from independently generating unique data keys that COULD BE coordinated, which leads to poor caching performance at decryption, as each unique encrypting agent had a unique data key.
This Keystore interface defines operations that any implementation of its specification must support and implement.
- Branch Key(s): Data keys that are reused to wrap unique data keys for envelope encryption. For security considerations on when to rotate the branch key, refer to Appendix B.
- Beacon Key(s): A root key used to then derive different beacon keys per beacon.
- UUID: a universally unique identifier that can be represented as a byte sequence or a string.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
The following inputs MAY be specified to create a KeyStore:
The following inputs MUST be specified to create a KeyStore:
If neither Storage nor Table Name is configured initialization MUST fail. If both Storage and Table Name are configured initialization MUST fail. If both Storage and DynamoDb Client are configured initialization MUST fail. If both KeyManagement and KMS Client are configured initialization MUST fail. If both KeyManagement and Grant Tokens are configured initialization MUST fail.
If Storage is configured with KeyStorage then this MUST be the configured KeyStorage interface.
If Storage is not configured with KeyStorage a default key storage MUST be created.
This constructed default key storage MUST be configured with the provided logical keystore name.
This constructed default key storage MUST be configured with either the Table Name or the DynamoDBTable table name depending on which one is configured.
This constructed default key storage MUST be configured with either the DynamoDb Client, the DDB client in the DynamoDBTable or a constructed DDB client depending on what is configured.
If a DDB client needs to be constructed and the AWS KMS Configuration is KMS Key ARN or KMS MRKey ARN, a new DynamoDb client MUST be created with the region of the supplied KMS ARN.
If a DDB client needs to be constructed and the AWS KMS Configuration is Discovery, a new DynamoDb client MUST be created with the default configuration.
If a DDB client needs to be constructed and the AWS KMS Configuration is MRDiscovery, a new DynamoDb client MUST be created with the region configured in the MRDiscovery.
If no AWS KMS client is provided one MUST be constructed.
If AWS KMS client needs to be constructed and the AWS KMS Configuration is KMS Key ARN or KMS MRKey ARN, a new AWS KMS client MUST be created with the region of the supplied KMS ARN.
If AWS KMS client needs to be constructed and the AWS KMS Configuration is Discovery, a new AWS KMS client MUST be created with the default configuration.
If AWS KMS client needs to be constructed and the AWS KMS Configuration is MRDiscovery, a new AWS KMS client MUST be created with the region configured in the MRDiscovery.
On initialization the KeyStore SHOULD
append a user agent string to the AWS KMS SDK Client with
the value aws-kms-hierarchy
.
The Identifier for this KeyStore. If one is not supplied, then a version 4 UUID MUST be used.
A list of AWS KMS grant tokens.
The DynamoDb Client used to put and get keys from the backing DDB table. This options is deprecated in favor of storage
The KMS Client used when wrapping and unwrapping keys. This options is deprecated in favor of KeyManagement
The table name of the DynamoDb table that backs this Keystore. This options is deprecated in favor of storage
This configures the Keystore's KMS Key ARN restrictions, which determines which KMS Key(s) is used to wrap and unwrap the keys stored in Amazon DynamoDB. There are four (4) options:
- Discovery
- MRDiscovery
- Single Region Key Compatibility, denoted as
KMS Key ARN
- Multi Region Key Compatibility, denoted as
KMS MRKey ARN
KMS Key ARN
and KMS MRKey ARN
MUST take an additional argument
that is a KMS ARN.
This ARN MUST NOT be an Alias.
This ARN MUST be a valid
AWS KMS Key ARN.
Both KMS Key ARN
and KMS MRKey ARN
accept MRK or regular Single Region KMS ARNs.
To be clear, an KMS ARN for a Multi-Region Key MAY be provided to the KMS Key ARN
configuration,
and a KMS ARN for non Multi-Region Key MAY be provided to the KMS MRKey ARN
configuration.
Discovery
does not take an additional argument.
MRDiscovery
MUST take an additional argument, which is a region.
Any MRK ARN discovered will be changed to this region before use.
This configures how the Keystore will get encrypted data. There are two valid storage options:
- DynamoDBTable
- KeyStorage
A DynamoDBTable configuration MUST take the DynamoDB table name. A DynamoDBTable configuration MAY take DynamoDb Client.
This configures how the Keystore will authenticate encrypted data or create new branch keys. There is one valid storage option:
- AwsKms
An AwsKms configuration MAY take a list of AWS KMS grant tokens. An AwsKms configuration MAY take an AWS KMS SDK client.
For two ARNs to be compatible:
If the AWS KMS Configuration designates single region ARN compatibility, then two ARNs are compatible if they are exactly equal.
If the AWS KMS Configuration designates MRK ARN compatibility, then two ARNs are compatible if they are equal in all parts other than the region. That is, they are compatible if AWS KMS MRK Match for Decrypt returns true.
If the AWS KMS Configuration is Discovery or MRDiscovery, no comparison is ever made between ARNs.
Discovery takes no additional information.
The Keystore MAY use the KMS Key ARNs already persisted to the backing DynamoDB table, provided they are in records created with an identical Logical Keystore Name.
The VersionKey
and CreateKey
Operations are NOT supported
and will fail with a runtime exception.
There is no Multi-Region logic with this configuration; if a Multi-Region Key is encountered, and the region in the ARN is not the region of the KMS Client, requests will fail with KMS Exceptions.
MRDiscovery takes an additional argument, which is a region. Any MRK ARN discovered will be changed to this region before use.
The Keystore MAY use the KMS Key ARNs already persisted to the backing DynamoDB table, provided they are in records created with an identical Logical Keystore Name.
The VersionKey
and CreateKey
Operations are NOT supported
and will fail with a runtime exception.
This name is cryptographically bound to all data stored in this table, and logically separates data between different tables.
The logical keystore name MUST be bound to every created key.
There needs to be a one to one mapping between DynamoDB Table Names and the Logical KeyStore Name. This value can be set to the DynamoDB table name itself, but does not need to.
Controlling this value independently enables restoring from DDB table backups even when the table name after restoration is not exactly the same.
The Keystore MUST support the following operations:
- GetKeyStoreInfo
- CreateKeyStore
- CreateKey
- VersionKey
- GetActiveBranchKey
- GetBranchKeyVersion
- GetBeaconKey
This operation MUST return the keystore information in this keystore configuration.
This MUST include:
The keystore name MUST be obtained from the configured KeyStorage by calling GetKeyStorageInfo.
If a table Name was not configured then CreateKeyStore MUST fail.
This operation MUST first calls the DDB::DescribeTable API with the configured tableName
.
If the response is successful, this operation validates that the table has the expected KeySchema as defined below. If the KeySchema does not match this operation MUST yield an error. The table MAY have additional information, like GlobalSecondaryIndex defined.
If the client responds with a ResourceNotFoundException
,
then this operation MUST continue and
MUST call AWS DDB CreateTable
with the following specifics:
- TableName is the configured tableName.
- KeySchema as defined below.
If the operation fails to create table, the operation MUST fail.
If the operation successfully creates a table, the operation MUST return the AWS DDB Table Arn back to the caller.
The following KeySchema MUST be configured on the table:
AttributeName | KeyType | Type |
---|---|---|
branch-key-id | Partition | S |
type | Sort | S |
The CreateKey caller MUST provide:
- An optional branch key id
- An optional encryption context
If an optional branch key id is provided and no encryption context is provided this operation MUST fail.
If the Keystore's KMS Configuration is Discovery
or MRDiscovery
,
this operation MUST fail.
If no branch key id is provided, then this operation MUST create a version 4 UUID to be used as the branch key id.
This operation MUST create a branch key and a beacon key according to the Branch Key and Beacon Key Creation section.
If creation of the keys are successful, then the key store MUST call the configured KeyStorage interface's WriteNewEncryptedBranchKey with these 3 EncryptedHierarchicalKeys.
If writing to the keystore succeeds, the operation MUST return the branch-key-id that maps to both the branch key and the beacon key.
Otherwise, this operation MUST yield an error.
To create a branch key, this operation MUST take the following:
branchKeyId
: The identifierencryptionContext
: Additional encryption context to bind to the created keys
This operation needs to generate the following:
version
: a new guid. This guid MUST be version 4 UUIDtimestamp
: a timestamp for the current time. This timestamp MUST be in ISO 8601 format in UTC, to microsecond precision (e.g. “YYYY-MM-DDTHH:mm:ss.ssssssZ“)
The wrapped Branch Keys, DECRYPT_ONLY and ACTIVE, MUST be created according to Wrapped Branch Key Creation.
To create a beacon key, this operation will continue to use the branchKeyId
and timestamp
as the Branch Key.
The operation MUST call AWS KMS API GenerateDataKeyWithoutPlaintext. The call to AWS KMS GenerateDataKeyWithoutPlaintext MUST use the configured AWS KMS client to make the call. The operation MUST call AWS KMS GenerateDataKeyWithoutPlaintext with a request constructed as follows:
KeyId
MUST be compatible with the configured KMS Key in the AWS KMS Configuration for this keystore.NumberOfBytes
MUST be 32.EncryptionContext
MUST be the encryption context for beacon keys.GrantTokens
MUST be this keystore's grant tokens.
If the call to AWS KMS GenerateDataKeyWithoutPlaintext succeeds,
the operation MUST use the CiphertextBlob
as the wrapped Beacon Key.
Given a branchKeyId
, version
and timestamp
The operation MUST call AWS KMS API GenerateDataKeyWithoutPlaintext. The call to AWS KMS GenerateDataKeyWithoutPlaintext MUST use the configured AWS KMS client to make the call. The operation MUST call AWS KMS GenerateDataKeyWithoutPlaintext with a request constructed as follows:
KeyId
MUST be compatible with the configured KMS Key in the AWS KMS Configuration for this keystore.NumberOfBytes
MUST be 32.EncryptionContext
MUST be the DECRYPT_ONLY encryption context for branch keys.- GenerateDataKeyWithoutPlaintext
GrantTokens
MUST be this keystore's grant tokens.
If the call to AWS KMS GenerateDataKeyWithoutPlaintext succeeds,
the operation MUST use the GenerateDataKeyWithoutPlaintext result CiphertextBlob
as the wrapped DECRYPT_ONLY Branch Key.
The operation MUST call AWS KMS API ReEncrypt with a request constructed as follows:
SourceEncryptionContext
MUST be the DECRYPT_ONLY encryption context for branch keys.SourceKeyId
MUST be compatible with the configured KMS Key in the AWS KMS Configuration for this keystore.CiphertextBlob
MUST be the wrapped DECRYPT_ONLY Branch Key.- ReEncrypt
GrantTokens
MUST be this keystore's grant tokens. DestinationKeyId
MUST be compatible with the configured KMS Key in the AWS KMS Configuration for this keystore.DestinationEncryptionContext
MUST be the ACTIVE encryption context for branch keys.
If the call to AWS KMS ReEncrypt succeeds,
the operation MUST use the ReEncrypt result CiphertextBlob
as the wrapped ACTIVE Branch Key.
On invocation, the caller:
- MUST supply a
branch-key-id
If the Keystore's KMS Configuration is Discovery
or MRDiscovery
,
this operation MUST immediately fail.
VersionKey MUST first get the active version for the branch key from the keystore
by calling the configured KeyStorage interface's
GetEncryptedActiveBranchKey
using the branch-key-id
.
The KmsArn
of the EncryptedHierarchicalKey
MUST be compatible with
the configured KMS ARN
in the AWS KMS Configuration for this keystore.
Because the storage interface can be a custom implementation the key store needs to verify correctness.
VersionKey MUST verify that the returned EncryptedHierarchicalKey MUST have the requested branch-key-id
.
VersionKey MUST verify that the returned EncryptedHierarchicalKey is an ActiveHierarchicalSymmetricVersion.
VersionKey MUST verify that the returned EncryptedHierarchicalKey MUST have a logical table name equal to the configured logical table name.
The kms-arn
stored in the table MUST NOT change as a result of this operation,
even if the KeyStore is configured with a KMS MRKey ARN
that does not exactly match the stored ARN.
If such were allowed, clients using non-MRK KeyStores might suddenly stop working.
The EncryptedHierarchicalKey MUST be authenticated according to authenticating a keystore item. If the item fails to authenticate this operation MUST fail.
The wrapped Branch Keys, DECRYPT_ONLY and ACTIVE, MUST be created according to Wrapped Branch Key Creation.
If creation of the keys are successful,
then the key store MUST call the configured KeyStorage interface's
WriteNewEncryptedBranchKeyVersion
with an OverWriteEncryptedHierarchicalKey
with an Item
that is the new ACTIVE
and an Old
that is the original ACTIVE,
along with DECRYPT_ONLY.
If the WriteNewEncryptedBranchKeyVersion is successful, this operation MUST return a successful response containing no additional data. Otherwise, this operation MUST yield an error.
The operation MUST use the configured KMS SDK Client
to authenticate the value of the keystore item.
The operation MUST call AWS KMS API ReEncrypt with a request constructed as follows:
SourceEncryptionContext
MUST be the encryption context of the EncryptedHierarchicalKey to be authenticatedSourceKeyId
MUST be compatible with the configured KMS Key in the AWS KMS Configuration for this keystore.CiphertextBlob
MUST be theCiphertextBlob
attribute value on the EncryptedHierarchicalKey to be authenticatedGrantTokens
MUST be the configured grant tokens.DestinationKeyId
MUST be compatible with the configured KMS Key in the AWS KMS Configuration for this keystore.DestinationEncryptionContext
MUST be the encryption context of the EncryptedHierarchicalKey to be authenticated
On invocation, the caller:
- MUST supply a
branch-key-id
GetActiveBranchKey MUST get the active version for the branch key id from the keystore
by calling the configured KeyStorage interface's
GetEncryptedActiveBranchKey
using the supplied branch-key-id
.
Because the storage interface can be a custom implementation the key store needs to verify correctness.
GetActiveBranchKey MUST verify that the returned EncryptedHierarchicalKey MUST have the requested branch-key-id
.
GetActiveBranchKey MUST verify that the returned EncryptedHierarchicalKey is an ActiveHierarchicalSymmetricVersion.
GetActiveBranchKey MUST verify that the returned EncryptedHierarchicalKey MUST have a logical table name equal to the configured logical table name.
The operation MUST decrypt the EncryptedHierarchicalKey according to the AWS KMS Branch Key Decryption section.
If the branch key fails to decrypt, GetActiveBranchKey MUST fail.
This GetActiveBranchKey MUST construct branch key materials according to Branch Key Materials From Authenticated Encryption Context.
This operation MUST return the constructed branch key materials.
On invocation, the caller:
- MUST supply a
branch-key-id
- MUST supply a
branchKeyVersion
GetBranchKeyVersion MUST get the requested version for the branch key id from the keystore
by calling the configured KeyStorage interface's
GetEncryptedActiveBranchKey
using the supplied branch-key-id
.
Because the storage interface can be a custom implementation the key store needs to verify correctness.
GetBranchKeyVersion MUST verify that the returned EncryptedHierarchicalKey MUST have the requested branch-key-id
.
GetBranchKeyVersion MUST verify that the returned EncryptedHierarchicalKey MUST have the requested branchKeyVersion
.
GetActiveBranchKey MUST verify that the returned EncryptedHierarchicalKey is an HierarchicalSymmetricVersion.
GetBranchKeyVersion MUST verify that the returned EncryptedHierarchicalKey MUST have a logical table name equal to the configured logical table name.
The operation MUST decrypt the branch key according to the AWS KMS Branch Key Decryption section.
If the branch key fails to decrypt, this operation MUST fail.
This GetBranchKeyVersion MUST construct branch key materials according to Branch Key Materials From Authenticated Encryption Context.
This operation MUST return the constructed branch key materials.
On invocation, the caller:
- MUST supply a
branch-key-id
GetBeaconKey MUST get the requested beacon key from the keystore
by calling the configured KeyStorage interface's
GetEncryptedBeaconKey
using the supplied branch-key-id
.
Because the storage interface can be a custom implementation the key store needs to verify correctness.
GetBeaconKey MUST verify that the returned EncryptedHierarchicalKey MUST have the requested branch-key-id
.
GetBeaconKey MUST verify that the returned EncryptedHierarchicalKey is an ActiveHierarchicalSymmetricBeacon.
GetBeaconKey MUST verify that the returned EncryptedHierarchicalKey MUST have a logical table name equal to the configured logical table name.
The operation MUST decrypt the beacon key according to the AWS KMS Branch Key Decryption section.
If the beacon key fails to decrypt, this operation MUST fail.
This GetBeaconKey MUST construct beacon key materials from the decrypted branch key material
and the branchKeyId
from the returned branch-key-id
field.
This operation MUST return the constructed beacon key materials.
This section describes how the AWS KMS encryption context is built from an encrypted hierarchical key.
The following encryption context keys are shared:
- MUST have a
branch-key-id
attribute - The
branch-key-id
field MUST not be an empty string - MUST have a
type
attribute - The
type
field MUST not be an empty string - MUST have a
create-time
attribute - MUST have a
tablename
attribute to store the logicalKeyStoreName - MUST have a
kms-arn
attribute - MUST have a
hierarchy-version
- MUST NOT have a
enc
attribute
Any additionally attributes in the EncryptionContext of the encrypted hierarchical key MUST be added to the encryption context.
The ACTIVE branch key is a copy of the DECRYPT_ONLY with the same version
.
It is structured slightly differently so that the active version can be accessed quickly.
In addition to the encryption context:
The ACTIVE encryption context value of the type
attribute MUST equal to "branch:ACTIVE"
.
The ACTIVE encryption context MUST have a version
attribute.
The version
attribute MUST store the branch key version formatted like "branch:version:"
+ version
.
In addition to the encryption context:
The DECRYPT_ONLY encryption context MUST NOT have a version
attribute.
The type
attribute MUST stores the branch key version formatted like "branch:version:"
+ version
.
In addition to the encryption context:
The Beacon key encryption context value of the type
attribute MUST equal to "beacon:ACTIVE"
.
The Beacon key encryption context MUST NOT have a version
attribute.
If custom encryption context
is associated with the branch key these values MUST be added to the AWS KMS encryption context.
To avoid name collisions each added attribute from the custom encryption context
MUST be prefixed with aws-crypto-ec:
.
Across all versions of a Branch Key, the custom encryption context MUST be equal.
The operation MUST use the configured KMS SDK Client
to decrypt the value of the branch key field.
If the Keystore's AWS KMS Configuration is KMS Key ARN
or KMS MRKey ARN
,
the kms-arn
field of the DDB response item MUST be
compatible with the configured KMS Key in
the AWS KMS Configuration for this keystore,
or the operation MUST fail.
If the Keystore's AWS KMS Configuration is Discovery
or MRDiscovery
,
the kms-arn
field of DDB response item MUST NOT be an Alias
or the operation MUST fail.
When calling AWS KMS Decrypt, the keystore operation MUST call with a request constructed as follows:
KeyId
, if the KMS Configuration is Discovery, MUST be thekms-arn
attribute value of the AWS DDB response item. If the KMS Configuration is MRDiscovery,KeyId
MUST be thekms-arn
attribute value of the AWS DDB response item, with the region replaced by the configured region. Otherwise, it MUST BE the Keystore's configured KMS Key.CiphertextBlob
MUST be theCiphertextBlob
attribute value on the provided EncryptedHierarchicalKeyEncryptionContext
MUST be the encryption context of the provided EncryptedHierarchicalKeyGrantTokens
MUST be this keystore's grant tokens.
The type
attribute MUST either be equal to "branch:ACTIVE"
or start with "branch:version:"
.
If the type
attribute is equal to "branch:ACTIVE"
then the authenticated encryption context MUST have a version
attribute
and the version string is this value.
If the type
attribute start with "branch:version:"
then the version string MUST be equal to this value.
To construct branch key materials from authenticated encryption context as follows:
- Branch Key MUST be the decrypted branch key material
- Branch Key Id MUST be the
branch-key-id
- Branch Key Version
The version string MUST start with
branch:version:
. The remaining string encoded as UTF8 bytes MUST be the Branch Key version. - Encryption Context MUST be constructed by Custom Encryption Context From Authenticated Encryption Context
The custom encryption context is stored as map of UTF8 Encoded bytes.
For every key in the encryption context
the string aws-crypto-ec:
+ the UTF8 decode of this key
MUST exist as a key in the authenticated encryption context.
Also, the value in the encryption context for this key
MUST equal the value in the authenticated encryption context
for the constructed key.
Given the simplified branch key material structure
BranchKeyMaterials(
branchKey := [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
branchKeyId := "bbb9baf1-03e6-4716-a586-6bf29995314b",
branchKeyVersion := "83eec007-5659-4554-bf11-699b90f41ac6",
encryptionContext := [ "department" := "admin"]
)
There would be three items in the keystore table. The DECRYPT_ONLY version, the ACTIVE version, and a beacon key.
The DECRYPT_ONLY simplified JavaScript JSON format would look like this
{
"branch-key-id": "bbb9baf1-03e6-4716-a586-6bf29995314b",
"type": "branch:version:83eec007-5659-4554-bf11-699b90f41ac6",
"enc": "NnYwxJ/oiQCLnqRh/IcrCR2mmOnO4SAVLw2pspKJKd6rpa0H8z/4hGpGxcWozdb7VByebDFWb0VTWxaOUA8=",
"kms-arn": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
"create-time": "2023-06-03T19:03:29.358Z",
"hierarchy-version": "1",
"aws-crypto-ec:department": "admin"
}
The ACTIVE simplified JavaScript JSON format would look like this
{
"branch-key-id" : "bbb9baf1-03e6-4716-a586-6bf29995314b",
"type" : ""branch:ACTIVE",
"version": "branch:version:83eec007-5659-4554-bf11-699b90f41ac6"
"enc" : "BiXHTm0j27+jsgJZ7yCnvI6yvjFyStMsHiC8fnR9KzKjwwhi0gB+5CZTfXFC2ufmBtCYX/sLvKsFnEITR+k=",
"kms-arn" : "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
"create-time" : "2023-06-03T19:03:29.358Z",
"hierarchy-version" : "1",
"aws-crypto-ec:department" : "admin",
}
The BEACON simplified JavaScript JSON format would look like this
{
"branch-key-id": "bbb9baf1-03e6-4716-a586-6bf29995314b",
"type": "beacon:ACTIVE",
"enc": "hgb2RyDQinOCpzKWdi17E+t9WB9pRExQXpD/20bsu9hxr38HjQvGvihoYpL6sKuF0Ek+37B1UE9tK3SIOiE=",
"kms-arn": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
"create-time": "2023-06-03T19:03:29.358Z",
"hierarchy-version": "1",
"aws-crypto-ec:department": "admin"
}