/* * Copyright (c) 2017, Linaro Limited * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #define AES128_KEY_BIT_SIZE 128 #define AES128_KEY_BYTE_SIZE (AES128_KEY_BIT_SIZE / 8) #define AES256_KEY_BIT_SIZE 256 #define AES256_KEY_BYTE_SIZE (AES256_KEY_BIT_SIZE / 8) /* * Ciphering context: each opened session relates to a cipehring operation. * - configure the AES flavour from a command. * - load key from a command (here the key is provided by the REE) * - reset init vector (here IV is provided by the REE) * - cipher a buffer frame (here input and output buffers are non-secure) */ struct aes_cipher { uint32_t algo; /* AES flavour */ uint32_t mode; /* Encode or decode */ uint32_t key_size; /* AES key size in byte */ TEE_OperationHandle op_handle; /* AES ciphering operation */ TEE_ObjectHandle key_handle; /* transient object to load the key */ }; /* * Few routines to convert IDs from TA API into IDs from OP-TEE. */ static TEE_Result ta2tee_algo_id(uint32_t param, uint32_t *algo) { switch (param) { case TA_AES_ALGO_ECB: *algo = TEE_ALG_AES_ECB_NOPAD; return TEE_SUCCESS; case TA_AES_ALGO_CBC: *algo = TEE_ALG_AES_CBC_NOPAD; return TEE_SUCCESS; case TA_AES_ALGO_CTR: *algo = TEE_ALG_AES_CTR; return TEE_SUCCESS; default: EMSG("Invalid algo %u", param); return TEE_ERROR_BAD_PARAMETERS; } } static TEE_Result ta2tee_key_size(uint32_t param, uint32_t *key_size) { switch (param) { case AES128_KEY_BYTE_SIZE: case AES256_KEY_BYTE_SIZE: *key_size = param; return TEE_SUCCESS; default: EMSG("Invalid key size %u", param); return TEE_ERROR_BAD_PARAMETERS; } } static TEE_Result ta2tee_mode_id(uint32_t param, uint32_t *mode) { switch (param) { case TA_AES_MODE_ENCODE: *mode = TEE_MODE_ENCRYPT; return TEE_SUCCESS; case TA_AES_MODE_DECODE: *mode = TEE_MODE_DECRYPT; return TEE_SUCCESS; default: EMSG("Invalid mode %u", param); return TEE_ERROR_BAD_PARAMETERS; } } /* * Process command TA_AES_CMD_PREPARE. API in aes_ta.h * * Allocate resources required for the ciphering operation. * During ciphering operation, when expect client can: * - update the key materials (provided by client) * - reset the initial vector (provided by client) * - cipher an input buffer into an output buffer (provided by client) */ static TEE_Result alloc_resources(void *session, uint32_t param_types, TEE_Param params[4]) { const uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE); struct aes_cipher *sess; TEE_Attribute attr; TEE_Result res; char *key; /* Get ciphering context from session ID */ DMSG("Session %p: get ciphering resources", session); sess = (struct aes_cipher *)session; /* Safely get the invocation parameters */ if (param_types != exp_param_types) return TEE_ERROR_BAD_PARAMETERS; res = ta2tee_algo_id(params[0].value.a, &sess->algo); if (res != TEE_SUCCESS) return res; res = ta2tee_key_size(params[1].value.a, &sess->key_size); if (res != TEE_SUCCESS) return res; res = ta2tee_mode_id(params[2].value.a, &sess->mode); if (res != TEE_SUCCESS) return res; /* * Ready to allocate the resources which are: * - an operation handle, for an AES ciphering of given configuration * - a transient object that will be use to load the key materials * into the AES ciphering operation. */ /* Free potential previous operation */ if (sess->op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(sess->op_handle); /* Allocate operation: AES/CTR, mode and size from params */ res = TEE_AllocateOperation(&sess->op_handle, sess->algo, sess->mode, sess->key_size * 8); if (res != TEE_SUCCESS) { EMSG("Failed to allocate operation"); sess->op_handle = TEE_HANDLE_NULL; goto err; } /* Free potential previous transient object */ if (sess->key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(sess->key_handle); /* Allocate transient object according to target key size */ res = TEE_AllocateTransientObject(TEE_TYPE_AES, sess->key_size * 8, &sess->key_handle); if (res != TEE_SUCCESS) { EMSG("Failed to allocate transient object"); sess->key_handle = TEE_HANDLE_NULL; goto err; } /* * When loading a key in the cipher session, set_aes_key() * will reset the operation and load a key. But we cannot * reset and operation that has no key yet (GPD TEE Internal * Core API Specification – Public Release v1.1.1, section * 6.2.5 TEE_ResetOperation). In consequence, we will load a * dummy key in the operation so that operation can be reset * when updating the key. */ key = TEE_Malloc(sess->key_size, 0); if (!key) { res = TEE_ERROR_OUT_OF_MEMORY; goto err; } TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key, sess->key_size); res = TEE_PopulateTransientObject(sess->key_handle, &attr, 1); if (res != TEE_SUCCESS) { EMSG("TEE_PopulateTransientObject failed, %x", res); goto err; } res = TEE_SetOperationKey(sess->op_handle, sess->key_handle); if (res != TEE_SUCCESS) { EMSG("TEE_SetOperationKey failed %x", res); goto err; } return res; err: if (sess->op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(sess->op_handle); sess->op_handle = TEE_HANDLE_NULL; if (sess->key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(sess->key_handle); sess->key_handle = TEE_HANDLE_NULL; return res; } /* * Process command TA_AES_CMD_SET_KEY. API in aes_ta.h */ static TEE_Result set_aes_key(void *session, uint32_t param_types, TEE_Param params[4]) { const uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); struct aes_cipher *sess; TEE_Attribute attr; TEE_Result res; uint32_t key_sz; char *key; /* Get ciphering context from session ID */ DMSG("Session %p: load key material", session); sess = (struct aes_cipher *)session; /* Safely get the invocation parameters */ if (param_types != exp_param_types) return TEE_ERROR_BAD_PARAMETERS; key = params[0].memref.buffer; key_sz = params[0].memref.size; if (key_sz != sess->key_size) { EMSG("Wrong key size %" PRIu32 ", expect %" PRIu32 " bytes", key_sz, sess->key_size); return TEE_ERROR_BAD_PARAMETERS; } /* * Load the key material into the configured operation * - create a secret key attribute with the key material * TEE_InitRefAttribute() * - reset transient object and load attribute data * TEE_ResetTransientObject() * TEE_PopulateTransientObject() * - load the key (transient object) into the ciphering operation * TEE_SetOperationKey() * * TEE_SetOperationKey() requires operation to be in "initial state". * We can use TEE_ResetOperation() to reset the operation but this * API cannot be used on operation with key(s) not yet set. Hence, * when allocating the operation handle, we load a dummy key. * Thus, set_key sequence always reset then set key on operation. */ TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key, key_sz); TEE_ResetTransientObject(sess->key_handle); res = TEE_PopulateTransientObject(sess->key_handle, &attr, 1); if (res != TEE_SUCCESS) { EMSG("TEE_PopulateTransientObject failed, %x", res); return res; } TEE_ResetOperation(sess->op_handle); res = TEE_SetOperationKey(sess->op_handle, sess->key_handle); if (res != TEE_SUCCESS) { EMSG("TEE_SetOperationKey failed %x", res); return res; } return res; } /* * Process command TA_AES_CMD_SET_IV. API in aes_ta.h */ static TEE_Result reset_aes_iv(void *session, uint32_t param_types, TEE_Param params[4]) { const uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); struct aes_cipher *sess; size_t iv_sz; char *iv; /* Get ciphering context from session ID */ DMSG("Session %p: reset initial vector", session); sess = (struct aes_cipher *)session; /* Safely get the invocation parameters */ if (param_types != exp_param_types) return TEE_ERROR_BAD_PARAMETERS; iv = params[0].memref.buffer; iv_sz = params[0].memref.size; /* * Init cipher operation with the initialization vector. */ TEE_CipherInit(sess->op_handle, iv, iv_sz); return TEE_SUCCESS; } /* * Process command TA_AES_CMD_CIPHER. API in aes_ta.h */ static TEE_Result cipher_buffer(void *session, uint32_t param_types, TEE_Param params[4]) { const uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); struct aes_cipher *sess; /* Get ciphering context from session ID */ DMSG("Session %p: cipher buffer", session); sess = (struct aes_cipher *)session; /* Safely get the invocation parameters */ if (param_types != exp_param_types) return TEE_ERROR_BAD_PARAMETERS; if (params[1].memref.size < params[0].memref.size) { EMSG("Bad sizes: in %d, out %d", params[0].memref.size, params[1].memref.size); return TEE_ERROR_BAD_PARAMETERS; } if (sess->op_handle == TEE_HANDLE_NULL) return TEE_ERROR_BAD_STATE; /* * Process ciphering operation on provided buffers */ return TEE_CipherUpdate(sess->op_handle, params[0].memref.buffer, params[0].memref.size, params[1].memref.buffer, ¶ms[1].memref.size); } TEE_Result TA_CreateEntryPoint(void) { /* Nothing to do */ return TEE_SUCCESS; } void TA_DestroyEntryPoint(void) { /* Nothing to do */ } TEE_Result TA_OpenSessionEntryPoint(uint32_t __unused param_types, TEE_Param __unused params[4], void __unused **session) { struct aes_cipher *sess; /* * Allocate and init ciphering materials for the session. * The address of the structure is used as session ID for * the client. */ sess = TEE_Malloc(sizeof(*sess), 0); if (!sess) return TEE_ERROR_OUT_OF_MEMORY; sess->key_handle = TEE_HANDLE_NULL; sess->op_handle = TEE_HANDLE_NULL; *session = (void *)sess; DMSG("Session %p: newly allocated", *session); return TEE_SUCCESS; } void TA_CloseSessionEntryPoint(void *session) { struct aes_cipher *sess; /* Get ciphering context from session ID */ DMSG("Session %p: release session", session); sess = (struct aes_cipher *)session; /* Release the session resources */ if (sess->key_handle != TEE_HANDLE_NULL) TEE_FreeTransientObject(sess->key_handle); if (sess->op_handle != TEE_HANDLE_NULL) TEE_FreeOperation(sess->op_handle); TEE_Free(sess); } TEE_Result TA_InvokeCommandEntryPoint(void *session, uint32_t cmd, uint32_t param_types, TEE_Param params[4]) { switch (cmd) { case TA_AES_CMD_PREPARE: return alloc_resources(session, param_types, params); case TA_AES_CMD_SET_KEY: return set_aes_key(session, param_types, params); case TA_AES_CMD_SET_IV: return reset_aes_iv(session, param_types, params); case TA_AES_CMD_CIPHER: return cipher_buffer(session, param_types, params); default: EMSG("Command ID 0x%x is not supported", cmd); return TEE_ERROR_NOT_SUPPORTED; } }