From 67600bd896ac316cd9cf67cf43592cdc86718440 Mon Sep 17 00:00:00 2001 From: Zach Halvorsen Date: Fri, 3 Mar 2023 11:49:28 -0800 Subject: [PATCH] Add verification library code. A golang library and test suite has been added to start testing DPE end to end. There will need to be a layer added to abstract the transport layer and allow testing with devices as well as the simulator. Currently the simulator is the only working option. --- dpe/src/commands.rs | 19 ++- dpe/src/dpe_instance.rs | 10 +- simulator/Cargo.lock | 195 ++++++++++++++++++++++++++++++ simulator/Cargo.toml | 1 + simulator/src/main.rs | 72 ++++++++--- verification/README.md | 4 + verification/abi.go | 45 +++++++ verification/client.go | 93 ++++++++++++++ verification/go.mod | 3 + verification/simulator.go | 38 ++++++ verification/verification_test.go | 21 ++++ 11 files changed, 480 insertions(+), 21 deletions(-) create mode 100644 verification/README.md create mode 100644 verification/abi.go create mode 100644 verification/client.go create mode 100644 verification/go.mod create mode 100644 verification/simulator.go create mode 100644 verification/verification_test.go diff --git a/dpe/src/commands.rs b/dpe/src/commands.rs index 2a6930a7..470646da 100644 --- a/dpe/src/commands.rs +++ b/dpe/src/commands.rs @@ -79,7 +79,12 @@ impl TryFrom<&[u8]> for CommandHdr { cmd_id: u32::from_le_bytes(raw[4..8].try_into().unwrap()), profile: u32::from_le_bytes(raw[8..12].try_into().unwrap()), }; - if header.magic != Self::DPE_COMMAND_MAGIC || header.profile != DPE_PROFILE as u32 { + if header.magic != Self::DPE_COMMAND_MAGIC { + return Err(DpeErrorCode::InvalidCommand); + } + // The client doesn't know what profile is implemented when calling the `GetProfile` + // command. But, all other commands should be directed towards the correct profile. + if header.cmd_id != Command::GET_PROFILE && header.profile != DPE_PROFILE as u32 { return Err(DpeErrorCode::InvalidCommand); } Ok(header) @@ -257,17 +262,29 @@ mod tests { #[cfg(feature = "dpe_profile_p384_sha384")] let wrong_profile = DpeProfile::P256Sha256 as u32; + // All commands should check the profile except GetProfile. assert_eq!( invalid_command, CommandHdr::try_from( Vec::::from(CommandHdr { profile: wrong_profile, + cmd_id: Command::INITIALIZE_CONTEXT, ..DEFAULT_COMMAND }) .as_slice() ) ); + // Make sure GetProfile doesn't care. + assert!(CommandHdr::try_from( + Vec::::from(CommandHdr { + profile: wrong_profile, + ..DEFAULT_COMMAND + }) + .as_slice() + ) + .is_ok()); + // Test correct command. Using random command ID to check endianness and consistency. const GOOD_HEADER: CommandHdr = CommandHdr { cmd_id: 0x8765_4321, diff --git a/dpe/src/dpe_instance.rs b/dpe/src/dpe_instance.rs index 541c0008..d8d7e568 100644 --- a/dpe/src/dpe_instance.rs +++ b/dpe/src/dpe_instance.rs @@ -113,11 +113,11 @@ impl Default for TciMeasurement { #[derive(Default)] pub struct Support { - simulation: bool, - extend_tci: bool, - auto_init: bool, - tagging: bool, - rotate_context: bool, + pub simulation: bool, + pub extend_tci: bool, + pub auto_init: bool, + pub tagging: bool, + pub rotate_context: bool, } impl Support { diff --git a/simulator/Cargo.lock b/simulator/Cargo.lock index 887912f1..2b940765 100644 --- a/simulator/Cargo.lock +++ b/simulator/Cargo.lock @@ -26,6 +26,43 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "ctrlc" version = "3.2.5" @@ -40,6 +77,27 @@ dependencies = [ name = "dpe" version = "0.1.0" +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -55,12 +113,52 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", +] + [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "nix" version = "0.26.2" @@ -118,12 +216,42 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + [[package]] name = "pkg-config" version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.51" @@ -142,10 +270,25 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustix" +version = "0.36.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "simulator" version = "0.1.0" dependencies = [ + "clap", "ctrlc", "dpe", "openssl", @@ -157,6 +300,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.109" @@ -168,6 +317,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + [[package]] name = "unicode-ident" version = "1.0.6" @@ -180,6 +338,43 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/simulator/Cargo.toml b/simulator/Cargo.toml index e6ad1918..330767a0 100644 --- a/simulator/Cargo.toml +++ b/simulator/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" [dependencies] ctrlc = { version = "3.0", features = ["termination"] } openssl = "0.10" +clap = { version = "4.1.8", features = ["derive"] } dpe = { path = "../dpe" } diff --git a/simulator/src/main.rs b/simulator/src/main.rs index 5d23bc5f..306dc785 100644 --- a/simulator/src/main.rs +++ b/simulator/src/main.rs @@ -1,30 +1,38 @@ -use dpe::commands::CommandHdr; +use clap::Parser; +use dpe::commands::Command; use dpe::crypto::Crypto; use dpe::dpe_instance::{DpeInstance, Support}; use dpe::response::DpeErrorCode; -use dpe::DpeProfile; +use dpe::{execute_command, DpeProfile}; use std::fs; -use std::io::Read; -use std::mem; +use std::io::{Read, Write}; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::Path; use std::process; const SOCKET_PATH: &str = "/tmp/dpe-sim.socket"; -fn handle_request(_dpe: &mut DpeInstance, stream: &mut UnixStream) { - let mut hdr_buf = [0u8; mem::size_of::()]; - match stream.read_exact(&mut hdr_buf) { - Ok(()) => (), - Err(_) => { - println!("Failed to read command header"); - return; - } +fn handle_request(dpe: &mut DpeInstance, stream: &mut UnixStream) { + let mut buf = [0u8; 128]; + let len = stream.read(&mut buf).unwrap(); + + println!("----------------------------------"); + if let Ok(command) = Command::deserialize(&buf[..len]) { + println!("| {command:x?}",); + } else { + println!("| Received invalid command. {:02x?}", &buf[..len]) } + println!("|"); - let (_, hdr, _) = unsafe { hdr_buf.align_to::() }; + let mut response = [0u8; 128]; + let len = execute_command::(dpe, &buf[..len], &mut response).unwrap(); - println!("Got command {:#x}", hdr[0].cmd_id); + let response_code = u32::from_le_bytes(response[4..8].try_into().unwrap()); + // There are a few vendor error codes starting at 0x1000, so this can be a 2 bytes. + println!("| Response Code {response_code:#06x}"); + println!("----------------------------------"); + + stream.write_all(&response[..len]).unwrap(); } fn cleanup() { @@ -38,7 +46,34 @@ fn cleanup() { } } +/// Starts a DPE simulator that will receive commands and send responses over unix streams. +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Supports simulation contexts. + #[arg(long)] + supports_simulation: bool, + + /// Supports the ExtendTci command. + #[arg(long)] + supports_extend_tci: bool, + + /// Automatically initializes the default context. + #[arg(long)] + supports_auto_init: bool, + + /// Supports the TagTci and GetTaggedTci commands. + #[arg(long)] + supports_tagging: bool, + + /// Supports the RotateContextHandle command. + #[arg(long)] + supports_rotate_context: bool, +} + fn main() -> std::io::Result<()> { + let args = Args::parse(); + let socket = Path::new(SOCKET_PATH); // Delete old socket if necessary if socket.exists() { @@ -53,7 +88,14 @@ fn main() -> std::io::Result<()> { }) .unwrap(); - let mut dpe = DpeInstance::new::(Support::default()); + let support = Support { + simulation: args.supports_simulation, + extend_tci: args.supports_extend_tci, + auto_init: args.supports_auto_init, + tagging: args.supports_tagging, + rotate_context: args.supports_rotate_context, + }; + let mut dpe = DpeInstance::new::(support); println!("DPE listening to socket {SOCKET_PATH}"); diff --git a/verification/README.md b/verification/README.md new file mode 100644 index 00000000..06e38086 --- /dev/null +++ b/verification/README.md @@ -0,0 +1,4 @@ +# DPE Verification Tests + +This test suite is a userspace test-suite which exercises DPE commands +end-to-end and ensures compliance with the DPE iRoT Profile. diff --git a/verification/abi.go b/verification/abi.go new file mode 100644 index 00000000..b65b267f --- /dev/null +++ b/verification/abi.go @@ -0,0 +1,45 @@ +package verification + +const ( + CmdMagic uint32 = 0x44504543 + RespMagic uint32 = 0x44504552 + + GetProfileCode uint32 = 0x1 + InitCtxCode uint32 = 0x5 + + DPE_PROFILE_P256_SHA256 uint32 = 1 + DPE_PROFILE_P384_SHA384 uint32 = 2 +) + +type CommandHdr struct { + magic uint32 + cmd uint32 + profile uint32 +} + +type RespHdr struct { + Magic uint32 + Status uint32 + Profile uint32 +} + +type InitCtxCmd struct { + flags uint32 +} + +func NewInitCtxIsDefault() *InitCtxCmd { + return &InitCtxCmd{flags: 1 << 30} +} + +func NewInitCtxIsSimulation() *InitCtxCmd { + return &InitCtxCmd{flags: 1 << 31} +} + +type InitCtxResp struct { + Handle [16]byte +} + +type GetProfileResp struct { + Version uint32 + Flags uint32 +} diff --git a/verification/client.go b/verification/client.go new file mode 100644 index 00000000..99dbb0b5 --- /dev/null +++ b/verification/client.go @@ -0,0 +1,93 @@ +package verification + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" +) + +type Transport interface { + SendCmd(buf []byte) (error, []byte) +} + +type DpeClient struct { + transport Transport + profile uint32 +} + +func (c *DpeClient) Initialize(cmd *InitCtxCmd) (error, InitCtxResp) { + hdr := CommandHdr{ + magic: CmdMagic, + cmd: InitCtxCode, + profile: c.profile, + } + + buf := &bytes.Buffer{} + binary.Write(buf, binary.LittleEndian, hdr) + binary.Write(buf, binary.LittleEndian, cmd) + + err, resp := c.transport.SendCmd(buf.Bytes()) + if err != nil { + return err, InitCtxResp{} + } + + respHdr := RespHdr{} + respStruct := InitCtxResp{} + + r := bytes.NewReader(resp) + binary.Read(r, binary.LittleEndian, &respHdr) + check_hdr_err := c.checkRespHdr(respHdr) + if check_hdr_err != nil { + return check_hdr_err, InitCtxResp{} + } + + binary.Read(r, binary.LittleEndian, &respStruct) + + return nil, respStruct +} + +func (c *DpeClient) GetProfile() (error, GetProfileResp) { + hdr := CommandHdr{ + magic: CmdMagic, + cmd: GetProfileCode, + } + + buf := &bytes.Buffer{} + binary.Write(buf, binary.LittleEndian, hdr) + + err, resp := c.transport.SendCmd(buf.Bytes()) + if err != nil { + return err, GetProfileResp{} + } + + respHdr := RespHdr{} + respStruct := GetProfileResp{} + + r := bytes.NewReader(resp) + binary.Read(r, binary.LittleEndian, &respHdr) + check_hdr_err := c.checkRespHdr(respHdr) + if check_hdr_err != nil { + return check_hdr_err, GetProfileResp{} + } + + binary.Read(r, binary.LittleEndian, &respStruct) + + return nil, respStruct +} + +// Check that the response header has all expected values and did not have any +// errors. +func (c *DpeClient) checkRespHdr(hdr RespHdr) error { + if hdr.Magic != RespMagic { + fmt.Println(hdr) + return errors.New("Invalid response magic value.") + } + if hdr.Status != 0 { + return errors.New("Received an error status.") + } + if hdr.Profile != c.profile { + return errors.New("Incorrect profile value.") + } + return nil +} diff --git a/verification/go.mod b/verification/go.mod new file mode 100644 index 00000000..8d304ab7 --- /dev/null +++ b/verification/go.mod @@ -0,0 +1,3 @@ +module dpe/verification + +go 1.20 diff --git a/verification/simulator.go b/verification/simulator.go new file mode 100644 index 00000000..28eabe9b --- /dev/null +++ b/verification/simulator.go @@ -0,0 +1,38 @@ +package verification + +import ( + "errors" + "net" +) + +type SimulatorTransport struct { + Transport +} + +func (s *SimulatorTransport) SendCmd(buf []byte) (error, []byte) { + // Connect to DPE instance. + conn, err := net.Dial("unix", "/tmp/dpe-sim.socket") + if err != nil { + return err, []byte{} + } + + // Send the command. + num_sent, err := conn.Write(buf) + if err != nil { + return err, []byte{} + } + if num_sent != len(buf) { + return errors.New("Didn't send the whole command."), []byte{} + } + + // Get the response. + num_rec, err := conn.Read(buf) + if err != nil { + return err, []byte{} + } + if num_rec == 0 { + return errors.New("Didn't receive anything in the response."), []byte{} + } + + return nil, buf +} diff --git a/verification/verification_test.go b/verification/verification_test.go new file mode 100644 index 00000000..67b0d213 --- /dev/null +++ b/verification/verification_test.go @@ -0,0 +1,21 @@ +package verification + +import ( + "testing" +) + +func TestGetProfile(t *testing.T) { + client := DpeClient{transport: &SimulatorTransport{}, profile: DPE_PROFILE_P256_SHA256} + err, _ := client.GetProfile() + if err != nil { + t.Fatal(err) + } +} + +func TestInitializeContext(t *testing.T) { + client := DpeClient{transport: &SimulatorTransport{}, profile: DPE_PROFILE_P256_SHA256} + err, _ := client.Initialize(NewInitCtxIsDefault()) + if err != nil { + t.Fatal(err) + } +}