It is an exciting time: Conclave 1.1 is out and loaded with a bunch of new features and enhancements. In this blog I am going to talk about when you should compile and load an enclave in mock mode and how to do it, thus simplifying the development and testing when writing enclaves. Let’s start by downloading the SDK and the hello-world sample included within it.
Now, let’s talk about what an enclave is and how we use Conclave to build enclave applications. Then, we can explore the different modes you can use when building and running an enclave using Conclave. Finally, we’ll talk about ‘mock’ enclaves.
What is an enclave?
An Intel SGX enclave is a protected region of memory where the code executed and the data accessed is isolated in terms of:
- Confidentiality: no one has access to the data
- Integrity: no one can change the code and its behavior
No one can access this memory; not even the processes running at higher levels, the operating system, or even the user himself.
What is the role of Conclave when writing an SGX enclave?
Conclave builds on top of SGX, making it easier to develop enclaves in high-level languages, like Java or Kotlin.
An enclave can be built and loaded as a shared object on Linux OS. When using Conclave, this is managed automatically for you. You develop your application in two parts: the ‘host’ and the ‘enclave.’
The host runs outside the enclave and is responsible for loading the enclave and connecting it with the outside world. The enclave is where you put your code that handles data, keeping it locked away and safe in a small, controlled environment.
The command shown here is an example of how to load and start a Conclave application on a Linux machine
But what if you’re using a Mac or Windows platform?
How to load enclaves on Mac or Windows
Conclave provides you with a container-gradle plugin, which essentially provides a Linux environment to build your enclave using a docker container. You can compile, load, start, and then run a host and enclave using the container-gradle plugin command.
When building an enclave for Intel SGX, Conclave compiles the code down to a native binary using the native-image capability in GraalVM. The resulting enclave benefits from a faster startup time and lower runtime memory overhead than if we had embedded an alternative JVM inside the enclave.
You might be wondering how to run an enclave if you don’t have an SGX-enabled CPU. This takes us to our next point.
Running an enclave in simulation mode
Conclave provides a ‘simulation’ mode for running enclaves on systems that don’t have an Intel SGX capability. Building and running Conclave applications in simulation mode uses the same process as production enclaves, except that you can run your application on a system that does not support SGX. However, simulation mode still needs to run on a Linux platform.
Again, the container-gradle script comes to our rescue on non-Linux platforms, allowing us to load our simulation enclave using a Linux docker container. If you build the ‘hello-world’ sample provided with the Conclave SDK, then by default when you run the container-gradle script, Conclave loads the enclave in simulation mode.
This mode still compiles your code down to a native binary, which can result in fairly lengthy build times. You might be wondering — are there any other modes in which you can develop your enclave which makes build times as short as possible and debugging your enclave as easy as possible?
Running an enclave in modes other than simulation
Enclaves can also be built and run in debug and release modes. Both require an SGX-enabled CPU.
Debug mode lets you run your enclave code in a real SGX environment but with certain debugging features enabled. For instance, if you write to the console inside an enclave in debug mode, it will be shown in the console output. It is also possible to connect a debugger to a debug mode enclave, however, at present it is not easy to step through Java code using this mode. Although debug enclaves run in a real SGX environment, the debug capability opens the door to accessing data inside the enclave so debug mode enclaves should never be used in production!
Use release mode enclaves when you want to deploy your enclave to production. Release mode enclaves give you the full protection provided by Intel SGX. Unlike with a debug mode enclave, any writes to the console inside the enclave never leave the enclave, which prevents you from accidentally leaking data via the console.
To build an enclave in debug or release mode, specify the mode in container-gradle command for non-Linux systems.
./container-gradle -PenclaveMode=debug host:build
For Linux systems, use the below command:
./gradlew -PenclaveMode=debug host:build
We spoke about building an enclave in release, debug and simulation mode.
Now what if you want to test your enclave logic quickly, without the need to convert the enclave code to a native image? This is where mock mode comes into the picture.
How to run an enclave in mock mode
With the Conclave 1.1 release you can now load your enclave in mock mode. This means the enclave is loaded in the same JVM as that of the host, but no native image is created, no SGX-enabled CPU is required, and the build time is also reduced drastically. In mock mode, calling an enclave function is similar to calling a plain Java function. This is useful when you are continuously changing the enclave logic in the development phase and want to quickly test it. To run your enclave in mock mode, use the same script and provide “mock” as a parameter.
Unlike when using simulation, debug or release modes, the below command will work on macOS and Windows, as well as Linux. You do not need to use the container-gradle script for running mock mode enclaves on non-Linux platforms.
./gradlew -PenclaveMode=mock host:run
Unit testing your Enclave Module using Mock Enclave
To test the enclave logic you can write unit tests in the enclave module. You need to add the host dependency to your enclave build.gradle, as the enclave instance is loaded by the EnclaveHost class present in the host module.
You can then create an instance of the enclave by calling
EnclaveHost mockHost = EnclaveHost.load("com.r3.conclave.sample.enclave.ReverseEnclave"); mockHost.start(null, null);
Conclave automatically loads the enclave in mock mode by internally checking if the enclave class file is available on classpath. You can obtain the enclave instance using the
EnclaveHost.mockEnclave property and then access the mock enclave’s internals.
ReverseEnclave reverseEnclave = (ReverseEnclave)mockHost.getMockEnclave();
Once you have the enclave handle, you can unit test the business logic in the enclave just as you would test any simple Java function. All the business logic written in your enclave class should ideally be unit tested in the enclave module using the mock enclave mode.
If you run the below command, by default the enclave is loaded in mock mode.
Unit testing your Host Module using Mock Enclave
When you want to test your enclave in simulation or on real SGX hardware (debug or release mode), it would make sense to write an integration test in the host module. Use the below command to run the host tests. By default, an enclave is loaded in simulation mode in the host.
You can now take advantage of the fact that an enclave can also be loaded in mock mode from the host test class as shown by the below command. Though I would say, when writing integration tests from the host, you would usually want to load your enclave in simulation mode or on real SGX hardware.
./gradlew -PenclaveMode=mock host:test
Sealing is the ability to encrypt and save data outside the enclave and make sure that only the appropriate enclave can decrypt it. When you build an enclave, the resulting enclave can be identified by two ‘measurements:’
- MRENCLAVE is a hash of the code, static data and layout of the enclave binary and ensures the enclave cannot be modified before it has been loaded.
- MRSIGNER is a hash of the public part of the key used to sign the enclave.
To encrypt the data, a key is used which is derived either from MRENCLAVE or MRSIGNER, and also version numbers for the software revocation level and the SGX TCB level. When data is sealed using MRENCLAVE, only that enclave can decrypt the data, hence updates to the enclave will leave the data useless. Sealing data to MRSIGNER allows different enclaves to access the data and also allows for enclave upgrades. Enclaves with higher revocation levels or TCB levels can read data sealed to lower levels, but enclaves with lower revocation or TCB levels cannot read data sealed by higher levels. You can test this behavior by passing in the different levels to a mock configuration as shown below.
MockConfiguration mockConfiguration = new MockConfiguration(); mockConfiguration.setRevocationLevel(1); mockConfiguration.setTcbLevel(2); mockConfiguration.setProductID(1); EnclaveHost mockHost = EnclaveHost.load("com.r3.conclave.sample.enclave.ReverseEnclave", mockConfiguration);
Revocation level is the mock revocation level of the enclave, and gets incremented if a bug in the enclave/Conclave code is fixed.
TCB level is the current security state of the CPU platform identified by Intel. This also includes the CPU SGX microcode version.
Want to learn more?
Below are some helpful resources to learn more about Conclave and Confidential Computing: