This sample demonstrates how to perform data sealing and unsealing.
It has the following properties:
- Explain the concept of sealing and unsealing in the OE
- Demonstrate how to use OE sealing APIs
- Use OE APIs
oe_get_seal_key_by_policy
oe_get_seal_key
The states of an enclave are not persisted by a system, that is, when an enclave is destroyed, all of its states are lost. This could happen when an enclave application exits, when a system reboots, or simply when a system goes into deep sleep states. To preserve an enclave's states, those states information must explicitly be sent outside the enclave to some persistent storage. When the same enclave is brought back, its states could be restored from the persistent storage. Some data-sealing is more for caching purpose.
The exact definition of an enclave's states is up to a host app and those enclaves involved. It could be some information about the current processing stage in a data pipeline or as simple as some internal secret.
Before those states information leaves an enclave, they are encrypted to protect it from an untrusted host. Data Sealing is the process of encrypting enclave's states for persistent storage outside of an enclave. This encrypted states are called sealed data. This encryption is performed using a private seal key, which is derived from the TEE system an enclave is running on. On the other hand, Data Unsealing is the reverse process that decrypts an enclave's sealed data using the same seal key. This allows an enclave's states to be restored when the same enclave was subsequently brought back up.
Instead of publishing convenient APIs, such as Intel SGX SDK's sgx_seal_data and sgx_unseal_data, which uses a pre-determined fixed encryption algorithm(AES-GCM) for encryption, OE decides to only provide generic routine for getting seal key and leaves the encryption algorithm up to the enclave developers to choose whatever algorithm they see fit.
Two type of seal keys are supported by OE: In the OE terminology, each seal key type is called a seal policy.
-
OE_SEAL_POLICY_UNIQUE
This type of seal key is derived from a measurement of the enclave. Under this policy, the sealed secret can only be unsealed by an instance of the exact enclave code that sealed it. This policy corresponds to using the SGX MRENCLAVE identity for deriving the sealing key.
-
OE_SEAL_POLICY_PRODUCT
This type of seal key is derived from the signer of the enclave. Under this policy, the sealed secret can be unsealed by any enclave signed by the same signer as that of the sealing enclave. The "PRODUCT" in the policy name assumes all enclaves signed by the same signer belong to the same product. This policy corresponds to using the SGX MRSIGNER and ISVPRODID values for deriving the sealing key.
For sealing data to the enclave for caching, OE SDK exposes two methods for an application to take advantage of sealing keys provided by underlying platform:
-
Call
oe_get_seal_key_by_policy
to obtain a symmetric key based on the current enclave properties (such as its SecurityVersion and debug state) and your choice of identity properties as specified by the seal policy. -
Call
oe_get_seal_key
to obtain a seal key with the same properties as returned in the keyInfo from a previous call tooe_get_seal_key_by_policy
. This method is used to get the sealing key to unseal previously sealed data. This is recommended because events such as patching of the server can change the properties used to derive the sealing key (e.g. CPUSVN) inoe_get_seal_key_by_policy
. As a best practice, you should persist thekeyInfo
along with your encrypted data for such scenarios.
Once an enclave gets a seal key, it can use it to seal/unseal data.
This sample emphasizes on demonstrating how an OE host application initiates seal and unseal operations with simple input data with different policies on enclave environments.
Note: While it's not shown in this sample, seal/unseal operations could be triggered from inside an enclave on the data invisible to host.
-
enclave_a_v1
andenclave_a_v2
were created and signed by the sameprivate_a.pem
file, which means they share the same signer. -
enclave_b
was signed by a newly createdprivate_b.pem
and has a different signer/product identity.
-
The host seals a test data string into enclave_a_v1 with
OE_SEAL_POLICY_UNIQUE
, which was done through anunseal_data()
ecall into enclave1. Upon finishing sealing, asealed_data_t
structure is returned back to the host. -
The host unseals the
sealed_data_t
inenclave_a_v1
and expects to see it's unsealed successfully because this sealed data was sealed in the sameenclave_a_v1 enclave
. -
The host unseals the
sealed_data_t
inenclave_a_v2
and expects to see it fail to unseal because this sealed data was not sealed by the same enclave. -
The host unseals the
sealed_data_t
inenclave_b
and expects to see it fail to unseal because this sealed data was not sealed by the same enclave.
-
The host seals a test data string into
enclave_a_v1
withOE_SEAL_POLICY_PRODUCT
, which was done through anunseal_data()
ecall into enclave1. Upon finishing sealing, asealed_data_t
structure is returned back to the host. -
The host unseals the
sealed_data_t
inenclave_a_v1
and expects to see it's unsealed successfully because this sealed data was sealed in the sameenclave_a_v1 enclave
. -
The host unseals the
sealed_data_t
inenclave_a_v2
and expects to see it successfully unseal because this sealed data was signed by the same signature for the same product. -
The host unseals the
sealed_data_t
inenclave_b
and expects to see it fail to unseal because this sealed data was not sealed by the same enclave.
All three enclaves are almost identical except signed by two different private.pem files.
There are two ECALLs implemented inside each enclave library.
typedef struct _data_t
{
uint8_t* data;
size_t size;
} data_t;
The structure is used by the ECALLs to pass deep-copied buffer.
int seal_data(int sealPolicy,
unsigned char* opt_mgs, size_t opt_msg_len,
unsigned char* data, size_t data_size,
data_t* sealed_data)
The enclave allocates the following sealed data structure and fills with iv,encrypted data, and other fields before adding the generated signature to it.
typedef struct _sealed_data_t
{
size_t total_size;
unsigned char signature[SIGNATURE_LEN];
unsigned char opt_msg[MAX_OPT_MESSAGE_LEN];
unsigned char iv[IV_SIZE];
size_t key_info_size;
size_t encrypted_data_len;
unsigned char encrypted_data[];
} sealed_data_t;
seal_data
callsoe_get_seal_key_by_policy
with eitherOE_SEAL_POLICY_UNIQUE
orOE_SEAL_POLICY_PRODUCT
to get a unique seal key and its seal key info.- Generate an initialization vector.
- Encrypt the input data.
- Allocate the
sealed_data_t
structure - Generate a signature from the
sealed_data_t
structure with the seal key. - Fill the
sealed_data_t
with above information before returning to the host.
int unseal_data(const data_t* sealed_data, data_t* output_data)
seal_data
callsoe_get_seal_key
with key info fromsealed_data_t
.- Retrieve initialization vector from
sealed_data_t.iv
. - Regenerate a new signature from
sealed_data_t
and validate it againstsealed_data_t.signature
. - Decrypt
sealed_data_t.encrypted_data
. - Return the decrypted data before returning to the host.
-
Security implications with sealing/unsealing: a host is a untrusted entity. And it can load and invoke an enclave in any order they chose. It's important that an enclave implementation does NOT allow the sealing and unsealing capability to leak secrets, or grant unauthorized access to them.
-
In a cloud environment, you must not expect that any information sealed by an enclave can be unsealed by it at a future point. This is because the sealing keys generated by platform are machine specific, and your VM may be migrated off a node at any time. For example, if a server is misbehaving and the service healing moves the VM to a different server to maintain a minimum level of service. As such, these APIs should only be used for caching enclave information across restarts of the application or reboots of the VM. It should always be able to fall back to obtaining the sealed data in some other way, such as requesting the secret again from a trusted source via remote attestation.
Note that there are two different build systems here, one using GNU Make and
pkg-config
, the other using CMake.
This uses the CMake package provided by the Open Enclave SDK.
cd data-sealing
mkdir build && cd build
cmake ..
make run
This uses GNU Make with pkg-config
to build in the source tree.
cd data-sealing
make build
make run