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.

Signed-off-by: Jarkko Sakkinen <jarkko.sakki...@linux.intel.com>
---
Since this is my very first addition to selftests, I decided to roll out
an RFC patch before including this to the main SGX series.
 tools/testing/selftests/x86/Makefile          |  10 +
 tools/testing/selftests/x86/sgx/Makefile      |  47 ++
 tools/testing/selftests/x86/sgx/encl.c        |  20 +
 tools/testing/selftests/x86/sgx/encl.lds      |  33 ++
 .../selftests/x86/sgx/encl_bootstrap.S        |  94 ++++
 tools/testing/selftests/x86/sgx/encl_piggy.S  |  16 +
 tools/testing/selftests/x86/sgx/encl_piggy.h  |  13 +
 .../testing/selftests/x86/sgx/sgx-selftest.c  | 146 +++++
 tools/testing/selftests/x86/sgx/sgx_arch.h    | 109 ++++
 tools/testing/selftests/x86/sgx/sgx_call.S    |  20 +
 tools/testing/selftests/x86/sgx/sgx_uapi.h    | 100 ++++
 tools/testing/selftests/x86/sgx/sgxsign.c     | 503 ++++++++++++++++++
 .../testing/selftests/x86/sgx/signing_key.pem |  39 ++
 13 files changed, 1150 insertions(+)
 create mode 100644 tools/testing/selftests/x86/sgx/Makefile
 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/encl_piggy.S
 create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h
 create mode 100644 tools/testing/selftests/x86/sgx/sgx-selftest.c
 create mode 100644 tools/testing/selftests/x86/sgx/sgx_arch.h
 create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
 create mode 100644 tools/testing/selftests/x86/sgx/sgx_uapi.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/Makefile 
b/tools/testing/selftests/x86/Makefile
index 186520198de7..4fc9a42f56ea 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+SUBDIRS_64 := sgx
+
 all:
 
 include ../lib.mk
@@ -67,6 +70,13 @@ all_32: $(BINARIES_32)
 
 all_64: $(BINARIES_64)
 
+all_64: $(SUBDIRS_64)
+       @for DIR in $(SUBDIRS_64); do                   \
+               BUILD_TARGET=$(OUTPUT)/$$DIR;           \
+               mkdir $$BUILD_TARGET  -p;               \
+               make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+       done
+
 EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64)
 
 $(BINARIES_32): $(OUTPUT)/%_32: %.c
