Skip to content

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). Documentation is updated to reflect
this.

A corresponding LibOS test is added. As a side-effect, read/write
utilities are extracted into a separate helper file `rw_file.c`.

Signed-off-by: Dmitrii Kuvaiskii <dmitrii.kuvaiskii@intel.com>
  • Loading branch information
dimakuv committed Oct 3, 2021
1 parent 601fb00 commit 62fafb1
Show file tree
Hide file tree
Showing 16 changed files with 542 additions and 180 deletions.
26 changes: 26 additions & 0 deletions Documentation/manifest-syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -525,11 +525,22 @@ Protected files
::

sgx.protected_files_key = "[16-byte hex value]"

sgx.protected_files = [
"[URI]",
"[URI]",
]

sgx.protected_mrenclave_files = [
"[URI]",
"[URI]",
]

sgx.protected_mrsigner_files = [
"[URI]",
"[URI]",
]

This syntax specifies the files that are encrypted on disk and transparently
decrypted when accessed by Gramine or by application running inside Gramine.
Protected files guarantee data confidentiality and integrity (tamper
Expand All @@ -555,6 +566,21 @@ be used only for debugging purposes.
manifest option at all. Instead, use the Secret Provisioning interface (see
:doc:`attestation`).

There are three types of protected files:

* ``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
signed with the same key (and on the same platform) to unseal files.

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

Expand Down
3 changes: 3 additions & 0 deletions LibOS/shim/test/regression/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/*.dat
/*.manifest
/*.xml

Expand Down Expand Up @@ -92,6 +93,8 @@
/run_test
/sched
/sched_set_get_affinity
/sealed_file
/sealed_file_mod
/select
/send_handle
/shared_object
Expand Down
20 changes: 18 additions & 2 deletions LibOS/shim/test/regression/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ c_executables = \
run_test \
sched \
sched_set_get_affinity \
sealed_file \
sealed_file_mod \
select \
send_handle \
shared_object \
Expand Down Expand Up @@ -166,10 +168,23 @@ else
CFLAGS-openmp = -fopenmp
endif

proc_common: proc_common.o dump.o
attestation: attestation.c rw_file.c
$(call cmd,cmulti)

sysfs_common: sysfs_common.o dump.o
file_size: file_size.c rw_file.c
$(call cmd,cmulti)

proc_common: proc_common.c dump.c
$(call cmd,cmulti)

sealed_file: sealed_file.c rw_file.c
$(call cmd,cmulti)

CFLAGS-sealed_file_mod += -DMODIFY_MRENCLAVE # see comment in the test's source
sealed_file_mod: sealed_file.c rw_file.c
$(call cmd,cmulti)

sysfs_common: sysfs_common.c dump.c
$(call cmd,cmulti)

%: %.c
Expand All @@ -190,6 +205,7 @@ libos-regression.xml: test_libos.py $(call expand_target_to_token,$(target))
clean-tmp:
$(RM) -r \
*.cached \
*.dat \
*.manifest \
*.manifest.sgx \
*.sig \
Expand Down
144 changes: 24 additions & 120 deletions LibOS/shim/test/regression/attestation.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "mbedtls/base64.h"
#include "mbedtls/cmac.h"

#include "rw_file.h"
#include "sgx_api.h"
#include "sgx_arch.h"
#include "sgx_attest.h"
Expand All @@ -23,105 +24,8 @@ char user_report_data_str[] = "This is user-provided report data";

enum { SUCCESS = 0, FAILURE = -1 };

ssize_t (*rw_file_f)(const char* path, char* buf, size_t bytes, bool do_write);

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

int fd = open(path, do_write ? O_WRONLY : O_RDONLY);
if (fd < 0) {
fprintf(stderr, "opening %s failed\n", path);
return fd;
}

while (bytes > rv) {
if (do_write)
ret = write(fd, buf + rv, bytes - rv);
else
ret = read(fd, buf + rv, bytes - rv);

if (ret > 0) {
rv += ret;
} else if (ret == 0) {
/* end of file */
if (rv == 0)
fprintf(stderr, "%s failed: unexpected end of file\n", do_write ? "write" : "read");
break;
} else {
if (ret < 0 && (errno == EAGAIN || errno == EINTR)) {
continue;
} else {
fprintf(stderr, "%s failed: %s\n", do_write ? "write" : "read", strerror(errno));
goto out;
}
}
}

out:
if (ret < 0) {
/* error path */
close(fd);
return ret;
}

ret = close(fd);
if (ret < 0) {
fprintf(stderr, "closing %s failed\n", path);
return ret;
}
return rv;
}

static ssize_t rw_file_stdio(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 ? "wb" : "rb");
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;
}
ssize_t (*file_read_f)(const char* path, char* buf, size_t bytes);
ssize_t (*file_write_f)(const char* path, char* buf, size_t bytes);

/*!
* \brief Verify the signature on `report`.
Expand All @@ -137,7 +41,7 @@ static int verify_report_mac(sgx_report_t* report) {
/* setup key request structure */
__sgx_mem_aligned sgx_key_request_t key_request;
memset(&key_request, 0, sizeof(key_request));
key_request.key_name = REPORT_KEY;
key_request.key_name = SGX_REPORT_KEY;
memcpy(&key_request.key_id, &report->key_id, sizeof(key_request.key_id));

