Nitro Enclaves are the AWS take on Trusted Computing Environments (or TPE). Running a workload inside such an enclave enables two things:
- The only possible interactions between host and workload take place over services exposed over the vsock interface.
- The workload can prove to external services that it’s indeed running inside a Nitro Enclave.
The first point is relatively straightforward. Nitro Enclave workloads have no network or persistent filesystem access and their EC2 hosts cannot access CPU or memory resources used by the workload, leading to strong isolation.
The escape hatch to this isolation is services exposed over the vsock interface. Workloads can access services on the host on context ID (CID) 3 and hosts can access services on workload-specific CIDs, both bound to arbitrary ports.
Workloads that require regular network access need to do so through some type of proxy1 provided by the host, over a vsock port.
The second point is more involved. A workload can prove to another party that it is running inside a Nitro Enclave by generating a cryptographical attestation document. Optional values such as a public key, generic user-data, and a nonce can tailor this document to the protocol requirements. After verifying the signature and contents of the attestation document, a receiving party can verify the following claims:
- A Nitro Enclave hypervisor signed this document with a valid signature.
- The measurements of the workload (expressed as platform configuration registers or PCRs), such as e.g. contents of the image, IAM roles used, instance ID of the host or a signing certificate used by the creator of the workloads.
AWS Key Management Service (KMS) for examples uses these properties to enable the usage of PCRs as KMS key policy conditions. Use those restricts the usage of supported operations to callers which can generate valid attestation documents tied to specific PCRs.
Unfortunately, as of today (January 2025) the official documentation for using KMS together with cryptographic attestations is misleading and sparse, making Nitro Enclaves more difficult to use when deviating from the official SDKs.
In “Cryptographic attestation for AWS Nitro Enclaves” the documentation talks about using “the public key” of the Nitro Enclave. This is misleading. A workload can maintain arbitrary RSA keypairs and use / re-use them, however it sees fit. The public key in question is just a parameter passed into the attestation document generating API of the Nitro Enclave.
The key generation methods documentation correctly states that the data key is encrypted (somehow) using this public key and some (vague) KMS key. Looking into the API documentation of e.g. “GenerateDataKey” does not make the situation much clearer.
The “Recipient” request parameter at least clears up the second bit: the mysterious KMS key used is the key itself. In order to decrypt the “CiphertextBlob” out-of-band, a client would need to call the KMS Decrypt operation with a supplied attestation document and follow the steps to decrypt the “CiphertextForRecipient” field.
Unfortunately, the method for decrypting “CiphertextForRecipient” remains undocumented.
The following steps need to be completed to request any such payload:
- Generate a RSA keypair.
- Encode the public key into ASN.1 DER and use this representation as the parameter for the Nitro Enclave API to generate an attestation document (as untagged CBOR message with a COSE signature).
- Add this attestation document along with “RSAES_OAEP_SHA_256” as the key encryption algorithm to the KMS request parameters.
In reverse, the following steps need to be completed to decrypt any such payload:
- Parse “CiphertextForRecipient” as ASN.1 BER encoded RCF5652 CMS message.
- Extract the encrypted key “recipientInfos/encrypedKey” and use the OAEP scheme with SHA256 and the private key from the matching RSA keypair to decrypt it.
- Extract the initialization vector (IV) from the “encryptedContentInfo/contentEncryptionAlgorithm/parameter”.
- Decrypt “encryptedContentInfo/encryptedContent” with AES in CBC mode using the decrypted key and IV.
- Remove PKCS#7 padding.
A demo showcasing these exact steps (including necessary infrastructure) is available on GitHub.
Unfortunately, the CMS message does not contain a signature the proves the KMS did in fact create the message. It also does not commit to the parameter used in the KMS request (such as e.g. “NumberOfBytes” or the encryption context). While the host remains unable to read contents of messages, it can still manipulate and generate arbitrary messages.
Without a change to this protocol, workloads sadly cannot use KMS without a direct network connection.
-
Typically some form of layer 3 proxy to prevent the host from inspecting or manipulating TLS payloads exchanged between workload and external services (e.g. vsock_proxy). ↩︎