Re: [PATCH v28 14/22] selftests/x86: Add a selftest for SGX

From: Nathaniel McCallum
Date: Wed Mar 04 2020 - 14:28:17 EST


On Tue, Mar 3, 2020 at 6:39 PM Jarkko Sakkinen
<jarkko.sakkinen@xxxxxxxxxxxxxxx> wrote:
>
> Add a selftest for SGX. It is a trivial test where a simple enclave
> copies one 64-bit word of memory between two memory locations given to
> the enclave as arguments. Use ENCLS[EENTER] to invoke the enclave.
>
> Cc: linux-sgx@xxxxxxxxxxxxxxx
> Cc: linux-kselftest@xxxxxxxxxxxxxxx
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@xxxxxxxxxxxxxxx>
> ---
> tools/testing/selftests/x86/sgx/.gitignore | 3 +
> tools/testing/selftests/x86/sgx/Makefile | 48 ++
> tools/testing/selftests/x86/sgx/defines.h | 17 +
> tools/testing/selftests/x86/sgx/encl.c | 20 +
> tools/testing/selftests/x86/sgx/encl.lds | 34 ++
> .../selftests/x86/sgx/encl_bootstrap.S | 94 ++++
> tools/testing/selftests/x86/sgx/main.c | 247 +++++++++
> tools/testing/selftests/x86/sgx/sgx_call.S | 23 +
> tools/testing/selftests/x86/sgx/sgx_call.h | 11 +
> tools/testing/selftests/x86/sgx/sgxsign.c | 493 ++++++++++++++++++
> .../testing/selftests/x86/sgx/signing_key.pem | 39 ++
> 11 files changed, 1029 insertions(+)
> create mode 100644 tools/testing/selftests/x86/sgx/.gitignore
> create mode 100644 tools/testing/selftests/x86/sgx/Makefile
> create mode 100644 tools/testing/selftests/x86/sgx/defines.h
> create mode 100644 tools/testing/selftests/x86/sgx/encl.c
> create mode 100644 tools/testing/selftests/x86/sgx/encl.lds
> create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S
> create mode 100644 tools/testing/selftests/x86/sgx/main.c
> create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
> create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.h
> create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c
> create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem
>
> diff --git a/tools/testing/selftests/x86/sgx/.gitignore b/tools/testing/selftests/x86/sgx/.gitignore
> new file mode 100644
> index 000000000000..98eb2d439606
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/.gitignore
> @@ -0,0 +1,3 @@
> +encl.ss
> +sgxsign
> +test_sgx
> diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile
> new file mode 100644
> index 000000000000..f838700029e2
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/Makefile
> @@ -0,0 +1,48 @@
> +top_srcdir = ../../../../..
> +
> +include ../../lib.mk
> +
> +ifndef OBJCOPY
> +OBJCOPY := $(CROSS_COMPILE)objcopy
> +endif
> +
> +INCLUDES := -I$(top_srcdir)/tools/include
> +HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC -z noexecstack
> +ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \
> + -fno-stack-protector -mrdrnd $(INCLUDES)
> +
> +TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
> +
> +all: $(TEST_CUSTOM_PROGS)
> +
> +$(OUTPUT)/test_sgx: $(OUTPUT)/main.o $(OUTPUT)/sgx_call.o
> + $(CC) $(HOST_CFLAGS) -o $@ $^
> +
> +$(OUTPUT)/main.o: main.c
> + $(CC) $(HOST_CFLAGS) -c $< -o $@
> +
> +$(OUTPUT)/sgx_call.o: sgx_call.S
> + $(CC) $(HOST_CFLAGS) -c $< -o $@
> +
> +$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf $(OUTPUT)/sgxsign
> + $(OBJCOPY) -O binary $< $@
> +
> +$(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S
> + $(CC) $(ENCL_CFLAGS) -T $^ -o $@
> +
> +$(OUTPUT)/encl.ss: $(OUTPUT)/encl.bin
> + $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
> +
> +$(OUTPUT)/sgxsign: sgxsign.c
> + $(CC) $(INCLUDES) -o $@ $< -lcrypto
> +
> +EXTRA_CLEAN := \
> + $(OUTPUT)/encl.bin \
> + $(OUTPUT)/encl.elf \
> + $(OUTPUT)/encl.ss \
> + $(OUTPUT)/sgx_call.o \
> + $(OUTPUT)/sgxsign \
> + $(OUTPUT)/test_sgx \
> + $(OUTPUT)/test_sgx.o \
> +
> +.PHONY: clean
> diff --git a/tools/testing/selftests/x86/sgx/defines.h b/tools/testing/selftests/x86/sgx/defines.h
> new file mode 100644
> index 000000000000..87264f85cb9f
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/defines.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright(c) 2016-19 Intel Corporation.
> + */
> +
> +#ifndef DEFINES_H
> +#define DEFINES_H
> +
> +#include <stdint.h>
> +
> +#define __aligned(x) __attribute__((__aligned__(x)))
> +#define __packed __attribute__((packed))
> +
> +#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h"
> +#include "../../../../../arch/x86/include/uapi/asm/sgx.h"
> +
> +#endif /* DEFINES_H */
> diff --git a/tools/testing/selftests/x86/sgx/encl.c b/tools/testing/selftests/x86/sgx/encl.c
> new file mode 100644
> index 000000000000..ede915399742
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/encl.c
> @@ -0,0 +1,20 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +// Copyright(c) 2016-18 Intel Corporation.
> +
> +#include <stddef.h>
> +#include "defines.h"
> +
> +static void *memcpy(void *dest, const void *src, size_t n)
> +{
> + size_t i;
> +
> + for (i = 0; i < n; i++)
> + ((char *)dest)[i] = ((char *)src)[i];
> +
> + return dest;
> +}
> +
> +void encl_body(void *rdi, void *rsi)
> +{
> + memcpy(rsi, rdi, 8);
> +}
> diff --git a/tools/testing/selftests/x86/sgx/encl.lds b/tools/testing/selftests/x86/sgx/encl.lds
> new file mode 100644
> index 000000000000..9a56d3064104
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/encl.lds
> @@ -0,0 +1,34 @@
> +OUTPUT_FORMAT(elf64-x86-64)
> +
> +SECTIONS
> +{
> + . = 0;
> + .tcs : {
> + *(.tcs*)
> + }
> +
> + . = ALIGN(4096);
> + .text : {
> + *(.text*)
> + *(.rodata*)
> + }
> +
> + . = ALIGN(4096);
> + .data : {
> + *(.data*)
> + }
> +
> + /DISCARD/ : {
> + *(.data*)
> + *(.comment*)
> + *(.note*)
> + *(.debug*)
> + *(.eh_frame*)
> + }
> +}
> +
> +ASSERT(!DEFINED(.altinstructions), "ALTERNATIVES are not supported in enclaves")
> +ASSERT(!DEFINED(.altinstr_replacement), "ALTERNATIVES are not supported in enclaves")
> +ASSERT(!DEFINED(.discard.retpoline_safe), "RETPOLINE ALTERNATIVES are not supported in enclaves")
> +ASSERT(!DEFINED(.discard.nospec), "RETPOLINE ALTERNATIVES are not supported in enclaves")
> +ASSERT(!DEFINED(.got.plt), "Libcalls are not supported in enclaves")
> diff --git a/tools/testing/selftests/x86/sgx/encl_bootstrap.S b/tools/testing/selftests/x86/sgx/encl_bootstrap.S
> new file mode 100644
> index 000000000000..d07f970ccdf9
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/encl_bootstrap.S
> @@ -0,0 +1,94 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Copyright(c) 2016-18 Intel Corporation.
> + */
> +
> + .macro ENCLU
> + .byte 0x0f, 0x01, 0xd7
> + .endm
> +
> + .section ".tcs", "a"
> + .balign 4096
> +
> + .fill 1, 8, 0 # STATE (set by CPU)
> + .fill 1, 8, 0 # FLAGS
> + .quad encl_ssa # OSSA
> + .fill 1, 4, 0 # CSSA (set by CPU)
> + .fill 1, 4, 1 # NSSA
> + .quad encl_entry # OENTRY
> + .fill 1, 8, 0 # AEP (set by EENTER and ERESUME)
> + .fill 1, 8, 0 # OFSBASE
> + .fill 1, 8, 0 # OGSBASE
> + .fill 1, 4, 0xFFFFFFFF # FSLIMIT
> + .fill 1, 4, 0xFFFFFFFF # GSLIMIT
> + .fill 4024, 1, 0 # Reserved
> +
> + .text
> +
> +encl_entry:
> + # RBX contains the base address for TCS, which is also the first address
> + # inside the enclave. By adding the value of le_stack_end to it, we get
> + # the absolute address for the stack.
> + lea (encl_stack)(%rbx), %rax
> + xchg %rsp, %rax
> + push %rax
> +
> + push %rcx # push the address after EENTER
> + push %rbx # push the enclave base address
> +
> + call encl_body
> +
> + pop %rbx # pop the enclave base address
> +
> + # Restore XSAVE registers to a synthetic state.
> + mov $0xFFFFFFFF, %rax
> + mov $0xFFFFFFFF, %rdx
> + lea (xsave_area)(%rbx), %rdi
> + fxrstor (%rdi)
> +
> + # Clear GPRs.
> + xor %rcx, %rcx
> + xor %rdx, %rdx
> + xor %rdi, %rdi
> + xor %rsi, %rsi
> + xor %r8, %r8
> + xor %r9, %r9
> + xor %r10, %r10
> + xor %r11, %r11
> + xor %r12, %r12
> + xor %r13, %r13
> + xor %r14, %r14
> + xor %r15, %r15
> +
> + # Reset status flags.
> + add %rdx, %rdx # OF = SF = AF = CF = 0; ZF = PF = 1
> +
> + # Prepare EEXIT target by popping the address of the instruction after
> + # EENTER to RBX.
> + pop %rbx
> +
> + # Restore the caller stack.
> + pop %rax
> + mov %rax, %rsp
> +
> + # EEXIT
> + mov $4, %rax
> + enclu
> +
> + .section ".data", "aw"
> +
> +encl_ssa:
> + .space 4096
> +
> +xsave_area:
> + .fill 1, 4, 0x037F # FCW
> + .fill 5, 4, 0
> + .fill 1, 4, 0x1F80 # MXCSR
> + .fill 1, 4, 0xFFFF # MXCSR_MASK
> + .fill 123, 4, 0
> + .fill 1, 4, 0x80000000 # XCOMP_BV[63] = 1, compaction mode
> + .fill 12, 4, 0

I find this much more readable:

xsave_area:
# Legacy
.fill 1, 4, 0x037F # FCW
.fill 5, 4, 0
.fill 1, 4, 0x1F80 # MXCSR
.fill 1, 4, 0xFFFF # MXCSR_MASK
.fill 60, 8, 0

# Header
.fill 1, 8, 0 # XSTATE_BV
.fill 1, 8, 1 << 63 # XCOMP_BV (compaction mode)
.fill 6, 8, 0

Also, since people are likely to copy this code for their own
enclaves, it would be helpful to document which flags are set in FCW
and MXCSR.

> +
> + .balign 4096
> + .space 8192
> +encl_stack:
> diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c
> new file mode 100644
> index 000000000000..48ed5fdfb3cb
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/main.c
> @@ -0,0 +1,247 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +// Copyright(c) 2016-18 Intel Corporation.
> +
> +#include <elf.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +#include <sys/time.h>
> +#include <sys/types.h>
> +#include "defines.h"
> +#include "sgx_call.h"
> +
> +#define PAGE_SIZE 4096
> +
> +static const uint64_t MAGIC = 0x1122334455667788ULL;
> +
> +static bool encl_create(int dev_fd, unsigned long bin_size,
> + struct sgx_secs *secs)
> +{
> + struct sgx_enclave_create ioc;
> + void *area;
> + int rc;
> +
> + memset(secs, 0, sizeof(*secs));
> + secs->ssa_frame_size = 1;
> + secs->attributes = SGX_ATTR_MODE64BIT;
> + secs->xfrm = 3;
> +
> + for (secs->size = 4096; secs->size < bin_size; )
> + secs->size <<= 1;
> +
> + area = mmap(NULL, secs->size * 2, PROT_NONE, MAP_SHARED, dev_fd, 0);
> + if (area == MAP_FAILED) {
> + perror("mmap");
> + return false;
> + }
> +
> + secs->base = ((uint64_t)area + secs->size - 1) & ~(secs->size - 1);
> +
> + munmap(area, secs->base - (uint64_t)area);
> + munmap((void *)(secs->base + secs->size),
> + (uint64_t)area + secs->size - secs->base);
> +
> + ioc.src = (unsigned long)secs;
> + rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_CREATE, &ioc);
> + if (rc) {
> + fprintf(stderr, "ECREATE failed rc=%d, err=%d.\n", rc, errno);
> + munmap((void *)secs->base, secs->size);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static bool encl_add_pages(int dev_fd, unsigned long offset, void *data,
> + unsigned long length, uint64_t flags)
> +{
> + struct sgx_enclave_add_pages ioc;
> + struct sgx_secinfo secinfo;
> + int rc;
> +
> + memset(&secinfo, 0, sizeof(secinfo));
> + secinfo.flags = flags;
> +
> + ioc.src = (uint64_t)data;
> + ioc.offset = offset;
> + ioc.length = length;
> + ioc.secinfo = (unsigned long)&secinfo;
> + ioc.flags = SGX_PAGE_MEASURE;
> +
> + rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_ADD_PAGES, &ioc);
> + if (rc) {
> + fprintf(stderr, "EADD failed rc=%d.\n", rc);
> + return false;
> + }
> +
> + if (ioc.count != ioc.length) {
> + fprintf(stderr, "Partially processed, update the test.\n");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +#define SGX_REG_PAGE_FLAGS \
> + (SGX_SECINFO_REG | SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_X)
> +
> +static bool encl_build(struct sgx_secs *secs, void *bin,
> + unsigned long bin_size, struct sgx_sigstruct *sigstruct)
> +{
> + struct sgx_enclave_init ioc;
> + void *addr;
> + int dev_fd;
> + int rc;
> +
> + dev_fd = open("/dev/sgx/enclave", O_RDWR);
> + if (dev_fd < 0) {
> + fprintf(stderr, "Unable to open /dev/sgx\n");
> + return false;
> + }
> +
> + if (!encl_create(dev_fd, bin_size, secs))
> + goto out_dev_fd;
> +
> + if (!encl_add_pages(dev_fd, 0, bin, PAGE_SIZE, SGX_SECINFO_TCS))
> + goto out_dev_fd;
> +
> + if (!encl_add_pages(dev_fd, PAGE_SIZE, bin + PAGE_SIZE,
> + bin_size - PAGE_SIZE, SGX_REG_PAGE_FLAGS))
> + goto out_dev_fd;
> +
> + ioc.sigstruct = (uint64_t)sigstruct;
> + rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_INIT, &ioc);
> + if (rc) {
> + printf("EINIT failed rc=%d\n", rc);
> + goto out_map;
> + }
> +
> + addr = mmap((void *)secs->base, PAGE_SIZE, PROT_READ | PROT_WRITE,
> + MAP_SHARED | MAP_FIXED, dev_fd, 0);
> + if (addr == MAP_FAILED) {
> + fprintf(stderr, "mmap() failed on TCS, errno=%d.\n", errno);
> + return false;
> + }
> +
> + addr = mmap((void *)(secs->base + PAGE_SIZE), bin_size - PAGE_SIZE,
> + PROT_READ | PROT_WRITE | PROT_EXEC,
> + MAP_SHARED | MAP_FIXED, dev_fd, 0);
> + if (addr == MAP_FAILED) {
> + fprintf(stderr, "mmap() failed, errno=%d.\n", errno);
> + return false;
> + }
> +
> + close(dev_fd);
> + return true;
> +out_map:
> + munmap((void *)secs->base, secs->size);
> +out_dev_fd:
> + close(dev_fd);
> + return false;
> +}
> +
> +bool get_file_size(const char *path, off_t *bin_size)
> +{
> + struct stat sb;
> + int ret;
> +
> + ret = stat(path, &sb);
> + if (ret) {
> + perror("stat");
> + return false;
> + }
> +
> + if (!sb.st_size || sb.st_size & 0xfff) {
> + fprintf(stderr, "Invalid blob size %lu\n", sb.st_size);
> + return false;
> + }
> +
> + *bin_size = sb.st_size;
> + return true;
> +}
> +
> +bool encl_data_map(const char *path, void **bin, off_t *bin_size)
> +{
> + int fd;
> +
> + fd = open(path, O_RDONLY);
> + if (fd == -1) {
> + fprintf(stderr, "open() %s failed, errno=%d.\n", path, errno);
> + return false;
> + }
> +
> + if (!get_file_size(path, bin_size))
> + goto err_out;
> +
> + *bin = mmap(NULL, *bin_size, PROT_READ, MAP_PRIVATE, fd, 0);
> + if (*bin == MAP_FAILED) {
> + fprintf(stderr, "mmap() %s failed, errno=%d.\n", path, errno);
> + goto err_out;
> + }
> +
> + close(fd);
> + return true;
> +
> +err_out:
> + close(fd);
> + return false;
> +}
> +
> +bool load_sigstruct(const char *path, void *sigstruct)
> +{
> + int fd;
> +
> + fd = open(path, O_RDONLY);
> + if (fd == -1) {
> + fprintf(stderr, "open() %s failed, errno=%d.\n", path, errno);
> + return false;
> + }
> +
> + if (read(fd, sigstruct, sizeof(struct sgx_sigstruct)) !=
> + sizeof(struct sgx_sigstruct)) {
> + fprintf(stderr, "read() %s failed, errno=%d.\n", path, errno);
> + close(fd);
> + return false;
> + }
> +
> + close(fd);
> + return true;
> +}
> +
> +int main(int argc, char *argv[], char *envp[])
> +{
> + struct sgx_sigstruct sigstruct;
> + struct sgx_secs secs;
> + uint64_t result = 0;
> + off_t bin_size;
> + void *bin;
> +
> + if (!encl_data_map("encl.bin", &bin, &bin_size))
> + exit(1);
> +
> + if (!load_sigstruct("encl.ss", &sigstruct))
> + exit(1);
> +
> + if (!encl_build(&secs, bin, bin_size, &sigstruct))
> + exit(1);
> +
> + printf("Input: 0x%lx\n", MAGIC);
> +
> + sgx_call_eenter((void *)&MAGIC, &result, (void *)secs.base);
> + if (result != MAGIC) {
> + fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
> + exit(1);
> + }
> +
> + printf("Output: 0x%lx\n", result);
> +
> + exit(0);
> +}
> diff --git a/tools/testing/selftests/x86/sgx/sgx_call.S b/tools/testing/selftests/x86/sgx/sgx_call.S
> new file mode 100644
> index 000000000000..ca4c7893f9d9
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/sgx_call.S
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/**
> +* Copyright(c) 2016-18 Intel Corporation.
> +*/
> +
> + .text
> +
> + .macro ENCLU
> + .byte 0x0f, 0x01, 0xd7
> + .endm
> +
> + .text
> +
> + .global sgx_call_eenter
> +sgx_call_eenter:
> + push %rbx
> + mov $0x02, %rax
> + mov %rdx, %rbx
> + lea sgx_async_exit(%rip), %rcx
> +sgx_async_exit:
> + ENCLU
> + pop %rbx
> + ret
> diff --git a/tools/testing/selftests/x86/sgx/sgx_call.h b/tools/testing/selftests/x86/sgx/sgx_call.h
> new file mode 100644
> index 000000000000..bf72068ada23
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/sgx_call.h
> @@ -0,0 +1,11 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright(c) 2016-19 Intel Corporation.
> + */
> +
> +#ifndef SGX_CALL_H
> +#define SGX_CALL_H
> +
> +void sgx_call_eenter(void *rdi, void *rsi, void *entry);
> +
> +#endif /* SGX_CALL_H */
> diff --git a/tools/testing/selftests/x86/sgx/sgxsign.c b/tools/testing/selftests/x86/sgx/sgxsign.c
> new file mode 100644
> index 000000000000..3d9007af40c9
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/sgxsign.c
> @@ -0,0 +1,493 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +// Copyright(c) 2016-18 Intel Corporation.
> +
> +#define _GNU_SOURCE
> +#include <getopt.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <openssl/err.h>
> +#include <openssl/pem.h>
> +#include "defines.h"
> +
> +struct sgx_sigstruct_payload {
> + struct sgx_sigstruct_header header;
> + struct sgx_sigstruct_body body;
> +};
> +
> +static bool check_crypto_errors(void)
> +{
> + int err;
> + bool had_errors = false;
> + const char *filename;
> + int line;
> + char str[256];
> +
> + for ( ; ; ) {
> + if (ERR_peek_error() == 0)
> + break;
> +
> + had_errors = true;
> + err = ERR_get_error_line(&filename, &line);
> + ERR_error_string_n(err, str, sizeof(str));
> + fprintf(stderr, "crypto: %s: %s:%d\n", str, filename, line);
> + }
> +
> + return had_errors;
> +}
> +
> +static void exit_usage(const char *program)
> +{
> + fprintf(stderr,
> + "Usage: %s/sign-le <key> <enclave> <sigstruct>\n", program);
> + exit(1);
> +}
> +
> +static inline const BIGNUM *get_modulus(RSA *key)
> +{
> +#if OPENSSL_VERSION_NUMBER < 0x10100000L
> + return key->n;
> +#else
> + const BIGNUM *n;
> +
> + RSA_get0_key(key, &n, NULL, NULL);
> + return n;
> +#endif
> +}
> +
> +static RSA *load_sign_key(const char *path)
> +{
> + FILE *f;
> + RSA *key;
> +
> + f = fopen(path, "rb");
> + if (!f) {
> + fprintf(stderr, "Unable to open %s\n", path);
> + return NULL;
> + }
> + key = RSA_new();
> + if (!PEM_read_RSAPrivateKey(f, &key, NULL, NULL))
> + return NULL;
> + fclose(f);
> +
> + if (BN_num_bytes(get_modulus(key)) != SGX_MODULUS_SIZE) {
> + fprintf(stderr, "Invalid key size %d\n",
> + BN_num_bytes(get_modulus(key)));
> + RSA_free(key);
> + return NULL;
> + }
> +
> + return key;
> +}
> +
> +static void reverse_bytes(void *data, int length)
> +{
> + int i = 0;
> + int j = length - 1;
> + uint8_t temp;
> + uint8_t *ptr = data;
> +
> + while (i < j) {
> + temp = ptr[i];
> + ptr[i] = ptr[j];
> + ptr[j] = temp;
> + i++;
> + j--;
> + }
> +}
> +
> +enum mrtags {
> + MRECREATE = 0x0045544145524345,
> + MREADD = 0x0000000044444145,
> + MREEXTEND = 0x00444E4554584545,
> +};
> +
> +static bool mrenclave_update(EVP_MD_CTX *ctx, const void *data)
> +{
> + if (!EVP_DigestUpdate(ctx, data, 64)) {
> + fprintf(stderr, "digest update failed\n");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static bool mrenclave_commit(EVP_MD_CTX *ctx, uint8_t *mrenclave)
> +{
> + unsigned int size;
> +
> + if (!EVP_DigestFinal_ex(ctx, (unsigned char *)mrenclave, &size)) {
> + fprintf(stderr, "digest commit failed\n");
> + return false;
> + }
> +
> + if (size != 32) {
> + fprintf(stderr, "invalid digest size = %u\n", size);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +struct mrecreate {
> + uint64_t tag;
> + uint32_t ssaframesize;
> + uint64_t size;
> + uint8_t reserved[44];
> +} __attribute__((__packed__));
> +
> +
> +static bool mrenclave_ecreate(EVP_MD_CTX *ctx, uint64_t blob_size)
> +{
> + struct mrecreate mrecreate;
> + uint64_t encl_size;
> +
> + for (encl_size = 0x1000; encl_size < blob_size; )
> + encl_size <<= 1;
> +
> + memset(&mrecreate, 0, sizeof(mrecreate));
> + mrecreate.tag = MRECREATE;
> + mrecreate.ssaframesize = 1;
> + mrecreate.size = encl_size;
> +
> + if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL))
> + return false;
> +
> + return mrenclave_update(ctx, &mrecreate);
> +}
> +
> +struct mreadd {
> + uint64_t tag;
> + uint64_t offset;
> + uint64_t flags; /* SECINFO flags */
> + uint8_t reserved[40];
> +} __attribute__((__packed__));
> +
> +static bool mrenclave_eadd(EVP_MD_CTX *ctx, uint64_t offset, uint64_t flags)
> +{
> + struct mreadd mreadd;
> +
> + memset(&mreadd, 0, sizeof(mreadd));
> + mreadd.tag = MREADD;
> + mreadd.offset = offset;
> + mreadd.flags = flags;
> +
> + return mrenclave_update(ctx, &mreadd);
> +}
> +
> +struct mreextend {
> + uint64_t tag;
> + uint64_t offset;
> + uint8_t reserved[48];
> +} __attribute__((__packed__));
> +
> +static bool mrenclave_eextend(EVP_MD_CTX *ctx, uint64_t offset, uint8_t *data)
> +{
> + struct mreextend mreextend;
> + int i;
> +
> + for (i = 0; i < 0x1000; i += 0x100) {
> + memset(&mreextend, 0, sizeof(mreextend));
> + mreextend.tag = MREEXTEND;
> + mreextend.offset = offset + i;
> +
> + if (!mrenclave_update(ctx, &mreextend))
> + return false;
> +
> + if (!mrenclave_update(ctx, &data[i + 0x00]))
> + return false;
> +
> + if (!mrenclave_update(ctx, &data[i + 0x40]))
> + return false;
> +
> + if (!mrenclave_update(ctx, &data[i + 0x80]))
> + return false;
> +
> + if (!mrenclave_update(ctx, &data[i + 0xC0]))
> + return false;
> + }
> +
> + return true;
> +}
> +
> +/**
> + * measure_encl - measure enclave
> + * @path: path to the enclave
> + * @mrenclave: measurement
> + *
> + * Calculates MRENCLAVE. Assumes that the very first page is a TCS page and
> + * following pages are regular pages. Does not measure the contents of the
> + * enclave as the signing tool is used at the moment only for the launch
> + * enclave, which is pass-through (everything gets a token).
> + */
> +static bool measure_encl(const char *path, uint8_t *mrenclave)
> +{
> + FILE *file;
> + struct stat sb;
> + EVP_MD_CTX *ctx;
> + uint64_t flags;
> + uint64_t offset;
> + uint8_t data[0x1000];
> + int rc;
> +
> + ctx = EVP_MD_CTX_create();
> + if (!ctx)
> + return false;
> +
> + file = fopen(path, "rb");
> + if (!file) {
> + perror("fopen");
> + EVP_MD_CTX_destroy(ctx);
> + return false;
> + }
> +
> + rc = stat(path, &sb);
> + if (rc) {
> + perror("stat");
> + goto out;
> + }
> +
> + if (!sb.st_size || sb.st_size & 0xfff) {
> + fprintf(stderr, "Invalid blob size %lu\n", sb.st_size);
> + goto out;
> + }
> +
> + if (!mrenclave_ecreate(ctx, sb.st_size))
> + goto out;
> +
> + for (offset = 0; offset < sb.st_size; offset += 0x1000) {
> + if (!offset)
> + flags = SGX_SECINFO_TCS;
> + else
> + flags = SGX_SECINFO_REG | SGX_SECINFO_R |
> + SGX_SECINFO_W | SGX_SECINFO_X;
> +
> + if (!mrenclave_eadd(ctx, offset, flags))
> + goto out;
> +
> + rc = fread(data, 1, 0x1000, file);
> + if (!rc)
> + break;
> + if (rc < 0x1000)
> + goto out;
> +
> + if (!mrenclave_eextend(ctx, offset, data))
> + goto out;
> + }
> +
> + if (!mrenclave_commit(ctx, mrenclave))
> + goto out;
> +
> + fclose(file);
> + EVP_MD_CTX_destroy(ctx);
> + return true;
> +out:
> + fclose(file);
> + EVP_MD_CTX_destroy(ctx);
> + return false;
> +}
> +
> +/**
> + * sign_encl - sign enclave
> + * @sigstruct: pointer to SIGSTRUCT
> + * @key: 3072-bit RSA key
> + * @signature: byte array for the signature
> + *
> + * Calculates EMSA-PKCSv1.5 signature for the given SIGSTRUCT. The result is
> + * stored in big-endian format so that it can be further passed to OpenSSL
> + * libcrypto functions.
> + */
> +static bool sign_encl(const struct sgx_sigstruct *sigstruct, RSA *key,
> + uint8_t *signature)
> +{
> + struct sgx_sigstruct_payload payload;
> + unsigned int siglen;
> + uint8_t digest[SHA256_DIGEST_LENGTH];
> + bool ret;
> +
> + memcpy(&payload.header, &sigstruct->header, sizeof(sigstruct->header));
> + memcpy(&payload.body, &sigstruct->body, sizeof(sigstruct->body));
> +
> + SHA256((unsigned char *)&payload, sizeof(payload), digest);
> +
> + ret = RSA_sign(NID_sha256, digest, SHA256_DIGEST_LENGTH, signature,
> + &siglen, key);
> +
> + return ret;
> +}
> +
> +struct q1q2_ctx {
> + BN_CTX *bn_ctx;
> + BIGNUM *m;
> + BIGNUM *s;
> + BIGNUM *q1;
> + BIGNUM *qr;
> + BIGNUM *q2;
> +};
> +
> +static void free_q1q2_ctx(struct q1q2_ctx *ctx)
> +{
> + BN_CTX_free(ctx->bn_ctx);
> + BN_free(ctx->m);
> + BN_free(ctx->s);
> + BN_free(ctx->q1);
> + BN_free(ctx->qr);
> + BN_free(ctx->q2);
> +}
> +
> +static bool alloc_q1q2_ctx(const uint8_t *s, const uint8_t *m,
> + struct q1q2_ctx *ctx)
> +{
> + ctx->bn_ctx = BN_CTX_new();
> + ctx->s = BN_bin2bn(s, SGX_MODULUS_SIZE, NULL);
> + ctx->m = BN_bin2bn(m, SGX_MODULUS_SIZE, NULL);
> + ctx->q1 = BN_new();
> + ctx->qr = BN_new();
> + ctx->q2 = BN_new();
> +
> + if (!ctx->bn_ctx || !ctx->s || !ctx->m || !ctx->q1 || !ctx->qr ||
> + !ctx->q2) {
> + free_q1q2_ctx(ctx);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static bool calc_q1q2(const uint8_t *s, const uint8_t *m, uint8_t *q1,
> + uint8_t *q2)
> +{
> + struct q1q2_ctx ctx;
> +
> + if (!alloc_q1q2_ctx(s, m, &ctx)) {
> + fprintf(stderr, "Not enough memory for Q1Q2 calculation\n");
> + return false;
> + }
> +
> + if (!BN_mul(ctx.q1, ctx.s, ctx.s, ctx.bn_ctx))
> + goto out;
> +
> + if (!BN_div(ctx.q1, ctx.qr, ctx.q1, ctx.m, ctx.bn_ctx))
> + goto out;
> +
> + if (BN_num_bytes(ctx.q1) > SGX_MODULUS_SIZE) {
> + fprintf(stderr, "Too large Q1 %d bytes\n",
> + BN_num_bytes(ctx.q1));
> + goto out;
> + }
> +
> + if (!BN_mul(ctx.q2, ctx.s, ctx.qr, ctx.bn_ctx))
> + goto out;
> +
> + if (!BN_div(ctx.q2, NULL, ctx.q2, ctx.m, ctx.bn_ctx))
> + goto out;
> +
> + if (BN_num_bytes(ctx.q2) > SGX_MODULUS_SIZE) {
> + fprintf(stderr, "Too large Q2 %d bytes\n",
> + BN_num_bytes(ctx.q2));
> + goto out;
> + }
> +
> + BN_bn2bin(ctx.q1, q1);
> + BN_bn2bin(ctx.q2, q2);
> +
> + free_q1q2_ctx(&ctx);
> + return true;
> +out:
> + free_q1q2_ctx(&ctx);
> + return false;
> +}
> +
> +static bool save_sigstruct(const struct sgx_sigstruct *sigstruct,
> + const char *path)
> +{
> + FILE *f = fopen(path, "wb");
> +
> + if (!f) {
> + fprintf(stderr, "Unable to open %s\n", path);
> + return false;
> + }
> +
> + fwrite(sigstruct, sizeof(*sigstruct), 1, f);
> + fclose(f);
> + return true;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + uint64_t header1[2] = {0x000000E100000006, 0x0000000000010000};
> + uint64_t header2[2] = {0x0000006000000101, 0x0000000100000060};
> + struct sgx_sigstruct ss;
> + const char *program;
> + int opt;
> + RSA *sign_key;
> +
> + memset(&ss, 0, sizeof(ss));
> + ss.header.header1[0] = header1[0];
> + ss.header.header1[1] = header1[1];
> + ss.header.header2[0] = header2[0];
> + ss.header.header2[1] = header2[1];
> + ss.exponent = 3;
> +
> +#ifndef CONFIG_EINITTOKENKEY
> + ss.body.attributes = SGX_ATTR_MODE64BIT;
> +#else
> + ss.body.attributes = SGX_ATTR_MODE64BIT | SGX_ATTR_EINITTOKENKEY;
> +#endif
> + ss.body.xfrm = 3,
> +
> + program = argv[0];
> +
> + do {
> + opt = getopt(argc, argv, "");
> + switch (opt) {
> + case -1:
> + break;
> + default:
> + exit_usage(program);
> + }
> + } while (opt != -1);
> +
> + argc -= optind;
> + argv += optind;
> +
> + if (argc < 3)
> + exit_usage(program);
> +
> + /* sanity check only */
> + if (check_crypto_errors())
> + exit(1);
> +
> + sign_key = load_sign_key(argv[0]);
> + if (!sign_key)
> + goto out;
> +
> + BN_bn2bin(get_modulus(sign_key), ss.modulus);
> +
> + if (!measure_encl(argv[1], ss.body.mrenclave))
> + goto out;
> +
> + if (!sign_encl(&ss, sign_key, ss.signature))
> + goto out;
> +
> + if (!calc_q1q2(ss.signature, ss.modulus, ss.q1, ss.q2))
> + goto out;
> +
> + /* convert to little endian */
> + reverse_bytes(ss.signature, SGX_MODULUS_SIZE);
> + reverse_bytes(ss.modulus, SGX_MODULUS_SIZE);
> + reverse_bytes(ss.q1, SGX_MODULUS_SIZE);
> + reverse_bytes(ss.q2, SGX_MODULUS_SIZE);
> +
> + if (!save_sigstruct(&ss, argv[2]))
> + goto out;
> + exit(0);
> +out:
> + check_crypto_errors();
> + exit(1);
> +}
> diff --git a/tools/testing/selftests/x86/sgx/signing_key.pem b/tools/testing/selftests/x86/sgx/signing_key.pem
> new file mode 100644
> index 000000000000..d76f21f19187
> --- /dev/null
> +++ b/tools/testing/selftests/x86/sgx/signing_key.pem
> @@ -0,0 +1,39 @@
> +-----BEGIN RSA PRIVATE KEY-----
> +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
> +-----END RSA PRIVATE KEY-----
> --
> 2.25.0
>