[HowTo] Confidential coding with Intel® SGX on RHEL/CentOS 8.x (1)
In our previous post, we defined what we meant by “Confidential coding”. We evoked Intel® SGX as one main technology, and will now produce a working example on Linux RHEL/CentOS 8.
Let us suppose we have the following C code:
int i, tmp_number = 0;
char password[8];
/* get random string from RDRAND, an Intel CPU module */
sgx_read_rand(password, 8);
/* apply our special treatment while converting to integer… */
for (i = 0; i < 8; i++)
tmp_number += pow(10, i) * abs(password[i]);
/* … then to string again */
snprintf(password, 8, “%08d”, tmp_number);
We already use sgx_read_rand() to take advantage of the Intel CPU’s RDRAND module and generate a random string (the instruction exists on AMD CPUs, but is less fool-proof).
Then we have our corporate “magic formula” : we iterate on each character as a number (int8_t), make it strictly positive, multiply it by an exponent of 10, and add it; the sum as a numerical sequence will be the password.
This is our secret, and we don’t want to disclose it! We will make it all happen inside an Intel® SGX Secure Enclave memory area (in simulation mode).
- PREREQUISITES
Here are the required distribution packages:
$ dnf -y install openssl-devel libcurl-devel wget git cmake rpm-build perl python2 protobuf
$ dnf --enablerepo=PowerTools install -y protobuf-devel ocaml ocaml-ocamlbuild
$ alternatives --set python /usr/bin/python2
2. INSTALL THE INTEL® SGX SDK
- Grab the SDK for CentOS 8.2 and install it to “$HOME/sgxsdk” :
$ chmod a+x sgx_linux_x64_sdk_2.12.100.3.bin
$ echo -e “no\n$HOME” | ./sgx_linux_x64_sdk_2.12.100.3.bin - Grab the linker tools and install them to “$HOME/sgxsdk/bin”:
$ tar xfvz as.ld.objdump.gold.r3.tar.gz
$ cp as.ld.objdump.gold.r3/external/toolset/centos8.2/* $HOME/sgxsdk/bin/
$ rm -rf as.ld.objdump.gold.r3/ - Modify the environment file so they are prioritized in the $PATH:
$ sed -i ‘s/export PATH=$PATH:/export PATH=/’ $HOME/sgxsdk/environment
$ sed -i ‘s/x64/x64:$PATH/’ $HOME/sgxsdk/environment
3. TRY THE ENVIRONMENT
Each time before compiling, we will source the SDK’s environment:
$ source $HOME/sgxsdk/environment
and verify the Intel linker tools are used:
$ which ld
(should return: “$HOME/sgxsdk/bin/ld”)
4. GRAB THE PROJECT
Grab my Enclave project, already containing all required files ; and enter it:
$ tar xfvj Enclave.tar.bz2 && cd Enclave
5. GENERATE A PRIVATE/PUBLIC KEY PAIR (key.pem / key.pub)
Only signed Enclave objects (shared libraries) can be loaded; we produce a strong RSA key pair with OpenSSL:
$ openssl genrsa -out key.pem -3 3072
$ openssl rsa -in key.pem -pubout -out key.pub
6. GENERATE PROXY CODE/HEADERS FROM .EDL FILE
The Enclave.edl file contains a definition of functions which will be allowed to go through the invisible wall separating “test_sgx” and “Enclave.so” :
trusted {
/* application to Enclave (ECALLs) */
public void get_secure_password([in, out, string] char* out);
};
This in an ECALL ; it goes “test_sgx”(app) -> “Enclave.so” (secure library). As there is a pointer parameter, we need to specify directions ; depending on who gets to access behind the pointer ([in]: pass the string to Enclave, but Enclave cannot return a modified string; [out]: the reverse).
As this is intended to exchange the password freely, we use both: [in, out].
([string] is a kind a sugar, it allows us to omit a “size” variable which would let us know about string length. We can simply use “strlen(string)” here)
untrusted {
/* Enclave to application (OCALLs) */
void debug_int(uint32_t n);
void debug_str([in, string] const char *str);
};
This are OCALLs ; they go “Enclave.so” (secure library) -> “test_sgx”(app).
Proxy code/headers will need to be generated by this SDK command:
$ sgx_edger8r Enclave.edl
(generates Enclave_u.c/.h for “test_sgx”, Enclave_t.c/.h for “Enclave.so”)
7. COMPILE THE APP (“test_sgx”)
The app loads a signed version of the Enclave (“Enclave.so.signed”) and gets an “eid” pointer from it:
sgx_enclave_id_t eid;
sgx_create_enclave(“Enclave.so.signed”, […], &eid, NULL);
it later uses it to call the “trusted” secure “get_secure_password()” function:
char pw[] = “********”;
get_secure_password(eid, pw);
(we pre-allocate the string memory here in for obvious reasons; memory cannot be allocated in the Enclave then passed back to the app, as memory areas are distinct. It will only write into an existing buffer bidirectionally)
We compile it simply by adding the “Enclave_u.c” proxy (“Enclave_u.h” is #included in “test_sgx.c”) and linking with the simulation library :
$ gcc -I$HOME/sgxsdk/include -L$HOME/sgxsdk/lib64 test_sgx.c Enclave_u.c -o test_sgx -lsgx_urts_sim
8. COMPILE THE ENCLAVE LIBRARY (“Enclave.so”)
- We first generate the “Enclave.o” object file :
$ gcc -m64 -O0 -fstack-protector -fPIC -Wno-attributes -I$HOME/sgxsdk/include -I$HOME/sgxsdk/include/tlibc -c Enclave_t.c Enclave.c
(note the “$HOME/sgxsdx/include/tlibc” folder contains specific version of C headers such as “stdio.h”, “string.h”… Lots of functions are restricted or missing on purpose, such as printf() -Enclave doesn’t have access to stdout/OS environment). - We then link it to “Enclave.so”, which is the arcane part:
$ gcc Enclave_t.o Enclave.o -o Enclave.so -Wl, — no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$HOME/sgxsdk/lib64 -Wl, — whole-archive -lsgx_trts_sim -Wl, — no-whole-archive -Wl, — start-group -lsgx_tstdc -lsgx_tservice_sim -lsgx_tcrypto -Wl, — end-group -Wl, — version-script=Enclave.lds -Wl, — no-undefined -Wl,-pie,-eenclave_entry -Wl, — export-dynamic -Wl, — defsym,__ImageBase=0
(“-lsgx_tstdc” is SGX’s restricted version of the C standard library; “Enclave.lds” is a kind of capability file which defines available features; ImageBase is a missing symbol which we NULLify)
9. SIGN THE ENCLAVE LIBRARY (“Enclave.so.signed”)
We generate a hash signature from the binary, sign it with our private key, then sign the binary itself with our public key:
$ sgx_sign gendata -enclave Enclave.so -out Enclave.hex
$ openssl dgst -sha256 -keyform PEM -sign key.pem -out Enclave.hex.signed Enclave.hex
$ sgx_sign catsig -enclave Enclave.so -unsigned Enclave.hex -sig Enclave.hex.signed -key key.pub -out Enclave.so.signed
10. TRY THE APP!
$ ./test_sgx
(you can use the “build.sh” and “clean.sh” scripts to rebuild it quickly later)
(you can call “./build.sh -DDEBUG” to make Enclave.so call the OCALL function implemented in test_sgx and debug printf() confidential variables)
You may have noticed the application workings even if you do not have a compatible CPU. This is because we linked with the “_sim” -simulation- version of libraries ; on a real-supported system, we are supposed to install a driver and enforce keys on a hardware level. This will be a topic for a future post !