Nitro Enclaves and Key Management Service (KMS) feel like a natural fit: since the KMS can verify attestation documents generated by the enclaves, developers can offload key management tasks from their applications to the AWS-managed service. But integrating an external service with your trusted enclaves comes with new threats, even if that service comes from the same provider.

In this blog post—the third in our series on Nitro Enclaves, following our posts on attack surface and images and attestation—we catalog passive and active attack classes against the enclave-KMS communication channel, and cover the operational risks that persist even when the cryptography is correct. We also disclose vulnerabilities in AWS Nitro Enclaves SDK for C and recommend safer alternatives.

Intro to KMS

The KMS is a set of Hardware Security Modules (HSM) with public API integrated to the broader AWS ecosystem. There are three main key types supported by KMS that devs need to care about:

  • Customer-managed keys (Customer master keys, CMK)
  • Data keys (DK, symmetric)
  • Data key pairs (asymmetric)

CMKs never leave KMS. You request KMS to perform cryptographic operations (like encryption or signing) for you.

Data keys and key pairs are generated in KMS, are not stored in KMS, and are intended for
programmatic uses.

For symmetric keys, the KMS gives you a plaintext key and the same key encrypted to CMK. Your application performs encryptions, removes the plaintext key, and stores the key encrypted to a CMK along the ciphertexts; this pattern is called envelope encryption.

For asymmetric keys, the KMS gives you a plaintext key pair and the private key encrypted to CMK. Your application creates signatures or encrypts data, deletes the private key, and keeps the public key and encrypted private key (along with signatures/ciphertexts).

Both types of data keys can be used with Decrypt operation to get plaintext keys again.

Access to keys is subject to authorization policies, including key policies, IAM policies, and grants. Cross-account access for keys can be enabled.

Keys can be identified in multiple ways: ARN, Id, Alias ARN, and Alias name. Keys are usually per-region (single-region), but multi-region keys can be created too.

Enclave-KMS communication

There are two mechanisms that are in play when integrating KMS with Nitro Enclaves:

  • KMS policies restricting access to CMKs to specific enclaves (by PCR values)
  • KMS encrypting responses to enclave’s public keys

In the first mechanism, the key policy may authorize access to only requests that contain fresh and correctly signed attestation documents with the expected PCR values. Enclaves have to generate attestations and include them in requests to KMS. Note that the enclave still needs IAM credentials to access KMS in the first place.

The second mechanism is about enclaves sending asymmetric public keys (inside the attestation documents) to KMS, and KMS encrypting part of the responses to the key. This mechanism is supposed to ensure that only the requesting enclave can see output from KMS.

Only a few KMS operations support these two mechanisms. The operations are:

  • GenerateDataKey,- GenerateDataKeyPair
  • Decrypt
  • DeriveSharedSecret
  • GenerateRandom

Note the absence of the Encrypt operation: enclaves can request this operation, but without the
attestation-based security mechanisms. CMKs cannot be used directly by enclaves for encryption
without missing on the attestation checks. This means cryptography operations are supposed to be
implemented via data keys, and not directly via CMKs.

Use cases

KMS can be integrated with Nitro Enclaves for various reasons: for application-specific needs, to sign enclave image files (EIFs), or to increase the entropy available in the enclave.

The application-specific use cases are based on KMS’ ability to verify attestation documents, which in turn enables developers to write KMS authorization policies based on PCR measurements from the attestation. A common use case is implementation of authenticated external storage for the enclaves. When access to KMS keys is restricted by PCRs 0-2, only a specific enclave version has access to the keys.

Enclave image files can be signed. Any signing certificate (private key) can be used for the task, but the officially supported ways include signing with a key stored in a local file, and via KMS. The signing certificate used for the EIF is then exposed as PCR8. This PCR can be used in KMS policies. This feature lets one to restrict access to KMS keys to enclaves created by the same developer, while developer identity is protected by the KMS too.

Finally, the GenerateRandom method of
KMS can be used to
add more entropy to the
enclave. While not critically
important – enclaves already have access to high quality entropy from the hypervisor – additional
randomness may increase trust in the system. On the other hand, one may argue that the added
complexity exceeds the benefits. No strong opinions here.

Passive attack prevention

Threats to the enclave-KMS communication can be divided into two categories: passive and active. Passive attackers can observe traffic and modify data that is stored outside of the enclave and is not attested. Active attackers can additionally modify all traffic coming in and out of the enclave.

