blob: 146ab14c20d6299d48d63892cddd6414671b5c8f [file]
// Licensed under the Apache-2.0 license
//! SPDM Requester Application
//!
//! Runs the SPDM requester flow using spdm-lib's requester API. Communicates
//! with the SPDM responder via the MCTP loopback server using IPC channels.
//!
//! The requester:
//! 1. Connects to the MCTP loopback server via an IPC channel
//! 2. Creates an `MctpSpdmTransport` in requester mode (targeting responder EID)
//! 3. Creates an `SpdmContext` with mock platform implementations
//! 4. Executes the SPDM VCA flow:
//! - GET_VERSION → VERSION
//! - GET_CAPABILITIES → CAPABILITIES
//! - NEGOTIATE_ALGORITHMS → ALGORITHMS
//! 5. Reports success/failure and shuts down
#![no_std]
#![no_main]
use openprot_mctp_client::IpcMctpClient;
use openprot_spdm_transport_mctp::MctpSpdmTransport;
use pw_log::{error, info};
use spdm_lib::codec::MessageBuf;
use spdm_lib::commands::algorithms::request::generate_negotiate_algorithms_request;
use spdm_lib::commands::capabilities::request::generate_capabilities_request_local;
use spdm_lib::commands::version::VersionReqPayload;
use spdm_lib::commands::version::request::generate_get_version;
use spdm_lib::context::SpdmContext;
use spdm_lib::platform::transport::SpdmTransport;
use spdm_lib::protocol::{
AeadCipherSuite, AlgorithmPriorityTable, BaseAsymAlgo, BaseHashAlgo, CapabilityFlags,
DeviceAlgorithms, DeviceCapabilities, DheNamedGroup, KeySchedule, LocalDeviceAlgorithms,
MeasurementHashAlgo, MeasurementSpecification, MelSpecification, OtherParamSupport,
ReqBaseAsymAlg, SpdmVersion,
};
use userspace::entry;
use userspace::syscall;
use app_spdm_requester::handle;
use mock_platform::{DemoPeerCertStore, MockCertStore, MockEvidence, MockHash, MockRng};
/// Remote EID of the SPDM responder
const RESPONDER_EID: u8 = 42;
/// Create local device algorithms configuration for SPDM requester
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,
}
}
fn spdm_requester_test() -> Result<(), &'static str> {
info!("SPDM requester starting");
info!("========================================");
// Create MCTP client via IPC to loopback server
let mctp_client = IpcMctpClient::new(handle::MCTP);
// Create SPDM transport in requester mode (targeting responder EID)
let mut transport = MctpSpdmTransport::new_requester(mctp_client, RESPONDER_EID);
// Initialize transport (establishes MCTP request handle)
if let Err(_) = transport.init_sequence() {
error!("Failed to initialize requester transport");
return Err("Transport init failed");
}
info!(
"SPDM transport initialized (requester mode, target EID {})",
RESPONDER_EID as u32
);
// Create mock platform implementations
// Note: The requester needs a cert store for its own identity (even if unused
// in the VCA flow) and the context constructor requires it.
let mut cert_store = MockCertStore::new();
let mut hash = MockHash::new();
let mut m1_hash = MockHash::new();
let mut l1_hash = MockHash::new();
let mut rng = MockRng::new();
let evidence = MockEvidence::new();
let mut peer_cert_store = DemoPeerCertStore::default();
// Configure requester capabilities
let mut flags = CapabilityFlags::default();
flags.set_cert_cap(1);
flags.set_chal_cap(1);
flags.set_meas_cap(0); // Requester doesn't need measurement capability
flags.set_chunk_cap(1);
let capabilities = DeviceCapabilities {
ct_exponent: 0,
flags,
data_transfer_size: 1024,
max_spdm_msg_size: 4096,
// Setting true at V1.3 encodes param1 bit 2 of GET_CAPABILITIES, which the
// responder currently rejects as an unexpected reserved field. We handle
// algorithm negotiation via NEGOTIATE_ALGORITHMS instead.
include_supported_algorithms: false,
};
let algorithms = create_local_algorithms();
// Supported SPDM versions
static SUPPORTED_VERSIONS: [SpdmVersion; 2] = [SpdmVersion::V12, SpdmVersion::V13];
// Create SPDM requester context
// Note: peer_cert_store is None for now (VCA flow doesn't need it).
// For GET_DIGESTS/GET_CERTIFICATE flows, a PeerCertStore would be required.
let mut ctx = match SpdmContext::new(
&SUPPORTED_VERSIONS,
&mut transport,
capabilities,
algorithms,
&mut cert_store,
Some(&mut peer_cert_store),
&mut hash,
&mut m1_hash,
&mut l1_hash,
&mut rng,
&evidence,
) {
Ok(ctx) => ctx,
Err(_) => {
error!("Failed to create SPDM requester context");
return Err("SPDM context creation failed");
}
};
info!("SPDM requester context created");
info!("========================================");
// Buffer for SPDM message encoding/decoding
let mut buf = [0u8; 4096];
let mut msg_buf = MessageBuf::new(&mut buf);
// ── Step 1: GET_VERSION ──────────────────────────────────────────────
info!("Step 1: GET_VERSION");
{
generate_get_version(&mut ctx, &mut msg_buf, VersionReqPayload::new(0, 0))
.map_err(|_| "Failed to generate GET_VERSION request")?;
ctx.requester_send_request(&mut msg_buf, RESPONDER_EID)
.map_err(|_| "Failed to send GET_VERSION request")?;
}
{
msg_buf.reset();
ctx.requester_process_message(&mut msg_buf)
.map_err(|_| "Failed to process VERSION response")?;
}
info!(" GET_VERSION completed successfully");
// ── Step 2: GET_CAPABILITIES ─────────────────────────────────────────
info!("Step 2: GET_CAPABILITIES");
{
msg_buf.reset();
generate_capabilities_request_local(&mut ctx, &mut msg_buf)
.map_err(|_| "Failed to generate GET_CAPABILITIES request")?;
ctx.requester_send_request(&mut msg_buf, RESPONDER_EID)
.map_err(|_| "Failed to send GET_CAPABILITIES request")?;
}
{
msg_buf.reset();
ctx.requester_process_message(&mut msg_buf)
.map_err(|_| "Failed to process CAPABILITIES response")?;
}
info!(" GET_CAPABILITIES completed successfully");
// ── Step 3: NEGOTIATE_ALGORITHMS ─────────────────────────────────────
info!("Step 3: NEGOTIATE_ALGORITHMS");
{
msg_buf.reset();
generate_negotiate_algorithms_request(&mut ctx, &mut msg_buf, None, None, None, None)
.map_err(|_| "Failed to generate NEGOTIATE_ALGORITHMS request")?;
ctx.requester_send_request(&mut msg_buf, RESPONDER_EID)
.map_err(|_| "Failed to send NEGOTIATE_ALGORITHMS request")?;
}
{
msg_buf.reset();
ctx.requester_process_message(&mut msg_buf)
.map_err(|_| "Failed to process ALGORITHMS response")?;
}
info!(" NEGOTIATE_ALGORITHMS completed successfully");
info!("========================================");
info!("VCA (Version, Capabilities, Algorithms) flow completed!");
Ok(())
}
#[entry]
fn entry() -> ! {
info!("SPDM Requester Application");
match spdm_requester_test() {
Ok(_) => {
info!("SUCCESS: All SPDM requester tests passed");
let _ = syscall::debug_shutdown(Ok(()));
}
Err(e) => {
error!("FAILURE: {}", e as &str);
let _ = syscall::debug_shutdown(Err(pw_status::Error::Unknown));
}
}
loop {}
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
error!("PANIC in SPDM requester");
loop {}
}