qemu/backends/spdm-socket.c
Huai-Cheng Kuo bc419a1cc5 backends: Initial support for SPDM socket support
SPDM enables authentication, attestation and key exchange to assist in
providing infrastructure security enablement. It's a standard published
by the DMTF [1].

SPDM supports multiple transports, including PCIe DOE and MCTP.
This patch adds support to QEMU to connect to an external SPDM
instance.

SPDM support can be added to any QEMU device by exposing a
TCP socket to a SPDM server. The server can then implement the SPDM
decoding/encoding support, generally using libspdm [2].

This is similar to how the current TPM implementation works and means
that the heavy lifting of setting up certificate chains, capabilities,
measurements and complex crypto can be done outside QEMU by a well
supported and tested library.

1: https://www.dmtf.org/standards/SPDM
2: https://github.com/DMTF/libspdm

Signed-off-by: Huai-Cheng Kuo <hchkuo@avery-design.com.tw>
Signed-off-by: Chris Browy <cbrowy@avery-design.com>
Co-developed-by: Jonathan Cameron <Jonathan.cameron@huawei.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
[ Changes by WM
 - Bug fixes from testing
]
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
[ Changes by AF:
 - Convert to be more QEMU-ified
 - Move to backends as it isn't PCIe specific
]
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Message-Id: <20240703092027.644758-3-alistair.francis@wdc.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2024-07-22 20:15:42 -04:00

217 lines
5.4 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause */
/*
* QEMU SPDM socket support
*
* This is based on:
* https://github.com/DMTF/spdm-emu/blob/07c0a838bcc1c6207c656ac75885c0603e344b6f/spdm_emu/spdm_emu_common/command.c
* but has been re-written to match QEMU style
*
* Copyright (c) 2021, DMTF. All rights reserved.
* Copyright (c) 2023. Western Digital Corporation or its affiliates.
*/
#include "qemu/osdep.h"
#include "sysemu/spdm-socket.h"
#include "qapi/error.h"
static bool read_bytes(const int socket, uint8_t *buffer,
size_t number_of_bytes)
{
ssize_t number_received = 0;
ssize_t result;
while (number_received < number_of_bytes) {
result = recv(socket, buffer + number_received,
number_of_bytes - number_received, 0);
if (result <= 0) {
return false;
}
number_received += result;
}
return true;
}
static bool read_data32(const int socket, uint32_t *data)
{
bool result;
result = read_bytes(socket, (uint8_t *)data, sizeof(uint32_t));
if (!result) {
return result;
}
*data = ntohl(*data);
return true;
}
static bool read_multiple_bytes(const int socket, uint8_t *buffer,
uint32_t *bytes_received,
uint32_t max_buffer_length)
{
uint32_t length;
bool result;
result = read_data32(socket, &length);
if (!result) {
return result;
}
if (length > max_buffer_length) {
return false;
}
if (bytes_received) {
*bytes_received = length;
}
if (length == 0) {
return true;
}
return read_bytes(socket, buffer, length);
}
static bool receive_platform_data(const int socket,
uint32_t transport_type,
uint32_t *command,
uint8_t *receive_buffer,
uint32_t *bytes_to_receive)
{
bool result;
uint32_t response;
uint32_t bytes_received;
result = read_data32(socket, &response);
if (!result) {
return result;
}
*command = response;
result = read_data32(socket, &transport_type);
if (!result) {
return result;
}
bytes_received = 0;
result = read_multiple_bytes(socket, receive_buffer, &bytes_received,
*bytes_to_receive);
if (!result) {
return result;
}
*bytes_to_receive = bytes_received;
return result;
}
static bool write_bytes(const int socket, const uint8_t *buffer,
uint32_t number_of_bytes)
{
ssize_t number_sent = 0;
ssize_t result;
while (number_sent < number_of_bytes) {
result = send(socket, buffer + number_sent,
number_of_bytes - number_sent, 0);
if (result == -1) {
return false;
}
number_sent += result;
}
return true;
}
static bool write_data32(const int socket, uint32_t data)
{
data = htonl(data);
return write_bytes(socket, (uint8_t *)&data, sizeof(uint32_t));
}
static bool write_multiple_bytes(const int socket, const uint8_t *buffer,
uint32_t bytes_to_send)
{
bool result;
result = write_data32(socket, bytes_to_send);
if (!result) {
return result;
}
return write_bytes(socket, buffer, bytes_to_send);
}
static bool send_platform_data(const int socket,
uint32_t transport_type, uint32_t command,
const uint8_t *send_buffer, size_t bytes_to_send)
{
bool result;
result = write_data32(socket, command);
if (!result) {
return result;
}
result = write_data32(socket, transport_type);
if (!result) {
return result;
}
return write_multiple_bytes(socket, send_buffer, bytes_to_send);
}
int spdm_socket_connect(uint16_t port, Error **errp)
{
int client_socket;
struct sockaddr_in server_addr;
client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_socket < 0) {
error_setg(errp, "cannot create socket: %s", strerror(errno));
return -1;
}
memset((char *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
server_addr.sin_port = htons(port);
if (connect(client_socket, (struct sockaddr *)&server_addr,
sizeof(server_addr)) < 0) {
error_setg(errp, "cannot connect: %s", strerror(errno));
close(client_socket);
return -1;
}
return client_socket;
}
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
void *req, uint32_t req_len,
void *rsp, uint32_t rsp_len)
{
uint32_t command;
bool result;
result = send_platform_data(socket, transport_type,
SPDM_SOCKET_COMMAND_NORMAL,
req, req_len);
if (!result) {
return 0;
}
result = receive_platform_data(socket, transport_type, &command,
(uint8_t *)rsp, &rsp_len);
if (!result) {
return 0;
}
assert(command != 0);
return rsp_len;
}
void spdm_socket_close(const int socket, uint32_t transport_type)
{
send_platform_data(socket, transport_type,
SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
}