diff mbox series

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

Message ID 20200303233609.713348-15-jarkko.sakkinen@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series Intel SGX foundations | expand

Commit Message

Jarkko Sakkinen March 3, 2020, 11:36 p.m. UTC
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@vger.kernel.org
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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

Comments

Nathaniel McCallum March 4, 2020, 7:27 p.m. UTC | #1
On Tue, Mar 3, 2020 at 6:39 PM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> 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@vger.kernel.org
> Cc: linux-kselftest@vger.kernel.org
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> ---
>  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-----
> +MIIG4wIBAAKCAYEApalGbq7Q+usM91CPtksu3D+b0Prc8gAFL6grM3mg85A5Bx8V
> +cfMXPgtrw8EYFwQxDAvzZWwl+9VfOX0ECrFRBkOHcOiG0SnADN8+FLj1UiNUQwbp
> +S6OzhNWuRcSbGraSOyUlVlV0yMQSvewyzGklOaXBe30AJqzIBc8QfdSxKuP8rs0Z
> +ga6k/Bl73osrYKByILJTUUeZqjLERsE6GebsdzbWgKn8qVqng4ZS4yMNg6LeRlH3
> ++9CIPgg4jwpSLHcp7dq2qTIB9a0tGe9ayp+5FbucpB6U7ePold0EeRN6RlJGDF9k
> +L93v8P5ykz5G5gYZ2g0K1X2sHIWV4huxPgv5PXgdyQYbK+6olqj0d5rjYuwX57Ul
> +k6SroPS1U6UbdCjG5txM+BNGU0VpD0ZhrIRw0leQdnNcCO9sTJuInZrgYacSVJ7u
> +mtB+uCt+uzUesc+l+xPRYA+9e14lLkZp7AAmo9FvL816XDI09deehJ3i/LmHKCRN
> +tuqC5TprRjFwUr6dAgEDAoIBgG5w2Z8fNfycs0+LCnmHdJLVEotR6KFVWMpwHMz7
> +wKJgJgS/Y6FMuilc8oKAuroCy11dTO5IGVKOP3uorVx2NgQtBPXwWeDGgAiU1A3Q
> +o4wXjYIEm4fCd63jyYPYZ2ckYXzDbjmOTdstYdPyzIhGGNEZK6eoqsRzMAPfYFPj
> +IMdCqHSIu6vJw1K7p+myHOsVoWshjODaZnF3LYSA0WaZ8vokjwBxUxuRxQJZjJds
> +s60XPtmL+qfgWtQFewoG4XL6GuD8FcXccynRRtzrLtFNPIl9BQfWfjBBhTC1/Te1
> +0Z6XbZvpdUTD9OfLB7SbR2OUFNpKQgriO0iYVdbW3cr7uu38Zwp4W1TX73DPjoi6
> +KNooP6SGWd4mRJW2+dUmSYS4QNG8eVVZswKcploEIXlAKRsOe4kzJJ1iETugIe85
> +uX8nd1WYEp65xwoRUg8hqng0MeyveVbXqNKuJG6tzNDt9kgFYo+hmC/oouAW2Dtc
> +T9jdRAwKJXqA2Eg6OkgXCEv+kwKBwQDYaQiFMlFhsmLlqI+EzCUh7c941/cL7m6U
> +7j98+8ngl0HgCEcrc10iJVCKakQW3YbPzAx3XkKTaGjWazvvrFarXIGlOud64B8a
> +iWyQ7VdlnmZnNEdk+C83tI91OQeaTKqRLDGzKh29Ry/jL8Pcbazt+kDgxa0H7qJp
> +roADUanLQuNkYubpbhFBh3xpa2EExaVq6rF7nIVsD8W9TrbmPKA4LgH7z0iy544D
> +kVCNYsTjYDdUWP+WiSor8kCnnpjnN9sCgcEAw/eNezUD1UDf6OYFC9+5JZJFn4Tg
> +mZMyN93JKIb199ffwnjtHUSjcyiWeesXucpzwtGbTcwQnDisSW4oneYKLSEBlBaq
> +scqiUugyGZZOthFSCbdXYXMViK2vHrKlkse7GxVlROKcEhM/pRBrmjaGO8eWR+D4
> +FO2wCXzVs3KgV6j779frw0vC54oHOxc9+Lu1rSHp4i+600koyvL/zF6U/5tZXIvN
> +YW2yoiQJnjCmVA1pwbwV6KAUTPDTMnBK+YjnAoHBAJBGBa4hi5Z27JkbCliIGMFJ
> +NPs6pLKe9GNJf6in2+sPgUAFhMeiPhbDiwbxgrnpBIqICE+ULGJFmzmc0p/IOceT
> +ARjR76dAFLxbnbXzj5kURETNhO36yiUjCk4mBRGIcbYddndxaSjaH+zKgpLzyJ6m
> +1esuc1qfFvEfAAI2cTIsl5hB70ZJYNZaUvDyQK3ZGPHxy6e9rkgKg9OJz0QoatAe
> +q/002yHvtAJg4F5B2JeVejg7VQ8GHB1MKxppu0TP5wKBwQCCpQj8zgKOKz/wmViy
> +lSYZDC5qWJW7t3bP6TDFr06lOpUsUJ4TgxeiGw778g/RMaKB4RIz3WBoJcgw9BsT
> +7rFza1ZiucchMcGMmswRDt8kC4wGejpA92Owc8oUdxkMhSdnY5jYlxK2t3/DYEe8
> +JFl9L7mFQKVjSSAGUzkiTGrlG1Kf5UfXh9dFBq98uilQfSPIwUaWynyM23CHTKqI
> +Pw3/vOY9sojrnncWwrEUIG7is5vWfWPwargzSzd29YdRBe8CgcEAuRVewK/YeNOX
> +B7ZG6gKKsfsvrGtY7FPETzLZAHjoVXYNea4LVZ2kn4hBXXlvw/4HD+YqcTt4wmif
> +5JQlDvjNobUiKJZpzy7hklVhF7wZFl4pCF7Yh43q9iQ7gKTaeUG7MiaK+G8Zz8aY
> +HW9rsiihbdZkccMvnPfO9334XMxl3HtBRzLstjUlbLB7Sdh+7tZ3JQidCOFNs5pE
> +XyWwnASPu4tKfDahH1UUTp1uJcq/6716CSWg080avYxFcn75qqsb
> +-----END RSA PRIVATE KEY-----
> --
> 2.25.0
>
Nathaniel McCallum March 4, 2020, 7:44 p.m. UTC | #2
On Tue, Mar 3, 2020 at 6:39 PM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> 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@vger.kernel.org
> Cc: linux-kselftest@vger.kernel.org
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> ---
>  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

