Verify attestations using oyster-cvm
If you went through the tutorial, you would have noticed that the oyster-cvm CLI tool enables you to verify attestations given the ip of the enclave. In this guide, we enumerate all the ways you can verify attestations using oyster-cvm, including variations and custom options.
See the quickstart for instructions on installing oyster-cvm if you do not have it already.
oyster-cvm verify
$ oyster-cvm verify --help
Verify Oyster Enclave Attestation
For verifying a running enclave (among other flags): --enclave-ip <ENCLAVE_IP>
For verifying an existing attestation (among other flags): --attestation-hex <ATTESTATION_HEX>
For verifying an existing attestation, read from a file (among other flags): --attestation-hex-file <ATTESTATION_HEX_FILE>
Usage: oyster-cvm verify [OPTIONS]
Options:
-x, --attestation-hex <ATTESTATION_HEX>
Hex encoded attestation
--attestation-hex-file <ATTESTATION_HEX_FILE>
Path to file containing a hex encoded attestation
-e, --enclave-ip <ENCLAVE_IP>
Enclave IP
--pcr-preset <PCR_PRESET>
Preset PCRs for known enclave images
-j, --pcr-json <PCR_JSON>
Path to PCR JSON file
-0, --pcr0 <PCR0>
PCR 0 value
-1, --pcr1 <PCR1>
PCR 1 value
-2, --pcr2 <PCR2>
PCR 2 value
-u, --user-data <USER_DATA>
Attestation user data, hex encoded
-i, --image-id <IMAGE_ID>
Image id, hex encoded
-p, --attestation-port <ATTESTATION_PORT>
Attestation Port (default: 1300)
[default: 1300]
-a, --max-age <MAX_AGE>
Maximum age of attestation (in milliseconds) (default: 300000)
[default: 300000]
-t, --timestamp <TIMESTAMP>
Attestation timestamp (in milliseconds)
[default: 0]
-r, --root-public-key <ROOT_PUBLIC_KEY>
Root public key
[default: fc0254eba608c1f36870e29ada90be46383292736e894bfff672d989444b5051e534a4b1f6dbe3c0bc581a32b7b176070ede12d69a3fea211b66e752cf7dd1dd095f6f1370f4170843d9dc100121e4cf63012809664487c9796284304dc53ff4]
--preset <PRESET>
Preset for parameters (e.g. blue, debug)
--arch <ARCH>
Platform architecture (e.g. amd64, arm64)
[default: arm64]
[possible values: amd64, arm64]
-h, --help
Print help (see a summary with '-h')
As the help text suggests, there are 3 variants you can use to verify attestations.
Verify a running enclave
You can verify a running enclave given its IP using the --enclave-ip option. You can also customize the port being queried using the --attestation-port option.
The verifier fetches the attestation by making a HTTP request to the http://<ENCLAVE_IP>:<ATTESTATION_PORT>/attestation/raw endpoint, which means that the enclave needs to have a server running on that endpoint. This is handled automatically for enclaves deployed using the blue enclave image (which is the default unless you explicitly specified --image-url during deployment) since it runs an attestation server at the expected endpoint, but it is something to keep in mind for custom enclave images. We recommend custom images to also run an attestation server on the same port for consistency across the ecosystem unless it is truly unavoidable, so it is very likely that the above works across all enclaves regardless of the underlying enclave image.
Verify a hex-encoded attestation
You can also verify an existing hex-encoded attestation by using the --attestation-hex option and passing the attestation directly on the command line.
If passing it directly on the command line is unwieldy or you already have the attestation in a file, you can use the --attestation-hex-file option and give it a text file containing the hex-encoded attestation.
Attestation expectations
Usually, attestation verification only implies verification of the attestation document structure and the certificate chain. The command also allows you to specify expected values for fields in the attestation in order to run additional checks against the attestation. Most commonly, you would want to verify that the attestation is coming from a real Nitro enclave that is running an expected version of code.
Verify the root of trust
The --root-public-key option can be used to specify a hex encoded public key to match the root of trust against.
The attestation has a certificate chain with a public key as the root of trust. Verifying that it matches the expected AWS Nitro public key lets you ensure that the attestation came from a valid AWS Nitro Enclave with all the expected guarantees of a TEE.
It is a critical security check that must not be missed for production deployments, therefore it is enabled by default with the AWS Nitro root key.
Less commonly, you can also use this to override the root public key for local deployments done using the oyster-cvm simulate command.
Verify measurements
Measurements lets you know what code is running inside an enclave and thereby deduce the behaviour of an enclave.
It is a critical security check that must not be missed for production deployments. However, it is not enabled by default since there isn't really a sensible default that can be set. Unless you really know what you are doing (e.g. you intend to verify them manually in the logs), you should not be verifying attestations without verifying measurements.
There are multiple ways to specify expected measurements.
The recommended and most common way is by specifying --image-id, think twice before using the other method. This value is printed by the oyster-cvm deploy command, so you can directly obtain it from there. You can also use the oyster-cvm compute-image-id command to compute it from parameters similar to the deploy command like the docker compose file and any init params.
You can also specify expected measurements directly as PCRs and user data, in case you are deploying a custom enclave image. However, please be careful to specify all the parameters needed for verification. The PCRs can be specified
- individually using
-0,-1and-2, common if you just have the PCRs - as a JSON measurements file using
--pcr-json, common if you built the enclave image usingoyster-cvm build - as a preset using
--pcr-presetto target known enclave images, unlikely you will really ever need this, prefer--presetinstead
For manually specifying PCRs and user data, we cannot think of a single situation where you would not want to specify all of PCR0, PCR1, PCR2 and user data. Even if you need an empty user data, you can specify --user-data "" to verify that it is indeed empty. Think really hard about the security implications if you decide to specify some and not others (most commonly specifying PCRs without user data).
Verify liveness
You can also specify expectations about the timestamp field in the attestation. The most common option is --max-age to check if the attestation is too old.
We consider liveness checks a good practice for regular deployments, hence it is enabled by default at 5 minutes. However, some use cases require you to verify attestations generated quite a while ago, hence you can simply override it by specifying a very high max age.
A less common option is verifying the timestamp in the attestation directly by specifying --timestamp.
So what exactly is verified?
To summarize, the above method verifies the following:
- the attestation document is valid (in terms of structure), thereby enabling extraction and verification of attestation fields
- the attestation document is signed by a valid certificate chain, thereby enabling extraction and verification of the root public key
- all specified attestation expectations
- by default, whether the certificate chain is rooted at the AWS Nitro root public key, thereby proving that the attestation was generated by a real AWS Nitro enclave
- by default, whether the attestation was generated within the last 5 minutes, thereby proving that the attestation was generated recently
The verify command prints the attestation expectations that it verified against at the end, including the default expectations. You can double check if it is missing anything.