diff mbox series

[2/2] tools/arch/x86/intel_sdsi: Add attestation support

Message ID 20240503025727.10254-2-david.e.box@linux.intel.com (mailing list archive)
State Changes Requested, archived
Headers show
Series [1/2] platform/x86/intel/sdsi: Add ioctl SPDM transport | expand

Commit Message

David E. Box May 3, 2024, 2:57 a.m. UTC
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@linux.intel.com>
---
 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

Comments

Ilpo Järvinen May 3, 2024, 8:30 a.m. UTC | #1
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@linux.intel.com>
> ---
>  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?
diff mbox series

Patch

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
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");
 }
 
 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>
+
+#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;
+
+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");
+		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;
+
+#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;
+
+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);
+