# Reset status flags.
add     %rdx, %rdx # OF = SF = AF = CF = 0; ZF = PF = 1
cld # DF = 0

This was a recent CVE on the SDK. We don't need more people copying
vulnerable code into their own projects.

Likewise, you should also clear GPRs, reset extended GPU state and
reset flags before calling encl_body().

Alternatively, drop all the state clearing and simply put a comment in
for people to do that. My general rule here is to either not provide
an example at all or provide a secure example that people can copy.

> +       # 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
> +
> +       .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
>
Nathaniel McCallum March 4, 2020, 7:51 p.m. UTC | #3
On Tue, Mar 3, 2020 at 6:39 PM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
> 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

You need to push and pop all the callee-saved registers here since the
enclave zeros them. This code works today by accident. A future
compiler may emit different register allocation which will cause this
to break.

We might consider making it part of the Linux enclave ABI that the
enclave has to save and restore these registers. This would have a
slight performance advantage in a critical code-path compared to
zeroing and then restoring them. But the VDSO code will need to know
what the expectation is.
Jarkko Sakkinen March 5, 2020, 11:33 a.m. UTC | #4
On Wed, 2020-03-04 at 14:27 -0500, Nathaniel McCallum wrote:
> > > +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:

And I always aim to get things more readable. Thank you.

> 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.

It was meant as a test program but I'd guess what you say is true
because it also might be the only alternative user space to Intel's
:-) And a great starting point if you want to do things from scratch.

Because I meant it as a smoke test program for SGX, not everything is
too well documented but given the multipurpose use for that code I'll
make the improvements that you are suggesting.

/Jarkko
Dr. Greg March 6, 2020, 5:32 a.m. UTC | #5
On Wed, Mar 04, 2020 at 01:36:01AM +0200, Jarkko Sakkinen wrote:

Good evening, I hope the end of the week is going well for everyone.

> 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.

Just as a clarification, are you testing the new driver against signed
production class enclaves in .so format that also include metadata
layout directives or is the driver just getting tested against the two
page toy enclave that copies a word of memory from one memory location
to another?

