1 /*
2 * AWS Nitro Secure Module (NSM) device
3 *
4 * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or
7 * (at your option) any later version. See the COPYING file in the
8 * top-level directory.
9 */
10
11 #include "qemu/osdep.h"
12 #include "qemu/iov.h"
13 #include "qemu/guest-random.h"
14 #include "qapi/error.h"
15
16 #include "crypto/hash.h"
17 #include "hw/virtio/virtio.h"
18 #include "hw/virtio/virtio-nsm.h"
19 #include "hw/virtio/cbor-helpers.h"
20 #include "standard-headers/linux/virtio_ids.h"
21
22 #define NSM_REQUEST_MAX_SIZE 0x1000
23 #define NSM_RESPONSE_BUF_SIZE 0x3000
24 #define NSM_RND_BUF_SIZE 256
25
26 enum NSMResponseTypes {
27 NSM_SUCCESS = 0,
28 NSM_INVALID_ARGUMENT = 1,
29 NSM_INVALID_INDEX = 2,
30 NSM_READONLY_INDEX = 3,
31 NSM_INVALID_OPERATION = 4,
32 NSM_BUFFER_TOO_SMALL = 5,
33 NSM_INPUT_TOO_LARGE = 6,
34 NSM_INTERNAL_ERROR = 7,
35 };
36
error_string(enum NSMResponseTypes type)37 static const char *error_string(enum NSMResponseTypes type)
38 {
39 const char *str;
40 switch (type) {
41 case NSM_INVALID_ARGUMENT:
42 str = "InvalidArgument";
43 break;
44 case NSM_INVALID_INDEX:
45 str = "InvalidIndex";
46 break;
47 case NSM_READONLY_INDEX:
48 str = "ReadOnlyIndex";
49 break;
50 case NSM_INVALID_OPERATION:
51 str = "InvalidOperation";
52 break;
53 case NSM_BUFFER_TOO_SMALL:
54 str = "BufferTooSmall";
55 break;
56 case NSM_INPUT_TOO_LARGE:
57 str = "InputTooLarge";
58 break;
59 default:
60 str = "InternalError";
61 break;
62 }
63
64 return str;
65 }
66
67 /*
68 * Error response structure:
69 *
70 * {
71 * Map(1) {
72 * key = String("Error"),
73 * value = String(error_name)
74 * }
75 * }
76 *
77 * where error_name can be one of the following:
78 * InvalidArgument
79 * InvalidIndex
80 * InvalidResponse
81 * ReadOnlyIndex
82 * InvalidOperation
83 * BufferTooSmall
84 * InputTooLarge
85 * InternalError
86 */
87
error_response(struct iovec * response,enum NSMResponseTypes error,Error ** errp)88 static bool error_response(struct iovec *response, enum NSMResponseTypes error,
89 Error **errp)
90 {
91 cbor_item_t *root;
92 size_t len;
93 bool r = false;
94
95 root = cbor_new_definite_map(1);
96 if (!root) {
97 goto err;
98 }
99
100 if (!qemu_cbor_add_string_to_map(root, "Error", error_string(error))) {
101 goto err;
102 }
103
104 len = cbor_serialize(root, response->iov_base, response->iov_len);
105 if (len == 0) {
106 error_setg(errp, "Response buffer is small for %s response",
107 error_string(error));
108 goto out;
109 }
110 response->iov_len = len;
111 r = true;
112
113 out:
114 if (root) {
115 cbor_decref(&root);
116 }
117 return r;
118
119 err:
120 error_setg(errp, "Failed to initialize %s response", error_string(error));
121 goto out;
122 }
123
124 /*
125 * GetRandom response structure:
126 *
127 * {
128 * Map(1) {
129 * key = String("GetRandom"),
130 * value = Map(1) {
131 * key = String("random"),
132 * value = Byte_String()
133 * }
134 * }
135 * }
136 */
handle_get_random(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)137 static bool handle_get_random(VirtIONSM *vnsm, struct iovec *request,
138 struct iovec *response, Error **errp)
139 {
140 cbor_item_t *root, *nested_map;
141 size_t len;
142 uint8_t rnd[NSM_RND_BUF_SIZE];
143 bool r = false;
144
145 root = cbor_new_definite_map(1);
146 if (!root) {
147 goto err;
148 }
149
150 if (!qemu_cbor_add_map_to_map(root, "GetRandom", 1, &nested_map)) {
151 goto err;
152 }
153
154 qemu_guest_getrandom_nofail(rnd, NSM_RND_BUF_SIZE);
155
156 if (!qemu_cbor_add_bytestring_to_map(nested_map, "random", rnd,
157 NSM_RND_BUF_SIZE)) {
158 goto err;
159 }
160
161 len = cbor_serialize(root, response->iov_base, response->iov_len);
162 if (len == 0) {
163 if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
164 r = true;
165 }
166 goto out;
167 }
168
169 response->iov_len = len;
170 r = true;
171
172 out:
173 if (root) {
174 cbor_decref(&root);
175 }
176 return r;
177
178 err:
179 error_setg(errp, "Failed to initialize GetRandom response");
180 goto out;
181 }
182
183 /*
184 * DescribeNSM response structure:
185 *
186 * {
187 * Map(1) {
188 * key = String("DescribeNSM"),
189 * value = Map(7) {
190 * key = String("digest"),
191 * value = String("SHA384"),
192 * key = String("max_pcrs"),
193 * value = Uint8(32),
194 * key = String("module_id"),
195 * value = String("i-1234-enc5678"),
196 * key = String("locked_pcrs"),
197 * value = Array<Uint8>(),
198 * key = String("version_major"),
199 * value = Uint8(1),
200 * key = String("version_minor"),
201 * value = Uint8(0),
202 * key = String("version_patch"),
203 * value = Uint8(0)
204 * }
205 * }
206 * }
207 */
handle_describe_nsm(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)208 static bool handle_describe_nsm(VirtIONSM *vnsm, struct iovec *request,
209 struct iovec *response, Error **errp)
210 {
211 cbor_item_t *root, *nested_map;
212 uint16_t locked_pcrs_cnt = 0;
213 uint8_t locked_pcrs_ind[NSM_MAX_PCRS];
214 size_t len;
215 bool r = false;
216
217 root = cbor_new_definite_map(1);
218 if (!root) {
219 goto err;
220 }
221
222 if (!qemu_cbor_add_map_to_map(root, "DescribeNSM", 7, &nested_map)) {
223 goto err;
224 }
225
226 if (!qemu_cbor_add_string_to_map(nested_map, "digest", vnsm->digest)) {
227 goto err;
228 }
229
230 if (!qemu_cbor_add_uint8_to_map(nested_map, "max_pcrs", vnsm->max_pcrs)) {
231 goto err;
232 }
233
234 if (!qemu_cbor_add_string_to_map(nested_map, "module_id",
235 vnsm->module_id)) {
236 goto err;
237 }
238
239 for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
240 if (vnsm->pcrs[i].locked) {
241 locked_pcrs_ind[locked_pcrs_cnt++] = i;
242 }
243 }
244 if (!qemu_cbor_add_uint8_array_to_map(nested_map, "locked_pcrs",
245 locked_pcrs_ind, locked_pcrs_cnt)) {
246 goto err;
247 }
248
249 if (!qemu_cbor_add_uint8_to_map(nested_map, "version_major",
250 vnsm->version_major)) {
251 goto err;
252 }
253
254 if (!qemu_cbor_add_uint8_to_map(nested_map, "version_minor",
255 vnsm->version_minor)) {
256 goto err;
257 }
258
259 if (!qemu_cbor_add_uint8_to_map(nested_map, "version_patch",
260 vnsm->version_patch)) {
261 goto err;
262 }
263
264 len = cbor_serialize(root, response->iov_base, response->iov_len);
265 if (len == 0) {
266 if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
267 r = true;
268 }
269 goto out;
270 }
271
272 response->iov_len = len;
273 r = true;
274
275 out:
276 if (root) {
277 cbor_decref(&root);
278 }
279 return r;
280
281 err:
282 error_setg(errp, "Failed to initialize DescribeNSM response");
283 goto out;
284 }
285
286 /*
287 * DescribePCR request structure:
288 *
289 * {
290 * Map(1) {
291 * key = String("DescribePCR"),
292 * value = Map(1) {
293 * key = String("index"),
294 * value = Uint8(pcr)
295 * }
296 * }
297 * }
298 */
299 typedef struct NSMDescribePCRReq {
300 uint8_t index;
301 } NSMDescribePCRReq;
302
get_nsm_describe_pcr_req(uint8_t * req,size_t len,NSMDescribePCRReq * nsm_req)303 static enum NSMResponseTypes get_nsm_describe_pcr_req(
304 uint8_t *req, size_t len,
305 NSMDescribePCRReq *nsm_req)
306 {
307 size_t size;
308 uint8_t *str;
309 struct cbor_pair *pair;
310 cbor_item_t *item = NULL;
311 struct cbor_load_result result;
312 enum NSMResponseTypes r = NSM_INVALID_OPERATION;
313
314 item = cbor_load(req, len, &result);
315 if (!item || result.error.code != CBOR_ERR_NONE) {
316 goto cleanup;
317 }
318
319 pair = cbor_map_handle(item);
320 if (!cbor_isa_map(pair->value)) {
321 goto cleanup;
322 }
323 size = cbor_map_size(pair->value);
324 if (size < 1) {
325 goto cleanup;
326 }
327
328 pair = cbor_map_handle(pair->value);
329 for (int i = 0; i < size; ++i) {
330 if (!cbor_isa_string(pair[i].key)) {
331 continue;
332 }
333
334 str = cbor_string_handle(pair[i].key);
335 if (str && cbor_string_length(pair[i].key) == 5 &&
336 memcmp(str, "index", 5) == 0) {
337 if (!cbor_isa_uint(pair[i].value) ||
338 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
339 break;
340 }
341
342 nsm_req->index = cbor_get_uint8(pair[i].value);
343 r = NSM_SUCCESS;
344 break;
345 }
346 }
347
348 cleanup:
349 if (item) {
350 cbor_decref(&item);
351 }
352 return r;
353 }
354
355 /*
356 * DescribePCR response structure:
357 *
358 * {
359 * Map(1) {
360 * key = String("DescribePCR"),
361 * value = Map(2) {
362 * key = String("data"),
363 * value = Byte_String(),
364 * key = String("lock"),
365 * value = Bool()
366 * }
367 * }
368 * }
369 */
handle_describe_pcr(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)370 static bool handle_describe_pcr(VirtIONSM *vnsm, struct iovec *request,
371 struct iovec *response, Error **errp)
372 {
373 cbor_item_t *root = NULL;
374 cbor_item_t *nested_map;
375 size_t len;
376 NSMDescribePCRReq nsm_req;
377 enum NSMResponseTypes type;
378 struct PCRInfo *pcr;
379 bool r = false;
380
381 type = get_nsm_describe_pcr_req(request->iov_base, request->iov_len,
382 &nsm_req);
383 if (type != NSM_SUCCESS) {
384 if (error_response(response, type, errp)) {
385 r = true;
386 }
387 goto out;
388 }
389 if (nsm_req.index >= vnsm->max_pcrs) {
390 if (error_response(response, NSM_INVALID_INDEX, errp)) {
391 r = true;
392 }
393 goto out;
394 }
395 pcr = &(vnsm->pcrs[nsm_req.index]);
396
397 root = cbor_new_definite_map(1);
398 if (!root) {
399 goto err;
400 }
401
402 if (!qemu_cbor_add_map_to_map(root, "DescribePCR", 2, &nested_map)) {
403 goto err;
404 }
405
406 if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
407 QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
408 goto err;
409 }
410
411 if (!qemu_cbor_add_bool_to_map(nested_map, "lock", pcr->locked)) {
412 goto err;
413 }
414
415 len = cbor_serialize(root, response->iov_base, response->iov_len);
416 if (len == 0) {
417 if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
418 r = true;
419 }
420 goto out;
421 }
422
423 response->iov_len = len;
424 r = true;
425
426 out:
427 if (root) {
428 cbor_decref(&root);
429 }
430 return r;
431
432 err:
433 error_setg(errp, "Failed to initialize DescribePCR response");
434 goto out;
435 }
436
437 /*
438 * ExtendPCR request structure:
439 *
440 * {
441 * Map(1) {
442 * key = String("ExtendPCR"),
443 * value = Map(2) {
444 * key = String("index"),
445 * value = Uint8(pcr),
446 * key = String("data"),
447 * value = Byte_String(data) || String(data),
448 * }
449 * }
450 * }
451 */
452 typedef struct NSMExtendPCRReq {
453 uint8_t index;
454 uint16_t data_len;
455 uint8_t data[NSM_REQUEST_MAX_SIZE];
456 } NSMExtendPCRReq;
457
get_nsm_extend_pcr_req(uint8_t * req,size_t len,NSMExtendPCRReq * nsm_req)458 static enum NSMResponseTypes get_nsm_extend_pcr_req(uint8_t *req, size_t len,
459 NSMExtendPCRReq *nsm_req)
460 {
461 cbor_item_t *item = NULL;
462 size_t size ;
463 uint8_t *str;
464 bool index_found = false;
465 bool data_found = false;
466 struct cbor_pair *pair;
467 struct cbor_load_result result;
468 enum NSMResponseTypes r = NSM_INVALID_OPERATION;
469
470 item = cbor_load(req, len, &result);
471 if (!item || result.error.code != CBOR_ERR_NONE) {
472 goto cleanup;
473 }
474
475 pair = cbor_map_handle(item);
476 if (!cbor_isa_map(pair->value)) {
477 goto cleanup;
478 }
479 size = cbor_map_size(pair->value);
480 if (size < 2) {
481 goto cleanup;
482 }
483
484 pair = cbor_map_handle(pair->value);
485 for (int i = 0; i < size; ++i) {
486 if (!cbor_isa_string(pair[i].key)) {
487 continue;
488 }
489 str = cbor_string_handle(pair[i].key);
490 if (!str) {
491 continue;
492 }
493
494 if (cbor_string_length(pair[i].key) == 5 &&
495 memcmp(str, "index", 5) == 0) {
496 if (!cbor_isa_uint(pair[i].value) ||
497 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
498 goto cleanup;
499 }
500 nsm_req->index = cbor_get_uint8(pair[i].value);
501 index_found = true;
502 continue;
503 }
504
505 if (cbor_string_length(pair[i].key) == 4 &&
506 memcmp(str, "data", 4) == 0) {
507 if (cbor_isa_bytestring(pair[i].value)) {
508 str = cbor_bytestring_handle(pair[i].value);
509 if (!str) {
510 goto cleanup;
511 }
512 nsm_req->data_len = cbor_bytestring_length(pair[i].value);
513 } else if (cbor_isa_string(pair[i].value)) {
514 str = cbor_string_handle(pair[i].value);
515 if (!str) {
516 goto cleanup;
517 }
518 nsm_req->data_len = cbor_string_length(pair[i].value);
519 } else {
520 goto cleanup;
521 }
522 /*
523 * nsm_req->data_len will be smaller than NSM_REQUEST_MAX_SIZE as
524 * we already check for the max request size before processing
525 * any request. So it's safe to copy.
526 */
527 memcpy(nsm_req->data, str, nsm_req->data_len);
528 data_found = true;
529 continue;
530 }
531 }
532
533 if (index_found && data_found) {
534 r = NSM_SUCCESS;
535 }
536
537 cleanup:
538 if (item) {
539 cbor_decref(&item);
540 }
541 return r;
542 }
543
544 /*
545 * ExtendPCR response structure:
546 *
547 * {
548 * Map(1) {
549 * key = String("ExtendPCR"),
550 * value = Map(1) {
551 * key = String("data"),
552 * value = Byte_String()
553 * }
554 * }
555 * }
556 */
handle_extend_pcr(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)557 static bool handle_extend_pcr(VirtIONSM *vnsm, struct iovec *request,
558 struct iovec *response, Error **errp)
559 {
560 cbor_item_t *root = NULL;
561 cbor_item_t *nested_map;
562 size_t len;
563 struct PCRInfo *pcr;
564 enum NSMResponseTypes type;
565 bool r = false;
566 g_autofree NSMExtendPCRReq *nsm_req = g_malloc(sizeof(NSMExtendPCRReq));
567
568 type = get_nsm_extend_pcr_req(request->iov_base, request->iov_len,
569 nsm_req);
570 if (type != NSM_SUCCESS) {
571 if (error_response(response, type, errp)) {
572 r = true;
573 }
574 goto out;
575 }
576 if (nsm_req->index >= vnsm->max_pcrs) {
577 if (error_response(response, NSM_INVALID_INDEX, errp)) {
578 r = true;
579 }
580 goto out;
581 }
582
583 pcr = &(vnsm->pcrs[nsm_req->index]);
584
585 if (pcr->locked) {
586 if (error_response(response, NSM_READONLY_INDEX, errp)) {
587 r = true;
588 }
589 goto out;
590 }
591
592 if (!vnsm->extend_pcr(vnsm, nsm_req->index, nsm_req->data,
593 nsm_req->data_len)) {
594 if (error_response(response, NSM_INTERNAL_ERROR, errp)) {
595 r = true;
596 }
597 goto out;
598 }
599
600 root = cbor_new_definite_map(1);
601 if (!root) {
602 goto err;
603 }
604
605 if (!qemu_cbor_add_map_to_map(root, "ExtendPCR", 1, &nested_map)) {
606 goto err;
607 }
608
609 if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
610 QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
611 goto err;
612 }
613
614 len = cbor_serialize(root, response->iov_base, response->iov_len);
615 if (len == 0) {
616 if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
617 r = true;
618 }
619 goto out;
620 }
621
622 response->iov_len = len;
623 r = true;
624
625 out:
626 if (root) {
627 cbor_decref(&root);
628 }
629 return r;
630
631 err:
632 error_setg(errp, "Failed to initialize DescribePCR response");
633 goto out;
634 }
635
636 /*
637 * LockPCR request structure:
638 *
639 * {
640 * Map(1) {
641 * key = String("LockPCR"),
642 * value = Map(1) {
643 * key = String("index"),
644 * value = Uint8(pcr)
645 * }
646 * }
647 * }
648 */
649 typedef struct NSMLockPCRReq {
650 uint8_t index;
651 } NSMLockPCRReq;
652
get_nsm_lock_pcr_req(uint8_t * req,size_t len,NSMLockPCRReq * nsm_req)653 static enum NSMResponseTypes get_nsm_lock_pcr_req(uint8_t *req, size_t len,
654 NSMLockPCRReq *nsm_req)
655 {
656 cbor_item_t *item = NULL;
657 size_t size;
658 uint8_t *str;
659 struct cbor_pair *pair;
660 struct cbor_load_result result;
661 enum NSMResponseTypes r = NSM_INVALID_OPERATION;
662
663 item = cbor_load(req, len, &result);
664 if (!item || result.error.code != CBOR_ERR_NONE) {
665 goto cleanup;
666 }
667
668 pair = cbor_map_handle(item);
669 if (!cbor_isa_map(pair->value)) {
670 goto cleanup;
671 }
672 size = cbor_map_size(pair->value);
673 if (size < 1) {
674 goto cleanup;
675 }
676
677 pair = cbor_map_handle(pair->value);
678 for (int i = 0; i < size; ++i) {
679 if (!cbor_isa_string(pair[i].key)) {
680 continue;
681 }
682 str = cbor_string_handle(pair[i].key);
683 if (str && cbor_string_length(pair[i].key) == 5 &&
684 memcmp(str, "index", 5) == 0) {
685 if (!cbor_isa_uint(pair[i].value) ||
686 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
687 break;
688 }
689
690 nsm_req->index = cbor_get_uint8(pair[i].value);
691 r = NSM_SUCCESS;
692 break;
693 }
694 }
695
696 cleanup:
697 if (item) {
698 cbor_decref(&item);
699 }
700 return r;
701 }
702
703 /*
704 * LockPCR success response structure:
705 * {
706 * String("LockPCR")
707 * }
708 */
handle_lock_pcr(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)709 static bool handle_lock_pcr(VirtIONSM *vnsm, struct iovec *request,
710 struct iovec *response, Error **errp)
711 {
712 cbor_item_t *root = NULL;
713 size_t len;
714 NSMLockPCRReq nsm_req;
715 enum NSMResponseTypes type;
716 struct PCRInfo *pcr;
717 bool r = false;
718
719 type = get_nsm_lock_pcr_req(request->iov_base, request->iov_len, &nsm_req);
720 if (type != NSM_SUCCESS) {
721 if (error_response(response, type, errp)) {
722 r = true;
723 }
724 goto cleanup;
725 }
726 if (nsm_req.index >= vnsm->max_pcrs) {
727 if (error_response(response, NSM_INVALID_INDEX, errp)) {
728 r = true;
729 }
730 goto cleanup;
731 }
732
733 pcr = &(vnsm->pcrs[nsm_req.index]);
734
735 if (pcr->locked) {
736 if (error_response(response, NSM_READONLY_INDEX, errp)) {
737 r = true;
738 }
739 goto cleanup;
740 }
741
742 pcr->locked = true;
743
744 root = cbor_build_string("LockPCR");
745 if (!root) {
746 goto err;
747 }
748
749 len = cbor_serialize(root, response->iov_base, response->iov_len);
750 if (len == 0) {
751 if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
752 r = true;
753 }
754 goto cleanup;
755 }
756
757 response->iov_len = len;
758 r = true;
759 goto cleanup;
760
761 err:
762 error_setg(errp, "Failed to initialize LockPCR response");
763
764 cleanup:
765 if (root) {
766 cbor_decref(&root);
767 }
768 return r;
769 }
770
771 /*
772 * LockPCRs request structure:
773 *
774 * {
775 * Map(1) {
776 * key = String("LockPCRs"),
777 * value = Map(1) {
778 * key = String("range"),
779 * value = Uint8(pcr)
780 * }
781 * }
782 * }
783 */
784 typedef struct NSMLockPCRsReq {
785 uint16_t range;
786 } NSMLockPCRsReq;
787
get_nsm_lock_pcrs_req(uint8_t * req,size_t len,NSMLockPCRsReq * nsm_req)788 static enum NSMResponseTypes get_nsm_lock_pcrs_req(uint8_t *req, size_t len,
789 NSMLockPCRsReq *nsm_req)
790 {
791 cbor_item_t *item = NULL;
792 size_t size;
793 uint8_t *str;
794 struct cbor_pair *pair;
795 struct cbor_load_result result;
796 enum NSMResponseTypes r = NSM_INVALID_OPERATION;
797
798 item = cbor_load(req, len, &result);
799 if (!item || result.error.code != CBOR_ERR_NONE) {
800 goto cleanup;
801 }
802
803 pair = cbor_map_handle(item);
804 if (!cbor_isa_map(pair->value)) {
805 goto cleanup;
806 }
807 size = cbor_map_size(pair->value);
808 if (size < 1) {
809 goto cleanup;
810 }
811
812 pair = cbor_map_handle(pair->value);
813 for (int i = 0; i < size; ++i) {
814 if (!cbor_isa_string(pair[i].key)) {
815 continue;
816 }
817 str = cbor_string_handle(pair[i].key);
818 if (str && cbor_string_length(pair[i].key) == 5 &&
819 memcmp(str, "range", 5) == 0) {
820 if (!cbor_isa_uint(pair[i].value) ||
821 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
822 break;
823 }
824
825 nsm_req->range = cbor_get_uint8(pair[i].value);
826 r = NSM_SUCCESS;
827 break;
828 }
829 }
830
831 cleanup:
832 if (item) {
833 cbor_decref(&item);
834 }
835 return r;
836 }
837
838 /*
839 * LockPCRs success response structure:
840 * {
841 * String("LockPCRs")
842 * }
843 */
handle_lock_pcrs(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)844 static bool handle_lock_pcrs(VirtIONSM *vnsm, struct iovec *request,
845 struct iovec *response, Error **errp)
846 {
847 cbor_item_t *root = NULL;
848 size_t len;
849 NSMLockPCRsReq nsm_req;
850 enum NSMResponseTypes type;
851 bool r = false;
852
853 type = get_nsm_lock_pcrs_req(request->iov_base, request->iov_len, &nsm_req);
854 if (type != NSM_SUCCESS) {
855 if (error_response(response, type, errp)) {
856 r = true;
857 }
858 goto cleanup;
859 }
860 if (nsm_req.range > vnsm->max_pcrs) {
861 if (error_response(response, NSM_INVALID_INDEX, errp)) {
862 r = true;
863 }
864 goto cleanup;
865 }
866
867 for (int i = 0; i < nsm_req.range; ++i) {
868 vnsm->pcrs[i].locked = true;
869 }
870
871 root = cbor_build_string("LockPCRs");
872 if (!root) {
873 goto err;
874 }
875
876 len = cbor_serialize(root, response->iov_base, response->iov_len);
877 if (len == 0) {
878 if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
879 r = true;
880 }
881 goto cleanup;
882 }
883
884 response->iov_len = len;
885 r = true;
886 goto cleanup;
887
888 err:
889 error_setg(errp, "Failed to initialize response");
890
891 cleanup:
892 if (root) {
893 cbor_decref(&root);
894 }
895 return r;
896 }
897
898 /*
899 * Attestation request structure:
900 *
901 * Map(1) {
902 * key = String("Attestation"),
903 * value = Map(3) {
904 * key = String("user_data"),
905 * value = Byte_String() || null, // Optional
906 * key = String("nonce"),
907 * value = Byte_String() || null, // Optional
908 * key = String("public_key"),
909 * value = Byte_String() || null, // Optional
910 * }
911 * }
912 * }
913 */
914
915 struct AttestationProperty {
916 bool is_null; /* True if property is not present in map or is null */
917 uint16_t len;
918 uint8_t buf[NSM_REQUEST_MAX_SIZE];
919 };
920
921 typedef struct NSMAttestationReq {
922 struct AttestationProperty public_key;
923 struct AttestationProperty user_data;
924 struct AttestationProperty nonce;
925 } NSMAttestationReq;
926
fill_attestation_property(struct AttestationProperty * prop,cbor_item_t * value)927 static bool fill_attestation_property(struct AttestationProperty *prop,
928 cbor_item_t *value)
929 {
930 uint8_t *str;
931 bool ret = false;
932
933 if (cbor_is_null(value)) {
934 prop->is_null = true;
935 ret = true;
936 goto out;
937 } else if (cbor_isa_bytestring(value)) {
938 str = cbor_bytestring_handle(value);
939 if (!str) {
940 goto out;
941 }
942 prop->len = cbor_bytestring_length(value);
943 } else if (cbor_isa_string(value)) {
944 str = cbor_string_handle(value);
945 if (!str) {
946 goto out;
947 }
948 prop->len = cbor_string_length(value);
949 } else {
950 goto out;
951 }
952
953 /*
954 * prop->len will be smaller than NSM_REQUEST_MAX_SIZE as we
955 * already check for the max request size before processing
956 * any request. So it's safe to copy.
957 */
958 memcpy(prop->buf, str, prop->len);
959 prop->is_null = false;
960 ret = true;
961
962 out:
963 return ret;
964 }
965
get_nsm_attestation_req(uint8_t * req,size_t len,NSMAttestationReq * nsm_req)966 static enum NSMResponseTypes get_nsm_attestation_req(uint8_t *req, size_t len,
967 NSMAttestationReq *nsm_req)
968 {
969 cbor_item_t *item = NULL;
970 size_t size;
971 uint8_t *str;
972 struct cbor_pair *pair;
973 struct cbor_load_result result;
974 enum NSMResponseTypes r = NSM_INVALID_OPERATION;
975
976 nsm_req->public_key.is_null = true;
977 nsm_req->user_data.is_null = true;
978 nsm_req->nonce.is_null = true;
979
980 item = cbor_load(req, len, &result);
981 if (!item || result.error.code != CBOR_ERR_NONE) {
982 goto cleanup;
983 }
984
985 pair = cbor_map_handle(item);
986 if (!cbor_isa_map(pair->value)) {
987 goto cleanup;
988 }
989 size = cbor_map_size(pair->value);
990 if (size == 0) {
991 r = NSM_SUCCESS;
992 goto cleanup;
993 }
994
995 pair = cbor_map_handle(pair->value);
996 for (int i = 0; i < size; ++i) {
997 if (!cbor_isa_string(pair[i].key)) {
998 continue;
999 }
1000
1001 str = cbor_string_handle(pair[i].key);
1002 if (!str) {
1003 continue;
1004 }
1005
1006 if (cbor_string_length(pair[i].key) == 10 &&
1007 memcmp(str, "public_key", 10) == 0) {
1008 if (!fill_attestation_property(&(nsm_req->public_key),
1009 pair[i].value)) {
1010 goto cleanup;
1011 }
1012 continue;
1013 }
1014
1015 if (cbor_string_length(pair[i].key) == 9 &&
1016 memcmp(str, "user_data", 9) == 0) {
1017 if (!fill_attestation_property(&(nsm_req->user_data),
1018 pair[i].value)) {
1019 goto cleanup;
1020 }
1021 continue;
1022 }
1023
1024 if (cbor_string_length(pair[i].key) == 5 &&
1025 memcmp(str, "nonce", 5) == 0) {
1026 if (!fill_attestation_property(&(nsm_req->nonce), pair[i].value)) {
1027 goto cleanup;
1028 }
1029 continue;
1030 }
1031 }
1032
1033 r = NSM_SUCCESS;
1034
1035 cleanup:
1036 if (item) {
1037 cbor_decref(&item);
1038 }
1039 return r;
1040 }
1041
add_protected_header_to_cose(cbor_item_t * cose)1042 static bool add_protected_header_to_cose(cbor_item_t *cose)
1043 {
1044 cbor_item_t *map = NULL;
1045 cbor_item_t *key = NULL;
1046 cbor_item_t *value = NULL;
1047 cbor_item_t *bs = NULL;
1048 size_t len;
1049 bool r = false;
1050 size_t buf_len = 4096;
1051 g_autofree uint8_t *buf = g_malloc(buf_len);
1052
1053 map = cbor_new_definite_map(1);
1054 if (!map) {
1055 goto cleanup;
1056 }
1057 key = cbor_build_uint8(1);
1058 if (!key) {
1059 goto cleanup;
1060 }
1061 value = cbor_new_int8();
1062 if (!value) {
1063 goto cleanup;
1064 }
1065 cbor_mark_negint(value);
1066 /* we don't actually sign the data, so we use -1 as the 'alg' value */
1067 cbor_set_uint8(value, 0);
1068
1069 if (!qemu_cbor_map_add(map, key, value)) {
1070 goto cleanup;
1071 }
1072
1073 len = cbor_serialize(map, buf, buf_len);
1074 if (len == 0) {
1075 goto cleanup_map;
1076 }
1077
1078 bs = cbor_build_bytestring(buf, len);
1079 if (!bs) {
1080 goto cleanup_map;
1081 }
1082 if (!qemu_cbor_array_push(cose, bs)) {
1083 cbor_decref(&bs);
1084 goto cleanup_map;
1085 }
1086 r = true;
1087 goto cleanup_map;
1088
1089 cleanup:
1090 if (key) {
1091 cbor_decref(&key);
1092 }
1093 if (value) {
1094 cbor_decref(&value);
1095 }
1096
1097 cleanup_map:
1098 if (map) {
1099 cbor_decref(&map);
1100 }
1101 return r;
1102 }
1103
add_unprotected_header_to_cose(cbor_item_t * cose)1104 static bool add_unprotected_header_to_cose(cbor_item_t *cose)
1105 {
1106 cbor_item_t *map = cbor_new_definite_map(0);
1107 if (!map) {
1108 goto cleanup;
1109 }
1110 if (!qemu_cbor_array_push(cose, map)) {
1111 goto cleanup;
1112 }
1113
1114 return true;
1115
1116 cleanup:
1117 if (map) {
1118 cbor_decref(&map);
1119 }
1120 return false;
1121 }
1122
add_ca_bundle_to_payload(cbor_item_t * map)1123 static bool add_ca_bundle_to_payload(cbor_item_t *map)
1124 {
1125 cbor_item_t *key_cbor = NULL;
1126 cbor_item_t *value_cbor = NULL;
1127 cbor_item_t *bs = NULL;
1128 uint8_t zero[64] = {0};
1129
1130 key_cbor = cbor_build_string("cabundle");
1131 if (!key_cbor) {
1132 goto cleanup;
1133 }
1134 value_cbor = cbor_new_definite_array(1);
1135 if (!value_cbor) {
1136 goto cleanup;
1137 }
1138 bs = cbor_build_bytestring(zero, 64);
1139 if (!bs) {
1140 goto cleanup;
1141 }
1142 if (!qemu_cbor_array_push(value_cbor, bs)) {
1143 cbor_decref(&bs);
1144 goto cleanup;
1145 }
1146 if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
1147 goto cleanup;
1148 }
1149
1150 return true;
1151
1152 cleanup:
1153 if (key_cbor) {
1154 cbor_decref(&key_cbor);
1155 }
1156 if (value_cbor) {
1157 cbor_decref(&value_cbor);
1158 }
1159 return false;
1160 }
1161
add_payload_to_cose(cbor_item_t * cose,VirtIONSM * vnsm,NSMAttestationReq * req)1162 static bool add_payload_to_cose(cbor_item_t *cose, VirtIONSM *vnsm,
1163 NSMAttestationReq *req)
1164 {
1165 cbor_item_t *root = NULL;
1166 cbor_item_t *nested_map;
1167 cbor_item_t *bs = NULL;
1168 size_t locked_cnt;
1169 uint8_t ind[NSM_MAX_PCRS];
1170 size_t payload_map_size = 9;
1171 size_t len;
1172 struct PCRInfo *pcr;
1173 uint8_t zero[64] = {0};
1174 bool r = false;
1175 size_t buf_len = 16384;
1176 g_autofree uint8_t *buf = g_malloc(buf_len);
1177
1178 root = cbor_new_definite_map(payload_map_size);
1179 if (!root) {
1180 goto cleanup;
1181 }
1182 if (!qemu_cbor_add_string_to_map(root, "module_id", vnsm->module_id)) {
1183 goto cleanup;
1184 }
1185 if (!qemu_cbor_add_string_to_map(root, "digest", vnsm->digest)) {
1186 goto cleanup;
1187 }
1188 if (!qemu_cbor_add_uint64_to_map(root, "timestamp",
1189 (uint64_t) time(NULL) * 1000)) {
1190 goto cleanup;
1191 }
1192
1193 locked_cnt = 0;
1194 for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
1195 if (vnsm->pcrs[i].locked) {
1196 ind[locked_cnt++] = i;
1197 }
1198 }
1199 if (!qemu_cbor_add_map_to_map(root, "pcrs", locked_cnt, &nested_map)) {
1200 goto cleanup;
1201 }
1202 for (uint8_t i = 0; i < locked_cnt; ++i) {
1203 pcr = &(vnsm->pcrs[ind[i]]);
1204 if (!qemu_cbor_add_uint8_key_bytestring_to_map(
1205 nested_map, ind[i],
1206 pcr->data,
1207 QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
1208 goto cleanup;
1209 }
1210 }
1211 if (!qemu_cbor_add_bytestring_to_map(root, "certificate", zero, 64)) {
1212 goto cleanup;
1213 }
1214 if (!add_ca_bundle_to_payload(root)) {
1215 goto cleanup;
1216 }
1217
1218 if (req->public_key.is_null) {
1219 if (!qemu_cbor_add_null_to_map(root, "public_key")) {
1220 goto cleanup;
1221 }
1222 } else if (!qemu_cbor_add_bytestring_to_map(root, "public_key",
1223 req->public_key.buf,
1224 req->public_key.len)) {
1225 goto cleanup;
1226 }
1227
1228 if (req->user_data.is_null) {
1229 if (!qemu_cbor_add_null_to_map(root, "user_data")) {
1230 goto cleanup;
1231 }
1232 } else if (!qemu_cbor_add_bytestring_to_map(root, "user_data",
1233 req->user_data.buf,
1234 req->user_data.len)) {
1235 goto cleanup;
1236 }
1237
1238 if (req->nonce.is_null) {
1239 if (!qemu_cbor_add_null_to_map(root, "nonce")) {
1240 goto cleanup;
1241 }
1242 } else if (!qemu_cbor_add_bytestring_to_map(root, "nonce",
1243 req->nonce.buf,
1244 req->nonce.len)) {
1245 goto cleanup;
1246 }
1247
1248 len = cbor_serialize(root, buf, buf_len);
1249 if (len == 0) {
1250 goto cleanup;
1251 }
1252
1253 bs = cbor_build_bytestring(buf, len);
1254 if (!bs) {
1255 goto cleanup;
1256 }
1257 if (!qemu_cbor_array_push(cose, bs)) {
1258 cbor_decref(&bs);
1259 goto cleanup;
1260 }
1261
1262 r = true;
1263
1264 cleanup:
1265 if (root) {
1266 cbor_decref(&root);
1267 }
1268 return r;
1269 }
1270
add_signature_to_cose(cbor_item_t * cose)1271 static bool add_signature_to_cose(cbor_item_t *cose)
1272 {
1273 cbor_item_t *bs = NULL;
1274 uint8_t zero[64] = {0};
1275
1276 /* we don't actually sign the data, so we just put 64 zero bytes */
1277 bs = cbor_build_bytestring(zero, 64);
1278 if (!bs) {
1279 goto cleanup;
1280 }
1281
1282 if (!qemu_cbor_array_push(cose, bs)) {
1283 goto cleanup;
1284 }
1285
1286 return true;
1287
1288 cleanup:
1289 if (bs) {
1290 cbor_decref(&bs);
1291 }
1292 return false;
1293 }
1294
1295 /*
1296 * Attestation response structure:
1297 *
1298 * {
1299 * Map(1) {
1300 * key = String("Attestation"),
1301 * value = Map(1) {
1302 * key = String("document"),
1303 * value = Byte_String()
1304 * }
1305 * }
1306 * }
1307 *
1308 * The document is a serialized COSE sign1 blob of the structure:
1309 * {
1310 * Array(4) {
1311 * [0] { ByteString() }, // serialized protected header
1312 * [1] { Map(0) }, // 0 length map
1313 * [2] { ByteString() }, // serialized payload
1314 * [3] { ByteString() }, // signature
1315 * }
1316 * }
1317 *
1318 * where [0] protected header is a serialized CBOR blob of the structure:
1319 * {
1320 * Map(1) {
1321 * key = Uint8(1) // alg
1322 * value = NegativeInt8() // Signing algorithm
1323 * }
1324 * }
1325 *
1326 * [2] payload is serialized CBOR blob of the structure:
1327 * {
1328 * Map(9) {
1329 * [0] { key = String("module_id"), value = String(module_id) },
1330 * [1] { key = String("digest"), value = String("SHA384") },
1331 * [2] {
1332 * key = String("timestamp"),
1333 * value = Uint64(unix epoch of when document was created)
1334 * },
1335 * [3] {
1336 * key = String("pcrs"),
1337 * value = Map(locked_pcr_cnt) {
1338 * key = Uint8(pcr_index),
1339 * value = ByteString(pcr_data)
1340 * },
1341 * },
1342 * [4] {
1343 * key = String("certificate"),
1344 * value = ByteString(Signing certificate)
1345 * },
1346 * [5] { key = String("cabundle"), value = Array(N) { ByteString()... } },
1347 * [6] { key = String("public_key"), value = ByteString() || null },
1348 * [7] { key = String("user_data"), value = ByteString() || null},
1349 * [8] { key = String("nonce"), value = ByteString() || null},
1350 * }
1351 * }
1352 */
handle_attestation(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)1353 static bool handle_attestation(VirtIONSM *vnsm, struct iovec *request,
1354 struct iovec *response, Error **errp)
1355 {
1356 cbor_item_t *root = NULL;
1357 cbor_item_t *cose = NULL;
1358 cbor_item_t *nested_map;
1359 size_t len;
1360 enum NSMResponseTypes type;
1361 bool r = false;
1362 size_t buf_len = 16384;
1363 g_autofree uint8_t *buf = g_malloc(buf_len);
1364 g_autofree NSMAttestationReq *nsm_req = g_malloc(sizeof(NSMAttestationReq));
1365
1366 nsm_req->public_key.is_null = true;
1367 nsm_req->user_data.is_null = true;
1368 nsm_req->nonce.is_null = true;
1369
1370 type = get_nsm_attestation_req(request->iov_base, request->iov_len,
1371 nsm_req);
1372 if (type != NSM_SUCCESS) {
1373 if (error_response(response, type, errp)) {
1374 r = true;
1375 }
1376 goto out;
1377 }
1378
1379 cose = cbor_new_definite_array(4);
1380 if (!cose) {
1381 goto err;
1382 }
1383 if (!add_protected_header_to_cose(cose)) {
1384 goto err;
1385 }
1386 if (!add_unprotected_header_to_cose(cose)) {
1387 goto err;
1388 }
1389 if (!add_payload_to_cose(cose, vnsm, nsm_req)) {
1390 goto err;
1391 }
1392 if (!add_signature_to_cose(cose)) {
1393 goto err;
1394 }
1395
1396 len = cbor_serialize(cose, buf, buf_len);
1397 if (len == 0) {
1398 goto err;
1399 }
1400
1401 root = cbor_new_definite_map(1);
1402 if (!root) {
1403 goto err;
1404 }
1405 if (!qemu_cbor_add_map_to_map(root, "Attestation", 1, &nested_map)) {
1406 goto err;
1407 }
1408 if (!qemu_cbor_add_bytestring_to_map(nested_map, "document", buf, len)) {
1409 goto err;
1410 }
1411
1412 len = cbor_serialize(root, response->iov_base, response->iov_len);
1413 if (len == 0) {
1414 if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
1415 r = true;
1416 }
1417 goto out;
1418 }
1419
1420 response->iov_len = len;
1421 r = true;
1422
1423 out:
1424 if (root) {
1425 cbor_decref(&root);
1426 }
1427 if (cose) {
1428 cbor_decref(&cose);
1429 }
1430 return r;
1431
1432 err:
1433 error_setg(errp, "Failed to initialize Attestation response");
1434 goto out;
1435 }
1436
1437 enum CBOR_ROOT_TYPE {
1438 CBOR_ROOT_TYPE_STRING = 0,
1439 CBOR_ROOT_TYPE_MAP = 1,
1440 };
1441
1442 struct nsm_cmd {
1443 char name[16];
1444 /*
1445 * There are 2 types of request
1446 * 1) String(); "GetRandom", "DescribeNSM"
1447 * 2) Map(1) { key: String(), value: ... }
1448 */
1449 enum CBOR_ROOT_TYPE root_type;
1450 bool (*response_fn)(VirtIONSM *vnsm, struct iovec *request,
1451 struct iovec *response, Error **errp);
1452 };
1453
1454 const struct nsm_cmd nsm_cmds[] = {
1455 { "GetRandom", CBOR_ROOT_TYPE_STRING, handle_get_random },
1456 { "DescribeNSM", CBOR_ROOT_TYPE_STRING, handle_describe_nsm },
1457 { "DescribePCR", CBOR_ROOT_TYPE_MAP, handle_describe_pcr },
1458 { "ExtendPCR", CBOR_ROOT_TYPE_MAP, handle_extend_pcr },
1459 { "LockPCR", CBOR_ROOT_TYPE_MAP, handle_lock_pcr },
1460 { "LockPCRs", CBOR_ROOT_TYPE_MAP, handle_lock_pcrs },
1461 { "Attestation", CBOR_ROOT_TYPE_MAP, handle_attestation },
1462 };
1463
get_nsm_request_cmd(uint8_t * buf,size_t len)1464 static const struct nsm_cmd *get_nsm_request_cmd(uint8_t *buf, size_t len)
1465 {
1466 size_t size;
1467 uint8_t *req;
1468 enum CBOR_ROOT_TYPE root_type;
1469 struct cbor_load_result result;
1470 cbor_item_t *item = cbor_load(buf, len, &result);
1471 if (!item || result.error.code != CBOR_ERR_NONE) {
1472 goto cleanup;
1473 }
1474
1475 if (cbor_isa_string(item)) {
1476 size = cbor_string_length(item);
1477 req = cbor_string_handle(item);
1478 root_type = CBOR_ROOT_TYPE_STRING;
1479 } else if (cbor_isa_map(item) && cbor_map_size(item) == 1) {
1480 struct cbor_pair *handle = cbor_map_handle(item);
1481 if (cbor_isa_string(handle->key)) {
1482 size = cbor_string_length(handle->key);
1483 req = cbor_string_handle(handle->key);
1484 root_type = CBOR_ROOT_TYPE_MAP;
1485 } else {
1486 goto cleanup;
1487 }
1488 } else {
1489 goto cleanup;
1490 }
1491
1492 if (size == 0 || req == NULL) {
1493 goto cleanup;
1494 }
1495
1496 for (int i = 0; i < ARRAY_SIZE(nsm_cmds); ++i) {
1497 if (nsm_cmds[i].root_type == root_type &&
1498 strlen(nsm_cmds[i].name) == size &&
1499 memcmp(nsm_cmds[i].name, req, size) == 0) {
1500 cbor_decref(&item);
1501 return &nsm_cmds[i];
1502 }
1503 }
1504
1505 cleanup:
1506 if (item) {
1507 cbor_decref(&item);
1508 }
1509 return NULL;
1510 }
1511
get_nsm_request_response(VirtIONSM * vnsm,struct iovec * req,struct iovec * resp,Error ** errp)1512 static bool get_nsm_request_response(VirtIONSM *vnsm, struct iovec *req,
1513 struct iovec *resp, Error **errp)
1514 {
1515 const struct nsm_cmd *cmd;
1516
1517 if (req->iov_len > NSM_REQUEST_MAX_SIZE) {
1518 if (error_response(resp, NSM_INPUT_TOO_LARGE, errp)) {
1519 return true;
1520 }
1521 error_setg(errp, "Failed to initialize InputTooLarge response");
1522 return false;
1523 }
1524
1525 cmd = get_nsm_request_cmd(req->iov_base, req->iov_len);
1526
1527 if (cmd == NULL) {
1528 if (error_response(resp, NSM_INVALID_OPERATION, errp)) {
1529 return true;
1530 }
1531 error_setg(errp, "Failed to initialize InvalidOperation response");
1532 return false;
1533 }
1534
1535 return cmd->response_fn(vnsm, req, resp, errp);
1536 }
1537
handle_input(VirtIODevice * vdev,VirtQueue * vq)1538 static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
1539 {
1540 g_autofree VirtQueueElement *out_elem = NULL;
1541 g_autofree VirtQueueElement *in_elem = NULL;
1542 VirtIONSM *vnsm = VIRTIO_NSM(vdev);
1543 Error *err = NULL;
1544 size_t sz;
1545 struct iovec req = {.iov_base = NULL, .iov_len = 0};
1546 struct iovec res = {.iov_base = NULL, .iov_len = 0};
1547
1548 out_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
1549 if (!out_elem) {
1550 /* nothing in virtqueue */
1551 return;
1552 }
1553
1554 sz = iov_size(out_elem->out_sg, out_elem->out_num);
1555 if (sz == 0) {
1556 virtio_error(vdev, "Expected non-zero sized request buffer in "
1557 "virtqueue");
1558 goto cleanup;
1559 }
1560
1561 in_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
1562 if (!in_elem) {
1563 virtio_error(vdev, "Expected response buffer after request buffer "
1564 "in virtqueue");
1565 goto cleanup;
1566 }
1567 if (iov_size(in_elem->in_sg, in_elem->in_num) != NSM_RESPONSE_BUF_SIZE) {
1568 virtio_error(vdev, "Expected response buffer of length 0x3000");
1569 goto cleanup;
1570 }
1571
1572 req.iov_base = g_malloc(sz);
1573 req.iov_len = iov_to_buf(out_elem->out_sg, out_elem->out_num, 0,
1574 req.iov_base, sz);
1575 if (req.iov_len != sz) {
1576 virtio_error(vdev, "Failed to copy request buffer");
1577 goto cleanup;
1578 }
1579
1580 res.iov_base = g_malloc(NSM_RESPONSE_BUF_SIZE);
1581 res.iov_len = NSM_RESPONSE_BUF_SIZE;
1582
1583 if (!get_nsm_request_response(vnsm, &req, &res, &err)) {
1584 error_report_err(err);
1585 virtio_error(vdev, "Failed to get NSM request response");
1586 goto cleanup;
1587 }
1588
1589 sz = iov_from_buf(in_elem->in_sg, in_elem->in_num, 0, res.iov_base,
1590 res.iov_len);
1591 if (sz != res.iov_len) {
1592 virtio_error(vdev, "Failed to copy response buffer");
1593 goto cleanup;
1594 }
1595
1596 g_free(req.iov_base);
1597 g_free(res.iov_base);
1598 virtqueue_push(vq, out_elem, 0);
1599 virtqueue_push(vq, in_elem, sz);
1600 virtio_notify(vdev, vq);
1601 return;
1602
1603 cleanup:
1604 g_free(req.iov_base);
1605 g_free(res.iov_base);
1606 if (out_elem) {
1607 virtqueue_detach_element(vq, out_elem, 0);
1608 }
1609 if (in_elem) {
1610 virtqueue_detach_element(vq, in_elem, 0);
1611 }
1612 }
1613
get_features(VirtIODevice * vdev,uint64_t f,Error ** errp)1614 static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp)
1615 {
1616 return f;
1617 }
1618
extend_pcr(VirtIONSM * vnsm,int ind,uint8_t * data,uint16_t len)1619 static bool extend_pcr(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len)
1620 {
1621 Error *err = NULL;
1622 struct PCRInfo *pcr = &(vnsm->pcrs[ind]);
1623 size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
1624 uint8_t result[QCRYPTO_HASH_DIGEST_LEN_SHA384];
1625 uint8_t *ptr = result;
1626 struct iovec iov[2] = {
1627 { .iov_base = pcr->data, .iov_len = QCRYPTO_HASH_DIGEST_LEN_SHA384 },
1628 { .iov_base = data, .iov_len = len },
1629 };
1630
1631 if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iov, 2, &ptr, &digest_len,
1632 &err) < 0) {
1633 return false;
1634 }
1635
1636 memcpy(pcr->data, result, QCRYPTO_HASH_DIGEST_LEN_SHA384);
1637 return true;
1638 }
1639
lock_pcr(VirtIONSM * vnsm,int ind)1640 static void lock_pcr(VirtIONSM *vnsm, int ind)
1641 {
1642 vnsm->pcrs[ind].locked = true;
1643 }
1644
virtio_nsm_device_realize(DeviceState * dev,Error ** errp)1645 static void virtio_nsm_device_realize(DeviceState *dev, Error **errp)
1646 {
1647 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1648 VirtIONSM *vnsm = VIRTIO_NSM(dev);
1649
1650 vnsm->max_pcrs = NSM_MAX_PCRS;
1651 vnsm->digest = (char *) "SHA384";
1652 if (vnsm->module_id == NULL) {
1653 vnsm->module_id = (char *) "i-234-enc5678";
1654 }
1655 vnsm->version_major = 1;
1656 vnsm->version_minor = 0;
1657 vnsm->version_patch = 0;
1658 vnsm->extend_pcr = extend_pcr;
1659 vnsm->lock_pcr = lock_pcr;
1660
1661 virtio_init(vdev, VIRTIO_ID_NITRO_SEC_MOD, 0);
1662
1663 vnsm->vq = virtio_add_queue(vdev, 2, handle_input);
1664 }
1665
virtio_nsm_device_unrealize(DeviceState * dev)1666 static void virtio_nsm_device_unrealize(DeviceState *dev)
1667 {
1668 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1669
1670 virtio_del_queue(vdev, 0);
1671 virtio_cleanup(vdev);
1672 }
1673
1674 static const VMStateDescription vmstate_pcr_info_entry = {
1675 .name = "pcr_info_entry",
1676 .minimum_version_id = 1,
1677 .version_id = 1,
1678 .fields = (const VMStateField[]) {
1679 VMSTATE_BOOL(locked, struct PCRInfo),
1680 VMSTATE_UINT8_ARRAY(data, struct PCRInfo,
1681 QCRYPTO_HASH_DIGEST_LEN_SHA384),
1682 VMSTATE_END_OF_LIST()
1683 },
1684 };
1685
1686 static const VMStateDescription vmstate_virtio_nsm_device = {
1687 .name = "virtio-nsm-device",
1688 .minimum_version_id = 1,
1689 .version_id = 1,
1690 .fields = (const VMStateField[]) {
1691 VMSTATE_STRUCT_ARRAY(pcrs, VirtIONSM, NSM_MAX_PCRS, 1,
1692 vmstate_pcr_info_entry, struct PCRInfo),
1693 VMSTATE_END_OF_LIST()
1694 },
1695 };
1696
1697 static const VMStateDescription vmstate_virtio_nsm = {
1698 .name = "virtio-nsm",
1699 .minimum_version_id = 1,
1700 .version_id = 1,
1701 .fields = (const VMStateField[]) {
1702 VMSTATE_VIRTIO_DEVICE,
1703 VMSTATE_END_OF_LIST()
1704 },
1705 };
1706
1707 static const Property virtio_nsm_properties[] = {
1708 DEFINE_PROP_STRING("module-id", VirtIONSM, module_id),
1709 };
1710
virtio_nsm_class_init(ObjectClass * klass,const void * data)1711 static void virtio_nsm_class_init(ObjectClass *klass, const void *data)
1712 {
1713 DeviceClass *dc = DEVICE_CLASS(klass);
1714 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
1715
1716 device_class_set_props(dc, virtio_nsm_properties);
1717 dc->vmsd = &vmstate_virtio_nsm;
1718 set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1719 vdc->realize = virtio_nsm_device_realize;
1720 vdc->unrealize = virtio_nsm_device_unrealize;
1721 vdc->get_features = get_features;
1722 vdc->vmsd = &vmstate_virtio_nsm_device;
1723 }
1724
1725 static const TypeInfo virtio_nsm_info = {
1726 .name = TYPE_VIRTIO_NSM,
1727 .parent = TYPE_VIRTIO_DEVICE,
1728 .instance_size = sizeof(VirtIONSM),
1729 .class_init = virtio_nsm_class_init,
1730 };
1731
virtio_register_types(void)1732 static void virtio_register_types(void)
1733 {
1734 type_register_static(&virtio_nsm_info);
1735 }
1736
1737 type_init(virtio_register_types)
1738