Skip to content

Commit

Permalink
KEYS: trusted: Introduce TEE based Trusted Keys
Browse files Browse the repository at this point in the history
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
b49020 authored and jarkkojs committed Apr 14, 2021
1 parent 5d0682b commit 0a95ebc
Show file tree
Hide file tree
Showing 4 changed files with 340 additions and 0 deletions.
16 changes: 16 additions & 0 deletions include/keys/trusted_tee.h
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
2 changes: 2 additions & 0 deletions security/keys/trusted-keys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ trusted-y += trusted_tpm1.o
$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
trusted-y += trusted_tpm2.o
trusted-y += tpm2key.asn1.o

trusted-$(CONFIG_TEE) += trusted_tee.o
4 changes: 4 additions & 0 deletions security/keys/trusted-keys/trusted_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <keys/user-type.h>
#include <keys/trusted-type.h>
#include <keys/trusted_tee.h>
#include <keys/trusted_tpm.h>
#include <linux/capability.h>
#include <linux/err.h>
Expand All @@ -29,6 +30,9 @@ static const struct trusted_key_source trusted_key_sources[] = {
#if defined(CONFIG_TCG_TPM)
{ "tpm", &trusted_key_tpm_ops },
#endif
#if defined(CONFIG_TEE)
{ "tee", &trusted_key_tee_ops },
#endif
};

DEFINE_STATIC_CALL_NULL(trusted_key_init, *trusted_key_sources[0].ops->init);
Expand Down
318 changes: 318 additions & 0 deletions security/keys/trusted-keys/trusted_tee.c
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(&param, 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(&param, 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(&param, 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,
};

0 comments on commit 0a95ebc

Please sign in to comment.