From e7320c60db1ad47b168823382eec44e68e7b7110 Mon Sep 17 00:00:00 2001 From: "Lu, Xiaoyu1" Date: Thu, 31 Aug 2023 11:59:45 -0400 Subject: [PATCH] Test: introduce a responder test runner. 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 --- test/spdmlib-test/Cargo.toml | 3 + .../src/common/crypto_callback.rs | 5 + test/spdmlib-test/src/common/device_io.rs | 157 +++++++++++++++++- .../src/common/secret_callback.rs | 27 +++ test/spdmlib-test/src/common/util.rs | 142 +++++++++++++++- .../src/responder_tests/examples/Readme.md | 51 ++++++ 6 files changed, 380 insertions(+), 5 deletions(-) create mode 100644 test/spdmlib-test/src/responder_tests/examples/Readme.md diff --git a/test/spdmlib-test/Cargo.toml b/test/spdmlib-test/Cargo.toml index 77f7bf1a..60ac74b8 100644 --- a/test/spdmlib-test/Cargo.toml +++ b/test/spdmlib-test/Cargo.toml @@ -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 = "*" diff --git a/test/spdmlib-test/src/common/crypto_callback.rs b/test/spdmlib-test/src/common/crypto_callback.rs index 53469b71..4161dbc9 100644 --- a/test/spdmlib-test/src/common/crypto_callback.rs +++ b/test/spdmlib-test/src/common/crypto_callback.rs @@ -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()); } diff --git a/test/spdmlib-test/src/common/device_io.rs b/test/spdmlib-test/src/common/device_io.rs index 62cbd340..7c2e5f24 100644 --- a/test/spdmlib-test/src/common/device_io.rs +++ b/test/spdmlib-test/src/common/device_io.rs @@ -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; @@ -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>, + secured_message: bool, + ) -> SpdmResult { + // 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>, + ) -> 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>, + _is_app_message: bool, + ) -> SpdmResult { + 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>, + ) -> 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>>, + pub tx: Arc>>, +} + +impl TestSpdmDeviceIo { + pub fn new(rx: Arc>>, tx: Arc>>) -> Self { + Self { rx, tx } + } +} + +#[async_trait] +impl SpdmDeviceIo for TestSpdmDeviceIo { + async fn receive( + &mut self, + out_buffer: Arc>, + _timeout: usize, + ) -> Result { + let mut rx = self.rx.lock(); + if (rx.len() < 4) { + return Err(0); + } + // Length 4 bytes + let length_buf: Vec = 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) -> VecDeque { + // 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::::new())); + let tx = Arc::new(Mutex::new(VecDeque::::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] + ) +} diff --git a/test/spdmlib-test/src/common/secret_callback.rs b/test/spdmlib-test/src/common/secret_callback.rs index 9295f2bf..7ea828f1 100644 --- a/test/spdmlib-test/src/common/secret_callback.rs +++ b/test/spdmlib-test/src/common/secret_callback.rs @@ -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( @@ -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 { + 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!(); + } + } +} diff --git a/test/spdmlib-test/src/common/util.rs b/test/spdmlib-test/src/common/util.rs index 2c6c9026..948ae263 100644 --- a/test/spdmlib-test/src/common/util.rs +++ b/test/spdmlib-test/src/common/util.rs @@ -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: [ @@ -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> { + 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> { + 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) -> VecDeque, + ) -> VecDeque { + 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) -> VecDeque, + ) -> VecDeque { + 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> { + 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) -> VecDeque) -> 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::::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 + } +} diff --git a/test/spdmlib-test/src/responder_tests/examples/Readme.md b/test/spdmlib-test/src/responder_tests/examples/Readme.md new file mode 100644 index 00000000..a34d9857 --- /dev/null +++ b/test/spdmlib-test/src/responder_tests/examples/Readme.md @@ -0,0 +1,51 @@ +## SPDM Message examples + +This folder contains example of SPDM Message requests/response. + +Here is a simple example + +``` +--- +input: + - - false + - "10 84 00 00 " + - - false + - "12 e1 00 00 00 07 00 00 c0 62 00 00 00 10 00 00 00 10 00 00 " + - - false + - "12 e3 04 00 30 00 01 02 80 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 20 10 00 03 20 02 00 04 20 02 00 05 20 01 00 " + - - false + - "12 82 00 00 00 00 00 02 " + - - false + - "12 82 00 00 00 02 00 02 " + - - false + - "12 82 00 00 00 04 00 02 " + - - false + - "12 82 00 00 00 06 33 00 " + - - false + - "12 81 00 00 " +expected: + - - false + - "10 04 00 00 00 03 00 10 00 11 00 12 " + - - false + - "12 61 00 00 00 00 00 00 f6 7b 00 00 00 12 00 00 00 12 00 00 " + - - false + - "12 63 04 00 34 00 01 02 04 00 00 00 80 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 20 10 00 03 20 02 00 04 20 02 00 05 20 01 00 " + - - false + - "12 02 00 00 00 02 33 04 33 06 00 00 71 0b a5 94 61 1d 3a 37 c9 10 a1 44 38 f6 d9 2e 7d b9 bb aa 6b ab 66 de bc ea b1 cf 23 a3 38 90 73 24 2e 5f 6c e9 f6 7b c9 8a 7f a2 fa 38 46 e2 30 82 01 d4 30 82 01 5a a0 03 02 01 02 02 14 21 41 dd 4e ec f8 19 62 24 2e 7e d6 ae 64 cb 29 5d c4 90 51 30 0a 06 08 2a 86 48 ce 3d 04 03 03 30 21 31 1f 30 1d 06 03 55 04 03 0c 16 44 4d 54 46 20 6c 69 62 73 70 64 6d 20 45 43 50 33 38 34 20 43 41 30 1e 17 0d 32 33 30 34 32 30 30 31 31 33 35 34 5a 17 0d 33 33 30 34 31 37 30 31 31 33 35 34 5a 30 21 31 1f 30 1d 06 03 55 04 03 0c 16 44 4d 54 46 20 6c 69 62 73 70 64 6d 20 45 43 50 33 38 34 20 43 41 30 76 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b 81 04 00 22 03 62 00 04 15 1c 0b 41 fb 87 d5 20 e0 7a f8 58 67 ef c0 ed 61 af 58 bf 92 52 33 ae 87 a9 7f 46 8b d4 b6 ef aa 2c 5b 0f 84 19 a0 8c 10 9b 81 4c d7 64 7e 32 6e 02 c5 fc 3d 25 7c a3 bc b5 ca a6 ce 04 11 8d bd ba 19 73 92 98 56 cc 99 24 48 17 ef b2 fb 54 3f f0 31 1d b4 a3 04 ea dc 22 ca c8 9a 2d fd fc a3 53 30 51 30 1d 06 03 55 1d 0e 04 16 04 14 4a e2 a4 0a c0 5d ae a2 30 aa d0 57 70 d0 51 58 5f 5c a3 52 30 1f 06 03 55 1d 23 04 18 30 16 80 14 4a e2 a4 0a c0 5d ae a2 30 aa d0 57 70 d0 51 58 5f 5c a3 52 30 0f 06 03 55 1d 13 01 01 ff 04 05 30 03 01 01 ff 30 0a 06 08 2a 86 48 ce 3d 04 03 03 03 68 00 30 65 02 30 2a 32 95 2c ff aa 1f 33 52 6f 6c fd 40 72 ff 17 89 6e 03 5f bb 42 03 c4 6e 17 86 aa 98 a8 c1 20 12 d9 04 7c 16 ea 1f ae 8a ef 85 11 41 57 df fc 02 31 00 a8 4c b9 90 4b 2f 6a 4f c2 8e d8 23 ad 81 de 4c 51 50 0b 27 17 8f 46 26 89 3b 9f f9 ce 48 e0 ee 28 fd 3f 06 " + - - false + - "12 02 00 00 00 02 33 02 e1 b3 4d 8f 6c 48 1c 2a 94 a9 d0 de 30 82 01 dc 30 82 01 61 a0 03 02 01 02 02 01 01 30 0a 06 08 2a 86 48 ce 3d 04 03 03 30 21 31 1f 30 1d 06 03 55 04 03 0c 16 44 4d 54 46 20 6c 69 62 73 70 64 6d 20 45 43 50 33 38 34 20 43 41 30 1e 17 0d 32 33 30 34 32 30 30 31 31 34 30 35 5a 17 0d 33 33 30 34 31 37 30 31 31 34 30 35 5a 30 30 31 2e 30 2c 06 03 55 04 03 0c 25 44 4d 54 46 20 6c 69 62 73 70 64 6d 20 45 43 50 33 38 34 20 69 6e 74 65 72 6d 65 64 69 61 74 65 20 63 65 72 74 30 76 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b 81 04 00 22 03 62 00 04 31 7d 01 41 1a f7 e9 ea 5c 39 1e 37 6a 16 8d 2c 6e d9 7e ea 19 25 c5 97 40 dc ce 39 7f 92 46 cb fb 4b 44 d8 f7 3d db 47 a6 44 bf 55 77 02 35 d1 37 27 68 a4 e8 04 4e 87 75 8f 59 57 a9 80 b8 b4 3c 97 ca fa 1b 13 a8 50 dc 0d 7a be 13 3d 31 5b fd d8 9f d6 fc aa 27 33 b8 92 5c 5b b8 0c 24 ca a3 5e 30 5c 30 0c 06 03 55 1d 13 04 05 30 03 01 01 ff 30 0b 06 03 55 1d 0f 04 04 03 02 01 fe 30 1d 06 03 55 1d 0e 04 16 04 14 00 0f 6f a7 6b 1f 34 03 0e 5a 21 c7 38 eb f1 a5 57 d5 57 cd 30 20 06 03 55 1d 25 01 01 ff 04 16 30 14 06 08 2b 06 01 05 05 07 03 01 06 08 2b 06 01 05 05 07 03 02 30 0a 06 08 2a 86 48 ce 3d 04 03 03 03 69 00 30 66 02 31 00 99 03 64 47 66 6c 30 6a b7 9d d3 5a 99 21 68 ae 09 50 6f eb b4 fd ef 69 0b cf b2 56 4f b6 8f 1f 43 11 0c de f6 fd 77 fd a9 34 25 81 7e 97 64 4c 02 31 00 f9 c7 5c 54 3d 43 92 9a 30 3d 61 1a 1e f8 65 51 d4 d5 20 90 d2 85 f0 44 de 40 85 ea 34 61 dd 6b fe 60 bd 0a c0 db 3d 43 2c ad d4 c4 e9 fa 22 33 30 82 02 43 30 82 01 c8 a0 03 02 01 02 02 01 03 30 0a 06 08 " + - - false + - "12 02 00 00 00 02 33 00 2a 86 48 ce 3d 04 03 03 30 30 31 2e 30 2c 06 03 55 04 03 0c 25 44 4d 54 46 20 6c 69 62 73 70 64 6d 20 45 43 50 33 38 34 20 69 6e 74 65 72 6d 65 64 69 61 74 65 20 63 65 72 74 30 1e 17 0d 32 33 30 34 32 30 30 31 31 34 30 39 5a 17 0d 33 33 30 34 31 37 30 31 31 34 30 39 5a 30 2d 31 2b 30 29 06 03 55 04 03 0c 22 44 4d 54 46 20 6c 69 62 73 70 64 6d 20 45 43 50 33 38 34 20 72 65 73 70 6f 6e 64 65 72 20 63 65 72 74 30 76 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b 81 04 00 22 03 62 00 04 0d 1c b9 20 1c dc ed c8 64 e9 70 a3 62 12 da d4 fe 59 94 ce bd ae d7 d2 94 e4 e3 11 88 78 b9 88 c0 ed 5b e1 41 4a df 28 db 49 0f 38 81 52 68 b5 4c 6a 11 0b 58 b1 9c c1 4d 47 41 14 1f 6d d6 57 ac a3 9f d1 b9 64 de 07 11 e8 31 78 de 75 3a b7 2c 15 56 ea f5 20 63 93 0f 33 a9 71 a3 7e 78 19 a3 81 b8 30 81 b5 30 0c 06 03 55 1d 13 01 01 ff 04 02 30 00 30 0b 06 03 55 1d 0f 04 04 03 02 05 e0 30 1d 06 03 55 1d 0e 04 16 04 14 14 3c 4f 66 c7 d7 60 40 7f 67 c1 95 68 b3 87 28 3e c5 23 83 30 31 06 03 55 1d 11 04 2a 30 28 a0 26 06 0a 2b 06 01 04 01 83 1c 82 12 01 a0 18 0c 16 41 43 4d 45 3a 57 49 44 47 45 54 3a 31 32 33 34 35 36 37 38 39 30 30 2a 06 03 55 1d 25 01 01 ff 04 20 30 1e 06 08 2b 06 01 05 05 07 03 01 06 08 2b 06 01 05 05 07 03 02 06 08 2b 06 01 05 05 07 03 09 30 1a 06 0a 2b 06 01 04 01 83 1c 82 12 06 04 0c 06 0a 2b 06 01 04 01 83 1c 82 12 02 30 0a 06 08 2a 86 48 ce 3d 04 03 03 03 69 00 30 66 02 31 00 fe de 7a df 8a 27 2e 2e 62 a5 05 2e ef 49 6d 81 e0 a9 51 3b e6 50 7a 87 c2 64 10 46 57 a7 5e 32 d3 f1 e1 33 23 4c 84 aa 4f 76 56 a6 cd 89 a0 6f " + - - false + - "12 02 00 00 33 00 00 00 02 31 00 f1 9d fd 84 cf 9f d5 ff 22 7c 06 bf e5 c3 1b 19 dc aa 02 83 6c a7 1f e7 8b 31 2e b7 ca 6f 4a ba b1 1c af 9a 29 4e 58 e4 13 01 b1 e0 91 61 7f 61 " + - - false + - "12 01 00 01 e6 d9 c7 be 0d a9 c7 0b 21 5f 96 f0 1a 80 3e fb 15 da 62 0a fb b9 83 df 58 fb 50 c9 93 b7 0d 86 4d 07 be d8 08 37 f6 51 b0 f2 f7 03 9b 68 14 0a " +``` + +- input: input is used to describe the input sequence +- expected: expected is used to describe the expected return + + + + +