The main theme of SGX is the Enclave!

It’s rather an abstract concept, right?!
Yet in this post, we will take a close how to create and use an Enclave as well as understanding where it sits in the memory!

As we previously showed you, it is best to think of the Enclave as the ‘container‘ of the trusted code.

Don’t mix the Linux containers though! it’s a totally different beast! That’s why I wrote it between two annotations!

In this post, we will see an Enclave that contains a sensitive information of a secret password! and an API to determine whether the user is trying to log-in using the right password.

Obviously, the password thing is just to add juice to the mixture of concept here 🙂

Here how our system should look like (from space)

passwd

Notice how the whole project (i.e. solution in Visual-Studio terms ) is divided into two separate projects

  1. Trusted code
  2. Un-trusted code

The trusted code is located in the Enclave project, and the untrusted code, goes into the App project, thus, in the same project solution, there are going to be two separate projects!

Since the password which the user inputs goes through the untrusted code first, therefore, the App project ought to be set as the start-up project.

// App.cpp
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include "sgx_urts.h"
#include "sample_enclave_u.h"
#include <string>

#define ENCLAVE_FILE _T("sample_enclave.signed.dll")
#define MAX_BUF_LEN 100

using namespace std;

int main() {
	sgx_enclave_id_t eid;
	sgx_status_t	 ret = SGX_SUCCESS;
	sgx_launch_token_t	token = {0};
	int updated = 0;
	char userPassword[MAX_BUF_LEN] = {0};

	the Enclave with above launch token.
	ret = sgx_create_enclave(ENCLAVE_FILE, SGX_DEBUG_FLAG,
					&token, &updated, &eid, NULL);
	if (ret != SGX_SUCCESS) {
		printf("App: [Error] - failed to create enclave.\n", ret);
		return -1;
	}

       do {
		printf("Enter password: ");
		gets_s(userPassword);
		checkPassword(eid, userPassword, MAX_BUF_LEN);
} while(strcmp(userPassword, "C") != 0);			

printf("Welcome!\n");

	// Destroy the enclave when all Enclave calls finished.
	if(SGX_SUCCESS != sgx_destroy_enclave(eid))
	return -1;

	getchar();
	return 0;
}

Notice the function above – load_and_initialize_enclave, which creates the Enclave and assigns to it an ID (in case you want to use more than one Enclave in the same application) by calling to sgx_create_enclave which does all the heavy lifting.

If we look closely at the highlighted do-while
loop in the code above, we see that the untrusted function checkPassword(…) takes the ID of the Enclave as well as the password which the user inputed via the console.

userPassword is a pointer to char, thus the trusted function which compares it with the correct password, would make it point to char “C” (i.e. Correct) if it matches the correct password!

Finally, after being done with using the Enclave, we must destroy it, think about it as freeing an allocating memory.

Let’s consider this snapshot of our memory

enclaveappmemmap
the effect of sgx_create_enclave’

Code of the Enclave

In this section the code is divided into 2 sections:

  •  EDL file with the declaration of the functions.
  • The source code which implement those functions.

The purpose of the EDL file is to sort the functions of the solution into two categories:

  1. Trusted functions: implemented in the enclave
  2. Untrusted functions: outside of the enclave and can invoke trusted functions as well.

Also, the EDL file specifies the arguments of each functions, and whether they are inputs, outputs or even both!

Here is how an EDL file looks for this example:

// sample_enclave.edl
enclave {
    trusted {
               /*
                * This is the first step in introducing a trusted function
                * Declaring the trusted function.
                * returns true if the passed password identical to the inner-
                * password in the function.
                */
               public void checkPassword([in, out,  size=len] char* password, 
                                                               size_t len);
    };
};
  1.  The password is an input, as well as an output, because if it is correct, then it will be pointing at the char “C“ instead, hence the user (outside the Enclave) can know whether his password is correct or not.
  2. Length of the password that the user inputs, serves internal use only (strcmp).

implementation of the trusted function in the enclave

// sample_enclave.cpp
#include "sample_enclave_t.h"
#include <string.h>

const char* CORRECT_PASS = "very very secret";

void checkPassword (char* password, size_t len) {
	if(strcmp(password, CORRECT_PASS) == 0) {           // if correct
		memcpy(password, "C" ,  strlen("C")+ 1);
	}
}

All the confidential data must be stored in the Enclave, like the CORRECT_PASS string.

Note that in the Enclave functions, we cannot invoke system calls! We are going to talk about system calls in another section and present a workaround!

output

Advertisements