mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
313 lines
7.5 KiB
313 lines
7.5 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) 2019-2021 Linaro Ltd. |
|
* |
|
* Author: |
|
* Sumit Garg <[email protected]> |
|
*/ |
|
|
|
#include <linux/err.h> |
|
#include <linux/key-type.h> |
|
#include <linux/module.h> |
|
#include <linux/slab.h> |
|
#include <linux/string.h> |
|
#include <linux/tee_drv.h> |
|
#include <linux/uuid.h> |
|
|
|
#include <keys/trusted_tee.h> |
|
|
|
#define DRIVER_NAME "trusted-key-tee" |
|
|
|
/* |
|
* Get random data for symmetric key |
|
* |
|
* [out] memref[0] Random data |
|
*/ |
|
#define TA_CMD_GET_RANDOM 0x0 |
|
|
|
/* |
|
* Seal trusted key using hardware unique key |
|
* |
|
* [in] memref[0] Plain key |
|
* [out] memref[1] Sealed key datablob |
|
*/ |
|
#define TA_CMD_SEAL 0x1 |
|
|
|
/* |
|
* Unseal trusted key using hardware unique key |
|
* |
|
* [in] memref[0] Sealed key datablob |
|
* [out] memref[1] Plain key |
|
*/ |
|
#define TA_CMD_UNSEAL 0x2 |
|
|
|
/** |
|
* struct trusted_key_tee_private - TEE Trusted key private data |
|
* @dev: TEE based Trusted key device. |
|
* @ctx: TEE context handler. |
|
* @session_id: Trusted key TA session identifier. |
|
* @shm_pool: Memory pool shared with TEE device. |
|
*/ |
|
struct trusted_key_tee_private { |
|
struct device *dev; |
|
struct tee_context *ctx; |
|
u32 session_id; |
|
struct tee_shm *shm_pool; |
|
}; |
|
|
|
static struct trusted_key_tee_private pvt_data; |
|
|
|
/* |
|
* Have the TEE seal(encrypt) the symmetric key |
|
*/ |
|
static int trusted_tee_seal(struct trusted_key_payload *p, char *datablob) |
|
{ |
|
int ret; |
|
struct tee_ioctl_invoke_arg inv_arg; |
|
struct tee_param param[4]; |
|
struct tee_shm *reg_shm_in = NULL, *reg_shm_out = NULL; |
|
|
|
memset(&inv_arg, 0, sizeof(inv_arg)); |
|
memset(¶m, 0, sizeof(param)); |
|
|
|
reg_shm_in = tee_shm_register_kernel_buf(pvt_data.ctx, p->key, |
|
p->key_len); |
|
if (IS_ERR(reg_shm_in)) { |
|
dev_err(pvt_data.dev, "key shm register failed\n"); |
|
return PTR_ERR(reg_shm_in); |
|
} |
|
|
|
reg_shm_out = tee_shm_register_kernel_buf(pvt_data.ctx, p->blob, |
|
sizeof(p->blob)); |
|
if (IS_ERR(reg_shm_out)) { |
|
dev_err(pvt_data.dev, "blob shm register failed\n"); |
|
ret = PTR_ERR(reg_shm_out); |
|
goto out; |
|
} |
|
|
|
inv_arg.func = TA_CMD_SEAL; |
|
inv_arg.session = pvt_data.session_id; |
|
inv_arg.num_params = 4; |
|
|
|
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; |
|
param[0].u.memref.shm = reg_shm_in; |
|
param[0].u.memref.size = p->key_len; |
|
param[0].u.memref.shm_offs = 0; |
|
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; |
|
param[1].u.memref.shm = reg_shm_out; |
|
param[1].u.memref.size = sizeof(p->blob); |
|
param[1].u.memref.shm_offs = 0; |
|
|
|
ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); |
|
if ((ret < 0) || (inv_arg.ret != 0)) { |
|
dev_err(pvt_data.dev, "TA_CMD_SEAL invoke err: %x\n", |
|
inv_arg.ret); |
|
ret = -EFAULT; |
|
} else { |
|
p->blob_len = param[1].u.memref.size; |
|
} |
|
|
|
out: |
|
if (reg_shm_out) |
|
tee_shm_free(reg_shm_out); |
|
if (reg_shm_in) |
|
tee_shm_free(reg_shm_in); |
|
|
|
return ret; |
|
} |
|
|
|
/* |
|
* Have the TEE unseal(decrypt) the symmetric key |
|
*/ |
|
static int trusted_tee_unseal(struct trusted_key_payload *p, char *datablob) |
|
{ |
|
int ret; |
|
struct tee_ioctl_invoke_arg inv_arg; |
|
struct tee_param param[4]; |
|
struct tee_shm *reg_shm_in = NULL, *reg_shm_out = NULL; |
|
|
|
memset(&inv_arg, 0, sizeof(inv_arg)); |
|
memset(¶m, 0, sizeof(param)); |
|
|
|
reg_shm_in = tee_shm_register_kernel_buf(pvt_data.ctx, p->blob, |
|
p->blob_len); |
|
if (IS_ERR(reg_shm_in)) { |
|
dev_err(pvt_data.dev, "blob shm register failed\n"); |
|
return PTR_ERR(reg_shm_in); |
|
} |
|
|
|
reg_shm_out = tee_shm_register_kernel_buf(pvt_data.ctx, p->key, |
|
sizeof(p->key)); |
|
if (IS_ERR(reg_shm_out)) { |
|
dev_err(pvt_data.dev, "key shm register failed\n"); |
|
ret = PTR_ERR(reg_shm_out); |
|
goto out; |
|
} |
|
|
|
inv_arg.func = TA_CMD_UNSEAL; |
|
inv_arg.session = pvt_data.session_id; |
|
inv_arg.num_params = 4; |
|
|
|
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; |
|
param[0].u.memref.shm = reg_shm_in; |
|
param[0].u.memref.size = p->blob_len; |
|
param[0].u.memref.shm_offs = 0; |
|
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; |
|
param[1].u.memref.shm = reg_shm_out; |
|
param[1].u.memref.size = sizeof(p->key); |
|
param[1].u.memref.shm_offs = 0; |
|
|
|
ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); |
|
if ((ret < 0) || (inv_arg.ret != 0)) { |
|
dev_err(pvt_data.dev, "TA_CMD_UNSEAL invoke err: %x\n", |
|
inv_arg.ret); |
|
ret = -EFAULT; |
|
} else { |
|
p->key_len = param[1].u.memref.size; |
|
} |
|
|
|
out: |
|
if (reg_shm_out) |
|
tee_shm_free(reg_shm_out); |
|
if (reg_shm_in) |
|
tee_shm_free(reg_shm_in); |
|
|
|
return ret; |
|
} |
|
|
|
/* |
|
* Have the TEE generate random symmetric key |
|
*/ |
|
static int trusted_tee_get_random(unsigned char *key, size_t key_len) |
|
{ |
|
int ret; |
|
struct tee_ioctl_invoke_arg inv_arg; |
|
struct tee_param param[4]; |
|
struct tee_shm *reg_shm = NULL; |
|
|
|
memset(&inv_arg, 0, sizeof(inv_arg)); |
|
memset(¶m, 0, sizeof(param)); |
|
|
|
reg_shm = tee_shm_register_kernel_buf(pvt_data.ctx, key, key_len); |
|
if (IS_ERR(reg_shm)) { |
|
dev_err(pvt_data.dev, "key shm register failed\n"); |
|
return PTR_ERR(reg_shm); |
|
} |
|
|
|
inv_arg.func = TA_CMD_GET_RANDOM; |
|
inv_arg.session = pvt_data.session_id; |
|
inv_arg.num_params = 4; |
|
|
|
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; |
|
param[0].u.memref.shm = reg_shm; |
|
param[0].u.memref.size = key_len; |
|
param[0].u.memref.shm_offs = 0; |
|
|
|
ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); |
|
if ((ret < 0) || (inv_arg.ret != 0)) { |
|
dev_err(pvt_data.dev, "TA_CMD_GET_RANDOM invoke err: %x\n", |
|
inv_arg.ret); |
|
ret = -EFAULT; |
|
} else { |
|
ret = param[0].u.memref.size; |
|
} |
|
|
|
tee_shm_free(reg_shm); |
|
|
|
return ret; |
|
} |
|
|
|
static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) |
|
{ |
|
if (ver->impl_id == TEE_IMPL_ID_OPTEE) |
|
return 1; |
|
else |
|
return 0; |
|
} |
|
|
|
static int trusted_key_probe(struct device *dev) |
|
{ |
|
struct tee_client_device *rng_device = to_tee_client_device(dev); |
|
int ret; |
|
struct tee_ioctl_open_session_arg sess_arg; |
|
|
|
memset(&sess_arg, 0, sizeof(sess_arg)); |
|
|
|
pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, |
|
NULL); |
|
if (IS_ERR(pvt_data.ctx)) |
|
return -ENODEV; |
|
|
|
memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN); |
|
sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; |
|
sess_arg.num_params = 0; |
|
|
|
ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL); |
|
if ((ret < 0) || (sess_arg.ret != 0)) { |
|
dev_err(dev, "tee_client_open_session failed, err: %x\n", |
|
sess_arg.ret); |
|
ret = -EINVAL; |
|
goto out_ctx; |
|
} |
|
pvt_data.session_id = sess_arg.session; |
|
|
|
ret = register_key_type(&key_type_trusted); |
|
if (ret < 0) |
|
goto out_sess; |
|
|
|
pvt_data.dev = dev; |
|
|
|
return 0; |
|
|
|
out_sess: |
|
tee_client_close_session(pvt_data.ctx, pvt_data.session_id); |
|
out_ctx: |
|
tee_client_close_context(pvt_data.ctx); |
|
|
|
return ret; |
|
} |
|
|
|
static int trusted_key_remove(struct device *dev) |
|
{ |
|
unregister_key_type(&key_type_trusted); |
|
tee_client_close_session(pvt_data.ctx, pvt_data.session_id); |
|
tee_client_close_context(pvt_data.ctx); |
|
|
|
return 0; |
|
} |
|
|
|
static const struct tee_client_device_id trusted_key_id_table[] = { |
|
{UUID_INIT(0xf04a0fe7, 0x1f5d, 0x4b9b, |
|
0xab, 0xf7, 0x61, 0x9b, 0x85, 0xb4, 0xce, 0x8c)}, |
|
{} |
|
}; |
|
MODULE_DEVICE_TABLE(tee, trusted_key_id_table); |
|
|
|
static struct tee_client_driver trusted_key_driver = { |
|
.id_table = trusted_key_id_table, |
|
.driver = { |
|
.name = DRIVER_NAME, |
|
.bus = &tee_bus_type, |
|
.probe = trusted_key_probe, |
|
.remove = trusted_key_remove, |
|
}, |
|
}; |
|
|
|
static int trusted_tee_init(void) |
|
{ |
|
return driver_register(&trusted_key_driver.driver); |
|
} |
|
|
|
static void trusted_tee_exit(void) |
|
{ |
|
driver_unregister(&trusted_key_driver.driver); |
|
} |
|
|
|
struct trusted_key_ops trusted_key_tee_ops = { |
|
.migratable = 0, /* non-migratable */ |
|
.init = trusted_tee_init, |
|
.seal = trusted_tee_seal, |
|
.unseal = trusted_tee_unseal, |
|
.get_random = trusted_tee_get_random, |
|
.exit = trusted_tee_exit, |
|
};
|
|
|