/* retrieve key via EGETKEY instruction leaf */
Expand Down Expand Up @@ -186,18 +90,17 @@ static int test_local_attestation(void) {

/* 1. read `my_target_info` file */
sgx_target_info_t target_info;
bytes = rw_file_f("/dev/attestation/my_target_info", (char*)&target_info, sizeof(target_info),
/*do_write=*/false);
bytes = file_read_f("/dev/attestation/my_target_info", (char*)&target_info,
sizeof(target_info));
if (bytes != sizeof(target_info)) {
/* error is already printed by rw_file_f() */
/* error is already printed by file_read_f() */
return FAILURE;
}

/* 2. write data from `my_target_info` to `target_info` file */
bytes = rw_file_f("/dev/attestation/target_info", (char*)&target_info, sizeof(target_info),
/*do_write=*/true);
bytes = file_write_f("/dev/attestation/target_info", (char*)&target_info, sizeof(target_info));
if (bytes != sizeof(target_info)) {
/* error is already printed by rw_file_f() */
/* error is already printed by file_write_f() */
return FAILURE;
}

Expand All @@ -208,19 +111,18 @@ static int test_local_attestation(void) {

memcpy((void*)&user_report_data, (void*)user_report_data_str, sizeof(user_report_data_str));

bytes = rw_file_f("/dev/attestation/user_report_data", (char*)&user_report_data,
sizeof(user_report_data), /*do_write=*/true);
bytes = file_write_f("/dev/attestation/user_report_data", (char*)&user_report_data,
sizeof(user_report_data));
if (bytes != sizeof(user_report_data)) {
/* error is already printed by rw_file_f() */
/* error is already printed by file_write_f() */
return FAILURE;
}

/* 4. read `report` file */
sgx_report_t report;
bytes = rw_file_f("/dev/attestation/report", (char*)&report, sizeof(report),
/*do_write=*/false);
bytes = file_read_f("/dev/attestation/report", (char*)&report, sizeof(report));
if (bytes != sizeof(report)) {
/* error is already printed by rw_file_f() */
/* error is already printed by file_read_f() */
return FAILURE;
}

Expand Down Expand Up @@ -287,18 +189,17 @@ static int test_quote_interface(void) {

memcpy((void*)&user_report_data, (void*)user_report_data_str, sizeof(user_report_data_str));

bytes = rw_file_f("/dev/attestation/user_report_data", (char*)&user_report_data,
sizeof(user_report_data), /*do_write=*/true);
bytes = file_write_f("/dev/attestation/user_report_data", (char*)&user_report_data,
sizeof(user_report_data));
if (bytes != sizeof(user_report_data)) {
/* error is already printed by rw_file_f() */
/* error is already printed by file_write_f() */
return FAILURE;
}

/* 2. read `quote` file */
bytes = rw_file_f("/dev/attestation/quote", (char*)&g_quote, sizeof(g_quote),
/*do_write=*/false);
bytes = file_read_f("/dev/attestation/quote", (char*)&g_quote, sizeof(g_quote));
if (bytes < 0) {
/* error is already printed by rw_file_f() */
/* error is already printed by file_read_f() */
return FAILURE;
}

Expand Down Expand Up @@ -327,10 +228,13 @@ static int test_quote_interface(void) {
}

int main(int argc, char** argv) {
rw_file_f = rw_file_posix;
file_read_f = posix_file_read;
file_write_f = posix_file_write;

if (argc > 1) {
/* simple trick to test stdio-style interface to pseudo-files in our tests */
rw_file_f = rw_file_stdio;
file_read_f = stdio_file_read;
file_write_f = stdio_file_write;
}

printf("Test local attestation... %s\n",
Expand Down
35 changes: 6 additions & 29 deletions LibOS/shim/test/regression/file_size.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <sys/types.h>
#include <unistd.h>

#include "rw_file.h"

#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

Expand All @@ -17,31 +19,6 @@
#define TEST_DIR "tmp"
#define TEST_FILE "__testfile__"

static ssize_t rw_file(int fd, char* buf, size_t bytes, bool write_flag) {
ssize_t rv = 0;
ssize_t ret;

while (bytes > rv) {
if (write_flag)
ret = write(fd, buf + rv, bytes - rv);
else
ret = read(fd, buf + rv, bytes - rv);

if (ret > 0) {
rv += ret;
} else {
if (ret < 0 && (errno == EAGAIN || errno == EINTR)) {
continue;
} else {
fprintf(stderr, "%s failed:%s\n", write_flag ? "write" : "read", strerror(errno));
return ret;
}
}
}

return rv;
}

int main(int argc, const char** argv) {
char buf[BUF_LENGTH];
ssize_t bytes;
Expand Down Expand Up @@ -71,7 +48,7 @@ int main(int argc, const char** argv) {
}

/* test file size: write a file of type != FILEBUF_MAP */
bytes = rw_file(fd, buf, BUF_LENGTH, true);
bytes = posix_fd_write(fd, buf, BUF_LENGTH);
if (bytes != BUF_LENGTH) {
perror("writing " STR(BUF_LENGTH) " bytes to test file failed");
return 1;
Expand All @@ -83,7 +60,7 @@ int main(int argc, const char** argv) {
return 1;
}

bytes = rw_file(fd, buf, 1, true);
bytes = posix_fd_write(fd, buf, 1);
if (bytes != 1) {
perror("writing one byte to test file failed");
return 1;
Expand All @@ -95,7 +72,7 @@ int main(int argc, const char** argv) {
return 1;
}

bytes = rw_file(fd, buf, BUF_LENGTH, true);
bytes = posix_fd_write(fd, buf, BUF_LENGTH);
if (bytes != BUF_LENGTH) {
perror("writing " STR(BUF_LENGTH) " bytes to test file failed");
return 1;
Expand All @@ -120,7 +97,7 @@ int main(int argc, const char** argv) {
return 1;
}

bytes = rw_file(fd, buf, BUF_LENGTH, false);
bytes = posix_fd_read(fd, buf, BUF_LENGTH);
if (bytes != BUF_LENGTH) {
perror("reading " STR(BUF_LENGTH) " bytes from test file failed");
return 1;
Expand Down

0 comments on commit 62fafb1

Please sign in to comment.