The exact landscape of passive attacks depends on specific system design, but KMS operations allow us to reason about them fairly well, as an attacker can control any and all of the inputs to these operations. This tl;dr checklist helps avoid passive attacks:

  • Requests to KMS always contain the Recipientparameter.
  • Encryption context is used for supported operations.- Context is decided by enclaves, and is not fully attacker-controlled.
  • EncryptandGenDataKeyoperations are authorized properly.

  • Data encrypted with data keys has context.- Key commitment is considered.

  • Correct CMK is used.- CMK ARN is hardcoded.

  • keyIdfrom response is checked.
  • Decryptrequests always specify key ID.
  • IAM role is attested.
  • Full ARNs are used, key aliases are not used.

  • Freshness/replay attacks are mitigated.

  • Side-channel attacks are considered.
  • Key types and cryptographic algorithms are validated.

The Recipient
parameter includes
attestation, which allows KMS to validate PCRs. If key policies are correctly configured, requests
without this param fail, so it is rather hard to miss.

A single CMK key can be used to generate multiple data keys and shared secrets. Since the encrypted data keys are stored outside of the enclave, an attacker can swap them. It is therefore important to cryptographically distinguish the ciphertexts, and the encryption context is one of the ways to achieve that. Importantly, this solution works only if the attacker does not have full control over the encryption context; if they do, they can swap the ciphertext blob while also making the enclave use the wrong context.

Below are diagrams for simple “data swap” attacks that encryption contexts can prevent.

Passive attackers that can call Encrypt (or ReEncrypt) on a CMK can perform an even more severe
version of the attack above and swap the DK-ciphertext pair with a custom one, effectively providing
arbitrary plaintext to the enclave. The same issue applies if an attacker can call
GenerateDataKey. Note that some cases may
require authorization to these
operations for non-enclave entities, but this authorization should be revoked after the initial
setup.

The attacks we’ve discussed so far have been on the “envelope” level. Similar issues exist on the DK level if a DK is used multiple times (though this rarely happens). These issues should be solvable with correct encryption context implemented via AAD.

Some funky attacks are possible if an algorithm without key commitment property is used with data keys: an attacker can generate a single ciphertext that correctly decrypts under different keys. Though this is unlikely, the key commitment should be considered as part of a security audit.

The next class of attacks is when the host can select CMK that enclave uses. The exact nature of the attack depends on specific degrees of freedom, but in the worst case, the host can force the use of a completely unprotected CMK.

To protect against these attacks, the enclave must ensure the expected CMK is used; this can be done
by hardcoding full ARN, so it is attested. Then the attested ARN must be provided as the optional
keyId
parameter in Decrypt requests, and validated against keyId from KMS responses. Note that the
keyId param is optional, because CiphertextBlob includes a reference to the CMK as metadata
(HBKID in Appendix A): the metadata is not cryptographically protected, and the attacker may be
able to manipulate it.

Using key aliases instead of ARNs is possible but risky, as the aliases are more ambiguous.
Specifically, an attacker can manipulate the enclave’s IAM credentials to trick the enclave into
using the wrong CMK. That’s why we recommend attesting the IAM role that the enclave must use and
validating that role against IAM credentials provided at runtime. Enclave can do this by calling
sts:GetCallerIdentity.

Replay attacks are an interesting attack vector. Attestations include timestamps that KMS validates
to be at most five minutes old. While this means old documents cannot be replayed, there is still a
time window when a malicious host can observe a document and use it multiple times. As attestations
are not cryptographically bound to the requests, the attacker can use the attestation with any
supported operation with arbitrary params. Although the responses are encrypted with the
attestation’s public key and cannot be decrypted by the attacker, this gives the attacker some
abilities that must be considered during an audit. For example, an attacker can request multiple
decryption with different CMK keys and later use the KMS responses to confuse the state machine of
the enclave. Note that the user_data and nonce fields from attestation documents are not used by
KMS at all.

Even when an attacker cannot observe exact traffic exchanged with KMS, the attacker can note times, orders, and sizes of communication. This may be used to deduce some information, depending on the specific protocol your enclaves implement.

Finally, requests and responses to KMS include many key specifications and algorithm identifiers
(CMK KeySpec, attestation’s KeyEncryptionAlgorithm, Decryption operation’s
EncryptionAlgorithm, for example). Ideally these must not be attacker-controlled in requests (e.g.
are bundled in EIF) and the identifiers from responses are checked against the expected ones by the
enclave.

Active attack prevention

As a reminder, active attackers can additionally modify all traffic coming in and out of the enclave. This tl;dr checklist helps avoid active attacks:

  • Active attacks are prevented with enclave-initiated TLS.
  • TLS CA is bundled inside the enclave (attested).
  • VPC is used.

