]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
8732b070 CC |
2 | /* |
3 | * Copyright (c) 2013 The Chromium OS Authors. | |
be6c1529 | 4 | * Coypright (c) 2013 Guntermann & Drunck GmbH |
8732b070 CC |
5 | */ |
6 | ||
6e64ec12 SG |
7 | #define LOG_CATEGORY UCLASS_TPM |
8 | ||
8732b070 | 9 | #include <common.h> |
c8a8c510 | 10 | #include <dm.h> |
f7ae49fc | 11 | #include <log.h> |
8732b070 | 12 | #include <asm/unaligned.h> |
c8a8c510 | 13 | #include <u-boot/sha1.h> |
d677bfe2 MR |
14 | #include <tpm-common.h> |
15 | #include <tpm-v1.h> | |
16 | #include "tpm-utils.h" | |
8732b070 | 17 | |
be6c1529 RP |
18 | #ifdef CONFIG_TPM_AUTH_SESSIONS |
19 | ||
20 | #ifndef CONFIG_SHA1 | |
21 | #error "TPM_AUTH_SESSIONS require SHA1 to be configured, too" | |
22 | #endif /* !CONFIG_SHA1 */ | |
23 | ||
24 | struct session_data { | |
25 | int valid; | |
b9804e5b MR |
26 | u32 handle; |
27 | u8 nonce_even[DIGEST_LENGTH]; | |
28 | u8 nonce_odd[DIGEST_LENGTH]; | |
be6c1529 RP |
29 | }; |
30 | ||
31 | static struct session_data oiap_session = {0, }; | |
32 | ||
33 | #endif /* CONFIG_TPM_AUTH_SESSIONS */ | |
34 | ||
d6a885f0 | 35 | u32 tpm1_startup(struct udevice *dev, enum tpm_startup_type mode) |
8732b070 | 36 | { |
b9804e5b | 37 | const u8 command[12] = { |
8732b070 CC |
38 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x0, |
39 | }; | |
40 | const size_t mode_offset = 10; | |
b9804e5b | 41 | u8 buf[COMMAND_BUFFER_SIZE]; |
8732b070 CC |
42 | |
43 | if (pack_byte_string(buf, sizeof(buf), "sw", | |
c6179187 MR |
44 | 0, command, sizeof(command), |
45 | mode_offset, mode)) | |
8732b070 CC |
46 | return TPM_LIB_ERROR; |
47 | ||
abdc7b8a | 48 | return tpm_sendrecv_command(dev, buf, NULL, NULL); |
8732b070 CC |
49 | } |
50 | ||
d6a885f0 | 51 | u32 tpm1_resume(struct udevice *dev) |
6e64ec12 | 52 | { |
d6a885f0 | 53 | return tpm1_startup(dev, TPM_ST_STATE); |
6e64ec12 SG |
54 | } |
55 | ||
d6a885f0 | 56 | u32 tpm1_self_test_full(struct udevice *dev) |
8732b070 | 57 | { |
b9804e5b | 58 | const u8 command[10] = { |
8732b070 CC |
59 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50, |
60 | }; | |
abdc7b8a | 61 | return tpm_sendrecv_command(dev, command, NULL, NULL); |
8732b070 CC |
62 | } |
63 | ||
d6a885f0 | 64 | u32 tpm1_continue_self_test(struct udevice *dev) |
8732b070 | 65 | { |
b9804e5b | 66 | const u8 command[10] = { |
8732b070 CC |
67 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53, |
68 | }; | |
abdc7b8a | 69 | return tpm_sendrecv_command(dev, command, NULL, NULL); |
8732b070 CC |
70 | } |
71 | ||
d6a885f0 | 72 | u32 tpm1_clear_and_reenable(struct udevice *dev) |
6e64ec12 SG |
73 | { |
74 | u32 ret; | |
75 | ||
76 | log_info("TPM: Clear and re-enable\n"); | |
d6a885f0 | 77 | ret = tpm1_force_clear(dev); |
6e64ec12 SG |
78 | if (ret != TPM_SUCCESS) { |
79 | log_err("Can't initiate a force clear\n"); | |
80 | return ret; | |
81 | } | |
82 | ||
d6a885f0 SG |
83 | ret = tpm1_physical_enable(dev); |
84 | if (ret != TPM_SUCCESS) { | |
85 | log_err("TPM: Can't set enabled state\n"); | |
86 | return ret; | |
87 | } | |
0a60a0a6 | 88 | |
d6a885f0 SG |
89 | ret = tpm1_physical_set_deactivated(dev, 0); |
90 | if (ret != TPM_SUCCESS) { | |
91 | log_err("TPM: Can't set deactivated state\n"); | |
92 | return ret; | |
6e64ec12 | 93 | } |
6e64ec12 SG |
94 | |
95 | return TPM_SUCCESS; | |
96 | } | |
97 | ||
d6a885f0 | 98 | u32 tpm1_nv_define_space(struct udevice *dev, u32 index, u32 perm, u32 size) |
8732b070 | 99 | { |
b9804e5b | 100 | const u8 command[101] = { |
8732b070 CC |
101 | 0x0, 0xc1, /* TPM_TAG */ |
102 | 0x0, 0x0, 0x0, 0x65, /* parameter size */ | |
103 | 0x0, 0x0, 0x0, 0xcc, /* TPM_COMMAND_CODE */ | |
104 | /* TPM_NV_DATA_PUBLIC->... */ | |
105 | 0x0, 0x18, /* ...->TPM_STRUCTURE_TAG */ | |
106 | 0, 0, 0, 0, /* ...->TPM_NV_INDEX */ | |
107 | /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */ | |
108 | 0x0, 0x3, | |
109 | 0, 0, 0, | |
110 | 0x1f, | |
111 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
112 | /* TPM_NV_DATA_PUBLIC->TPM_PCR_INFO_SHORT */ | |
113 | 0x0, 0x3, | |
114 | 0, 0, 0, | |
115 | 0x1f, | |
116 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
117 | /* TPM_NV_ATTRIBUTES->... */ | |
118 | 0x0, 0x17, /* ...->TPM_STRUCTURE_TAG */ | |
119 | 0, 0, 0, 0, /* ...->attributes */ | |
120 | /* End of TPM_NV_ATTRIBUTES */ | |
121 | 0, /* bReadSTClear */ | |
122 | 0, /* bWriteSTClear */ | |
123 | 0, /* bWriteDefine */ | |
124 | 0, 0, 0, 0, /* size */ | |
125 | }; | |
126 | const size_t index_offset = 12; | |
127 | const size_t perm_offset = 70; | |
128 | const size_t size_offset = 77; | |
b9804e5b | 129 | u8 buf[COMMAND_BUFFER_SIZE]; |
8732b070 CC |
130 | |
131 | if (pack_byte_string(buf, sizeof(buf), "sddd", | |
c6179187 MR |
132 | 0, command, sizeof(command), |
133 | index_offset, index, | |
134 | perm_offset, perm, | |
135 | size_offset, size)) | |
8732b070 CC |
136 | return TPM_LIB_ERROR; |
137 | ||
abdc7b8a | 138 | return tpm_sendrecv_command(dev, buf, NULL, NULL); |
8732b070 CC |
139 | } |
140 | ||
d6a885f0 | 141 | u32 tpm1_nv_set_locked(struct udevice *dev) |
6e64ec12 | 142 | { |
d6a885f0 | 143 | return tpm1_nv_define_space(dev, TPM_NV_INDEX_LOCK, 0, 0); |
6e64ec12 SG |
144 | } |
145 | ||
d6a885f0 | 146 | u32 tpm1_nv_read_value(struct udevice *dev, u32 index, void *data, u32 count) |
8732b070 | 147 | { |
b9804e5b | 148 | const u8 command[22] = { |
8732b070 CC |
149 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcf, |
150 | }; | |
151 | const size_t index_offset = 10; | |
152 | const size_t length_offset = 18; | |
153 | const size_t data_size_offset = 10; | |
154 | const size_t data_offset = 14; | |
b9804e5b | 155 | u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; |
8732b070 | 156 | size_t response_length = sizeof(response); |
b9804e5b MR |
157 | u32 data_size; |
158 | u32 err; | |
8732b070 CC |
159 | |
160 | if (pack_byte_string(buf, sizeof(buf), "sdd", | |
c6179187 MR |
161 | 0, command, sizeof(command), |
162 | index_offset, index, | |
163 | length_offset, count)) | |
8732b070 | 164 | return TPM_LIB_ERROR; |
abdc7b8a | 165 | err = tpm_sendrecv_command(dev, buf, response, &response_length); |
8732b070 CC |
166 | if (err) |
167 | return err; | |
168 | if (unpack_byte_string(response, response_length, "d", | |
c6179187 | 169 | data_size_offset, &data_size)) |
8732b070 CC |
170 | return TPM_LIB_ERROR; |
171 | if (data_size > count) | |
172 | return TPM_LIB_ERROR; | |
173 | if (unpack_byte_string(response, response_length, "s", | |
c6179187 | 174 | data_offset, data, data_size)) |
8732b070 CC |
175 | return TPM_LIB_ERROR; |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
d6a885f0 SG |
180 | u32 tpm1_nv_write_value(struct udevice *dev, u32 index, const void *data, |
181 | u32 length) | |
8732b070 | 182 | { |
b9804e5b | 183 | const u8 command[256] = { |
8732b070 CC |
184 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd, |
185 | }; | |
186 | const size_t command_size_offset = 2; | |
187 | const size_t index_offset = 10; | |
188 | const size_t length_offset = 18; | |
189 | const size_t data_offset = 22; | |
190 | const size_t write_info_size = 12; | |
b9804e5b | 191 | const u32 total_length = |
8732b070 | 192 | TPM_REQUEST_HEADER_LENGTH + write_info_size + length; |
b9804e5b | 193 | u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; |
8732b070 | 194 | size_t response_length = sizeof(response); |
b9804e5b | 195 | u32 err; |
8732b070 CC |
196 | |
197 | if (pack_byte_string(buf, sizeof(buf), "sddds", | |
c6179187 MR |
198 | 0, command, sizeof(command), |
199 | command_size_offset, total_length, | |
200 | index_offset, index, | |
201 | length_offset, length, | |
202 | data_offset, data, length)) | |
8732b070 | 203 | return TPM_LIB_ERROR; |
abdc7b8a | 204 | err = tpm_sendrecv_command(dev, buf, response, &response_length); |
8732b070 CC |
205 | if (err) |
206 | return err; | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
d6a885f0 SG |
211 | u32 tpm1_extend(struct udevice *dev, u32 index, const void *in_digest, |
212 | void *out_digest) | |
8732b070 | 213 | { |
b9804e5b | 214 | const u8 command[34] = { |
8732b070 CC |
215 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x14, |
216 | }; | |
217 | const size_t index_offset = 10; | |
218 | const size_t in_digest_offset = 14; | |
219 | const size_t out_digest_offset = 10; | |
b9804e5b MR |
220 | u8 buf[COMMAND_BUFFER_SIZE]; |
221 | u8 response[TPM_RESPONSE_HEADER_LENGTH + PCR_DIGEST_LENGTH]; | |
8732b070 | 222 | size_t response_length = sizeof(response); |
b9804e5b | 223 | u32 err; |
8732b070 CC |
224 | |
225 | if (pack_byte_string(buf, sizeof(buf), "sds", | |
c6179187 MR |
226 | 0, command, sizeof(command), |
227 | index_offset, index, | |
228 | in_digest_offset, in_digest, | |
229 | PCR_DIGEST_LENGTH)) | |
8732b070 | 230 | return TPM_LIB_ERROR; |
abdc7b8a | 231 | err = tpm_sendrecv_command(dev, buf, response, &response_length); |
8732b070 CC |
232 | if (err) |
233 | return err; | |
234 | ||
235 | if (unpack_byte_string(response, response_length, "s", | |
c6179187 MR |
236 | out_digest_offset, out_digest, |
237 | PCR_DIGEST_LENGTH)) | |
8732b070 CC |
238 | return TPM_LIB_ERROR; |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
d6a885f0 | 243 | u32 tpm1_pcr_read(struct udevice *dev, u32 index, void *data, size_t count) |
8732b070 | 244 | { |
b9804e5b | 245 | const u8 command[14] = { |
8732b070 CC |
246 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x15, |
247 | }; | |
248 | const size_t index_offset = 10; | |
249 | const size_t out_digest_offset = 10; | |
b9804e5b | 250 | u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; |
8732b070 | 251 | size_t response_length = sizeof(response); |
b9804e5b | 252 | u32 err; |
8732b070 CC |
253 | |
254 | if (count < PCR_DIGEST_LENGTH) | |
255 | return TPM_LIB_ERROR; | |
256 | ||
257 | if (pack_byte_string(buf, sizeof(buf), "sd", | |
c6179187 MR |
258 | 0, command, sizeof(command), |
259 | index_offset, index)) | |
8732b070 | 260 | return TPM_LIB_ERROR; |
abdc7b8a | 261 | err = tpm_sendrecv_command(dev, buf, response, &response_length); |
8732b070 CC |
262 | if (err) |
263 | return err; | |
264 | if (unpack_byte_string(response, response_length, "s", | |
c6179187 | 265 | out_digest_offset, data, PCR_DIGEST_LENGTH)) |
8732b070 CC |
266 | return TPM_LIB_ERROR; |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
d6a885f0 | 271 | u32 tpm1_tsc_physical_presence(struct udevice *dev, u16 presence) |
8732b070 | 272 | { |
b9804e5b | 273 | const u8 command[12] = { |
8732b070 CC |
274 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x0, |
275 | }; | |
276 | const size_t presence_offset = 10; | |
b9804e5b | 277 | u8 buf[COMMAND_BUFFER_SIZE]; |
8732b070 CC |
278 | |
279 | if (pack_byte_string(buf, sizeof(buf), "sw", | |
c6179187 MR |
280 | 0, command, sizeof(command), |
281 | presence_offset, presence)) | |
8732b070 CC |
282 | return TPM_LIB_ERROR; |
283 | ||
abdc7b8a | 284 | return tpm_sendrecv_command(dev, buf, NULL, NULL); |
8732b070 CC |
285 | } |
286 | ||
d6a885f0 | 287 | u32 tpm1_finalise_physical_presence(struct udevice *dev) |
6e64ec12 SG |
288 | { |
289 | const u8 command[12] = { | |
290 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x2, 0xa0, | |
291 | }; | |
292 | ||
abdc7b8a | 293 | return tpm_sendrecv_command(dev, command, NULL, NULL); |
6e64ec12 SG |
294 | } |
295 | ||
d6a885f0 | 296 | u32 tpm1_read_pubek(struct udevice *dev, void *data, size_t count) |
8732b070 | 297 | { |
b9804e5b | 298 | const u8 command[30] = { |
8732b070 CC |
299 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7c, |
300 | }; | |
301 | const size_t response_size_offset = 2; | |
302 | const size_t data_offset = 10; | |
303 | const size_t header_and_checksum_size = TPM_RESPONSE_HEADER_LENGTH + 20; | |
b9804e5b | 304 | u8 response[COMMAND_BUFFER_SIZE + TPM_PUBEK_SIZE]; |
8732b070 | 305 | size_t response_length = sizeof(response); |
b9804e5b MR |
306 | u32 data_size; |
307 | u32 err; | |
8732b070 | 308 | |
abdc7b8a | 309 | err = tpm_sendrecv_command(dev, command, response, &response_length); |
8732b070 CC |
310 | if (err) |
311 | return err; | |
312 | if (unpack_byte_string(response, response_length, "d", | |
c6179187 | 313 | response_size_offset, &data_size)) |
8732b070 CC |
314 | return TPM_LIB_ERROR; |
315 | if (data_size < header_and_checksum_size) | |
316 | return TPM_LIB_ERROR; | |
317 | data_size -= header_and_checksum_size; | |
318 | if (data_size > count) | |
319 | return TPM_LIB_ERROR; | |
320 | if (unpack_byte_string(response, response_length, "s", | |
c6179187 | 321 | data_offset, data, data_size)) |
8732b070 CC |
322 | return TPM_LIB_ERROR; |
323 | ||
324 | return 0; | |
325 | } | |
326 | ||
d6a885f0 | 327 | u32 tpm1_force_clear(struct udevice *dev) |
8732b070 | 328 | { |
b9804e5b | 329 | const u8 command[10] = { |
8732b070 CC |
330 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x5d, |
331 | }; | |
332 | ||
abdc7b8a | 333 | return tpm_sendrecv_command(dev, command, NULL, NULL); |
8732b070 CC |
334 | } |
335 | ||
d6a885f0 | 336 | u32 tpm1_physical_enable(struct udevice *dev) |
8732b070 | 337 | { |
b9804e5b | 338 | const u8 command[10] = { |
8732b070 CC |
339 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x6f, |
340 | }; | |
341 | ||
abdc7b8a | 342 | return tpm_sendrecv_command(dev, command, NULL, NULL); |
8732b070 CC |
343 | } |
344 | ||
d6a885f0 | 345 | u32 tpm1_physical_disable(struct udevice *dev) |
8732b070 | 346 | { |
b9804e5b | 347 | const u8 command[10] = { |
8732b070 CC |
348 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x70, |
349 | }; | |
350 | ||
abdc7b8a | 351 | return tpm_sendrecv_command(dev, command, NULL, NULL); |
8732b070 CC |
352 | } |
353 | ||
d6a885f0 | 354 | u32 tpm1_physical_set_deactivated(struct udevice *dev, u8 state) |
8732b070 | 355 | { |
b9804e5b | 356 | const u8 command[11] = { |
8732b070 CC |
357 | 0x0, 0xc1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x72, |
358 | }; | |
359 | const size_t state_offset = 10; | |
b9804e5b | 360 | u8 buf[COMMAND_BUFFER_SIZE]; |
8732b070 CC |
361 | |
362 | if (pack_byte_string(buf, sizeof(buf), "sb", | |
c6179187 MR |
363 | 0, command, sizeof(command), |
364 | state_offset, state)) | |
8732b070 CC |
365 | return TPM_LIB_ERROR; |
366 | ||
abdc7b8a | 367 | return tpm_sendrecv_command(dev, buf, NULL, NULL); |
8732b070 CC |
368 | } |
369 | ||
d6a885f0 SG |
370 | u32 tpm1_get_capability(struct udevice *dev, u32 cap_area, u32 sub_cap, |
371 | void *cap, size_t count) | |
8732b070 | 372 | { |
b9804e5b | 373 | const u8 command[22] = { |
8732b070 CC |
374 | 0x0, 0xc1, /* TPM_TAG */ |
375 | 0x0, 0x0, 0x0, 0x16, /* parameter size */ | |
376 | 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */ | |
377 | 0x0, 0x0, 0x0, 0x0, /* TPM_CAPABILITY_AREA */ | |
378 | 0x0, 0x0, 0x0, 0x4, /* subcap size */ | |
379 | 0x0, 0x0, 0x0, 0x0, /* subcap value */ | |
380 | }; | |
381 | const size_t cap_area_offset = 10; | |
382 | const size_t sub_cap_offset = 18; | |
383 | const size_t cap_offset = 14; | |
384 | const size_t cap_size_offset = 10; | |
b9804e5b | 385 | u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; |
8732b070 | 386 | size_t response_length = sizeof(response); |
b9804e5b MR |
387 | u32 cap_size; |
388 | u32 err; | |
8732b070 CC |
389 | |
390 | if (pack_byte_string(buf, sizeof(buf), "sdd", | |
c6179187 MR |
391 | 0, command, sizeof(command), |
392 | cap_area_offset, cap_area, | |
393 | sub_cap_offset, sub_cap)) | |
8732b070 | 394 | return TPM_LIB_ERROR; |
abdc7b8a | 395 | err = tpm_sendrecv_command(dev, buf, response, &response_length); |
8732b070 CC |
396 | if (err) |
397 | return err; | |
398 | if (unpack_byte_string(response, response_length, "d", | |
c6179187 | 399 | cap_size_offset, &cap_size)) |
8732b070 CC |
400 | return TPM_LIB_ERROR; |
401 | if (cap_size > response_length || cap_size > count) | |
402 | return TPM_LIB_ERROR; | |
403 | if (unpack_byte_string(response, response_length, "s", | |
c6179187 | 404 | cap_offset, cap, cap_size)) |
8732b070 CC |
405 | return TPM_LIB_ERROR; |
406 | ||
407 | return 0; | |
408 | } | |
be6c1529 | 409 | |
d6a885f0 SG |
410 | u32 tpm1_get_permanent_flags(struct udevice *dev, |
411 | struct tpm_permanent_flags *pflags) | |
2132f971 | 412 | { |
b9804e5b | 413 | const u8 command[22] = { |
2132f971 SG |
414 | 0x0, 0xc1, /* TPM_TAG */ |
415 | 0x0, 0x0, 0x0, 0x16, /* parameter size */ | |
416 | 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */ | |
417 | 0x0, 0x0, 0x0, 0x4, /* TPM_CAP_FLAG_PERM */ | |
418 | 0x0, 0x0, 0x0, 0x4, /* subcap size */ | |
419 | 0x0, 0x0, 0x1, 0x8, /* subcap value */ | |
420 | }; | |
e8155dfe | 421 | const size_t data_size_offset = TPM_HEADER_SIZE; |
b9804e5b MR |
422 | const size_t data_offset = TPM_HEADER_SIZE + sizeof(u32); |
423 | u8 response[COMMAND_BUFFER_SIZE]; | |
2132f971 | 424 | size_t response_length = sizeof(response); |
b9804e5b MR |
425 | u32 err; |
426 | u32 data_size; | |
2132f971 | 427 | |
abdc7b8a | 428 | err = tpm_sendrecv_command(dev, command, response, &response_length); |
2132f971 SG |
429 | if (err) |
430 | return err; | |
e8155dfe | 431 | if (unpack_byte_string(response, response_length, "d", |
6e64ec12 SG |
432 | data_size_offset, &data_size)) { |
433 | log_err("Cannot unpack data size\n"); | |
e8155dfe | 434 | return TPM_LIB_ERROR; |
6e64ec12 SG |
435 | } |
436 | if (data_size < sizeof(*pflags)) { | |
437 | log_err("Data size too small\n"); | |
e8155dfe | 438 | return TPM_LIB_ERROR; |
6e64ec12 | 439 | } |
e8155dfe | 440 | if (unpack_byte_string(response, response_length, "s", |
6e64ec12 SG |
441 | data_offset, pflags, sizeof(*pflags))) { |
442 | log_err("Cannot unpack pflags\n"); | |
e8155dfe | 443 | return TPM_LIB_ERROR; |
6e64ec12 | 444 | } |
2132f971 SG |
445 | |
446 | return 0; | |
447 | } | |
448 | ||
d6a885f0 | 449 | u32 tpm1_get_permissions(struct udevice *dev, u32 index, u32 *perm) |
2132f971 | 450 | { |
b9804e5b | 451 | const u8 command[22] = { |
2132f971 SG |
452 | 0x0, 0xc1, /* TPM_TAG */ |
453 | 0x0, 0x0, 0x0, 0x16, /* parameter size */ | |
454 | 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */ | |
455 | 0x0, 0x0, 0x0, 0x11, | |
456 | 0x0, 0x0, 0x0, 0x4, | |
457 | }; | |
458 | const size_t index_offset = 18; | |
459 | const size_t perm_offset = 60; | |
b9804e5b | 460 | u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; |
2132f971 | 461 | size_t response_length = sizeof(response); |
b9804e5b | 462 | u32 err; |
2132f971 SG |
463 | |
464 | if (pack_byte_string(buf, sizeof(buf), "d", 0, command, sizeof(command), | |
465 | index_offset, index)) | |
466 | return TPM_LIB_ERROR; | |
abdc7b8a | 467 | err = tpm_sendrecv_command(dev, buf, response, &response_length); |
2132f971 SG |
468 | if (err) |
469 | return err; | |
470 | if (unpack_byte_string(response, response_length, "d", | |
471 | perm_offset, perm)) | |
472 | return TPM_LIB_ERROR; | |
473 | ||
474 | return 0; | |
475 | } | |
476 | ||
7690be35 | 477 | #ifdef CONFIG_TPM_FLUSH_RESOURCES |
d6a885f0 | 478 | u32 tpm1_flush_specific(struct udevice *dev, u32 key_handle, u32 resource_type) |
7690be35 | 479 | { |
b9804e5b | 480 | const u8 command[18] = { |
7690be35 MS |
481 | 0x00, 0xc1, /* TPM_TAG */ |
482 | 0x00, 0x00, 0x00, 0x12, /* parameter size */ | |
483 | 0x00, 0x00, 0x00, 0xba, /* TPM_COMMAND_CODE */ | |
484 | 0x00, 0x00, 0x00, 0x00, /* key handle */ | |
485 | 0x00, 0x00, 0x00, 0x00, /* resource type */ | |
486 | }; | |
487 | const size_t key_handle_offset = 10; | |
488 | const size_t resource_type_offset = 14; | |
b9804e5b | 489 | u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; |
7690be35 | 490 | size_t response_length = sizeof(response); |
b9804e5b | 491 | u32 err; |
7690be35 MS |
492 | |
493 | if (pack_byte_string(buf, sizeof(buf), "sdd", | |
494 | 0, command, sizeof(command), | |
495 | key_handle_offset, key_handle, | |
496 | resource_type_offset, resource_type)) | |
497 | return TPM_LIB_ERROR; | |
498 | ||
abdc7b8a | 499 | err = tpm_sendrecv_command(dev, buf, response, &response_length); |
7690be35 MS |
500 | if (err) |
501 | return err; | |
502 | return 0; | |
503 | } | |
504 | #endif /* CONFIG_TPM_FLUSH_RESOURCES */ | |
505 | ||
be6c1529 RP |
506 | #ifdef CONFIG_TPM_AUTH_SESSIONS |
507 | ||
508 | /** | |
509 | * Fill an authentication block in a request. | |
510 | * This func can create the first as well as the second auth block (for | |
511 | * double authorized commands). | |
512 | * | |
513 | * @param request pointer to the request (w/ uninitialised auth data) | |
514 | * @param request_len0 length of the request without auth data | |
515 | * @param handles_len length of the handles area in request | |
516 | * @param auth_session pointer to the (valid) auth session to be used | |
517 | * @param request_auth pointer to the auth block of the request to be filled | |
518 | * @param auth authentication data (HMAC key) | |
519 | */ | |
b9804e5b MR |
520 | static u32 create_request_auth(const void *request, size_t request_len0, |
521 | size_t handles_len, | |
522 | struct session_data *auth_session, | |
523 | void *request_auth, const void *auth) | |
be6c1529 | 524 | { |
b9804e5b | 525 | u8 hmac_data[DIGEST_LENGTH * 3 + 1]; |
be6c1529 RP |
526 | sha1_context hash_ctx; |
527 | const size_t command_code_offset = 6; | |
528 | const size_t auth_nonce_odd_offset = 4; | |
529 | const size_t auth_continue_offset = 24; | |
530 | const size_t auth_auth_offset = 25; | |
531 | ||
532 | if (!auth_session || !auth_session->valid) | |
533 | return TPM_LIB_ERROR; | |
534 | ||
535 | sha1_starts(&hash_ctx); | |
536 | sha1_update(&hash_ctx, request + command_code_offset, 4); | |
537 | if (request_len0 > TPM_REQUEST_HEADER_LENGTH + handles_len) | |
538 | sha1_update(&hash_ctx, | |
539 | request + TPM_REQUEST_HEADER_LENGTH + handles_len, | |
540 | request_len0 - TPM_REQUEST_HEADER_LENGTH | |
541 | - handles_len); | |
542 | sha1_finish(&hash_ctx, hmac_data); | |
543 | ||
544 | sha1_starts(&hash_ctx); | |
545 | sha1_update(&hash_ctx, auth_session->nonce_odd, DIGEST_LENGTH); | |
546 | sha1_update(&hash_ctx, hmac_data, sizeof(hmac_data)); | |
547 | sha1_finish(&hash_ctx, auth_session->nonce_odd); | |
548 | ||
549 | if (pack_byte_string(request_auth, TPM_REQUEST_AUTH_LENGTH, "dsb", | |
550 | 0, auth_session->handle, | |
551 | auth_nonce_odd_offset, auth_session->nonce_odd, | |
552 | DIGEST_LENGTH, | |
553 | auth_continue_offset, 1)) | |
554 | return TPM_LIB_ERROR; | |
555 | if (pack_byte_string(hmac_data, sizeof(hmac_data), "ss", | |
556 | DIGEST_LENGTH, | |
557 | auth_session->nonce_even, | |
558 | DIGEST_LENGTH, | |
559 | 2 * DIGEST_LENGTH, | |
560 | request_auth + auth_nonce_odd_offset, | |
561 | DIGEST_LENGTH + 1)) | |
562 | return TPM_LIB_ERROR; | |
563 | sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data), | |
564 | request_auth + auth_auth_offset); | |
565 | ||
566 | return TPM_SUCCESS; | |
567 | } | |
568 | ||
569 | /** | |
570 | * Verify an authentication block in a response. | |
571 | * Since this func updates the nonce_even in the session data it has to be | |
572 | * called when receiving a succesfull AUTH response. | |
573 | * This func can verify the first as well as the second auth block (for | |
574 | * double authorized commands). | |
575 | * | |
576 | * @param command_code command code of the request | |
577 | * @param response pointer to the request (w/ uninitialised auth data) | |
578 | * @param handles_len length of the handles area in response | |
579 | * @param auth_session pointer to the (valid) auth session to be used | |
580 | * @param response_auth pointer to the auth block of the response to be verified | |
581 | * @param auth authentication data (HMAC key) | |
582 | */ | |
b9804e5b MR |
583 | static u32 verify_response_auth(u32 command_code, const void *response, |
584 | size_t response_len0, size_t handles_len, | |
585 | struct session_data *auth_session, | |
586 | const void *response_auth, const void *auth) | |
be6c1529 | 587 | { |
b9804e5b MR |
588 | u8 hmac_data[DIGEST_LENGTH * 3 + 1]; |
589 | u8 computed_auth[DIGEST_LENGTH]; | |
be6c1529 RP |
590 | sha1_context hash_ctx; |
591 | const size_t return_code_offset = 6; | |
592 | const size_t auth_continue_offset = 20; | |
593 | const size_t auth_auth_offset = 21; | |
b9804e5b | 594 | u8 auth_continue; |
be6c1529 RP |
595 | |
596 | if (!auth_session || !auth_session->valid) | |
597 | return TPM_AUTHFAIL; | |
598 | if (pack_byte_string(hmac_data, sizeof(hmac_data), "d", | |
599 | 0, command_code)) | |
600 | return TPM_LIB_ERROR; | |
601 | if (response_len0 < TPM_RESPONSE_HEADER_LENGTH) | |
602 | return TPM_LIB_ERROR; | |
603 | ||
604 | sha1_starts(&hash_ctx); | |
605 | sha1_update(&hash_ctx, response + return_code_offset, 4); | |
606 | sha1_update(&hash_ctx, hmac_data, 4); | |
607 | if (response_len0 > TPM_RESPONSE_HEADER_LENGTH + handles_len) | |
608 | sha1_update(&hash_ctx, | |
609 | response + TPM_RESPONSE_HEADER_LENGTH + handles_len, | |
610 | response_len0 - TPM_RESPONSE_HEADER_LENGTH | |
611 | - handles_len); | |
612 | sha1_finish(&hash_ctx, hmac_data); | |
613 | ||
614 | memcpy(auth_session->nonce_even, response_auth, DIGEST_LENGTH); | |
b9804e5b | 615 | auth_continue = ((u8 *)response_auth)[auth_continue_offset]; |
be6c1529 RP |
616 | if (pack_byte_string(hmac_data, sizeof(hmac_data), "ssb", |
617 | DIGEST_LENGTH, | |
618 | response_auth, | |
619 | DIGEST_LENGTH, | |
620 | 2 * DIGEST_LENGTH, | |
621 | auth_session->nonce_odd, | |
622 | DIGEST_LENGTH, | |
623 | 3 * DIGEST_LENGTH, | |
624 | auth_continue)) | |
625 | return TPM_LIB_ERROR; | |
626 | ||
627 | sha1_hmac(auth, DIGEST_LENGTH, hmac_data, sizeof(hmac_data), | |
628 | computed_auth); | |
629 | ||
630 | if (memcmp(computed_auth, response_auth + auth_auth_offset, | |
631 | DIGEST_LENGTH)) | |
632 | return TPM_AUTHFAIL; | |
633 | ||
634 | return TPM_SUCCESS; | |
635 | } | |
636 | ||
d6a885f0 | 637 | u32 tpm1_terminate_auth_session(struct udevice *dev, u32 auth_handle) |
be6c1529 | 638 | { |
b9804e5b | 639 | const u8 command[18] = { |
be6c1529 RP |
640 | 0x00, 0xc1, /* TPM_TAG */ |
641 | 0x00, 0x00, 0x00, 0x00, /* parameter size */ | |
642 | 0x00, 0x00, 0x00, 0xba, /* TPM_COMMAND_CODE */ | |
643 | 0x00, 0x00, 0x00, 0x00, /* TPM_HANDLE */ | |
52da18a3 | 644 | 0x00, 0x00, 0x00, 0x02, /* TPM_RESOURCE_TYPE */ |
be6c1529 RP |
645 | }; |
646 | const size_t req_handle_offset = TPM_REQUEST_HEADER_LENGTH; | |
b9804e5b | 647 | u8 request[COMMAND_BUFFER_SIZE]; |
be6c1529 RP |
648 | |
649 | if (pack_byte_string(request, sizeof(request), "sd", | |
650 | 0, command, sizeof(command), | |
651 | req_handle_offset, auth_handle)) | |
652 | return TPM_LIB_ERROR; | |
653 | if (oiap_session.valid && oiap_session.handle == auth_handle) | |
654 | oiap_session.valid = 0; | |
655 | ||
abdc7b8a | 656 | return tpm_sendrecv_command(dev, request, NULL, NULL); |
be6c1529 RP |
657 | } |
658 | ||
d6a885f0 | 659 | u32 tpm1_end_oiap(struct udevice *dev) |
be6c1529 | 660 | { |
b9804e5b | 661 | u32 err = TPM_SUCCESS; |
96cc4e31 | 662 | |
be6c1529 | 663 | if (oiap_session.valid) |
d6a885f0 | 664 | err = tpm1_terminate_auth_session(dev, oiap_session.handle); |
be6c1529 RP |
665 | return err; |
666 | } | |
667 | ||
d6a885f0 | 668 | u32 tpm1_oiap(struct udevice *dev, u32 *auth_handle) |
be6c1529 | 669 | { |
b9804e5b | 670 | const u8 command[10] = { |
be6c1529 RP |
671 | 0x00, 0xc1, /* TPM_TAG */ |
672 | 0x00, 0x00, 0x00, 0x0a, /* parameter size */ | |
673 | 0x00, 0x00, 0x00, 0x0a, /* TPM_COMMAND_CODE */ | |
674 | }; | |
675 | const size_t res_auth_handle_offset = TPM_RESPONSE_HEADER_LENGTH; | |
676 | const size_t res_nonce_even_offset = TPM_RESPONSE_HEADER_LENGTH + 4; | |
b9804e5b | 677 | u8 response[COMMAND_BUFFER_SIZE]; |
be6c1529 | 678 | size_t response_length = sizeof(response); |
b9804e5b | 679 | u32 err; |
be6c1529 RP |
680 | |
681 | if (oiap_session.valid) | |
d6a885f0 | 682 | tpm1_terminate_auth_session(dev, oiap_session.handle); |
be6c1529 | 683 | |
abdc7b8a | 684 | err = tpm_sendrecv_command(dev, command, response, &response_length); |
be6c1529 RP |
685 | if (err) |
686 | return err; | |
687 | if (unpack_byte_string(response, response_length, "ds", | |
688 | res_auth_handle_offset, &oiap_session.handle, | |
689 | res_nonce_even_offset, &oiap_session.nonce_even, | |
b9804e5b | 690 | (u32)DIGEST_LENGTH)) |
be6c1529 RP |
691 | return TPM_LIB_ERROR; |
692 | oiap_session.valid = 1; | |
693 | if (auth_handle) | |
694 | *auth_handle = oiap_session.handle; | |
695 | return 0; | |
696 | } | |
697 | ||
d6a885f0 SG |
698 | u32 tpm1_load_key2_oiap(struct udevice *dev, u32 parent_handle, const void *key, |
699 | size_t key_length, const void *parent_key_usage_auth, | |
700 | u32 *key_handle) | |
be6c1529 | 701 | { |
b9804e5b | 702 | const u8 command[14] = { |
be6c1529 RP |
703 | 0x00, 0xc2, /* TPM_TAG */ |
704 | 0x00, 0x00, 0x00, 0x00, /* parameter size */ | |
705 | 0x00, 0x00, 0x00, 0x41, /* TPM_COMMAND_CODE */ | |
706 | 0x00, 0x00, 0x00, 0x00, /* parent handle */ | |
707 | }; | |
708 | const size_t req_size_offset = 2; | |
709 | const size_t req_parent_handle_offset = TPM_REQUEST_HEADER_LENGTH; | |
710 | const size_t req_key_offset = TPM_REQUEST_HEADER_LENGTH + 4; | |
711 | const size_t res_handle_offset = TPM_RESPONSE_HEADER_LENGTH; | |
b9804e5b MR |
712 | u8 request[sizeof(command) + TPM_KEY12_MAX_LENGTH + |
713 | TPM_REQUEST_AUTH_LENGTH]; | |
714 | u8 response[COMMAND_BUFFER_SIZE]; | |
be6c1529 | 715 | size_t response_length = sizeof(response); |
b9804e5b | 716 | u32 err; |
be6c1529 RP |
717 | |
718 | if (!oiap_session.valid) { | |
d6a885f0 | 719 | err = tpm1_oiap(dev, NULL); |
be6c1529 RP |
720 | if (err) |
721 | return err; | |
722 | } | |
723 | if (pack_byte_string(request, sizeof(request), "sdds", | |
724 | 0, command, sizeof(command), | |
725 | req_size_offset, | |
726 | sizeof(command) + key_length | |
727 | + TPM_REQUEST_AUTH_LENGTH, | |
728 | req_parent_handle_offset, parent_handle, | |
729 | req_key_offset, key, key_length | |
730 | )) | |
731 | return TPM_LIB_ERROR; | |
732 | ||
733 | err = create_request_auth(request, sizeof(command) + key_length, 4, | |
c6179187 MR |
734 | &oiap_session, |
735 | request + sizeof(command) + key_length, | |
736 | parent_key_usage_auth); | |
be6c1529 RP |
737 | if (err) |
738 | return err; | |
abdc7b8a | 739 | err = tpm_sendrecv_command(dev, request, response, &response_length); |
be6c1529 RP |
740 | if (err) { |
741 | if (err == TPM_AUTHFAIL) | |
742 | oiap_session.valid = 0; | |
743 | return err; | |
744 | } | |
745 | ||
746 | err = verify_response_auth(0x00000041, response, | |
c6179187 MR |
747 | response_length - TPM_RESPONSE_AUTH_LENGTH, |
748 | 4, &oiap_session, | |
749 | response + response_length - | |
750 | TPM_RESPONSE_AUTH_LENGTH, | |
751 | parent_key_usage_auth); | |
be6c1529 RP |
752 | if (err) |
753 | return err; | |
754 | ||
755 | if (key_handle) { | |
756 | if (unpack_byte_string(response, response_length, "d", | |
757 | res_handle_offset, key_handle)) | |
758 | return TPM_LIB_ERROR; | |
759 | } | |
760 | ||
761 | return 0; | |
762 | } | |
763 | ||
d6a885f0 SG |
764 | u32 tpm1_get_pub_key_oiap(struct udevice *dev, u32 key_handle, |
765 | const void *usage_auth, void *pubkey, | |
766 | size_t *pubkey_len) | |
be6c1529 | 767 | { |
b9804e5b | 768 | const u8 command[14] = { |
be6c1529 RP |
769 | 0x00, 0xc2, /* TPM_TAG */ |
770 | 0x00, 0x00, 0x00, 0x00, /* parameter size */ | |
771 | 0x00, 0x00, 0x00, 0x21, /* TPM_COMMAND_CODE */ | |
772 | 0x00, 0x00, 0x00, 0x00, /* key handle */ | |
773 | }; | |
774 | const size_t req_size_offset = 2; | |
775 | const size_t req_key_handle_offset = TPM_REQUEST_HEADER_LENGTH; | |
776 | const size_t res_pubkey_offset = TPM_RESPONSE_HEADER_LENGTH; | |
b9804e5b MR |
777 | u8 request[sizeof(command) + TPM_REQUEST_AUTH_LENGTH]; |
778 | u8 response[TPM_RESPONSE_HEADER_LENGTH + TPM_PUBKEY_MAX_LENGTH + | |
779 | TPM_RESPONSE_AUTH_LENGTH]; | |
be6c1529 | 780 | size_t response_length = sizeof(response); |
b9804e5b | 781 | u32 err; |
be6c1529 RP |
782 | |
783 | if (!oiap_session.valid) { | |
d6a885f0 | 784 | err = tpm1_oiap(dev, NULL); |
be6c1529 RP |
785 | if (err) |
786 | return err; | |
787 | } | |
788 | if (pack_byte_string(request, sizeof(request), "sdd", | |
789 | 0, command, sizeof(command), | |
790 | req_size_offset, | |
b9804e5b | 791 | (u32)(sizeof(command) |
be6c1529 RP |
792 | + TPM_REQUEST_AUTH_LENGTH), |
793 | req_key_handle_offset, key_handle | |
794 | )) | |
795 | return TPM_LIB_ERROR; | |
796 | err = create_request_auth(request, sizeof(command), 4, &oiap_session, | |
c6179187 | 797 | request + sizeof(command), usage_auth); |
be6c1529 RP |
798 | if (err) |
799 | return err; | |
abdc7b8a | 800 | err = tpm_sendrecv_command(dev, request, response, &response_length); |
be6c1529 RP |
801 | if (err) { |
802 | if (err == TPM_AUTHFAIL) | |
803 | oiap_session.valid = 0; | |
804 | return err; | |
805 | } | |
806 | err = verify_response_auth(0x00000021, response, | |
c6179187 MR |
807 | response_length - TPM_RESPONSE_AUTH_LENGTH, |
808 | 0, &oiap_session, | |
809 | response + response_length - | |
810 | TPM_RESPONSE_AUTH_LENGTH, | |
811 | usage_auth); | |
be6c1529 RP |
812 | if (err) |
813 | return err; | |
814 | ||
815 | if (pubkey) { | |
816 | if ((response_length - TPM_RESPONSE_HEADER_LENGTH | |
c6179187 | 817 | - TPM_RESPONSE_AUTH_LENGTH) > *pubkey_len) |
be6c1529 RP |
818 | return TPM_LIB_ERROR; |
819 | *pubkey_len = response_length - TPM_RESPONSE_HEADER_LENGTH | |
820 | - TPM_RESPONSE_AUTH_LENGTH; | |
821 | memcpy(pubkey, response + res_pubkey_offset, | |
822 | response_length - TPM_RESPONSE_HEADER_LENGTH | |
823 | - TPM_RESPONSE_AUTH_LENGTH); | |
824 | } | |
825 | ||
826 | return 0; | |
827 | } | |
828 | ||
0f4b2ba1 | 829 | #ifdef CONFIG_TPM_LOAD_KEY_BY_SHA1 |
d6a885f0 SG |
830 | u32 tpm1_find_key_sha1(struct udevice *dev, const u8 auth[20], |
831 | const u8 pubkey_digest[20], u32 *handle) | |
0f4b2ba1 | 832 | { |
b9804e5b MR |
833 | u16 key_count; |
834 | u32 key_handles[10]; | |
835 | u8 buf[288]; | |
836 | u8 *ptr; | |
837 | u32 err; | |
838 | u8 digest[20]; | |
0f4b2ba1 | 839 | size_t buf_len; |
840 | unsigned int i; | |
841 | ||
842 | /* fetch list of already loaded keys in the TPM */ | |
abdc7b8a SG |
843 | err = tpm_get_capability(dev, TPM_CAP_HANDLE, TPM_RT_KEY, buf, |
844 | sizeof(buf)); | |
0f4b2ba1 | 845 | if (err) |
846 | return -1; | |
847 | key_count = get_unaligned_be16(buf); | |
848 | ptr = buf + 2; | |
849 | for (i = 0; i < key_count; ++i, ptr += 4) | |
850 | key_handles[i] = get_unaligned_be32(ptr); | |
851 | ||
852 | /* now search a(/ the) key which we can access with the given auth */ | |
853 | for (i = 0; i < key_count; ++i) { | |
854 | buf_len = sizeof(buf); | |
855 | err = tpm_get_pub_key_oiap(key_handles[i], auth, buf, &buf_len); | |
856 | if (err && err != TPM_AUTHFAIL) | |
857 | return -1; | |
858 | if (err) | |
859 | continue; | |
860 | sha1_csum(buf, buf_len, digest); | |
861 | if (!memcmp(digest, pubkey_digest, 20)) { | |
862 | *handle = key_handles[i]; | |
863 | return 0; | |
864 | } | |
865 | } | |
866 | return 1; | |
867 | } | |
868 | #endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */ | |
869 | ||
be6c1529 | 870 | #endif /* CONFIG_TPM_AUTH_SESSIONS */ |
3c605027 | 871 | |
d6a885f0 | 872 | u32 tpm1_get_random(struct udevice *dev, void *data, u32 count) |
3c605027 | 873 | { |
b9804e5b | 874 | const u8 command[14] = { |
3c605027 AD |
875 | 0x0, 0xc1, /* TPM_TAG */ |
876 | 0x0, 0x0, 0x0, 0xe, /* parameter size */ | |
877 | 0x0, 0x0, 0x0, 0x46, /* TPM_COMMAND_CODE */ | |
878 | }; | |
879 | const size_t length_offset = 10; | |
880 | const size_t data_size_offset = 10; | |
881 | const size_t data_offset = 14; | |
b9804e5b | 882 | u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; |
3c605027 | 883 | size_t response_length = sizeof(response); |
b9804e5b MR |
884 | u32 data_size; |
885 | u8 *out = data; | |
3c605027 AD |
886 | |
887 | while (count > 0) { | |
b9804e5b MR |
888 | u32 this_bytes = min((size_t)count, |
889 | sizeof(response) - data_offset); | |
890 | u32 err; | |
3c605027 AD |
891 | |
892 | if (pack_byte_string(buf, sizeof(buf), "sd", | |
893 | 0, command, sizeof(command), | |
894 | length_offset, this_bytes)) | |
895 | return TPM_LIB_ERROR; | |
abdc7b8a SG |
896 | err = tpm_sendrecv_command(dev, buf, response, |
897 | &response_length); | |
3c605027 AD |
898 | if (err) |
899 | return err; | |
900 | if (unpack_byte_string(response, response_length, "d", | |
901 | data_size_offset, &data_size)) | |
902 | return TPM_LIB_ERROR; | |
903 | if (data_size > count) | |
904 | return TPM_LIB_ERROR; | |
905 | if (unpack_byte_string(response, response_length, "s", | |
906 | data_offset, out, data_size)) | |
907 | return TPM_LIB_ERROR; | |
908 | ||
909 | count -= data_size; | |
910 | out += data_size; | |
911 | } | |
912 | ||
913 | return 0; | |
914 | } |