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) || 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 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 */ 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 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 */ 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 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 */ 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 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 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 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 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 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 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 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 */ 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 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 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 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 1614 static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) 1615 { 1616 return f; 1617 } 1618 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 1640 static void lock_pcr(VirtIONSM *vnsm, int ind) 1641 { 1642 vnsm->pcrs[ind].locked = true; 1643 } 1644 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 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 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 1732 static void virtio_register_types(void) 1733 { 1734 type_register_static(&virtio_nsm_info); 1735 } 1736 1737 type_init(virtio_register_types) 1738