Skip to content

Does the Nitro Security Module provide a monotonic counter or any state-freshness primitive?

0

I'm building a signing service inside an AWS Nitro Enclave and I need a way to detect rollback of state that the enclave itself authored but cannot store.

Setup

The enclave has no persistent storage and no network. Outside it, in a database the enclave treats as untrusted, I keep an append-only log per user. Each entry records an authorization change: which public keys may approve operations on that user's account. Every entry is signed with a key held only inside the enclave, and hash-chained to the previous entry.

On each operation the parent passes the whole log in over vsock. The enclave replays it from genesis, verifies its own signatures and the chain, and only then knows who is currently authorized.

That stops forgery. It does not stop rollback: a compromised host can return an older version of the log that is internally consistent and correctly signed, for example a version from before a key was revoked. The enclave has no way to tell that it is not the latest.

What I have already considered

  1. Binding the log's head hash into the KMS encryption context of a wrapped secret, and re-wrapping on every mutation. This narrows the attack to whoever retained an older ciphertext, which does not help against a privileged insider on the host.
  2. Having the client remember the last head hash it saw and compare. This detects rollback from the one party the host does not control, but client-side storage is not durable.
  3. Replicating the head to a separate append-only store in a different AWS account.

Why "just use an external store" does not close this

Everything the enclave talks to goes through the untrusted parent. Any response from an external service can be withheld or replayed unless it is cryptographically bound to a challenge the enclave chose for that specific request. KMS solves this for Decrypt because the plaintext comes back encrypted to the ephemeral public key carried inside the attestation document. I have not found another service that binds to attestation the same way.

Questions

  1. Does the Nitro Security Module expose a monotonic counter, a sequence number, or any other freshness primitive that I have missed? Looking at the attestation document fields, I only see timestamp, which tells me when a document was produced but not whether a newer log entry exists somewhere.

  2. Is there an established pattern for anti-rollback of enclave-authored state on Nitro? This feels like it should be a solved problem and I may be looking in the wrong place.

  3. Is there any AWS service other than KMS whose response can be bound to a nonce inside an attestation document, so that a replayed response would be detectable by the enclave?

This is a design question rather than a failure I am debugging, so region and instance type shouldn't be relevant. Happy to add detail if anything is unclear.

1 Answer
1

Q1: NSM lacks a monotonic counter

NSM provides three features—attestation reports, PCR state manipulation, and entropy (HWRNG)—but offers no persistent counters or sequence numbers.

While ExtendPCR is monotonic, it is so only within the enclave's lifetime. The value resets upon reboot, and since there is no persistent storage, it cannot be sealed and carried over. As you noted, the timestamp in the attestation document indicates when the document was generated; it reveals nothing about whether a "newer state" exists.

This is not an oversight but a gap in the platform design. While SGX (albeit with its own issues) has monotonic counters and TPMs have NV counters, Nitro does not.

Q3: No service other than KMS supports attestation binding—but the premise can be reframed

KMS (and recently NitroTPM) is the only service with condition keys (kms:RecipientAttestation:*) natively linked to attestation documents; other AWS services lack this feature.

However, if you terminate TLS within the enclave, you can achieve equivalent freshness guarantees for any service.

This is a point worth reconsidering. You mentioned that requests could be held or replayed unless cryptographically bound to a challenge chosen by the enclave for that specific request—but the TLS handshake serves as exactly that challenge. The enclave selects the ClientRandom, and session keys are derived uniquely for each handshake. Injecting a response from an old session into a new session will fail decryption and authentication.

Therefore:

  • The vsock-proxy merely transports byte streams and cannot break TLS.
  • Pin the AWS Root CA inside the enclave and perform SigV4 signing within the enclave.
  • Directly invoke DynamoDB operations using strongly consistent reads and conditional writes.

The parent instance can only block or delay requests. This results in reduced availability, not a silent rollback. If the enclave is implemented with a "fail-closed" design, integrity is maintained.

Q2: Established Patterns

A configuration consisting of a single enclave and an untrusted host cannot solve this problem in principle. What is required is either trusted persistent state or a witness that the enclave can authenticate and whose freshness is guaranteed.

A standard academic approach is ROTE, a method where a group of enclaves across multiple platforms cooperatively maintain a monotonic counter. However, ROTE assumes a static set of nodes that must be pre-configured by an administrator, and it requires the application enclave to run on one of the ROTE nodes.

In practical terms, the following combinations offer realistic solutions:

1. TLS-in-enclave + DynamoDB conditional writes

Maintain a head hash and a version number, requiring a version match via ConditionExpression. While the host could spoof a successful write, the inconsistency would be exposed during the next strongly consistent read.

2. Duplication with existing KMS encryption context

Retain the "binding the head hash to the encryption context" mechanism you have already implemented. An attack would only succeed if both a DynamoDB rollback and the retention of the old KMS ciphertext were achieved simultaneously.

3. Placing the witness store in a separate account

As a countermeasure against privileged insiders on the host, this effectively establishes a separation of duties. The approach you considered—replicating data to an append-only store in a separate account—is correct; combining this with TLS-in-enclave ensures response freshness.

4. Enclave quorum

If truly strong guarantees are required, implement a consensus protocol (like Raft) across multiple enclaves—similar to ROTE—so that a rollback would require compromising the quorum. Signal and others have adopted this approach on SGX. Note, however, that operational costs will increase significantly. To summarize: Q1 is definitively "no"; regarding Q3, while it currently relies solely on KMS, the underlying assumptions can be restructured using TLS-in-enclave; and for Q2, the problem cannot be solved with a single enclave—the practical solution involves a combination of TLS-in-enclave, conditional writes, and cross-account isolation, whereas the complete solution requires a quorum.

EXPERT

answered a month ago

  • Thank you — the point that the TLS handshake is the enclave-chosen challenge is what I was missing.

    One gap though: I think TLS closes replay but not misdirection.

    The enclave has no IAM identity of its own; credentials come from the parent. A malicious parent can supply credentials for an account it controls and point the enclave at its own DynamoDB table. The enclave then reads over a genuine, correctly pinned TLS session and gets entries that verify perfectly — because the attacker copied real, enclave-signed entries into that table. The rollback succeeds and every check passes.

    TLS proves I'm talking to a DynamoDB endpoint, not to my table. KMS avoids this because the binding comes from the ciphertext being tied to a specific CMK, not from the transport.

    Trail of Bits suggests attesting the expected IAM role and validating it at runtime via sts:GetCallerIdentity. Two questions:

    1. Is comparing the full role ARN from sts:GetCallerIdentity against a value baked into the EIF sufficient? The account ID is in the ARN, so I think so, but I'd rather not assume.

    2. Would you still keep the KMS encryption-context layer on top? My instinct is yes, since it's the only binding that doesn't depend on credential provenance.

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.