spdm: remove unnecessary files
diff --git a/services/spdm/requester/BUILD.bazel b/services/spdm/requester/BUILD.bazel index 1f4b65d..f52aa4d 100644 --- a/services/spdm/requester/BUILD.bazel +++ b/services/spdm/requester/BUILD.bazel
@@ -21,27 +21,3 @@ name = "spdm_requester_test", crate = ":spdm_requester_lib", ) - -# Host integration test: SPDM VCA flow with in-memory MCTP transport. -# Uses the same pattern as //services/mctp/server:mctp_server_echo_test. -# Demonstrates usage of SpdmRequester and SpdmResponder APIs. -rust_test( - name = "spdm_requester_vca_host_test", - srcs = [ - "tests/common/mod.rs", - "tests/vca_host.rs", - ], - crate_root = "tests/vca_host.rs", - edition = "2024", - deps = [ - ":spdm_requester_lib", - "//services/mctp/api:mctp_api", - "//services/mctp/server:mctp_server_lib", - "//services/spdm/responder:spdm_responder_lib", - "//services/spdm/transport-mctp:spdm_transport_mctp", - "@rust_crates//:mctp", - "@rust_crates//:mctp-lib", - "@rust_crates//:spdm-lib", - "@rust_crates//:zerocopy", - ], -)
diff --git a/services/spdm/requester/tests/common/mod.rs b/services/spdm/requester/tests/common/mod.rs deleted file mode 100644 index 4d83579..0000000 --- a/services/spdm/requester/tests/common/mod.rs +++ /dev/null
@@ -1,684 +0,0 @@ -// Licensed under the Apache-2.0 license -// SPDX-License-Identifier: Apache-2.0 - -//! Shared test fixtures for SPDM host integration tests. -//! -//! Provides: -//! - [`BufferSender`] — captures outbound packets into a `Vec` -//! - [`DirectClient`] — implements `MctpClient` directly against a `Server` -//! - [`MockCertStore`] — mock certificate store for SPDM -//! - [`MockHash`] — mock hash implementation -//! - [`MockRng`] — mock RNG implementation -//! - [`MockEvidence`] — mock evidence/measurements provider -//! - [`DemoPeerCertStore`] — peer certificate store for requester - -#![allow(dead_code)] - -use std::cell::RefCell; - -use mctp::{Eid, Tag}; -use mctp_lib::fragment::{Fragmenter, SendOutput}; -use mctp_lib::Sender; -use openprot_mctp_api::{Handle, MctpClient, MctpError, RecvMetadata, ResponseCode}; -use openprot_mctp_server::Server; - -use spdm_lib::cert_store::{CertStoreError, CertStoreResult, PeerCertStore, SpdmCertStore}; -use spdm_lib::commands::challenge::MeasurementSummaryHashType; -use spdm_lib::platform::evidence::{SpdmEvidence, SpdmEvidenceError, SpdmEvidenceResult}; -use spdm_lib::platform::hash::{SpdmHash, SpdmHashAlgoType, SpdmHashError, SpdmHashResult}; -use spdm_lib::platform::rng::{SpdmRng, SpdmRngResult}; -use spdm_lib::protocol::algorithms::{AsymAlgo, ECC_P384_SIGNATURE_SIZE, SHA384_HASH_SIZE}; -use spdm_lib::protocol::certs::{CertificateInfo, KeyUsageMask}; -use spdm_lib::protocol::{BaseHashAlgoType, SpdmCertChainHeader}; -use zerocopy::FromBytes; - -// --------------------------------------------------------------------------- -// BufferSender — captures outbound MCTP packets -// --------------------------------------------------------------------------- - -/// A mock [`Sender`] that captures every outbound MCTP packet into a shared buffer. -pub struct BufferSender<'a> { - pub packets: &'a RefCell<Vec<Vec<u8>>>, -} - -/// MTU for MCTP payload (without header) -const MCTP_MTU: usize = 255; -/// MCTP header size (4 bytes) -const MCTP_HEADER_SIZE: usize = 4; - -impl Sender for BufferSender<'_> { - fn send_vectored( - &mut self, - mut fragmenter: Fragmenter, - payload: &[&[u8]], - ) -> mctp::Result<Tag> { - loop { - // Buffer must be MTU + header size - let mut buf = [0u8; MCTP_MTU + MCTP_HEADER_SIZE]; - match fragmenter.fragment_vectored(payload, &mut buf) { - SendOutput::Packet(p) => { - self.packets.borrow_mut().push(p.to_vec()); - } - SendOutput::Complete { tag, .. } => return Ok(tag), - SendOutput::Error { err, .. } => return Err(err), - } - } - } - - fn get_mtu(&self) -> usize { - MCTP_MTU - } -} - -// --------------------------------------------------------------------------- -// transfer — moves packets between servers -// --------------------------------------------------------------------------- - -/// Drain `packets` into `dest` as inbound MCTP packets. -pub fn transfer<S: Sender, const N: usize>( - packets: &RefCell<Vec<Vec<u8>>>, - dest: &mut Server<S, N>, -) { - let pkts = packets.borrow(); - for pkt in pkts.iter() { - dest.inbound(pkt).unwrap(); - } -} - -// --------------------------------------------------------------------------- -// DirectClient — implements MctpClient against a Server -// --------------------------------------------------------------------------- - -/// Implements [`MctpClient`] by calling [`Server`] methods directly. -pub struct DirectClient<'a, S: Sender, const N: usize> { - pub server: &'a RefCell<Server<S, N>>, -} - -impl<'a, S: Sender, const N: usize> DirectClient<'a, S, N> { - pub fn new(server: &'a RefCell<Server<S, N>>) -> Self { - Self { server } - } -} - -impl<S: Sender, const N: usize> MctpClient for DirectClient<'_, S, N> { - fn req(&self, eid: u8) -> Result<Handle, MctpError> { - self.server.borrow_mut().req(eid) - } - - fn listener(&self, msg_type: u8) -> Result<Handle, MctpError> { - self.server.borrow_mut().listener(msg_type) - } - - fn get_eid(&self) -> u8 { - self.server.borrow().get_eid() - } - - fn set_eid(&self, eid: u8) -> Result<(), MctpError> { - self.server.borrow_mut().set_eid(eid) - } - - fn recv( - &self, - handle: Handle, - _timeout_millis: u32, - buf: &mut [u8], - ) -> Result<RecvMetadata, MctpError> { - self.server - .borrow_mut() - .try_recv(handle, buf) - .ok_or(MctpError::from_code(ResponseCode::TimedOut)) - } - - fn send( - &self, - handle: Option<Handle>, - msg_type: u8, - eid: Option<u8>, - tag: Option<u8>, - integrity_check: bool, - buf: &[u8], - ) -> Result<u8, MctpError> { - self.server - .borrow_mut() - .send(handle, msg_type, eid, tag, integrity_check, buf) - } - - fn drop_handle(&self, handle: Handle) { - let _ = self.server.borrow_mut().unbind(handle); - } -} - -// --------------------------------------------------------------------------- -// make_server helper -// --------------------------------------------------------------------------- - -/// Construct a `Server` + its outbound packet buffer. -pub fn make_server(eid: u8, packets: &RefCell<Vec<Vec<u8>>>) -> Server<BufferSender<'_>, 16> { - Server::new(Eid(eid), 0, BufferSender { packets }) -} - -// --------------------------------------------------------------------------- -// MockCertStore — mock certificate store -// --------------------------------------------------------------------------- - -/// Mock certificate store with fixed placeholder data. -pub struct MockCertStore; - -impl MockCertStore { - pub fn new() -> Self { - Self - } -} - -impl SpdmCertStore for MockCertStore { - fn slot_count(&self) -> u8 { - 1 - } - - fn is_provisioned(&self, slot_id: u8) -> bool { - slot_id == 0 - } - - fn cert_chain_len(&mut self, asym_algo: AsymAlgo, slot_id: u8) -> CertStoreResult<usize> { - if slot_id != 0 { - return Err(CertStoreError::InvalidSlotId(slot_id)); - } - if asym_algo != AsymAlgo::EccP384 { - return Err(CertStoreError::UnsupportedHashAlgo); - } - Ok(32) - } - - fn get_cert_chain( - &mut self, - slot_id: u8, - asym_algo: AsymAlgo, - offset: usize, - cert_portion: &mut [u8], - ) -> CertStoreResult<usize> { - if slot_id != 0 { - return Err(CertStoreError::InvalidSlotId(slot_id)); - } - if asym_algo != AsymAlgo::EccP384 { - return Err(CertStoreError::UnsupportedHashAlgo); - } - - const CERT_CHAIN: [u8; 32] = [0xAA; 32]; - - if offset >= CERT_CHAIN.len() { - return Err(CertStoreError::InvalidOffset); - } - - let remaining = CERT_CHAIN.len() - offset; - let to_copy = remaining.min(cert_portion.len()); - cert_portion[..to_copy].copy_from_slice(&CERT_CHAIN[offset..offset + to_copy]); - - if to_copy < cert_portion.len() { - cert_portion[to_copy..].fill(0); - } - - Ok(to_copy) - } - - fn root_cert_hash( - &mut self, - slot_id: u8, - asym_algo: AsymAlgo, - cert_hash: &mut [u8; SHA384_HASH_SIZE], - ) -> CertStoreResult<()> { - if slot_id != 0 { - return Err(CertStoreError::InvalidSlotId(slot_id)); - } - if asym_algo != AsymAlgo::EccP384 { - return Err(CertStoreError::UnsupportedHashAlgo); - } - - const ROOT_HASH: [u8; SHA384_HASH_SIZE] = [0xBB; SHA384_HASH_SIZE]; - cert_hash.copy_from_slice(&ROOT_HASH); - Ok(()) - } - - fn sign_hash( - &self, - slot_id: u8, - _hash: &[u8; SHA384_HASH_SIZE], - signature: &mut [u8; ECC_P384_SIGNATURE_SIZE], - ) -> CertStoreResult<()> { - if slot_id != 0 { - return Err(CertStoreError::InvalidSlotId(slot_id)); - } - - const SIGNATURE: [u8; ECC_P384_SIGNATURE_SIZE] = [0xCC; ECC_P384_SIGNATURE_SIZE]; - signature.copy_from_slice(&SIGNATURE); - Ok(()) - } - - fn key_pair_id(&self, _slot_id: u8) -> Option<u8> { - None - } - - fn cert_info(&self, _slot_id: u8) -> Option<CertificateInfo> { - None - } - - fn key_usage_mask(&self, _slot_id: u8) -> Option<KeyUsageMask> { - None - } -} - -// --------------------------------------------------------------------------- -// MockHash — mock hash implementation -// --------------------------------------------------------------------------- - -/// Mock hash implementation that XORs all input bytes. -pub struct MockHash { - buffer: Vec<u8>, - algo: Option<SpdmHashAlgoType>, -} - -impl MockHash { - pub fn new() -> Self { - Self { - buffer: Vec::new(), - algo: None, - } - } -} - -impl SpdmHash for MockHash { - fn init(&mut self, algo: SpdmHashAlgoType, _secret: Option<&[u8]>) -> SpdmHashResult<()> { - self.buffer.clear(); - self.algo = Some(algo); - Ok(()) - } - - fn update(&mut self, data: &[u8]) -> SpdmHashResult<()> { - self.buffer.extend_from_slice(data); - Ok(()) - } - - fn finalize(&mut self, dest: &mut [u8]) -> SpdmHashResult<()> { - let hash_size = match self.algo { - Some(SpdmHashAlgoType::SHA384) => 48, - Some(SpdmHashAlgoType::SHA512) => 64, - _ => return Err(SpdmHashError::InvalidAlgorithm), - }; - - if dest.len() < hash_size { - return Err(SpdmHashError::BufferTooSmall); - } - - let mut hash_byte = 0u8; - for byte in &self.buffer { - hash_byte ^= byte; - } - dest[..hash_size].fill(hash_byte); - - Ok(()) - } - - fn hash(&mut self, algo: SpdmHashAlgoType, data: &[u8], dest: &mut [u8]) -> SpdmHashResult<()> { - self.init(algo, None)?; - self.update(data)?; - self.finalize(dest) - } - - fn reset(&mut self) { - self.buffer.clear(); - self.algo = None; - } - - fn algo(&self) -> SpdmHashAlgoType { - self.algo.unwrap_or(SpdmHashAlgoType::SHA384) - } -} - -// --------------------------------------------------------------------------- -// MockRng — mock RNG implementation -// --------------------------------------------------------------------------- - -/// Mock RNG that produces deterministic incrementing patterns. -pub struct MockRng; - -impl MockRng { - pub fn new() -> Self { - Self - } -} - -impl SpdmRng for MockRng { - fn get_random_bytes(&mut self, buf: &mut [u8]) -> SpdmRngResult<()> { - for (i, byte) in buf.iter_mut().enumerate() { - *byte = (i & 0xFF) as u8; - } - Ok(()) - } - - fn generate_random_number(&mut self, random_number: &mut [u8]) -> SpdmRngResult<()> { - for (i, byte) in random_number.iter_mut().enumerate() { - *byte = ((i + 0x42) & 0xFF) as u8; - } - Ok(()) - } -} - -// --------------------------------------------------------------------------- -// MockEvidence — mock evidence/measurements provider -// --------------------------------------------------------------------------- - -/// Mock evidence implementation with fixed measurements. -pub struct MockEvidence; - -impl MockEvidence { - pub fn new() -> Self { - Self - } -} - -impl SpdmEvidence for MockEvidence { - fn pcr_quote_size(&self, _with_pqc_sig: bool) -> SpdmEvidenceResult<usize> { - // Format: count(1) + [index(1) + size(2) + data]*2 - Ok(1 + (1 + 2 + 23) + (1 + 2 + 20)) - } - - fn pcr_quote(&self, dest: &mut [u8], _with_pqc_sig: bool) -> SpdmEvidenceResult<usize> { - let required_size = self.pcr_quote_size(false)?; - if dest.len() < required_size { - return Err(SpdmEvidenceError::InvalidEvidenceFormat); - } - - let mut offset = 0; - - // Measurement count - dest[offset] = 2; - offset += 1; - - // Measurement 0 - dest[offset] = 0; - offset += 1; - dest[offset..offset + 2].copy_from_slice(&23u16.to_le_bytes()); - offset += 2; - dest[offset..offset + 23].copy_from_slice(b"OpenPRoT SPDM Loopback"); - offset += 23; - - // Measurement 1 - dest[offset] = 1; - offset += 1; - dest[offset..offset + 2].copy_from_slice(&20u16.to_le_bytes()); - offset += 2; - dest[offset..offset + 20].copy_from_slice(b"MCTP Loopback Test "); - offset += 20; - - Ok(offset) - } -} - -// --------------------------------------------------------------------------- -// PeerSlot — peer certificate slot data -// --------------------------------------------------------------------------- - -#[derive(Debug, Default)] -pub struct PeerSlot { - pub cert_chain: Vec<u8>, - pub digest: Vec<u8>, - pub keypair_id: Option<u8>, - pub certificate_info: Option<CertificateInfo>, - pub key_usage_mask: Option<KeyUsageMask>, - pub requested_msh_type: Option<MeasurementSummaryHashType>, -} - -impl PeerSlot { - fn get_root_hash(&self, hash_algo: BaseHashAlgoType) -> Option<&[u8]> { - let (length, rest) = SpdmCertChainHeader::ref_from_prefix(&self.cert_chain).ok()?; - if length.get_length() != self.cert_chain.len() as u32 { - return None; - } - Some(&rest[..hash_algo.hash_byte_size()]) - } - - fn get_cert_chain(&self, hash_algo: BaseHashAlgoType) -> Option<&[u8]> { - let (length, rest) = SpdmCertChainHeader::ref_from_prefix(&self.cert_chain).ok()?; - if length.get_length() != self.cert_chain.len() as u32 { - return None; - } - Some(&rest[hash_algo.hash_byte_size()..]) - } -} - -// --------------------------------------------------------------------------- -// DemoPeerCertStore — peer certificate store for requester -// --------------------------------------------------------------------------- - -/// Peer certificate store for SPDM requester (stores responder's certificates). -#[derive(Debug, Default)] -pub struct DemoPeerCertStore { - supported_slots_mask: u8, - provisioned_slots_mask: u8, - pub peer_slots: Vec<Option<PeerSlot>>, -} - -impl DemoPeerCertStore { - pub fn new() -> Self { - let slots = vec![None]; - Self { - supported_slots_mask: 0, - provisioned_slots_mask: 0, - peer_slots: slots, - } - } -} - -impl PeerCertStore for DemoPeerCertStore { - fn slot_count(&self) -> u8 { - self.peer_slots.len() as u8 - } - - fn assemble( - &mut self, - slot_id: u8, - portion: &[u8], - ) -> Result<spdm_lib::cert_store::ReassemblyStatus, CertStoreError> { - let slot = self - .peer_slots - .get_mut(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_mut() - .ok_or(CertStoreError::PlatformError)?; - - slot.cert_chain.extend_from_slice(portion); - Ok(spdm_lib::cert_store::ReassemblyStatus::InProgress) - } - - fn reset(&mut self, slot_id: u8) { - if let Some(Some(slot)) = self.peer_slots.get_mut(slot_id as usize) { - *slot = PeerSlot::default(); - } - } - - fn get_raw_chain(&self, slot_id: u8) -> CertStoreResult<&[u8]> { - let slot = self - .peer_slots - .get(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_ref() - .ok_or(CertStoreError::PlatformError)?; - Ok(&slot.cert_chain) - } - - fn get_cert_chain(&self, slot_id: u8, hash_algo: BaseHashAlgoType) -> CertStoreResult<&[u8]> { - let slot = self - .peer_slots - .get(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_ref() - .ok_or(CertStoreError::PlatformError)?; - slot.get_cert_chain(hash_algo) - .ok_or(CertStoreError::CertReadError) - } - - fn set_supported_slots(&mut self, slot_mask: u8) -> CertStoreResult<()> { - for b in 0..8 { - if slot_mask & (1 << b) != 0 - && let Some(slot) = self.peer_slots.get_mut(b as usize) - && slot.is_none() - { - *slot = Some(PeerSlot::default()); - } - } - self.supported_slots_mask = slot_mask; - Ok(()) - } - - fn get_supported_slots(&self) -> CertStoreResult<u8> { - Ok(self.supported_slots_mask) - } - - fn set_provisioned_slots(&mut self, provisioned_slot_mask: u8) -> CertStoreResult<()> { - self.provisioned_slots_mask = provisioned_slot_mask; - Ok(()) - } - - fn get_provisioned_slots(&self) -> CertStoreResult<u8> { - Ok(self.provisioned_slots_mask) - } - - fn set_cert_chain(&mut self, slot_id: u8, cert_chain: &[u8]) -> CertStoreResult<()> { - let slot = self - .peer_slots - .get_mut(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_mut() - .ok_or(CertStoreError::PlatformError)?; - - slot.cert_chain = cert_chain.to_vec(); - Ok(()) - } - - fn get_digest(&self, slot_id: u8) -> CertStoreResult<&[u8]> { - let slot = self - .peer_slots - .get(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_ref() - .ok_or(CertStoreError::PlatformError)?; - Ok(&slot.digest) - } - - fn set_digest(&mut self, slot_id: u8, digest: &[u8]) -> CertStoreResult<()> { - let slot = self - .peer_slots - .get_mut(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_mut() - .ok_or(CertStoreError::PlatformError)?; - slot.digest = digest.to_vec(); - Ok(()) - } - - fn get_cert_info(&self, slot_id: u8) -> CertStoreResult<CertificateInfo> { - let slot = self - .peer_slots - .get(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_ref() - .ok_or(CertStoreError::PlatformError)?; - slot.certificate_info - .ok_or(CertStoreError::InvalidSlotId(slot_id)) - } - - fn set_cert_info(&mut self, slot_id: u8, cert_info: CertificateInfo) -> CertStoreResult<()> { - let slot = self - .peer_slots - .get_mut(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_mut() - .ok_or(CertStoreError::PlatformError)?; - slot.certificate_info = Some(cert_info); - Ok(()) - } - - fn get_key_usage_mask(&self, slot_id: u8) -> CertStoreResult<KeyUsageMask> { - let slot = self - .peer_slots - .get(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_ref() - .ok_or(CertStoreError::PlatformError)?; - slot.key_usage_mask - .ok_or(CertStoreError::InvalidSlotId(slot_id)) - } - - fn set_key_usage_mask( - &mut self, - slot_id: u8, - key_usage_mask: KeyUsageMask, - ) -> CertStoreResult<()> { - let slot = self - .peer_slots - .get_mut(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_mut() - .ok_or(CertStoreError::PlatformError)?; - slot.key_usage_mask = Some(key_usage_mask); - Ok(()) - } - - fn get_keypair(&self, slot_id: u8) -> CertStoreResult<u8> { - let slot = self - .peer_slots - .get(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_ref() - .ok_or(CertStoreError::PlatformError)?; - slot.keypair_id - .ok_or(CertStoreError::InvalidSlotId(slot_id)) - } - - fn set_keypair(&mut self, slot_id: u8, keypair: u8) -> CertStoreResult<()> { - let slot = self - .peer_slots - .get_mut(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_mut() - .ok_or(CertStoreError::PlatformError)?; - slot.keypair_id = Some(keypair); - Ok(()) - } - - fn get_root_hash(&self, slot_id: u8, hash_algo: BaseHashAlgoType) -> CertStoreResult<&[u8]> { - let slot = self - .peer_slots - .get(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_ref() - .ok_or(CertStoreError::PlatformError)?; - slot.get_root_hash(hash_algo) - .ok_or(CertStoreError::CertReadError) - } - - fn get_requested_msh_type(&self, slot_id: u8) -> CertStoreResult<MeasurementSummaryHashType> { - self.peer_slots - .get(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_ref() - .ok_or(CertStoreError::PlatformError)? - .requested_msh_type - .clone() - .ok_or(CertStoreError::Undefined) - } - - fn set_requested_msh_type( - &mut self, - slot_id: u8, - msh_type: MeasurementSummaryHashType, - ) -> CertStoreResult<()> { - let slot = self - .peer_slots - .get_mut(slot_id as usize) - .ok_or(CertStoreError::InvalidSlotId(slot_id))? - .as_mut() - .ok_or(CertStoreError::PlatformError)?; - slot.requested_msh_type = Some(msh_type); - Ok(()) - } -}
diff --git a/services/spdm/requester/tests/vca_host.rs b/services/spdm/requester/tests/vca_host.rs deleted file mode 100644 index fc976d2..0000000 --- a/services/spdm/requester/tests/vca_host.rs +++ /dev/null
@@ -1,323 +0,0 @@ -// Licensed under the Apache-2.0 license -// SPDX-License-Identifier: Apache-2.0 - -//! SPDM VCA (Version, Capabilities, Algorithms) host integration test. -//! -//! Exercises the full SPDM requester ↔ responder flow using the same -//! in-memory transport pattern as the MCTP echo tests. No IPC, no QEMU — -//! runs entirely on the host. -//! -//! This test demonstrates how to use the `SpdmRequester` and `SpdmResponder` -//! APIs with custom capabilities passed via configuration. -//! -//! ## Architecture -//! -//! ```text -//! ┌─ Requester (EID 8) ────────┐ ┌─ Responder (EID 42) ───────┐ -//! │ Stack<DirectClient> │ │ Stack<DirectClient> │ -//! │ └→ MctpSpdmTransport │ │ └→ MctpSpdmTransport │ -//! │ └→ SpdmRequester │ │ └→ SpdmResponder │ -//! └────────────────────────────┘ └────────────────────────────┘ -//! │ │ -//! └──── BufferSender ←─ transfer() ───┘ -//! ``` - -mod common; - -use std::cell::RefCell; - -use mctp::Eid; -use openprot_mctp_api::stack::Stack; -#[allow(unused_imports)] -use openprot_mctp_api::MctpClient; -use openprot_mctp_server::Server; -use openprot_spdm_requester::{RequesterConfig, SpdmRequester}; -use openprot_spdm_responder::{ResponderConfig, SpdmResponder}; -use openprot_spdm_transport_mctp::MctpSpdmTransport; -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::request::generate_get_version; -use spdm_lib::commands::version::VersionReqPayload; -use spdm_lib::platform::transport::SpdmTransport; - -use common::{ - transfer, BufferSender, DemoPeerCertStore, DirectClient, MockCertStore, MockEvidence, MockHash, - MockRng, -}; - -/// Requester EID -const REQUESTER_EID: u8 = 8; - -/// Responder EID -const RESPONDER_EID: u8 = 42; - -/// Sanity check: verify the MCTP layer works before testing SPDM. -#[test] -fn mctp_sanity_check() { - use openprot_mctp_api::{MctpListener, MctpReqChannel}; - - let buf_a = RefCell::new(Vec::new()); - let buf_b = RefCell::new(Vec::new()); - - let server_a: RefCell<Server<_, 16>> = - RefCell::new(Server::new(Eid(8), 0, BufferSender { packets: &buf_a })); - let server_b: RefCell<Server<_, 16>> = - RefCell::new(Server::new(Eid(42), 0, BufferSender { packets: &buf_b })); - - let stack_a = Stack::new(DirectClient::new(&server_a)); - let stack_b = Stack::new(DirectClient::new(&server_b)); - - // A registers listener FIRST (so router retains matching messages) - let mut listener = stack_a.listener(0x05, 0).expect("listener"); - - // B sends a request to A - let mut req = stack_b.req(8, 0).expect("req channel alloc"); - req.send(0x05, b"hello SPDM").expect("req send"); - - // Transfer B -> A - transfer(&buf_b, &mut server_a.borrow_mut()); - - // A receives - let mut recv_buf = [0u8; 256]; - let (meta, payload, _resp) = listener.recv(&mut recv_buf).expect("listener recv"); - - assert_eq!(payload, b"hello SPDM"); - assert_eq!(meta.msg_type, 0x05); - eprintln!("MCTP sanity check passed!"); -} - -/// SPDM VCA (Version, Capabilities, Algorithms) roundtrip test. -/// -/// This test exercises the full VCA flow between a requester and responder, -/// using in-memory MCTP transport with no platform dependencies. -/// -/// Demonstrates usage of `SpdmRequester` and `SpdmResponder` APIs with -/// custom capabilities passed via `RequesterConfig` and `ResponderConfig`. -#[test] -fn spdm_vca_roundtrip() { - // -- Set up MCTP servers with in-memory transport -- - let buf_req = RefCell::new(Vec::new()); - let buf_resp = RefCell::new(Vec::new()); - - let server_req: RefCell<Server<_, 16>> = RefCell::new(Server::new( - Eid(REQUESTER_EID), - 0, - BufferSender { packets: &buf_req }, - )); - let server_resp: RefCell<Server<_, 16>> = RefCell::new(Server::new( - Eid(RESPONDER_EID), - 0, - BufferSender { packets: &buf_resp }, - )); - - // -- Create Stack facades (same API as production) -- - let stack_req = Stack::new(DirectClient::new(&server_req)); - let stack_resp = Stack::new(DirectClient::new(&server_resp)); - - // Set EIDs on stacks - stack_req.set_eid(REQUESTER_EID).expect("set requester EID"); - stack_resp - .set_eid(RESPONDER_EID) - .expect("set responder EID"); - - // -- Create SPDM transports -- - let mut transport_req = MctpSpdmTransport::new_requester(&stack_req, RESPONDER_EID); - let mut transport_resp = MctpSpdmTransport::new_responder(&stack_resp); - - // Initialize transports - transport_req - .init_sequence() - .expect("requester transport init"); - transport_resp - .init_sequence() - .expect("responder transport init"); - - // -- Create mock platform implementations -- - // Requester side - let mut req_cert_store = MockCertStore::new(); - let mut req_hash = MockHash::new(); - let mut req_m1_hash = MockHash::new(); - let mut req_l1_hash = MockHash::new(); - let mut req_rng = MockRng::new(); - let req_evidence = MockEvidence::new(); - let mut req_peer_cert_store = DemoPeerCertStore::new(); - - // Responder side - let mut resp_cert_store = MockCertStore::new(); - let mut resp_hash = MockHash::new(); - let mut resp_m1_hash = MockHash::new(); - let mut resp_l1_hash = MockHash::new(); - let mut resp_rng = MockRng::new(); - let resp_evidence = MockEvidence::new(); - - // -- Create SPDM requester and responder using library APIs -- - // This demonstrates how to use the SpdmRequester/SpdmResponder with - // custom capabilities and algorithms passed via configuration. - // - // Pattern: Get defaults, modify as needed, pass to new() - // Here we just use the defaults which apply DEFAULT_DTS and DEFAULT_SMS. - let req_caps = RequesterConfig::default_capabilities(); - let req_algos = RequesterConfig::default_algorithms(); - - let requester_config = RequesterConfig { - capabilities: Some(req_caps), - algorithms: Some(req_algos), - }; - - let resp_caps = ResponderConfig::default_capabilities(); - let resp_algos = ResponderConfig::default_algorithms(); - - let responder_config = ResponderConfig { - capabilities: Some(resp_caps), - algorithms: Some(resp_algos), - }; - - let mut requester = SpdmRequester::new( - &mut transport_req, - &mut req_cert_store, - &mut req_peer_cert_store, - &mut req_hash, - &mut req_m1_hash, - &mut req_l1_hash, - &mut req_rng, - &req_evidence, - Some(requester_config), - ) - .expect("requester creation"); - - let mut responder = SpdmResponder::new( - &mut transport_resp, - &mut resp_cert_store, - &mut resp_hash, - &mut resp_m1_hash, - &mut resp_l1_hash, - &mut resp_rng, - &resp_evidence, - Some(responder_config), - ) - .expect("responder creation"); - - // -- Message buffers (one per context, reused via reset) -- - let mut req_buf_storage = [0u8; 4096]; - let mut resp_buf_storage = [0u8; 4096]; - let mut req_buf = MessageBuf::new(&mut req_buf_storage); - let mut resp_buf = MessageBuf::new(&mut resp_buf_storage); - - // ══════════════════════════════════════════════════════════════════════ - // Step 1: GET_VERSION → VERSION - // ══════════════════════════════════════════════════════════════════════ - - // Requester: generate and send GET_VERSION - generate_get_version( - requester.context_mut(), - &mut req_buf, - VersionReqPayload::new(0, 0), - ) - .expect("generate GET_VERSION"); - - requester - .context_mut() - .requester_send_request(&mut req_buf, RESPONDER_EID) - .expect("send GET_VERSION"); - - // Transfer: requester → responder - transfer(&buf_req, &mut server_resp.borrow_mut()); - buf_req.borrow_mut().clear(); - - // Responder: process request and send VERSION response - responder - .context_mut() - .responder_process_message(&mut resp_buf) - .expect("responder process GET_VERSION"); - - // Transfer: responder → requester - transfer(&buf_resp, &mut server_req.borrow_mut()); - buf_resp.borrow_mut().clear(); - - // Requester: process VERSION response - req_buf.reset(); - requester - .context_mut() - .requester_process_message(&mut req_buf) - .expect("requester process VERSION"); - - // ══════════════════════════════════════════════════════════════════════ - // Step 2: GET_CAPABILITIES → CAPABILITIES - // ══════════════════════════════════════════════════════════════════════ - - // Requester: generate and send GET_CAPABILITIES - req_buf.reset(); - generate_capabilities_request_local(requester.context_mut(), &mut req_buf) - .expect("generate GET_CAPABILITIES"); - requester - .context_mut() - .requester_send_request(&mut req_buf, RESPONDER_EID) - .expect("send GET_CAPABILITIES"); - - // Transfer: requester → responder - transfer(&buf_req, &mut server_resp.borrow_mut()); - buf_req.borrow_mut().clear(); - - // Responder: process request and send CAPABILITIES response - resp_buf.reset(); - responder - .context_mut() - .responder_process_message(&mut resp_buf) - .expect("responder process GET_CAPABILITIES"); - - // Transfer: responder → requester - transfer(&buf_resp, &mut server_req.borrow_mut()); - buf_resp.borrow_mut().clear(); - - // Requester: process CAPABILITIES response - req_buf.reset(); - requester - .context_mut() - .requester_process_message(&mut req_buf) - .expect("requester process CAPABILITIES"); - - // ══════════════════════════════════════════════════════════════════════ - // Step 3: NEGOTIATE_ALGORITHMS → ALGORITHMS - // ══════════════════════════════════════════════════════════════════════ - - // Requester: generate and send NEGOTIATE_ALGORITHMS - req_buf.reset(); - generate_negotiate_algorithms_request( - requester.context_mut(), - &mut req_buf, - None, - None, - None, - None, - ) - .expect("generate NEGOTIATE_ALGORITHMS"); - requester - .context_mut() - .requester_send_request(&mut req_buf, RESPONDER_EID) - .expect("send NEGOTIATE_ALGORITHMS"); - - // Transfer: requester → responder - transfer(&buf_req, &mut server_resp.borrow_mut()); - buf_req.borrow_mut().clear(); - - // Responder: process request and send ALGORITHMS response - resp_buf.reset(); - responder - .context_mut() - .responder_process_message(&mut resp_buf) - .expect("responder process NEGOTIATE_ALGORITHMS"); - - // Transfer: responder → requester - transfer(&buf_resp, &mut server_req.borrow_mut()); - buf_resp.borrow_mut().clear(); - - // Requester: process ALGORITHMS response - req_buf.reset(); - requester - .context_mut() - .requester_process_message(&mut req_buf) - .expect("requester process ALGORITHMS"); - - // VCA flow completed successfully! -}