Many problems may arise when active attacks are in scope. Most importantly, the attestation and its
pubkey are not bound to other parts of the request. This allows the attacker to change the CMK ID in
requests and responses (even if the ID is bundled in EIF); to encrypt any data key under the
attestation pubkey and use it for replays; or to attack not-authenticated AES-CBC encryption in
CiphertextForRecipient responses.

These vulnerabilities are basically unsolvable without a secure communication channel. Therefore, TLS initiated inside the enclave is required if active attacks are in scope. For the enclave-initiated TLS solution to be secure, the enclave’s CA set must be limited; ideally, the KMS’ CA certificate (Amazon’s) is attested and pinned.

With this setup, the active attacker threat may be considered prevented. Note that having a secure communication channel implicitly prevents some of the possible vulnerabilities described in the “passive attacks” section.

KMS terminates TLS outside of HSM (most likely), and the attestation’s pubkey encryption is probably done outside of HSM. This makes it impossible to have an end-to-end TLS channel between enclave and HSM, and AWS insiders may theoretically constitute an active attacker threat. Your threat model should account for this possibility.

To further protect the communication channel, VPC can be used. This ensures that traffic never leaves AWS infrastructure and generally isolates the parent EC2 at the network level. Moreover, key policy can authorize requests based on the VPC. This makes attacks easier to detect in case of stolen IAM credentials; this is valuable even if key access is authorized via PCRs, as demonstrated in the previous sections.

KMS policies

Correctly authorizing access to CMK keys is critical. The list below includes basic checks for your KMS key policy. AWS’ recommendations for IAM policies provides more generic advice.

  • Configured KMS policy authorizes enclaves in a reasonable way.- No unexpected IAM roles have or can get access.
  • PCR0 is used for authorization. PCRs 1-2 are used for defense in depth. Alternatively, PCR8 is used.
  • Principal for RecipientAttestationis not a wildcard.
  • PCR3 is used to restrict by EC2 IAM role.
  • kms:EncryptionContextcondition is used when relevant.

  • For critical key operations (e.g., deletion) the policy requires MFA.

  • TLS and VPC restrictions are considered.
  • For end-to-end security, the clients can verify that the enclave uses correct and properly
    secured KMS keys.- Immutable key policies are likely not possible, and clients must be aware of this.

Of course, the exact CMK policy setup is business-dependent. Generally, you should ensure that the
key can be managed only by the expected IAM principal, and the principal doesn’t have access to
Decrypt operation (and possibly others like GenDataKeys and Encrypt).

The figure below shows an interesting example of a vulnerable key policy that violates the “only expected IAM principal” check. One may assume that only the root user and the enclave can operate on the key, but this is incorrect: the first policy entry grants full access to any IAM role that has access to the key configured in the role’s policy. The fix is to use a specific IAM user or role instead of root or to add an explicit deny statement for non-root users.

For the PCRs, you want to use PCR0, as it binds the policy to specific enclave code. Additionally, using PCRs 1-2 is recommended for the reasons stated in our blog post on the Nitro Enclaves attack surface. Alternatively, you can use PCR8, which allows updating the enclave code without needing to update key policy. This allows more restricted access to key policy modification permission at the cost of managing the signing key.

The Principal field and PCR3 measurement provide further restrictions. Principal is used to
authorize the IAM role used to access KMS, while PCR3 is measured by hypervisor at the time of
attestation request based on EC2 role. The EC2 role can be dynamically changed and should be
considered untrusted from the enclave’s perspective. Yet both Principal and PCR3 can be used to
prevent attackers from running (signed) enclaves on their own EC2 instance (which could make
side-channel attacks easier) and accessing the KMS key.

Access to the key can be further improved with TLS and VPC restrictions. VPC can be enforced with
aws:SourceVpc and similar condition keys. TLS can be enforced with the aws:SecureTransport
condition (although this condition is redundant, as it’s not possible to access the KMS API with
plain HTTP).

As the key has to be manageable by some IAM role (at least to allow key deletion), the
aws:MultiFactorAuthPresent and aws:MultiFactorAuthAge conditions can be used to strengthen the
authorization.

KMS policy end-to-end verification

So far, our discussion has focused on how to secure the KMS keys. A much more difficult problem arises when you want your system to provide end-to-end verifiability to end-users. If enclaves can be reproducibly built and remotely attested by users, then users likely have to validate that the KMS keys are properly protected, too. Otherwise, a malicious insider can pass remote attestation (not modify enclave code), yet use KMS directly with IAM permissions to get full access to the keys.

One solution is to hardcode the hash of the key policy in the enclave, provide full policy along
with enclave’s code to clients, and make the enclave validate the hash against the dynamically
obtained policy before sending attestation-protected requests to the KMS. This requires the enclave
to have kms:GetKeyPolicy and kms:DescribeKey permissions.

