Skip to content
This repository has been archived by the owner on Jan 20, 2022. It is now read-only.

Commit

Permalink
[Pal/Linux-SGX] Add sgx.protected_mr{enclave,signer}_files manifest…
Browse files Browse the repository at this point in the history
… options

Previously, only `sgx.protected_files` were available in the manifest.
This kind of protected files needs a provisioned master (wrap) key. But
sometimes it is enough to seal files on the same platform for later
usage by the same enclave or by enclaves of the same signer: this is the
SGX sealing feature.

This commit adds two more options to support SGX sealing:
`sgx.protected_mrenclave_files` and `sgx.protected_mrsigner_files`.
Similarly to `sgx.protected_files`, these new options specify lists of
files that are encrypted by the SGX key generated via SGX instruction
`EGETKEY(SEAL_KEY)`, bound to the MRENCLAVE/MRSIGNER enclave measurement
(so that only instances of the same enclave/only enclaves with the same
signer may decrypt protected files). A corresponding LibOS test is added
and documentation is updated to reflect this.

Signed-off-by: Dmitrii Kuvaiskii <[email protected]>
  • Loading branch information
Dmitrii Kuvaiskii committed Jun 29, 2021
1 parent d9304f5 commit c8e152d
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 16 deletions.
14 changes: 13 additions & 1 deletion Documentation/manifest-syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,9 @@ Protected files
::

sgx.protected_files_key = "[16-byte hex value]"
sgx.protected_files.[identifier] = "[URI]"
sgx.protected_files.[identifier] = "[URI]"
sgx.protected_mrenclave_files.[identifier] = "[URI]"
sgx.protected_mrsigner_files.[identifier] = "[URI]"

This syntax specifies the files that are encrypted on disk and transparently
decrypted when accessed by Graphene or by application running inside Graphene.
Expand All @@ -508,6 +510,16 @@ size is limited to 260 bytes.
be used only for debugging purposes. In production environments, this key must
be provisioned to the enclave using local/remote attestation.

``sgx.protected_files`` are encrypted using the wrap (master) encryption key;
they are well-suited for input files encrypted by the user and sent to the
deployment platform as well as for output files sent back to the user and
decrypted at the user side. ``sgx.protected_mrenclave_files`` are encrypted
using the SGX sealing key based on the MRENCLAVE identity of the enclave; they
are useful to allow only the same enclave (on the same platform) to unseal
files. ``sgx.protected_mrsigner_files`` are encrypted using the SGX sealing key
based on the MRSIGNER identity of the enclave; they are useful to allow all
enclaves from the same signer (but on the same platform) to unseal files.

File check policy
^^^^^^^^^^^^^^^^^

Expand Down
5 changes: 4 additions & 1 deletion LibOS/shim/test/regression/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/*.manifest
/*.xml

/protected_file.dat
/testfile

/.cache
/abort
/abort_multithread
Expand Down Expand Up @@ -77,6 +80,7 @@
/proc_common
/proc_cpuinfo
/proc_path
/protected_file
/pselect
/pthread_set_get_affinity
/rdtsc
Expand All @@ -99,7 +103,6 @@
/sysfs_common
/tcp_ipv6_v6only
/tcp_msg_peek
/testfile
/tmp
/udp
/unix
Expand Down
2 changes: 2 additions & 0 deletions LibOS/shim/test/regression/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ c_executables = \
proc_common \
proc_cpuinfo \
proc_path \
protected_file \
pselect \
pthread_set_get_affinity \
readdir \
Expand Down Expand Up @@ -182,6 +183,7 @@ clean-tmp:
*.sig \
*.tmp \
*.token \
*.dat \
*~ \
.cache \
.pytest_cache \
Expand Down
3 changes: 3 additions & 0 deletions LibOS/shim/test/regression/manifest.template
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ sgx.allowed_files.tmp_dir = "file:tmp/"
sgx.allowed_files.root = "file:root" # for getdents test
sgx.allowed_files.testfile = "file:testfile" # for mmap_file test

# for protected_file test
sgx.protected_mrenclave_files.pffile = "file:protected_file.dat"

sgx.thread_num = 16

sgx.nonpie_binary = true
99 changes: 99 additions & 0 deletions LibOS/shim/test/regression/protected_file.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define SECRETSTRING "Secret string\n"

static ssize_t rw_file(const char* path, char* buf, size_t bytes, bool do_write) {
size_t rv = 0;
size_t ret = 0;

FILE* f = fopen(path, do_write ? "w" : "r");
if (!f) {
fprintf(stderr, "opening %s failed\n", path);
return -1;
}

while (bytes > rv) {
if (do_write)
ret = fwrite(buf + rv, /*size=*/1, /*nmemb=*/bytes - rv, f);
else
ret = fread(buf + rv, /*size=*/1, /*nmemb=*/bytes - rv, f);

if (ret > 0) {
rv += ret;
} else {
if (feof(f)) {
if (rv) {
/* read some bytes from file, success */
break;
}
assert(rv == 0);
fprintf(stderr, "%s failed: unexpected end of file\n", do_write ? "write" : "read");
fclose(f);
return -1;
}

assert(ferror(f));

if (errno == EAGAIN || errno == EINTR) {
continue;
}

fprintf(stderr, "%s failed: %s\n", do_write ? "write" : "read", strerror(errno));
fclose(f);
return -1;
}
}

