AWS Nitro Enclaves and Confidential Computing on AWS - Attestation, PCR Measurements, and KMS Condition Keys
First Published:
Last Updated:
AWS Nitro Enclaves is the Amazon EC2 capability that answers that requirement, and cryptographic attestation is what turns the answer from a promise into a control you can audit. An enclave is a separate, hardened, highly constrained virtual machine carved out of a parent EC2 instance's own vCPUs and memory, and AWS states plainly that "the data and applications inside the enclave cannot be accessed by the processes, applications, or users (root or admin) of the parent instance." That is the isolation. The part that makes it useful is the second half: the Nitro Hypervisor will sign a document describing exactly what code is running inside the enclave, and AWS KMS will refuse to decrypt anything unless that document matches values you wrote into your key policy.
That single mechanism — a key policy that names a measurement of your code rather than only a principal — is the spine of this article. Everything else is a consequence of it: why your build has to be reproducible, why pinning the wrong measurement makes an autoscaled fleet undeployable, why the order in which you update the enclave and the key policy determines whether you have an outage, and why multi-party data collaboration becomes possible at all.
AWS documents the mechanism accurately, but in pieces, across the Nitro Enclaves User Guide, the KMS Developer Guide, the KMS API Reference, several blogs, and now the EC2 User Guide as well. What is thin, even in English, is the layer between "an enclave can prove its identity" and "here is which of the six measurements you should actually pin, here is the deployment order that avoids locking yourself out, and here is what you and your counterparties have to agree on before any of it means anything." That gap is what this article fills.
1. Introduction: Why "the Operator Must Not See Plaintext" Becomes a Requirement
The requirement has several origins and one shape. It arrives as a regulatory obligation about a specific class of data, as a contractual term with a customer who will not send you their records otherwise, as an internal separation-of-duties rule that survives an audit finding, or as the precondition for two companies to compute something over their combined data without either one handing the other a copy. However it arrives, it reduces to the same technical question: can you name, in a policy that a machine enforces, the exact code that is allowed to see the plaintext?The audience for this article is the security architect or platform engineer who has to answer that question with an AWS design, and who will be asked to defend the answer. You are assumed to be comfortable with KMS key policies and IAM policy evaluation, and to know what envelope encryption is. You are not assumed to have used Nitro Enclaves.
What this article covers: what confidential computing means in the specific sense AWS uses it and what the resulting threat model does and does not include; the enclave isolation model and the three things it forces you to redesign; how an enclave image is built and measured; the structure of the attestation document and who verifies it; the KMS condition keys and the five API operations that honor them; key policy design patterns, including the deployment ordering that keeps you from locking yourself out; the data flow through vsock and where plaintext exists at each hop; multi-party collaboration and what the parties must agree on; the CloudTrail evidence and what to alert on; the operational realities of running a workload you cannot log into; how enclave attestation differs from the newer EC2 instance attestation; and the failure modes.
What it deliberately does not cover, because each already has a home:
- Envelope encryption and data key design — how data keys are generated, scoped, cached, and destroyed, and how the encryption context works — belongs to my AWS KMS Envelope Encryption and Data Key Caching Patterns. That guide answers how the data is encrypted. This one answers the question that sits directly above it: who is allowed to decrypt.
- The cryptographic primitives themselves — AES-GCM, SHA-384, ECDSA, RSA-OAEP, COSE, CBOR — are defined in my Cryptography Glossary for Engineers. This article uses them by name and does not re-explain them.
- Post-quantum migration for the algorithms underneath all of this is tracked in my Post-Quantum Cryptography Standardization Timeline and Migration on AWS.
- How key policies and IAM policies combine to produce an allow or a deny is stepped through in my IAM Policy Evaluation Logic Step by Step. This article assumes that evaluation model and adds one condition to it.
- The service history of KMS, including when each capability referenced here appeared, is in my AWS History and Timeline regarding AWS Key Management Service.
- Pricing. This site does not quote prices, and the decision to adopt an enclave is not a pricing decision. It is a decision about how far the reach of a key extends, whether a regulatory or contractual obligation can be met at all, and whether a multi-party computation can exist. Those are the terms this article argues in.
- Container orchestration. A container image is used as the input format when building an enclave image, and that is the entire extent of the container discussion here.
Every service behavior, condition key name, API name, measurement definition, and constraint below was verified against AWS documentation while writing, on 2026-08-04. Condition key names and PCR numbers are quoted exactly; a single character of difference changes what your key policy authorizes.
2. What Confidential Computing Means Here
The phrase has been stretched across so many products that it is worth pinning down which claim is being made before designing against it.2.1 The two dimensions, and which one enclaves address
AWS frames confidential computing along two dimensions. The first is protection of customer workloads from the cloud operator and from cloud system software. The second is the division of a customer's own workload into more-trusted and less-trusted elements. The distinction matters enormously in practice, because the two dimensions are delivered by two different things.The first dimension is delivered by the AWS Nitro System, for every modern EC2 instance, whether or not you ever create an enclave. The Nitro System whitepaper is unambiguous: "By design the Nitro System has no operator access. There is no mechanism for any system or person to log in to EC2 Nitro hosts, access the memory of EC2 instances, or access any customer data stored on local encrypted instance storage or remote encrypted EBS volumes." AWS also notes that this design has a cost it accepts — in rare cases AWS operators cannot debug an issue in place, and must reproduce it with the customer on dedicated non-production hardware.
The second dimension is what Nitro Enclaves is for. An enclave protects the workload from the rest of the customer's own environment: from the administrators, the operators, the other applications on the box, and the libraries you did not audit.
This produces the single most common misreading of the technology, and it is worth stating flatly. Nitro Enclaves is not the thing that protects your data from AWS. The Nitro System already does that, for ordinary instances. Nitro Enclaves is the thing that protects your data from you. If you deploy an enclave in order to answer the question "can AWS see my data," you have bought a complex tool to solve a problem AWS documents as already solved, and you will find the tool's constraints unbearable for a benefit you already had.
2.2 The threat model, stated explicitly
Security guidance that does not say what it fails to contain is not guidance. The table below separates what AWS states from what follows as a design consequence. The right column names the origin of each claim, because the two categories should not be defended the same way in a review.| Adversary or scenario | Contained | Origin of the claim |
|---|---|---|
| An AWS operator reading instance memory or customer data | Yes | AWS states the Nitro System has no operator access. This is a property of the Nitro System, not of enclaves |
| A root or admin user on the parent instance reading enclave memory | Yes | AWS states the enclave's isolated vCPUs and memory cannot be accessed by the processes, applications, kernel, or users of the parent instance |
| Another process or library on the parent instance | Yes | Same statement. The parent has no path into the partitioned vCPUs and memory |
| A second enclave on the same parent instance | Yes | AWS states there is no vsock connection between enclaves, and enclaves on the same or different parents cannot communicate with each other |
| Someone who obtains a copy of your enclave image file | No | AWS states the EIF contains unencrypted copies of all code and data, and that unencrypted secrets must not be part of the EIF |
| Someone who modifies the code before the image is built | Detected, not prevented | The modification changes the measurements, so a key policy pinned to the previous measurements denies the request |
| An enclave started in debug mode | No | AWS states debug-mode enclaves produce attestation documents with all-zero PCRs that cannot be used for cryptographic attestation |
| The parent withholding, delaying, or replaying enclave input | Not addressed by the isolation model | Design consequence. vsock is the only channel, so the parent mediates every byte |
| The parent denying service to the enclave | No | AWS states enclaves are active only while the parent instance is running, and are terminated when it stops or terminates |
| Inference from the size and timing of vsock traffic | Not addressed | Design consequence. Confidentiality of payloads is your protocol's job, not the isolation boundary's |
The rows marked as design consequences are my reading of what the documented constraints imply, not statements AWS makes. They are, in my experience, the rows that decide whether an enclave design survives contact with a real adversary model, because they establish that the enclave is a confidentiality and integrity boundary, not an availability boundary. The party that runs the parent instance can always stop the computation. What it cannot do is see inside it.
2.3 Measurements are identity, not secrets
One more idea has to be settled before the mechanics make sense.A measurement — a PCR value — is a hash of an artifact. It is not a credential and it is not a secret. Anyone who has your enclave image file can compute the same value, and anyone who can reproduce your build can compute it without your image at all. That is not a weakness; it is precisely the property that makes attestation useful, because it is what lets a counterparty independently confirm that the value in their key policy corresponds to code they have inspected.
The practical consequence is that a measurement authorizes what, never who. AWS KMS evaluates both. The request still carries AWS credentials and still has to satisfy the principal and action in the key policy statement; the attestation condition narrows an already-authorized principal down to a specific piece of code. Read the official example statement carefully and you can see both halves: it names an IAM role as the principal and requires a matching image digest. Neither half is redundant. Stolen credentials without the right code are denied. The right code without credentials never reaches the evaluation.
I find it useful to describe this to reviewers as a two-turn lock. It is also the reason the design degrades gracefully: if the credentials leak, the blast radius is bounded by the measurement; if the image leaks, the blast radius is bounded by the credentials.
3. The Enclave Isolation Model
3.1 The four constraints
AWS describes enclaves as "separate, hardened, and highly-constrained virtual machines" that "provide only secure local socket connectivity with their parent instance," with "no persistent storage, interactive access, or external networking." Those four constraints are the entire security argument, and each one is also a design problem you inherit.| Constraint | What it removes as an attack path | What it removes as a capability you had |
|---|---|---|
| No external networking | Exfiltration directly from the enclave; inbound connections that bypass the parent | Calling any API, fetching any dependency, or reaching any database on your own |
| No persistent storage | Plaintext surviving on disk; recovery of state by whoever mounts the volume later | Caches, checkpoints, and any state that outlives the process |
| No interactive access | An operator attaching a debugger or opening a shell | Your own ability to log in and diagnose the running workload |
| Only vsock to the parent | A network path an attacker can reach | Every form of I/O except a local socket you write both ends of |
Two further boundaries are worth knowing before you architect around them. The enclave itself must run Linux, while the parent instance may run Linux or Windows Server 2016 or later. And Nitro Enclaves is processor agnostic: AWS states it is supported on most Intel, AMD, and AWS Graviton-based EC2 instance types built on the Nitro System, with the smallest size in each family and, generally, the bare metal sizes excluded.
3.2 vsock, and what it is not
The only channel is a vsock socket, addressed by a context identifier rather than an IP address. The parent instance is always CID3. Each enclave receives its own CID, which you may set with --enclave-cid on launch — AWS notes that "only CIDs of 4 and higher can be specified" — and which is allocated at random if you omit it. On Linux the interface is ordinary POSIX socket calls such as connect, listen, and accept; on Windows it is Winsock2, where AWS notes that loopback is not supported.Enclaves launched from the same parent share the vsock, but AWS is explicit that "there is no vsock connection between the enclaves." There is no enclave-to-enclave channel at all, on the same parent or across parents. Every interaction between two enclaves is relayed by parents, which matters in Section 9.
The design consequence is easy to state and easy to forget: you write both ends of every conversation the enclave has. There is no framework default, no service mesh, no retry library that already knows the protocol. AWS's own guidance suggests a non-blocking, multi-threaded implementation inside the enclave so requests can be processed in parallel, and notes that virtio-vsock language bindings exist for languages including Rust, Go, and Python.
3.3 What the constraints force you to redesign
Three parts of an ordinary application do not survive the move into an enclave, and discovering this late is the most common reason an enclave project stalls.Logging and observability. There is no log agent inside the enclave and no route by which one could ship anything. Whatever you emit crosses the vsock and is therefore visible to the parent — the very party you were isolating from. This inverts the normal habit: instead of logging generously and redacting later, you must decide the sensitivity of every log line before you write it, because the boundary is crossed at the moment of emission. Debug mode is not the escape hatch it appears to be, for the reason in Section 4.5.
Secret supply. The enclave image file is not secret. AWS states it directly, in the context of the KMS integration: "The EIF contains unencrypted copies of all code and data, so unencrypted secrets must not be part of the EIF." Anything the enclave needs to keep secret has to arrive at runtime as ciphertext and be opened only inside. That is the entire purpose of the KMS integration in Section 6, and it is why the mechanism exists at all.
Dependency resolution. There is no package manager at runtime, no fetching a model file at boot, no pulling an OIDC discovery document or a CRL. Everything is either baked into the image — and therefore measured — or supplied over vsock — and therefore data rather than code. This produces the coupling that dominates the rest of this article: a dependency you bake in becomes part of your measurements, so bumping a library version changes the value your KMS key policy has to authorize. A dependency upgrade is, structurally, a key policy change. Section 7.2 is about surviving that.
3.4 Capacity is allocated, not elastic
An enclave's vCPUs and memory are partitioned away from the parent in advance by the Nitro Enclaves allocator service, configured in/etc/nitro_enclaves/allocator.yaml. AWS notes the default configuration preallocates 512 MiB of memory and 2 vCPUs.The launch-time rules constrain the design more than people expect:
- You must allocate a minimum of
64MiB of memory to an enclave, and the amount cannot exceed what the parent provides. - If the parent has multithreading enabled you must specify an even number of vCPUs and leave at least 2 vCPUs for the parent; if multithreading is not enabled you must leave at least 1.
- You supply either
--cpu-countor--cpu-ids, never both. - You can create up to four individual enclaves per parent instance, and the allocator must be configured to preallocate enough for all of them. AWS's worked example: four enclaves of four vCPUs each on a multithreaded parent requires an instance with at least 18 vCPUs.
- Each enclave has its own CID and its own ID, can be terminated individually, can be sized differently, and can be created from the same or a different enclave image file.
Three placement facts belong in the same decision. AWS states that hibernation and enclaves cannot both be enabled on the same instance, that Nitro Enclaves is not supported on AWS Outposts, and that it is not supported in Local Zones or Wavelength Zones. Availability is otherwise not a constraint any more: AWS announced on October 21, 2025 that Nitro Enclaves is available across all AWS Regions, and the User Guide now states it is supported in all AWS Regions including the AWS GovCloud (US) Regions.
4. Building and Measuring an Enclave
4.1 From image to enclave image file
An enclave boots from an enclave image file, an.eif that AWS describes as containing "everything that is needed to run the application inside the enclave, including the application code, runtimes, dependencies, operating system, and file system." You produce one with nitro-cli build-enclave, which converts a Docker image into an EIF. AWS is careful about why: "Nitro Enclaves uses Docker images as a convenient file format for packaging your applications" — the container image is an input format, and nothing about container orchestration follows from it.nitro-cli build-enclave --docker-uri sample:latest --output-file sample.eif
Two operational facts about this command shape your pipeline. It is not supported on Windows, so a Windows parent instance requires you to build on a Linux machine and transfer the
.eif. And AWS notes you "can build enclave images files using the Nitro CLI on any Linux environment, including outside of AWS," while lifecycle commands such as run-enclave must run on an enclaves-enabled parent instance. Building centrally and distributing one artifact is therefore possible, and it is the right choice, because the measurement is the artifact's identity — building separately on each host means each host may present a different identity.The command's output is the point of the exercise:
{
"Measurements": {
"HashAlgorithm": "Sha384 { ... }",
"PCR0": "EXAMPLE59044e337c00068c2c033546641e37aa466b853ca486dd149f641f15071961db2a0827beccea9cade3EXAMPLE",
"PCR1": "EXAMPLE7783d0c23167299fbe5a69622490a9bdf82e94a0a1a48b0e7c56130c0c1e6555de7c0aa3d7901fbc58EXAMPLE",
"PCR2": "EXAMPLE4b51589e8374b7f695b4649d1f1e9b528b05ab75a49f9a0a4a1ec36be81280caab0486f660b9207ac0EXAMPLE"
}
}
You can recover the same values later from an existing artifact with
nitro-cli describe-eif --eif-path image.eif, without launching anything. That command deserves a place in your release pipeline, because it turns "which measurements does this build produce" into a question you answer before deployment rather than after a denied request.4.2 The six measurements
An enclave has six measurements. AWS defines them as follows.| PCR | Hash of | Description |
|---|---|---|
| PCR0 | Enclave image file | A contiguous measure of the contents of the image file, without the section data |
| PCR1 | Linux kernel and bootstrap | A contiguous measurement of the kernel and boot ramfs data |
| PCR2 | Application | A contiguous, in-order measurement of the user applications, without the boot ramfs |
| PCR3 | IAM role assigned to the parent instance | Ensures that the attestation process succeeds only when the parent instance has the correct IAM role |
| PCR4 | Instance ID of the parent instance | Ensures that the attestation process succeeds only when the parent instance has a specific instance ID |
| PCR8 | Enclave image file signing certificate | Ensures that the attestation process succeeds only when the enclave was booted from an enclave image file signed by a specific certificate |
Only PCR0, PCR1, and PCR2 fall out of the build. AWS states that the others "need to be manually generated based on information about the parent instance," and gives the recipe: PCR3 is a SHA-384 hash over the ARN of the IAM role associated with the parent's instance profile, and PCR4 is a SHA-384 hash over the parent's instance ID. In both cases AWS's example pads the hash with 48 null characters before the value:
ROLEARN="arn:aws:iam::123456789012:role/Webserver"; \
python -c"import hashlib, sys; \
h=hashlib.sha384(); h.update(b'\0'*48); \
h.update(\"$ROLEARN\".encode('utf-8')); \
print(h.hexdigest())"
The same construction applied to an instance ID produces PCR4. PCR8 you do not compute by hand at all —
nitro-cli pcr --signing-certificate cert.pem returns it from the certificate, which AWS describes as performing "deserialisation of the certificate and PEM format validation."Notice what this table implies about stability, because Section 7.1 turns it into a decision. PCR0 and PCR2 move whenever your code moves. PCR1 moves when the kernel or bootstrap underneath you moves, which is to say when you upgrade tooling rather than when you ship a feature. PCR3 is stable for as long as the parent's role is stable, across any number of instance replacements. PCR4 changes every single time an instance is replaced. PCR8 is stable across code changes and moves only when the signing certificate does.
4.3 Signing the image, and why PCR8 is worth the trouble
PCR8 exists only if you build a signed image, which requires both--private-key and --signing-certificate; AWS notes that specifying one requires the other, and that "only ECDSA keys are supported for code for signing." The private key may be a local file or, usefully, a KMS key ARN — which means the authority to sign a production enclave image can itself be a KMS key policy decision rather than a file on a build host.nitro-cli build-enclave --docker-uri hello-world:latest --output-file hello-signed.eif --private-key key_name.pem --signing-certificate certificate.pem
A signed build emits PCR8 alongside PCR0, PCR1, and PCR2. AWS's guidance on why to bother is direct, and it is the most useful single sentence in the measurement documentation: using PCR8 "enables you to build more flexible condition keys that remain effective even if the enclave image or parent instance is changed. We recommend that you use PCR3 and PCR8 together for the best flexibility."
One trap comes with signing. AWS warns that a certificate which is no longer valid will not merely produce a warning: "If you attempt to start an enclave with an enclave image file that is signed with a certificate that is no longer valid, the
nitro-cli run-enclave command fails with errors E36, E39, and E11." Certificate expiry therefore becomes an availability event for the enclave, and the certificate's lifetime becomes a thing you monitor. Note that the enclave signing certificate here is a code-signing artifact you manage yourself; it is not the same as the certificate hierarchy you would design for TLS identity, which is a separate discipline.4.4 Reproducibility is the whole design
If your build is not reproducible, you do not have an attestation story. You have a ritual.The reasoning is short. The key policy authorizes a hash. If rebuilding the same source produces a different hash — because a base image floated, because a dependency resolved to a newer patch, because a timestamp or a file ordering leaked into the artifact — then every rebuild is a new identity that no key policy authorizes, and every rebuild requires a policy edit. Worse, you lose the ability to answer the only question that matters in a review: does the value in this key policy correspond to source code anyone has read?
AWS's own guidance on getting there is a good checklist, and it is short enough to hold in your head: pin the source and its dependencies, pin the base image and the dependency versions, eliminate sources of non-determinism such as timestamps and file ordering, use build tooling that is deterministic, verify by measuring the artifact, and only then convert to an EIF and read the PCRs. The verification step is the one people skip. It costs one command: build twice from the same commit, run
describe-eif on both, and compare PCR0, PCR1, and PCR2.This is also what makes Section 9 possible. A counterparty who can reproduce your build can compute the measurement themselves and confirm that the value they are about to write into their key policy corresponds to code they have inspected. Without reproducibility, asking a partner to authorize measurement X is asking them to trust whoever produced X — which is exactly the trust the enclave was supposed to make unnecessary.
4.5 Debug mode is not a testing shortcut
Launching with--debug-mode lets you read the enclave's console with nitro-cli console, and it is how every getting-started walkthrough begins. It also does something that surprises people at the worst moment. AWS states it twice, in two different guides, which is a fair signal of how often it bites: "Enclaves booted in debug mode generate attestation documents with PCRs that are made up entirely of zeros. These attestation documents can't be used for cryptographic attestation."Two consequences follow, and they are both structural rather than cosmetic.
First, you cannot debug the attestation path in debug mode. The moment you enable the console you have changed the identity the enclave presents to KMS. Testing an attestation failure therefore means running a non-debug enclave and pointing it at a test KMS key whose policy carries the measurements you expect — which is a better habit anyway, because it exercises the real code path.
Second, and more seriously: the all-zero value is a valid string, and it will happily sit in a key policy. AWS's own getting-started flow tells you to use
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 for PCR0 if you started the enclave in debug mode, which is correct for a throwaway key in a tutorial. Left in a production key policy, that value authorizes every debug-mode enclave that anyone can build, because they all present the same zeros. Grepping your key policies for a run of zeros is a two-minute audit that is worth scheduling.5. The Attestation Document
5.1 Who produces it, and who signs it
The attestation document is generated and signed by the Nitro Hypervisor, not by your code. AWS is precise about where the trust originates: "The root of trust for the enclave resides within the AWS Nitro system, which provides attestation documents to the enclave," and the documents "are signed by the AWS Nitro Attestation Public Key Infrastructure (PKI), which includes a published certificate authority that can be incorporated into any service."Your enclave requests one from inside itself. AWS states that "you can request an enclave's attestation document from inside the enclave only," using the
get-attestation-document API in the Nitro Enclaves SDK, which talks to the Nitro Secure Module device. There is no way to ask for an enclave's attestation document from the parent instance, which is the property that makes the document meaningful — a compromised parent cannot mint one on the enclave's behalf.5.2 The structure
AWS publishes the document's specification, and reading it once is worth more than any paraphrase, because every design choice in Section 5.3 is visible in it.AttestationDocument = {
module_id: text, ; issuing Nitro hypervisor module ID
timestamp: uint .size 8, ; UTC time when document was created, in
; milliseconds since UNIX epoch
digest: digest, ; the digest function used for calculating the
; register values
pcrs: { + index => pcr }, ; map of all locked PCRs at the moment the
; attestation document was generated
certificate: cert, ; the public key certificate for the public key
; that was used to sign the attestation document
cabundle: [* cert], ; issuing CA bundle for infrastructure certificate
? public_key: user_data, ; an optional DER-encoded key the attestation
; consumer can use to encrypt data with
? user_data: user_data, ; additional signed user data, defined by protocol
? nonce: user_data, ; an optional cryptographic nonce provided by the
; attestation consumer as a proof of authenticity
}
cert = bytes .size (1..1024) ; DER encoded certificate
user_data = bytes .size (0..1024)
pcr = bytes .size (32/48/64) ; PCR content
index = 0..31
digest = "SHA384"
Three details in that specification are load-bearing and easy to skim past.
The
pcrs field is a map of all locked PCRs, and the index range is 0..31 — considerably wider than the six measurements an enclave defines. That is why the KMS condition key is parameterized as kms:RecipientAttestation:PCR<PCR_ID> rather than being six fixed keys, and why the KMS documentation says you can specify "a PCR ID that identifies one of the six enclave measurements or a custom PCR ID that you defined for a specific use case."The
digest is fixed at SHA384, which is why every measurement in this article is a SHA-384 value and why the image-digest condition key is named ImageSha384.And
user_data is capped at 1024 bytes, which caps public_key and nonce with it. If you are designing your own attestation protocol, that is your entire budget for binding the document to a session.5.3 The three optional fields are where your protocol lives
AWS makes the design responsibility explicit: "The enclave and the service that wants to attest the enclave first need to agree on a common protocol to follow. The optional parameters in the attestation document (public_key, user_data, and nonce) allow the enclave and the entity to set up a variety of protocols depending on the security properties that the service and the enclave want to guarantee."| Field | Purpose | What it buys, and what its absence costs |
|---|---|---|
public_key | A DER-encoded key the consumer can use to encrypt data back to the enclave | Binds the response to this enclave instance. Without it, a verifier can conclude what code is running but has no channel that only that code can read |
nonce | A cryptographic nonce supplied by the consumer as proof of authenticity | Freshness. Without it, a document is a standing assertion that can be presented again later by whoever captured it |
user_data | Additional signed data, defined by your protocol | Lets you bind application-level context to the signature — a request identifier, a policy version, a transaction hash |
KMS is one such protocol, already agreed and already implemented. It uses
public_key exactly as described: the response is encrypted under it. If your verifier is anything other than KMS, you are the one designing the protocol, and skipping nonce is the mistake that turns attestation into theater.5.4 Validating it yourself, when the verifier is not KMS
AWS supports using other key management services — the Nitro Enclaves FAQ says so directly, adding that "you will need to build an attestation process or service to be able to validate the signed attestation documents generated by your enclave." The documentation for doing so is unusually complete, and reading it is the fastest way to appreciate what KMS is doing for you.The document is CBOR-encoded and COSE-signed using the
COSE_Sign1 structure, identified by CBOR tag 18, with the algorithm identifier -35 corresponding to ECDSA-384. AWS lists the validation as four steps: decode the CBOR object and map it to a COSE_Sign1 structure, extract the attestation document from it, verify the certificate's chain, and ensure the document is properly signed.The details that trip implementations are in the chain handling. The root certificate for the commercial AWS partitions is published for download, has a lifetime AWS states as 30 years, is based on an AWS Private CA private key, and has the subject
CN=aws.nitro-enclaves, C=US, O=Amazon, OU=AWS. It can be verified against this fingerprint:64:1A:03:21:A3:E2:44:EF:E4:56:46:31:95:D6:06:31:7E:D7:CD:CC:3C:17:56:E0:98:93:F3:C6:8F:79:BB:5B
AWS then warns about two things that are easy to get wrong. The
cabundle in the document is ordered root first, and AWS notes that "some existing tools, such as Java's CertPath ... might require them to be ordered differently" — you build the path as target certificate first and root last. And, tersely but importantly, "CRL must be disabled when doing the validation."Weigh that list honestly when choosing a verifier. Building it is a few hundred lines and a permanent maintenance obligation over a certificate chain you do not control. Using KMS as the verifier reduces the same work to writing condition keys into a key policy. Unless you have a specific reason to keep the key material outside KMS, that is the trade to make.
6. Binding Keys to Code with KMS
6.1 What changes about the API call
An attested call to KMS differs from an ordinary one in exactly one request parameter and one response field.The request carries
Recipient, a RecipientInfo structure with two members: AttestationDocument, a base64-encoded blob that AWS constrains to a maximum length of 262144, and KeyEncryptionAlgorithm, for which AWS states "the only valid value is RSAES_OAEP_SHA_256."{
"CiphertextBlob": "<base64 ciphertext>",
"KeyId": "arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
"Recipient": {
"AttestationDocument": "<base64 attestation document>",
"KeyEncryptionAlgorithm": "RSAES_OAEP_SHA_256"
}
}
The response then behaves differently. AWS describes the change generally — "instead of returning plaintext data in the response, these APIs encrypt the plaintext with the public key from the attestation document and return ciphertext that can be decrypted only by the corresponding private key in the enclave or EC2 instance" — and specifically per operation:
| AWS KMS operation | Standard response | Response for attested requests |
|---|---|---|
Decrypt | Returns plaintext data | Returns the plaintext data encrypted by the public key from the attestation document |
DeriveSharedSecret | Returns raw shared secret | Returns the raw shared secret encrypted by the public key from the attestation document |
GenerateDataKey | Returns a plaintext copy of the data key, and a copy encrypted by a KMS key | Returns a copy of the data key encrypted by the public key from the attestation document, and a copy encrypted by a KMS key |
GenerateDataKeyPair | Returns a plaintext copy of the private key, plus the public key and a copy of the private key encrypted by a KMS key | Returns a copy of the private key encrypted by the public key from the attestation document, plus the public key and a copy of the private key encrypted by a KMS key |
GenerateRandom | Returns a random byte string | Returns the random byte string encrypted by the public key from the attestation document |
Three consequences follow from that table. The encrypted result arrives in a
CiphertextForRecipient field, and AWS states that when it is present "the Plaintext field is null or empty" — so code that reads Plaintext unconditionally will silently get nothing. For Decrypt, KeyMaterialId "is omitted if the request includes the Recipient parameter." And GenerateDataKey still returns the KMS-encrypted copy of the data key, which is what you persist alongside the ciphertext; the mechanics of that are envelope encryption, and they are unchanged.Two more operational notes. The Nitro Enclaves SDK, which AWS says "is supported only within a Nitro enclave, automatically adds the
Recipient parameter and its values to every AWS KMS request" — with a plain AWS SDK you set it yourself, and all AWS SDKs support the field. And you cannot substitute your own signing authority: "AWS KMS relies on the digital signature for the attestation document to prove that the public key in the request came from a valid source. You cannot supply your own certificate to digitally sign the attestation document."6.2 The condition keys
AWS provides condition keys for attested platforms in three forms. All three apply to the same five operations and are usable in both key policies and IAM policies.| Condition key | Matches against | Notes from the documentation |
|---|---|---|
kms:RecipientAttestation:ImageSha384 | The image digest in the attestation document | "The ImageSha384 value corresponds to PCR0 in the attestation document" |
kms:RecipientAttestation:PCR<PCR_ID> | Any PCR in the attestation document | Concatenate the PCR ID to the key name. "The PCR value must be a lower-case hexadecimal string of up to 96 bytes" |
kms:RecipientAttestation:NitroTPMPCR<PCR_ID> | Any PCR in a NitroTPM attestation document | Effective only when the Recipient parameter specifies a signed attestation document from NitroTPM. See Section 12 |
Four properties of these keys decide how you write policies with them.
They are only satisfiable by an attested request. AWS states it for each key: the condition "is effective only when the
Recipient parameter in the request specifies a signed attestation document," and "if the request does not include a valid attestation document from an AWS Nitro enclave, permission is denied because this condition is not satisfied." There is no implicit pass for a caller that simply does not supply one.They are case-insensitively compared, by convention. AWS's examples use
StringEqualsIgnoreCase throughout and say to "use the StringEqualsIgnoreCase policy operator to require a case-insensitive comparison of the PCR values." Follow that; a policy written with StringEquals against an upper-case transcription of a measurement will deny everything, and the failure looks identical to a genuine measurement mismatch.They are single-valued context keys, and this has a specific implication that is easy to get wrong. IAM defines the distinction clearly: "The difference between single-valued and multivalued context keys lies in the number of values in the request context, not the number of values in the policy condition." So you may list several accepted measurements in the policy — which is exactly what Section 7.2 needs — but you must not reach for a set operator to do it. IAM is explicit: "Do not use condition set operators
ForAllValues or ForAnyValue with single-valued context keys. Using condition set operators with single-valued context keys can lead to overly permissive policies."And the matched values are recorded in CloudTrail, which Section 10 builds on.
6.3 A key policy that says "only this code"
The following is the shape AWS documents, with the principal and the measurement both present.{
"Sid": "Enable enclave data processing",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/data-processing"
},
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:GenerateRandom"
],
"Resource": "*",
"Condition": {
"StringEqualsIgnoreCase": {
"kms:RecipientAttestation:PCR0": "EXAMPLE8abcdef7abcdef6abcdef5abcdef4abcdef3abcdef2abcdef1abcdef1abcdef0abcdef1abcdEXAMPLE",
"kms:RecipientAttestation:PCR8": "EXAMPLE34a884328944cd806127c7784677ab60a154249fd21546a217299ccfa1ebfe4fa96a163bf41d3bcfaeEXAMPLE"
}
}
}
Read it as three independent gates that must all pass. The principal gate is ordinary IAM: the request must be signed by credentials for the
data-processing role, which in practice reach the enclave from the parent instance. The action gate limits which of the five attested operations this statement authorizes. The condition gate requires a signed attestation document whose PCR0 and PCR8 both match. Multiple condition keys inside one StringEqualsIgnoreCase block are ANDed, so this statement means "this role, running this exact image, signed by this certificate."Figure 2 traces the same evaluation end to end, including the path a request takes when the measurements do not match.

