1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2019-2021 Linaro Ltd. |
4 | * |
5 | * Author: |
6 | * Sumit Garg <sumit.garg@linaro.org> |
7 | */ |
8 | |
9 | #include <linux/err.h> |
10 | #include <linux/key-type.h> |
11 | #include <linux/module.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/string.h> |
14 | #include <linux/tee_drv.h> |
15 | #include <linux/uuid.h> |
16 | |
17 | #include <keys/trusted_tee.h> |
18 | |
19 | #define DRIVER_NAME "trusted-key-tee" |
20 | |
21 | /* |
22 | * Get random data for symmetric key |
23 | * |
24 | * [out] memref[0] Random data |
25 | */ |
26 | #define TA_CMD_GET_RANDOM 0x0 |
27 | |
28 | /* |
29 | * Seal trusted key using hardware unique key |
30 | * |
31 | * [in] memref[0] Plain key |
32 | * [out] memref[1] Sealed key datablob |
33 | */ |
34 | #define TA_CMD_SEAL 0x1 |
35 | |
36 | /* |
37 | * Unseal trusted key using hardware unique key |
38 | * |
39 | * [in] memref[0] Sealed key datablob |
40 | * [out] memref[1] Plain key |
41 | */ |
42 | #define TA_CMD_UNSEAL 0x2 |
43 | |
44 | /** |
45 | * struct trusted_key_tee_private - TEE Trusted key private data |
46 | * @dev: TEE based Trusted key device. |
47 | * @ctx: TEE context handler. |
48 | * @session_id: Trusted key TA session identifier. |
49 | * @shm_pool: Memory pool shared with TEE device. |
50 | */ |
51 | struct trusted_key_tee_private { |
52 | struct device *dev; |
53 | struct tee_context *ctx; |
54 | u32 session_id; |
55 | struct tee_shm *shm_pool; |
56 | }; |
57 | |
58 | static struct trusted_key_tee_private pvt_data; |
59 | |
60 | /* |
61 | * Have the TEE seal(encrypt) the symmetric key |
62 | */ |
63 | static int trusted_tee_seal(struct trusted_key_payload *p, char *datablob) |
64 | { |
65 | int ret; |
66 | struct tee_ioctl_invoke_arg inv_arg; |
67 | struct tee_param param[4]; |
68 | struct tee_shm *reg_shm = NULL; |
69 | |
70 | memset(&inv_arg, 0, sizeof(inv_arg)); |
71 | memset(¶m, 0, sizeof(param)); |
72 | |
73 | reg_shm = tee_shm_register_kernel_buf(ctx: pvt_data.ctx, addr: p->key, |
74 | length: sizeof(p->key) + sizeof(p->blob)); |
75 | if (IS_ERR(ptr: reg_shm)) { |
76 | dev_err(pvt_data.dev, "shm register failed\n" ); |
77 | return PTR_ERR(ptr: reg_shm); |
78 | } |
79 | |
80 | inv_arg.func = TA_CMD_SEAL; |
81 | inv_arg.session = pvt_data.session_id; |
82 | inv_arg.num_params = 4; |
83 | |
84 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; |
85 | param[0].u.memref.shm = reg_shm; |
86 | param[0].u.memref.size = p->key_len; |
87 | param[0].u.memref.shm_offs = 0; |
88 | param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; |
89 | param[1].u.memref.shm = reg_shm; |
90 | param[1].u.memref.size = sizeof(p->blob); |
91 | param[1].u.memref.shm_offs = sizeof(p->key); |
92 | |
93 | ret = tee_client_invoke_func(ctx: pvt_data.ctx, arg: &inv_arg, param); |
94 | if ((ret < 0) || (inv_arg.ret != 0)) { |
95 | dev_err(pvt_data.dev, "TA_CMD_SEAL invoke err: %x\n" , |
96 | inv_arg.ret); |
97 | ret = -EFAULT; |
98 | } else { |
99 | p->blob_len = param[1].u.memref.size; |
100 | } |
101 | |
102 | tee_shm_free(shm: reg_shm); |
103 | |
104 | return ret; |
105 | } |
106 | |
107 | /* |
108 | * Have the TEE unseal(decrypt) the symmetric key |
109 | */ |
110 | static int trusted_tee_unseal(struct trusted_key_payload *p, char *datablob) |
111 | { |
112 | int ret; |
113 | struct tee_ioctl_invoke_arg inv_arg; |
114 | struct tee_param param[4]; |
115 | struct tee_shm *reg_shm = NULL; |
116 | |
117 | memset(&inv_arg, 0, sizeof(inv_arg)); |
118 | memset(¶m, 0, sizeof(param)); |
119 | |
120 | reg_shm = tee_shm_register_kernel_buf(ctx: pvt_data.ctx, addr: p->key, |
121 | length: sizeof(p->key) + sizeof(p->blob)); |
122 | if (IS_ERR(ptr: reg_shm)) { |
123 | dev_err(pvt_data.dev, "shm register failed\n" ); |
124 | return PTR_ERR(ptr: reg_shm); |
125 | } |
126 | |
127 | inv_arg.func = TA_CMD_UNSEAL; |
128 | inv_arg.session = pvt_data.session_id; |
129 | inv_arg.num_params = 4; |
130 | |
131 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; |
132 | param[0].u.memref.shm = reg_shm; |
133 | param[0].u.memref.size = p->blob_len; |
134 | param[0].u.memref.shm_offs = sizeof(p->key); |
135 | param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; |
136 | param[1].u.memref.shm = reg_shm; |
137 | param[1].u.memref.size = sizeof(p->key); |
138 | param[1].u.memref.shm_offs = 0; |
139 | |
140 | ret = tee_client_invoke_func(ctx: pvt_data.ctx, arg: &inv_arg, param); |
141 | if ((ret < 0) || (inv_arg.ret != 0)) { |
142 | dev_err(pvt_data.dev, "TA_CMD_UNSEAL invoke err: %x\n" , |
143 | inv_arg.ret); |
144 | ret = -EFAULT; |
145 | } else { |
146 | p->key_len = param[1].u.memref.size; |
147 | } |
148 | |
149 | tee_shm_free(shm: reg_shm); |
150 | |
151 | return ret; |
152 | } |
153 | |
154 | /* |
155 | * Have the TEE generate random symmetric key |
156 | */ |
157 | static int trusted_tee_get_random(unsigned char *key, size_t key_len) |
158 | { |
159 | int ret; |
160 | struct tee_ioctl_invoke_arg inv_arg; |
161 | struct tee_param param[4]; |
162 | struct tee_shm *reg_shm = NULL; |
163 | |
164 | memset(&inv_arg, 0, sizeof(inv_arg)); |
165 | memset(¶m, 0, sizeof(param)); |
166 | |
167 | reg_shm = tee_shm_register_kernel_buf(ctx: pvt_data.ctx, addr: key, length: key_len); |
168 | if (IS_ERR(ptr: reg_shm)) { |
169 | dev_err(pvt_data.dev, "key shm register failed\n" ); |
170 | return PTR_ERR(ptr: reg_shm); |
171 | } |
172 | |
173 | inv_arg.func = TA_CMD_GET_RANDOM; |
174 | inv_arg.session = pvt_data.session_id; |
175 | inv_arg.num_params = 4; |
176 | |
177 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; |
178 | param[0].u.memref.shm = reg_shm; |
179 | param[0].u.memref.size = key_len; |
180 | param[0].u.memref.shm_offs = 0; |
181 | |
182 | ret = tee_client_invoke_func(ctx: pvt_data.ctx, arg: &inv_arg, param); |
183 | if ((ret < 0) || (inv_arg.ret != 0)) { |
184 | dev_err(pvt_data.dev, "TA_CMD_GET_RANDOM invoke err: %x\n" , |
185 | inv_arg.ret); |
186 | ret = -EFAULT; |
187 | } else { |
188 | ret = param[0].u.memref.size; |
189 | } |
190 | |
191 | tee_shm_free(shm: reg_shm); |
192 | |
193 | return ret; |
194 | } |
195 | |
196 | static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) |
197 | { |
198 | if (ver->impl_id == TEE_IMPL_ID_OPTEE && |
199 | ver->gen_caps & TEE_GEN_CAP_REG_MEM) |
200 | return 1; |
201 | else |
202 | return 0; |
203 | } |
204 | |
205 | static int trusted_key_probe(struct device *dev) |
206 | { |
207 | struct tee_client_device *rng_device = to_tee_client_device(dev); |
208 | int ret; |
209 | struct tee_ioctl_open_session_arg sess_arg; |
210 | |
211 | memset(&sess_arg, 0, sizeof(sess_arg)); |
212 | |
213 | pvt_data.ctx = tee_client_open_context(NULL, match: optee_ctx_match, NULL, |
214 | NULL); |
215 | if (IS_ERR(ptr: pvt_data.ctx)) |
216 | return -ENODEV; |
217 | |
218 | memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN); |
219 | sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; |
220 | sess_arg.num_params = 0; |
221 | |
222 | ret = tee_client_open_session(ctx: pvt_data.ctx, arg: &sess_arg, NULL); |
223 | if ((ret < 0) || (sess_arg.ret != 0)) { |
224 | dev_err(dev, "tee_client_open_session failed, err: %x\n" , |
225 | sess_arg.ret); |
226 | ret = -EINVAL; |
227 | goto out_ctx; |
228 | } |
229 | pvt_data.session_id = sess_arg.session; |
230 | |
231 | ret = register_key_type(ktype: &key_type_trusted); |
232 | if (ret < 0) |
233 | goto out_sess; |
234 | |
235 | pvt_data.dev = dev; |
236 | |
237 | return 0; |
238 | |
239 | out_sess: |
240 | tee_client_close_session(ctx: pvt_data.ctx, session: pvt_data.session_id); |
241 | out_ctx: |
242 | tee_client_close_context(ctx: pvt_data.ctx); |
243 | |
244 | return ret; |
245 | } |
246 | |
247 | static int trusted_key_remove(struct device *dev) |
248 | { |
249 | unregister_key_type(ktype: &key_type_trusted); |
250 | tee_client_close_session(ctx: pvt_data.ctx, session: pvt_data.session_id); |
251 | tee_client_close_context(ctx: pvt_data.ctx); |
252 | |
253 | return 0; |
254 | } |
255 | |
256 | static const struct tee_client_device_id trusted_key_id_table[] = { |
257 | {UUID_INIT(0xf04a0fe7, 0x1f5d, 0x4b9b, |
258 | 0xab, 0xf7, 0x61, 0x9b, 0x85, 0xb4, 0xce, 0x8c)}, |
259 | {} |
260 | }; |
261 | MODULE_DEVICE_TABLE(tee, trusted_key_id_table); |
262 | |
263 | static struct tee_client_driver trusted_key_driver = { |
264 | .id_table = trusted_key_id_table, |
265 | .driver = { |
266 | .name = DRIVER_NAME, |
267 | .bus = &tee_bus_type, |
268 | .probe = trusted_key_probe, |
269 | .remove = trusted_key_remove, |
270 | }, |
271 | }; |
272 | |
273 | static int trusted_tee_init(void) |
274 | { |
275 | return driver_register(drv: &trusted_key_driver.driver); |
276 | } |
277 | |
278 | static void trusted_tee_exit(void) |
279 | { |
280 | driver_unregister(drv: &trusted_key_driver.driver); |
281 | } |
282 | |
283 | struct trusted_key_ops trusted_key_tee_ops = { |
284 | .migratable = 0, /* non-migratable */ |
285 | .init = trusted_tee_init, |
286 | .seal = trusted_tee_seal, |
287 | .unseal = trusted_tee_unseal, |
288 | .get_random = trusted_tee_get_random, |
289 | .exit = trusted_tee_exit, |
290 | }; |
291 | |