CryptoServer¶
Overview¶
The CryptoServer encapsulates key handling (e.g. storing, loading) and crypto operations (e.g. generation of signatures, etc.) in an isolated component. It is supposed to be a central element of the TRENTOS security architecture, usable by other CAmkES components.
Info: The CryptoServer uses the KeyStore for the persisting of key material. Since the KeyStore at this time does not support encryption or integrity protection, it needs to be ensured that keys cannot be extracted easily (e.g. by using only internal/sealed memories, etc.).
Concepts¶
In the following, we introduce the trust model and the idea of “key ownership” specific to the CryptoServer.
Trust Model¶
The CryptoServer component itself is trusted. It is used to isolate sensitive data (e.g. keys) in its own component from the rest of the system, which is potentially not trusted.
Ownership Model¶
Here we shortly describe certain concepts that are introduced in order to implement the requirements:
Component IDs: In a microkernel system all connections between components have a unique ID. Based on this, we can identify the sources and sinks of RPC communication.
Key name: Cryptographic keys are stored in the KeyStore under a given name. This name is used to address a key when interacting with the CryptoServer.
Ownership of keys: The concept of “key ownership” means that the entity who creates a key (identified by its ID) in the CryptoServer (via generate/import) is considered to be its owner and is initially the only entity who can load and then use that key.
Access rights on keys: Based on the idea of ownership, a policy checker can be implemented which checks whether application X can use a key owned by application Y (where X and Y are component IDs).
Architecture¶
The CryptoServer makes use of the Keystore API and the Crypto API. The CryptoServer uses both internally and extends them with the ownership concept for keys. Every client of the CryptoServer has its own instance of the Crypto API and KeyStore. This way, there is no way one client can interfere with another client’s keys or cryptographic operations.
In the following diagram, we see that the CryptoServer exposes two
interfaces via a single CAmkES RPC endpoint denoted here as
cryptoServer_rpc
:
if_OS_Crypto
: This is the RPC interface internally used by the Crypto API; as a result, the CryptoServer can be used as RPC server for a Crypto API instance configured inOS_Crypto_MODE_CLIENT
orOS_Crypto_MODE_KEY_SWITCH
mode.if_CryptoServer
: This is to be used by the client(s) to explicitly load and store keys. The “ownership” concept introduced above applies to this interface.
flowchart TD %% App Node subgraph App Application(Application) CryptoServer_client(CryptoServer_client) subgraph CryptoApi_Client["Crypto Library"] OS_Crypto_App[OS_Crypto] CryptoLibClient[CryptoLibClient] CryptoLib_App[CryptoLib] end Application --> OS_Crypto_App Application --> CryptoServer_client OS_Crypto_App --> CryptoLibClient OS_Crypto_App --> CryptoLib_App end %% CryptoServer Node subgraph CryptoServer OS_KeyStore[OS_KeyStore] cryptoServer_rpc[cryptoServer_rpc] subgraph CryptoApi_Server["Crypto Library"] OS_Crypto_Server[OS_Crypto] CryptoLib_Server[CryptoLib] end CryptoServer_client -. "if_CryptoServer" .- cryptoServer_rpc cryptoServer_rpc --> OS_Crypto_Server cryptoServer_rpc --> OS_KeyStore OS_Crypto_Server --> CryptoLib_Server CryptoLibClient -. "if_OS_Crypto" .- cryptoServer_rpc end
Please note that on the side of the application component, the
cryptoServer_rpc
endpoint is never accessed directly but always through
client libraries:
Functionality related to
if_OS_Crypto
is accessed through an instance of the Crypto API library which internally implements an RPC client for.Functionality related to
if_CryptoServer
is accessed throughCryptoServer_client
module, which internally implements an RPC client for.
Implementation¶
The CryptoServer maps all client requests to its internal data structure based on the client’s seL4 badge ID. The data structure is effectively an array with an entry for every client. The badge ID is used as an index into this array, to retrieve a client’s Keystore API and Crypto API context.
The CryptoServer does not allow the exporting of key material through
the Crypto API’s OS_Crypto_Key_export()
function.
This is to ensure that keys that are generated in the CryptoServer cannot leave
it and thus won’t be compromised.
The CryptoServer requires badges/IDs to be in the interval (101-108) and supports up to 8 clients.
Usage¶
This is how the component can be instantiated in the system.
Declaration of the Component in CMake¶
The CryptoServer can be instantiated via the following:
CryptoServer_DeclareCAmkESComponent(
<NameOfComponent>
)
In order to use the CryptoServer’s client interface, the respective component’s client code has to include it as part of its LIBS parameter:
DeclareCAmkESComponent(
<Client>
SOURCES
...
C_FLAGS
...
LIBS
...
os_crypto
<NameOfComponent>_client
)
Please note that os_crypto needs to be added as well, to be able to use the CryptoServer through the Crypto API.
Instantiation and Configuration in CAmkES¶
The CryptoServer offers two interfaces and uses another one, which is shown here.
Declaring the Component¶
The declaration of the CryptoServer works as follows:
#include "CryptoServer/camkes/CryptoServer.camkes"
CryptoServer_COMPONENT_DEFINE(
<NameOfComponent>
)
Instantiating and Connecting the Component¶
The CryptoServer uses if_OS_Entropy
and if_OS_Storage
, which need to
be set up before connecting any clients:
component <NameOfComponent> <nameOfInstance>;
CryptoServer_INSTANCE_CONNECT(
<nameOfInstance>,
<entropy>.<nameOfInterface>, <entropy>.<nameOfDataport>,
<storage>.<nameOfInterface>, <storage>.<nameOfDataport>
)
For its clients, the CryptoServer offers the if_OS_Crypto
and
if_CryptoServer
interfaces, which can be connected to the respective
clients as follows:
CryptoServer_INSTANCE_CONNECT_CLIENTS(
<nameOfInstance>,
<client1>.<nameOfInterface>, <client1>.<nameOfDataport>,
<client2>.<nameOfInterface>, <client2>.<nameOfDataport>,
...
)
This list can contain up to 8 clients.
Configuring the Instance0¶
The CryptoServer can be configured for each client and requires two parameters (unfortunately this cannot yet be done via a macro):
storageLimit:
sets the limit (in bytes) allowed by a client to be stored in its own KeyStore in the CryptoServer.allowedIds:
is a list of IDs that can access the (= load) keys owned by that particular client. Please note that each list ofallowedIds
needs to have as many entries as clients are connected to the CryptoServer. In order to “fill up” the list of IDs (in case a client’s keystore should not be accessible by all other clients) the value “0” should be added. Per convention, client IDs are assigned from 101 to 108, in the order in which clients are listed in theCryptoServer_CLIENT_ASSIGN_BADGES()
macro.
This config structure needs to have as many entries as there are clients
using the CryptoServer. The first entry in the config struct matches the
first client in the CryptoServer_INSTANCE_CONNECT_CLIENTS()
macro.
<nameOfInstance>.cryptoServer_config = {
"clients": [
{ "storageLimit": <sizeInBytes>, "allowedIds": [101,102,...] },
{ "storageLimit": <sizeInBytes>, "allowedIds": [...] },
...
]
};
The CryptoServer offers one RPC endpoint, for which the CAmkES badge ID needs to be assigned explicitly. For this, the following code can be used:
CryptoServer_CLIENT_ASSIGN_BADGES(
<client1>.<nameOfInterface>,
<client2>.<nameOfInterface>,
...
)
The order of assignment must be the same
for CryptoServer_INSTANCE_CONNECT_CLIENTS()
and
CryptoServer_CLIENT_ASSIGN_BADGES()
.
Example¶
We create an instance of the CryptoServer with three clients in this example.
Instantiation of the Component in CMake¶
We declare the CryptoServer in the CMakeLists.txt
as follows:
CryptoServer_DeclareCAmkESComponent(
MyCryptoServer
)
Assuming that the different clients are actually just instances of the same component, the following could be used:
DeclareCAmkESComponent(
Client
SOURCES
...
C_FLAGS
...
LIBS
...
os_crypto
MyCryptoServer_client
)
Instantiation and Configuration in CAmkES¶
Here we show how to set up the component in CAmkES.
Declaring the Component¶
The component is declared like this:
#include "CryptoServer/camkes/CryptoServer.camkes"
CryptoServer_COMPONENT_DEFINE(
MyCryptoServer
)
Instantiating and Connecting the Component¶
Here we see how to connect the CryptoServer with a RamDisk component, an EntropySource component and three clients:
// Instantiate CryptoServer
component MyCryptoServer myCryptoServer;
// Instantiate storage + entropy
Component RamDisk storage;
Component EntropySource entropy;
// Instantiate clients
Component Client client1;
Component Client client2;
Component Client client3;
// Connect interfaces USED by CryptoServer
CryptoServer_INSTANCE_CONNECT(
myCryptoServer,
entropy.entropy_rpc, entropy.entropy_port,
storage.storage_rpc, storage.storage_port
)
// Connect interfaces PROVIDED by CryptoServer
CryptoServer_INSTANCE_CONNECT_CLIENTS(
myCryptoServer,
client1.server_rpc, client1.server_port,
client2.server_rpc, client2.server_port,
client3.server_rpc, client3.server_port
)
Configuring the Instance¶
The main configuration block needs an entry for each client. The specific configuration below allows all three clients to use 16KiB of KeyStore storage. For the first two clients, only the clients themselves can access their KeyStores, keys from the KeyStore of the third client can be accessed by all others.
myCryptoServer.cryptoServer_config = {
"clients": [
{ "storageLimit": 1024*16, "allowedIds": [101, 0, 0] },
{ "storageLimit": 1024*16, "allowedIds": [102, 0, 0] },
{ "storageLimit": 1024*16, "allowedIds": [101,102,103] },
]
};
Please note that the order of configuration must correspond to the order
in which the clients appear in the CryptoServer_INSTANCE_CONNECT_CLIENTS()
macro.
We use the CryptoServer_CLIENT_ASSIGN_BADGES()
macro to assign a seL4
badge to the client’s side of the RPC endpoint.
CryptoServer_CLIENT_ASSIGN_BADGES(
client1.server_rpc,
client2.server_rpc,
client3.server_rpc
)
Using the Component’s Interfaces in C¶
The following shows how to use the CryptoServer through an instance of the Crypto API and through its extra interface (for handling keys):
// For Crypto API
#include "OS_Crypto.h"
// For client library code of CryptoServer
#include "CryptoServer.h"
// For RPC and dataports
#include <camkes.h>
// CryptoServer client interface as defined in CAmkES
static const if_CryptoServer_t cryptoServer =
IF_CRYPTOSERVER_ASSIGN(server_rpc);
// Crypto API instance in "CLIENT" mode,
// using the RPC interface (and dataport) as defined in CAmkES
static OS_Crypto_Config_t cryptoCfg =
{
.mode = OS_Crypto_MODE_CLIENT,
.rpc = IF_OS_CRYPTO_ASSIGN(
server_rpc,
server_port)
};
// Main demo function
int run()
{
// Declare handles for API and objects
OS_Crypto_Handle_t hCrypto;
OS_CryptoKey_Handle_t hKey;
OS_CryptoCipher_Handle_t hCipher;
// Declare inputs and outputs for crypto operation
uint8_t pt[16] = "0123456789abcdef";
uint8_t ct[16];
size_t ptLen = 16, ctLen = 16;
// Set up Crypto API in "CLIENT" mode to
// automatically switch between local and remote LIB instance
OS_Crypto_init(&hCrypto, &cryptoCfg);
// Load key with name "root key" owned by ID=0
CryptoServer_loadKey(&cryptoServer, &hKey, hCrypto, 0, "root key");
// Use newly loaded key for some crypto operation
OS_CryptoCipher_init(&hCipher, hCrypto, hKey, OS_CryptoCipher_ALG_AES_ECB_ENC, NULL, 0);
OS_CryptoCipher_process(hCipher, pt, ptLen, ct, &ctLen);
OS_CryptoCipher_free(hCipher);
// Free key and API if it is no longer needed
OS_CryptoKey_free(hKey);
OS_Crypto_free(hCrypto);
}
We can see here that the same CAmkES endpoint (myCryptoServer_rpc
) is
referenced to assign the if_CryptoServer
interface to cryptoServer
,
as well as to assign if_OS_Crypto
to cryptoCfg.rpc
.