KeyStore API

Overview

The Keystore API allows to store and load keys identified by memorable names. The keys might be created or used by the Crypto API.

The Keystore offers a range of functions for the handling of keys (defined in OS_Keystore.h):

  • store key,

  • load key,

  • delete key,

  • copy key from one Keystore to another,

  • move key from one Keystore to another,

  • wipe the Keystore.

The Keystore API is not limited to a fixed number implementations but is designed to be extensible with additional implementations. The TRENTOS SDK already contains two different Keystore implementations:

  • KeystoreFile which uses a file based storage backend and

  • KeystoreRamFV which stores the keys in RAM and whose code is formally verified.

The class diagram below shows the current implementations available with their dependencies. KeystoreFile has a dependency on a FileSystem handle (to store/load keys) and a Crypto handle (to hash the keys) while KeystoreRamFV has no dependency.

        classDiagram
    class OS_Keystore {
        +free()
        +storeKey()
        +loadKey()
        +deleteKey()
        +copyKey()
        +moveKey()
        +wipeKeystore()
    }
    class OS_KeystoreFile {
        +init()
    }
    class OS_KeystoreRamFV {
        +init()
    }

    class OS_FileSystem
    note for OS_FileSystem "Store/load the keys"

    class OS_Crypto
    note for OS_Crypto "Hash the keys"

    OS_Keystore <|-- OS_KeystoreFile : implements
    OS_Keystore <|-- OS_KeystoreRamFV : implements
    OS_KeystoreFile o-- OS_FileSystem : uses
    OS_KeystoreFile o-- OS_Crypto : uses
    

The Keystore implementations are separate libraries which must be linked into the application that wants to use them. Individual initialization routines for the different implementations need to be called by the user application before the Keystore API can be used. The init functions are designed to return an  OS_Keystore_Handle_t value (an abstract reference to the object initialized) that can be used with the Keystore API.

Info: Dependent on the support for encryption or integrity protection of a certain Keystore implementation it needs to be checked that keys cannot be extracted and/or corrupted (e.g. by using only internal/sealed memories).

The following chapter will give more detailed information about the included Keystore implementations.

Implementations

KeystoreFile

KeystoreFile is an implementation that performs storage by using an instance of the FileSystem API, it writes opaque buffers of cryptographic material (as exported from the Crypto API) into a file, for which the KeystoreFile needs a handle to a mounted file system. Conversely, it can also load keys back into memory.

Every KeystoreFile instance has an “instance name” (set during the instantiation of the KeystoreFile), which allows having several instances of the KeystoreFile on the same file system. Each key is stored in its own file and the file name is constructed from a Keystore’s “instance name” and the respective “key name”.

Info: Using different instances of the KeystoreFile with the same file system requires each instance to have a unique instance name. Otherwise, these instances might interfere with each other’s files.

Info: The isolation between two KeystoreFile instances using the same piece of storage and the same file system is weak. If one instance needs to be separated from another instance, each instance should have its own piece of storage (disjunctive ranges of storage can be assigned via the StorageServer).

Usage

This is how the API can be instantiated in the system.

Declaration of API Library in CMake

There is a single build target for using the Keystore API with the KeystoreFile implementation, which needs to be included for every component that wants to use it.

  • os_keystore_file: Generic build target to include the KeystoreFile implementation

Additionally, the KeystoreFile will require crypto and file system, so the user must include the respective build targets as well.

Example

Here we show how to use the Keystore API with a simple example.

Instantiation of API in CMake

Here we see how a component can use the build target of the library to include it (besides crypto and file system):

DeclareCAmkESComponent(
    Client
    SOURCES
        ...
    C_FLAGS
        ...
    LIBS
        ...
        os_crypto
        os_filesystem
        os_keystore_file
)
Using the API in C

In the following example, we show how to set up the Keystore API with the KeystoreFile implementation and a file system and crypto instance, how to load a key (assuming it exists) and how to import it into the crypto instance.

Many implementation aspects of this example (configs of crypto, file system) have been omitted. Please refer to the respective chapters in the handbook for further information. Please also note that you need to include OS_KeystoreFile.h in the source file in which you call OS_KeystoreFile_init(). The init function of the Keystore is implementation specific and therefore not part of the Keystore API.

#include "OS_KeystoreFile.h"

...

int run()
{
    OS_FileSystem_Handle_t hFs;
    OS_Crypto_Handle_t hCrypto;
    OS_Keystore_Handle_t hKeys;
    OS_CryptoKey_Data_t data;
    OS_CryptoKey_Handle_t hMyKey;

    // Set up filesystem
    OS_FileSystem_init(&hFs, &cfgFs);
    OS_FileSystem_mount(hFs);

    // Set up crypto instance and keystore
    OS_Crypto_init(&hCrypto, &cfgCrypto);

    OS_KeystoreFile_init(&hKeys, hFs, hCrypto, "myKeystore");

    // Load a key with name "myKey" from keystore and import it into crypto instance
    OS_Keystore_loadKey(hKeys, "myKey", &data, &dataLen);
    OS_CryptoKey_import(&hMyKey, hCrypto, &data);

    // Use key for crypto operations
    ...
}

