Please see README for how to install Mystikos or build it from source code.
This document guides users to create TEE-aware applications, which are essential to many confidential computing scenarios.
Why would an application want to be aware of the TEE it's running within? Two possible requirements are:
- In an isolated confidential computing scenario, the application should behave differently based on whether it's running inside a TEE or not, and, if it is, which types of TEE;
- In a collaborated confidential computing scenario, the application wants to get help from the TEE, and gain trust to obtain secrets from an external party which enforces a policy such as only releasing keys to applications running inside a TEE with certain identities.
Why is collaborated confidential computing so important?
Imagine a doomsday scenario when the digital world has fallen: all routers/NICs
on the internet and all operating systems become untrustworthy, the
collaborated confidential computing would enable a group of
applications, serving as safe harbors
, to collaborate and fight against
the malicious world.
Mystikos provides two environment variables, MYST_TARGET
and
MYST_WANT_TEE_CREDENTIALS
, for TEE-aware applications.
MYST_TARGET
is read only, and applications can query the variable to
find out whether it is running outside or inside a TEE, and, when it's
running inside a TEE, which specific TEE platform it is.
When applications set MYST_WANT_TEE_CREDENTIALS
to CERT_PEMKEY
, the Mystikos
runtime, as part of the booting process, will generate:
- a self-signed TLS certificate with the root of trust from the TEE; and
- an ephemeral private key corresponding to the public key embedded in the certificate.
Both credentials are then saved to a fixed location in the file system. Note the private key is exported in PEM format. With both the certificate and the private key, the application can establish an attested TLS channel with a peer, as long as the peer could perform verification on the certificate and relate it to the root of trust from the TEE. Now both parties can exchange secrets without the fear of eavesdropping from malicious actors on the internet.
Furthermore, Mystikos provides two system calls, one for generating the above
mentioned certificate and private key, one for verifying the certificate,
for languages that support the direct invocation of syscalls, such as C/C++.
These syscalls give application the flexibility to generate or verify as
many TLS certificates as possible at any time, without relying on
MYST_WANT_TEE_CREDENTIALS
.
For applications written in high level languages which allow no direct syscalls, FFI can be used to call into a native library that exposes such system calls.
This example shows how to write a program that potentially performs secret operations only when running inside a TEE.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
const char* target = getenv("MYST_TARGET");
if ( !target )
{
printf("I am in unknown environment\n");
return 1;
}
if (strcmp(target, "sgx") != 0)
{
printf("I am in non-TEE\n");\
return 1;
}
printf("I am in SGX! Now, for secrets!\n");
// Perform secret operations below
// ...
return 0;
}
Save this to tee.c
. You can build and run the program on Ubuntu with the
following commands:
gcc tee.c -o tee && ./tee
The output should be: I am in unknown environment
.
Now build and run the program in SGX with the following commands:
mkdir appdir; myst-gcc tee.c -o appdir/tee
myst mkcpio appdir rootfs && myst exec rootfs /tee
The output should be: I am in SGX! Now, for secrets!
.
This example shows how to generate a TLS certificate and verify it using system calls with C/C++. This is not interesting by itself because there is no peers to establish trust with. For a peer-to-peer trusted channel example, please check solutions attested_tls.
A self-signed certificates generated by Mystikos includes:
- A public key for subsequent encrypted communications, and
- An attestation report containing:
- The proof that the application is running in a specific TEE;
- The application's identity; and
- The hash of the communication public key.
When running with Mystikos, an application verifies the self-signed certificate from a peer by issuing a syscall to Mystikos that:
- extracts the public key and the attestation report;
- checks if the attestation report is genuinely generated by a TEE;
- checks if the hash of the public key matches the hash value inside the attestation report;
- checks if the application identity is expected. See
_verifier
function below as an example.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdint.h>
#include <myst/tee.h>
int _verifier(myst_tee_identity_t* identity, void* ptr)
{
// Expected Product ID: {1}
const uint8_t PRODID[MYST_PRODUCT_ID_SIZE] = {1};
// Expected security version: 1
const int SVN = 1;
// Returning 0 means pass.
// We can easily expand this to more sophicated checks
// based on unique_id, signer_id, etc.
return memcmp(identity->product_id, PRODID, MYST_PRODUCT_ID_SIZE) ||
identity->security_version != SVN;
}
int main()
{
long ret;
void* cert = NULL;
void* pkey = NULL;
size_t cert_size = 0, pkey_size = 0;
ret = syscall(SYS_myst_gen_creds, &cert, &cert_size, &pkey, &pkey_size);
assert(ret == 0);
printf("Generated a self-signed certificate and a private key\n");
ret = syscall(SYS_myst_verify_cert, cert, cert_size, _verifier, NULL);
assert(ret == 0);
printf("Verified the self-signed certificate\n");
ret = syscall(SYS_myst_free_creds, cert, cert_size, pkey, pkey_size, NULL, 0);
assert(ret == 0);
return 0;
}
Save the file as tee2.c
. Now build and run the program in SGX with the
following commands:
gcc tee2.c -I/opt/mystikos/include -o appdir/tee2
myst mkcpio appdir rootfs && myst exec rootfs /tee2
And the output should be:
Generated a self-signed certificate and a private key
Verified the self-signed certificate
The application is free to provide NULL
for the third parameter of
SYS_myst_verify_cert
. In that case, Mystikos would accept any
application running inside a TEE with no regard to the application identity.
If the application does want to reject/approve based on an app identity, it
must include myst/tee.h
, which defines a function similar to _verifier
,
and pass it to the syscall SYS_myst_verify_cert
.
High level languages like C# don't have direct access to syscalls as
C/C++ do. Therefore, we introduce the environment variable
MYST_WANT_TEE_CREDENTIALS
, which when specified in config.json
, instructs
Mystikos to generate desired credentials and save them on the file system
for applications to make use of.
First we declare the desire in the config.json:
{
// OpenEnclave specific values
// Whether we are running myst+OE+app in debug mode
"Debug": 1,
"ProductID": 1,
"SecurityVersion": 1,
// Mystikos specific values
// The heap size of the user application. Increase this setting if your app experienced OOM.
"MemorySize": "1g",
// The path to the entry point application in rootfs
"ApplicationPath": "/app/TEEAware",
// The parameters to the entry point application
"ApplicationParameters": [],
// Whether we allow "ApplicationParameters" to be overridden by command line options of "myst exec"
"HostApplicationParameters": false,
// The environment variables accessible inside the enclave.
"EnvironmentVariables": ["COMPlus_EnableDiagnostics=0", "MYST_WANT_TEE_CREDENTIALS=CERT_PEMKEY_REPORT"],
"UnhandledSyscallEnosys": false
}
Note that the last line MYST_WANT_TEE_CREDENTIALS=CERT_PEMKEY_REPORT
tells
Mystikos to generate three files for the application:
- a self-signed x509 certificate with an ephemeral RSA public key
- a private RSA key (in PEM format) that is paired with the public key
- a TEE-backed report that attests to the public key (by including the hash of the public key in the signed report)
The application can then load these credentials in the following manner:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace dotnet
{
class Program
{
static void Main(string[] args)
{
string certFile = "/tmp/myst.crt";
string pkeyFile = "/tmp/myst.key";
string reportFile = "/tmp/myst.report";
// Load the certificate into an X509Certificate object.
X509Certificate2 cert = new X509Certificate2(File.ReadAllBytes(certFile));
Console.WriteLine("Subject: {0}", cert.Subject);
Console.WriteLine("Public key: {0}", cert.GetPublicKeyString());
byte[] report = File.ReadAllBytes(reportFile);
Console.Write("Report: ");
foreach(byte b in report)
{
Console.Write("{0:X2}", b);
}
Console.WriteLine();
string pem = System.IO.File.ReadAllText(pkeyFile);
const string header = "-----BEGIN RSA PRIVATE KEY-----";
const string footer = "-----END RSA PRIVATE KEY-----";
if (pem.StartsWith(header))
{
// The private key file is in PEM format. Read and parse the DER structure.
int endIdx = pem.IndexOf(footer, header.Length, StringComparison.Ordinal);
string base64 = pem.Substring(header.Length, endIdx - header.Length);
byte[] der = Convert.FromBase64String(base64);
// Import the private key into the cert.
RSA rsa = RSA.Create();
rsa.ImportRSAPrivateKey(der, out _);
cert = cert.CopyWithPrivateKey(rsa);
Console.WriteLine("Imported RSA private key into cert");
}
// At this moment, the application can send the certificate and
// the report to peers. The peer could validate the public key in
// the certificate as long as it has access to an attestation
// service like MAA.
}
}
}