// SPDX-License-Identifier: BSD-2-Clause
|
/*
|
* Copyright (c) 2015, Linaro Limited
|
* All rights reserved.
|
*/
|
|
#include <tee_internal_api_extensions.h>
|
#include <tee_internal_api.h>
|
#include <tee_ta_api.h>
|
#include <string.h>
|
#include <trace.h>
|
|
#include "ta_aes_perf.h"
|
#include "ta_aes_perf_priv.h"
|
|
#define CHECK(res, name, action) do { \
|
if ((res) != TEE_SUCCESS) { \
|
DMSG(name ": 0x%08x", (res)); \
|
action \
|
} \
|
} while(0)
|
|
#define TAG_LEN 128
|
|
static uint8_t iv[] = { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
|
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF };
|
static int use_iv;
|
|
static TEE_OperationHandle crypto_op = NULL;
|
static uint32_t algo;
|
|
static bool is_inbuf_a_secure_memref(TEE_Param *param)
|
{
|
TEE_Result res = TEE_ERROR_GENERIC;
|
|
/*
|
* Check secure attribute for the referenced buffer
|
* Trust core on validity of the memref size: test only 1st byte
|
* instead of the overall buffer, and if it's not secure, assume
|
* the buffer is nonsecure.
|
*/
|
res = TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_ANY_OWNER |
|
TEE_MEMORY_ACCESS_READ |
|
TEE_MEMORY_ACCESS_SECURE,
|
param->memref.buffer, 1);
|
return (res == TEE_SUCCESS);
|
}
|
|
static bool is_outbuf_a_secure_memref(TEE_Param *param)
|
{
|
TEE_Result res = TEE_ERROR_GENERIC;
|
|
/*
|
* Check secure attribute for the referenced buffer
|
* Trust core on validity of the memref size: test only 1st byte
|
* instead of the overall buffer, and if it's not secure, assume
|
* the buffer is nonsecure.
|
*/
|
res = TEE_CheckMemoryAccessRights(TEE_MEMORY_ACCESS_ANY_OWNER |
|
TEE_MEMORY_ACCESS_WRITE |
|
TEE_MEMORY_ACCESS_SECURE,
|
param->memref.buffer, 1);
|
return (res == TEE_SUCCESS);
|
}
|
|
#if defined(CFG_CACHE_API)
|
static TEE_Result flush_memref_buffer(TEE_Param *param)
|
{
|
TEE_Result res = TEE_ERROR_GENERIC;
|
|
res = TEE_CacheFlush(param->memref.buffer,
|
param->memref.size);
|
CHECK(res, "TEE_CacheFlush(in)", return res;);
|
return res;
|
}
|
#else
|
static __maybe_unused TEE_Result flush_memref_buffer(TEE_Param *param __unused)
|
{
|
return TEE_SUCCESS;
|
}
|
#endif /* CFG_CACHE_API */
|
|
TEE_Result cmd_process(uint32_t param_types,
|
TEE_Param params[TEE_NUM_PARAMS],
|
bool use_sdp)
|
{
|
TEE_Result res = TEE_ERROR_GENERIC;
|
int n = 0;
|
int unit = 0;
|
void *in = NULL;
|
void *out = NULL;
|
uint32_t insz = 0;
|
uint32_t outsz = 0;
|
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT,
|
TEE_PARAM_TYPE_MEMREF_INOUT,
|
TEE_PARAM_TYPE_VALUE_INPUT,
|
TEE_PARAM_TYPE_NONE);
|
bool secure_in = false;
|
bool secure_out = false;
|
TEE_Result (*do_update)(TEE_OperationHandle, const void *, uint32_t,
|
void *, uint32_t *) = NULL;
|
|
if (param_types != exp_param_types)
|
return TEE_ERROR_BAD_PARAMETERS;
|
|
if (use_sdp) {
|
/*
|
* Whatever is expected as memory reference, it is mandatory
|
* for SDP aware trusted applications of safely indentify all
|
* memory reference parameters. Hence these tests must be part
|
* of the performance test setup.
|
*/
|
secure_in = is_inbuf_a_secure_memref(¶ms[0]);
|
secure_out = is_outbuf_a_secure_memref(¶ms[1]);
|
|
/*
|
* We could invalidate only the caches. We prefer to flush
|
* them in case 2 sub-buffers are accessed by TAs from a single
|
* allocated SDP memory buffer, and those are not cache-aligned.
|
* Invalidating might cause data loss in cache lines. Hence
|
* rather flush them all before accessing (in read or write).
|
*/
|
if (secure_in) {
|
res = flush_memref_buffer(¶ms[0]);
|
CHECK(res, "pre-flush in memref param", return res;);
|
}
|
if (secure_out) {
|
res = flush_memref_buffer(¶ms[1]);
|
CHECK(res, "pre-flush out memref param", return res;);
|
}
|
}
|
|
in = params[0].memref.buffer;
|
insz = params[0].memref.size;
|
out = params[1].memref.buffer;
|
outsz = params[1].memref.size;
|
n = params[2].value.a;
|
unit = params[2].value.b;
|
if (!unit)
|
unit = insz;
|
|
if (algo == TEE_ALG_AES_GCM)
|
do_update = TEE_AEUpdate;
|
else
|
do_update = TEE_CipherUpdate;
|
|
while (n--) {
|
uint32_t i = 0;
|
for (i = 0; i < insz / unit; i++) {
|
res = do_update(crypto_op, in, unit, out, &outsz);
|
CHECK(res, "TEE_CipherUpdate/TEE_AEUpdate", return res;);
|
in = (void *)((uintptr_t)in + unit);
|
out = (void *)((uintptr_t)out + unit);
|
}
|
if (insz % unit) {
|
res = do_update(crypto_op, in, insz % unit, out, &outsz);
|
CHECK(res, "TEE_CipherUpdate/TEE_AEUpdate", return res;);
|
}
|
}
|
|
if (secure_out) {
|
/* intentionally flush output data from cache for SDP buffers */
|
res = flush_memref_buffer(¶ms[1]);
|
CHECK(res, "post-flush out memref param", return res;);
|
}
|
|
return TEE_SUCCESS;
|
}
|
|
TEE_Result cmd_prepare_key(uint32_t param_types, TEE_Param params[4])
|
{
|
TEE_Result res = TEE_ERROR_GENERIC;
|
TEE_ObjectHandle hkey = TEE_HANDLE_NULL;
|
TEE_ObjectHandle hkey2 = TEE_HANDLE_NULL;
|
TEE_Attribute attr = { };
|
uint32_t mode = 0;
|
uint32_t op_keysize = 0;
|
uint32_t keysize = 0;
|
const uint8_t *ivp = NULL;
|
size_t ivlen = 0;
|
static uint8_t aes_key[] = { 0x00, 0x01, 0x02, 0x03,
|
0x04, 0x05, 0x06, 0x07,
|
0x08, 0x09, 0x0A, 0x0B,
|
0x0C, 0x0D, 0x0E, 0x0F,
|
0x10, 0x11, 0x12, 0x13,
|
0x14, 0x15, 0x16, 0x17,
|
0x18, 0x19, 0x1A, 0x1B,
|
0x1C, 0x1D, 0x1E, 0x1F };
|
static uint8_t aes_key2[] = { 0x20, 0x21, 0x22, 0x23,
|
0x24, 0x25, 0x26, 0x27,
|
0x28, 0x29, 0x2A, 0x2B,
|
0x2C, 0x2D, 0x2E, 0x2F,
|
0x30, 0x31, 0x32, 0x33,
|
0x34, 0x35, 0x36, 0x37,
|
0x38, 0x39, 0x3A, 0x3B,
|
0x3C, 0x3D, 0x3E, 0x3F };
|
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
|
TEE_PARAM_TYPE_VALUE_INPUT,
|
TEE_PARAM_TYPE_NONE,
|
TEE_PARAM_TYPE_NONE);
|
|
if (param_types != exp_param_types)
|
return TEE_ERROR_BAD_PARAMETERS;
|
|
mode = params[0].value.a ? TEE_MODE_DECRYPT : TEE_MODE_ENCRYPT;
|
keysize = params[0].value.b;
|
op_keysize = keysize;
|
|
switch (params[1].value.a) {
|
case TA_AES_ECB:
|
algo = TEE_ALG_AES_ECB_NOPAD;
|
use_iv = 0;
|
break;
|
case TA_AES_CBC:
|
algo = TEE_ALG_AES_CBC_NOPAD;
|
use_iv = 1;
|
break;
|
case TA_AES_CTR:
|
algo = TEE_ALG_AES_CTR;
|
use_iv = 1;
|
break;
|
case TA_AES_XTS:
|
algo = TEE_ALG_AES_XTS;
|
use_iv = 1;
|
op_keysize *= 2;
|
break;
|
case TA_AES_GCM:
|
algo = TEE_ALG_AES_GCM;
|
use_iv = 1;
|
break;
|
default:
|
return TEE_ERROR_BAD_PARAMETERS;
|
}
|
|
cmd_clean_res();
|
|
res = TEE_AllocateOperation(&crypto_op, algo, mode, op_keysize);
|
CHECK(res, "TEE_AllocateOperation", return res;);
|
|
res = TEE_AllocateTransientObject(TEE_TYPE_AES, keysize, &hkey);
|
CHECK(res, "TEE_AllocateTransientObject", return res;);
|
|
attr.attributeID = TEE_ATTR_SECRET_VALUE;
|
attr.content.ref.buffer = aes_key;
|
attr.content.ref.length = keysize / 8;
|
|
res = TEE_PopulateTransientObject(hkey, &attr, 1);
|
CHECK(res, "TEE_PopulateTransientObject", return res;);
|
|
if (algo == TEE_ALG_AES_XTS) {
|
res = TEE_AllocateTransientObject(TEE_TYPE_AES, keysize,
|
&hkey2);
|
CHECK(res, "TEE_AllocateTransientObject", return res;);
|
|
attr.content.ref.buffer = aes_key2;
|
|
res = TEE_PopulateTransientObject(hkey2, &attr, 1);
|
CHECK(res, "TEE_PopulateTransientObject", return res;);
|
|
res = TEE_SetOperationKey2(crypto_op, hkey, hkey2);
|
CHECK(res, "TEE_SetOperationKey2", return res;);
|
|
TEE_FreeTransientObject(hkey2);
|
} else {
|
res = TEE_SetOperationKey(crypto_op, hkey);
|
CHECK(res, "TEE_SetOperationKey", return res;);
|
}
|
|
TEE_FreeTransientObject(hkey);
|
|
if (use_iv) {
|
ivp = iv;
|
ivlen = sizeof(iv);
|
} else {
|
ivp = NULL;
|
ivlen = 0;
|
}
|
|
if (algo == TEE_ALG_AES_GCM) {
|
return TEE_AEInit(crypto_op, ivp, ivlen, TAG_LEN, 0, 0);
|
} else {
|
TEE_CipherInit(crypto_op, ivp, ivlen);
|
return TEE_SUCCESS;
|
}
|
}
|
|
void cmd_clean_res(void)
|
{
|
if (crypto_op)
|
TEE_FreeOperation(crypto_op);
|
}
|