KeystoreRamFV

KeystoreRamFV is a formally verified implementation that performs storage in a RAM. The implementation expects to receive a pointer to a memory buffer and the buffer size. The macro OS_KeystoreRamFV_SIZE_OF_BUFFER(num_elements) helps to convert the desired number of keys to the appropriate buffer size.

Info: This implementation stores the keys without additional hashing.

Info: There is no persistence of the keys after a power cycle or after an init() - free() cycle.

Info: The formal verification (done with Isabelle theorem proof assistant, see https://isabelle.in.tum.de/) has been performed on the Keystore implementation core functions. Additional information is available from HENSOLDT Cyber on request.

Usage

This is how the API can be instantiated in the system.

Declaration of API Library in CMake

There is a single build target for using the Keystore API with the KeystoreRamFV implementation, which needs to be included for every component that wants to use it.

  • os_keystore_ram_fv: Generic build target to include the KeystoreRamFV implementation.

Example

Here we show how to use the Keystore API with a simple example.

Instantiation of API in CMake

Here we see how a component can use the build target of the library to include it:

DeclareCAmkESComponent(
    Client
    SOURCES
        ...
    C_FLAGS
        ...
    LIBS
        ...
        os_keystore_ram_fv
)
Using the API in C

In the following example, we show how to set up the Keystore API with KeystoreRamFV implementation, how to generate a key with the crypto instance, store the key and load it later with the Keystore and use the key to encrypt some text with the crypto instance.

Many implementation aspects of this example (configs of crypto) have been omitted. Please refer to the respective chapters in the handbook for further information. Please also note that you need to include OS_KeystoreRamFV.h in the source file in which you call OS_KeystoreRamFV_init(). The init function of the Keystore is implementation specific and therefore not part of the Keystore API.

#include "OS_KeystoreRamFV.h"

...

// Let's say we will need to store 10 keys
#define KEYSTORE_NUM_ELEMENTS 10

// The following is to define the size of the RAM buffer that we will use to
// initialize the KeystoreRamFV object.
// By using the macro OS_KeystoreRamFV_SIZE_OF_BUFFER() we can obtain the amount
// of memory in bytes starting from the number of elements.
#define KEYSTORE_RAM_BUF_SIZE OS_KeystoreRamFV_SIZE_OF_BUFFER(KEYSTORE_NUM_ELEMENTS)

#define PLAIN_TEXT \
\
"\"O frati\", dissi, \"che per cento milia \
perigli siete giunti a l'occidente, \
a questa tanto picciola vigilia \
\
d'i nostri sensi ch'e' del rimanente \
non vogliate negar l'esperienza, \
di retro al sol, del mondo sanza gente. \
\
Considerate la vostra semenza: \
fatti non foste a viver come bruti, \
ma per seguir virtute e canoscenza\""

static const OS_CryptoKey_Spec_t aes256Spec =
{
    .type = OS_CryptoKey_SPECTYPE_BITS,
    .key = {
        .type = OS_CryptoKey_TYPE_AES,
        .attribs.keepLocal = true,
        .params.bits = 256
    }
};

int run()
{
    OS_Crypto_Handle_t hCrypto;
    OS_Keystore_Handle_t hKeys;
    OS_CryptoKey_Data_t data;
    OS_CryptoKey_Handle_t hMyKey;
    OS_CryptoCipher_Handle_t hCipher;

    char keystoreRamBuf[KEYSTORE_RAM_BUF_SIZE];
    char bufEncText[sizeof(PLAIN_TEXT)] = {0};
    size_t sizeEncText = 0;

    // Set up crypto instance and keystore
    OS_Crypto_init(&hCrypto, &cfgCrypto);
    OS_KeystoreRamFV_init(&hKeys, keystoreRamBuf, KEYSTORE_RAM_BUF_SIZE);

    // Generate an AES key
    OS_CryptoKey_generate(&hMyKey, hCrypto, &aes256Spec);

    // Export the key data into a temporary variable
    OS_CryptoKey_export(hMyKey, &data);

    // Store the key with name "myKey" to keystore
    OS_Keystore_storeKey(hKeys, "myKey", &data, sizeof(data));

    // Application performs something
    ...

    // Load the key with name "myKey" from keystore and import it into crypto instance
    OS_Keystore_loadKey(hKeys, "myKey", &data, &dataLen);
    OS_CryptoKey_import(&hMyKey, hCrypto, &data);

    // Encrypt the plaint text
    sizeEncText = sizeof(bufEncText);
    OS_CryptoCipher_init(&hCipher, hCrypto, hMyKey, OS_CryptoCipher_ALG_AES_ECB_ENC, NULL, 0);
    OS_CryptoCipher_process(hCipher, PLAIN_TEXT, sizeof(PLAIN_TEXT), bufEncText, &sizeEncText);
    OS_CryptoCipher_free(hCipher);

    // Application performs something
    ...
}