This sample demonstrates how to do attestation between two enclaves and establish a secure communication channel for exchanging messages between them.
It has the following properties:
- Written in C++
- Demonstrates an implementation of attestation
- Use of mbedTLS within the enclave
- Use Asymmetric / Public-Key Encryption to establish secure communications between two attesting enclaves
- Enclave APIs used:
oe_verifier_initialize()
oe_attester_initialize()
oe_serialize_custom_claims()
oe_deserialize_custom_claims()
oe_get_evidence()
oe_verify_evidence()
oe_verifier_get_format_settings()
oe_is_within_enclave()
Note: Unlike the local SGX functionality, currently the remote SGX functionality only works on SGX-FLC systems. The underlying SGX library support for end-to-end remote attestation is available only on SGX-FLC systems. There is no plan to back port those libraries to either an SGX1 system or simulation mode.
Attestation is the process of demonstrating that a software component (such as an enclave image) has been properly instantiated on a Trusted Execution Environment (TEE, such as the SGX enabled platform).
A successfully attested enclave proves:
-
The enclave is running in a valid Trusted Execution Environment (TEE), which is Intel SGX in this case (trustworthiness).
-
The enclave has the correct identity and runtime properties that have not been tampered with (identity).
In the context of Open Enclave, when an enclave requests confidential information from a remote entity, the remote entity will issue a challenge to the requesting enclave to prove its identity and trustworthiness before provisioning any confidential information to the enclave. This process of proving its identity and trustworthiness to a challenger is known as attestation.
Most TEEs use one attestation mechanism specific to that TEE. SGX, however, has two separate mechanisms for local vs. remote attestation:
-
Local Attestation refers to two enclaves on the same TEE platform establishing trust in each other before exchanging information. In Open Enclave, this is done through the creation and validation of an enclave's "Intel SGX report".
-
Remote Attestation is the process of a trusted computing base (TCB), a combination of HW and SW, gaining the trust of a remote enclave/provider. In Open Enclave, this is done through the creation and validation of an enclave's "Intel SGX quote".
For more details on attestation in SGX, see the Intel SGX attestation article.
Attestation alone is not enough for a peer to be able to securely deliver secrets to a requesting enclave. Securely delivering secrets requires a secure communication channel which is often guaranteed by Transport Layer Security (TLS).
A few alternatives for establishing a secure communication channel without TLS are:
- Use the established ephemeral private keys to perform a signed Diffie-Hellman key exchange and use symmetric key cryptography to communicate after that point.
- Generate an ephemeral symmetric key in one of the enclaves, say enclave_a, encrypt with the public key of enclave_b, sign with your private key and then send it to enclave_b. This will ensure that the symmetric key is only known to the two enclaves and the root of trust is in the remote attestation.
This remote attestation sample only demonstrates the remote attestation process but does not establish a secure communication channel or communicate secrets after that. Please note that the established public keys cannot be used to encrypt the messages as they are visible to the external world, including the host. The host can fake messages on behalf of the enclaves.
In a typical Open Enclave application, it's common to see multiple enclaves working together to achieve common goals. Once an enclave verifies the counterpart is trustworthy, they can exchange information on a protected channel, which typically provides confidentiality, integrity and replay protection.
This is why instead of attesting an enclave to a remote (mostly cloud) service, this sample demonstrates how to attest two enclaves to each other by using Open Enclave APIs oe_verifier_get_format_settings()
, oe_get_evidence()
, and oe_verify_evidence()
which take care of all attestation operations.
To simplify this sample without losing the focus in explaining how the attestation works, host1 and host2 are combined into one single host to eliminate the need for additional socket code logic to deal with communication between two hosts.
The host process is what drives the enclave app. It is responsible for managing the lifetime of the enclave and invoking enclave ECALLs but should be considered an untrusted component that is never allowed to handle plaintext secrets intended for the enclave.
The host does the following in this sample:
-
Create two enclaves for attesting each other, let's say they are enclave_a and enclave_b
oe_create_attestation_enclave( enclaveImagePath, OE_ENCLAVE_TYPE_AUTO, OE_ENCLAVE_FLAG_DEBUG, NULL, 0, &enclave);
-
Ask enclave_a for evidence and a public key:
This is done through a call into the enclave_a
get_evidence_with_public_key()
OE_ECALL
get_evidence_with_public_key(enclave_a, &ret, format_id, &format_settings, &pem_key, &evidence);
Where:
-
pem_key
holds the public key that identifies enclave_a, and -
evidence
contains the evidence signed by the enclave platform for use in remote attestation.
-
-
Ask enclave_b to attest (validate) enclave_a's evidence.
This is done through the following call:
verify_evidence_and_set_public_key(enclave_a, &ret, &format_id, &pem_key, &evidence);
In the enclave_b's implementation of
verify_evidence_and_set_public_key()
, it callsoe_verify_evidence()
, which will be described in the enclave section to handle all the platform-specfic evidence validation operations. If successful the public key inpem_key
will be stored inside the enclave for future use. -
Repeat steps 2 and 3 for asking enclave_a to validate enclave_b.
-
Ask enclave_a to generate a message encrypted with enclave\b's public key.
generate_encrypted_message(enclave_a, &ret, &encrypted_message, &encrypted_message_size);
-
Send the encrypted message to enclave_b to decrypt and validate if the decrypted message is correct.
process_encrypted_message(enclave_b, &ret, encrypted_message, encrypted_message_size);
-
Free any resources used, including the enclaves themselves.
For example:
oe_terminate_enclave(enclave_a); oe_terminate_enclave(enclave_b);
For two enclaves to mutually attest to each other, each enclave needs to separately attest to the other. This sample illustrates both enclaves doing so using the same procedure.
Let's say, we want to attest enclave 2 (the "Attester") to enclave 1 (the "Verifier").
Attesting an enclave consists of three steps:
To conduct an attestation and ensure that the evidence is fresh,
the Verifier (enclave 1) needs to be able to construct a challenge that it
expects to be used when the Attester (enclave 2) generates its evidence.
(In SGX local attestation, the challenge also contains the identity
of the Verifier.)
This is done by calling oe_verifier_get_format_settings()
from the Verifier enclave,
where the format_id
identifies the attestation mechanism (e.g., SGX
local attestation vs SGX remote attestation).
oe_result_t oe_verifier_get_format_settings(
const oe_uuid_t* format_id,
uint8_t** settings,
size_t* settings_size);
Using the challenge provided by the Verifier, the Attester enclave needs to generate cryptographically strong evidence of its trustworthiness that the Verifier can appraise. In the sample this is done by asking the platform to generate such evidence.
An important feature of oe_get_evidence()
is that you can pass in application specific data as the custom_claims_buffer
parameter to be signed into the evidence.
-
This is limited to 64 bytes in SGX. As illustrated in the sample, you sign arbitrarily large data into the evidence by first hashing it and then passing it to the
oe_get_evidence()
method. -
This is useful to bootstrap a secure communication channel between the enclave and the challenger.
-
In this sample, the enclave signs the hash of an ephemeral public key into its evidence, which the challenger can then use to encrypt a response to it.
-
Other usage examples for
custom_claims_buffer
might be to include a nonce, or to initiate a Diffie-Helman key exchange.
-
Once the evidence is generated and passed to the Verifier, the Verifier can
call oe_verify_evidence()
to validate the evidence.
For Intel SGX remote attestation for example, an Intel SGX quote is verified using the certificate chain issued by Intel which is only valid for SGX platforms. Note: Currently, remote attestation verification is only supported in Azure ACC VMs, but Intel will be expanding support for this with Open Enclave SDK more broadly moving forward.
At this point, the challenger knows that the evidence originated from an enclave running in a TEE, and that the information in the evidence can be trusted.
This validation consists two parts:
-
Verifying the integrity of the evidence
Enclave 1 can call
oe_verify_evidence()
to validate the evidence originated from an Trusted Execution Environment (TEE).oe_result_t oe_verify_evidence( const oe_uuid_t* format_id, const uint8_t* evidence_buffer, size_t evidence_buffer_size, const uint8_t* endorsements_buffer, size_t endorsements_buffer_size, const oe_policy_t* policies, size_t policies_size, oe_claim_t** claims, size_t* claims_length);
At this point, Enclave 1 knows that the evidence originated from an enclave running in a TEE, and that the information in the evidence can be trusted.
-
Establish trust in the Attester enclave
To establish trust in the enclave that generated the evidence, the Signer ID, Product ID, and Security Version values are checked to see if they are predefined trusted values. Once the enclave's trust has been established, the validity of any accompanying data is ensured by comparing its SHA256 digest against the hash value stored in a custom claim in the signed evidence.
As shown in the sample, the set of validations performed on these properties is up to the app. In general, we would strongly recommend:
- Ensure that the identity of the enclave matches the expected value:
- Verify the
OE_CLAIM_UNIQUE_ID
value if you want to match the exact bitwise identity of the enclave. Bear in mind that any patches to the enclave will change theunique_id
claim in the future. - Verify the
OE_CLAIM_SIGNER_ID
andOE_CLAIM_PRODUCT_ID
values if you want to match the identity of an enclave that might span multiple binary versions. This is what the attestation sample does.
- Verify the
- Ensure that the
OE_CLAIM_SECURITY_VERSION
value of the enclave matches your minimum required security version. - Ensure that the hash encoded in the
OE_CLAIM_CUSTOM_CLAIMS_BUFFER
claim matches the hash of any accompanying data, as illustrated by the sample.
The attestation/common/crypto.cpp
file from the sample illustrates how to use mbedTLS inside the enclave for cryptographic operations such as:
- RSA key generation, encryption and decryption
- SHA256 hashing
In general, the Open Enclave SDK provides default support for mbedTLS layered on top of the Open Enclave core runtime with a small integration surface so that it can be switched out by open source developers in the future for your choice of crypto libraries.
See here for supported mbedTLS functions
In order to build and run this sample, please refer to the common sample README file.
To use only the SGX local functionality:
-
On Linux, use
make runsgxlocal
instead ofmake run
. -
On Windows, use
ninja runsgxlocal
instead ofninja run
.
To use only the SGX remote functionality:
-
On Linux, use
make runsgxremote
instead ofmake run
. -
On Windows, use
ninja runsgxremote
instead ofninja run
.
Evidence of format OE_FORMAT_UUID_SGX_ECDSA
can be generated by either taking the in-process call path or taking the out-of-process call path. However, if you wish to specify the call path it takes to generate a quote, here is what you can do:
- To perform in-process quote generation, unset the environment variable
SGX_AESM_ADDR
and ensure that the DCAP library is installed. - To perform out-of-process quote generation, set the environment variable
SGX_AESM_ADDR
to any value and ensure that SGX SDK quote-ex Library is installed.
On Linux, if SGX_AESM_ADDR
is not set, one can run an existing OE app with out-of-process attestation, using $ SGX_AESM_ADDR=1 <app_name>
.
- If
SGX_AESM_ADDR=1
is added to/etc/environment
instead, then it will setSGX_AESM_ADDR
for the whole system. To unset it for the whole system, simply remove the line. These actions require elevated privileges. - If
SGX_AESM_ADDR
is set by default globally, to run an existing OE app with in-process attestation, one can use$ env -u SGX_AESM_ADDR <app_name>
.
On Windows, in-process quote generation is preferred so there is no need to set the environment variable SGX_AESM_ADDR
.
Please refer to the following document for more information: