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),
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 goto cleanup;
509 }
510 str = cbor_bytestring_handle(pair[i].value);
511 if (!str) {
512 goto cleanup;
513 }
514 nsm_req->data_len = cbor_bytestring_length(pair[i].value);
515 /*
516 * nsm_req->data_len will be smaller than NSM_REQUEST_MAX_SIZE as
517 * we already check for the max request size before processing
518 * any request. So it's safe to copy.
519 */
520 memcpy(nsm_req->data, str, nsm_req->data_len);
521 data_found = true;
522 continue;
523 }
524 }
525
526 if (index_found && data_found) {
527 r = NSM_SUCCESS;
528 }
529
530 cleanup:
531 if (item) {
532 cbor_decref(&item);
533 }
534 return r;
535 }
536
537 /*
538 * ExtendPCR response structure:
539 *
540 * {
541 * Map(1) {
542 * key = String("ExtendPCR"),
543 * value = Map(1) {
544 * key = String("data"),
545 * value = Byte_String()
546 * }
547 * }
548 * }
549 */
handle_extend_pcr(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)550 static bool handle_extend_pcr(VirtIONSM *vnsm, struct iovec *request,
551 struct iovec *response, Error **errp)
552 {
553 cbor_item_t *root = NULL;
554 cbor_item_t *nested_map;
555 size_t len;
556 struct PCRInfo *pcr;
557 enum NSMResponseTypes type;
558 bool r = false;
559 g_autofree NSMExtendPCRReq *nsm_req = g_malloc(sizeof(NSMExtendPCRReq));
560
561 type = get_nsm_extend_pcr_req(request->iov_base, request->iov_len,
562 nsm_req);
563 if (type != NSM_SUCCESS) {
564 if (error_response(response, type, errp)) {
565 r = true;
566 }
567 goto out;
568 }
569 if (nsm_req->index >= vnsm->max_pcrs) {
570 if (error_response(response, NSM_INVALID_INDEX, errp)) {
571 r = true;
572 }
573 goto out;
574 }
575
576 pcr = &(vnsm->pcrs[nsm_req->index]);
577
578 if (pcr->locked) {
579 if (error_response(response, NSM_READONLY_INDEX, errp)) {
580 r = true;
581 }
582 goto out;
583 }
584
585 if (!vnsm->extend_pcr(vnsm, nsm_req->index, nsm_req->data,
586 nsm_req->data_len)) {
587 if (error_response(response, NSM_INTERNAL_ERROR, errp)) {
588 r = true;
589 }
590 goto out;
591 }
592
593 root = cbor_new_definite_map(1);
594 if (!root) {
595 goto err;
596 }
597
598 if (!qemu_cbor_add_map_to_map(root, "ExtendPCR", 1, &nested_map)) {
599 goto err;
600 }
601
602 if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
603 QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
604 goto err;
605 }
606
607 len = cbor_serialize(root, response->iov_base, response->iov_len);
608 if (len == 0) {
609 if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
610 r = true;
611 }
612 goto out;
613 }
614
615 response->iov_len = len;
616 r = true;
617
618 out:
619 if (root) {
620 cbor_decref(&root);
621 }
622 return r;
623
624 err:
625 error_setg(errp, "Failed to initialize DescribePCR response");
626 goto out;
627 }
628
629 /*
630 * LockPCR request structure:
631 *
632 * {
633 * Map(1) {
634 * key = String("LockPCR"),
635 * value = Map(1) {
636 * key = String("index"),
637 * value = Uint8(pcr)
638 * }
639 * }
640 * }
641 */
642 typedef struct NSMLockPCRReq {
643 uint8_t index;
644 } NSMLockPCRReq;
645
get_nsm_lock_pcr_req(uint8_t * req,size_t len,NSMLockPCRReq * nsm_req)646 static enum NSMResponseTypes get_nsm_lock_pcr_req(uint8_t *req, size_t len,
647 NSMLockPCRReq *nsm_req)
648 {
649 cbor_item_t *item = NULL;
650 size_t size;
651 uint8_t *str;
652 struct cbor_pair *pair;
653 struct cbor_load_result result;
654 enum NSMResponseTypes r = NSM_INVALID_OPERATION;
655
656 item = cbor_load(req, len, &result);
657 if (!item || result.error.code != CBOR_ERR_NONE) {
658 goto cleanup;
659 }
660
661 pair = cbor_map_handle(item);
662 if (!cbor_isa_map(pair->value)) {
663 goto cleanup;
664 }
665 size = cbor_map_size(pair->value);
666 if (size < 1) {
667 goto cleanup;
668 }
669
670 pair = cbor_map_handle(pair->value);
671 for (int i = 0; i < size; ++i) {
672 if (!cbor_isa_string(pair[i].key)) {
673 continue;
674 }
675 str = cbor_string_handle(pair[i].key);
676 if (str && cbor_string_length(pair[i].key) == 5 &&
677 memcmp(str, "index", 5) == 0) {
678 if (!cbor_isa_uint(pair[i].value) ||
679 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
680 break;
681 }
682
683 nsm_req->index = cbor_get_uint8(pair[i].value);
684 r = NSM_SUCCESS;
685 break;
686 }
687 }
688
689 cleanup:
690 if (item) {
691 cbor_decref(&item);
692 }
693 return r;
694 }
695
696 /*
697 * LockPCR success response structure:
698 * {
699 * String("LockPCR")
700 * }
701 */
handle_lock_pcr(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)702 static bool handle_lock_pcr(VirtIONSM *vnsm, struct iovec *request,
703 struct iovec *response, Error **errp)
704 {
705 cbor_item_t *root = NULL;
706 size_t len;
707 NSMLockPCRReq nsm_req;
708 enum NSMResponseTypes type;
709 struct PCRInfo *pcr;
710 bool r = false;
711
712 type = get_nsm_lock_pcr_req(request->iov_base, request->iov_len, &nsm_req);
713 if (type != NSM_SUCCESS) {
714 if (error_response(response, type, errp)) {
715 r = true;
716 }
717 goto cleanup;
718 }
719 if (nsm_req.index >= vnsm->max_pcrs) {
720 if (error_response(response, NSM_INVALID_INDEX, errp)) {
721 r = true;
722 }
723 goto cleanup;
724 }
725
726 pcr = &(vnsm->pcrs[nsm_req.index]);
727
728 if (pcr->locked) {
729 if (error_response(response, NSM_READONLY_INDEX, errp)) {
730 r = true;
731 }
732 goto cleanup;
733 }
734
735 pcr->locked = true;
736
737 root = cbor_build_string("LockPCR");
738 if (!root) {
739 goto err;
740 }
741
742 len = cbor_serialize(root, response->iov_base, response->iov_len);
743 if (len == 0) {
744 if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
745 r = true;
746 }
747 goto cleanup;
748 }
749
750 response->iov_len = len;
751 r = true;
752 goto cleanup;
753
754 err:
755 error_setg(errp, "Failed to initialize LockPCR response");
756
757 cleanup:
758 if (root) {
759 cbor_decref(&root);
760 }
761 return r;
762 }
763
764 /*
765 * LockPCRs request structure:
766 *
767 * {
768 * Map(1) {
769 * key = String("LockPCRs"),
770 * value = Map(1) {
771 * key = String("range"),
772 * value = Uint8(pcr)
773 * }
774 * }
775 * }
776 */
777 typedef struct NSMLockPCRsReq {
778 uint16_t range;
779 } NSMLockPCRsReq;
780
get_nsm_lock_pcrs_req(uint8_t * req,size_t len,NSMLockPCRsReq * nsm_req)781 static enum NSMResponseTypes get_nsm_lock_pcrs_req(uint8_t *req, size_t len,
782 NSMLockPCRsReq *nsm_req)
783 {
784 cbor_item_t *item = NULL;
785 size_t size;
786 uint8_t *str;
787 struct cbor_pair *pair;
788 struct cbor_load_result result;
789 enum NSMResponseTypes r = NSM_INVALID_OPERATION;
790
791 item = cbor_load(req, len, &result);
792 if (!item || result.error.code != CBOR_ERR_NONE) {
793 goto cleanup;
794 }
795
796 pair = cbor_map_handle(item);
797 if (!cbor_isa_map(pair->value)) {
798 goto cleanup;
799 }
800 size = cbor_map_size(pair->value);
801 if (size < 1) {
802 goto cleanup;
803 }
804
805 pair = cbor_map_handle(pair->value);
806 for (int i = 0; i < size; ++i) {
807 if (!cbor_isa_string(pair[i].key)) {
808 continue;
809 }
810 str = cbor_string_handle(pair[i].key);
811 if (str && cbor_string_length(pair[i].key) == 5 &&
812 memcmp(str, "range", 5) == 0) {
813 if (!cbor_isa_uint(pair[i].value) ||
814 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
815 break;
816 }
817
818 nsm_req->range = cbor_get_uint8(pair[i].value);
819 r = NSM_SUCCESS;
820 break;
821 }
822 }
823
824 cleanup:
825 if (item) {
826 cbor_decref(&item);
827 }
828 return r;
829 }
830
831 /*
832 * LockPCRs success response structure:
833 * {
834 * String("LockPCRs")
835 * }
836 */
handle_lock_pcrs(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)837 static bool handle_lock_pcrs(VirtIONSM *vnsm, struct iovec *request,
838 struct iovec *response, Error **errp)
839 {
840 cbor_item_t *root = NULL;
841 size_t len;
842 NSMLockPCRsReq nsm_req;
843 enum NSMResponseTypes type;
844 bool r = false;
845
846 type = get_nsm_lock_pcrs_req(request->iov_base, request->iov_len, &nsm_req);
847 if (type != NSM_SUCCESS) {
848 if (error_response(response, type, errp)) {
849 r = true;
850 }
851 goto cleanup;
852 }
853 if (nsm_req.range > vnsm->max_pcrs) {
854 if (error_response(response, NSM_INVALID_INDEX, errp)) {
855 r = true;
856 }
857 goto cleanup;
858 }
859
860 for (int i = 0; i < nsm_req.range; ++i) {
861 vnsm->pcrs[i].locked = true;
862 }
863
864 root = cbor_build_string("LockPCRs");
865 if (!root) {
866 goto err;
867 }
868
869 len = cbor_serialize(root, response->iov_base, response->iov_len);
870 if (len == 0) {
871 if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
872 r = true;
873 }
874 goto cleanup;
875 }
876
877 response->iov_len = len;
878 r = true;
879 goto cleanup;
880
881 err:
882 error_setg(errp, "Failed to initialize response");
883
884 cleanup:
885 if (root) {
886 cbor_decref(&root);
887 }
888 return r;
889 }
890
891 /*
892 * Attestation request structure:
893 *
894 * Map(1) {
895 * key = String("Attestation"),
896 * value = Map(3) {
897 * key = String("user_data"),
898 * value = Byte_String() || null, // Optional
899 * key = String("nonce"),
900 * value = Byte_String() || null, // Optional
901 * key = String("public_key"),
902 * value = Byte_String() || null, // Optional
903 * }
904 * }
905 * }
906 */
907
908 struct AttestationProperty {
909 bool is_null; /* True if property is not present in map or is null */
910 uint16_t len;
911 uint8_t buf[NSM_REQUEST_MAX_SIZE];
912 };
913
914 typedef struct NSMAttestationReq {
915 struct AttestationProperty public_key;
916 struct AttestationProperty user_data;
917 struct AttestationProperty nonce;
918 } NSMAttestationReq;
919
fill_attestation_property(struct AttestationProperty * prop,cbor_item_t * value)920 static bool fill_attestation_property(struct AttestationProperty *prop,
921 cbor_item_t *value)
922 {
923 uint8_t *str;
924 bool ret = false;
925
926 if (cbor_is_null(value)) {
927 prop->is_null = true;
928 ret = true;
929 goto out;
930 } else if (cbor_isa_bytestring(value)) {
931 str = cbor_bytestring_handle(value);
932 if (!str) {
933 goto out;
934 }
935 prop->len = cbor_bytestring_length(value);
936 } else if (cbor_isa_string(value)) {
937 str = cbor_string_handle(value);
938 if (!str) {
939 goto out;
940 }
941 prop->len = cbor_string_length(value);
942 } else {
943 goto out;
944 }
945
946 /*
947 * prop->len will be smaller than NSM_REQUEST_MAX_SIZE as we
948 * already check for the max request size before processing
949 * any request. So it's safe to copy.
950 */
951 memcpy(prop->buf, str, prop->len);
952 prop->is_null = false;
953 ret = true;
954
955 out:
956 return ret;
957 }
958
get_nsm_attestation_req(uint8_t * req,size_t len,NSMAttestationReq * nsm_req)959 static enum NSMResponseTypes get_nsm_attestation_req(uint8_t *req, size_t len,
960 NSMAttestationReq *nsm_req)
961 {
962 cbor_item_t *item = NULL;
963 size_t size;
964 uint8_t *str;
965 struct cbor_pair *pair;
966 struct cbor_load_result result;
967 enum NSMResponseTypes r = NSM_INVALID_OPERATION;
968
969 nsm_req->public_key.is_null = true;
970 nsm_req->user_data.is_null = true;
971 nsm_req->nonce.is_null = true;
972
973 item = cbor_load(req, len, &result);
974 if (!item || result.error.code != CBOR_ERR_NONE) {
975 goto cleanup;
976 }
977
978 pair = cbor_map_handle(item);
979 if (!cbor_isa_map(pair->value)) {
980 goto cleanup;
981 }
982 size = cbor_map_size(pair->value);
983 if (size == 0) {
984 r = NSM_SUCCESS;
985 goto cleanup;
986 }
987
988 pair = cbor_map_handle(pair->value);
989 for (int i = 0; i < size; ++i) {
990 if (!cbor_isa_string(pair[i].key)) {
991 continue;
992 }
993
994 str = cbor_string_handle(pair[i].key);
995 if (!str) {
996 continue;
997 }
998
999 if (cbor_string_length(pair[i].key) == 10 &&
1000 memcmp(str, "public_key", 10) == 0) {
1001 if (!fill_attestation_property(&(nsm_req->public_key),
1002 pair[i].value)) {
1003 goto cleanup;
1004 }
1005 continue;
1006 }
1007
1008 if (cbor_string_length(pair[i].key) == 9 &&
1009 memcmp(str, "user_data", 9) == 0) {
1010 if (!fill_attestation_property(&(nsm_req->user_data),
1011 pair[i].value)) {
1012 goto cleanup;
1013 }
1014 continue;
1015 }
1016
1017 if (cbor_string_length(pair[i].key) == 5 &&
1018 memcmp(str, "nonce", 5) == 0) {
1019 if (!fill_attestation_property(&(nsm_req->nonce), pair[i].value)) {
1020 goto cleanup;
1021 }
1022 continue;
1023 }
1024 }
1025
1026 r = NSM_SUCCESS;
1027
1028 cleanup:
1029 if (item) {
1030 cbor_decref(&item);
1031 }
1032 return r;
1033 }
1034
add_protected_header_to_cose(cbor_item_t * cose)1035 static bool add_protected_header_to_cose(cbor_item_t *cose)
1036 {
1037 cbor_item_t *map = NULL;
1038 cbor_item_t *key = NULL;
1039 cbor_item_t *value = NULL;
1040 cbor_item_t *bs = NULL;
1041 size_t len;
1042 bool r = false;
1043 size_t buf_len = 4096;
1044 g_autofree uint8_t *buf = g_malloc(buf_len);
1045
1046 map = cbor_new_definite_map(1);
1047 if (!map) {
1048 goto cleanup;
1049 }
1050 key = cbor_build_uint8(1);
1051 if (!key) {
1052 goto cleanup;
1053 }
1054 value = cbor_new_int8();
1055 if (!value) {
1056 goto cleanup;
1057 }
1058 cbor_mark_negint(value);
1059 /* we don't actually sign the data, so we use -1 as the 'alg' value */
1060 cbor_set_uint8(value, 0);
1061
1062 if (!qemu_cbor_map_add(map, key, value)) {
1063 goto cleanup;
1064 }
1065
1066 len = cbor_serialize(map, buf, buf_len);
1067 if (len == 0) {
1068 goto cleanup_map;
1069 }
1070
1071 bs = cbor_build_bytestring(buf, len);
1072 if (!bs) {
1073 goto cleanup_map;
1074 }
1075 if (!qemu_cbor_array_push(cose, bs)) {
1076 cbor_decref(&bs);
1077 goto cleanup_map;
1078 }
1079 r = true;
1080 goto cleanup_map;
1081
1082 cleanup:
1083 if (key) {
1084 cbor_decref(&key);
1085 }
1086 if (value) {
1087 cbor_decref(&value);
1088 }
1089
1090 cleanup_map:
1091 if (map) {
1092 cbor_decref(&map);
1093 }
1094 return r;
1095 }
1096
add_unprotected_header_to_cose(cbor_item_t * cose)1097 static bool add_unprotected_header_to_cose(cbor_item_t *cose)
1098 {
1099 cbor_item_t *map = cbor_new_definite_map(0);
1100 if (!map) {
1101 goto cleanup;
1102 }
1103 if (!qemu_cbor_array_push(cose, map)) {
1104 goto cleanup;
1105 }
1106
1107 return true;
1108
1109 cleanup:
1110 if (map) {
1111 cbor_decref(&map);
1112 }
1113 return false;
1114 }
1115
add_ca_bundle_to_payload(cbor_item_t * map)1116 static bool add_ca_bundle_to_payload(cbor_item_t *map)
1117 {
1118 cbor_item_t *key_cbor = NULL;
1119 cbor_item_t *value_cbor = NULL;
1120 cbor_item_t *bs = NULL;
1121 uint8_t zero[64] = {0};
1122
1123 key_cbor = cbor_build_string("cabundle");
1124 if (!key_cbor) {
1125 goto cleanup;
1126 }
1127 value_cbor = cbor_new_definite_array(1);
1128 if (!value_cbor) {
1129 goto cleanup;
1130 }
1131 bs = cbor_build_bytestring(zero, 64);
1132 if (!bs) {
1133 goto cleanup;
1134 }
1135 if (!qemu_cbor_array_push(value_cbor, bs)) {
1136 cbor_decref(&bs);
1137 goto cleanup;
1138 }
1139 if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
1140 goto cleanup;
1141 }
1142
1143 return true;
1144
1145 cleanup:
1146 if (key_cbor) {
1147 cbor_decref(&key_cbor);
1148 }
1149 if (value_cbor) {
1150 cbor_decref(&value_cbor);
1151 }
1152 return false;
1153 }
1154
add_payload_to_cose(cbor_item_t * cose,VirtIONSM * vnsm,NSMAttestationReq * req)1155 static bool add_payload_to_cose(cbor_item_t *cose, VirtIONSM *vnsm,
1156 NSMAttestationReq *req)
1157 {
1158 cbor_item_t *root = NULL;
1159 cbor_item_t *nested_map;
1160 cbor_item_t *bs = NULL;
1161 size_t locked_cnt;
1162 uint8_t ind[NSM_MAX_PCRS];
1163 size_t payload_map_size = 9;
1164 size_t len;
1165 struct PCRInfo *pcr;
1166 uint8_t zero[64] = {0};
1167 bool r = false;
1168 size_t buf_len = 16384;
1169 g_autofree uint8_t *buf = g_malloc(buf_len);
1170
1171 root = cbor_new_definite_map(payload_map_size);
1172 if (!root) {
1173 goto cleanup;
1174 }
1175 if (!qemu_cbor_add_string_to_map(root, "module_id", vnsm->module_id)) {
1176 goto cleanup;
1177 }
1178 if (!qemu_cbor_add_string_to_map(root, "digest", vnsm->digest)) {
1179 goto cleanup;
1180 }
1181 if (!qemu_cbor_add_uint64_to_map(root, "timestamp",
1182 (uint64_t) time(NULL) * 1000)) {
1183 goto cleanup;
1184 }
1185
1186 locked_cnt = 0;
1187 for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
1188 if (vnsm->pcrs[i].locked) {
1189 ind[locked_cnt++] = i;
1190 }
1191 }
1192 if (!qemu_cbor_add_map_to_map(root, "pcrs", locked_cnt, &nested_map)) {
1193 goto cleanup;
1194 }
1195 for (uint8_t i = 0; i < locked_cnt; ++i) {
1196 pcr = &(vnsm->pcrs[ind[i]]);
1197 if (!qemu_cbor_add_uint8_key_bytestring_to_map(
1198 nested_map, ind[i],
1199 pcr->data,
1200 QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
1201 goto cleanup;
1202 }
1203 }
1204 if (!qemu_cbor_add_bytestring_to_map(root, "certificate", zero, 64)) {
1205 goto cleanup;
1206 }
1207 if (!add_ca_bundle_to_payload(root)) {
1208 goto cleanup;
1209 }
1210
1211 if (req->public_key.is_null) {
1212 if (!qemu_cbor_add_null_to_map(root, "public_key")) {
1213 goto cleanup;
1214 }
1215 } else if (!qemu_cbor_add_bytestring_to_map(root, "public_key",
1216 req->public_key.buf,
1217 req->public_key.len)) {
1218 goto cleanup;
1219 }
1220
1221 if (req->user_data.is_null) {
1222 if (!qemu_cbor_add_null_to_map(root, "user_data")) {
1223 goto cleanup;
1224 }
1225 } else if (!qemu_cbor_add_bytestring_to_map(root, "user_data",
1226 req->user_data.buf,
1227 req->user_data.len)) {
1228 goto cleanup;
1229 }
1230
1231 if (req->nonce.is_null) {
1232 if (!qemu_cbor_add_null_to_map(root, "nonce")) {
1233 goto cleanup;
1234 }
1235 } else if (!qemu_cbor_add_bytestring_to_map(root, "nonce",
1236 req->nonce.buf,
1237 req->nonce.len)) {
1238 goto cleanup;
1239 }
1240
1241 len = cbor_serialize(root, buf, buf_len);
1242 if (len == 0) {
1243 goto cleanup;
1244 }
1245
1246 bs = cbor_build_bytestring(buf, len);
1247 if (!bs) {
1248 goto cleanup;
1249 }
1250 if (!qemu_cbor_array_push(cose, bs)) {
1251 cbor_decref(&bs);
1252 goto cleanup;
1253 }
1254
1255 r = true;
1256
1257 cleanup:
1258 if (root) {
1259 cbor_decref(&root);
1260 }
1261 return r;
1262 }
1263
add_signature_to_cose(cbor_item_t * cose)1264 static bool add_signature_to_cose(cbor_item_t *cose)
1265 {
1266 cbor_item_t *bs = NULL;
1267 uint8_t zero[64] = {0};
1268
1269 /* we don't actually sign the data, so we just put 64 zero bytes */
1270 bs = cbor_build_bytestring(zero, 64);
1271 if (!bs) {
1272 goto cleanup;
1273 }
1274
1275 if (!qemu_cbor_array_push(cose, bs)) {
1276 goto cleanup;
1277 }
1278
1279 return true;
1280
1281 cleanup:
1282 if (bs) {
1283 cbor_decref(&bs);
1284 }
1285 return false;
1286 }
1287
1288 /*
1289 * Attestation response structure:
1290 *
1291 * {
1292 * Map(1) {
1293 * key = String("Attestation"),
1294 * value = Map(1) {
1295 * key = String("document"),
1296 * value = Byte_String()
1297 * }
1298 * }
1299 * }
1300 *
1301 * The document is a serialized COSE sign1 blob of the structure:
1302 * {
1303 * Array(4) {
1304 * [0] { ByteString() }, // serialized protected header
1305 * [1] { Map(0) }, // 0 length map
1306 * [2] { ByteString() }, // serialized payload
1307 * [3] { ByteString() }, // signature
1308 * }
1309 * }
1310 *
1311 * where [0] protected header is a serialized CBOR blob of the structure:
1312 * {
1313 * Map(1) {
1314 * key = Uint8(1) // alg
1315 * value = NegativeInt8() // Signing algorithm
1316 * }
1317 * }
1318 *
1319 * [2] payload is serialized CBOR blob of the structure:
1320 * {
1321 * Map(9) {
1322 * [0] { key = String("module_id"), value = String(module_id) },
1323 * [1] { key = String("digest"), value = String("SHA384") },
1324 * [2] {
1325 * key = String("timestamp"),
1326 * value = Uint64(unix epoch of when document was created)
1327 * },
1328 * [3] {
1329 * key = String("pcrs"),
1330 * value = Map(locked_pcr_cnt) {
1331 * key = Uint8(pcr_index),
1332 * value = ByteString(pcr_data)
1333 * },
1334 * },
1335 * [4] {
1336 * key = String("certificate"),
1337 * value = ByteString(Signing certificate)
1338 * },
1339 * [5] { key = String("cabundle"), value = Array(N) { ByteString()... } },
1340 * [6] { key = String("public_key"), value = ByteString() || null },
1341 * [7] { key = String("user_data"), value = ByteString() || null},
1342 * [8] { key = String("nonce"), value = ByteString() || null},
1343 * }
1344 * }
1345 */
handle_attestation(VirtIONSM * vnsm,struct iovec * request,struct iovec * response,Error ** errp)1346 static bool handle_attestation(VirtIONSM *vnsm, struct iovec *request,
1347 struct iovec *response, Error **errp)
1348 {
1349 cbor_item_t *root = NULL;
1350 cbor_item_t *cose = NULL;
1351 cbor_item_t *nested_map;
1352 size_t len;
1353 enum NSMResponseTypes type;
1354 bool r = false;
1355 size_t buf_len = 16384;
1356 g_autofree uint8_t *buf = g_malloc(buf_len);
1357 g_autofree NSMAttestationReq *nsm_req = g_malloc(sizeof(NSMAttestationReq));
1358
1359 nsm_req->public_key.is_null = true;
1360 nsm_req->user_data.is_null = true;
1361 nsm_req->nonce.is_null = true;
1362
1363 type = get_nsm_attestation_req(request->iov_base, request->iov_len,
1364 nsm_req);
1365 if (type != NSM_SUCCESS) {
1366 if (error_response(response, type, errp)) {
1367 r = true;
1368 }
1369 goto out;
1370 }
1371
1372 cose = cbor_new_definite_array(4);
1373 if (!cose) {
1374 goto err;
1375 }
1376 if (!add_protected_header_to_cose(cose)) {
1377 goto err;
1378 }
1379 if (!add_unprotected_header_to_cose(cose)) {
1380 goto err;
1381 }
1382 if (!add_payload_to_cose(cose, vnsm, nsm_req)) {
1383 goto err;
1384 }
1385 if (!add_signature_to_cose(cose)) {
1386 goto err;
1387 }
1388
1389 len = cbor_serialize(cose, buf, buf_len);
1390 if (len == 0) {
1391 goto err;
1392 }
1393
1394 root = cbor_new_definite_map(1);
1395 if (!root) {
1396 goto err;
1397 }
1398 if (!qemu_cbor_add_map_to_map(root, "Attestation", 1, &nested_map)) {
1399 goto err;
1400 }
1401 if (!qemu_cbor_add_bytestring_to_map(nested_map, "document", buf, len)) {
1402 goto err;
1403 }
1404
1405 len = cbor_serialize(root, response->iov_base, response->iov_len);
1406 if (len == 0) {
1407 if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
1408 r = true;
1409 }
1410 goto out;
1411 }
1412
1413 response->iov_len = len;
1414 r = true;
1415
1416 out:
1417 if (root) {
1418 cbor_decref(&root);
1419 }
1420 if (cose) {
1421 cbor_decref(&cose);
1422 }
1423 return r;
1424
1425 err:
1426 error_setg(errp, "Failed to initialize Attestation response");
1427 goto out;
1428 }
1429
1430 enum CBOR_ROOT_TYPE {
1431 CBOR_ROOT_TYPE_STRING = 0,
1432 CBOR_ROOT_TYPE_MAP = 1,
1433 };
1434
1435 struct nsm_cmd {
1436 char name[16];
1437 /*
1438 * There are 2 types of request
1439 * 1) String(); "GetRandom", "DescribeNSM"
1440 * 2) Map(1) { key: String(), value: ... }
1441 */
1442 enum CBOR_ROOT_TYPE root_type;
1443 bool (*response_fn)(VirtIONSM *vnsm, struct iovec *request,
1444 struct iovec *response, Error **errp);
1445 };
1446
1447 const struct nsm_cmd nsm_cmds[] = {
1448 { "GetRandom", CBOR_ROOT_TYPE_STRING, handle_get_random },
1449 { "DescribeNSM", CBOR_ROOT_TYPE_STRING, handle_describe_nsm },
1450 { "DescribePCR", CBOR_ROOT_TYPE_MAP, handle_describe_pcr },
1451 { "ExtendPCR", CBOR_ROOT_TYPE_MAP, handle_extend_pcr },
1452 { "LockPCR", CBOR_ROOT_TYPE_MAP, handle_lock_pcr },
1453 { "LockPCRs", CBOR_ROOT_TYPE_MAP, handle_lock_pcrs },
1454 { "Attestation", CBOR_ROOT_TYPE_MAP, handle_attestation },
1455 };
1456
get_nsm_request_cmd(uint8_t * buf,size_t len)1457 static const struct nsm_cmd *get_nsm_request_cmd(uint8_t *buf, size_t len)
1458 {
1459 size_t size;
1460 uint8_t *req;
1461 enum CBOR_ROOT_TYPE root_type;
1462 struct cbor_load_result result;
1463 cbor_item_t *item = cbor_load(buf, len, &result);
1464 if (!item || result.error.code != CBOR_ERR_NONE) {
1465 goto cleanup;
1466 }
1467
1468 if (cbor_isa_string(item)) {
1469 size = cbor_string_length(item);
1470 req = cbor_string_handle(item);
1471 root_type = CBOR_ROOT_TYPE_STRING;
1472 } else if (cbor_isa_map(item) && cbor_map_size(item) == 1) {
1473 struct cbor_pair *handle = cbor_map_handle(item);
1474 if (cbor_isa_string(handle->key)) {
1475 size = cbor_string_length(handle->key);
1476 req = cbor_string_handle(handle->key);
1477 root_type = CBOR_ROOT_TYPE_MAP;
1478 } else {
1479 goto cleanup;
1480 }
1481 } else {
1482 goto cleanup;
1483 }
1484
1485 if (size == 0 || req == NULL) {
1486 goto cleanup;
1487 }
1488
1489 for (int i = 0; i < ARRAY_SIZE(nsm_cmds); ++i) {
1490 if (nsm_cmds[i].root_type == root_type &&
1491 strlen(nsm_cmds[i].name) == size &&
1492 memcmp(nsm_cmds[i].name, req, size) == 0) {
1493 cbor_decref(&item);
1494 return &nsm_cmds[i];
1495 }
1496 }
1497
1498 cleanup:
1499 if (item) {
1500 cbor_decref(&item);
1501 }
1502 return NULL;
1503 }
1504
get_nsm_request_response(VirtIONSM * vnsm,struct iovec * req,struct iovec * resp,Error ** errp)1505 static bool get_nsm_request_response(VirtIONSM *vnsm, struct iovec *req,
1506 struct iovec *resp, Error **errp)
1507 {
1508 const struct nsm_cmd *cmd;
1509
1510 if (req->iov_len > NSM_REQUEST_MAX_SIZE) {
1511 if (error_response(resp, NSM_INPUT_TOO_LARGE, errp)) {
1512 return true;
1513 }
1514 error_setg(errp, "Failed to initialize InputTooLarge response");
1515 return false;
1516 }
1517
1518 cmd = get_nsm_request_cmd(req->iov_base, req->iov_len);
1519
1520 if (cmd == NULL) {
1521 if (error_response(resp, NSM_INVALID_OPERATION, errp)) {
1522 return true;
1523 }
1524 error_setg(errp, "Failed to initialize InvalidOperation response");
1525 return false;
1526 }
1527
1528 return cmd->response_fn(vnsm, req, resp, errp);
1529 }
1530
handle_input(VirtIODevice * vdev,VirtQueue * vq)1531 static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
1532 {
1533 g_autofree VirtQueueElement *out_elem = NULL;
1534 g_autofree VirtQueueElement *in_elem = NULL;
1535 VirtIONSM *vnsm = VIRTIO_NSM(vdev);
1536 Error *err = NULL;
1537 size_t sz;
1538 struct iovec req = {.iov_base = NULL, .iov_len = 0};
1539 struct iovec res = {.iov_base = NULL, .iov_len = 0};
1540
1541 out_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
1542 if (!out_elem) {
1543 /* nothing in virtqueue */
1544 return;
1545 }
1546
1547 sz = iov_size(out_elem->out_sg, out_elem->out_num);
1548 if (sz == 0) {
1549 virtio_error(vdev, "Expected non-zero sized request buffer in "
1550 "virtqueue");
1551 goto cleanup;
1552 }
1553
1554 in_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
1555 if (!in_elem) {
1556 virtio_error(vdev, "Expected response buffer after request buffer "
1557 "in virtqueue");
1558 goto cleanup;
1559 }
1560 if (iov_size(in_elem->in_sg, in_elem->in_num) != NSM_RESPONSE_BUF_SIZE) {
1561 virtio_error(vdev, "Expected response buffer of length 0x3000");
1562 goto cleanup;
1563 }
1564
1565 req.iov_base = g_malloc(sz);
1566 req.iov_len = iov_to_buf(out_elem->out_sg, out_elem->out_num, 0,
1567 req.iov_base, sz);
1568 if (req.iov_len != sz) {
1569 virtio_error(vdev, "Failed to copy request buffer");
1570 goto cleanup;
1571 }
1572
1573 res.iov_base = g_malloc(NSM_RESPONSE_BUF_SIZE);
1574 res.iov_len = NSM_RESPONSE_BUF_SIZE;
1575
1576 if (!get_nsm_request_response(vnsm, &req, &res, &err)) {
1577 error_report_err(err);
1578 virtio_error(vdev, "Failed to get NSM request response");
1579 goto cleanup;
1580 }
1581
1582 sz = iov_from_buf(in_elem->in_sg, in_elem->in_num, 0, res.iov_base,
1583 res.iov_len);
1584 if (sz != res.iov_len) {
1585 virtio_error(vdev, "Failed to copy response buffer");
1586 goto cleanup;
1587 }
1588
1589 g_free(req.iov_base);
1590 g_free(res.iov_base);
1591 virtqueue_push(vq, out_elem, 0);
1592 virtqueue_push(vq, in_elem, in_elem->in_sg->iov_len);
1593 virtio_notify(vdev, vq);
1594 return;
1595
1596 cleanup:
1597 g_free(req.iov_base);
1598 g_free(res.iov_base);
1599 if (out_elem) {
1600 virtqueue_detach_element(vq, out_elem, 0);
1601 }
1602 if (in_elem) {
1603 virtqueue_detach_element(vq, in_elem, 0);
1604 }
1605 return;
1606 }
1607
get_features(VirtIODevice * vdev,uint64_t f,Error ** errp)1608 static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp)
1609 {
1610 return f;
1611 }
1612
extend_pcr(VirtIONSM * vnsm,int ind,uint8_t * data,uint16_t len)1613 static bool extend_pcr(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len)
1614 {
1615 Error *err = NULL;
1616 struct PCRInfo *pcr = &(vnsm->pcrs[ind]);
1617 size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
1618 uint8_t result[QCRYPTO_HASH_DIGEST_LEN_SHA384];
1619 uint8_t *ptr = result;
1620 struct iovec iov[2] = {
1621 { .iov_base = pcr->data, .iov_len = QCRYPTO_HASH_DIGEST_LEN_SHA384 },
1622 { .iov_base = data, .iov_len = len },
1623 };
1624
1625 if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iov, 2, &ptr, &digest_len,
1626 &err) < 0) {
1627 return false;
1628 }
1629
1630 memcpy(pcr->data, result, QCRYPTO_HASH_DIGEST_LEN_SHA384);
1631 return true;
1632 }
1633
lock_pcr(VirtIONSM * vnsm,int ind)1634 static void lock_pcr(VirtIONSM *vnsm, int ind)
1635 {
1636 vnsm->pcrs[ind].locked = true;
1637 }
1638
virtio_nsm_device_realize(DeviceState * dev,Error ** errp)1639 static void virtio_nsm_device_realize(DeviceState *dev, Error **errp)
1640 {
1641 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1642 VirtIONSM *vnsm = VIRTIO_NSM(dev);
1643
1644 vnsm->max_pcrs = NSM_MAX_PCRS;
1645 vnsm->digest = (char *) "SHA384";
1646 if (vnsm->module_id == NULL) {
1647 vnsm->module_id = (char *) "i-234-enc5678";
1648 }
1649 vnsm->version_major = 1;
1650 vnsm->version_minor = 0;
1651 vnsm->version_patch = 0;
1652 vnsm->extend_pcr = extend_pcr;
1653 vnsm->lock_pcr = lock_pcr;
1654
1655 virtio_init(vdev, VIRTIO_ID_NITRO_SEC_MOD, 0);
1656
1657 vnsm->vq = virtio_add_queue(vdev, 2, handle_input);
1658 }
1659
virtio_nsm_device_unrealize(DeviceState * dev)1660 static void virtio_nsm_device_unrealize(DeviceState *dev)
1661 {
1662 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1663
1664 virtio_del_queue(vdev, 0);
1665 virtio_cleanup(vdev);
1666 }
1667
1668 static const VMStateDescription vmstate_pcr_info_entry = {
1669 .name = "pcr_info_entry",
1670 .minimum_version_id = 1,
1671 .version_id = 1,
1672 .fields = (const VMStateField[]) {
1673 VMSTATE_BOOL(locked, struct PCRInfo),
1674 VMSTATE_UINT8_ARRAY(data, struct PCRInfo,
1675 QCRYPTO_HASH_DIGEST_LEN_SHA384),
1676 VMSTATE_END_OF_LIST()
1677 },
1678 };
1679
1680 static const VMStateDescription vmstate_virtio_nsm_device = {
1681 .name = "virtio-nsm-device",
1682 .minimum_version_id = 1,
1683 .version_id = 1,
1684 .fields = (const VMStateField[]) {
1685 VMSTATE_STRUCT_ARRAY(pcrs, VirtIONSM, NSM_MAX_PCRS, 1,
1686 vmstate_pcr_info_entry, struct PCRInfo),
1687 VMSTATE_END_OF_LIST()
1688 },
1689 };
1690
1691 static const VMStateDescription vmstate_virtio_nsm = {
1692 .name = "virtio-nsm",
1693 .minimum_version_id = 1,
1694 .version_id = 1,
1695 .fields = (const VMStateField[]) {
1696 VMSTATE_VIRTIO_DEVICE,
1697 VMSTATE_END_OF_LIST()
1698 },
1699 };
1700
1701 static Property virtio_nsm_properties[] = {
1702 DEFINE_PROP_STRING("module-id", VirtIONSM, module_id),
1703 DEFINE_PROP_END_OF_LIST(),
1704 };
1705
virtio_nsm_class_init(ObjectClass * klass,void * data)1706 static void virtio_nsm_class_init(ObjectClass *klass, void *data)
1707 {
1708 DeviceClass *dc = DEVICE_CLASS(klass);
1709 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
1710
1711 device_class_set_props(dc, virtio_nsm_properties);
1712 dc->vmsd = &vmstate_virtio_nsm;
1713 set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1714 vdc->realize = virtio_nsm_device_realize;
1715 vdc->unrealize = virtio_nsm_device_unrealize;
1716 vdc->get_features = get_features;
1717 vdc->vmsd = &vmstate_virtio_nsm_device;
1718 }
1719
1720 static const TypeInfo virtio_nsm_info = {
1721 .name = TYPE_VIRTIO_NSM,
1722 .parent = TYPE_VIRTIO_DEVICE,
1723 .instance_size = sizeof(VirtIONSM),
1724 .class_init = virtio_nsm_class_init,
1725 };
1726
virtio_register_types(void)1727 static void virtio_register_types(void)
1728 {
1729 type_register_static(&virtio_nsm_info);
1730 }
1731
1732 type_init(virtio_register_types)
1733