From 3ae02d95180cdb5b4d3ca4f5389b252e7d356358 Mon Sep 17 00:00:00 2001 From: "Lavi, Nir" Date: Tue, 30 Aug 2022 16:31:00 +0300 Subject: [PATCH 1/4] [Python,CI,Docs] Support SGX extended product ID KSS is a new feature that introduces additional fields to identify enclave in build time: ISVEXTPRODID and ISVFAMILYID. These fields are defined the the developer and signed in SIGSTRUCT. Add new fields to the manifest to add support, and support in SGX signing tool. Add a hello world example with KSS enabled: Product ID fields are configured in the manifest and the enclave prints them. This example must be built with remote attestation. At this point SGX loader is not updated and there are no sanity checks prior to enclave load. Therefore, if an enclave with KSS is loaded on an unsupported platform, it might crash. Signed-off-by: Lavi, Nir --- CI-Examples/kss-helloworld/.gitignore | 3 + CI-Examples/kss-helloworld/Makefile | 64 +++++++++++++++++++ CI-Examples/kss-helloworld/README.md | 24 +++++++ CI-Examples/kss-helloworld/kss-helloworld.c | 63 ++++++++++++++++++ .../kss-helloworld.manifest.template | 29 +++++++++ CI-Examples/kss-helloworld/run_test.sh | 19 ++++++ Documentation/manifest-syntax.rst | 13 ++++ pal/src/host/linux-sgx/enclave_framework.c | 2 + pal/src/host/linux-sgx/generated_offsets.c | 1 + pal/src/host/linux-sgx/sgx_arch.h | 1 + python/graminelibos/manifest.py | 3 + python/graminelibos/sgx_sign.py | 4 ++ python/graminelibos/sigstruct.py | 4 ++ 13 files changed, 230 insertions(+) create mode 100644 CI-Examples/kss-helloworld/.gitignore create mode 100644 CI-Examples/kss-helloworld/Makefile create mode 100644 CI-Examples/kss-helloworld/README.md create mode 100644 CI-Examples/kss-helloworld/kss-helloworld.c create mode 100644 CI-Examples/kss-helloworld/kss-helloworld.manifest.template create mode 100755 CI-Examples/kss-helloworld/run_test.sh diff --git a/CI-Examples/kss-helloworld/.gitignore b/CI-Examples/kss-helloworld/.gitignore new file mode 100644 index 0000000000..91bc3e7822 --- /dev/null +++ b/CI-Examples/kss-helloworld/.gitignore @@ -0,0 +1,3 @@ +/kss-helloworld +/*.o +/OUTPUT diff --git a/CI-Examples/kss-helloworld/Makefile b/CI-Examples/kss-helloworld/Makefile new file mode 100644 index 0000000000..ad742b3dc6 --- /dev/null +++ b/CI-Examples/kss-helloworld/Makefile @@ -0,0 +1,64 @@ +GRAMINEDIR ?= ../.. +RA_TYPE ?= dcap + +CFLAGS = -Wall -Wextra -I$(GRAMINEDIR)/pal/src/host/linux-sgx + +ifeq ($(DEBUG),1) +GRAMINE_LOG_LEVEL = debug +CFLAGS += -g +else +GRAMINE_LOG_LEVEL = error +CFLAGS += -O3 +endif + +.PHONY: all +all: kss-helloworld.manifest.sgx kss-helloworld.sig kss-helloworld.token + +kss-helloworld: kss-helloworld.o + +kss-helloworld.o: kss-helloworld.c + +kss-helloworld.manifest: kss-helloworld.manifest.template + gramine-manifest \ + -Dlog_level=$(GRAMINE_LOG_LEVEL) \ + -Dra_type=$(RA_TYPE) \ + $< $@ + +# gramine-sgx-sign generates both a .sig file and a .manifest.sgx file. This is somewhat +# hard to express properly in Make. The simple solution would be to use +# "Rules with Grouped Targets" (`&:`), however make on Ubuntu <= 20.04 doesn't support it. +# +# Simply using a normal rule with "two targets" is equivalent to creating separate rules +# for each of the targets, and when using `make -j`, this might cause two instances +# of gramine-sgx-sign to get launched simultaneously, potentially breaking the build. +# +# As a workaround, we use a dummy intermediate target, and mark both files as depending on it, to +# get the dependency graph we want. We mark this dummy target as .INTERMEDIATE, which means +# that make will consider the source tree up-to-date even if the sgx_sign file doesn't exist, +# as long as the other dependencies check out. This is in contrast to .PHONY, which would +# be rebuilt on every invocation of make. +kss-helloworld.sig kss-helloworld.manifest.sgx: sgx_sign + @: + +.INTERMEDIATE: sgx_sign +sgx_sign: kss-helloworld.manifest kss-helloworld + gramine-sgx-sign \ + --manifest $< \ + --output $<.sgx + +kss-helloworld.token: kss-helloworld.sig + gramine-sgx-get-token \ + --output $@ --sig $< + +GRAMINE = gramine-sgx + +.PHONY: check +check: all + $(GRAMINE) kss-helloworld + +.PHONY: clean +clean: + $(RM) *.token *.sig *.manifest.sgx *.manifest kss-helloworld.o kss-helloworld OUTPUT + +.PHONY: distclean +distclean: clean diff --git a/CI-Examples/kss-helloworld/README.md b/CI-Examples/kss-helloworld/README.md new file mode 100644 index 0000000000..e0f962f702 --- /dev/null +++ b/CI-Examples/kss-helloworld/README.md @@ -0,0 +1,24 @@ +# KSS Hello World + +This directory contains a Makefile and a manifest template for running a simple +"Hello World" program with KSS in Gramine. It is built with KSS support and non-zero +values for `isvextprodid` and `isvfamilyid`, which are printed by the enclave. +report. + +This example is SGX-specific. + +# Build + +Run `make` (non-debug) or `make DEBUG=1` (debug) in the directory. + +Remote attestation must be supported to run this test. Default attestation type is `dcap`. +It can be modified with the `RA_TYPE` flag in make. For example: `make RA_TYPE=epid`. + +# Run + +```sh +gramine-sgx ./kss-helloworld +``` + +Note that a platform with KSS support must be used, otherwise this example will not work. +Use `is-sgx-available` tool to determine if the platform supports KSS. diff --git a/CI-Examples/kss-helloworld/kss-helloworld.c b/CI-Examples/kss-helloworld/kss-helloworld.c new file mode 100644 index 0000000000..738c48c2a5 --- /dev/null +++ b/CI-Examples/kss-helloworld/kss-helloworld.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include "sgx_arch.h" + +#define MAX_DIGITS (256ULL) // Should be more than enough to contain all buffers + +static void blob2hex(const void* input, size_t len, char* output) { + static const char digits[] = "0123456789abcdef"; + for(size_t i=0;i> 4]; + output[2*i+1] = digits[byte & 0xf]; + } + output[2*len + 1] = 0; +} + +int main(void) { + char print_buffer[MAX_DIGITS]; + FILE* attestation_type_fd = NULL; + FILE* report_fd = NULL; + sgx_report_t report; + + attestation_type_fd = fopen("/dev/attestation/attestation_type", "rb"); + if(attestation_type_fd == NULL) { + fprintf(stderr, "Failed to open attestation type handle\n"); + return 1; + } + if(fread(print_buffer, 1, sizeof(print_buffer), attestation_type_fd) < 0) { + fprintf(stderr, "Failed to read attestation type\n"); + return 2; + } + // Report handles are loaded only if remote attestation is enabled + if(strcmp(print_buffer, "none") == 0) { + fprintf(stderr, "Must be built with remote attestation\n"); + return 3; + } + fclose(attestation_type_fd); + + // We only care about this enclave's attributes, so we omit report data and target info + report_fd = fopen("/dev/attestation/report", "rb"); + if(report_fd == NULL) { + fprintf(stderr, "Failed to open report handle\n"); + return 4; + } + if(fread(&report, sizeof(report), 1, report_fd) <= 0) { + fprintf(stderr, "Failed to read report\n"); + return 5; + } + fclose(report_fd); + + printf("isvprodid = %04x\n", report.body.isv_prod_id); + printf("isvsvn = %d\n", report.body.isv_svn); + blob2hex(report.body.isv_ext_prod_id, sizeof(report.body.isv_ext_prod_id), print_buffer); + printf("isvextprodid = %s\n", print_buffer); + blob2hex(report.body.isv_family_id, sizeof(report.body.isv_family_id), print_buffer); + printf("isvfamilyid = %s\n", print_buffer); + blob2hex(report.body.config_id.data, sizeof(report.body.config_id.data), print_buffer); + printf("configid = %s\n", print_buffer); + printf("configsvn = %d\n", report.body.config_svn); + return 0; +} diff --git a/CI-Examples/kss-helloworld/kss-helloworld.manifest.template b/CI-Examples/kss-helloworld/kss-helloworld.manifest.template new file mode 100644 index 0000000000..6cf852e1a9 --- /dev/null +++ b/CI-Examples/kss-helloworld/kss-helloworld.manifest.template @@ -0,0 +1,29 @@ +# Hello World manifest file example + +loader.entrypoint = "file:{{ gramine.libos }}" +libos.entrypoint = "/kss-helloworld" +loader.log_level = "{{ log_level }}" + +loader.env.LD_LIBRARY_PATH = "/lib" + +fs.mounts = [ + { path = "/lib", uri = "file:{{ gramine.runtimedir() }}" }, + { path = "/kss-helloworld", uri = "file:kss-helloworld" }, +] + +sgx.debug = true +sgx.nonpie_binary = true + +sgx.remote_attestation = "{{ ra_type }}" + +sgx.kss = true +sgx.isvprodid = 5 +sgx.isvsvn = 2 +sgx.isvfamilyid = "0x00112233445566778899aabbccddeeff" +sgx.isvextprodid = "0xcafef00dcafef00df00dcafef00dcafe" + +sgx.trusted_files = [ + "file:{{ gramine.libos }}", + "file:kss-helloworld", + "file:{{ gramine.runtimedir() }}/", +] diff --git a/CI-Examples/kss-helloworld/run_test.sh b/CI-Examples/kss-helloworld/run_test.sh new file mode 100755 index 0000000000..7afbc42489 --- /dev/null +++ b/CI-Examples/kss-helloworld/run_test.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e + +# Default is SGX, because this test is SGX-specific +if test -n "$DIRECT" +then + GRAMINE=gramine-direct +else + GRAMINE=gramine-sgx +fi + +echo Running kss-helloworld +$GRAMINE ./kss-helloworld > OUTPUT +grep -q 'isvprodid = 0005' OUTPUT && echo '[ Success 1/4 ]' +grep -q 'isvsvn = 2' OUTPUT && echo '[ Success 2/4 ]' +grep -q 'isvextprodid = cafef00dcafef00df00dcafef00dcafe' OUTPUT && echo '[ Success 3/4 ]' +grep -q 'isvfamilyid = 00112233445566778899aabbccddeeff' OUTPUT && echo '[ Success 4/4 ]' +rm OUTPUT diff --git a/Documentation/manifest-syntax.rst b/Documentation/manifest-syntax.rst index c53d302575..ba82182b3b 100644 --- a/Documentation/manifest-syntax.rst +++ b/Documentation/manifest-syntax.rst @@ -615,6 +615,19 @@ ISV Product ID and SVN This syntax specifies the ISV Product ID and SVN to be added to the enclave signature. +Extended ISV Product ID (KSS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sgx.kss = [true|false] (default: false) + sgx.isvextprodid = "[16-byte hex value]" (default: "0x00000000000000000000000000000000") + sgx.isvfamily = "[16-byte hex value]" (default: "0x00000000000000000000000000000000") + +If ``sgx.kss = true``, this syntax specifies the extended ISV product ID and family ID to be added to the +enclave signature. The platform must support KSS to launch the enclave. +Otherwise, these two fields are ignored and remain zero. + Attribute masks for SGX sealing key derivation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/pal/src/host/linux-sgx/enclave_framework.c b/pal/src/host/linux-sgx/enclave_framework.c index 0f86f32dc8..b3bc6e35ca 100644 --- a/pal/src/host/linux-sgx/enclave_framework.c +++ b/pal/src/host/linux-sgx/enclave_framework.c @@ -249,6 +249,8 @@ static void print_report(sgx_report_t* r) { log_debug(" attr.xfrm: %016lx", r->body.attributes.xfrm); log_debug(" isv_prod_id: %02x", r->body.isv_prod_id); log_debug(" isv_svn: %02x", r->body.isv_svn); + log_debug(" isv_ext_prod_id: %s", BYTES2HEX(r->body.isv_ext_prod_id)); + log_debug(" isv_family_id: %s", BYTES2HEX(r->body.isv_family_id)); log_debug(" report_data: %s", BYTES2HEX(r->body.report_data.d)); log_debug(" key_id: %s", BYTES2HEX(r->key_id.id)); log_debug(" mac: %s", BYTES2HEX(r->mac)); diff --git a/pal/src/host/linux-sgx/generated_offsets.c b/pal/src/host/linux-sgx/generated_offsets.c index bbd8d3a161..aa8d0ad124 100644 --- a/pal/src/host/linux-sgx/generated_offsets.c +++ b/pal/src/host/linux-sgx/generated_offsets.c @@ -15,6 +15,7 @@ const struct generated_offset generated_offsets[] = { /* defines from sgx_arch.h */ DEFINE(SGX_FLAGS_DEBUG, SGX_FLAGS_DEBUG), DEFINE(SGX_FLAGS_MODE64BIT, SGX_FLAGS_MODE64BIT), + DEFINE(SGX_FLAGS_KSS, SGX_FLAGS_KSS), DEFINE(SGX_XFRM_LEGACY, SGX_XFRM_LEGACY), DEFINE(SGX_XFRM_AVX, SGX_XFRM_AVX), DEFINE(SGX_XFRM_MPX, SGX_XFRM_MPX), diff --git a/pal/src/host/linux-sgx/sgx_arch.h b/pal/src/host/linux-sgx/sgx_arch.h index a0afb4fe39..58c38fde60 100644 --- a/pal/src/host/linux-sgx/sgx_arch.h +++ b/pal/src/host/linux-sgx/sgx_arch.h @@ -66,6 +66,7 @@ typedef uint8_t sgx_isvfamily_id_t[SGX_ISV_FAMILY_ID_SIZE]; #define SGX_FLAGS_MODE64BIT 0x04ULL #define SGX_FLAGS_PROVISION_KEY 0x10ULL #define SGX_FLAGS_LICENSE_KEY 0x20ULL +#define SGX_FLAGS_KSS 0x80ULL /* EINIT must verify *all* SECS.ATTRIBUTES[63..0] bits (FLAGS bits) against * SIGSTRUCT.ATTRIBUTES[63..0]. diff --git a/python/graminelibos/manifest.py b/python/graminelibos/manifest.py index a4f8d7c2ee..7918593bf2 100644 --- a/python/graminelibos/manifest.py +++ b/python/graminelibos/manifest.py @@ -95,6 +95,9 @@ def __init__(self, manifest_str): sgx.setdefault('isvprodid', 0) sgx.setdefault('isvsvn', 0) + sgx.setdefault('kss', False) + sgx.setdefault('isvextprodid', b'\x00'*16) + sgx.setdefault('isvfamilyid', b'\x00'*16) sgx.setdefault('remote_attestation', "none") sgx.setdefault('debug', False) sgx.setdefault('require_avx', False) diff --git a/python/graminelibos/sgx_sign.py b/python/graminelibos/sgx_sign.py index f03b8bc820..dd74e05aec 100644 --- a/python/graminelibos/sgx_sign.py +++ b/python/graminelibos/sgx_sign.py @@ -81,6 +81,7 @@ def collect_bits(manifest_sgx, options_dict): def get_enclave_attributes(manifest_sgx): flags_dict = { 'debug': offs.SGX_FLAGS_DEBUG, + 'kss': offs.SGX_FLAGS_KSS, } xfrms_dict = { @@ -539,6 +540,9 @@ def get_tbssigstruct(manifest_path, date, libpal=SGX_LIBPAL, verbose=False): sig['attribute_flags'] = attribute_flags sig['attribute_xfrms'] = attribute_xfrms sig['misc_select'] = misc_select + if attribute_flags & offs.SGX_FLAGS_KSS: + sig['isv_ext_prod_id'] = int(manifest_sgx['isvextprodid'], 16).to_bytes(16, 'big') + sig['isv_family_id'] = int(manifest_sgx['isvfamilyid'], 16).to_bytes(16, 'big') return sig diff --git a/python/graminelibos/sigstruct.py b/python/graminelibos/sigstruct.py index dc02a655cf..52b16f3b7e 100644 --- a/python/graminelibos/sigstruct.py +++ b/python/graminelibos/sigstruct.py @@ -34,6 +34,8 @@ class Sigstruct: 'attribute_xfrm_mask': (offs.SGX_ARCH_SIGSTRUCT_ATTRIBUTE_MASK + 8, ' Date: Wed, 11 Jan 2023 11:32:37 +0200 Subject: [PATCH 2/4] fixup! [Python,CI,Docs] Support SGX extended product ID Signed-off-by: Lavi, Nir --- CI-Examples/kss-helloworld/kss-helloworld.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI-Examples/kss-helloworld/kss-helloworld.c b/CI-Examples/kss-helloworld/kss-helloworld.c index 738c48c2a5..3a4f382cc3 100644 --- a/CI-Examples/kss-helloworld/kss-helloworld.c +++ b/CI-Examples/kss-helloworld/kss-helloworld.c @@ -27,7 +27,7 @@ int main(void) { fprintf(stderr, "Failed to open attestation type handle\n"); return 1; } - if(fread(print_buffer, 1, sizeof(print_buffer), attestation_type_fd) < 0) { + if(fread(print_buffer, 1, sizeof(print_buffer), attestation_type_fd) <= 0) { fprintf(stderr, "Failed to read attestation type\n"); return 2; } From 0451c88d7548d1b622cf20093682ec0501b4345f Mon Sep 17 00:00:00 2001 From: "Lavi, Nir" Date: Wed, 11 Jan 2023 11:58:44 +0200 Subject: [PATCH 3/4] fixup! [Python,CI,Docs] Support SGX extended product ID Signed-off-by: Lavi, Nir --- python/graminelibos/manifest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/graminelibos/manifest.py b/python/graminelibos/manifest.py index 7918593bf2..8a57ef9267 100644 --- a/python/graminelibos/manifest.py +++ b/python/graminelibos/manifest.py @@ -96,8 +96,8 @@ def __init__(self, manifest_str): sgx.setdefault('isvprodid', 0) sgx.setdefault('isvsvn', 0) sgx.setdefault('kss', False) - sgx.setdefault('isvextprodid', b'\x00'*16) - sgx.setdefault('isvfamilyid', b'\x00'*16) + sgx.setdefault('isvextprodid', 0) + sgx.setdefault('isvfamilyid', 0) sgx.setdefault('remote_attestation', "none") sgx.setdefault('debug', False) sgx.setdefault('require_avx', False) From 1e9721c8e7863843f6cbbd42d482026951b0f7c7 Mon Sep 17 00:00:00 2001 From: "Lavi, Nir" Date: Tue, 31 Jan 2023 14:40:37 +0200 Subject: [PATCH 4/4] fixup! [Python,CI,Docs] Support SGX extended product ID Signed-off-by: Lavi, Nir --- pal/src/host/linux-sgx/host_internal.h | 1 + pal/src/host/linux-sgx/host_main.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/pal/src/host/linux-sgx/host_internal.h b/pal/src/host/linux-sgx/host_internal.h index e10771a3b9..34133255f1 100644 --- a/pal/src/host/linux-sgx/host_internal.h +++ b/pal/src/host/linux-sgx/host_internal.h @@ -48,6 +48,7 @@ struct pal_enclave { unsigned long ssa_frame_size; bool nonpie_binary; bool edmm_enabled; + bool kss_enabled; enum sgx_attestation_type attestation_type; char* libpal_uri; /* Path to the PAL binary */ diff --git a/pal/src/host/linux-sgx/host_main.c b/pal/src/host/linux-sgx/host_main.c index d82dea2d80..a9b64519e9 100644 --- a/pal/src/host/linux-sgx/host_main.c +++ b/pal/src/host/linux-sgx/host_main.c @@ -670,6 +670,14 @@ static int parse_loader_config(char* manifest, struct pal_enclave* enclave_info, goto out; } + ret = toml_bool_in(manifest_root, "sgx.kss", /*defaultval=*/false, + &enclave_info->kss_enabled); + if (ret < 0) { + log_error("Cannot parse 'sgx.kss'"); + ret = -EINVAL; + goto out; + } + if (!enclave_info->size || !IS_POWER_OF_2(enclave_info->size)) { log_error("Enclave size not a power of two (an SGX-imposed requirement)"); ret = -EINVAL; @@ -1005,6 +1013,15 @@ static int load_enclave(struct pal_enclave* enclave, char* args, size_t args_siz return -EINVAL; } + if (enclave->kss_enabled) { + uint32_t cpuid_values[4]; + cpuid(INTEL_SGX_LEAF, 1, cpuid_values); + if (!(cpuid_values[0] & (1u << 7))) { + log_error("KSS feature was requested in manifest, but the platform doesn't support it"); + return -EPERM; + } + } + ret = initialize_enclave(enclave, enclave->raw_manifest_data); if (ret < 0) return ret;