Our PSW/runtime is currently failing to initialize production class
enclaves secondary to a return value of -4 from the ENCLU[EINIT]
instruction, which means the measurement of the loaded enclave has
failed to match the value in the signature structure.

The same enclave loads fine with the out of kernel driver.  Our
diagnostics tell us we are feeding identical page streams and
permissions to the page add ioctl's of both drivers.  The identity
modulus signature of the signing key for the enclave is being written
to the launch control registers.

We see the same behavior from both our unit test enclaves and the
Quoting Enclave from the Intel SGX runtime.

When we ported our runtime loader to the new driver ABI we kept things
simple and add only a single page at a time in order to replicate the
behavior of the old driver.

Secondly, we were wondering what distribution you are building the
self-tests with?  Initial indications are that the selftest signing
utility doesn't build properly with OpenSSL 1.1.1.

Have a good day.

Dr. Greg

As always,
Dr. Greg Wettstein, Ph.D, Worker
IDfusion, LLC               SGX secured infrastructure and
4206 N. 19th Ave.           autonomously self-defensive platforms.
Fargo, ND  58102
PH: 701-281-1686            EMAIL: greg@idfusion.net
------------------------------------------------------------------------------
"Don't worry about people stealing your ideas.  If your ideas are any
 good, you'll have to ram them down people's throats."
                                -- Howard Aiken
Dr. Greg March 6, 2020, 3:42 p.m. UTC | #6
On Thu, Mar 05, 2020 at 01:33:28PM +0200, Jarkko Sakkinen wrote:

Good morning, I hope the end of the week is going well for everyone.

> On Wed, 2020-03-04 at 14:27 -0500, Nathaniel McCallum wrote:
> >         # 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.

> It was meant as a test program but I'd guess what you say is true
> because it also might be the only alternative user space to Intel's
> :-) And a great starting point if you want to do things from
> scratch.
>
> Because I meant it as a smoke test program for SGX, not everything
> is too well documented but given the multipurpose use for that code
> I'll make the improvements that you are suggesting.

At the risk of what will certainly be a fair amount of criticism, I
will take on the moniker of being the pernicious voice of reality, if
not intellectual honesty, in all of this.  No market or security
relevant enclaves are going to get built by developers starting from
scratch or copying this code, useful and informative as it might be,
into their enclaves.

That isn't to say that it isn't good to have some example code but
Nate's point in a previous e-mail is well taken, it shouldn't have
known security vulnerabilities in it.  Given the current realities of
speculative execution attacks, there are a ton of subtle issues
surrounding entry and exit into enclaves, which by definition is the
primary attack surface for a trusted execution environment.

For the sake of those reading along at home, relevant enclave
development needs, at an absolute minimum, the following:

1.) A lot of trusted runtime initialization and scaffolding code.
2.) An embedded C/C++ library.
3.) A compiler intrinsics implementation.

That gets you, maybe, something that you can start thinking about,
'hello world', with, but nothing useful with respect to what anyone
would want to do with an enclave.

On top of that you need a lot of platform software to get the enclave
relevantly signed, loaded, executed and supported.  Not the least of
which is support for remote attestation, which means rolling up one's
sleeves to do either a DCAP implementation or an EPID provisioning
implementation.  The latter of which, believe me, is not for the faint
of heart given that you have to develop it in an unknown oracle model.

My point in all of this is that the only relevant consumers of this
driver are groups that are resourced well enough to understand,
deliver and support all of this infrastructure.  I will leave it to
others, and history, to judge whether or not the driver has been
developed with this frame of reference.

> /Jarkko

Best wishes for a pleasant weekend.

Dr. Greg

As always,
Dr. Greg Wettstein, Ph.D.   Enjellic Systems Development, LLC.
4206 N. 19th Ave.           SGX secured infrastructure and autonomously
Fargo, ND  58102            self-defensive platforms.
PH: 701-281-1686            EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"The couple is registered at Herbergers, Target and Fleet Farm."
                                -- Wedding invitation
                                   West Central Minnesota
