diff --git a/README.md b/README.md index 99a4bb06..de0b3bf2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ The skr sidecar can be queried by application containers hosted in the same pod The ``examples/skr`` shows an example of how the skr sidecar can be deployed and tested within a confidential container group on ACI. +## Fetching an attestion report. +``tools/get-snp-report provides a tool which will return an SNP attestation report from the AMD PSP via linux IOCTLs. it can take a hex encoded report data value on the command line. The output is a hex encoded binary object. If piped through hex2report it can be read by people. There are two implementations inside the one tool to support the different IOCTLs requirements between linux 5.15 and 6.1 and later. + ### Third-party code We modified the [AES unwrap key without padding method](https://github.com/NickBall/go-aes-key-wrap/blob/master/keywrap.go) to implement the aes key unwrap with padding method. @@ -21,7 +24,7 @@ The encrypted filesystem sidecar uses the SKR library to release key material fr The ``examples/encfs`` shows an example of how the encrypted filesystem sidecar can be deployed within a confidential container group on ACI. ## Dependencies: -- Golang 1.16 or later +- Golang 1.19 or later - Docker - GCC 9.4.0 or later diff --git a/tools/get-snp-report/Makefile b/tools/get-snp-report/Makefile index f561015d..28651800 100644 --- a/tools/get-snp-report/Makefile +++ b/tools/get-snp-report/Makefile @@ -4,13 +4,17 @@ CFLAGS:=-O2 -Wall LDFLAGS:=-static -s # strip C binaries -all: bin/get-snp-report bin/get-fake-snp-report bin/verbose-report +all: bin/get-snp-report bin/get-fake-snp-report bin/verbose-report bin/hex2report -bin/get-fake-snp-report: get-fake-snp-report.o +bin/hex2report: hex2report.o helpers.o @mkdir -p bin $(CC) $(LDFLAGS) -o $@ $^ -bin/get-snp-report: get-snp-report.o +bin/get-fake-snp-report: get-fake-snp-report.o helpers.o + @mkdir -p bin + $(CC) $(LDFLAGS) -o $@ $^ + +bin/get-snp-report: get-snp-report.o get-snp-report5.o get-snp-report6.o helpers.o @mkdir -p bin $(CC) $(LDFLAGS) -o $@ $^ diff --git a/tools/get-snp-report/fetch5.h b/tools/get-snp-report/fetch5.h new file mode 100644 index 00000000..4afe3400 --- /dev/null +++ b/tools/get-snp-report/fetch5.h @@ -0,0 +1,9 @@ +/* Copyright (c) Microsoft Corporation. + Licensed under the MIT License. */ + +#pragma once + +bool fetchAttestationReport5(const char* report_data_hexstring, void **snp_report); + +// does /dev/sev exists. This is where the PSP is exposed in 5.15.* +bool supportsDevSev(); \ No newline at end of file diff --git a/tools/get-snp-report/fetch6.h b/tools/get-snp-report/fetch6.h new file mode 100644 index 00000000..be25ac25 --- /dev/null +++ b/tools/get-snp-report/fetch6.h @@ -0,0 +1,10 @@ +/* Copyright (c) Microsoft Corporation. + Licensed under the MIT License. */ + +#pragma once + +bool fetchAttestationReport6(const char* report_data_hexstring, void **snp_report); + +// 6.1 linux exposees the PSP via /dev/sev-guest + +bool supportsDevSevGuest(); diff --git a/tools/get-snp-report/get-fake-snp-report.c b/tools/get-snp-report/get-fake-snp-report.c index 21b81397..6142c882 100644 --- a/tools/get-snp-report/get-fake-snp-report.c +++ b/tools/get-snp-report/get-fake-snp-report.c @@ -11,89 +11,15 @@ #include #include +#include "helpers.h" #include "snp-psp.h" -#define PRINT_VAL(ptr, field) printBytes(#field, (const uint8_t *)&(ptr->field), sizeof(ptr->field), true) -#define PRINT_BYTES(ptr, field) printBytes(#field, (const uint8_t *)&(ptr->field), sizeof(ptr->field), false) - -// Helper functions -uint8_t* decodeHexString(char *hexstring) -{ - size_t len = strlen(hexstring); - uint8_t *byte_array = (uint8_t*) malloc(strlen(hexstring)*sizeof(uint8_t)); - - for (size_t i = 0; i < len; i+=2) { - sscanf(hexstring, "%2hhx", &byte_array[i/2]); - hexstring += 2; - } - - return byte_array; -} - -char* encodeHexToString(uint8_t byte_array[], size_t len) -{ - char* hexstring = (char*) malloc((2*len+1)*sizeof(char)); - - for (size_t i = 0; i < len; i++) - sprintf(&hexstring[i*2], "%02x", byte_array[i]); - - hexstring[2*len] = '\0'; // string padding character - return hexstring; -} - -void printBytes(const char *desc, const uint8_t *data, size_t len, bool swap) -{ - fprintf(stderr, " %s: ", desc); - int padding = 20 - strlen(desc); - if (padding < 0) - padding = 0; - for (int count = 0; count < padding; count++) - putchar(' '); - - for (size_t pos = 0; pos < len; pos++) { - fprintf(stderr, "%02x", data[swap ? len - pos - 1 : pos]); - if (pos % 32 == 31) - printf("\n "); - else if (pos % 16 == 15) - putchar(' '); - } - fprintf(stderr, "\n"); -} - -void printReport(const snp_attestation_report *r) -{ - PRINT_VAL(r, version); - PRINT_VAL(r, guest_svn); - PRINT_VAL(r, policy); - PRINT_VAL(r, family_id); - PRINT_VAL(r, image_id); - PRINT_VAL(r, vmpl); - PRINT_VAL(r, signature_algo); - PRINT_BYTES(r, platform_version); - PRINT_BYTES(r, platform_info); - PRINT_VAL(r, author_key_en); - PRINT_VAL(r, reserved1); - PRINT_BYTES(r, report_data); - PRINT_BYTES(r, measurement); - PRINT_BYTES(r, host_data); - PRINT_BYTES(r, id_key_digest); - PRINT_BYTES(r, author_key_digest); - PRINT_BYTES(r, report_id); - PRINT_BYTES(r, report_id_ma); - PRINT_VAL(r, reported_tcb); - PRINT_BYTES(r, reserved2); - PRINT_BYTES(r, chip_id); - PRINT_BYTES(r, reserved3); - PRINT_BYTES(r, signature); -} - - bool fetchAttestationReport(char report_data_hexstring[], char host_data_hexstring[], void **snp_report) { snp_attestation_report attestation_report; - uint8_t *default_report = decodeHexString("01000000010000001f00030000000000010000000000000000000000000000000200000000000000000000000000000000000000010000000000000000000028010000000000000000000000000000007ab000a323b3c873f5b81bbe584e7c1a26bcf40dc27e00f8e0d144b1ed2d14f10000000000000000000000000000000000000000000000000000000000000000e29af700e85b39996fa38226d2804b78cad746ffef4477360a61b47874bdecd640f9d32f5ff64a55baad3c545484d9ed28603a3ea835a83bd688b0ec1dcb36b6b8c22412e5b63115b75db8628b989bc598c475ca5f7683e8d351e7e789a1baff19041750567161ad52bf0d152bd76d7c6f313d0a0fd72d0089692c18f521155800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040aea62690b08eb6d680392c9a9b3db56a9b3cc44083b9da31fb88bcfc493407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000028000000000000000000000000000000000000000000000000e6c86796cd44b0bc6b7c0d4fdab33e2807e14b5fc4538b3750921169d97bcf4447c7d3ab2a7c25f74c1641e2885c1011d025cc536f5c9a2504713136c7877f480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003131c0f3e7be5c6e400f22404596e1874381e99d03de45ef8b97eee0a0fa93a4911550330343f14dddbbd6c0db83744f000000000000000000000000000000000000000000000000db07c83c5e6162c2387f3b76cd547672657f6a5df99df98efee7c15349320d83e086c5003ec43050a9b18d1c39dedc340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + uint8_t *default_report = decodeHexString("01000000010000001f00030000000000010000000000000000000000000000000200000000000000000000000000000000000000010000000000000000000028010000000000000000000000000000007ab000a323b3c873f5b81bbe584e7c1a26bcf40dc27e00f8e0d144b1ed2d14f10000000000000000000000000000000000000000000000000000000000000000e29af700e85b39996fa38226d2804b78cad746ffef4477360a61b47874bdecd640f9d32f5ff64a55baad3c545484d9ed28603a3ea835a83bd688b0ec1dcb36b6b8c22412e5b63115b75db8628b989bc598c475ca5f7683e8d351e7e789a1baff19041750567161ad52bf0d152bd76d7c6f313d0a0fd72d0089692c18f521155800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040aea62690b08eb6d680392c9a9b3db56a9b3cc44083b9da31fb88bcfc493407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000028000000000000000000000000000000000000000000000000e6c86796cd44b0bc6b7c0d4fdab33e2807e14b5fc4538b3750921169d97bcf4447c7d3ab2a7c25f74c1641e2885c1011d025cc536f5c9a2504713136c7877f480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003131c0f3e7be5c6e400f22404596e1874381e99d03de45ef8b97eee0a0fa93a4911550330343f14dddbbd6c0db83744f000000000000000000000000000000000000000000000000db07c83c5e6162c2387f3b76cd547672657f6a5df99df98efee7c15349320d83e086c5003ec43050a9b18d1c39dedc340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 0); memcpy((uint8_t*)&attestation_report, default_report, sizeof(snp_attestation_report)); memset(attestation_report.report_data, 0 , 64); @@ -103,10 +29,10 @@ bool fetchAttestationReport(char report_data_hexstring[], char host_data_hexstri // the report data is passed as a hexstring which needs to be decoded into an array of // unsigned bytes - uint8_t *reportData = decodeHexString(report_data_hexstring); + uint8_t *reportData = decodeHexString(report_data_hexstring, 64); memcpy(attestation_report.report_data, reportData , strlen(report_data_hexstring)/2); - uint8_t *hostData = decodeHexString(host_data_hexstring); + uint8_t *hostData = decodeHexString(host_data_hexstring, 32); memcpy(attestation_report.host_data, hostData , strlen(host_data_hexstring)/2); *snp_report = (snp_attestation_report *) malloc (sizeof(snp_attestation_report)); diff --git a/tools/get-snp-report/get-snp-report.c b/tools/get-snp-report/get-snp-report.c index 1acee3a2..84ed710c 100644 --- a/tools/get-snp-report/get-snp-report.c +++ b/tools/get-snp-report/get-snp-report.c @@ -11,161 +11,34 @@ #include #include -#include "snp-psp.h" +#include "snp-attestation.h" +#include "fetch5.h" +#include "fetch6.h" -#define PRINT_VAL(ptr, field) printBytes(#field, (const uint8_t *)&(ptr->field), sizeof(ptr->field), true) -#define PRINT_BYTES(ptr, field) printBytes(#field, (const uint8_t *)&(ptr->field), sizeof(ptr->field), false) -// Helper functions -uint8_t* decodeHexString(char *hexstring) -{ - size_t len = strlen(hexstring); - uint8_t *byte_array = (uint8_t*) malloc(strlen(hexstring)*sizeof(uint8_t)); - - for (size_t i = 0; i < len; i+=2) { - sscanf(hexstring, "%2hhx", &byte_array[i/2]); - hexstring += 2; - } - - return byte_array; -} - -char* encodeHexToString(uint8_t byte_array[], size_t len) -{ - char* hexstring = (char*) malloc((2*len+1)*sizeof(char)); - - for (size_t i = 0; i < len; i++) - sprintf(&hexstring[i*2], "%02x", byte_array[i]); - - hexstring[2*len] = '\0'; // string padding character - return hexstring; -} - -void printBytes(const char *desc, const uint8_t *data, size_t len, bool swap) -{ - fprintf(stderr, " %s: ", desc); - int padding = 20 - strlen(desc); - if (padding < 0) - padding = 0; - for (int count = 0; count < padding; count++) - putchar(' '); - - for (size_t pos = 0; pos < len; pos++) { - fprintf(stderr, "%02x", data[swap ? len - pos - 1 : pos]); - if (pos % 32 == 31) - printf("\n "); - else if (pos % 16 == 15) - putchar(' '); - } - fprintf(stderr, "\n"); -} - -void printReport(const snp_attestation_report *r) -{ - PRINT_VAL(r, version); - PRINT_VAL(r, guest_svn); - PRINT_VAL(r, policy); - PRINT_VAL(r, family_id); - PRINT_VAL(r, image_id); - PRINT_VAL(r, vmpl); - PRINT_VAL(r, signature_algo); - PRINT_BYTES(r, platform_version); - PRINT_BYTES(r, platform_info); - PRINT_VAL(r, author_key_en); - PRINT_VAL(r, reserved1); - PRINT_BYTES(r, report_data); - PRINT_BYTES(r, measurement); - PRINT_BYTES(r, host_data); - PRINT_BYTES(r, id_key_digest); - PRINT_BYTES(r, author_key_digest); - PRINT_BYTES(r, report_id); - PRINT_BYTES(r, report_id_ma); - PRINT_VAL(r, reported_tcb); - PRINT_BYTES(r, reserved2); - PRINT_BYTES(r, chip_id); - PRINT_BYTES(r, reserved3); - PRINT_BYTES(r, signature); -} - -bool fetchAttestationReport(char report_data_hexstring[], void **snp_report) -{ - msg_report_req msg_report_in; - msg_response_resp msg_report_out; - - int fd, rc; - - struct sev_snp_guest_request payload = { - .req_msg_type = SNP_MSG_REPORT_REQ, - .rsp_msg_type = SNP_MSG_REPORT_RSP, - .msg_version = 1, - .request_len = sizeof(msg_report_in), - .request_uaddr = (uint64_t) (void*) &msg_report_in, - .response_len = sizeof(msg_report_out), - .response_uaddr = (uint64_t) (void*) &msg_report_out, - .error = 0 - }; - - memset((void*) &msg_report_in, 0, sizeof(msg_report_in)); - memset((void*) &msg_report_out, 0, sizeof(msg_report_out)); - - // MAA expects a SHA-256. So we use 32 bytes as size instead of msg_report_in.report_data - // the report data is passed as a hexstring which needs to be decoded into an array of - // unsigned bytes - uint8_t *reportData = decodeHexString(report_data_hexstring); - memcpy(msg_report_in.report_data, reportData, 32); - - // open the file descriptor of the PSP - fd = open("/dev/sev", O_RDWR | O_CLOEXEC); - - if (fd < 0) { - fprintf(stdout, "Failed to open /dev/sev\n"); - return false; - } - - // issue the custom SEV_SNP_GUEST_MSG_REPORT sys call to the sev driver - rc = ioctl(fd, SEV_SNP_GUEST_MSG_REPORT, &payload); - - if (rc < 0) { - fprintf(stdout, "Failed to issue ioctl SEV_SNP_GUEST_MSG_REPORT\n"); - return false; - } - - #ifdef DEBUG_OUTPUT - fprintf(stderr, "Response header:\n"); - uint8_t *hdr = (uint8_t*) &msg_report_out; - - for (size_t i = 0; i < 32; i++) { - fprintf(stderr, "%02x", hdr[i]); - if (i % 16 == 15) - fprintf(stderr, "\n"); - else - fprintf(stderr, " "); - } - fprintf(stderr, "Attestation report:\n"); - printReport(&msg_report_out.report); - #endif - - *snp_report = (snp_attestation_report *) malloc (sizeof(snp_attestation_report)); - memcpy(*snp_report, &msg_report_out.report, sizeof(snp_attestation_report)); - - return true; -} // Main expects the hex string representation of the report data as the only argument // Prints the raw binary format of the report so it can be consumed by the tools under // the directory internal/guest/attestation int main(int argc, char *argv[]) { - bool success; + bool success = false; uint8_t *snp_report_hex; + const char *report_data_hexstring = ""; + + if (argc > 1) { + report_data_hexstring = argv[1]; + } - if (argc > 1) { - success = fetchAttestationReport(argv[1], (void*) &snp_report_hex); - } else { - success = fetchAttestationReport("", (void*) &snp_report_hex); + if (supportsDevSev()) { + success = fetchAttestationReport5(report_data_hexstring, (void*) &snp_report_hex); + } else if (supportsDevSevGuest()) { + success = fetchAttestationReport6(report_data_hexstring, (void*) &snp_report_hex); + } else { + fprintf(stderr, "No supported SNP device found\n"); } - if (success == true) { + if (success) { for (size_t i = 0; i < sizeof(snp_attestation_report); i++) { fprintf(stdout, "%02x", (uint8_t) snp_report_hex[i]); } diff --git a/tools/get-snp-report/get-snp-report5.c b/tools/get-snp-report/get-snp-report5.c new file mode 100644 index 00000000..93517f29 --- /dev/null +++ b/tools/get-snp-report/get-snp-report5.c @@ -0,0 +1,88 @@ +/* Copyright (c) Microsoft Corporation. + Licensed under the MIT License. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snp-attestation.h" +#include "snp-ioctl5.h" + +#include "helpers.h" + +bool supportsDevSev() +{ + return access("/dev/sev", W_OK) == 0; +} + +bool fetchAttestationReport5(const char* report_data_hexstring, void **snp_report) +{ + msg_report_req msg_report_in; + msg_response_resp msg_report_out; + + int fd, rc; + + struct sev_snp_guest_request payload = { + .req_msg_type = SNP_MSG_REPORT_REQ, + .rsp_msg_type = SNP_MSG_REPORT_RSP, + .msg_version = 1, + .request_len = sizeof(msg_report_in), + .request_uaddr = (uint64_t) (void*) &msg_report_in, + .response_len = sizeof(msg_report_out), + .response_uaddr = (uint64_t) (void*) &msg_report_out, + .error = 0 + }; + + memset((void*) &msg_report_in, 0, sizeof(msg_report_in)); + memset((void*) &msg_report_out, 0, sizeof(msg_report_out)); + + // the report data is passed as a hexstring which needs to be decoded into an array of + // unsigned bytes + // MAA expects a SHA-256. So we use left align the bytes in the report data + + uint8_t *reportData = decodeHexString(report_data_hexstring, sizeof(msg_report_in.report_data)); + memcpy(msg_report_in.report_data, reportData, sizeof(msg_report_in.report_data)); + + // open the file descriptor of the PSP + fd = open("/dev/sev", O_RDWR | O_CLOEXEC); + + if (fd < 0) { + fprintf(stderr, "Failed to open /dev/sev\n"); + return false; + } + + // issue the custom SEV_SNP_GUEST_MSG_REPORT sys call to the sev driver + rc = ioctl(fd, SEV_SNP_GUEST_MSG_REPORT, &payload); + + if (rc < 0) { + fprintf(stderr, "Failed to issue ioctl SEV_SNP_GUEST_MSG_REPORT\n"); + return false; + } + + #ifdef DEBUG_OUTPUT + fprintf(stderr, "Response header:\n"); + uint8_t *hdr = (uint8_t*) &msg_report_out; + + for (size_t i = 0; i < 32; i++) { + fprintf(stderr, "%02x", hdr[i]); + if (i % 16 == 15) + fprintf(stderr, "\n"); + else + fprintf(stderr, " "); + } + fprintf(stderr, "Attestation report:\n"); + printReport(&msg_report_out.report); + #endif + + *snp_report = (snp_attestation_report *) malloc (sizeof(snp_attestation_report)); + memcpy(*snp_report, &msg_report_out.report, sizeof(snp_attestation_report)); + + return true; +} diff --git a/tools/get-snp-report/get-snp-report6.c b/tools/get-snp-report/get-snp-report6.c new file mode 100644 index 00000000..f2bec610 --- /dev/null +++ b/tools/get-snp-report/get-snp-report6.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snp-attestation.h" +#include "snp-ioctl6.h" +#include "helpers.h" + +bool supportsDevSevGuest() +{ + return access("/dev/sev-guest", W_OK) == 0; +} + +bool fetchAttestationReport6(const char* report_data_hexstring, void **snp_report) +{ + int fd; + int rc; + + fd = open("/dev/sev-guest", O_RDWR | O_CLOEXEC); + + if (fd < 0) { + fprintf(stdout, "Failed to open /dev/sev-guest\n"); + return false; + } + + // this is the request, mostly the report data, vmpl + snp_report_req snp_request; + // and the result from the ioctl, in the get report case this will be the report + snp_report_resp snp_response; + + // the object we pass to the ioctl that wraps the psp request. + snp_guest_request_ioctl ioctl_request; + + memset(&snp_request, 0, sizeof(snp_request)); + + // the report data is passed as a hexstring which needs to be decoded into an array of + // unsigned bytes + // MAA expects a SHA-256. So we use left align the bytes in the report data. + + uint8_t *reportData = decodeHexString(report_data_hexstring, sizeof(snp_request.report_data)); + memcpy(snp_request.report_data, reportData, sizeof(snp_request.report_data)); + + memset(&snp_response, 0, sizeof(snp_response)); + memset(&ioctl_request, 0, sizeof(ioctl_request)); + + ioctl_request.msg_version = 1; + ioctl_request.req_data = (uint64_t)&snp_request; + ioctl_request.resp_data = (uint64_t)&snp_response; + + rc = ioctl(fd, SNP_GET_REPORT, &ioctl_request); + + if (rc < 0) { + fprintf(stderr, "Failed to issue ioctl SEV_SNP_GUEST_MSG_REPORT\n"); + return false; + } + + msg_response_resp *response = (msg_response_resp *)&snp_response.data; + snp_attestation_report *report = &response->report; + + + *snp_report = (snp_attestation_report *) malloc (sizeof(snp_attestation_report)); + memcpy(*snp_report, report, sizeof(snp_attestation_report)); + + return true; +} diff --git a/tools/get-snp-report/helpers.c b/tools/get-snp-report/helpers.c new file mode 100644 index 00000000..a7a2beee --- /dev/null +++ b/tools/get-snp-report/helpers.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snp-ioctl5.h" + +#include "helpers.h" + + + +// Helper functions +uint8_t* decodeHexString(const char *hexstring, size_t padTo) // will zero pad to bufferLen +{ + size_t len = strlen(hexstring); + size_t out_len = len/2+1; + if (out_len < padTo) + out_len = padTo; + uint8_t *byte_array = (uint8_t*) malloc(out_len); + memset(byte_array, 0, out_len); + + for (size_t i = 0; i < len; i+=2) { + sscanf(hexstring, "%2hhx", &byte_array[i/2]); + hexstring += 2; + } + + return byte_array; +} + +char* encodeHexToString(uint8_t byte_array[], size_t len) +{ + char* hexstring = (char*) malloc((2*len+1)*sizeof(char)); + + for (size_t i = 0; i < len; i++) + sprintf(&hexstring[i*2], "%02x", byte_array[i]); + + hexstring[2*len] = '\0'; // string padding character + return hexstring; +} + +void printBytes(const char *desc, const uint8_t *data, size_t len, bool swap) +{ + fprintf(stderr, " %s: ", desc); + int padding = 20 - strlen(desc); + if (padding < 0) + padding = 0; + for (int count = 0; count < padding; count++) + putchar(' '); + + for (size_t pos = 0; pos < len; pos++) { + fprintf(stderr, "%02x", data[swap ? len - pos - 1 : pos]); + if (pos % 32 == 31) + printf("\n "); + else if (pos % 16 == 15) + putchar(' '); + } + fprintf(stderr, "\n"); +} + +void printReport(const snp_attestation_report *r) +{ + PRINT_VAL(r, version); + PRINT_VAL(r, guest_svn); + PRINT_VAL(r, policy); + PRINT_VAL(r, family_id); + PRINT_VAL(r, image_id); + PRINT_VAL(r, vmpl); + PRINT_VAL(r, signature_algo); + PRINT_BYTES(r, platform_version); + PRINT_BYTES(r, platform_info); + PRINT_VAL(r, author_key_en); + PRINT_VAL(r, reserved1); + PRINT_BYTES(r, report_data); + PRINT_BYTES(r, measurement); + PRINT_BYTES(r, host_data); + PRINT_BYTES(r, id_key_digest); + PRINT_BYTES(r, author_key_digest); + PRINT_BYTES(r, report_id); + PRINT_BYTES(r, report_id_ma); + PRINT_VAL(r, reported_tcb); + PRINT_BYTES(r, reserved2); + PRINT_BYTES(r, chip_id); + PRINT_BYTES(r, reserved3); + PRINT_BYTES(r, signature); +} \ No newline at end of file diff --git a/tools/get-snp-report/helpers.h b/tools/get-snp-report/helpers.h new file mode 100644 index 00000000..af7e16ff --- /dev/null +++ b/tools/get-snp-report/helpers.h @@ -0,0 +1,29 @@ +/* Copyright (c) Microsoft Corporation. + Licensed under the MIT License. */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snp-attestation.h" + + +#define PRINT_VAL(ptr, field) printBytes(#field, (const uint8_t *)&(ptr->field), sizeof(ptr->field), true) +#define PRINT_BYTES(ptr, field) printBytes(#field, (const uint8_t *)&(ptr->field), sizeof(ptr->field), false) + +// Helper functions +uint8_t* decodeHexString(const char *hexstring, size_t padTo); + +char* encodeHexToString(uint8_t byte_array[], size_t len); + +void printBytes(const char *desc, const uint8_t *data, size_t len, bool swap); + +void printReport(const snp_attestation_report *r); \ No newline at end of file diff --git a/tools/get-snp-report/hex2report.c b/tools/get-snp-report/hex2report.c new file mode 100644 index 00000000..c3fe4269 --- /dev/null +++ b/tools/get-snp-report/hex2report.c @@ -0,0 +1,38 @@ + +/* Copyright (c) Microsoft Corporation. + Licensed under the MIT License. */ + +#include +#include +#include +#include +#include + +#include "helpers.h" + + + +int main(int argc, char** argv) +{ + char buffer[102400]; + int bytes_read = fread(buffer, 1, sizeof(buffer)-1, stdin); + if (bytes_read < 0) { + fprintf(stderr, "pipe read failed\n"); + exit(-1); + } + if (bytes_read == 0) { + fprintf(stderr, "empty pipe\n"); + exit(-1); + } + if (bytes_read < sizeof(snp_attestation_report)) { + fprintf(stderr, "pipe too short\n"); + exit(-1); + } + buffer[bytes_read] = 0; + + uint8_t* byte_array = decodeHexString(buffer, 0); + + printReport((const snp_attestation_report *)byte_array); + + return 0; +} \ No newline at end of file diff --git a/tools/get-snp-report/snp-attestation.h b/tools/get-snp-report/snp-attestation.h new file mode 100644 index 00000000..affab65c --- /dev/null +++ b/tools/get-snp-report/snp-attestation.h @@ -0,0 +1,58 @@ +/* Copyright (c) Microsoft Corporation. + Licensed under the MIT License. */ + +#pragma once + +#include +#include + +/* structures common to both 5.15.* and 6.* kernels */ +/* essentially this is the interface to the PSP */ + +/* from SEV-SNP Firmware ABI Specification Table 20 */ +typedef struct { + uint8_t report_data[64]; + uint32_t vmpl; + uint8_t reserved[28]; // needs to be zero +} msg_report_req; + +/* from SEV-SNP Firmware ABI Specification from Table 21 */ +typedef struct { + uint32_t version; // version no. of this attestation report. Set to 1 for this specification. + uint32_t guest_svn; // The guest SVN + uint64_t policy; // see table 8 - various settings + __uint128_t family_id; // as provided at launch + __uint128_t image_id; // as provided at launch + uint32_t vmpl; // the request VMPL for the attestation report + uint32_t signature_algo; + uint64_t platform_version; // The install version of the firmware + uint64_t platform_info; // information about the platform see table 22 + // not going to try to use bit fields for this next one. Too confusing as to which bit of the byte will be used. Make a mask if you need it + uint32_t author_key_en; // 31 bits of reserved, must be zero, bottom bit indicates that the digest of the + // author key is present in AUTHOR_KEY_DIGEST. Set to the value of GCTX.AuthorKeyEn. + uint32_t reserved1; // must be zero + uint8_t report_data[64]; // Guest provided data. + uint8_t measurement[48]; // measurement calculated at launch + uint8_t host_data[32]; // data provided by the hypervisor at launch + uint8_t id_key_digest[48]; // SHA-384 digest of the ID public key that signed the ID block provided in SNP_LAUNCH_FINISH + uint8_t author_key_digest[48]; // SHA-384 digest of the Author public key that certified the ID key, if provided in SNP_LAUNCH_FINISH. Zeros if author_key_en is 1 (sounds backwards to me). + uint8_t report_id[32]; // Report ID of this guest. + uint8_t report_id_ma[32]; // Report ID of this guest's mmigration agent. + uint64_t reported_tcb; // Reported TCB version used to derive the VCEK that signed this report + uint8_t reserved2[24]; // reserved + uint8_t chip_id[64]; // Identifier unique to the chip + uint8_t committed_svn[8]; // The current commited SVN of the firware (version 2 report feature) + uint8_t committed_version[8]; // The current commited version of the firware + uint8_t launch_svn[8]; // The SVN that this guest was launched or migrated at + uint8_t reserved3[168]; // reserved + uint8_t signature[512]; // Signature of this attestation report. See table 23. +} snp_attestation_report; + +/* from SEV-SNP Firmware ABI Specification Table 22 */ +typedef struct { + uint32_t status; + uint32_t report_size; + uint8_t reserved[24]; + snp_attestation_report report; + uint8_t padding[64]; // padding to the size of SEV_SNP_REPORT_RSP_BUF_SZ (i.e., 1280 bytes) +} msg_response_resp; diff --git a/tools/get-snp-report/snp-ioctl5.h b/tools/get-snp-report/snp-ioctl5.h new file mode 100644 index 00000000..2be91fbc --- /dev/null +++ b/tools/get-snp-report/snp-ioctl5.h @@ -0,0 +1,47 @@ +/* Copyright (c) Microsoft Corporation. + Licensed under the MIT License. */ + +#pragma once + +#include +#include +#include + +/* linux kernel 5.15.* versions of the ioctls that talk to the PSP */ + +/* From sev-snp driver include/uapi/linux/psp-sev-guest.h */ +struct sev_snp_guest_request { + uint8_t req_msg_type; + uint8_t rsp_msg_type; + uint8_t msg_version; + uint16_t request_len; + uint64_t request_uaddr; + uint16_t response_len; + uint64_t response_uaddr; + uint32_t error; /* firmware error code on failure (see psp-sev.h) */ +}; + +enum snp_msg_type { + SNP_MSG_TYPE_INVALID = 0, + SNP_MSG_CPUID_REQ, + SNP_MSG_CPUID_RSP, + SNP_MSG_KEY_REQ, + SNP_MSG_KEY_RSP, + SNP_MSG_REPORT_REQ, + SNP_MSG_REPORT_RSP, + SNP_MSG_EXPORT_REQ, + SNP_MSG_EXPORT_RSP, + SNP_MSG_IMPORT_REQ, + SNP_MSG_IMPORT_RSP, + SNP_MSG_ABSORB_REQ, + SNP_MSG_ABSORB_RSP, + SNP_MSG_VMRK_REQ, + SNP_MSG_VMRK_RSP, + SNP_MSG_TYPE_MAX +}; + + +#define SEV_GUEST_IOC_TYPE 'S' +#define SEV_SNP_GUEST_MSG_REQUEST _IOWR(SEV_GUEST_IOC_TYPE, 0x0, struct sev_snp_guest_request) +#define SEV_SNP_GUEST_MSG_REPORT _IOWR(SEV_GUEST_IOC_TYPE, 0x1, struct sev_snp_guest_request) +#define SEV_SNP_GUEST_MSG_KEY _IOWR(SEV_GUEST_IOC_TYPE, 0x2, struct sev_snp_guest_request) diff --git a/tools/get-snp-report/snp-ioctl6.h b/tools/get-snp-report/snp-ioctl6.h new file mode 100644 index 00000000..0621c60a --- /dev/null +++ b/tools/get-snp-report/snp-ioctl6.h @@ -0,0 +1,75 @@ +/* Copyright (c) Microsoft Corporation. + Licensed under the MIT License. */ + +#pragma once + + +#include +#include +#include + +/* linux kernel 6.* versions of the ioctls that talk to the PSP */ + +/* From sev-snp driver include/uapi/linux/psp-sev-guest.h */ + +struct sev_snp_guest_request { + uint8_t req_msg_type; + uint8_t rsp_msg_type; + uint8_t msg_version; + uint16_t request_len; + uint64_t request_uaddr; + uint16_t response_len; + uint64_t response_uaddr; + uint32_t error; /* firmware error code on failure (see psp-sev.h) */ +}; + +// aka/replaced by this from include/uapi/linux/sev-guest.h +// +typedef struct { + /* message version number (must be non-zero) */ + uint8_t msg_version; + + /* Request and response structure address */ + uint64_t req_data; + uint64_t resp_data; + + /* firmware error code on failure (see psp-sev.h) */ + uint64_t fw_err; +} snp_guest_request_ioctl; + +enum snp_msg_type { + SNP_MSG_TYPE_INVALID = 0, + SNP_MSG_CPUID_REQ, + SNP_MSG_CPUID_RSP, + SNP_MSG_KEY_REQ, + SNP_MSG_KEY_RSP, + SNP_MSG_REPORT_REQ, + SNP_MSG_REPORT_RSP, + SNP_MSG_EXPORT_REQ, + SNP_MSG_EXPORT_RSP, + SNP_MSG_IMPORT_REQ, + SNP_MSG_IMPORT_RSP, + SNP_MSG_ABSORB_REQ, + SNP_MSG_ABSORB_RSP, + SNP_MSG_VMRK_REQ, + SNP_MSG_VMRK_RSP, + SNP_MSG_TYPE_MAX +}; + +#define SNP_GUEST_REQ_IOC_TYPE 'S' +#define SNP_GET_REPORT _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x0, snp_guest_request_ioctl) +#define SNP_GET_DERIVED_KEY _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x1, snp_guest_request_ioctl) +#define SNP_GET_EXT_REPORT _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x2, snp_guest_request_ioctl) + +/* from SEV-SNP Firmware ABI Specification Table 20 */ + +typedef struct { + uint8_t report_data[64]; + uint32_t vmpl; + uint8_t reserved[28]; // needs to be zero +} snp_report_req; // aka snp_report_req in (linux) include/uapi/linux/sev-guest.h + +typedef struct { +/* response data, see SEV-SNP spec for the format */ + uint8_t data[4000]; +} snp_report_resp; diff --git a/tools/get-snp-report/snp-psp.h b/tools/get-snp-report/snp-psp.h index 2a8fe063..e4a63712 100644 --- a/tools/get-snp-report/snp-psp.h +++ b/tools/get-snp-report/snp-psp.h @@ -1,90 +1,45 @@ /* Copyright (c) Microsoft Corporation. Licensed under the MIT License. */ +#pragma once + #include #include #include /* From sev-snp driver include/uapi/linux/psp-sev-guest.h */ struct sev_snp_guest_request { - uint8_t req_msg_type; - uint8_t rsp_msg_type; - uint8_t msg_version; - uint16_t request_len; - uint64_t request_uaddr; - uint16_t response_len; - uint64_t response_uaddr; - uint32_t error; /* firmware error code on failure (see psp-sev.h) */ + uint8_t req_msg_type; + uint8_t rsp_msg_type; + uint8_t msg_version; + uint16_t request_len; + uint64_t request_uaddr; + uint16_t response_len; + uint64_t response_uaddr; + uint32_t error; /* firmware error code on failure (see psp-sev.h) */ }; enum snp_msg_type { - SNP_MSG_TYPE_INVALID = 0, - SNP_MSG_CPUID_REQ, - SNP_MSG_CPUID_RSP, - SNP_MSG_KEY_REQ, - SNP_MSG_KEY_RSP, - SNP_MSG_REPORT_REQ, - SNP_MSG_REPORT_RSP, - SNP_MSG_EXPORT_REQ, - SNP_MSG_EXPORT_RSP, - SNP_MSG_IMPORT_REQ, - SNP_MSG_IMPORT_RSP, - SNP_MSG_ABSORB_REQ, - SNP_MSG_ABSORB_RSP, - SNP_MSG_VMRK_REQ, - SNP_MSG_VMRK_RSP, - SNP_MSG_TYPE_MAX + SNP_MSG_TYPE_INVALID = 0, + SNP_MSG_CPUID_REQ, + SNP_MSG_CPUID_RSP, + SNP_MSG_KEY_REQ, + SNP_MSG_KEY_RSP, + SNP_MSG_REPORT_REQ, + SNP_MSG_REPORT_RSP, + SNP_MSG_EXPORT_REQ, + SNP_MSG_EXPORT_RSP, + SNP_MSG_IMPORT_REQ, + SNP_MSG_IMPORT_RSP, + SNP_MSG_ABSORB_REQ, + SNP_MSG_ABSORB_RSP, + SNP_MSG_VMRK_REQ, + SNP_MSG_VMRK_RSP, + SNP_MSG_TYPE_MAX }; -/* from SEV-SNP Firmware ABI Specification Table 20 */ -typedef struct { - uint8_t report_data[64]; - uint32_t vmpl; - uint8_t reserved[28]; // needs to be zero -} msg_report_req; - -/* from SEV-SNP Firmware ABI Specification from Table 21 */ -typedef struct { - uint32_t version; // version no. of this attestation report. Set to 1 for this specification. - uint32_t guest_svn; // The guest SVN - uint64_t policy; // see table 8 - various settings - __uint128_t family_id; // as provided at launch - __uint128_t image_id; // as provided at launch - uint32_t vmpl; // the request VMPL for the attestation report - uint32_t signature_algo; - uint64_t platform_version; // The install version of the firmware - uint64_t platform_info; // information about the platform see table 22 - // not going to try to use bit fields for this next one. Too confusing as to which bit of the byte will be used. Make a mask if you need it - uint32_t author_key_en; // 31 bits of reserved, must be zero, bottom bit indicates that the digest of the - // author key is present in AUTHOR_KEY_DIGEST. Set to the value of GCTX.AuthorKeyEn. - uint32_t reserved1; // must be zero - uint8_t report_data[64]; // Guest provided data. - uint8_t measurement[48]; // measurement calculated at launch - uint8_t host_data[32]; // data provided by the hypervisor at launch - uint8_t id_key_digest[48]; // SHA-384 digest of the ID public key that signed the ID block provided in SNP_LAUNCH_FINISH - uint8_t author_key_digest[48]; // SHA-384 digest of the Author public key that certified the ID key, if provided in SNP_LAUNCH_FINISH. Zeros if author_key_en is 1 (sounds backwards to me). - uint8_t report_id[32]; // Report ID of this guest. - uint8_t report_id_ma[32]; // Report ID of this guest's mmigration agent. - uint64_t reported_tcb; // Reported TCB version used to derive the VCEK that signed this report - uint8_t reserved2[24]; // reserved - uint8_t chip_id[64]; // Identifier unique to the chip - uint8_t committed_svn[8]; // The current commited SVN of the firware (version 2 report feature) - uint8_t committed_version[8]; // The current commited version of the firware - uint8_t launch_svn[8]; // The SVN that this guest was launched or migrated at - uint8_t reserved3[168]; // reserved - uint8_t signature[512]; // Signature of this attestation report. See table 23. -} snp_attestation_report; - -/* from SEV-SNP Firmware ABI Specification Table 22 */ -typedef struct { - uint32_t status; - uint32_t report_size; - uint8_t reserved[24]; - snp_attestation_report report; - uint8_t padding[64]; // padding to the size of SEV_SNP_REPORT_RSP_BUF_SZ (i.e., 1280 bytes) -} msg_response_resp; -#define SEV_GUEST_IOC_TYPE 'S' -#define SEV_SNP_GUEST_MSG_REQUEST _IOWR(SEV_GUEST_IOC_TYPE, 0x0, struct sev_snp_guest_request) -#define SEV_SNP_GUEST_MSG_REPORT _IOWR(SEV_GUEST_IOC_TYPE, 0x1, struct sev_snp_guest_request) -#define SEV_SNP_GUEST_MSG_KEY _IOWR(SEV_GUEST_IOC_TYPE, 0x2, struct sev_snp_guest_request) +#define SEV_GUEST_IOC_TYPE 'S' +#define SEV_SNP_GUEST_MSG_REQUEST _IOWR(SEV_GUEST_IOC_TYPE, 0x0, struct sev_snp_guest_request) +#define SEV_SNP_GUEST_MSG_REPORT _IOWR(SEV_GUEST_IOC_TYPE, 0x1, struct sev_snp_guest_request) +#define SEV_SNP_GUEST_MSG_KEY _IOWR(SEV_GUEST_IOC_TYPE, 0x2, struct sev_snp_guest_request)