On Thu, 2 May 2024, David E. Box wrote: > Add support on the intel_sdsi tool to perform SPDM GET_DIGESTS and > GET_CERTIFICATE commands. Output is sent to stdout. > > Example reading the certificate chain from socket 0: > > intel_sdsi -d 1 -attest get_certificate | openssl x509 -inform DER -nout -text > > Signed-off-by: David E. Box <david.e.box@xxxxxxxxxxxxxxx> > --- > tools/arch/x86/intel_sdsi/Makefile | 27 +- > tools/arch/x86/intel_sdsi/intel_sdsi.c | 70 +++- > tools/arch/x86/intel_sdsi/spdm.c | 466 +++++++++++++++++++++++++ > tools/arch/x86/intel_sdsi/spdm.h | 13 + > 4 files changed, 565 insertions(+), 11 deletions(-) > create mode 100644 tools/arch/x86/intel_sdsi/spdm.c > create mode 100644 tools/arch/x86/intel_sdsi/spdm.h > > diff --git a/tools/arch/x86/intel_sdsi/Makefile b/tools/arch/x86/intel_sdsi/Makefile > index 5de2288cda79..6da6c4ee96d7 100644 > --- a/tools/arch/x86/intel_sdsi/Makefile > +++ b/tools/arch/x86/intel_sdsi/Makefile > @@ -1,21 +1,28 @@ > # SPDX-License-Identifier: GPL-2.0 > # Makefile for Intel Software Defined Silicon provisioning tool > - > -intel_sdsi: intel_sdsi.c > - > -CFLAGS = -Wextra > - > +include ../../../scripts/Makefile.include > BINDIR ?= /usr/sbin > > -override CFLAGS += -O2 -Wall > +SRCS = intel_sdsi.c spdm.c > > -%: %.c > - $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) > +OBJS = $(SRCS:.c=.o) > + > +override CFLAGS += -O2 -Wall -Wextra -I../../../include -ggdb3 > + > +intel_sdsi: intel_sdsi.h $(OBJS) > + $(CC) $(CFLAGS) $(OBJS) -o $@ > + > +intel_sdsi.h: ../../../../include/uapi/linux/intel_sdsi.h > + ln -sf ../../../../include/uapi/linux/intel_sdsi.h $@ > + > +%.o: %.c > + $(CC) $(CFLAGS) -c $< -o $@ > > -.PHONY : clean > clean : > - @rm -f intel_sdsi > + @rm -f intel_sdsi intel_sdsi.h $(OBJS) > > install : intel_sdsi > install -d $(DESTDIR)$(BINDIR) > install -m 755 -p intel_sdsi $(DESTDIR)$(BINDIR)/intel_sdsi > + > +.PHONY : clean install Why is Makefile rework in this patch. Should it like be in own patch? > diff --git a/tools/arch/x86/intel_sdsi/intel_sdsi.c b/tools/arch/x86/intel_sdsi/intel_sdsi.c > index 766a5d26f534..419f43e403b7 100644 > --- a/tools/arch/x86/intel_sdsi/intel_sdsi.c > +++ b/tools/arch/x86/intel_sdsi/intel_sdsi.c > @@ -22,6 +22,9 @@ > > #include <sys/types.h> > > +#include "spdm.h" > +#include "intel_sdsi.h" > + > #ifndef __packed > #define __packed __attribute__((packed)) > #endif > @@ -179,6 +182,7 @@ struct sdsi_dev { > struct state_certificate sc; > char *dev_name; > char *dev_path; > + int dev_no; > uint32_t guid; > }; > > @@ -189,6 +193,12 @@ enum command { > CMD_STATE_CERT, > CMD_PROV_AKC, > CMD_PROV_CAP, > + CMD_ATTESTATION, > +}; > + > +enum spdm_message { > + GET_DIGESTS, > + GET_CERTIFICATE, > }; > > static void sdsi_list_devices(void) > @@ -647,6 +657,41 @@ static int sdsi_provision_cap(struct sdsi_dev *s, char *bin_file) > return sdsi_provision(s, bin_file, CMD_PROV_CAP); > } > > +static int sdsi_attestation(struct sdsi_dev *s, enum spdm_message message) > +{ > + struct cert_chain c; > + uint8_t digest[TPM_ALG_SHA_384_SIZE]; > + size_t size; > + int ret, i; > + > + switch (message) { > + case GET_CERTIFICATE: > + ret = spdm_get_certificate(s->dev_no, &c); > + if (ret) > + return ret; > + > + size = fwrite(c.chain, sizeof(uint8_t), c.len, stdout); > + if (size != c.len) { > + fprintf(stderr, "Unable to write complete certificate chain\n"); > + ret = -1; > + } > + > + free(c.chain); > + break; > + case GET_DIGESTS: > + ret = spdm_get_digests(s->dev_no, digest); > + if (ret) > + return ret; > + > + for (i = 0; i < TPM_ALG_SHA_384_SIZE; i++) > + printf("%02x", digest[i]); > + printf("\n"); > + break; > + } > + > + return ret; > +} > + > static int read_sysfs_data(const char *file, int *value) > { > char buff[16]; > @@ -728,6 +773,7 @@ static struct sdsi_dev *sdsi_create_dev(char *dev_no) > } > > s->guid = guid; > + s->dev_no = atoi(dev_no); > > return s; > } > @@ -742,6 +788,7 @@ static void sdsi_free_dev(struct sdsi_dev *s) > static void usage(char *prog) > { > printf("Usage: %s [-l] [-d DEVNO [-i] [-s] [-m | -C] [-a FILE] [-c FILE]\n", prog); > + printf(" [-attest MESSAGE]\n"); Long options use -- ? > } > > static void show_help(void) > @@ -754,12 +801,15 @@ static void show_help(void) > printf(" %-18s\t%s\n", "-m, --meter", "show meter certificate data"); > printf(" %-18s\t%s\n", "-C, --meter_current", "show live unattested meter data"); > printf(" %-18s\t%s\n", "-a, --akc FILE", "provision socket with AKC FILE"); > - printf(" %-18s\t%s\n", "-c, --cap FILE>", "provision socket with CAP FILE"); > + printf(" %-18s\t%s\n", "-c, --cap FILE", "provision socket with CAP FILE"); > + printf(" %-18s\t%s\n", "-attest MESSAGE", "send attestion MESSAGE. MESSAGE"); -- ? > + printf(" %-18s\t%s\n", "", "values are:"); > } > > int main(int argc, char *argv[]) > { > char bin_file[PATH_MAX], *dev_no = NULL; > + enum spdm_message message = GET_DIGESTS; > bool device_selected = false; > char *progname; > enum command command = -1; > @@ -769,6 +819,7 @@ int main(int argc, char *argv[]) > > static struct option long_options[] = { > {"akc", required_argument, 0, 'a'}, > + {"attest", required_argument, 0, 0}, > {"cap", required_argument, 0, 'c'}, > {"devno", required_argument, 0, 'd'}, > {"help", no_argument, 0, 'h'}, > @@ -820,6 +871,20 @@ int main(int argc, char *argv[]) > > command = (opt == 'a') ? CMD_PROV_AKC : CMD_PROV_CAP; > break; > + case 0: > + if (strcmp(long_options[option_index].name, "attest") == 0) { > + command = CMD_ATTESTATION; > + > + if (strcmp(optarg, "get_digests") == 0) > + message = GET_DIGESTS; > + else if (strcmp(optarg, "get_certificate") == 0) > + message = GET_CERTIFICATE; > + else { > + fprintf(stderr, "Unrecognized attestation command\n"); > + return -1; > + } > + } > + break; > case 'h': > usage(progname); > show_help(); > @@ -854,6 +919,9 @@ int main(int argc, char *argv[]) > case CMD_PROV_CAP: > ret = sdsi_provision_cap(s, bin_file); > break; > + case CMD_ATTESTATION: > + ret = sdsi_attestation(s, message); > + break; > default: > fprintf(stderr, "No command specified\n"); > return -1; > diff --git a/tools/arch/x86/intel_sdsi/spdm.c b/tools/arch/x86/intel_sdsi/spdm.c > new file mode 100644 > index 000000000000..b5bf91215cbb > --- /dev/null > +++ b/tools/arch/x86/intel_sdsi/spdm.c > @@ -0,0 +1,466 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * spdm: Lightweight Security Protocol and Data Model (SPDM) specification > + * support code for performing attestation commands using the Intel On > + * Demand driver ioctl interface. Intel On Demand currently supports > + * SPDM version 1.0 > + * > + * See the SPDM v1.0 specification at: > + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.0.1.pdf > + * > + * Copyright (C) 2024 Intel Corporation. All rights reserved. > + */ > + > +#include<linux/bits.h> > + > +#include<fcntl.h> > +#include<stdio.h> > +#include<stdlib.h> > +#include<stdint.h> > +#include<string.h> > +#include<unistd.h> > +#include<sys/ioctl.h> Where's the whitespace? > + > +#include "spdm.h" > +#include "intel_sdsi.h" > + > +// SPDM constants > +#define SPDM_VERSION 0x10 > +#define SPDM_REQUEST 0x80 > +#define SPDM_ERROR 0x7f > + > +// SPDM request codes > +#define SPDM_GET_VERSION 0x84 > +#define SPDM_GET_CAPABILITIES 0xE1 > +#define SPDM_NEGOTIATE_ALGORITHMS 0xE3 > +#define SPDM_GET_DIGESTS 0x81 > +#define SPDM_GET_CERTIFICATE 0x82 > +#define SPDM_CHALLENGE 0x83 > +#define SPDM_GET_MEASUREMENTS 0xE0 > + > +#define SDSI_DEV_PATH "/dev/isdsi" > + > +#define SPDM_RSVD 0 > + > +#ifndef __packed > +#define __packed __attribute__((packed)) > +#endif > + > +struct spdm_header { > + uint8_t version; > + uint8_t code; > + uint8_t param1; > + uint8_t param2; > +} __packed; Does this really need packed? > +static int error_response(struct spdm_header *response) > +{ > + if (response->code != SPDM_ERROR) > + fprintf(stderr, "ERROR: Unrecognized SPDM response\n"); > + > + switch (response->param1) { > + case 0x00: > + case 0x02: > + case 0x06: > + case 0x08 ... 0x40: > + case 0x44 ... 0xfe: > + fprintf(stderr, "SPDM RSP ERROR: Reserved.\n"); > + break; > + case 0x01: > + fprintf(stderr, "SPDM RSP ERROR: One or more request fields are invalid.\n"); > + break; > + case 0x03: > + fprintf(stderr, "SPDM RSP ERROR: The Responder received the request message\n"); > + fprintf(stderr, "and the Responder decided to ignore the request message\n"); > + fprintf(stderr, "but the Responder may be able to process the request message\n"); > + fprintf(stderr, "if the request message is sent again in the future.\n"); > + break; > + case 0x04: > + fprintf(stderr, "SPDM RSP ERROR: The Responder received an unexpected request\n"); > + fprintf(stderr, "message. For example, CHALLENGE before NEGOTIATE_ALGORITHMS.\n"); > + break; > + case 0x05: > + fprintf(stderr, "SPDM RSP ERROR: Unspecified error occurred.\n"); > + break; > + case 0x07: > + fprintf(stderr, "SPDM RSP ERROR: The RequestResponseCode in the request\n"); > + fprintf(stderr, "message is unsupported.\n"); > + break; > + case 0x41: > + fprintf(stderr, "SPDM RSP ERROR: Requested SPDM Major Version is not\n"); > + fprintf(stderr, "supported.\n"); > + break; > + case 0x42: > + fprintf(stderr, "SPDM RSP ERROR: See the RESPONSE_IF_READY request message.\n"); > + break; > + case 0x43: > + fprintf(stderr, "SPDM RSP ERROR: Responder is requesting Requester to reissue\n"); > + fprintf(stderr, "GET_VERSION to resynchronize.\n"); > + break; > + case 0xFF: > + fprintf(stderr, "SPDM RSP ERROR: Vendor or Other Standards defined.\n"); > + break; > + } > + > + return -1; > +} > + > +static int sdsi_process_ioctl(int ioctl_no, void *info, uint8_t dev_no) > +{ > + char pathname[14]; > + int fd, ret; > + > + ret = snprintf(pathname, 14, "%s%d", SDSI_DEV_PATH, dev_no); > + if (ret < 0) > + return ret; > + > + fd = open(pathname, O_RDONLY); > + if (fd < 0) > + return fd; > + > + ret = ioctl(fd, ioctl_no, info); > + if (ret) > + perror("Failed to process ioctl\n"); > + > + close(fd); > + > + return ret; > +} > + > +static int > +sdsi_process_spdm(void *request, void *response, int req_size, uint32_t rsp_size, > + int dev_no) > +{ > + struct sdsi_spdm_command *command; > + struct sdsi_spdm_message *message = request; > + uint8_t request_code; > + int ret; > + > + command = malloc(sizeof(*command)); > + if (!command) { > + perror("malloc\n"); perror() strings should not end into newline characted because the colon and error message is appended into the string argument. > + return -1; > + } > + > + command->size = req_size; > + command->message = *message; > + request_code = command->message.request_response_code; > + > + ret = sdsi_process_ioctl(SDSI_IF_SPDM_COMMAND, command, dev_no); > + if (ret) > + goto free_command; > + > + if (command->size < sizeof(struct spdm_header)) { > + fprintf(stderr, "Bad SPDM message size\n"); > + ret = -1; > + goto free_command; > + } > + > + if (command->message.request_response_code != (request_code & ~SPDM_REQUEST)) { > + ret = error_response((struct spdm_header *)&command->message); > + goto free_command; > + } > + > + if (response) { > + if (command->size > rsp_size) { > + fprintf(stderr, "SPDM response buffer too small\n"); > + ret = -1; > + goto free_command; > + } > + > + memcpy(response, &command->message, command->size); > + } > + > +free_command: > + free(command); > + return ret; > +} > + > +struct version_number_entry { > + uint8_t alpha:4; > + uint8_t update_version_number:4; > + union { > + uint8_t version; > + struct { > + uint8_t minor:4; > + uint8_t major:4; > + }; > + }; > +} __packed; > + > +struct get_version_response { > + struct spdm_header header; > + uint16_t reserved:8; > + uint16_t version_number_entry_count:8; > + struct version_number_entry entry[10]; > +} __packed; > + > +static int spdm_get_version(int dev_no) > +{ > + struct spdm_header request = {}; > + struct get_version_response response = {}; > + uint8_t version; > + int ret; > + > + request.version = SPDM_VERSION; > + request.code = SPDM_GET_VERSION; > + request.param1 = SPDM_RSVD; > + request.param2 = SPDM_RSVD; > + > + ret = sdsi_process_spdm(&request, &response, sizeof(request), > + sizeof(response), dev_no); > + if (ret) { > + fprintf(stderr, "Failed GET_VERSION\n"); > + return ret; > + } > + > + if (!response.version_number_entry_count) { > + fprintf(stderr, "Bad GET_VERSION entry count\n"); > + return -1; > + } > + > + version = response.entry[0].version; > + > + if (version != SPDM_VERSION) { > + fprintf(stderr, "Unsupported version 0x%x\n", SPDM_VERSION); > + return -1; > + } > + > + return 0; > +} > + > +static int spdm_get_capabilities(int dev_no) > +{ > + struct spdm_header request = {}; > + int ret; > + > + request.version = SPDM_VERSION; > + request.code = SPDM_GET_CAPABILITIES; > + request.param1 = SPDM_RSVD; > + request.param2 = SPDM_RSVD; > + > + ret = sdsi_process_spdm(&request, NULL, sizeof(request), 0, dev_no); > + if (ret) { > + fprintf(stderr, "Failed GET_CAPABILITIES\n"); > + return ret; > + } > + > + return 0; > +} > + > +struct spdm_negotiate_alg { > + struct spdm_header header; > + uint32_t length:16; > + uint32_t measurement_specification:8; > + uint32_t reserved:8; > + uint32_t base_asym_algo; > + uint32_t base_hash_algo; > + uint32_t reserved2[3]; > + uint32_t ext_asym_count:8; > + uint32_t ext_hash_count:8; > + uint32_t reserved3:16; > +} __packed; I'd expect this to not need __packed. > +#define MEASUREMENT_SPEC_DMTF BIT(0) > +#define BASE_ASYM_ALG_ECDSA_ECC_NIST_P384 BIT(7) > +#define BASE_HASH_ALG_SHA_384 BIT(1) > + > +static int spdm_negotiate_algorithms(int dev_no) > +{ > + struct spdm_negotiate_alg request = {}; > + int ret; > + > + request.header.version = SPDM_VERSION; > + request.header.code = SPDM_NEGOTIATE_ALGORITHMS; > + request.header.param1 = SPDM_RSVD; > + request.header.param2 = SPDM_RSVD; > + > + request.length = sizeof(request); > + request.measurement_specification = MEASUREMENT_SPEC_DMTF; > + request.base_asym_algo = BASE_ASYM_ALG_ECDSA_ECC_NIST_P384; > + request.base_hash_algo = BASE_HASH_ALG_SHA_384; > + > + ret = sdsi_process_spdm(&request, NULL, sizeof(request), 0, dev_no); > + if (ret) { > + fprintf(stderr, "Failed NEGOTIATE_ALGORITHMS\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int spdm_negotiate(int dev_no) > +{ > + int ret; > + > + ret = spdm_get_version(dev_no); > + if (ret) > + return ret; > + > + ret = spdm_get_capabilities(dev_no); > + if (ret) > + return ret; > + > + return spdm_negotiate_algorithms(dev_no); > +} > + > +struct get_digests_response { > + struct spdm_header header; > + uint8_t digest[TPM_ALG_SHA_384_SIZE]; > +}; > + > +#define SLOT_MASK(slot) BIT(slot) > + > +int spdm_get_digests(int dev_no, uint8_t digest[TPM_ALG_SHA_384_SIZE]) > +{ > + struct spdm_header request = {}; > + struct get_digests_response response = {}; > + int ret; > + > + ret = spdm_negotiate(dev_no); > + if (ret) > + return ret; > + > + request.version = SPDM_VERSION; > + request.code = SPDM_GET_DIGESTS; > + request.param1 = SPDM_RSVD; > + request.param2 = SPDM_RSVD; > + > + ret = sdsi_process_spdm(&request, &response, sizeof(request), > + sizeof(response), dev_no); > + if (ret) { > + fprintf(stderr, "Failed GET_DIGESTS\n"); > + return ret; > + } > + > + if (!(response.header.param2 & SLOT_MASK(0))) { > + fprintf(stderr, "Error, Slot 0 not selected in GET_DIGESTS\n"); > + return -1; > + } > + > + if (digest) > + memcpy(digest, response.digest, TPM_ALG_SHA_384_SIZE); > + > + return 0; > +} > + > +#define CERT_SLOT 0 > +#define CERT_BUF_LEN 4096 > + > +struct get_cert_request { > + struct spdm_header header; > + uint16_t offset; > + uint16_t length; > +} __packed; > + > +struct get_cert_response { > + struct spdm_header header; > + uint16_t portion_length; > + uint16_t remainder_length; > + uint8_t certificate_chain[CERT_BUF_LEN]; > +} __packed; Neither of these needs packed? -- i. > +static int get_certificate_size(int dev_no) > +{ > + struct get_cert_request request = {}; > + struct get_cert_response response = {}; > + int ret; > + > + request.header.version = SPDM_VERSION; > + request.header.code = SPDM_GET_CERTIFICATE; > + request.header.param1 = CERT_SLOT; > + request.header.param2 = SPDM_RSVD; > + request.offset = 0; > + request.length = CERT_BUF_LEN; > + > + ret = sdsi_process_spdm(&request, &response, sizeof(request), > + sizeof(response), dev_no); > + if (ret) { > + fprintf(stderr, "Error getting size during GET_CERTIFICATE\n"); > + return ret; > + } > + > + return response.portion_length + response.remainder_length; > +} > + > +static int get_certificate_portion(int dev_no, uint16_t offset, uint16_t length, > + uint16_t *portion_length, uint16_t *remainder_length, > + uint8_t *cert_chain) > +{ > + struct get_cert_request request = {}; > + struct get_cert_response response = {}; > + int ret; > + > + request.header.version = SPDM_VERSION; > + request.header.code = SPDM_GET_CERTIFICATE; > + request.header.param1 = CERT_SLOT; > + request.header.param2 = SPDM_RSVD; > + request.offset = offset; > + request.length = length; > + > + ret = sdsi_process_spdm(&request, &response, sizeof(request), > + sizeof(response), dev_no); > + if (ret) { > + fprintf(stderr, "Failed GET_CERTIFICATE\n"); > + return ret; > + } > + > + *portion_length = response.portion_length; > + *remainder_length = response.remainder_length; > + > + memcpy(cert_chain + offset, response.certificate_chain, *portion_length); > + > + return 0; > +} > + > +int spdm_get_certificate(int dev_no, struct cert_chain *c) > +{ > + uint16_t remainder_length = CERT_BUF_LEN; > + uint16_t portion_length = 0; > + uint16_t offset = 0; > + int ret, size; > + > + ret = spdm_negotiate(dev_no); > + if (ret) > + return ret; > + > + ret = spdm_get_digests(dev_no, NULL); > + if (ret) > + return ret; > + > + size = get_certificate_size(dev_no); > + if (size < 0) > + return size; > + > + c->chain = malloc(size); > + if (!c->chain) { > + perror("malloc"); > + return -1; > + } > + > + while (remainder_length) { > + int length; > + > + if (remainder_length > CERT_BUF_LEN) > + length = CERT_BUF_LEN; > + else > + length = remainder_length; > + > + offset += portion_length; > + > + ret = get_certificate_portion(dev_no, offset, length, > + &portion_length, > + &remainder_length, > + c->chain); > + if (ret < 0) > + goto free_cert_chain; > + } > + > + c->len = offset + portion_length; > + return 0; > + > +free_cert_chain: > + free(c->chain); > + c->chain = NULL; > + return ret; > +} > diff --git a/tools/arch/x86/intel_sdsi/spdm.h b/tools/arch/x86/intel_sdsi/spdm.h > new file mode 100644 > index 000000000000..aa7e08ffb872 > --- /dev/null > +++ b/tools/arch/x86/intel_sdsi/spdm.h > @@ -0,0 +1,13 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#include <stdint.h> > + > +#define TPM_ALG_SHA_384_SIZE 48 > + > +struct cert_chain { > + void *chain; > + size_t len; > +}; > + > +int spdm_get_digests(int dev_no, uint8_t digest[TPM_ALG_SHA_384_SIZE]); > +int spdm_get_certificate(int dev_no, struct cert_chain *c); > + >