1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2022, Linaro Limited
4 */
5
6 #include <assert.h>
7 #include <drivers/tpm2_chip.h>
8 #include <drivers/tpm2_cmd.h>
9 #include <io.h>
10 #include <kernel/panic.h>
11 #include <malloc.h>
12 #include <string.h>
13 #include <tpm2.h>
14 #include <trace.h>
15
tpm2_cmd_init_hdr(void * buf,uint32_t len,uint16_t tag,uint32_t cmd_code)16 static void tpm2_cmd_init_hdr(void *buf, uint32_t len, uint16_t tag,
17 uint32_t cmd_code)
18 {
19 struct tpm2_cmd *cmd = (struct tpm2_cmd *)buf;
20
21 assert(len >= sizeof(*cmd));
22
23 put_unaligned_be16(&cmd->hdr.tag, tag);
24 put_unaligned_be32(&cmd->hdr.size, sizeof(struct tpm2_cmd_hdr));
25 put_unaligned_be32(&cmd->hdr.code, cmd_code);
26 }
27
tpm2_cmd_add(void * buf,uint32_t len,uint8_t * val,uint32_t val_len)28 static void tpm2_cmd_add(void *buf, uint32_t len, uint8_t *val,
29 uint32_t val_len)
30 {
31 struct tpm2_cmd *cmd = (struct tpm2_cmd *)buf;
32 uint32_t cmd_len = tpm2_cmd_len(cmd);
33
34 assert(len >= (cmd_len + val_len));
35
36 memcpy(&cmd->data[cmd_len - sizeof(struct tpm2_cmd_hdr)], val, val_len);
37
38 cmd_len += val_len;
39 put_unaligned_be32(&cmd->hdr.size, cmd_len);
40 }
41
tpm2_cmd_add_u8(void * buf,uint32_t len,uint8_t val)42 static void tpm2_cmd_add_u8(void *buf, uint32_t len, uint8_t val)
43 {
44 tpm2_cmd_add(buf, len, &val, 1);
45 }
46
tpm2_cmd_add_u16(void * buf,uint32_t len,uint16_t val)47 static void tpm2_cmd_add_u16(void *buf, uint32_t len, uint16_t val)
48 {
49 uint16_t val_be = TEE_U16_FROM_BIG_ENDIAN(val);
50
51 tpm2_cmd_add(buf, len, (uint8_t *)&val_be, 2);
52 }
53
tpm2_cmd_add_u32(void * buf,uint32_t len,uint32_t val)54 static void tpm2_cmd_add_u32(void *buf, uint32_t len, uint32_t val)
55 {
56 uint32_t val_be = TEE_U32_FROM_BIG_ENDIAN(val);
57
58 tpm2_cmd_add(buf, len, (uint8_t *)&val_be, 4);
59 }
60
61 /* Timeout has been picked up from Table 17 - Command Timing */
tpm2_get_cmd_duration(uint32_t cmd_code)62 static uint32_t tpm2_get_cmd_duration(uint32_t cmd_code)
63 {
64 switch (cmd_code) {
65 case TPM2_CC_STARTUP:
66 case TPM2_CC_PCR_EXTEND:
67 case TPM2_CC_GET_CAPABILITY:
68 return TPM2_CMD_DURATION_MEDIUM;
69 case TPM2_CC_SELFTEST:
70 case TPM2_CC_NV_READ:
71 return TPM2_CMD_DURATION_LONG;
72 default:
73 return TPM2_CMD_DURATION_DEFAULT;
74 }
75 }
76
tpm2_transmit(void * buf,uint32_t bufsz,void * resp,uint32_t * len)77 static enum tpm2_result tpm2_transmit(void *buf, uint32_t bufsz, void *resp,
78 uint32_t *len)
79 {
80 uint32_t cmd_duration = 0;
81 enum tpm2_result ret = TPM2_OK;
82 uint32_t err_code = 0;
83
84 if (!buf || !resp || !len || (len && !*len))
85 return TPM2_ERR_INVALID_ARG;
86
87 if (bufsz < tpm2_cmd_len(buf))
88 return TPM2_ERR_INVALID_ARG;
89
90 DHEXDUMP(buf, tpm2_cmd_len(buf));
91
92 ret = tpm2_chip_send(buf, bufsz);
93 if (ret)
94 return ret;
95
96 cmd_duration = tpm2_get_cmd_duration(tpm2_cmd_code(buf));
97
98 ret = tpm2_chip_recv(resp, len, cmd_duration);
99 if (ret)
100 return ret;
101
102 err_code = tpm2_ret_code(resp);
103 if (err_code) {
104 EMSG("Command Error code %" PRIx32, err_code);
105 ret = TPM2_ERR_CMD;
106 }
107
108 DHEXDUMP(resp, tpm2_cmd_len(resp));
109
110 return ret;
111 }
112
tpm2_startup(uint16_t mode)113 enum tpm2_result tpm2_startup(uint16_t mode)
114 {
115 uint8_t buf[16] = { };
116 uint32_t buf_len = sizeof(buf);
117 uint8_t resp_buf[TPM2_HDR_LEN] = { };
118 uint32_t resp_len = sizeof(resp_buf);
119 enum tpm2_result ret = TPM2_OK;
120
121 tpm2_cmd_init_hdr(buf, buf_len, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
122 tpm2_cmd_add_u16(buf, buf_len, mode);
123
124 ret = tpm2_transmit(buf, tpm2_cmd_len((struct tpm2_cmd *)buf), resp_buf,
125 &resp_len);
126 if (ret == TPM2_ERR_SHORT_BUFFER)
127 EMSG("Increase size of response buffer to %#" PRIx32, resp_len);
128
129 return ret;
130 }
131
tpm2_selftest(uint8_t full)132 enum tpm2_result tpm2_selftest(uint8_t full)
133 {
134 uint8_t buf[16] = { };
135 uint32_t buf_len = sizeof(buf);
136 uint8_t resp_buf[TPM2_HDR_LEN] = { };
137 uint32_t resp_len = sizeof(resp_buf);
138 enum tpm2_result ret = TPM2_OK;
139
140 tpm2_cmd_init_hdr(buf, buf_len, TPM2_ST_NO_SESSIONS, TPM2_CC_SELFTEST);
141 tpm2_cmd_add_u8(buf, buf_len, full);
142
143 ret = tpm2_transmit(buf, tpm2_cmd_len((struct tpm2_cmd *)buf), resp_buf,
144 &resp_len);
145 if (ret == TPM2_ERR_SHORT_BUFFER)
146 EMSG("Increase size of response buffer to %#" PRIx32, resp_len);
147
148 return ret;
149 }
150
tpm2_get_capability(uint32_t capability,uint32_t property,uint32_t prop_cnt,void * prop,uint32_t * prop_len)151 enum tpm2_result tpm2_get_capability(uint32_t capability, uint32_t property,
152 uint32_t prop_cnt, void *prop,
153 uint32_t *prop_len)
154 {
155 uint8_t buf[32] = { };
156 uint32_t buf_len = sizeof(buf);
157 uint8_t *resp_buf = NULL;
158 uint32_t resp_len = 256;
159 uint32_t prop_offset = 0;
160 enum tpm2_result ret = TPM2_OK;
161
162 if (!prop || !prop_len || !*prop_len)
163 return TPM2_ERR_INVALID_ARG;
164
165 resp_buf = malloc(resp_len);
166 if (!resp_buf)
167 return TPM2_ERR_GENERIC;
168
169 tpm2_cmd_init_hdr(buf, buf_len, TPM2_ST_NO_SESSIONS,
170 TPM2_CC_GET_CAPABILITY);
171 tpm2_cmd_add_u32(buf, buf_len, capability);
172 tpm2_cmd_add_u32(buf, buf_len, property);
173 tpm2_cmd_add_u32(buf, buf_len, prop_cnt);
174
175 ret = tpm2_transmit(buf, tpm2_cmd_len((struct tpm2_cmd *)buf), resp_buf,
176 &resp_len);
177 if (ret)
178 goto out;
179
180 resp_len = tpm2_cmd_len((struct tpm2_cmd *)resp_buf);
181
182 /*
183 * Response include
184 * tpm2_cmd_hdr [10 bytes]
185 * TPM1_YES_NO (byte)
186 * capability (uin32_t)
187 * capability data [ This is the property data to be returned ]
188 */
189 prop_offset = sizeof(struct tpm2_cmd_hdr) + sizeof(uint8_t) +
190 sizeof(uint32_t);
191
192 if (*prop_len >= resp_len - prop_offset) {
193 memcpy(prop, &resp_buf[prop_offset], resp_len - prop_offset);
194 } else {
195 EMSG("Response Buffer size for property too small");
196 ret = TPM2_ERR_SHORT_BUFFER;
197 }
198
199 *prop_len = resp_len - prop_offset;
200 out:
201 free(resp_buf);
202 return ret;
203 }
204
tpm2_pcr_read(uint8_t pcr_idx,uint16_t alg,void * digest,uint32_t * digest_len)205 enum tpm2_result tpm2_pcr_read(uint8_t pcr_idx, uint16_t alg, void *digest,
206 uint32_t *digest_len)
207 {
208 uint8_t buf[32] = { };
209 uint8_t *resp_buf = NULL;
210 uint32_t buf_len = sizeof(buf);
211 uint32_t resp_len = 0;
212 uint32_t alg_len = 0;
213 uint32_t count = 1;
214 uint32_t digest_offset = 0;
215 uint8_t *pcr_select = 0;
216 uint8_t pcr_select_idx = 0;
217 uint8_t pcr_select_size = 0;
218 struct tpm2_caps caps = { };
219 enum tpm2_result ret = TPM2_OK;
220 struct tpml_digest *resp_dgst = NULL;
221 struct tpm2b_digest *dgst = resp_dgst->digest;
222
223 if (!digest || !digest_len)
224 return TPM2_ERR_INVALID_ARG;
225
226 ret = tpm2_chip_get_caps(&caps);
227 if (ret)
228 return ret;
229
230 if (pcr_idx >= caps.num_pcrs || !tpm2_chip_is_active_bank(alg))
231 return TPM2_ERR_INVALID_ARG;
232
233 alg_len = tpm2_get_alg_len(alg);
234 if (*digest_len < alg_len)
235 return TPM2_ERR_INVALID_ARG;
236
237 /*
238 * pcr_select is an array of octets where the octet contains the bit
239 * corresponding to a specific PCR. Octet index is found by dividing the
240 * PCR number by 8.
241 */
242 pcr_select_idx = pcr_idx >> 3;
243
244 /*
245 * pcr_select_size indicates the number of octets in pcr_select. It's
246 * minimum value is available in caps structure.
247 */
248 pcr_select_size = MAX(pcr_select_idx, caps.pcr_select_min);
249
250 /* Double check - the size shouldn't exceed TPM2_PCR_SELECT_MAX */
251 if (pcr_select_size > TPM2_PCR_SELECT_MAX)
252 return TPM2_ERR_INVALID_ARG;
253
254 pcr_select = calloc(pcr_select_size, sizeof(*pcr_select));
255 if (!pcr_select)
256 return TPM2_ERR_GENERIC;
257
258 pcr_select[pcr_select_idx] = BIT(pcr_idx % 8);
259
260 /* Create the PCR Read command */
261 tpm2_cmd_init_hdr(buf, buf_len, TPM2_ST_NO_SESSIONS,
262 TPM2_CC_PCR_READ);
263 /* TPML_PCR_SELECTION */
264 tpm2_cmd_add_u32(buf, buf_len, count);
265 tpm2_cmd_add_u16(buf, buf_len, alg);
266 tpm2_cmd_add_u8(buf, buf_len, pcr_select_size);
267 tpm2_cmd_add(buf, buf_len, pcr_select, pcr_select_size);
268
269 free(pcr_select);
270
271 /* Response includes:
272 * TPM Command header
273 * PCR update counter (uint32_t)
274 * TPML PCR Selection
275 * TPML DIGEST
276 */
277 digest_offset = sizeof(struct tpm2_cmd_hdr) + sizeof(uint32_t) +
278 offsetof(struct tpml_pcr_selection, pcr_selections) +
279 offsetof(struct tpms_pcr_selection, pcr_select) +
280 pcr_select_size;
281 resp_len = digest_offset + sizeof(struct tpml_digest);
282
283 resp_buf = malloc(resp_len);
284 if (!resp_buf)
285 return TPM2_ERR_GENERIC;
286
287 ret = tpm2_transmit(buf, tpm2_cmd_len((struct tpm2_cmd *)buf), resp_buf,
288 &resp_len);
289 if (ret)
290 goto out;
291
292 resp_len = tpm2_cmd_len((struct tpm2_cmd *)resp_buf);
293
294 DHEXDUMP(resp_buf, tpm2_cmd_len((struct tpm2_cmd *)resp_buf));
295
296 if (digest_offset > resp_len) {
297 ret = TPM2_ERR_GENERIC;
298 goto out;
299 }
300
301 resp_dgst = (struct tpml_digest *)&resp_buf[digest_offset];
302 dgst = resp_dgst->digest;
303
304 /* We had requested for only 1 digest so expected count is 1 */
305 if (get_be32(&resp_dgst->count) != 1) {
306 ret = TPM2_ERR_GENERIC;
307 goto out;
308 }
309
310 if (get_be16(&dgst->size) != alg_len) {
311 ret = TPM2_ERR_GENERIC;
312 goto out;
313 }
314
315 memcpy(digest, dgst->buffer, alg_len);
316
317 *digest_len = alg_len;
318 out:
319 free(resp_buf);
320 return ret;
321 }
322
tpm2_add_password_auth_cmd(uint8_t * buf,uint32_t buf_len,uint8_t * passwd,uint32_t pass_sz)323 static enum tpm2_result tpm2_add_password_auth_cmd(uint8_t *buf,
324 uint32_t buf_len,
325 uint8_t *passwd,
326 uint32_t pass_sz)
327 {
328 struct tpms_auth_command cmd = { };
329 uint32_t auth_sz = 0;
330
331 if (pass_sz && (pass_sz > sizeof(cmd.hmac.buffer) || !passwd))
332 return TPM2_ERR_INVALID_ARG;
333
334 cmd.handle = TPM_RS_PW;
335 /* For password authorization, nonce is empty buffer */
336 cmd.nonce.size = 0;
337 /* Password session is always available so has no effect */
338 cmd.session_attributes = 0;
339 cmd.hmac.size = pass_sz;
340
341 auth_sz = sizeof(cmd.handle) + sizeof(cmd.nonce.size) +
342 sizeof(cmd.session_attributes) + sizeof(cmd.hmac.size) +
343 cmd.hmac.size;
344
345 tpm2_cmd_add_u32(buf, buf_len, auth_sz);
346 tpm2_cmd_add_u32(buf, buf_len, cmd.handle);
347 tpm2_cmd_add_u16(buf, buf_len, cmd.nonce.size);
348 tpm2_cmd_add_u8(buf, buf_len, cmd.session_attributes);
349 tpm2_cmd_add_u16(buf, buf_len, cmd.hmac.size);
350 if (cmd.hmac.size)
351 tpm2_cmd_add(buf, buf_len, passwd, pass_sz);
352
353 return TPM2_OK;
354 }
355
tpm2_pcr_extend(uint8_t pcr_idx,uint16_t alg,void * digest,uint32_t digest_len)356 enum tpm2_result tpm2_pcr_extend(uint8_t pcr_idx, uint16_t alg, void *digest,
357 uint32_t digest_len)
358 {
359 void *buf = NULL;
360 uint32_t buf_len = 128;
361 uint8_t *resp_buf = NULL;
362 uint32_t resp_len = 256;
363 uint32_t alg_len = 0;
364 uint32_t count = 1;
365 struct tpm2_caps caps = { };
366 enum tpm2_result ret = TPM2_OK;
367
368 if (!digest)
369 return TPM2_ERR_INVALID_ARG;
370
371 ret = tpm2_chip_get_caps(&caps);
372 if (ret)
373 return ret;
374
375 if (pcr_idx >= caps.num_pcrs || !tpm2_chip_is_active_bank(alg))
376 return TPM2_ERR_INVALID_ARG;
377
378 alg_len = tpm2_get_alg_len(alg);
379 if (digest_len < alg_len)
380 return TPM2_ERR_INVALID_ARG;
381
382 /* CMD size will not exceed 128 bytes */
383 buf = malloc(buf_len);
384 if (!buf)
385 return TPM2_ERR_GENERIC;
386
387 resp_buf = malloc(resp_len);
388 if (!resp_buf) {
389 free(buf);
390 return TPM2_ERR_GENERIC;
391 }
392
393 tpm2_cmd_init_hdr(buf, buf_len, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
394
395 /* PCR Handle */
396 tpm2_cmd_add_u32(buf, buf_len, pcr_idx);
397
398 /* Add NULL authorization structure */
399 ret = tpm2_add_password_auth_cmd(buf, buf_len, NULL, 0);
400 if (ret)
401 goto out;
402
403 /*
404 * Add TPML_DIGEST_VALUES structure, we are adding
405 * a single digest.
406 */
407 tpm2_cmd_add_u32(buf, buf_len, count);
408 tpm2_cmd_add_u16(buf, buf_len, alg);
409 tpm2_cmd_add(buf, buf_len, digest, digest_len);
410
411 ret = tpm2_transmit(buf, tpm2_cmd_len((struct tpm2_cmd *)buf), resp_buf,
412 &resp_len);
413 if (ret)
414 goto out;
415
416 DHEXDUMP(resp_buf, tpm2_cmd_len((struct tpm2_cmd *)resp_buf));
417 out:
418 free(resp_buf);
419 free(buf);
420
421 return ret;
422 }
423