Cryptographic implementation

This document describes how the TEE Cryptographic Operations API is implemented, how the default crypto provider may be configured at compile time, and how it may be replaced by another implementation.

Overview

There are several layers from the Trusted Application to the actual crypto algorithms. Most of the crypto code runs in kernel mode inside the TEE core. Here is a schematic view of a typical call to the crypto API. The numbers in square brackets ([1], [2]…) refer to the sections below.

-   some_function()                             (Trusted App) -
[1]   TEE_*()                      User space   (libutee.a)
------- utee_*() ----------------------------------------------
[2]       tee_svc_*()              Kernel space
[3]         crypto_*()                          (libtomcrypt.a and crypto.c)
[4]           /* LibTomCrypt */                 (libtomcrypt.a)

[1] The TEE Cryptographic Operations API

OP-TEE implements the Cryptographic Operations API defined by the GlobalPlatform association in the TEE Internal Core API. This includes cryptographic functions that span various cryptographic needs: message digests, symmetric ciphers, message authentication codes (MAC), authenticated encryption, asymmetric operations (encryption/decryption or signing/verifying), key derivation, and random data generation. These functions make up the TEE Cryptographic Operations API.

The Internal API is implemented in tee_api_operations.c, which is compiled into a static library: ${O}/ta_arm{32,64}-lib/libutee/libutee.a.

Most API functions perform some parameter checking and manipulations, then invoke some utee_* function to switch to kernel mode and perform the low-level work.

The utee_* functions are declared in utee_syscalls.h and implemented in utee_syscalls_asm.S They are simple system call wrappers which use the SVC instruction to switch to the appropriate system service in the OP-TEE kernel.

[2] The crypto services

All cryptography-related system calls are declared in tee_svc_cryp.h and implemented in tee_svc_cryp.c. In addition to dealing with the usual work required at the user/kernel interface (checking parameters and copying memory buffers between user and kernel space), the system calls invoke a private abstraction layer: the Crypto API, which is declared in crypto.h. It serves two main purposes:

  1. Allow for alternative implementations, such as hardware-accelerated versions.
  2. Provide an easy way to disable some families of algorithms at compile-time to save space. See LibTomCrypt below.

[3] crypto_*()

The crypto_*() functions implement the actual algorithms and helper functions. TEE Core has one global active implementation of this interface. The default implementation, mostly based on LibTomCrypt, is as follows:

File: core/crypto/crypto.c
/*
 * Default implementation for all functions in crypto.h
 */

#if !defined(_CFG_CRYPTO_WITH_HASH)
TEE_Result crypto_hash_get_ctx_size(uint32_t algo __unused,
                                    size_t *size __unused)
{
        return TEE_ERROR_NOT_IMPLEMENTED;
}
...
#endif /*_CFG_CRYPTO_WITH_HASH*/
File: core/lib/libtomcrypt/tee_ltc_provider.c
#if defined(_CFG_CRYPTO_WITH_HASH)
TEE_Result crypto_hash_get_ctx_size(uint32_t algo, size_t *size)
{
    /* ... */
    return TEE_SUCCESS;
}

#endif /*_CFG_CRYPTO_WITH_HASH*/

As shown above, families of algorithms can be disabled and crypto.c will provide default null implementations that will return TEE_ERROR_NOT_IMPLEMENTED.

Public/private key format

crypto.h uses implementation-specific types to hold key data for asymmetric algorithms. For instance, here is how a public RSA key is represented:

File: core/include/crypto/crypto.h
struct rsa_public_key {
    struct bignum *e;       /* Public exponent */
    struct bignum *n;       /* Modulus */
};

This is also how such keys are stored inside the TEE object attributes (TEE_ATTR_RSA_PUBLIC_KEY in this case). struct bignum is an opaque type, known to the underlying implementation only. struct bignum_ops provides functions so that the system services can manipulate data of this type. This includes allocation/deallocation, copy, and conversion to or from the big endian binary format.

File: core/include/crypto/crypto.h
struct bignum *crypto_bignum_allocate(size_t size_bits);

TEE_Result crypto_bignum_bin2bn(const uint8_t *from, size_t fromsize,
                struct bignum *to);

void crypto_bignum_bn2bin(const struct bignum *from, uint8_t *to);
/*...*/

[4] LibTomCrypt

Some algorithms may be disabled at compile time if they are not needed, in order to reduce the size of the OP-TEE image and reduces its memory usage. This is done by setting the appropriate configuration variable. For example:

$ make CFG_CRYPTO_AES=n              # disable AES only
$ make CFG_CRYPTO_{AES,DES}=n        # disable symmetric ciphers
$ make CFG_CRYPTO_{DSA,RSA,DH,ECC}=n # disable public key algorithms
$ make CFG_CRYPTO=n                  # disable all algorithms

Please refer to core/lib/libtomcrypt/sub.mk for the list of all supported variables.

Note that the application interface is not modified when algorithms are disabled. This means, for instance, that the functions TEE_CipherInit(), TEE_CipherUpdate() and TEE_CipherFinal() would remain present in libutee.a even if all symmetric ciphers are disabled (they would simply return TEE_ERROR_NOT_IMPLEMENTED).

Add a new software based crypto implementation

To add a new software based implementation, the default one in core/lib/libtomcrypt in combination with what is in core/crypto should be used as a reference. Here are the main things to consider when adding a new crypto provider:

  • Put all the new code in its own directory under core/lib unless it is code that will be used regardless of which crypto provider is in use. How we are dealing with AES-GCM in core/crypto could serve as an example.
  • Avoid modifying tee_svc_cryp.c. It should not be needed.
  • Although not all crypto families need to be defined, all are required for compliance to the GlobalPlatform specification.
  • If you intend to make some algorithms optional, please try to re-use the same names for configuration variables as the default implementation.

[5] Support for crypto IC

Some cryptographic co-processors and secure elements are supported under a Generic Cryptographic Driver interface, connecting the TEE Crypto generic APIs to the HW driver interface. This interface is in core/drivers/crypto/crypto_api and should be followed when adding support for new devices.

At the time of writing, OP-TEE does not support the GP TEE Secure Element API and therefore the access to the secure element - the NXP EdgeLock® SE05x - follows the Cryptographic Operations API presenting a single session to the device. This session is shared with the normal world through the PKCS#11 interface but also through a more generic interface (libseetec) which allows clients to send Application Protocol Data Units (APDUs) directly to the device.

Notice that cryptographic co-processors do not necessarily comply with all the GP requirements tested and covered by the OP-TEE sanity test suite (optee_test). In those cases where the cryptographic operations are not supported - i.e: the SE05x does not implement all RSA key sizes - we opted for disabling those particular tests at build time rather than letting them fail.