4.1. Tutorial 1: Hello, World!¶
This tutorial explains how to build and run a simple “hello world” enclave. An enclave consists of an eapp and a runtime, but also needs the host that initializes and launches the enclave. Thus, each enclave source tree contains at least the host and eapp.
Before jumping into the tutorial, please complete Quick Start.
4.1.1. Prerequisite¶
The Eyrie runtime allows the enclave to be statically linked with
libc, and will then support a few standard functions such as
printf
. This is not a secure I/O interface, but is useful for
demos and benchmarking.
Set PATH
to include RISC-V tools and KEYSTONE_SDK_DIR
to point the
absolute path to sdk
directory.
export PATH=$PATH:<path to RISC-V tools>
export KEYSTONE_SDK_DIR=<path to SDK>
Let’s take a look at the example provided in Keystone SDK.
ls sdk/examples/hello
You can find two directories and a build script called vault.sh
4.1.2. vault.sh¶
vault.sh
is a sample script that builds the enclave. See full
documentation at vault.sh.
sdk/examples/hello/vault.sh
To build the enclave application package, run:
./vault.sh
4.1.3. Enclave Application: hello.c¶
Open hello.c
file in sdk/exmamples/hello/eapp/
. This is the source code of the enclave
application.
#include <stdio.h>
int main()
{
printf("hello, world!\n");
return 0;
}
This is the standard C program that we will run isolated in an enclave.
4.1.4. Host Application: host.cpp¶
Open host.cpp
in sdk/examples/hello/host/
. This is the source code of the host application.
#include "keystone.h"
#include "edge_call.h"
int main(int argc, char** argv)
{
Keystone enclave;
Params params;
params.setFreeMemSize(1024*1024);
params.setUntrustedMem(DEFAULT_UNTRUSTED_PTR, 1024*1024);
enclave.init(argv[1], argv[2], params);
enclave.registerOcallDispatch(incoming_call_dispatch);
edge_call_init_internals((uintptr_t) enclave.getSharedBuffer(),
enclave.getSharedBufferSize());
enclave.run();
return 0;
}
keystone.h
contains Keystone
class which has several member functions to control the
enclave.
Following code initializes the enclave memory with the eapp/runtime.
Keystone enclave;
Params params;
enclave.init(<eapp binary>, <runtime binary>, params);
Params
class is defined in sdk/lib/host/include/params.h
, and contains enclave paraeters
such as the size of free memory and the address/size of the untrusted shared buffer.
These parameters can be configured by following lines:
params.setFreeMemSize(1024*1024);
params.setUntrustedMem(DEFAULT_UNTRUSTED_PTR, 1024*1024);
In order to handle the edge calls (including system calls), the enclave must register the edge call handler and initialize the buffer addresses. This is done as following:
enclave.registerOcallDispatch(incoming_call_dispatch);
edge_call_init_internals((uintptr_t) enclave.getSharedBuffer(),
enclave.getSharedBufferSize());
Finally, the host launches the enclave by
enclave.run();
4.1.5. Enclave Package¶
vault.sh
also contains packaging commands using makeself
.
makeself
generates a self-extracting archive with a start-up command.
All files included in $PACKAGE_FILES
are copied into a directory and archived with makeself
.
The final output is hello.ke
which is an executable file for our enclave.
Since we set $OUTPUT_DIR
to buildroot overlay
directory $KEYSTONE_SDK_DIR/../buildroot_overlay/root/$NAME
,
running make image
in the top-level directory (keystone
) will generate the buildroot disk
image
containing the outputs.
# go to top-level keystone directory
make image
4.1.6. Deploying Enclave¶
Boot the machine with QEMU.
./scripts/run-qemu.sh
Insert the Keystone driver
# [inside QEMU]
insmod keystone-driver.ko
Deploy the enclave
# [inside QEMU]
./hello/hello.ke
You’ll see the enclave running!
Verifying archive integrity... All good.
Uncompressing Keystone vault archive 100%
hello, world!