Jarkko Sakkinen March 6, 2020, 7:04 p.m. UTC | #7
On Thu, Mar 05, 2020 at 11:32:10PM -0600, Dr. Greg wrote:
> On Wed, Mar 04, 2020 at 01:36:01AM +0200, Jarkko Sakkinen wrote:
> 
> Good evening, I hope the end of the week is going well for everyone.
> 
> > 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.
> 
> Just as a clarification, are you testing the new driver against signed
> production class enclaves in .so format that also include metadata
> layout directives or is the driver just getting tested against the two
> page toy enclave that copies a word of memory from one memory location
> to another?

That is the kind of role kselftests to smoke stuff. Obviously it will
be refined over time but to do a "hello world" from scratch as an
enclave was already quite a big effort.

> Our PSW/runtime is currently failing to initialize production class
> enclaves secondary to a return value of -4 from the ENCLU[EINIT]
> instruction, which means the measurement of the loaded enclave has
> failed to match the value in the signature structure.
> 
> The same enclave loads fine with the out of kernel driver.  Our
> diagnostics tell us we are feeding identical page streams and
> permissions to the page add ioctl's of both drivers.  The identity
> modulus signature of the signing key for the enclave is being written
> to the launch control registers.
> 
> We see the same behavior from both our unit test enclaves and the
> Quoting Enclave from the Intel SGX runtime.
> 
> When we ported our runtime loader to the new driver ABI we kept things
> simple and add only a single page at a time in order to replicate the
> behavior of the old driver.
> 
> Secondly, we were wondering what distribution you are building the
> self-tests with?  Initial indications are that the selftest signing
> utility doesn't build properly with OpenSSL 1.1.1.

I don't use a distribution. I just build user space with BuildRoot
when I test a kernel.

Do you have a build log available to look at?

/Jarkko
Jarkko Sakkinen March 6, 2020, 7:07 p.m. UTC | #8
On Fri, Mar 06, 2020 at 09:42:22AM -0600, Dr. Greg wrote:
> On Thu, Mar 05, 2020 at 01:33:28PM +0200, Jarkko Sakkinen wrote:
> 
> Good morning, I hope the end of the week is going well for everyone.
> 
> > On Wed, 2020-03-04 at 14:27 -0500, Nathaniel McCallum wrote:
> > >         # 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.
> 
> > It was meant as a test program but I'd guess what you say is true
> > because it also might be the only alternative user space to Intel's
> > :-) And a great starting point if you want to do things from
> > scratch.
> >
> > Because I meant it as a smoke test program for SGX, not everything
> > is too well documented but given the multipurpose use for that code
> > I'll make the improvements that you are suggesting.
> 
> At the risk of what will certainly be a fair amount of criticism, I
> will take on the moniker of being the pernicious voice of reality, if
> not intellectual honesty, in all of this.  No market or security
> relevant enclaves are going to get built by developers starting from
> scratch or copying this code, useful and informative as it might be,
> into their enclaves.
> 
> That isn't to say that it isn't good to have some example code but
> Nate's point in a previous e-mail is well taken, it shouldn't have
> known security vulnerabilities in it.  Given the current realities of
> speculative execution attacks, there are a ton of subtle issues
> surrounding entry and exit into enclaves, which by definition is the
> primary attack surface for a trusted execution environment.
> 
> For the sake of those reading along at home, relevant enclave
> development needs, at an absolute minimum, the following:
> 
> 1.) A lot of trusted runtime initialization and scaffolding code.
> 2.) An embedded C/C++ library.
> 3.) A compiler intrinsics implementation.
> 
> That gets you, maybe, something that you can start thinking about,
> 'hello world', with, but nothing useful with respect to what anyone
> would want to do with an enclave.

Actually many people have applaused to have a small scoped, even if not
perfect, test program to look at how SGX works. One that is only
dependent on glibc. None of the selftests are meant to be production
peaces of code. You are getting wrong the role of the selftest in the
first place.

/Jarkko
Dr. Greg March 7, 2020, 5:42 p.m. UTC | #9
On Fri, Mar 06, 2020 at 09:07:53PM +0200, Jarkko Sakkinen wrote:

Good morning, I hope the weekend is going well for everyone.

> Actually many people have applaused to have a small scoped, even if
> not perfect, test program to look at how SGX works. One that is only
> dependent on glibc. None of the selftests are meant to be production
> peaces of code. You are getting wrong the role of the selftest in
> the first place.

We certainly want to be counted in the camp of those who are
applausing you for making the selftests available, particularly the
new VDSO setup and entry code.