diff --git a/tools/testing/selftests/x86/sgx/Makefile 
b/tools/testing/selftests/x86/sgx/Makefile
new file mode 100644
index 000000000000..04004d244de4
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/Makefile
@@ -0,0 +1,47 @@
+top_srcdir = ../../../../..
+
+include ../../lib.mk
+
+HOST_CFLAGS := -Wall -Werror -g
+ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \
+              -fno-stack-protector -mrdrnd
+
+TEST_CUSTOM_PROGS := $(OUTPUT)/sgx-selftest
+all_64: $(TEST_CUSTOM_PROGS)
+
+$(TEST_CUSTOM_PROGS): $(OUTPUT)/sgx-selftest.o $(OUTPUT)/sgx_call.o \
+                     $(OUTPUT)/encl_piggy.o
+       $(CC) $(HOST_CFLAGS) -o $@ $^
+
+$(OUTPUT)/sgx-selftest.o: sgx-selftest.c
+       $(CC) $(HOST_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/sgx_call.o: sgx_call.S
+       $(CC) $(HOST_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl_piggy.o: $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+
+$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf $(OUTPUT)/sgxsign
+       objcopy --remove-section=.got.plt -O binary $< $@
+
+$(OUTPUT)/encl.elf: $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o
+       $(CC) $(ENCL_CFLAGS) -T encl.lds -o $@ $^
+
+$(OUTPUT)/encl.o: encl.c
+       $(CC) $(ENCL_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl_bootstrap.o: encl_bootstrap.S
+       $(CC) $(ENCL_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl.ss: $(OUTPUT)/encl.bin  $(OUTPUT)/sgxsign
+       $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+
+$(OUTPUT)/sgxsign: sgxsign.c
+       $(CC) -o $@ $< -lcrypto
+
+EXTRA_CLEAN := $(OUTPUT)/sgx-selftest $(OUTPUT)/sgx-selftest.o \
+              $(OUTPUT)/sgx_call.o $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss \
+              $(OUTPUT)/encl.elf $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o \
+              $(OUTPUT)/sgxsign
+
+.PHONY: clean
diff --git a/tools/testing/selftests/x86/sgx/encl.c 
b/tools/testing/selftests/x86/sgx/encl.c
new file mode 100644
index 000000000000..eb6aa318d3f1
--- /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 "sgx_arch.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..4997628813b8
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl.lds
@@ -0,0 +1,33 @@
+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 the SGX 
LE")
+ASSERT(!DEFINED(.altinstr_replacement), "ALTERNATIVES are not supported in the 
SGX LE")
+ASSERT(!DEFINED(.discard.retpoline_safe), "RETPOLINE ALTERNATIVES are not 
supported in the SGX LE")
+ASSERT(!DEFINED(.discard.nospec), "RETPOLINE ALTERNATIVES are not supported in 
the SGX LE")
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..62251c7d9927
--- /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
+       .long   encl_ssa                # OSSA
+       .fill   1, 4, 0
+       .fill   1, 4, 0                 # CSSA (set by CPU)
+       .fill   1, 4, 1                 # NSSA
+       .long   encl_entry              # OENTRY
+       .fill   1, 4, 0
+       .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   503, 8, 0               # Reserved
+
+       .text
+
+encl_entry:
+       # %rbx contains the base address for TCS, which is also the first
+       # address inside the enclave. By adding $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
+
+       pop     %rbx # pop the address after EENTER
+
+       # 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/encl_piggy.S 
b/tools/testing/selftests/x86/sgx/encl_piggy.S
new file mode 100644
index 000000000000..c5d5787a7c89
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl_piggy.S
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+       .section ".rodata", "a"
+
+encl_bin:
+       .globl encl_bin
+       .incbin "encl.bin"
+encl_bin_end:
+       .globl encl_bin_end
+
+encl_ss:
+       .globl encl_ss
+       .incbin "encl.ss"
diff --git a/tools/testing/selftests/x86/sgx/encl_piggy.h 
b/tools/testing/selftests/x86/sgx/encl_piggy.h
new file mode 100644
index 000000000000..6692d3a8cee0
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl_piggy.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+#ifndef ENCL_PIGGY_H
+#define ENCL_PIGGY_H
+
+extern unsigned char encl_bin[];
+extern unsigned char encl_bin_end[];
+extern unsigned char encl_ss[];
+
+#endif /* ENCL_PIGGY_H */
diff --git a/tools/testing/selftests/x86/sgx/sgx-selftest.c 
b/tools/testing/selftests/x86/sgx/sgx-selftest.c
new file mode 100644
index 000000000000..ef05565948e6
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgx-selftest.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#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 "encl_piggy.h"
+#include "sgx_arch.h"
+#include "sgx_uapi.h"
+
+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 *base;
+       int rc;
+
+       memset(secs, 0, sizeof(*secs));
+       secs->ssaframesize = 1;
+#ifndef CONFIG_EINITTOKENKEY
+       secs->attributes = SGX_ATTR_MODE64BIT;
+#else
+       secs->attributes = SGX_ATTR_MODE64BIT | SGX_ATTR_EINITTOKENKEY;
+#endif
+       secs->xfrm = 3;
+
+       for (secs->size = 4096; secs->size < bin_size; )
+               secs->size <<= 1;
+
+       base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC,
+                   MAP_SHARED, dev_fd, 0);
+       if (base == MAP_FAILED) {
+               perror("mmap");
+               return false;
+       }
+
+       secs->base = (uint64_t)base;
+
+       ioc.src = (unsigned long)secs;
+       rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_CREATE, &ioc);
+       if (rc) {
+               fprintf(stderr, "ECREATE failed rc=%d.\n", rc);
+               munmap(base, secs->size);
+               return false;
+       }
+
+       return true;
+}
+
+static bool encl_add_page(int dev_fd, unsigned long addr, void *data,
+                         uint64_t flags)
+{
+       struct sgx_enclave_add_page ioc;
+       struct sgx_secinfo secinfo;
+       int rc;
+
+       memset(&secinfo, 0, sizeof(secinfo));
+       secinfo.flags = flags;
+
+       ioc.secinfo = (unsigned long)&secinfo;
+       ioc.mrmask = 0xFFFF;
+       ioc.addr = addr;
+       ioc.src = (uint64_t)data;
+
+       rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_ADD_PAGE, &ioc);
+       if (rc) {
+               fprintf(stderr, "EADD failed rc=%d.\n", rc);
+               return false;
+       }
+
+       return true;
+}
+
+static bool encl_load(struct sgx_secs *secs, unsigned long bin_size)
+{
+       struct sgx_enclave_init ioc;
+       uint64_t offset;
+       uint64_t flags;
+       int dev_fd;
+       int rc;
+
+       dev_fd = open("/dev/sgx", 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;
+
+       for (offset = 0; offset < bin_size; offset += 0x1000) {
+               if (!offset)
+                       flags = SGX_SECINFO_TCS;
+               else
+                       flags = SGX_SECINFO_REG | SGX_SECINFO_R |
+                               SGX_SECINFO_W | SGX_SECINFO_X;
+
+               if (!encl_add_page(dev_fd, secs->base + offset,
+                                  encl_bin + offset, flags))
+                       goto out_map;
+       }
+
+       ioc.addr = secs->base;
+       ioc.sigstruct = (uint64_t)encl_ss;
+       rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_INIT, &ioc);
+       if (rc)
+               goto out_map;
+
+       close(dev_fd);
+       return true;
+out_map:
+       munmap((void *)secs->base, secs->size);
+out_dev_fd:
+       close(dev_fd);
+       return false;
+}
+
+void sgx_call(void *rdi, void *rsi, void *entry);
+
+int main(int argc, char **argv)
+{
+       unsigned long bin_size = (unsigned long)&encl_bin_end -
+                                (unsigned long)&encl_bin;
+       struct sgx_secs secs;
+       uint64_t result = 0;
+
+       if (!encl_load(&secs, bin_size))
+               exit(1);
+
+       sgx_call((void *)&MAGIC, &result, (void *)secs.base);
+       if (result != MAGIC) {
+               fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
+               exit(1);
+       }
+
+       exit(0);
+}
diff --git a/tools/testing/selftests/x86/sgx/sgx_arch.h 
b/tools/testing/selftests/x86/sgx/sgx_arch.h
new file mode 100644
index 000000000000..7711a1be8ad4
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgx_arch.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+#ifndef SGX_ARCH_H
+#define SGX_ARCH_H
+
+#include <stdint.h>
+
+enum sgx_attribute {
+       SGX_ATTR_DEBUG          = 0x02,
+       SGX_ATTR_MODE64BIT      = 0x04,
+       SGX_ATTR_PROVISIONKEY   = 0x10,
+       SGX_ATTR_EINITTOKENKEY  = 0x20,
+};
+
+#define SGX_ATTR_RESERVED_MASK 0xFFFFFFFFFFFFFFC9L
+
+#define SGX_SECS_RESERVED1_SIZE 24
+#define SGX_SECS_RESERVED2_SIZE 32
+#define SGX_SECS_RESERVED3_SIZE 96
+#define SGX_SECS_RESERVED4_SIZE 3836
+
+struct sgx_secs {
+       uint64_t size;
+       uint64_t base;
+       uint32_t ssaframesize;
+       uint32_t miscselect;
+       uint8_t reserved1[SGX_SECS_RESERVED1_SIZE];
+       uint64_t attributes;
+       uint64_t xfrm;
+       uint32_t mrenclave[8];
+       uint8_t reserved2[SGX_SECS_RESERVED2_SIZE];
+       uint32_t mrsigner[8];
+       uint8_t reserved3[SGX_SECS_RESERVED3_SIZE];
+       uint16_t isvvprodid;
+       uint16_t isvsvn;
+       uint8_t reserved4[SGX_SECS_RESERVED4_SIZE];
+};
+
+#define SGX_SECINFO_PERMISSION_MASK    0x0000000000000007L
+#define SGX_SECINFO_PAGE_TYPE_MASK     0x000000000000FF00L
+#define SGX_SECINFO_RESERVED_MASK      0xFFFFFFFFFFFF00F8L
+
+enum sgx_page_type {
+       SGX_PAGE_TYPE_SECS      = 0x00,
+       SGX_PAGE_TYPE_TCS       = 0x01,
+       SGX_PAGE_TYPE_REG       = 0x02,
+       SGX_PAGE_TYPE_VA        = 0x03,
+       SGX_PAGE_TYPE_TRIM      = 0x04,
+};
+
+enum sgx_secinfo_flags {
+       SGX_SECINFO_R           = 0x01,
+       SGX_SECINFO_W           = 0x02,
+       SGX_SECINFO_X           = 0x04,
+       SGX_SECINFO_SECS        = (SGX_PAGE_TYPE_SECS << 8),
+       SGX_SECINFO_TCS         = (SGX_PAGE_TYPE_TCS << 8),
+       SGX_SECINFO_REG         = (SGX_PAGE_TYPE_REG << 8),
+       SGX_SECINFO_TRIM        = (SGX_PAGE_TYPE_TRIM << 8),
+};
+
+struct sgx_secinfo {
+       uint64_t flags;
+       uint64_t reserved[7];
+} __attribute__((aligned(64)));
+
+#define SGX_MODULUS_SIZE 384
+
+struct sgx_sigstruct_header {
+       uint64_t header1[2];
+       uint32_t vendor;
+       uint32_t date;
+       uint64_t header2[2];
+       uint32_t swdefined;
+       uint8_t reserved1[84];
+};
+
+struct sgx_sigstruct_body {
+       uint32_t miscselect;
+       uint32_t miscmask;
+       uint8_t reserved2[20];
+       uint64_t attributes;
+       uint64_t xfrm;
+       uint8_t attributemask[16];
+       uint8_t mrenclave[32];
+       uint8_t reserved3[32];
+       uint16_t isvprodid;
+       uint16_t isvsvn;
+} __attribute__((__packed__));
+
+struct sgx_sigstruct {
+       struct sgx_sigstruct_header header;
+       uint8_t modulus[SGX_MODULUS_SIZE];
+       uint32_t exponent;
+       uint8_t signature[SGX_MODULUS_SIZE];
+       struct sgx_sigstruct_body body;
+       uint8_t reserved4[12];
+       uint8_t q1[SGX_MODULUS_SIZE];
+       uint8_t q2[SGX_MODULUS_SIZE];
+};
+
+struct sgx_sigstruct_payload {
+       struct sgx_sigstruct_header header;
+       struct sgx_sigstruct_body body;
+};
+
+#endif /* SGX_ARCH_H */
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..b841d7345533
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgx_call.S
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+* Copyright(c) 2016-18 Intel Corporation.
+*/
+.macro ENCLU
+.byte 0x0f, 0x01, 0xd7
+.endm
+
+       .text
+
+       .global sgx_call
+sgx_call:
+       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_uapi.h 
b/tools/testing/selftests/x86/sgx/sgx_uapi.h
new file mode 100644
index 000000000000..ef19f1c013d1
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgx_uapi.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+#ifndef _UAPI_ASM_X86_SGX_H
+#define _UAPI_ASM_X86_SGX_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define SGX_MAGIC 0xA4
+
+#define SGX_IOC_ENCLAVE_CREATE \
+       _IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create)
+#define SGX_IOC_ENCLAVE_ADD_PAGE \
+       _IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page)
+#define SGX_IOC_ENCLAVE_INIT \
+       _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
+#define SGX_IOC_ENCLAVE_REMOVE_PAGES \
+       _IOW(SGX_MAGIC, 0x03, struct sgx_enclave_remove_pages)
+#define SGX_IOC_ENCLAVE_MODIFY_PAGES \
+       _IOW(SGX_MAGIC, 0x04, struct sgx_enclave_modify_pages)
+
+/* IOCTL return values */
+#define SGX_POWER_LOST_ENCLAVE         0x40000000
+
+/**
+ * struct sgx_enclave_create - parameter structure for the
+ *                             %SGX_IOC_ENCLAVE_CREATE ioctl
+ * @src:       address for the SECS page data
+ */
+struct sgx_enclave_create  {
+       __u64   src;
+};
+
+/**
+ * struct sgx_enclave_add_page - parameter structure for the
+ *                               %SGX_IOC_ENCLAVE_ADD_PAGE ioctl
+ * @addr:      address within the ELRANGE
+ * @src:       address for the page data
+ * @secinfo:   address for the SECINFO data
+ * @mrmask:    bitmask for the measured 256 byte chunks
+ */
+struct sgx_enclave_add_page {
+       __u64   addr;
+       __u64   src;
+       __u64   secinfo;
+       __u16   mrmask;
+} __attribute__((__packed__));
+
+
+/**
+ * struct sgx_enclave_init - parameter structure for the
+ *                           %SGX_IOC_ENCLAVE_INIT ioctl
+ * @addr:      address within the ELRANGE
+ * @sigstruct: address for the SIGSTRUCT data
+ */
+struct sgx_enclave_init {
+       __u64   addr;
+       __u64   sigstruct;
+};
+
+/**
+ * struct sgx_enclave_remove_pages - parameter structure for the
+ *                                   %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl
+ * @addr:      address in the ELRANGE for the first page
+ * @length:    length of the address range (must be multiple of the page size)
+ */
+struct sgx_enclave_remove_pages {
+       __u64   addr;
+       __u64   length;
+} __packed;
+
+/**
+ * enum sgx_enclave_modify_ops - page modification operations
+ * @SGX_ENCLAVE_MODIFY_PERMISSIONS:    change page permissions
+ * @SGX_ENCLAVE_MODIFY_TYPES:          change page type
+ */
+enum sgx_enclave_modify_ops {
+       SGX_ENCLAVE_MODIFY_PERMISSIONS  = 0,
+       SGX_ENCLAVE_MODIFY_TYPES        = 1,
+};
+
+/**
+ * struct sgx_enclave_modify_pages - parameter structure for the
+ *                                   %SGX_IOC_ENCLAVE_MOD_PAGES ioctl
+ * @addr:      address in the ELRANGE for the first page
+ * @length:    length of the address range (must be multiple of the page size)
+ * @secinfo:   address of the new SECINFO data
+ * @op:                a value of &sgx_enclave_modify_ops
+ */
+struct sgx_enclave_modify_pages {
+       __u64   addr;
+       __u64   length;
+       __u64   secinfo;
+       __u8    op;
+} __attribute__((__packed__));
+
+#endif /* _UAPI_ASM_X86_SGX_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..a670aa186dba
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgxsign.c
@@ -0,0 +1,503 @@
+// 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 "sgx_arch.h"
+
+static const char *sign_key_pass;
+
+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 int pem_passwd_cb(char *buf, int size, int rwflag, void *u)
+{
+       if (!sign_key_pass)
+               return -1;
+
+       strncpy(buf, sign_key_pass, size);
+       /* no retry */
+       sign_key_pass = NULL;
+
+       return strlen(buf) >= size ? size - 1 : strlen(buf);
+}
+
+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, pem_passwd_cb, 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,
+
+       sign_key_pass = getenv("KBUILD_SGX_SIGN_PIN");
+       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.19.1

Reply via email to