From cd6a9cca9585110a9bcd5c63dcc75b5c4d49466b Mon Sep 17 00:00:00 2001 From: Dmitrii Kuvaiskii Date: Fri, 24 Mar 2023 08:29:10 -0700 Subject: [PATCH] [LibOS] Expose SGX sealing keys under `/dev/attestation/keys/` Gramine has two SGX sealing keys, named `_sgx_mrenclave` and `_sgx_mrsigner`. Previously, these keys could be used in the manifest file's `fs.mounts = [ key_name = "..." ]` syntax, but these keys were not exposed as `/dev/attestation/keys/{_sgx_mrenclave,_sgx_mrsigner}`. Signed-off-by: Dmitrii Kuvaiskii --- Documentation/manifest-syntax.rst | 9 ++++++++ libos/src/fs/dev/attestation.c | 24 ++++++++++++++++------ libos/test/regression/keys.c | 32 ++++++++++++++++++++++++++++- libos/test/regression/test_libos.py | 5 ++++- pal/include/pal/pal.h | 5 ++++- pal/src/host/linux-sgx/pal_misc.c | 4 ++-- 6 files changed, 68 insertions(+), 11 deletions(-) diff --git a/Documentation/manifest-syntax.rst b/Documentation/manifest-syntax.rst index 2e73e5c9af..804599b782 100644 --- a/Documentation/manifest-syntax.rst +++ b/Documentation/manifest-syntax.rst @@ -769,6 +769,15 @@ Gramine: identity of the enclave. This is useful to allow all enclaves signed with the same key (and on the same platform) to unseal files. +.. warning:: + The same key must not be used for the encrypted-files mount and for the + application's own crypto operations. Such "double" use of the same key may + lead to compromise of the key. For example, specifying an FS mount via + ``{type = "encrypted", ..., key_name = "_sgx_mrenclave"}`` in the manifest + and using the same key obtained via ``/dev/attestation/keys/_sgx_mrenclave`` + in the application is insecure. If you need to derive encryption keys from + such a "doubly-used" key, you must apply a KDF. + File check policy ^^^^^^^^^^^^^^^^^ diff --git a/libos/src/fs/dev/attestation.c b/libos/src/fs/dev/attestation.c index 51b2771d2d..1355722c43 100644 --- a/libos/src/fs/dev/attestation.c +++ b/libos/src/fs/dev/attestation.c @@ -18,6 +18,7 @@ #include "api.h" #include "libos_fs_encrypted.h" #include "libos_fs_pseudo.h" +#include "pal.h" #include "toml_utils.h" /* user_report_data, target_info and quote are opaque blobs of predefined maximum sizes. Currently @@ -322,7 +323,7 @@ static int key_save(struct libos_dentry* dent, const char* data, size_t size) { return 0; } -static int init_sgx_attestation(struct pseudo_node* attestation) { +static int init_sgx_attestation(struct pseudo_node* attestation, struct pseudo_node* keys) { if (strcmp(g_pal_public_state->host_type, "Linux-SGX")) return 0; @@ -344,6 +345,21 @@ static int init_sgx_attestation(struct pseudo_node* attestation) { target_info->perm = PSEUDO_PERM_FILE_RW; target_info->str.save = &target_info_save; + /* dummy retrieval of SGX sealing keys, so that they appear under /dev/attestation/keys/ */ + const char* sealing_keys_names[] = { PAL_KEY_NAME_SGX_MRENCLAVE, PAL_KEY_NAME_SGX_MRSIGNER }; + for (size_t i = 0; i < ARRAY_SIZE(sealing_keys_names); i++) { + struct libos_encrypted_files_key* key; + int ret = get_or_create_encrypted_files_key(sealing_keys_names[i], &key); + if (ret < 0) { + log_error("Cannot initialize SGX sealing key `%s`", sealing_keys_names[i]); + return ret; + } + } + + /* SGX sealing keys must be read-only, so we mount them over other /dev/attestation/keys/ */ + pseudo_add_str(keys, PAL_KEY_NAME_SGX_MRENCLAVE, &key_load); + pseudo_add_str(keys, PAL_KEY_NAME_SGX_MRSIGNER, &key_load); + if (!strcmp(g_pal_public_state->attestation_type, "none")) { log_debug("host is Linux-SGX and remote attestation type is 'none', skipping " "/dev/attestation/quote file"); @@ -359,10 +375,6 @@ static int init_sgx_attestation(struct pseudo_node* attestation) { int init_attestation(struct pseudo_node* dev) { struct pseudo_node* attestation = pseudo_add_dir(dev, "attestation"); - int ret = init_sgx_attestation(attestation); - if (ret < 0) - return ret; - struct pseudo_node* keys = pseudo_add_dir(attestation, "keys"); struct pseudo_node* key = pseudo_add_str(keys, /*name=*/NULL, &key_load); key->name_exists = &key_name_exists; @@ -370,5 +382,5 @@ int init_attestation(struct pseudo_node* dev) { key->perm = PSEUDO_PERM_FILE_RW; key->str.save = &key_save; - return 0; + return init_sgx_attestation(attestation, keys); } diff --git a/libos/test/regression/keys.c b/libos/test/regression/keys.c index 2ba376b658..daa7a90bcc 100644 --- a/libos/test/regression/keys.c +++ b/libos/test/regression/keys.c @@ -26,6 +26,10 @@ typedef uint8_t pf_key_t[KEY_SIZE]; #define DEFAULT_KEY_PATH "/dev/attestation/keys/default" #define CUSTOM_KEY_PATH "/dev/attestation/keys/my_custom_key" +/* Special keys (SGX sealing keys), always existing under SGX and read-only */ +#define MRENCLAVE_KEY_PATH "/dev/attestation/keys/_sgx_mrenclave" +#define MRSIGNER_KEY_PATH "/dev/attestation/keys/_sgx_mrsigner" + static const pf_key_t default_key = { 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; @@ -47,6 +51,15 @@ static void format_key(char buf[static KEY_STR_SIZE], const pf_key_t* key) { buf[KEY_SIZE * 2] = '\0'; } +static void verify_key_exists(const char* desc, const char* path) { + pf_key_t key; + ssize_t n = posix_file_read(path, (char*)&key, sizeof(key)); + if (n < 0) + err(1, "%s: error reading %s", desc, path); + if ((size_t)n != sizeof(key)) + errx(1, "%s: file %s has wrong size: expected %zd, got %zd", desc, path, sizeof(key), n); +} + static void expect_key(const char* desc, const char* path, const pf_key_t* expected_key) { pf_key_t key; @@ -75,7 +88,24 @@ static void write_key(const char* desc, const char* path, const pf_key_t* key) { errx(1, "%s: not enough bytes written to %s: %zd", desc, path, n); } -int main(void) { +static void fail_write_key(const char* desc, const char* path) { + pf_key_t dummy_key = {0}; + ssize_t n = posix_file_write(path, (char*)&dummy_key, sizeof(dummy_key)); + if (n >= 0) + errx(1, "%s: writing to %s unexpectedly succeeded", desc, path); +} + +int main(int argc, char** argv) { + if (argc > 2 || (argc == 2 && strcmp(argv[1], "sgx") != 0)) + errx(1, "test expects either no arguments, or one argument `sgx`"); + + if (argc == 2) { + verify_key_exists("SGX sealing keys", MRENCLAVE_KEY_PATH); + verify_key_exists("SGX sealing keys", MRSIGNER_KEY_PATH); + fail_write_key("SGX sealing keys", MRENCLAVE_KEY_PATH); + fail_write_key("SGX sealing keys", MRSIGNER_KEY_PATH); + } + expect_key("before writing key", DEFAULT_KEY_PATH, &default_key); expect_key("before writing key", CUSTOM_KEY_PATH, &custom_key); diff --git a/libos/test/regression/test_libos.py b/libos/test/regression/test_libos.py index c6152e68ce..5dde145dd4 100644 --- a/libos/test/regression/test_libos.py +++ b/libos/test/regression/test_libos.py @@ -323,7 +323,10 @@ def _test_send_handle(self, path, delete=False): self.assertIn('TEST OK', stdout, 'test failed: {}'.format(cmd)) def test_230_keys(self): - stdout, _ = self.run_binary(['keys']) + if HAS_SGX: + stdout, _ = self.run_binary(['keys', 'sgx']) + else: + stdout, _ = self.run_binary(['keys']) self.assertIn('TEST OK', stdout) def test_300_shared_object(self): diff --git a/pal/include/pal/pal.h b/pal/include/pal/pal.h index c3356b3418..7d1acc0c30 100644 --- a/pal/include/pal/pal.h +++ b/pal/include/pal/pal.h @@ -949,13 +949,16 @@ int PalAttestationQuote(const void* user_report_data, size_t user_report_data_si * size. * * Retrieve the value of a special key. Currently implemented for Linux-SGX PAL, which supports two - * such keys: `_sgx_mrenclave` and `_sgx_mrsigner`. + * such keys: `_sgx_mrenclave` and `_sgx_mrsigner` (see macros below). * * If a given key is not supported by the current PAL host, the function will return * -PAL_ERROR_NOTIMPLEMENTED. */ int PalGetSpecialKey(const char* name, void* key, size_t* key_size); +#define PAL_KEY_NAME_SGX_MRENCLAVE "_sgx_mrenclave" +#define PAL_KEY_NAME_SGX_MRSIGNER "_sgx_mrsigner" + #ifdef __GNUC__ #define symbol_version_default(real, name, version) \ __asm__(".symver " #real "," #name "@@" #version "\n") diff --git a/pal/src/host/linux-sgx/pal_misc.c b/pal/src/host/linux-sgx/pal_misc.c index b2ca85f5d9..7e3a6183f4 100644 --- a/pal/src/host/linux-sgx/pal_misc.c +++ b/pal/src/host/linux-sgx/pal_misc.c @@ -627,9 +627,9 @@ int _PalGetSpecialKey(const char* name, void* key, size_t* key_size) { return -PAL_ERROR_INVAL; int ret; - if (!strcmp(name, "_sgx_mrenclave")) { + if (!strcmp(name, PAL_KEY_NAME_SGX_MRENCLAVE)) { ret = sgx_get_seal_key(SGX_KEYPOLICY_MRENCLAVE, &sgx_key); - } else if (!strcmp(name, "_sgx_mrsigner")) { + } else if (!strcmp(name, PAL_KEY_NAME_SGX_MRSIGNER)) { ret = sgx_get_seal_key(SGX_KEYPOLICY_MRSIGNER, &sgx_key); } else { return -PAL_ERROR_NOTIMPLEMENTED;