We arguably have similar motivations.  We architected and authored an
entire SGX runtime that has as its only dependencies the MUSL C
library, libelf and OpenSSL, primarily because we needed an easily
auditable and low footprint SGX implementation.

To the point at hand though, I'm certainly not a very smart guy so I
doubt that I am able to understand the role of the selftests.  We do
seem to agree though that they only provide a rudimentary exercise of
the driver.

We also seem to agree that the primary role of the driver is to
service the needs of those of us that are building production level
SGX runtime stacks.  In service of that premise, it would be helpful
to know if you are internally testing the driver/VDSO against enclaves
of production quality, with metadata, or just the two page selftest
enclave.

Since Intel signs and releases binary enclaves, (QE, LE, PCE), it
would seem that a load and initialization of these enclaves would
provide a good reference point for all of us to agree on with respect
to basic driver testing and validation.

> /Jarkko

Have a good remainder of the weekend.

Dr. Greg

As always,
Dr. G.W. Wettstein, Ph.D.   Enjellic Systems Development, LLC.
4206 N. 19th Ave.           SGX secured infrastructure and
Fargo, ND  58102            autonomously self-defensive platforms.
PH: 701-281-1686            EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"... remember that innovation is saying 'no' to 1000 things."
                                -- Moxie Marlinspike
Jarkko Sakkinen March 10, 2020, 1:08 p.m. UTC | #10
On Sat, Mar 07, 2020 at 11:42:23AM -0600, Dr. Greg wrote:
> On Fri, Mar 06, 2020 at 09:07:53PM +0200, Jarkko Sakkinen wrote:
> 
> Good morning, I hope the weekend is going well for everyone.
> 
> > Actually many people have applaused to have a small scoped, even if
> > not perfect, test program to look at how SGX works. One that is only
> > dependent on glibc. None of the selftests are meant to be production
> > peaces of code. You are getting wrong the role of the selftest in
> > the first place.
> 
> We certainly want to be counted in the camp of those who are
> applausing you for making the selftests available, particularly the
> new VDSO setup and entry code.
> 
> We arguably have similar motivations.  We architected and authored an
> entire SGX runtime that has as its only dependencies the MUSL C
> library, libelf and OpenSSL, primarily because we needed an easily
> auditable and low footprint SGX implementation.

Good to hear!

> To the point at hand though, I'm certainly not a very smart guy so I
> doubt that I am able to understand the role of the selftests.  We do
> seem to agree though that they only provide a rudimentary exercise of
> the driver.

The role of kselftests is not to be production code. They are somewhat
adhoc pieces of code that just check that "things turn on" e.g. in a
new kernel release or a new hardware platform.

> We also seem to agree that the primary role of the driver is to
> service the needs of those of us that are building production level
> SGX runtime stacks.  In service of that premise, it would be helpful
> to know if you are internally testing the driver/VDSO against enclaves
> of production quality, with metadata, or just the two page selftest
> enclave.

I do agree that a more complete test suite would be an essential thing
to have. In that I'd just use the SDK and implement it outside the
kernel tree.

Unfortunately I do not have time to implement such.

/Jarkko
Haitao Huang March 10, 2020, 7:29 p.m. UTC | #11
On Thu, 05 Mar 2020 23:32:10 -0600, Dr. Greg <greg@enjellic.com> wrote:

> On Wed, Mar 04, 2020 at 01:36:01AM +0200, Jarkko Sakkinen wrote:
>
> Good evening, I hope the end of the week is going well for everyone.
>
>> 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.
>
> Just as a clarification, are you testing the new driver against signed
> production class enclaves in .so format that also include metadata
> layout directives or is the driver just getting tested against the two
> page toy enclave that copies a word of memory from one memory location
> to another?
>

We (Intel SGX SDK/PSW team) tested this driver for enclaves in .so format  
with metadata. Our 2.8 release supports v24 and 2.9 supports v25+. Both  
production signed and debug signed enclaves worked.

*Note* we did make some code changes in our runtime for v24+, mainly  
dealing with src & EPC page alignment for EADD, open one fd per enclave,  
use -z noexecstack linker option, etc. You can see the changes on GitHub.