7. Key Policy Design Patterns
This is where the mechanism becomes an operations problem, and where most of the avoidable damage happens.7.1 Choosing which measurements to pin
You have six measurements and no obligation to use all of them. Each one buys a different guarantee at a different maintenance cost.| Measurement | What it pins | Changes when | Reasonable use |
|---|---|---|---|
PCR0 or ImageSha384 | The entire enclave image file | Any change to code, dependencies, or base layers | The tightest binding. Correct when you want "exactly this build and nothing else" |
| PCR1 | The kernel and bootstrap | The Nitro CLI or its bundled kernel changes | Rarely pinned alone. Pinning it couples your key policy to your tooling upgrades |
| PCR2 | The user application, without the boot ramfs | Your application code changes | Useful when you want to allow kernel or tooling updates but not application changes |
| PCR3 | The IAM role ARN attached to the parent | You change the parent's role | Survives instance replacement. Recommended by AWS |
| PCR4 | The parent instance ID | Every instance replacement | Only for a deliberately pinned single host. See Section 13.2 |
| PCR8 | The image signing certificate | The signing certificate changes | Survives code changes. Recommended by AWS, together with PCR3 |
AWS's own recommendation is worth quoting rather than paraphrasing, because it resolves the choice for most designs: using PCR8 "enables you to build more flexible condition keys that remain effective even if the enclave image or parent instance is changed. We recommend that you use PCR3 and PCR8 together for the best flexibility."
The way I would put the trade-off is that PCR0 authorizes a build, while PCR3 with PCR8 authorizes a release process. Pinning PCR0 means every deployment is a key policy change. Pinning PCR3 with PCR8 means the key trusts anything signed by your release certificate and running under your parent role — which is a weaker claim, but one whose strength is now carried by the controls around the signing key rather than by the key policy. If your signing private key is itself a KMS key with a tight policy, that is a defensible place to put the trust. If it is a file on a build host that several people can read, it is not.
Many designs end up with both: PCR8 with PCR3 as the standing grant, and PCR0 or PCR2 added when a particular key protects something that must be bound to one exact build.
7.2 The deployment ordering problem
Here is the trap, stated as a sequence. You build a new enclave image. Its PCR0 and PCR2 differ from the running one. If the key policy still names only the old measurement, the new enclave is denied the moment it starts. If instead you update the key policy first, the currently running enclave is denied — while it is serving traffic.There is no atomic swap available, so the answer is an overlap window. The safe order is:
- Build the new image and record its measurements with
nitro-cli describe-eif, before deploying anything. - Add the new measurement values to the key policy alongside the existing ones, so both the old and new builds are authorized.
- Deploy the new enclaves.
- Confirm from CloudTrail that successful calls are now presenting the new measurement, and that the old one has stopped appearing.
- Remove the old measurement values from the key policy.
- Confirm no denials followed the removal, then close the change.
Step 2 is the one that requires the policy to accept more than one value for the same condition key. Because these are single-valued context keys, you express that as a plain list and specifically do not wrap it in a set operator:
"Condition": {
"StringEqualsIgnoreCase": {
"kms:RecipientAttestation:PCR0": [
"EXAMPLEold7abcdef6abcdef5abcdef4abcdef3abcdef2abcdef1abcdef1abcdef0abcdef1abcdEXAMPLE",
"EXAMPLEnew7abcdef6abcdef5abcdef4abcdef3abcdef2abcdef1abcdef1abcdef0abcdef1abcdEXAMPLE"
]
}
}
Step 4 is the step people drop, and it is the one that makes step 5 safe. Without evidence that nothing is still presenting the old measurement, removing it is a guess.
7.3 Not locking yourself out
Adding an attestation condition to a KMS key policy can make the key unusable, and the failure is not always immediate or obvious. This warning deserves its own subsection because the mechanism that makes the feature valuable is the same mechanism that makes the mistake possible: the condition can only ever be satisfied by an attested request, so any principal that relied on the statement you just amended stops working the instant you save it.Four rules keep this from happening.
Put the attested grant in its own statement. Do not add
kms:RecipientAttestation:* to a statement that also serves your applications, your backup process, or your operators. Give the enclave its own Sid with its own principal, its own action list, and its own condition. Everything else keeps its existing statement, unconditioned.Keep an administrative path that has no attestation condition. A key policy is the root of authorization for a KMS key. Retain a statement that lets a break-glass administrative principal call
kms:PutKeyPolicy on the key without needing an attestation document, so a bad policy can be rolled back. AWS notes the related default behavior — if you do not provide a key policy at creation, AWS creates one granting the account full access and allowing IAM policies to grant access — and suggests that where you want to narrow this you can change the administrative statement's kms:* action to NotAction with kms:Decrypt, which preserves administration while removing the blanket decrypt path.Test the permission, not the ciphertext.
Decrypt supports a DryRun parameter, and DryRunModifiers set to IGNORE_CIPHERTEXT makes KMS perform "only authorization validation without ciphertext validation," which AWS describes as allowing you "to test permissions without requiring a valid ciphertext blob." That turns a policy change into something you can verify from outside the enclave before you rely on it.Never rehearse on the key that protects live data. Bring up a second KMS key with the same policy shape, point a non-debug enclave at it, and make your mistakes there. Changing a key policy is not destructive in the way that scheduling key deletion is, but an incorrect policy on a key that guards production ciphertext is an outage, and the recovery path runs through the same policy you just broke.
7.4 Key policy or IAM policy
Both policy types accept these condition keys. Prefer the key policy. KMS's own guidance forDecrypt is that you should "use key policies to give users permission to call the Decrypt operation on a particular KMS key, instead of using IAM policies," warning that an IAM policy can easily end up granting Decrypt on all KMS keys.The reasoning is sharper in this context than in general. The claim you are making is "only this code can read this data." That claim is a property of the key, so it belongs in the artifact that travels with the key and is read by anyone auditing it. An identity policy attached to a role somewhere else is a claim about the role, and it will not be found by someone reviewing the key. How the two combine, and how an explicit deny interacts with both, is stepped through in my IAM Policy Evaluation Logic Step by Step.
7.5 One key per enclave role
Because the condition lives on the key, the granularity of "which code may read what" is exactly the granularity of your keys. If two enclave applications need different data, give them different KMS keys rather than one key with a compound condition that tries to describe both. A single key whose policy enumerates several measurements says that any of those builds may read everything the key protects, which is almost never what the design intended.The same reasoning applies inside one application. If an enclave handles two classes of data with different rules — one that may be exported in aggregate and one that may not — those are two keys, and ideally two enclaves, for the reason in Section 11.4.
For generating the surrounding statements once you have decided on the shape, my AWS KMS Key Policy Builder Tool produces the boilerplate, and my IAM Policy Analyzer Tool is useful for checking what a combined identity and resource policy actually permits.
8. Data Flow Patterns
8.1 The canonical flow
AWS documents the reference flow for decrypting a secret inside an enclave, using the prepackagedkmstool-enclave-cli. It is worth walking because every design decision in Sections 3 and 7 shows up in it.- The parent instance passes credentials and ciphertext into the enclave over virtio-vsock.
- The application inside the enclave invokes the
kmstool-enclave-clibinary. - The CLI utility generates an ephemeral private and public key pair.
- The CLI utility requests an attestation document from the Nitro Secure Module device, and includes the public key in it.
- The CLI creates a KMS decrypt request using a standard AWS SDK and attaches the attestation document, dialing out to an HTTPS KMS endpoint over vsock.
- The
vsock-proxyprocess on the parent listens on CID3, port8000, accepts the connection, and transparently forwards packets to the configured KMS endpoint, establishing a secure bidirectional TLS connection between KMS and the enclave. - KMS receives the request, validates the attestation document, and decrypts the payload. If the attestation document contains a public key, KMS uses that key to re-encrypt the plaintext so that only the owner of the matching private key can read it.
- The CLI receives the response and decrypts it with the ephemeral private key from step 3.
- The connection closes.
Step 6 is the one to look at twice. The proxy on the parent forwards packets; the TLS session terminates inside the enclave. AWS puts it plainly in a different page: "Sessions with KMS are established logically between AWS KMS and the enclave itself, and all session traffic is protected from the parent instance and from other enclaves." The parent is a pipe, not a participant.
8.2 What the parent must supply, and what that means
The enclave cannot obtain anything by itself, so the parent's contributions are part of the design rather than plumbing.- Credentials. AWS's pattern is to "provide a mechanism to get fresh AWS credentials from IMDSv2 and pass them into the enclave with the ciphertext." The enclave has no instance metadata access of its own, so the identity used against KMS is the parent's role, delivered over vsock.
- Ciphertext. Also delivered over vsock, from wherever it is stored.
- Network reachability. The
vsock-proxyservice must have the specific KMS endpoints on its allow list, configured in/etc/nitro_enclaves/vsock-proxy.yamlwith entries such askms.us-east-1.amazonaws.comandkms-fips.us-east-1.amazonaws.comon port 443. AWS also flags a consistency requirement that produces a confusing failure when violated: the Region used to encrypt, the endpoints in the proxy allow list, and the Region passed to the tooling must all match.
The credentials point is the one worth dwelling on, because it looks like a weakness and is actually the design working. The parent holds credentials that name the principal in the key policy; the enclave holds code that produces the measurement in the condition. Neither is sufficient. An attacker with the parent's credentials cannot decrypt, because they cannot produce a matching attestation document. An attacker who somehow ran the exact image elsewhere cannot decrypt, because they do not have credentials for the principal — and if PCR3 is pinned, the role itself is part of the measurement.
8.3 Where plaintext exists, and where it does not
Figure 1 shows the boundary and the path.
| Location | What exists there |
|---|---|
| Object storage | Ciphertext, and the encrypted data key alongside it |
| Parent instance memory | Ciphertext, AWS credentials, and the KMS response, which is opaque to the parent |
| The vsock channel | Exactly what your protocol puts on it |
| Enclave memory | Plaintext, and the ephemeral private key |
| The enclave image file | Your code and any baked-in data, in the clear |
| AWS KMS | The key material, and the plaintext transiently during the operation |
The row that surprises people is the parent's. It forwards every packet of the KMS session and still cannot read the result, because the result is encrypted under a public key that only the enclave holds the private half of. That is what
CiphertextForRecipient is for, and it is the reason this design does not merely move the trust from the operator to the network path.The row people forget is the enclave image file. It is in the clear, and it lives in whatever artifact store you put it in. Treat the EIF as public with respect to confidentiality, and as highly sensitive with respect to integrity.
8.4 Getting results out
Everything the enclave returns crosses the parent. That single fact should shape the enclave's interface more than anything else.The rule I would apply is that the output contract must be as narrow as the input contract is broad. An enclave that ingests every party's raw records and returns "the identifier of the highest bidder" has moved the confidentiality boundary correctly. The same enclave returning "the bids, sorted" has not — it has performed an expensive decryption ceremony and then handed the plaintext, reordered, to precisely the party it was isolating from. AWS's own multi-party proof of concept is built around this discipline: bids are decrypted inside, compared inside, and only the winner is returned, with the unencrypted result written out by the parent.
If the output is itself sensitive, re-encrypt it inside the enclave before returning it — to a key that the recipient can use and the parent cannot. The enclave has
GenerateDataKey and GenerateRandom available under the same attested path, so it has everything it needs to do this without ever exposing a plaintext key to the parent.9. Multi-Party Data Collaboration
The use case AWS names for attestation beyond secret protection is multi-party computation, where "several parties can join and process highly sensitive data without having to disclose or share the actual data to each individual party," and which AWS notes "can also be done within the same organization to establish separation of duties."9.1 The shape of the pattern
The structure is simpler than the idea suggests, and it falls directly out of Section 6.Each party encrypts its own data under its own KMS key, in its own account. Each party then independently adds a statement to its own key policy authorizing the same enclave measurement. No party gives another party its key, its data, or any credential. The enclave, running on one party's parent instance, presents the same attestation document to each party's KMS key in turn, decrypts each input, computes the agreed result, and returns only that result. AWS's proof of concept describes exactly this: the enclave "will decrypt the data using both buyers' AWS KMS keys and present attestation documents signed by the Nitro Hypervisor. AWS KMS will validate that the PCR values in the attestation documents match the key policy before performing the decryption."
The property that makes it work is the one from Section 2.3: a measurement is not a secret, so every party can compute it independently and authorize it without coordinating through a trusted third party.
9.2 What the parties actually have to agree on
This is where these projects succeed or stall, and it is almost entirely non-technical. The list below is what I would put on the table before writing any code.- The artifact. Everyone authorizes a hash. Someone has to say which source produces it, and everyone has to be able to read that source.
- A reproducible build. Without it, "authorize measurement X" is a request to trust whoever produced X, which reintroduces the trusted third party the design was meant to remove. With it, each party rebuilds, runs
describe-eif, and confirms the value themselves. Section 4.4 is not an engineering nicety here; it is the load-bearing element of the trust model. - The output contract. What the enclave returns, in what form, to whom. Section 8.4.
- The principal. Whose account runs the parent instance, and therefore which IAM role appears in every party's key policy — and, if PCR3 is pinned, in the measurement itself.
- The change process. Who may publish a new version, and how each party's key policy moves in step with it. The ordering in Section 7.2 now has to be executed by N organizations with N change processes, which is the single largest operational cost of the pattern.
- Termination. What happens to the ciphertext, to the key policies, and to the enclave image when the collaboration ends.
Items 2 and 5 are the ones I would not compromise on. The rest can be renegotiated later; those two determine whether the arrangement means anything.
9.3 Attesting to each other, not only to KMS
Parties sometimes want mutual verification directly, rather than through a shared key. AWS describes the pattern: each peer presents its own attestation document to the other, and each validates the received document and compares the values against its own set of trusted PCR measurements, then repeats the exchange in the opposite direction.Two constraints shape any implementation. There is no enclave-to-enclave channel — AWS states that enclaves on the same or different parent instances cannot communicate with each other — so every exchange is relayed by the parents, and the protocol must therefore assume a hostile relay. And you are now the verifier, which means implementing Section 5.4 in full, including the certificate chain handling.
AWS also documents the shortcut, along with its cost: two enclaves that each decrypt a common shared secret under the same KMS key can trust each other transitively, because each has been validated by KMS. AWS notes the trade-off directly — "though technically simple, this approach always requires AWS KMS, which introduces additional API calls overhead." For most designs that is the right trade, because it replaces a bespoke verifier with a policy document.
9.4 When an enclave is the wrong tool for this
Be honest about the alternative before committing to build one. If what the parties want is to run analytics over overlapping datasets — joins, aggregates, audience overlap — then AWS Clean Rooms exists for exactly that, and AWS describes it as helping companies and partners "analyze and collaborate on your collective datasets to gain new insights without revealing underlying data to one another," with analysis rules that restrict SQL queries and constrain output, analysis logs for audit, differential privacy, and Cryptographic Computing for Clean Rooms to keep data encrypted while queries are processed.The dividing line I would draw is the nature of the computation. If it is expressible as constrained SQL over tabular data, a managed collaboration service gives you output controls, query logging, and privacy techniques that you would otherwise build and defend yourself. If the computation is arbitrary code — a matching algorithm, a signing ceremony, a model evaluated over combined inputs — then there is no query language to constrain, and an attested enclave is the mechanism that fits.
10. Auditing and Detection
10.1 What CloudTrail records
Attested requests to KMS carry the measurements into the audit log, which turns "which code decrypted this" from an assertion into evidence.AWS states that for Nitro enclave attestation, "the CloudTrail log includes the module ID (
attestationDocumentModuleId), image digest (attestationDocumentEnclaveImageDigest), and platform configuration registers (PCRs) from the attestation document," where "the module ID is the enclave ID of the Nitro enclave" and "the image digest is the SHA384 hash of the enclave image." The fields appear under additionalEventData.recipient:"additionalEventData": {
"recipient": {
"attestationDocumentModuleId": "i-123456789abcde123-enc123456789abcde12",
"attestationDocumentEnclaveImageDigest": "<AttestationDocument.PCR0>",
"attestationDocumentEnclavePCR1": "<AttestationDocument.PCR1>",
"attestationDocumentEnclavePCR2": "<AttestationDocument.PCR2>",
"attestationDocumentEnclavePCR3": "<AttestationDocument.PCR3>",
"attestationDocumentEnclavePCR4": "<AttestationDocument.PCR4>",
"attestationDocumentEnclavePCR8": "<AttestationDocument.PCR8>"
}
}
Two details make this more useful than it first appears. The module ID is the enclave ID, which embeds the parent instance ID — so the log identifies the specific enclave process, not merely the image. And the image digest field carries PCR0, so a single field answers "which build was this." AWS publishes example entries for
Decrypt, GenerateDataKey, GenerateDataKeyPair, and GenerateRandom; in each of them responseElements is null and readOnly is true, so nothing about the plaintext leaks into the trail.10.2 What to alert on
The value of this trail is that it lets you detect a class of problem that is otherwise invisible: a key policy that is looser than your inventory of published builds. Five signals are worth wiring up.- A successful attested call whose image digest is not on your allow-list of published measurements. This means something you did not release is authorized. It is the highest-signal alert in the set, and it requires you to maintain a list of released measurements — which is a good reason to record
describe-eifoutput as a release artifact. AccessDeniedon the enclave's key whereadditionalEventData.recipientis absent. Something is calling the key without attestation. Most often a misconfigured deployment or an SDK path that dropped theRecipientparameter; occasionally more interesting.- A new
attestationDocumentModuleIdoutside a deployment window. A new enclave started when no change was in flight. - PCR3 or PCR4 values that do not match the expected parent role or instance. Relevant when you pin PCR8 rather than PCR0, because then the parent identity is doing more of the work.
PutKeyPolicyon the enclave's key. The entire design rests on this document. Any change to it is a change to the security control, and should be reviewed as one.
Building the pipeline that collects, normalizes, and queries these events across accounts is a separate design problem, and one I have written up in my AWS Centralized Logging and Audit Architecture Guide.
10.3 The limits of this evidence
Say plainly what the trail does not show, because an auditor will ask.It records which measurement was presented, not what the enclave did with the plaintext afterwards. The isolation that makes the enclave valuable is the same isolation that prevents you from observing its behavior. Your assurance that the code behaves correctly comes from having read the code that produces the measurement — which is, once more, why reproducible builds carry so much weight in this design.
It also does not stand alone. Anything the enclave logs about its own processing, it logs through the parent, so the parent is in your audit path for application-level events even though it is outside your confidentiality boundary for data.
One genuinely convenient property: debug-mode enclaves show all-zero PCRs in CloudTrail as well, so a query for a run of zeros in
attestationDocumentEnclaveImageDigest finds every debug enclave that ever succeeded against a real key.11. Operational Realities
11.1 Deployment
The parent instance must be launched with enclaves enabled —--enclave-options 'Enabled=true' on run-instances, or the equivalent in a launch template — and AWS notes you cannot enable Nitro Enclaves and hibernation on the same instance. Instance type selection is constrained to the supported list, which excludes the smallest size in most families; if you are choosing a family for other reasons as well, my Amazon EC2 Instance Types History and Timeline covers how the families relate.The enclave image has to reach the parent. Because measurements are the artifact's identity, build once and distribute the resulting
.eif rather than building on each host — AWS explicitly supports building "on any Linux environment, including outside of AWS," and a Windows parent leaves you no choice, since build-enclave is not supported there.Secrets, meanwhile, do not belong in the image at all. Where they should live, and how the surrounding stores use KMS, is the subject of my AWS Secrets Manager and Parameter Store Decision Guide; the enclave's role is to be the only thing that can open them.
11.2 Restart and lifecycle
Enclaves are ephemeral in a stronger sense than most workloads. AWS states that "enclaves are active only while their parent instance is in therunning state" and that "if the parent instance is stopped or terminated, its enclaves are terminated." There is no persistent storage, so nothing survives.Design for that rather than around it. Everything the enclave needs must be re-fetchable as ciphertext at start-up, and start-up must be able to complete without human involvement, because there is no way to intervene inside. The allocator service must also be running and configured before an enclave can start, so a parent reboot has an ordering dependency that belongs in your instance bootstrap rather than in a runbook.
11.3 Troubleshooting with the console closed
You cannot log in. What you have is:nitro-cli describe-enclaves for state, nitro-cli console only when the enclave was started in debug mode, nitro-cli describe-eif to re-derive measurements from an artifact, nitro-cli pcr to compute a PCR from a file or certificate, the run-enclave failure codes E36, E39, and E11 for an expired signing certificate, and CloudTrail.That is a thinner toolkit than usual, so it helps to know where the probability mass is. In my experience the first thing to check is not the enclave at all — it is whether the measurement in the key policy matches the measurement of the image you actually deployed. Run
describe-eif on the artifact on the parent, and compare it to the key policy, before assuming anything about the application.The second thing to internalize is the constraint from Section 4.5: because debug mode zeroes the PCRs, you cannot reproduce an attestation failure with the console attached. The workable procedure is to run the same image without
--debug-mode against a test key whose policy contains the measurements you expect, and use Decrypt with DryRun and DryRunModifiers set to IGNORE_CIPHERTEXT to separate a permissions problem from a ciphertext problem.11.4 Running more than one
Up to four enclaves per parent, each with its own CID and ID, individually terminable, independently sized, and built from the same or different images. There is no channel between them.The design use for this is worth stating because it is not obvious. Prefer one enclave per trust domain over one enclave with a branch in it. A measurement covers an entire image, so a single image that performs two jobs cannot be authorized for one job and denied for the other — any key policy that admits it admits both code paths. Splitting the jobs into two images gives you two measurements and therefore two independently governable grants. The cost is the allocator arithmetic from Section 3.4 and a second deployment; the benefit is that your authorization boundary matches your actual trust boundary.
12. Enclave Attestation versus Instance Attestation
Since September 23, 2025 there has been a second way to make an attested call to KMS, and choosing between them is now part of this design. Treating them as the same guarantee is a mistake an auditor will find.12.1 What NitroTPM attestation is
AWS announced the general availability of EC2 instance attestation, which lets customers "cryptographically verify that their EC2 instances are running trusted configurations and software," calling out instances with AI chips and GPUs as a motivating case. It is available in all AWS Commercial Regions including the AWS GovCloud (US) Regions.The mechanism parallels enclaves closely. You build an Attestable AMI — which AWS describes as "an AMI built from the ground up for attestation" — and derive reference measurements of its contents; the
nitro-tpm-pcr-compute utility generates them at build time from the AMI's Unified Kernel Image. At run time you retrieve an attestation document from NitroTPM, using the nitro-tpm-attest utility rather than the NSM API, and attach it to KMS requests through the same Recipient parameter. The condition keys are the NitroTPM variants: AWS documents kms:RecipientAttestation:NitroTPMPCR4, kms:RecipientAttestation:NitroTPMPCR7, and kms:RecipientAttestation:NitroTPMPCR12, advising that you "use PCR4 and PCR12 measurements for standard boot or PCR7 measurements for Secure Boot."The rest is identical: same five operations, same
CiphertextForRecipient behavior, same StringEqualsIgnoreCase comparison, and CloudTrail records the module ID and PCRs the same way — with the module ID being, as AWS puts it, "the ID of the EC2 instance with NitroTPM with a TPM identifier."12.2 How they differ
| Dimension | Nitro Enclaves | NitroTPM instance attestation |
|---|---|---|
| What is measured | The enclave image file | The whole AMI, via its Unified Kernel Image |
| Isolation from your own operators | Enforced by the Nitro Hypervisor, which partitions vCPUs and memory | You must build the AMI to be an isolated compute environment; AWS provides a sample image description as a starting point |
| Networking | None. Only vsock to the parent | Ordinary instance networking |
| Persistent storage | None | Ordinary volumes |
| Accelerators | The enclave receives vCPUs and memory only | The instance can have GPUs and AI chips, which AWS names as the motivating case |
| Attestation retrieval | get-attestation-document via the NSM, from inside the enclave only | The nitro-tpm-attest utility on the instance |
| Condition keys | ImageSha384 and PCR<PCR_ID> | NitroTPMPCR<PCR_ID> |
| Scope of a compromise | Bounded by the enclave | The whole instance |
AWS is careful about the responsibility split on the instance side, and the wording matters when you write this up for a reviewer: "NitroTPM and Attestable AMIs are building blocks that can help you to set up and configure attestation on your EC2 instances. You are responsible for configuring the AMI to meet your respective use case."
12.3 The honest recommendation
If the sensitive computation fits inside an environment with no network, no disk, and no accelerator, use an enclave. The boundary is enforced by the hypervisor rather than by your AMI hardening, the thing being measured is small enough to review, and a defect elsewhere on the instance does not reach it.If it does not fit — and the case that most often does not fit is inference or training over sensitive data on a GPU or an AI chip — instance attestation lets you make a real but weaker claim about a whole machine that you have configured to have no interactive access. That is a genuine control, and before September 2025 there was no supported way to prove it at all.
What you should not do is present them as equivalent. The enclave claim is "this exact code ran in an environment your administrators cannot reach, enforced by the hypervisor." The instance claim is "this machine booted an AMI whose contents hash to a value you approved, and I configured that AMI to be an isolated environment." Both are useful. They are not the same sentence.
13. Failure Modes and Anti-Patterns
13.1 A build that is not reproducible
The symptom is that every deployment requires a key policy edit, and nobody can explain why the measurement changed. The cause is a floating base image, an unpinned dependency, or a timestamp in the artifact. The consequence is worse than the toil: you lose the ability to say that the value in the key policy corresponds to source anyone has reviewed, which is the only thing the measurement was ever supposed to establish.13.2 Pinning PCR4 in an autoscaled fleet
PCR4 is a hash of the parent instance ID. Every instance replacement — a scaling event, an AZ rebalance, a patched AMI, a hardware retirement — produces a new instance ID and therefore a new PCR4, and the key policy denies everything until someone edits it. PCR4 is for a deliberately pinned single host. For a fleet, use PCR3, which pins the role and survives replacement, and pair it with PCR8 as AWS recommends.13.3 Updating the key policy and the enclave in the wrong order
Covered in Section 7.2, and repeated here because it is the most common self-inflicted outage in this design. There is no atomic swap. Add the new measurement, deploy, verify from CloudTrail, then remove the old one.13.4 Locking yourself out of the key
Adding an attestation condition to a statement that non-enclave principals depend on will break them immediately, because the condition can only be satisfied by an attested request. Keep the attested grant in its own statement, keep an administrative statement without the condition so a bad policy can be rolled back, and rehearse on a key that does not protect production ciphertext. This is the one mistake in this article whose recovery path runs through the very document you just broke.13.5 Putting secrets in the enclave image
AWS says it as plainly as it can be said: the EIF "contains unencrypted copies of all code and data, so unencrypted secrets must not be part of the EIF." An API key baked into the image is readable by anyone who can read the artifact, and it is measured, so rotating it changes PCR0 and forces a key policy change. Secrets arrive at runtime as ciphertext. That is the whole mechanism.13.6 The all-zero measurement left in a production policy
From Section 4.5. Every debug-mode enclave in the world presents the same zeros. A value of forty-eight zero bytes in a key policy is not a placeholder; it is a grant.13.7 Trying to give the enclave a network
There are user-space network stacks that tunnel atap interface over vsock, and AWS has written about using them. They are legitimate engineering, and they do not change the isolation model: every packet still traverses the parent, which is the party you were isolating from. The anti-pattern is not using them — it is believing they have restored ordinary networking semantics, and then adding a runtime dependency that your measurement says nothing about and your availability now depends on.13.8 Not verifying the attestation document, when you are the verifier
If your verifier is KMS, this cannot happen. If you built your own, a document is only evidence if you verify the certificate chain to the published Nitro root, confirm the PCR values equal the ones you published rather than merely being present, and bind the document to the current session with thenonce field. A verifier that parses the document and reads the PCRs without checking the signature has implemented a very expensive way of trusting its input.13.9 Treating the measurement as a secret
Measurements are hashes of artifacts. Anyone with the artifact has them, and with a reproducible build anyone with the source has them. Designing as though a leaked PCR value is a breach leads to withholding the measurements from the counterparties who need them in order to authorize you — which defeats Section 9 entirely.13.10 One key that has trusted every version you ever shipped
The natural drift of Section 7.2 is that step 2 always happens and step 5 never does. After a year the key authorizes a dozen measurements, including the build with the bug you fixed in March. Removing old values is part of the change, not cleanup to be done later, and the CloudTrail evidence from Section 10 is what tells you it is safe.14. Frequently Asked Questions
Does Nitro Enclaves stop AWS from seeing my data?
That protection comes from the Nitro System, not from enclaves. AWS states that "by design the Nitro System has no operator access" and that there is "no mechanism for any system or person to log in to EC2 Nitro hosts, access the memory of EC2 instances, or access any customer data stored on local encrypted instance storage or remote encrypted EBS volumes" — for ordinary EC2 instances. Nitro Enclaves addresses the second dimension of confidential computing: separating a more-trusted part of your own workload from the less-trusted rest of it, including your own administrators.Is the enclave's memory encrypted?
The enclave's isolation comes from the Nitro Hypervisor partitioning vCPUs and memory away from the parent, which AWS describes as ensuring "that the parent instance has no access to the isolated vCPUs and memory of the enclave." Memory encryption is a separate, instance-level property: AWS states that "starting with AWS Graviton2, AMD EPYC (Milan), and Intel Xeon Scalable (Ice Lake) processors, instance memory is always encrypted," and that instances enabled with AMD SEV-SNP use an instance-specific key. The two mechanisms compose; neither is a substitute for the other.What happens if I forget the Recipient parameter?
The request is denied, assuming your grant carries an attestation condition. AWS states that the condition keys are "effective only when theRecipient parameter in the request specifies a signed attestation document," and that "if the request does not include a valid attestation document from an AWS Nitro enclave, permission is denied because this condition is not satisfied." There is no fallback to an unattested allow.Can I use a key management service other than AWS KMS?
Yes. The Nitro Enclaves FAQ says you can use your own or a third-party service, adding that "you will need to build an attestation process or service to be able to validate the signed attestation documents generated by your enclave." Section 5.4 is what that entails, and it is a real ongoing obligation rather than a one-time integration.Do I need PCR8, or is PCR0 enough?
PCR0 alone is sufficient and is the tightest binding, at the cost of a key policy change for every build. AWS recommends the alternative for flexibility: PCR8 "enables you to build more flexible condition keys that remain effective even if the enclave image or parent instance is changed. We recommend that you use PCR3 and PCR8 together for the best flexibility." Choose based on where you want the trust to sit — in the key policy, or in the controls around your signing key.How many enclaves can one instance run, and can they talk to each other?
Up to four per parent instance. They cannot communicate: AWS states that enclaves "running on the same or different parent instances cannot communicate with each other," and that "there is no vsock connection between the enclaves." Each has its own CID and its own ID, and each can be terminated individually.Can the enclave call S3, or reach the internet directly?
No. An enclave has no external network connectivity; vsock to its parent is the only channel. Reaching any AWS service means proxying through the parent — for KMS, AWS shipsvsock-proxy for exactly this, and you add the specific service endpoints to its allow list. The TLS session still terminates inside the enclave, so the proxy forwards bytes it cannot read.Is an enclave a container?
No. A container image is used as the packaging format for the build step — AWS describes Docker images as "a convenient file format for packaging your applications" — butnitro-cli build-enclave converts that image into an enclave image file that boots its own Linux kernel as a separate virtual machine with vCPUs and memory partitioned away from the parent by the Nitro Hypervisor.What breaks when I upgrade the Nitro CLI or the base kernel?
PCR1 covers the kernel and bootstrap, and PCR0 covers the whole image, so a tooling upgrade can move your measurements without any change to your application code. This is not a failure mode so much as a reason to measure before you deploy: runnitro-cli describe-eif on the new artifact and compare against the values in the key policy as part of the upgrade, not after it.Where can I run this?
AWS states that Nitro Enclaves is supported in all AWS Regions, including the AWS GovCloud (US) Regions, following the October 21, 2025 expansion. It is not supported on AWS Outposts, and not supported in Local Zones or Wavelength Zones. The parent must be a supported instance type running Linux or Windows Server 2016 or later, and the enclave itself must run Linux.How do I test a key policy change without breaking production?
Use a separate KMS key with the same policy shape and a non-debug enclave. For the permission check itself,Decrypt accepts DryRun with DryRunModifiers set to IGNORE_CIPHERTEXT, which AWS describes as performing "only authorization validation without ciphertext validation," letting you "test permissions without requiring a valid ciphertext blob." Never rehearse on the key that guards live ciphertext.15. Summary
Nitro Enclaves answers a requirement that ordinary access control cannot: not "who is authorized to read this," but "which code is able to read this." The mechanism is a measurement of your enclave image, signed by the Nitro Hypervisor, checked by AWS KMS against a condition key in your key policy.- The threat model is specific, and enclaves are not the part that protects you from AWS. The Nitro System's no-operator-access design does that for every instance. An enclave protects a workload from the rest of your own environment — your administrators, your other processes, your unaudited libraries (Section 2).
- Measurements are identity, not secrets. Anyone with your image can compute them, which is exactly what lets a counterparty authorize you without trusting you. The key policy still checks the principal; the condition narrows an already-authorized identity down to a specific build (Sections 2.3, 6.3).
- The constraints are the security argument, and they cost you three things. No external network, no persistent storage, no interactive access, vsock only. That forces you to redesign logging, secret supply, and dependency resolution before anything else works (Section 3.3).
- Reproducible builds are load-bearing, not hygiene. The key policy authorizes a hash. If rebuilding produces a different hash, you cannot tie the authorized value to reviewed source, and multi-party collaboration becomes trust in whoever ran the build (Sections 4.4, 9.2).
- Choose measurements by their stability, not their tightness. PCR0 authorizes a build; PCR3 with PCR8 authorizes a release process, which is what AWS recommends for flexibility. PCR4 pins an instance ID and does not survive a scaling event (Sections 7.1, 13.2).
- The deployment order is the outage risk. Add the new measurement alongside the old, deploy, verify from CloudTrail, then remove the old one. There is no atomic swap (Section 7.2).
- Do not lock yourself out. Put the attested grant in its own statement, keep an administrative path with no attestation condition, use
DryRunwithIGNORE_CIPHERTEXTto test permissions, and rehearse on a key that protects nothing (Sections 7.3, 13.4). - The audit trail records the measurement, not the behavior. CloudTrail gives you the image digest and PCRs on every attested call, which is enough to detect a policy looser than your inventory — and nothing about what the enclave did with the plaintext (Section 10).
- Enclave attestation and instance attestation are different claims. Since September 2025 NitroTPM and Attestable AMIs let you attest a whole instance, which is the only option when the workload needs a GPU or an AI chip. Do not describe the two as the same guarantee (Section 12).
If you are starting today, the first two steps are worth doing whether or not you ever create an enclave: make the build of the sensitive component reproducible, and write down which code is allowed to see which data. Those are the parts that make the rest possible, and both improve the system on their own.
For the decisions this article deliberately delegates: design the encryption itself in my AWS KMS Envelope Encryption and Data Key Caching Patterns, look up the primitives in my Cryptography Glossary for Engineers, place the surrounding network and identity controls with my AWS Zero Trust Network Architecture Guide, and handle the sensitive data that never needed to reach an enclave in the first place with my PII Detection and Redaction Patterns for Generative AI on AWS.
16. References
- AWS Nitro Enclaves User Guide - What is Nitro Enclaves?
- AWS Nitro Enclaves User Guide - Nitro Enclaves concepts
- AWS Nitro Enclaves User Guide - Cryptographic attestation
- AWS Nitro Enclaves User Guide - Using cryptographic attestation with AWS KMS
- AWS Nitro Enclaves User Guide - Verifying the root of trust
- AWS Nitro Enclaves User Guide - Getting started with Nitro Enclaves
- AWS Nitro Enclaves User Guide - Getting started: Hello Enclaves sample application
- AWS Nitro Enclaves User Guide - Connect the parent instance with an enclave by using virtio-vsock
- AWS Nitro Enclaves User Guide - Connect an enclave with AWS KMS for cryptographic attestation and secrets management
- AWS Nitro Enclaves User Guide - Building an enclave image file
- AWS Nitro Enclaves User Guide - Working with multiple enclaves
- AWS Nitro Enclaves User Guide - Install the Nitro Enclaves CLI on Linux
- AWS Nitro Enclaves User Guide - Nitro Enclaves application development on Linux instances
- AWS Nitro Enclaves User Guide - Working with the vsock socket in Windows
- AWS Nitro Enclaves User Guide - AWS Certificate Manager for Nitro Enclaves
- AWS Nitro Enclaves User Guide - nitro-cli build-enclave
- AWS Nitro Enclaves User Guide - nitro-cli run-enclave
- AWS Nitro Enclaves User Guide - nitro-cli describe-eif
- AWS Nitro Enclaves User Guide - nitro-cli pcr
- AWS Nitro Enclaves User Guide - Document history
- AWS KMS Developer Guide - Cryptographic attestation support in AWS KMS
- AWS KMS Developer Guide - How to make attested calls to AWS KMS
- AWS KMS Developer Guide - AWS KMS condition keys for attested platforms
- AWS KMS Developer Guide - Condition keys for Nitro Enclaves
- AWS KMS Developer Guide - Condition keys for NitroTPM
- AWS KMS Developer Guide - Monitoring requests for Nitro enclaves
- AWS KMS Developer Guide - Monitoring requests for NitroTPM
- AWS KMS API Reference - Decrypt
- AWS KMS API Reference - GenerateDataKey
- AWS KMS API Reference - GenerateDataKeyPair
- AWS KMS API Reference - GenerateRandom
- AWS KMS API Reference - DeriveSharedSecret
- AWS KMS API Reference - RecipientInfo
- Amazon EC2 User Guide - Amazon EC2 instance attestation
- Amazon EC2 User Guide - Attestable AMIs
- Amazon EC2 User Guide - Prepare AWS KMS for attestation
- Amazon EC2 User Guide - Compute PCR measurements for a custom AMI
- Amazon EC2 User Guide - Validate a NitroTPM Attestation Document
- AWS Whitepaper - The Security Design of the AWS Nitro System
- AWS Whitepaper - The Security Design of the AWS Nitro System: No AWS operator access
- IAM User Guide - Single-valued vs. multivalued context keys
- IAM User Guide - IAM JSON policy elements: Condition operators
- AWS Clean Rooms User Guide - What is AWS Clean Rooms?
- AWS Nitro Enclaves product page
- AWS Nitro Enclaves FAQs
- AWS Confidential Computing
- AWS What's New - AWS announces Nitro Enclaves are now available in all AWS Regions
- AWS What's New - AWS announces EC2 instance attestation
- AWS Security Blog - Confidential computing: an AWS perspective
- AWS Security Blog - Introducing the Security Design of the AWS Nitro System whitepaper
- AWS Compute Blog - Validating attestation documents produced by AWS Nitro Enclaves
- AWS Compute Blog - Use AWS Nitro Enclaves to perform computation of multiple sensitive datasets
- AWS Compute Blog - Securing applications with AWS Nitro Enclaves: TLS termination, TAP networking, and IMDSv2
- AWS Web3 Blog - Establishing verifiable security: Reproducible builds and AWS Nitro Enclaves
- AWS Web3 Blog - Verify enclave counterparties with reproducible builds and cryptographic attestation using AWS Nitro Enclaves
Related Articles
- AWS KMS Envelope Encryption and Data Key Caching Patterns - How the data is encrypted underneath everything in this article: data key lifecycle, encryption context, and caching trade-offs.
- Cryptography Glossary for Engineers - SHA-384, ECDSA, RSA-OAEP, and the other primitives used by name above.
- AWS History and Timeline regarding AWS Key Management Service - When each KMS capability referenced here appeared.
- IAM Policy Evaluation Logic Step by Step - How key policies, identity policies, and explicit deny combine into the decision that an attestation condition narrows.
- Post-Quantum Cryptography Standardization Timeline and Migration on AWS - Where the signing and wrapping algorithms behind attestation are heading.
- AWS Secrets Manager and Parameter Store Decision Guide - Where secrets live before an enclave opens them.
- AWS Zero Trust Network Architecture Guide - The identity and network controls that surround a workload built this way.
- AWS Centralized Logging and Audit Architecture Guide - Collecting and querying the CloudTrail evidence from Section 10.
- Amazon EC2 Instance Types History and Timeline - How the instance families that can host a parent instance relate to one another.
- Lake Formation Fine-Grained Access Control for AI Data - Column and row level control for analytical data, the layer above encryption.
- PII Detection and Redaction Patterns for Generative AI on AWS - Reducing the sensitive data that has to enter a protected environment at all.
- AWS KMS Key Policy Builder Tool - Generates the key policy statements described in Sections 6 and 7.
- IAM Policy Analyzer Tool - Checks what a combined identity and resource policy actually permits.
References:
Tech Blog with curated related content
Written by Hidekazu Konishi