This alone doesn’t prevent attacks. A malicious IAM user can dynamically change the policy after the
enclave’s verification. To prevent this, the policy has to be made immutable, which can be achieved
by blocking kms:PutKeyPolicy permission for all users. Note that
--bypass-policy-lockout-safety-check
flag is required to
insert such a statement via CLI.

Locking access by PCR0 and denying all kms:PutKeyPolicy operations makes the system quite
immutable. This has the obvious downside of making updates and bug fixes difficult. As mentioned
earlier, the specific setup must be adjusted based on business requirements.

Note that key owners can always contact AWS support to restore default key policies. How AWS authenticates such requests I do not know, but AWS likely won’t check if the key is used in an enclave-enabled setup. This makes a system with full end-to-end trust hard to implement.

Finally, consider implementing publicly observable and verifiable monitoring and alerting for key policies. Such a system would alert end users when a policy changes, mitigating the impact of policy restoration by AWS support. However, we are not aware of any “Certificate Transparency”-style public, append-only log for KMS key policies that an external party can independently verify.

Operational concerns

Even if the system is secure point-in-time, there are operations that must be periodically performed. These introduce new risks into the system. This checklist covers these concerns:

  • Key rotation and revocation is implemented for CMK.- ReEncryptoperation is not used for data keys.

  • Backups:- Risks from CMK destruction are mitigated.
  • Regional outages are considered.
  • Data keys are backed up as needed.

  • Users cannot cause a denial of service or balloon the bill.- The number of user-triggered KMS operations is limited.

  • Request quotas are considered.
  • Limits on data lengths are respected.
  • KMS’s clients take into account delays in KMS updates.

AWS provides mechanisms to
easily rotate CMK keys. The only item to note here is that rotating a compromised CMK does not make
data keys protected by it non-decryptable. For a CMK revocation, a more involved approach than
just rotating CMK and destroying data keys must be implemented.

Rotating data keys is hard to implement securely, as the KMS
ReEncrypt operation does
not support attestations. The system should be designed so that such rotations are not needed.

A malicious actor deleting CMK keys permanently creates a risk of non-recoverable system state. The system’s design can sometimes be made so that destruction of a single key is recoverable (e.g., by setting up key hierarchy and using secret sharing). Nevertheless, there should be security controls in place mitigating the risk. First, configure a scheduled deletion period for keys to a time in which your team can act on an incident. Set up CloudWatch alarms for KMS keys for deletion events, and tighten IAM policies with Service Control Policies that prevent KMS key deletion.

Single-machine disasters in AWS infrastructure are not a concern, as single-region KMS keys are replicated within the region in multiple Availability Zones in multiple HSMs. However, if the system must be resilient to a regional outage, multi-region keys should be used instead of single-region keys.

Encrypted data keys backups are a responsibility of the system, not AWS. Note that the CMK key may become unusable in a few scenarios, and the data key backup system must account for this.

Yet another set of risks relates to billing. AWS charges dollars per KMS operations and CMK key maintenance, so the system must not let end-users make the enclaves send arbitrary many requests to KMS. When implementing rate-limits, KMS quotas must be taken into account.

Inputs to KMS have various size limits. For example, plaintexts can be up to 4096 bytes long, ciphertexts can be up to 6144 bytes long, and key IDs can be up to 2048 bytes long. These limits are unlikely to be reached with attestation-supported operations, but still should be considered.

Finally, changes to KMS resources need some time to propagate and synchronize inside AWS infrastructure. Your system must expect delays and possible temporary inconsistencies when requesting KMS.

Software and SDKs

Amazon ships a lot of SDKs for various tasks. Among them is
aws-nitro-enclaves-sdk-c that provides tools
and a library for enclaves-KMS communication. Avoid it: this particular SDK is written in C, and we
found it contains vulnerabilities that can be used to exploit enclaves from the parent host.

Rather than using the aws-nitro-enclaves-sdk-c, we recommend a combination of other libraries,
such as the following:

  • aws-nitro-enclaves-nsm-api(in Rust) to get attestation documents
  • KMS.Clientfrom Boto3 (in Python) to communicate with KMS
  • Any cryptographic library to parse and decrypt responses

Final notes

Many issues can arise from misusing the KMS within enclave-secured systems. This blog post does not
even cover all supported operations (GenerateDataKeyPair, DeriveSharedSecret), possible
vulnerabilities (key reuse, key wearout, forward secrecy, nonce management, …) and system features
(custom key stores, multi-region keys, …). Make sure to document your system’s protocol, have a
cryptographer review it, and check the actual implementation against it.

Appendix A

Data formats of the CiphertextBlob and CiphertextForRecipient structures are presented below.