blob: 09f3806defe2840f7d5a9bb6cc932636c5051c45 [file]
// Licensed under the Apache-2.0 license
//! SPDM Responder Service
//!
//! This service implements the SPDM responder (server) role, which responds
//! to attestation and measurement requests from SPDM requesters.
//!
//! ## Overview
//!
//! The SPDM responder receives requests and provides:
//! - Version and capability information
//! - Certificate chains for authentication
//! - Device measurements
//! - Challenge-response attestation
//!
//! ## Architecture
//!
//! ```text
//! ┌─────────────────────────┐
//! │ MCTP Transport │
//! │ (incoming messages) │
//! └───────────┬─────────────┘
//! │ SPDM requests
//! ▼
//! ┌─────────────────────────┐
//! │ SPDM Responder │◄── This crate
//! │ (SpdmContext wrapper) │
//! └───────────┬─────────────┘
//! │
//! ▼
//! ┌─────────────────────────────────────┐
//! │ Platform Implementations │
//! │ - CertStore (certificates) │
//! │ - Hash (SHA-384) │
//! │ - RNG (random numbers) │
//! │ - Evidence (measurements) │
//! │ - Transport (MCTP) │
//! └─────────────────────────────────────┘
//! ```
//!
//! ## Usage
//!
//! ```rust,no_run
//! use spdm_responder::SpdmResponder;
//!
//! // Create platform implementations
//! let cert_store = Ast1060CertStore::new(crypto_handle);
//! let hash = SpdmCryptoHash::new(crypto_handle);
//! let rng = SpdmCryptoRng::new(crypto_handle);
//! let evidence = Ast1060Evidence::new();
//! let transport = MctpSpdmTransport::new(mctp_client);
//!
//! // Create responder
//! let mut responder = SpdmResponder::new(
//! transport,
//! cert_store,
//! hash,
//! rng,
//! evidence,
//! )?;
//!
//! // Process messages in loop
//! loop {
//! responder.process_message()?;
//! }
//! ```
#![no_std]
use spdm_lib::cert_store::SpdmCertStore;
use spdm_lib::codec::MessageBuf;
use spdm_lib::context::SpdmContext;
use spdm_lib::error::SpdmError;
use spdm_lib::platform::evidence::SpdmEvidence;
use spdm_lib::platform::hash::SpdmHash;
use spdm_lib::platform::rng::SpdmRng;
use spdm_lib::platform::transport::SpdmTransport;
use spdm_lib::protocol::algorithms::{
AeadCipherSuite, AlgorithmPriorityTable, BaseAsymAlgo, BaseHashAlgo, DeviceAlgorithms,
DheNamedGroup, KeySchedule, LocalDeviceAlgorithms, MeasurementHashAlgo,
MeasurementSpecification, MelSpecification, OtherParamSupport, ReqBaseAsymAlg,
};
use spdm_lib::protocol::version::SpdmVersion;
/// Supported SPDM versions (static to avoid lifetime issues)
static SUPPORTED_VERSIONS: [SpdmVersion; 2] = [SpdmVersion::V12, SpdmVersion::V11];
use spdm_lib::protocol::{CapabilityFlags, DeviceCapabilities};
/// Maximum SPDM message size
const MAX_SPDM_MSG_SIZE: usize = 4096;
/// SPDM responder result type
pub type ResponderResult<T> = Result<T, ResponderError>;
/// SPDM responder errors
#[derive(Debug)]
pub enum ResponderError {
/// SPDM protocol error
SpdmError(SpdmError),
/// Message buffer error
BufferError,
}
impl From<SpdmError> for ResponderError {
fn from(e: SpdmError) -> Self {
ResponderError::SpdmError(e)
}
}
/// SPDM responder configuration
#[derive(Debug, Clone, Copy)]
pub struct ResponderConfig {
/// CT exponent for timing
pub ct_exponent: u8,
/// Data transfer size
pub data_transfer_size: u32,
/// Maximum SPDM message size
pub max_spdm_msg_size: u32,
}
impl Default for ResponderConfig {
fn default() -> Self {
Self {
ct_exponent: 0,
data_transfer_size: 1024,
max_spdm_msg_size: MAX_SPDM_MSG_SIZE as u32,
}
}
}
/// SPDM responder state and configuration.
///
/// This wraps the spdm-lib `SpdmContext` and provides a simplified interface
/// for processing SPDM messages.
pub struct SpdmResponder<'a> {
context: SpdmContext<'a>,
}
impl<'a> SpdmResponder<'a> {
/// Create a new SPDM responder with platform implementations.
///
/// # Arguments
///
/// * `transport` - Transport layer implementation (e.g., MCTP)
/// * `cert_store` - Certificate store with device certificates
/// * `hash` - Hash implementation for protocol operations
/// * `m1_hash` - Hash implementation for M1 transcript
/// * `l1_hash` - Hash implementation for L1 transcript
/// * `rng` - Random number generator
/// * `evidence` - Evidence provider for measurements
/// * `config` - Optional configuration (uses defaults if None)
///
/// # Returns
///
/// A new `SpdmResponder` instance ready to process messages.
pub fn new(
transport: &'a mut dyn SpdmTransport,
cert_store: &'a mut dyn SpdmCertStore,
hash: &'a mut dyn SpdmHash,
m1_hash: &'a mut dyn SpdmHash,
l1_hash: &'a mut dyn SpdmHash,
rng: &'a mut dyn SpdmRng,
evidence: &'a dyn SpdmEvidence,
config: Option<ResponderConfig>,
) -> ResponderResult<Self> {
let config = config.unwrap_or_default();
// Create device capabilities
let capabilities = create_device_capabilities(config);
// Create local algorithms
let algorithms = create_local_algorithms();
// Create SPDM context
let context = SpdmContext::new(
&SUPPORTED_VERSIONS,
transport,
capabilities,
algorithms,
cert_store,
None, // No peer cert store needed for responder
hash,
m1_hash,
l1_hash,
rng,
evidence,
)?;
Ok(Self { context })
}
/// Process a single SPDM message.
///
/// This method:
/// 1. Receives a request via the transport layer
/// 2. Processes it through the SPDM context
/// 3. Sends the response back via the transport layer
///
/// # Arguments
///
/// * `buffer` - Message buffer (must be at least MAX_SPDM_MSG_SIZE bytes)
///
/// # Returns
///
/// - `Ok(())` if message processed successfully
/// - `Err(ResponderError)` on error
///
/// # Note
///
/// This should be called in a loop to continuously process messages.
/// Transport errors indicate connection closed.
pub fn process_message(&mut self, buffer: &'a mut [u8]) -> ResponderResult<()> {
let mut message_buf = MessageBuf::new(buffer);
self.context.responder_process_message(&mut message_buf)?;
Ok(())
}
/// Get reference to the underlying SPDM context.
///
/// This allows direct access to context state if needed.
pub fn context(&self) -> &SpdmContext<'a> {
&self.context
}
/// Get mutable reference to the underlying SPDM context.
///
/// This allows direct manipulation of context state if needed.
pub fn context_mut(&mut self) -> &mut SpdmContext<'a> {
&mut self.context
}
}
/// Create SPDM device capabilities based on configuration.
fn create_device_capabilities(config: ResponderConfig) -> DeviceCapabilities {
let mut flags_value = 0u32;
// Certificate capability
flags_value |= 1 << 1; // CERT_CAP
// Challenge capability
flags_value |= 1 << 2; // CHAL_CAP
// Measurements capability (with signature)
flags_value |= 2 << 3; // MEAS_CAP (0b10 = measurements with signature)
// Measurements freshness capability
flags_value |= 1 << 5; // MEAS_FRESH_CAP
// Chunk capability
flags_value |= 1 << 17; // CHUNK_CAP
let flags = CapabilityFlags::new(flags_value);
DeviceCapabilities {
ct_exponent: config.ct_exponent,
flags,
data_transfer_size: config.data_transfer_size,
max_spdm_msg_size: config.max_spdm_msg_size,
include_supported_algorithms: true,
}
}
/// Create local device algorithms configuration.
///
/// Configures supported cryptographic algorithms:
/// - Measurement: DMTF specification with SHA-384
/// - Asymmetric: ECDSA with NIST P-384
/// - Hash: SHA-384
fn create_local_algorithms<'a>() -> LocalDeviceAlgorithms<'a> {
// Measurement specification (DMTF)
let mut measurement_spec = MeasurementSpecification::default();
measurement_spec.set_dmtf_measurement_spec(1);
// Measurement hash algorithm (SHA-384)
let mut measurement_hash_algo = MeasurementHashAlgo::default();
measurement_hash_algo.set_tpm_alg_sha_384(1);
// Base asymmetric algorithm (ECDSA P-384)
let mut base_asym_algo = BaseAsymAlgo::default();
base_asym_algo.set_tpm_alg_ecdsa_ecc_nist_p384(1);
// Base hash algorithm (SHA-384)
let mut base_hash_algo = BaseHashAlgo::default();
base_hash_algo.set_tpm_alg_sha_384(1);
let device_algorithms = DeviceAlgorithms {
measurement_spec,
other_param_support: OtherParamSupport::default(),
measurement_hash_algo,
base_asym_algo,
base_hash_algo,
mel_specification: MelSpecification::default(),
dhe_group: DheNamedGroup::default(),
aead_cipher_suite: AeadCipherSuite::default(),
req_base_asym_algo: ReqBaseAsymAlg::default(),
key_schedule: KeySchedule::default(),
};
let algorithm_priority_table = AlgorithmPriorityTable {
measurement_specification: None,
opaque_data_format: None,
base_asym_algo: None,
base_hash_algo: None,
mel_specification: None,
dhe_group: None,
aead_cipher_suite: None,
req_base_asym_algo: None,
key_schedule: None,
};
LocalDeviceAlgorithms {
device_algorithms,
algorithm_priority_table,
}
}