blob: 40bbe6ac7d20949536500a23ca6f85939aa9898a [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (C) 2019-2020, Linaro Limited
*/
#include <assert.h>
#include <pta_system.h>
#include <string.h>
#include <string_ext.h>
#include <tee_internal_api.h>
#include <tee_internal_api_extensions.h>
#include <trusted_keys.h>
#include <util.h>
#define IV_SIZE 16
#define TAG_SIZE 16
#define MAX_BUF_SIZE 512
/*
* Acronym:
*
* TK - Trusted Key
*/
struct tk_blob_hdr {
uint8_t reserved;
uint8_t iv[IV_SIZE];
uint8_t tag[TAG_SIZE];
uint8_t enc_key[];
};
static TEE_Result get_random(uint32_t types, TEE_Param params[TEE_NUM_PARAMS])
{
uint8_t *rng_buf = NULL;
DMSG("Invoked TA_CMD_GET_RANDOM");
if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE))
return TEE_ERROR_BAD_PARAMETERS;
if (!params[0].memref.buffer || !params[0].memref.size)
return TEE_ERROR_BAD_PARAMETERS;
rng_buf = TEE_Malloc(params[0].memref.size, TEE_MALLOC_FILL_ZERO);
if (!rng_buf)
return TEE_ERROR_OUT_OF_MEMORY;
TEE_GenerateRandom(rng_buf, params[0].memref.size);
memcpy(params[0].memref.buffer, rng_buf, params[0].memref.size);
memzero_explicit(rng_buf, params[0].memref.size);
TEE_Free(rng_buf);
return TEE_SUCCESS;
}
static TEE_Result derive_unique_key(uint8_t *key, uint16_t key_size,
uint8_t *extra, uint16_t extra_size)
{
TEE_TASessionHandle sess = TEE_HANDLE_NULL;
TEE_Param params[TEE_NUM_PARAMS] = { };
TEE_Result res = TEE_ERROR_GENERIC;
uint32_t ret_orig = 0;
uint32_t param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_MEMREF_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
res = TEE_OpenTASession(&(const TEE_UUID)PTA_SYSTEM_UUID,
TEE_TIMEOUT_INFINITE, 0, NULL, &sess,
&ret_orig);
if (res)
return res;
if (extra && extra_size) {
params[0].memref.buffer = extra;
params[0].memref.size = extra_size;
}
params[1].memref.buffer = key;
params[1].memref.size = key_size;
res = TEE_InvokeTACommand(sess, TEE_TIMEOUT_INFINITE,
PTA_SYSTEM_DERIVE_TA_UNIQUE_KEY,
param_types, params, &ret_orig);
TEE_CloseTASession(sess);
return res;
}
static TEE_Result huk_ae_encrypt(TEE_OperationHandle crypto_op, uint8_t *in,
uint32_t in_sz, uint8_t *out, uint32_t *out_sz)
{
TEE_Result res = TEE_ERROR_GENERIC;
struct tk_blob_hdr *hdr = (struct tk_blob_hdr *)out;
uint8_t iv[IV_SIZE] = { 0 };
uint32_t enc_key_len = in_sz;
uint32_t tag_len = TAG_SIZE;
hdr->reserved = 0;
TEE_GenerateRandom(iv, IV_SIZE);
memcpy(hdr->iv, iv, IV_SIZE);
res = TEE_AEInit(crypto_op, hdr->iv, IV_SIZE, TAG_SIZE * 8, 0, 0);
if (res)
return res;
res = TEE_AEEncryptFinal(crypto_op, in, in_sz, hdr->enc_key,
&enc_key_len, hdr->tag, &tag_len);
if (res || tag_len != TAG_SIZE)
return TEE_ERROR_SECURITY;
if (ADD_OVERFLOW(enc_key_len, sizeof(*hdr), out_sz))
return TEE_ERROR_SECURITY;
return res;
}
static TEE_Result huk_ae_decrypt(TEE_OperationHandle crypto_op, uint8_t *in,
uint32_t in_sz, uint8_t *out, uint32_t *out_sz)
{
TEE_Result res = TEE_ERROR_GENERIC;
struct tk_blob_hdr *hdr = (struct tk_blob_hdr *)in;
uint8_t tag[TAG_SIZE] = { 0 };
uint32_t enc_key_len = 0;
if (SUB_OVERFLOW(in_sz, sizeof(*hdr), &enc_key_len))
return TEE_ERROR_SECURITY;
res = TEE_AEInit(crypto_op, hdr->iv, IV_SIZE, TAG_SIZE * 8, 0, 0);
if (res)
return res;
memcpy(tag, hdr->tag, TAG_SIZE);
res = TEE_AEDecryptFinal(crypto_op, hdr->enc_key, enc_key_len, out,
out_sz, tag, TAG_SIZE);
if (res)
res = TEE_ERROR_SECURITY;
return res;
}
static TEE_Result huk_crypt(TEE_OperationMode mode, uint8_t *in, uint32_t in_sz,
uint8_t *out, uint32_t *out_sz)
{
TEE_Result res = TEE_ERROR_GENERIC;
TEE_OperationHandle crypto_op = TEE_HANDLE_NULL;
TEE_ObjectHandle hkey = TEE_HANDLE_NULL;
uint8_t huk_key[TA_DERIVED_KEY_MAX_SIZE] = { };
TEE_Attribute attr = { };
res = TEE_AllocateOperation(&crypto_op, TEE_ALG_AES_GCM, mode,
sizeof(huk_key) * 8);
if (res)
return res;
res = derive_unique_key(huk_key, sizeof(huk_key), NULL, 0);
if (res) {
EMSG("derive_unique_key failed: returned %#"PRIx32, res);
goto out_op;
}
res = TEE_AllocateTransientObject(TEE_TYPE_AES, sizeof(huk_key) * 8,
&hkey);
if (res)
goto out_op;
attr.attributeID = TEE_ATTR_SECRET_VALUE;
attr.content.ref.buffer = huk_key;
attr.content.ref.length = sizeof(huk_key);
res = TEE_PopulateTransientObject(hkey, &attr, 1);
if (res)
goto out_key;
res = TEE_SetOperationKey(crypto_op, hkey);
if (res)
goto out_key;
if (mode == TEE_MODE_ENCRYPT) {
res = huk_ae_encrypt(crypto_op, in, in_sz, out, out_sz);
if (res)
EMSG("huk_AE_encrypt failed: returned %#"PRIx32, res);
} else if (mode == TEE_MODE_DECRYPT) {
res = huk_ae_decrypt(crypto_op, in, in_sz, out, out_sz);
if (res)
EMSG("huk_AE_decrypt failed: returned %#"PRIx32, res);
} else {
TEE_Panic(0);
}
out_key:
TEE_FreeTransientObject(hkey);
out_op:
TEE_FreeOperation(crypto_op);
memzero_explicit(huk_key, sizeof(huk_key));
return res;
}
static TEE_Result seal_trusted_key(uint32_t types,
TEE_Param params[TEE_NUM_PARAMS])
{
TEE_Result res = TEE_SUCCESS;
uint8_t *in = NULL;
uint32_t in_sz = 0;
uint8_t *out = NULL;
uint32_t out_sz = 0;
DMSG("Invoked TA_CMD_SEAL");
if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_MEMREF_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE))
return TEE_ERROR_BAD_PARAMETERS;
in = params[0].memref.buffer;
in_sz = params[0].memref.size;
out = params[1].memref.buffer;
out_sz = params[1].memref.size;
if (!in || !in_sz || in_sz > MAX_BUF_SIZE)
return TEE_ERROR_BAD_PARAMETERS;
if ((!out && out_sz) ||
(out && !IS_ALIGNED_WITH_TYPE(out, struct tk_blob_hdr)) ||
out_sz > MAX_BUF_SIZE)
return TEE_ERROR_BAD_PARAMETERS;
if ((in_sz + sizeof(struct tk_blob_hdr)) > out_sz) {
params[1].memref.size = in_sz + sizeof(struct tk_blob_hdr);
return TEE_ERROR_SHORT_BUFFER;
}
res = huk_crypt(TEE_MODE_ENCRYPT, in, in_sz, out, &out_sz);
if (res == TEE_SUCCESS) {
assert(out_sz == in_sz + sizeof(struct tk_blob_hdr));
params[1].memref.size = out_sz;
}
return res;
}
static TEE_Result unseal_trusted_key(uint32_t types,
TEE_Param params[TEE_NUM_PARAMS])
{
TEE_Result res = TEE_SUCCESS;
uint8_t *in = NULL;
uint32_t in_sz = 0;
uint8_t *out = NULL;
uint32_t out_sz = 0;
DMSG("Invoked TA_CMD_UNSEAL");
if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_MEMREF_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE))
return TEE_ERROR_BAD_PARAMETERS;
in = params[0].memref.buffer;
in_sz = params[0].memref.size;
out = params[1].memref.buffer;
out_sz = params[1].memref.size;
if (!in || !IS_ALIGNED_WITH_TYPE(in, struct tk_blob_hdr) ||
in_sz <= sizeof(struct tk_blob_hdr) || in_sz > MAX_BUF_SIZE)
return TEE_ERROR_BAD_PARAMETERS;
if ((!out && out_sz) || out_sz > MAX_BUF_SIZE)
return TEE_ERROR_BAD_PARAMETERS;
if (in_sz > (out_sz + sizeof(struct tk_blob_hdr))) {
params[1].memref.size = in_sz - sizeof(struct tk_blob_hdr);
return TEE_ERROR_SHORT_BUFFER;
}
res = huk_crypt(TEE_MODE_DECRYPT, in, in_sz, out, &out_sz);
if (res == TEE_SUCCESS) {
assert(out_sz == in_sz - sizeof(struct tk_blob_hdr));
params[1].memref.size = out_sz;
}
return res;
}
TEE_Result TA_CreateEntryPoint(void)
{
return TEE_SUCCESS;
}
void TA_DestroyEntryPoint(void)
{
}
TEE_Result TA_OpenSessionEntryPoint(uint32_t pt __unused,
TEE_Param params[TEE_NUM_PARAMS] __unused,
void **session __unused)
{
TEE_Result res = TEE_ERROR_GENERIC;
TEE_PropSetHandle h = TEE_HANDLE_NULL;
TEE_Identity id = { };
res = TEE_AllocatePropertyEnumerator(&h);
if (res)
goto out;
TEE_StartPropertyEnumerator(h, TEE_PROPSET_CURRENT_CLIENT);
res = TEE_GetPropertyAsIdentity(h, NULL, &id);
if (res)
goto out;
if (id.login != TEE_LOGIN_REE_KERNEL)
res = TEE_ERROR_ACCESS_DENIED;
out:
if (h)
TEE_FreePropertyEnumerator(h);
return res;
}
void TA_CloseSessionEntryPoint(void *sess __unused)
{
}
TEE_Result TA_InvokeCommandEntryPoint(void *sess __unused, uint32_t cmd,
uint32_t pt,
TEE_Param params[TEE_NUM_PARAMS])
{
switch (cmd) {
case TA_CMD_GET_RANDOM:
return get_random(pt, params);
case TA_CMD_SEAL:
return seal_trusted_key(pt, params);
case TA_CMD_UNSEAL:
return unseal_trusted_key(pt, params);
default:
EMSG("Command ID %#"PRIx32" is not supported", cmd);
return TEE_ERROR_NOT_SUPPORTED;
}
}