tpm2: add marshaling/unmarshaling and tlcl support
[vboot.git] / firmware / lib / tpm2_lite / marshaling.c
1 /*
2  * Copyright 2016 The Chromium OS Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6
7 #include "tpm2_marshaling.h"
8 #include "utility.h"
9
10 static uint16_t tpm_tag;  /* Depends on the command type. */
11
12 static void write_be16(void *dest, uint16_t val)
13 {
14         uint8_t *byte_dest = dest;
15
16         byte_dest[0] = val >> 8;
17         byte_dest[1] = val;
18 }
19
20 static void write_be32(void *dest, uint32_t val)
21 {
22         uint8_t *byte_dest = dest;
23
24         byte_dest[0] = val >> 24;
25         byte_dest[1] = val >> 16;
26         byte_dest[2] = val >> 8;
27         byte_dest[3] = val;
28 }
29
30 static uint16_t read_be16(const void *src)
31 {
32         const uint8_t *s = src;
33         return (((uint16_t)s[0]) << 8) | (((uint16_t)s[1]) << 0);
34 }
35
36 static inline uint32_t read_be32(const void *src)
37 {
38         const uint8_t *s = src;
39
40         return (((uint32_t)s[0]) << 24) | (((uint32_t)s[1]) << 16) |
41                 (((uint32_t)s[2]) << 8) | (((uint32_t)s[3]) << 0);
42 }
43
44 /*
45  * Each unmarshaling function receives a pointer to the buffer pointer and a
46  * pointer to the size of data still in the buffer. The function extracts data
47  * from the buffer and adjusts both buffer pointer and remaining data size.
48  *
49  * Should there be not enough data in the buffer to unmarshal the required
50  * object, the remaining data size is set to -1 to indicate the error. The
51  * remaining data size is expected to be set to zero once the last data item
52  * has been extracted from the buffer.
53  */
54
55 static uint16_t unmarshal_u16(void **buffer, int *buffer_space)
56 {
57         uint16_t value;
58
59         if (*buffer_space < sizeof(value)) {
60                 *buffer_space = -1; /* Indicate a failure. */
61                 return 0;
62         }
63
64         value = read_be16(*buffer);
65         *buffer = (void *) ((uintptr_t) (*buffer) + sizeof(value));
66         *buffer_space -= sizeof(value);
67
68         return value;
69 }
70
71 static uint16_t unmarshal_u32(void **buffer, int *buffer_space)
72 {
73         uint32_t value;
74
75         if (*buffer_space < sizeof(value)) {
76                 *buffer_space = -1; /* Indicate a failure. */
77                 return 0;
78         }
79
80         value = read_be32(*buffer);
81         *buffer = (void *) ((uintptr_t) (*buffer) + sizeof(value));
82         *buffer_space -= sizeof(value);
83
84         return value;
85 }
86
87 static void unmarshal_TPM2B_MAX_NV_BUFFER(void **buffer,
88                                           int *size,
89                                           TPM2B_MAX_NV_BUFFER *nv_buffer)
90 {
91         nv_buffer->t.size = unmarshal_u16(buffer, size);
92         if (nv_buffer->t.size > *size) {
93                 VBDEBUG(("%s:%d - "
94                        "size mismatch: expected %d, remaining %d\n",
95                          __func__, __LINE__, nv_buffer->t.size, *size));
96                 return;
97         }
98
99         nv_buffer->t.buffer = *buffer;
100
101         *buffer = ((uint8_t *)(*buffer)) + nv_buffer->t.size;
102         *size -= nv_buffer->t.size;
103 }
104
105 static void unmarshal_nv_read(void **buffer, int *size,
106                               struct nv_read_response *nvr)
107 {
108         /* Total size of the parameter field. */
109         nvr->params_size = unmarshal_u32(buffer, size);
110         unmarshal_TPM2B_MAX_NV_BUFFER(buffer, size, &nvr->buffer);
111
112         if (nvr->params_size !=
113             (nvr->buffer.t.size + sizeof(nvr->buffer.t.size))) {
114                 VBDEBUG(("%s:%d - parameter/buffer %d/%d size mismatch",
115                          __func__, __LINE__, nvr->params_size,
116                          nvr->buffer.t.size));
117                 return;
118         }
119
120         if (*size < 0)
121                 return;
122         /*
123          * Let's ignore the authorisation section. It should be 5 bytes total,
124          * just confirm that this is the case and report any discrepancy.
125          */
126         if (*size != 5)
127                 VBDEBUG(("%s:%d - unexpected authorisation seciton size %d\n",
128                          __func__, __LINE__, *size));
129
130         *buffer = ((uint8_t *)(*buffer)) + *size;
131         *size = 0;
132 }
133
134
135 /*
136  * Each marshaling function receives a pointer to the buffer to marshal into,
137  * a pointer to the data item to be marshaled, and a pointer to the remaining
138  * room in the buffer.
139  */
140
141  /*
142   * Marshaling an arbitrary blob requires its size in addition to common
143   * parameter set.
144   */
145 static void marshal_blob(void **buffer, void *blob,
146                          size_t blob_size, int *buffer_space)
147 {
148         if (*buffer_space < blob_size) {
149                 *buffer_space = -1;
150                 return;
151         }
152
153         Memcpy(*buffer, blob, blob_size);
154         buffer_space -= blob_size;
155         *buffer = (void *)((uintptr_t)(*buffer) + blob_size);
156 }
157
158 static void marshal_u8(void **buffer, uint8_t value, int *buffer_space)
159 {
160         uint8_t *bp = *buffer;
161
162         if (*buffer_space < sizeof(value)) {
163                 *buffer_space = -1;
164                 return;
165         }
166
167         *bp++ = value;
168         *buffer = bp;
169         *buffer_space -= sizeof(value);
170 }
171
172 static void marshal_u16(void **buffer, uint16_t value, int *buffer_space)
173 {
174         if (*buffer_space < sizeof(value)) {
175                 *buffer_space = -1;
176                 return;
177         }
178         write_be16(*buffer, value);
179         *buffer = (void *)((uintptr_t)(*buffer) + sizeof(value));
180         *buffer_space -= sizeof(value);
181 }
182
183 static void marshal_u32(void **buffer, uint32_t value, int *buffer_space)
184 {
185         if (*buffer_space < sizeof(value)) {
186                 *buffer_space = -1;
187                 return;
188         }
189
190         write_be32(*buffer, value);
191         *buffer = (void *)((uintptr_t)(*buffer) + sizeof(value));
192         *buffer_space -= sizeof(value);
193 }
194
195 #define unmarshal_TPM_CC(a, b) unmarshal_u32(a, b)
196 #define marshal_TPM_HANDLE(a, b, c) marshal_u32(a, b, c)
197
198 static void marshal_session_header(void **buffer,
199                                    struct tpm2_session_header *session_header,
200                                    int *buffer_space)
201 {
202         int base_size;
203         void *size_location = *buffer;
204
205         /* Skip room for the session header size. */
206         *buffer_space -= sizeof(uint32_t);
207         *buffer = (void *)(((uintptr_t) *buffer) + sizeof(uint32_t));
208
209         base_size = *buffer_space;
210
211         marshal_u32(buffer, session_header->session_handle, buffer_space);
212         marshal_u16(buffer, session_header->nonce_size, buffer_space);
213         marshal_blob(buffer, session_header->nonce,
214                      session_header->nonce_size, buffer_space);
215         marshal_u8(buffer, session_header->session_attrs, buffer_space);
216         marshal_u16(buffer, session_header->auth_size, buffer_space);
217         marshal_blob(buffer, session_header->auth,
218                      session_header->auth_size, buffer_space);
219
220         if (*buffer_space < 0)
221                 return;  /* The structure did not fit. */
222
223         /* Paste in the session size. */
224         marshal_u32(&size_location, base_size - *buffer_space, &base_size);
225 }
226
227 static void marshal_TPM2B(void **buffer,
228                           TPM2B *data,
229                           int *buffer_space)
230 {
231         size_t total_size = data->size + sizeof(data->size);
232
233         if (total_size > *buffer_space) {
234                 *buffer_space = -1;
235                 return;
236         }
237         marshal_u16(buffer, data->size, buffer_space);
238         Memcpy(*buffer, data->buffer, data->size);
239         *buffer = ((uint8_t *)(*buffer)) + data->size;
240         *buffer_space -= data->size;
241 }
242
243 static void marshal_nv_write(void **buffer,
244                              struct tpm2_nv_write_cmd *command_body,
245                              int *buffer_space)
246 {
247         struct tpm2_session_header session_header;
248
249         marshal_TPM_HANDLE(buffer, TPM_RH_PLATFORM, buffer_space);
250         marshal_TPM_HANDLE(buffer, command_body->nvIndex, buffer_space);
251         Memset(&session_header, 0, sizeof(session_header));
252         session_header.session_handle = TPM_RS_PW;
253         marshal_session_header(buffer, &session_header, buffer_space);
254         tpm_tag = TPM_ST_SESSIONS;
255
256         marshal_TPM2B(buffer, &command_body->data.b, buffer_space);
257         marshal_u16(buffer, command_body->offset, buffer_space);
258 }
259
260 static void marshal_nv_read(void **buffer,
261                             struct tpm2_nv_read_cmd *command_body,
262                             int *buffer_space)
263 {
264         struct tpm2_session_header session_header;
265
266         marshal_TPM_HANDLE(buffer, TPM_RH_PLATFORM, buffer_space);
267         marshal_TPM_HANDLE(buffer, command_body->nvIndex, buffer_space);
268         Memset(&session_header, 0, sizeof(session_header));
269         session_header.session_handle = TPM_RS_PW;
270         marshal_session_header(buffer, &session_header, buffer_space);
271         tpm_tag = TPM_ST_SESSIONS;
272         marshal_u16(buffer, command_body->size, buffer_space);
273         marshal_u16(buffer, command_body->offset, buffer_space);
274 }
275
276
277 int tpm_marshal_command(TPM_CC command, void *tpm_command_body,
278                         void *buffer, int buffer_size)
279 {
280         void *cmd_body = (uint8_t *)buffer + sizeof(struct tpm_header);
281         int max_body_size = buffer_size - sizeof(struct tpm_header);
282         int body_size = max_body_size;
283
284         /* Will be modified when marshaling some commands. */
285         tpm_tag = TPM_ST_NO_SESSIONS;
286
287         switch (command) {
288
289         case TPM2_NV_Read:
290                 marshal_nv_read(&cmd_body, tpm_command_body, &body_size);
291                 break;
292
293         case TPM2_NV_Write:
294                 marshal_nv_write(&cmd_body, tpm_command_body, &body_size);
295                 break;
296
297         default:
298                 body_size = -1;
299                 VBDEBUG(("%s:%d:Request to marshal unsupported command %#x\n",
300                          __FILE__, __LINE__, command));
301         }
302
303         if (body_size > 0) {
304
305                 /* See how much room was taken by marshaling. */
306                 body_size = max_body_size - body_size;
307
308                 body_size += sizeof(struct tpm_header);
309
310                 marshal_u16(&buffer, tpm_tag, &max_body_size);
311                 marshal_u32(&buffer, body_size, &max_body_size);
312                 marshal_u32(&buffer, command, &max_body_size);
313         }
314
315         return body_size;
316 }
317
318 struct tpm2_response *tpm_unmarshal_response(TPM_CC command,
319                                              void *response_body,
320                                              int cr_size)
321 {
322         static struct tpm2_response tpm2_resp;
323
324         if (cr_size < sizeof(struct tpm_header))
325                 return NULL;
326
327         tpm2_resp.hdr.tpm_tag = unmarshal_u16(&response_body, &cr_size);
328         tpm2_resp.hdr.tpm_size = unmarshal_u32(&response_body, &cr_size);
329         tpm2_resp.hdr.tpm_code = unmarshal_TPM_CC(&response_body, &cr_size);
330
331         if (!cr_size) {
332                 if (tpm2_resp.hdr.tpm_size != sizeof(tpm2_resp.hdr))
333                         VBDEBUG(("%s: size mismatch in response to command %#x\n",
334                                  __func__, command));
335                 return &tpm2_resp;
336         }
337
338         switch (command) {
339         case TPM2_NV_Read:
340                 unmarshal_nv_read(&response_body, &cr_size,
341                                   &tpm2_resp.nvr);
342                 break;
343
344         case TPM2_NV_Write:
345                 /* Session data included in response can be safely ignored. */
346                 cr_size = 0;
347                 break;
348
349         default:
350                 {
351                         int i;
352
353                         VBDEBUG(("%s:%d:"
354                                "Request to unmarshal unexpected command %#x,"
355                                " code %#x",
356                                __func__, __LINE__, command,
357                                  tpm2_resp.hdr.tpm_code));
358
359                         for (i = 0; i < cr_size; i++) {
360                                 if (!(i % 16))
361                                         VBDEBUG(("\n"));
362                                 VBDEBUG(("%2.2x ",
363                                          ((uint8_t *)response_body)[i]));
364                         }
365                 }
366                 VBDEBUG(("\n"));
367                 return NULL;
368         }
369
370         if (cr_size) {
371                 VBDEBUG(("%s:%d got %d bytes back in response to %#x,"
372                          " failed to parse (%d)\n",
373                          __func__, __LINE__, tpm2_resp.hdr.tpm_size,
374                          command, cr_size));
375                 return NULL;
376         }
377
378         /* The entire message have been parsed. */
379         return &tpm2_resp;
380 }