Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
Test: introduce a responder test runner.
Browse files Browse the repository at this point in the history
with this test runner:

1. Check the response message sent by the responder.
2. Simplify the test code. Easy to maintain.
3. Load the test case from the file. Easy to add test case.

Signed-off-by: xiaoyuxlu <[email protected]>
  • Loading branch information
xiaoyuxlu committed Aug 31, 2023
1 parent 6baad58 commit e7320c6
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 5 deletions.
3 changes: 3 additions & 0 deletions test/spdmlib-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ async-recursion = "1.0.4"
spin = { version = "0.9.8" }
executor = { path = "../../executor" }
pcidoe_transport = { path = "../../pcidoe_transport" }
serde = "1.0"
serde_derive = "1.0"
serde_yaml = "0.8"

[dev-dependencies]
env_logger = "*"
Expand Down
5 changes: 5 additions & 0 deletions test/spdmlib-test/src/common/crypto_callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,5 +175,10 @@ fn fake_verify_cert_chain(_cert_chain: &[u8]) -> SpdmResult {
#[test]
// Make sure this is the first test case running by `cargo test`
fn test_0_crypto_init() {
use super::secret_callback::FAKE_SECRET_ASYM_IMPL_INSTANCE;
spdmlib::crypto::aead::register(FAKE_AEAD.clone());
spdmlib::crypto::asym_verify::register(FAKE_ASYM_VERIFY.clone());
spdmlib::crypto::aead::register(FAKE_AEAD.clone());
spdmlib::crypto::rand::register(FAKE_RAND.clone());
spdmlib::secret::asym_sign::register(FAKE_SECRET_ASYM_IMPL_INSTANCE.clone());
}
157 changes: 155 additions & 2 deletions test/spdmlib-test/src/common/device_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
#![allow(unused)]

use async_trait::async_trait;
use spdmlib::common::{SpdmDeviceIo, ST1};
use spdmlib::common::{SpdmDeviceIo, SpdmTransportEncap, ST1};
use spdmlib::config::RECEIVER_BUFFER_SIZE;
use spdmlib::error::{SpdmResult, SPDM_STATUS_ERROR_PEER};
use spdmlib::error::{SpdmResult, SPDM_STATUS_DECAP_FAIL, SPDM_STATUS_ERROR_PEER};
use spdmlib::responder;
use std::cell::RefCell;
use std::collections::VecDeque;
Expand Down Expand Up @@ -246,3 +246,156 @@ fn test_fake_device_io() {
};
executor::block_on(future);
}

pub struct TestTransportEncap;
#[async_trait]
impl SpdmTransportEncap for TestTransportEncap {
async fn encap(
&mut self,
spdm_buffer: Arc<&[u8]>,
transport_buffer: Arc<Mutex<&mut [u8]>>,
secured_message: bool,
) -> SpdmResult<usize> {
// format
// secure_message u8
let mut transport_buffer = transport_buffer.lock();
let len = spdm_buffer.len();
transport_buffer[0] = secured_message as u8;

if transport_buffer.len() < len + 1 {
return Err(SPDM_STATUS_DECAP_FAIL);
}
transport_buffer[1..(1 + len)].copy_from_slice(&spdm_buffer[..]);
Ok(1 + len)
}

async fn decap(
&mut self,
transport_buffer: Arc<&[u8]>,
spdm_buffer: Arc<Mutex<&mut [u8]>>,
) -> SpdmResult<(usize, bool)> {
let mut spdm_buffer = spdm_buffer.lock();
let spdm_buffer_len = transport_buffer.len() - 1;
let secure_message = if transport_buffer[0] == 0 {
false
} else {
true
};
spdm_buffer[0..spdm_buffer_len].copy_from_slice(&transport_buffer[1..]);
Ok((spdm_buffer_len, secure_message))
}

async fn encap_app(
&mut self,
spdm_buffer: Arc<&[u8]>,
app_buffer: Arc<Mutex<&mut [u8]>>,
_is_app_message: bool,
) -> SpdmResult<usize> {
let mut app_buffer = app_buffer.lock();
app_buffer[0..spdm_buffer.len()].copy_from_slice(&spdm_buffer);
Ok(spdm_buffer.len())
}

async fn decap_app(
&mut self,
app_buffer: Arc<&[u8]>,
spdm_buffer: Arc<Mutex<&mut [u8]>>,
) -> SpdmResult<(usize, bool)> {
let mut spdm_buffer = spdm_buffer.lock();
spdm_buffer[0..app_buffer.len()].copy_from_slice(&app_buffer);
Ok((app_buffer.len(), false))
}
fn get_sequence_number_count(&mut self) -> u8 {
todo!()
}

fn get_max_random_count(&mut self) -> u16 {
todo!()
}
}

pub struct TestSpdmDeviceIo {
pub rx: Arc<Mutex<VecDeque<u8>>>,
pub tx: Arc<Mutex<VecDeque<u8>>>,
}

impl TestSpdmDeviceIo {
pub fn new(rx: Arc<Mutex<VecDeque<u8>>>, tx: Arc<Mutex<VecDeque<u8>>>) -> Self {
Self { rx, tx }
}
}

#[async_trait]
impl SpdmDeviceIo for TestSpdmDeviceIo {
async fn receive(
&mut self,
out_buffer: Arc<Mutex<&mut [u8]>>,
_timeout: usize,
) -> Result<usize, usize> {
let mut rx = self.rx.lock();
if (rx.len() < 4) {
return Err(0);
}
// Length 4 bytes
let length_buf: Vec<u8> = rx.drain(0..4).collect();
let length =
u32::from_le_bytes([length_buf[0], length_buf[1], length_buf[2], length_buf[3]]);
let length = length as usize;
// Data length bytes
let mut out_buffer = out_buffer.lock();
if out_buffer.len() < length {
return Err(0);
}
for index in 0..length {
out_buffer[index] = rx.pop_front().unwrap();
}
println!("RECV RAW - {:02x?}", &out_buffer[..length]);
Ok(length)
}
async fn send(&mut self, buffer: Arc<&[u8]>) -> SpdmResult {
{
let mut tx = self.tx.lock();
let length = buffer.len() as u32;
tx.extend(length.to_le_bytes());
tx.extend(buffer.iter());
}
println!("SEND RAW - {:02x?}", &buffer);
Ok(())
}

async fn flush_all(&mut self) -> SpdmResult {
Ok(())
}
}

pub fn test_header_generater_callback(secure: bool, spdm_msg: VecDeque<u8>) -> VecDeque<u8> {
// This function is used to generate the header
// Note: The same method as device_io and transport encap should be use
// Current implementation is for TestDeviceIo and TestTransportEncap
let mut ret = VecDeque::new();
let length = (spdm_msg.len() + 1) as u32;
ret.extend(length.to_le_bytes());
ret.push_back(secure as u8);
ret.extend(spdm_msg);
ret
}

#[test]
fn test_test_device_io() {
let rx = Arc::new(Mutex::new(VecDeque::<u8>::new()));
let tx = Arc::new(Mutex::new(VecDeque::<u8>::new()));
let rx_shared = Arc::clone(&rx);
let tx_shared = Arc::clone(&tx);
let future = async {
let mut server = TestSpdmDeviceIo::new(rx, tx);
let _ = server.send(Arc::new(b"hello")).await;
};
executor::block_on(future);

let tx = tx_shared.lock();
let res = tx.as_slices();
assert_eq!(
res.0,
[0x05, 0x00, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f]
)
}
27 changes: 27 additions & 0 deletions test/spdmlib-test/src/common/secret_callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub static SECRET_PSK_IMPL_INSTANCE: SpdmSecretPsk = SpdmSecretPsk {

pub static SECRET_ASYM_IMPL_INSTANCE: SpdmSecretAsymSign =
SpdmSecretAsymSign { sign_cb: asym_sign };
pub static FAKE_SECRET_ASYM_IMPL_INSTANCE: SpdmSecretAsymSign = SpdmSecretAsymSign {
sign_cb: fake_asym_sign,
};

#[allow(clippy::field_reassign_with_default)]
fn measurement_collection_impl(
Expand Down Expand Up @@ -317,3 +320,27 @@ fn sign_ecdsa_asym_algo(
data: full_signature,
})
}

fn fake_asym_sign(
base_hash_algo: SpdmBaseHashAlgo,
base_asym_algo: SpdmBaseAsymAlgo,
data: &[u8],
) -> Option<SpdmSignatureStruct> {
match (base_hash_algo, base_asym_algo) {
(SpdmBaseHashAlgo::TPM_ALG_SHA_256, SpdmBaseAsymAlgo::TPM_ALG_ECDSA_ECC_NIST_P256) => {
Some(SpdmSignatureStruct {
data_size: 64,
data: [0x5a; SPDM_MAX_ASYM_KEY_SIZE],
})
}
(SpdmBaseHashAlgo::TPM_ALG_SHA_384, SpdmBaseAsymAlgo::TPM_ALG_ECDSA_ECC_NIST_P384) => {
Some(SpdmSignatureStruct {
data_size: 96,
data: [0x5a; SPDM_MAX_ASYM_KEY_SIZE],
})
}
_ => {
panic!();
}
}
}
142 changes: 139 additions & 3 deletions test/spdmlib-test/src/common/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,33 @@

#![allow(unused)]

use super::device_io::TestSpdmDeviceIo;
use super::USE_ECDSA;
use crate::common::device_io::MySpdmDeviceIo;
use crate::common::device_io::{MySpdmDeviceIo, TestTransportEncap};
use crate::common::secret_callback::SECRET_ASYM_IMPL_INSTANCE;
use crate::common::transport::PciDoeTransportEncap;
use codec::{Reader, Writer};
use spdmlib::common::{
SpdmCodec, SpdmConfigInfo, SpdmContext, SpdmDeviceIo, SpdmOpaqueSupport, SpdmProvisionInfo,
SpdmTransportEncap, DMTF_SECURE_SPDM_VERSION_10, DMTF_SECURE_SPDM_VERSION_11,
SpdmTransportEncap, DMTF_SECURE_SPDM_VERSION_10, DMTF_SECURE_SPDM_VERSION_11, ST1,
};
use spdmlib::config;
use spdmlib::crypto;
use spdmlib::message::SpdmMessage;
use spdmlib::protocol::*;
use spdmlib::{config, responder};
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

