Join our public slack channel for support, discussions and more...
Contents

Example: Obtaining a sealing key

This example shows you how to use the EGETKEY functionality of the CPU to get a sealing key. There are many different ways to use this instruction; refer to the Intel SGX documentation for more information. This example is for a typical use case where you want to store sealed data and be able to read back the data after a TCB recovery event.

Add the following to the [dependencies] section in your crate's Cargo.toml:

1
2
sgx-isa = { version = "0.2", features = ["sgxstd"] }
rand = "0.6.4"

First, define a structure to keep metadata about the sealed data that should be stored alongside the sealed data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use sgx_isa::{Attributes, Miscselect};

/// Information about how the sealing key was derived. This
/// should be stored alongside the sealed data, so that the enclave
/// can rederive the same key later.
pub struct SealData {
    rand: [u8; 16],
    isvsvn: u16,
    cpusvn: [u8; 16],
    // Record attributes and miscselect so that we can verify that
    // we can derive the correct wrapping key, but the actual input
    // to the derivation is CPU enclave state + SW-specified masks.
    attributes: Attributes,
    miscselect: Miscselect,
}

Then define a function that actually calls Keyrequest::egetkey based on a context-specific label and the SealData. The parameters chosen in this function determine the set of enclaves that may be able to generate the same sealing key. This is an internal function and should not be called directly by application code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use sgx_isa::{ErrorCode, Keyname, Keypolicy, Keyrequest};

/// Derive a sealing key for the current enclave given `label` and
/// `seal_data`.
fn egetkey(label: [u8; 16], seal_data: &SealData)
    -> Result<[u8; 16], ErrorCode>
{
    // Key ID is combined from fixed label and random data
    let mut keyid = [0; 32];
    {
        let (label_dst, rand_dst) = keyid.split_at_mut(16);
        label_dst.copy_from_slice(&label);
        rand_dst.copy_from_slice(&seal_data.rand);
    }

    Keyrequest {
        keyname: Keyname::Seal as _,
        keypolicy: Keypolicy::MRENCLAVE,
        isvsvn: seal_data.isvsvn,
        cpusvn: seal_data.cpusvn,
        attributemask: [!0; 2],
        keyid: keyid,
        miscmask: !0,
        ..Default::default()
    }.egetkey()
}

Next, define a function for getting a key when sealing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use sgx_isa::Report;
use rand::random;

/// Get a key for sealing data.
///
/// The returned key may be used for authenticated encryption.
///
/// If you call `seal_key` at different places in your code to seal
/// different types of data, make sure to pass a different `label`.
/// The returned `SealData` should be stored alongside the
/// ciphertext to make sure the data can be unsealed again later.
pub fn seal_key(label: [u8; 16]) -> ([u8; 16], SealData) {
    let report = Report::for_self();
    let seal_data = SealData {
        // Generate fresh randomness for each sealing operation.
        rand: random(),
        // Copy the parameters of the current enclave into SealData.
        isvsvn: report.isvsvn,
        cpusvn: report.cpusvn,
        attributes: report.attributes,
        miscselect: report.miscselect,
    };

    // EGETKEY should never error here because we used the
    // information from `Report::for_self`.
    (egetkey(label, &seal_data).unwrap(), seal_data)
}

And finally, define a function for getting the appropriate key when unsealing previously-sealed data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/// Get a key for unsealing data.
///
/// The returned key may be used for authenticated decryption.
///
/// Pass in the same `label` that was used to get the sealing key,
/// and pass in the `seal_data` that was returned when obtaining the
/// sealing key.
///
/// # Errors
///
/// May return an error if the sealing key was not generated by the
/// same enclave configuration, or if the SGX TCB level has been
/// downgraded.
pub fn unseal_key(label: [u8; 16], seal_data: SealData)
    -> Result<[u8; 16], ErrorCode>
{
    let report = Report::for_self();
    // Make sure the parameters that are not checked for correctness
    // by EGETKEY match the current enclave. Without this check,
    // EGETKEY will proceed to derive a key, which will be an
    // incorrect key.
    if report.attributes != seal_data.attributes
        || report.miscselect != seal_data.miscselect
    {
        return Err(ErrorCode::InvalidAttribute)
    }

    egetkey(label, &seal_data)
}

You can now use the seal_key and unseal_key functions in your code, alongside an authenticated encryption primitive such as AES-GCM.

Contents