> Our PSW/runtime is currently failing to initialize production class
> enclaves secondary to a return value of -4 from the ENCLU[EINIT]
> instruction, which means the measurement of the loaded enclave has
> failed to match the value in the signature structure.
>
> The same enclave loads fine with the out of kernel driver.  Our
> diagnostics tell us we are feeding identical page streams and
> permissions to the page add ioctl's of both drivers.  The identity
> modulus signature of the signing key for the enclave is being written
> to the launch control registers.
>
> We see the same behavior from both our unit test enclaves and the
> Quoting Enclave from the Intel SGX runtime.
>
We did not see any issue loading QE in our tests. Please directly email me  
on this test if you have specific questions.

> When we ported our runtime loader to the new driver ABI we kept things
> simple and add only a single page at a time in order to replicate the
> behavior of the old driver.
>
> Secondly, we were wondering what distribution you are building the
> self-tests with?  Initial indications are that the selftest signing
> utility doesn't build properly with OpenSSL 1.1.1.
>
> Have a good day.
>
> Dr. Greg
>
> As always,
> Dr. Greg Wettstein, Ph.D, Worker
> IDfusion, LLC               SGX secured infrastructure and
> 4206 N. 19th Ave.           autonomously self-defensive platforms.
> Fargo, ND  58102
> PH: 701-281-1686            EMAIL: greg@idfusion.net
> ------------------------------------------------------------------------------
> "Don't worry about people stealing your ideas.  If your ideas are any
>  good, you'll have to ram them down people's throats."
>                                 -- Howard Aiken
Dr. Greg March 11, 2020, 9:13 a.m. UTC | #12
On Tue, Mar 10, 2020 at 02:29:41PM -0500, Haitao Huang wrote:

Good morning, I hope this note finds the week going well for everyone.

> On Thu, 05 Mar 2020 23:32:10 -0600, Dr. Greg <greg@enjellic.com> wrote:
> 
> >On Wed, Mar 04, 2020 at 01:36:01AM +0200, Jarkko Sakkinen wrote:
> >
> >Good evening, I hope the end of the week is going well for everyone.
> >
> >>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.
> >
> >Just as a clarification, are you testing the new driver against signed
> >production class enclaves in .so format that also include metadata
> >layout directives or is the driver just getting tested against the two
> >page toy enclave that copies a word of memory from one memory location
> >to another?

> We (Intel SGX SDK/PSW team) tested this driver for enclaves in .so
> format with metadata. Our 2.8 release supports v24 and 2.9 supports
> v25+. Both production signed and debug signed enclaves worked.

Very good, this was the feedback we were hoping to get.

> *Note* we did make some code changes in our runtime for v24+, mainly
> dealing with src & EPC page alignment for EADD, open one fd per
> enclave, use -z noexecstack linker option, etc. You can see the
> changes on GitHub.

Yes, we made all of those changes as well, in a similar fashion.

This was the third time that we have changed the enclave creation
mmap() and alignment constraints, only to take us back to what we had
started with... :-)(

> >Our PSW/runtime is currently failing to initialize production class
> >enclaves secondary to a return value of -4 from the ENCLU[EINIT]
> >instruction, which means the measurement of the loaded enclave has
> >failed to match the value in the signature structure.
> >
> >The same enclave loads fine with the out of kernel driver.  Our
> >diagnostics tell us we are feeding identical page streams and
> >permissions to the page add ioctl's of both drivers.  The identity
> >modulus signature of the signing key for the enclave is being written
> >to the launch control registers.
> >
> >We see the same behavior from both our unit test enclaves and the
> >Quoting Enclave from the Intel SGX runtime.

> We did not see any issue loading QE in our tests. Please directly
> email me on this test if you have specific questions.

It isn't anything specific to the QE, we just used that as an example,
since it has no special attribute requirements and would seem to be a
good reference test for everyone working on runtimes.

We are missing some nuance with the latest version of the driver, our
runtime was working fine with the new driver a year ago.

It almost has to be a problem with metadata, we are suspicious of
guard page processing.

We are following the same strategy that we had used previously and
leaving a 'hole' in the enclave for each guard page at its RVA offset.
The comment in the add page ioctl indicating that the page range has
to be contiguous caught our eye and has us questioning if this
continues to be the correct approach.

Do you remember, off the top of your head, having to address guard
pages differently?

Have a good remainder of the week.

Dr. Greg

> Using Opera's mail client: http://www.opera.com/mail/

As always,
Dr. Greg Wettstein, Ph.D, Worker
IDfusion, LLC               SGX secured infrastructure and
4206 N. 19th Ave.           autonomously self-defensive platforms.
Fargo, ND  58102
PH: 701-281-1686            EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"Work makes life enjoyable."
                                -- North Dakota Germans
Jarkko Sakkinen March 11, 2020, 1:28 p.m. UTC | #13
On Thu, Mar 05, 2020 at 01:33:28PM +0200, Jarkko Sakkinen wrote:
> On Wed, 2020-03-04 at 14:27 -0500, Nathaniel McCallum wrote:
> > > > +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:
> 
> And I always aim to get things more readable. Thank you.
> 
> > 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.
> 
> It was meant as a test program but I'd guess what you say is true
> because it also might be the only alternative user space to Intel's
> :-) And a great starting point if you want to do things from scratch.
> 
> Because I meant it as a smoke test program for SGX, not everything is
> too well documented but given the multipurpose use for that code I'll
> make the improvements that you are suggesting.