use spin::Mutex;
extern crate alloc;
use alloc::boxed::Box;
use alloc::collections::VecDeque;
use alloc::sync::Arc;
use core::ops::DerefMut;

use serde_derive::{Deserialize, Serialize};

pub fn create_info() -> (SpdmConfigInfo, SpdmProvisionInfo) {
let config_info = SpdmConfigInfo {
spdm_version: [
Expand Down Expand Up @@ -395,3 +401,133 @@ pub fn get_rsp_cert_chain_buff() -> SpdmCertChainBuffer {
SpdmCertChainBuffer::new(cert_chain, root_cert_hash.as_ref())
.expect("Create format certificate chain failed.")
}

#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct TestCase {
pub input: Vec<(bool, String)>,
pub expected: Vec<(bool, String)>,
}

impl TestCase {
pub fn read_yaml_file(file_path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let mut file = File::open(file_path)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
let test: TestCase = serde_yaml::from_str(&content)?;
Ok(test)
}

pub fn read_yaml_str(content: &str) -> Result<Self, Box<dyn std::error::Error>> {
let test: TestCase = serde_yaml::from_str(content)?;
Ok(test)
}

pub fn config() -> (SpdmConfigInfo, SpdmProvisionInfo) {
create_info()
}

pub fn input_to_vec(
&self,
cb: fn(secure: bool, bufer: VecDeque<u8>) -> VecDeque<u8>,
) -> VecDeque<u8> {
let mut ret = VecDeque::new();
for (secure, hex) in &self.input {
ret.extend(
(cb)(
*secure,
Self::hex_string_to_bytes(hex).expect("Should use hex string"),
)
.iter(),
)
}
ret
}
pub fn expected_to_vec(
&self,
cb: fn(secure: bool, bufer: VecDeque<u8>) -> VecDeque<u8>,
) -> VecDeque<u8> {
let mut ret = VecDeque::new();
for (secure, hex) in &self.expected {
ret.extend(
(cb)(
*secure,
Self::hex_string_to_bytes(hex).expect("Should use hex string"),
)
.iter(),
)
}
ret
}
// Hex input will be trimmed and filter with is_ascii_hexdigit
fn hex_string_to_bytes(hex: &String) -> Option<VecDeque<u8>> {
let hex: String = hex
.trim()
.chars()
.filter(|&c| c.is_ascii_hexdigit())
.collect();

if hex.len() % 2 != 0 {
return None; // Hex string must have an even number of characters
}

let mut bytes = VecDeque::new();

for i in (0..hex.len()).step_by(2) {
if let Ok(byte) = u8::from_str_radix(&hex[i..i + 2], 16) {
bytes.push_back(byte);
} else {
return None; // Invalid hex characters
}
}
Some(bytes)
}
}

pub struct ResponderRunner;
impl ResponderRunner {
pub fn run(case: TestCase, cb: fn(secure: bool, bufer: VecDeque<u8>) -> VecDeque<u8>) -> bool {
use super::secret_callback::FAKE_SECRET_ASYM_IMPL_INSTANCE;
use crate::common::crypto_callback::{FAKE_AEAD, FAKE_ASYM_VERIFY, FAKE_RAND};
spdmlib::crypto::aead::register(FAKE_AEAD.clone());
spdmlib::crypto::rand::register(FAKE_RAND.clone());
spdmlib::crypto::asym_verify::register(FAKE_ASYM_VERIFY.clone());
spdmlib::secret::asym_sign::register(FAKE_SECRET_ASYM_IMPL_INSTANCE.clone());
let mut output = Arc::new(Mutex::new(VecDeque::<u8>::new()));
let mut rx = Arc::new(Mutex::new(case.input_to_vec(cb)));
let mut output_ref = Arc::clone(&output);
log::debug!("intput : {:02x?}", rx.lock().make_contiguous());
let future = async {
let mut device_io = TestSpdmDeviceIo::new(rx, output_ref);
let mut transport_encap = TestTransportEncap;
let (config_info, provision_info) = TestCase::config();
let mut context = responder::ResponderContext::new(
Arc::new(Mutex::new(device_io)),
Arc::new(Mutex::new(transport_encap)),
config_info,
provision_info,
);
let raw_packet = &mut [0u8; spdmlib::config::RECEIVER_BUFFER_SIZE];
loop {
let result = context.process_message(false, &[0], raw_packet).await;
match result {
Err(nread) => {
if nread == 0 {
break;
}
}
Ok(_) => continue,
}
}
};
executor::block_on(future);
// Check Result
// output and case.expected
let mut expected = case.expected_to_vec(cb);
let mut output = output.lock();
let output = output.make_contiguous();
let expected = expected.make_contiguous();
log::debug!("output : {:02x?}\n", output);
log::debug!("expected: {:02x?}\n", expected);
output == expected
}
}
Loading

0 comments on commit e7320c6

Please sign in to comment.