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 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 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 */ 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 */ 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 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 */ 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 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 */ 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 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 */ 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 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 */ 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 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 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 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 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 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 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 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 */ 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 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 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 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 1608 static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) 1609 { 1610 return f; 1611 } 1612 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 1634 static void lock_pcr(VirtIONSM *vnsm, int ind) 1635 { 1636 vnsm->pcrs[ind].locked = true; 1637 } 1638 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 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 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 1727 static void virtio_register_types(void) 1728 { 1729 type_register_static(&virtio_nsm_info); 1730 } 1731 1732 type_init(virtio_register_types) 1733