For FPU Control World (FCW), I think 0x037F is not the right value even
if section 18.5 in the x86 SDM says that it is the initial value for it.

I took that value from that section.

The reason I think that there is an error in the SDM is that if you look
at the section 8.1.5, you'll see that bit 6 is a reserved bit. Thus,
does not make to set it on.

I think the legit value ought to be 0x33F i.e. unset bit 6.

In any case check:

https://raw.githubusercontent.com/jsakkine-intel/linux-sgx/master/tools/testing/selftests/x86/sgx/encl_bootstrap.S

I.e. both have now a reference:

1. To the section that describes the default.
2. To the section that describes what the bits mean.

/Jarkko
Sean Christopherson March 11, 2020, 4:40 p.m. UTC | #14
On Wed, Mar 11, 2020 at 03:28:32PM +0200, Jarkko Sakkinen wrote:
> On Thu, Mar 05, 2020 at 01:33:28PM +0200, Jarkko Sakkinen wrote:
> > On Wed, 2020-03-04 at 14:27 -0500, Nathaniel McCallum wrote:
> > > > > +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:
> > 
> > And I always aim to get things more readable. Thank you.
> > 
> > > 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.
> > 
> > It was meant as a test program but I'd guess what you say is true
> > because it also might be the only alternative user space to Intel's
> > :-) And a great starting point if you want to do things from scratch.
> > 
> > Because I meant it as a smoke test program for SGX, not everything is
> > too well documented but given the multipurpose use for that code I'll
> > make the improvements that you are suggesting.
> 
> For FPU Control World (FCW), I think 0x037F is not the right value even
> if section 18.5 in the x86 SDM says that it is the initial value for it.
> 
> I took that value from that section.
> 
> The reason I think that there is an error in the SDM is that if you look
> at the section 8.1.5, you'll see that bit 6 is a reserved bit. Thus,
> does not make to set it on.
> 
> I think the legit value ought to be 0x33F i.e. unset bit 6.

Bit 6 is reserved, but it's forced to '1' by the CPU.

Regardless, IMO it'd be better to drop this code entirely, it's all kinds
of wonky.  The label says "xsave_area" and implies XSAVE state is being
loaded, but the code uses FXRSTOR, which will only load x86/MMX/XMM state,
i.e. the first 512 bytes of the so called xsave_area.

The test enclave doesn't touch state managed by XSAVE, let alone put
secrets into said state.  I see no reason to bother purging anything.

> In any case check:
> 
> https://raw.githubusercontent.com/jsakkine-intel/linux-sgx/master/tools/testing/selftests/x86/sgx/encl_bootstrap.S
> 
> I.e. both have now a reference:
> 
> 1. To the section that describes the default.
> 2. To the section that describes what the bits mean.
> 
> /Jarkko
Haitao Huang March 11, 2020, 5:15 p.m. UTC | #15
On Wed, 11 Mar 2020 04:13:13 -0500, Dr. Greg <greg@enjellic.com> wrote:

...
>
> Do you remember, off the top of your head, having to address guard
> pages differently?
>
No, we did not make changes specific to guard pages.