int close_ret = fclose(f);
if (close_ret) {
fprintf(stderr, "closing %s failed\n", path);
return -1;
}
return rv;
}


int main(int argc, char** argv) {
int ret;
ssize_t bytes;

if (argc != 2)
errx(EXIT_FAILURE, "Usage: %s <protected file to create/validate>", argv[0]);

ret = access(argv[1], F_OK);
if (ret < 0) {
if (errno == ENOENT) {
/* file is not yet created, create with secret string */
bytes = rw_file(argv[1], SECRETSTRING, sizeof(SECRETSTRING), /*do_write=*/true);
if (bytes != sizeof(SECRETSTRING)) {
/* error is already printed by rw_file_f() */
return EXIT_FAILURE;
}
printf("CREATION OK\n");
return 0;
}
err(EXIT_FAILURE, "access failed");
}

char buf[128];
bytes = rw_file(argv[1], buf, sizeof(buf), /*do_write=*/false);
if (bytes <= 0) {
/* error is already printed by rw_file_f() */
return EXIT_FAILURE;
}
buf[bytes - 1] = '\0';

size_t size_to_cmp = sizeof(SECRETSTRING) < bytes ? sizeof(SECRETSTRING) : bytes;
if (strncmp(SECRETSTRING, buf, size_to_cmp))
errx(EXIT_FAILURE, "Expected '%s' but read '%s'\n", SECRETSTRING, buf);

printf("TEST OK\n");
return 0;
}
10 changes: 10 additions & 0 deletions LibOS/shim/test/regression/test_libos.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,16 @@ def test_040_sysfs(self):
self.assertIn(f'{node}/hugepages/hugepages-2048kB/nr_hugepages: file', lines)
self.assertIn(f'{node}/hugepages/hugepages-1048576kB/nr_hugepages: file', lines)

def test_060_protected_file(self):
pf_path = 'protected_file.dat'
if os.path.exists(pf_path):
os.remove(pf_path)

stdout, _ = self.run_binary(['protected_file', pf_path])
self.assertIn('CREATION OK', stdout)
stdout, _ = self.run_binary(['protected_file', pf_path])
self.assertIn('TEST OK', stdout)


class TC_50_GDB(RegressionTestCase):
def setUp(self):
Expand Down
35 changes: 35 additions & 0 deletions Pal/src/host/Linux-SGX/enclave_framework.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,41 @@ int sgx_verify_report(sgx_report_t* report) {
return 0;
}

int sgx_get_seal_key(uint16_t key_policy, sgx_key_128bit_t* seal_key) {
assert(key_policy == KEYPOLICY_MRENCLAVE || key_policy == KEYPOLICY_MRSIGNER);

/* get our own SGX report to obtain this enclave's isv_svn, cpu_svn, config_svn */
__sgx_mem_aligned sgx_target_info_t empty_target_info = {0};
__sgx_mem_aligned sgx_report_data_t empty_report_data = {0};
__sgx_mem_aligned sgx_report_t our_sgx_report = {0};
int ret = sgx_get_report(&empty_target_info, &empty_report_data, &our_sgx_report);
if (ret) {
log_error("Failed to get our own enclave report\n");
return -PAL_ERROR_DENIED;
}

/* The keyrequest struct dictates the key derivation material used to generate the sealing key.
* It includes MRENCLAVE/MRSIGNER key policy (to allow secret migration/sealing between
* instances of the same enclave or between different enclaves of the same author/signer), and
* CPU/ISV/CONFIG SVNs (to prevent secret migration to older vulnerable versions of the
* enclave). The rest of the keyrequest fields are currently zeros -- CET attributes, enclave
* ATTRIBUTES, enclave MISCSELECT bits are *not* included in key derivation. KEYID is also zero,
* to generate the same sealing key in different instances of the same enclave/same signer. */
__sgx_mem_aligned sgx_key_request_t key_request = {0};
key_request.key_name = SEAL_KEY;
key_request.key_policy = key_policy;
memcpy(&key_request.cpu_svn, &our_sgx_report.body.cpu_svn, sizeof(sgx_cpu_svn_t));
memcpy(&key_request.isv_svn, &our_sgx_report.body.isv_svn, sizeof(sgx_isv_svn_t));
memcpy(&key_request.config_svn, &our_sgx_report.body.config_svn, sizeof(sgx_config_svn_t));

ret = sgx_getkey(&key_request, seal_key);
if (ret) {
log_error("Failed to generate sealing key using SGX EGETKEY\n");
return -PAL_ERROR_DENIED;
}
return 0;
}

/* For each file that requires authentication (specified in the manifest as "sgx.trusted_files"), a
* SHA256 hash is generated and stored in the manifest, signed and verified as part of the enclave's
* crypto measurement. When user opens such a file, Graphene loads the whole file, calculates its
Expand Down
Loading

0 comments on commit c8e152d

Please sign in to comment.