forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
KEYS: trusted: Introduce TEE based Trusted Keys
Add support for TEE based trusted keys where TEE provides the functionality to seal and unseal trusted keys using hardware unique key. Refer to Documentation/staging/tee.rst for detailed information about TEE. Signed-off-by: Sumit Garg <[email protected]> Tested-by: Jarkko Sakkinen <[email protected]> Reviewed-by: Jarkko Sakkinen <[email protected]> Signed-off-by: Jarkko Sakkinen <[email protected]>
- Loading branch information
Showing
4 changed files
with
340 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* Copyright (C) 2019-2021 Linaro Ltd. | ||
* | ||
* Author: | ||
* Sumit Garg <[email protected]> | ||
*/ | ||
|
||
#ifndef __TEE_TRUSTED_KEY_H | ||
#define __TEE_TRUSTED_KEY_H | ||
|
||
#include <keys/trusted-type.h> | ||
|
||
extern struct trusted_key_ops trusted_key_tee_ops; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,318 @@ | ||
// 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(pvt_data.ctx, (unsigned long)p->key, | ||
p->key_len, TEE_SHM_DMA_BUF | | ||
TEE_SHM_KERNEL_MAPPED); | ||
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(pvt_data.ctx, (unsigned long)p->blob, | ||
sizeof(p->blob), TEE_SHM_DMA_BUF | | ||
TEE_SHM_KERNEL_MAPPED); | ||
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(pvt_data.ctx, (unsigned long)p->blob, | ||
p->blob_len, TEE_SHM_DMA_BUF | | ||
TEE_SHM_KERNEL_MAPPED); | ||
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(pvt_data.ctx, (unsigned long)p->key, | ||
sizeof(p->key), TEE_SHM_DMA_BUF | | ||
TEE_SHM_KERNEL_MAPPED); | ||
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(pvt_data.ctx, (unsigned long)key, key_len, | ||
TEE_SHM_DMA_BUF | TEE_SHM_KERNEL_MAPPED); | ||
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, | ||
}; |