Haitao
Jarkko Sakkinen March 13, 2020, 7:24 p.m. UTC | #16
On Wed, Mar 11, 2020 at 09:40:47AM -0700, Sean Christopherson wrote:
> On Wed, Mar 11, 2020 at 03:28:32PM +0200, Jarkko Sakkinen wrote:
> > On Thu, Mar 05, 2020 at 01:33:28PM +0200, Jarkko Sakkinen wrote:
> > > On Wed, 2020-03-04 at 14:27 -0500, Nathaniel McCallum wrote:
> > > > > > +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:
> > > 
> > > And I always aim to get things more readable. Thank you.
> > > 
> > > > 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.
> > > 
> > > It was meant as a test program but I'd guess what you say is true
> > > because it also might be the only alternative user space to Intel's
> > > :-) And a great starting point if you want to do things from scratch.
> > > 
> > > Because I meant it as a smoke test program for SGX, not everything is
> > > too well documented but given the multipurpose use for that code I'll
> > > make the improvements that you are suggesting.
> > 
> > For FPU Control World (FCW), I think 0x037F is not the right value even
> > if section 18.5 in the x86 SDM says that it is the initial value for it.
> > 
> > I took that value from that section.
> > 
> > The reason I think that there is an error in the SDM is that if you look
> > at the section 8.1.5, you'll see that bit 6 is a reserved bit. Thus,
> > does not make to set it on.
> > 
> > I think the legit value ought to be 0x33F i.e. unset bit 6.
> 
> Bit 6 is reserved, but it's forced to '1' by the CPU.
> 
> Regardless, IMO it'd be better to drop this code entirely, it's all kinds
> of wonky.  The label says "xsave_area" and implies XSAVE state is being
> loaded, but the code uses FXRSTOR, which will only load x86/MMX/XMM state,
> i.e. the first 512 bytes of the so called xsave_area.
> 
> The test enclave doesn't touch state managed by XSAVE, let alone put
> secrets into said state.  I see no reason to bother purging anything.

Makes sense.

/Jarkko
Dr. Greg March 17, 2020, 1:07 a.m. UTC | #17
On Tue, Mar 10, 2020 at 02:29:41PM -0500, Haitao Huang wrote:

Good evening, I hope the week is going well for everyone.

> >Just as a clarification, are you testing the new driver against
> >signed production class enclaves in .so format that also include
> >metadata layout directives or is the driver just getting tested
> >against the two page toy enclave that copies a word of memory from
> >one memory location to another?

> We (Intel SGX SDK/PSW team) tested this driver for enclaves in .so
> format with metadata. Our 2.8 release supports v24 and 2.9 supports
> v25+. Both production signed and debug signed enclaves worked.
>
> *Note* we did make some code changes in our runtime for v24+, mainly
> dealing with src & EPC page alignment for EADD, open one fd per
> enclave, use -z noexecstack linker option, etc. You can see the
> changes on GitHub.

Lots of knobs getting turned at the same time but we sorted out all
the issues and our runtime is now passing its regression tests with
the new driver, with an exception that we note below.

I suspect that we might have the only complete and architecturally
independent runtime implementation so if the new driver is working
against yours and ours it would seem to be a reasonable test spectrum
for the driver.

> >We see the same behavior from both our unit test enclaves and the
> >Quoting Enclave from the Intel SGX runtime.

> We did not see any issue loading QE in our tests. Please directly
> email me on this test if you have specific questions.

As it turns out the major problem we were running into with respect to
the QE test was the fact that generic use of atexit() handlers was
disabled by changes that went into the 2.8 SDK.  Our runtime and SDK
assume that enclave atexit() handling works.

The enclave UNINIT ECALL is only allowed on runtimes that are
advertising EDMM support.  That seems excessively restrictive since
atexit() handling is generically useful for enclaves that are not
using EDMM.  Our runtime allows EDMM to be disabled and we have
enclaves that gate on that for security purposes.

On a quasi-related note, it appears that the 1.4 compatibility
metadata created by post 2.0 signing tools is leaking layout
descriptors that a version 1.4 runtime doesn't understand.

Do you want to exchange e-mail on this or should we direct
conversations about these issues with others on your SDK team.

Have a good remainder of the week.

Dr. Greg

As always,
Dr. Greg Wettstein, Ph.D, Worker      SGX secured infrastructure and
Enjellic Systems Development, LLC     autonomously self-defensive
4206 N. 19th Ave.                     platforms.
Fargo, ND  58102
PH: 701-281-1686                      EMAIL: greg@enjellic.com
------------------------------------------------------------------------------
"All parts should go together without forcing.  You must remember that
 the parts you are reassembling were disassembled by you.  Therefore,
 if you can't get them together again, there must be a reason.  By all
 means, do not use a hammer."
                                -- IBM maintenance manual, 1925
diff mbox series

Patch

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
+
+	.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-----