From aca1f3517057d1fa96808f06603a70e5854db605 Mon Sep 17 00:00:00 2001 From: hcyang <100930165+hcyang-google@users.noreply.github.com> Date: Wed, 20 Apr 2022 15:49:17 +0800 Subject: [PATCH] Make rng in TestEnv deterministic and seedable (#461) * Move three dependent customizations into new file * default_min_pin_length(_rp_ids) and max_rp_ids_length * Did some backing store tricks to make the list configurable in TestCustomization. * Add testing for TestCustomization * Change assert comparison to assert_eq * Separate tests * Move 3 pure constants to new file * Return Vec for rp_ids() * Make rng in TestEnv deterministic and seedable * Move seed method to TestRng256 * Change some constant name in comments to snake case * Move seed rng of env to the start * Fix unused warning * Make rng in TestEnv deterministic and seedable * Move seed method to TestRng256 * Move seed rng of env to the start * Fix unused warning * Seed rng in all fuzz targets * Fix error introduced when merging Co-authored-by: Julien Cretin --- Cargo.toml | 3 +- fuzz/fuzz_helper/src/lib.rs | 37 ++++++++++++++++--- .../fuzz_targets/fuzz_target_process_ctap1.rs | 2 +- .../fuzz_target_process_ctap2_client_pin.rs | 2 +- ...fuzz_target_process_ctap2_get_assertion.rs | 2 +- ...zz_target_process_ctap2_make_credential.rs | 2 +- .../fuzz_target_process_ctap_command.rs | 2 +- .../fuzz_target_split_assemble.rs | 2 +- src/ctap/credential_management.rs | 1 - src/ctap/storage.rs | 4 +- src/env/test/mod.rs | 34 +++++++++++++++-- 11 files changed, 71 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23d309a..19e0c5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,12 +23,13 @@ subtle = { version = "2.2", default-features = false, features = ["nightly"] } serde_json = { version = "=1.0.69", default-features = false, features = ["alloc"] } embedded-time = "0.12.1" arbitrary = { version = "0.4.7", features = ["derive"], optional = true } +rand = { version = "0.8.4", optional = true } [features] debug_allocations = ["lang_items/debug_allocations"] debug_ctap = ["libtock_drivers/debug_ctap"] panic_console = ["lang_items/panic_console"] -std = ["crypto/std", "lang_items/std", "persistent_store/std"] +std = ["crypto/std", "lang_items/std", "persistent_store/std", "rand"] verbose = ["debug_ctap", "libtock_drivers/verbose_usb"] with_ctap1 = ["crypto/with_ctap1"] with_nfc = ["libtock_drivers/with_nfc"] diff --git a/fuzz/fuzz_helper/src/lib.rs b/fuzz/fuzz_helper/src/lib.rs index c14f4b7..f3498bf 100644 --- a/fuzz/fuzz_helper/src/lib.rs +++ b/fuzz/fuzz_helper/src/lib.rs @@ -128,25 +128,40 @@ fn process_message(data: &[u8], ctap: &mut Ctap) { // Interprets the raw data as any ctap command (including the command byte) and // invokes message splitting, packet processing at CTAP HID level and response assembling // using an initialized and allocated channel. -pub fn process_ctap_any_type(data: &[u8]) { +pub fn process_ctap_any_type(data: &[u8]) -> arbitrary::Result<()> { + let mut unstructured = Unstructured::new(data); + + let mut env = TestEnv::new(); + env.rng() + .seed_rng_from_u64(u64::arbitrary(&mut unstructured)?); + + let data = unstructured.take_rest(); // Initialize ctap state and hid and get the allocated cid. - let mut ctap = Ctap::new(TestEnv::new(), CtapInstant::new(0)); + let mut ctap = Ctap::new(env, CtapInstant::new(0)); let cid = initialize(&mut ctap); // Wrap input as message with the allocated cid. let mut command = cid.to_vec(); command.extend(data); process_message(&command, &mut ctap); + Ok(()) } // Interprets the raw data as of the given input type and // invokes message splitting, packet processing at CTAP HID level and response assembling // using an initialized and allocated channel. -pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) { +pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) -> arbitrary::Result<()> { + let mut unstructured = Unstructured::new(data); + + let mut env = TestEnv::new(); + env.rng() + .seed_rng_from_u64(u64::arbitrary(&mut unstructured)?); + + let data = unstructured.take_rest(); if !is_type(data, input_type) { - return; + return Ok(()); } // Initialize ctap state and hid and get the allocated cid. - let mut ctap = Ctap::new(TestEnv::new(), CtapInstant::new(0)); + let mut ctap = Ctap::new(env, CtapInstant::new(0)); let cid = initialize(&mut ctap); // Wrap input as message with allocated cid and command type. let mut command = cid.to_vec(); @@ -166,12 +181,15 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) { } command.extend(data); process_message(&command, &mut ctap); + Ok(()) } pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> arbitrary::Result<()> { let unstructured = &mut Unstructured::new(data); let mut env = TestEnv::new(); + env.rng().seed_rng_from_u64(u64::arbitrary(unstructured)?); + let mut state = CtapState::new(&mut env, CtapInstant::new(0)); let command = match input_type { @@ -202,8 +220,14 @@ pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> arbitrary: } // Splits the given data as HID packets and reassembles it, verifying that the original input message is reconstructed. -pub fn split_assemble_hid_packets(data: &[u8]) { +pub fn split_assemble_hid_packets(data: &[u8]) -> arbitrary::Result<()> { + let mut unstructured = Unstructured::new(data); + let mut env = TestEnv::new(); + env.rng() + .seed_rng_from_u64(u64::arbitrary(&mut unstructured)?); + + let data = unstructured.take_rest(); let message = raw_to_message(data); if let Some(hid_packet_iterator) = HidPacketIterator::new(message.clone()) { let mut assembler = MessageAssembler::new(); @@ -221,4 +245,5 @@ pub fn split_assemble_hid_packets(data: &[u8]) { ); } } + Ok(()) } diff --git a/fuzz/fuzz_targets/fuzz_target_process_ctap1.rs b/fuzz/fuzz_targets/fuzz_target_process_ctap1.rs index d0cd61b..c873fc7 100644 --- a/fuzz/fuzz_targets/fuzz_target_process_ctap1.rs +++ b/fuzz/fuzz_targets/fuzz_target_process_ctap1.rs @@ -7,5 +7,5 @@ use libfuzzer_sys::fuzz_target; // For a more generic fuzz target including all CTAP commands, you can use // fuzz_target_process_ctap_command. fuzz_target!(|data: &[u8]| { - process_ctap_specific_type(data, InputType::Ctap1); + process_ctap_specific_type(data, InputType::Ctap1).ok(); }); diff --git a/fuzz/fuzz_targets/fuzz_target_process_ctap2_client_pin.rs b/fuzz/fuzz_targets/fuzz_target_process_ctap2_client_pin.rs index b54bdef..221a121 100644 --- a/fuzz/fuzz_targets/fuzz_target_process_ctap2_client_pin.rs +++ b/fuzz/fuzz_targets/fuzz_target_process_ctap2_client_pin.rs @@ -7,5 +7,5 @@ use libfuzzer_sys::fuzz_target; // For a more generic fuzz target including all CTAP commands, you can use // fuzz_target_process_ctap_command. fuzz_target!(|data: &[u8]| { - process_ctap_specific_type(data, InputType::CborClientPinParameter); + process_ctap_specific_type(data, InputType::CborClientPinParameter).ok(); }); diff --git a/fuzz/fuzz_targets/fuzz_target_process_ctap2_get_assertion.rs b/fuzz/fuzz_targets/fuzz_target_process_ctap2_get_assertion.rs index 2ad1c68..d93b17c 100644 --- a/fuzz/fuzz_targets/fuzz_target_process_ctap2_get_assertion.rs +++ b/fuzz/fuzz_targets/fuzz_target_process_ctap2_get_assertion.rs @@ -7,5 +7,5 @@ use libfuzzer_sys::fuzz_target; // For a more generic fuzz target including all CTAP commands, you can use // fuzz_target_process_ctap_command. fuzz_target!(|data: &[u8]| { - process_ctap_specific_type(data, InputType::CborGetAssertionParameter); + process_ctap_specific_type(data, InputType::CborGetAssertionParameter).ok(); }); diff --git a/fuzz/fuzz_targets/fuzz_target_process_ctap2_make_credential.rs b/fuzz/fuzz_targets/fuzz_target_process_ctap2_make_credential.rs index 8c61846..b41b555 100644 --- a/fuzz/fuzz_targets/fuzz_target_process_ctap2_make_credential.rs +++ b/fuzz/fuzz_targets/fuzz_target_process_ctap2_make_credential.rs @@ -7,5 +7,5 @@ use libfuzzer_sys::fuzz_target; // For a more generic fuzz target including all CTAP commands, you can use // fuzz_target_process_ctap_command. fuzz_target!(|data: &[u8]| { - process_ctap_specific_type(data, InputType::CborMakeCredentialParameter); + process_ctap_specific_type(data, InputType::CborMakeCredentialParameter).ok(); }); diff --git a/fuzz/fuzz_targets/fuzz_target_process_ctap_command.rs b/fuzz/fuzz_targets/fuzz_target_process_ctap_command.rs index 7eb7df3..ff2c167 100644 --- a/fuzz/fuzz_targets/fuzz_target_process_ctap_command.rs +++ b/fuzz/fuzz_targets/fuzz_target_process_ctap_command.rs @@ -5,5 +5,5 @@ use libfuzzer_sys::fuzz_target; // Generically fuzz inputs as CTAP commands. fuzz_target!(|data: &[u8]| { - process_ctap_any_type(data); + process_ctap_any_type(data).ok(); }); diff --git a/fuzz/fuzz_targets/fuzz_target_split_assemble.rs b/fuzz/fuzz_targets/fuzz_target_split_assemble.rs index ee6f8bf..879bc63 100644 --- a/fuzz/fuzz_targets/fuzz_target_split_assemble.rs +++ b/fuzz/fuzz_targets/fuzz_target_split_assemble.rs @@ -5,5 +5,5 @@ use libfuzzer_sys::fuzz_target; // Fuzzing HID packets splitting and assembling functions. fuzz_target!(|data: &[u8]| { - split_assemble_hid_packets(data); + split_assemble_hid_packets(data).ok(); }); diff --git a/src/ctap/credential_management.rs b/src/ctap/credential_management.rs index 0d51a03..10bf954 100644 --- a/src/ctap/credential_management.rs +++ b/src/ctap/credential_management.rs @@ -364,7 +364,6 @@ mod test { use super::super::CtapState; use super::*; use crate::env::test::TestEnv; - use crate::env::Env; use crypto::rng256::Rng256; const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]); diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 02d2a52..9700cb0 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -726,10 +726,10 @@ mod test { use super::*; use crate::ctap::data_formats::{PublicKeyCredentialSource, PublicKeyCredentialType}; use crate::env::test::TestEnv; - use crypto::rng256::{Rng256, ThreadRng256}; + use crypto::rng256::Rng256; fn create_credential_source( - rng: &mut ThreadRng256, + rng: &mut impl Rng256, rp_id: &str, user_handle: Vec, ) -> PublicKeyCredentialSource { diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 0580b65..d96b0c8 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -4,21 +4,41 @@ use crate::api::firmware_protection::FirmwareProtection; use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::Channel; use crate::env::{Env, UserPresence}; -use crypto::rng256::ThreadRng256; +use crypto::rng256::Rng256; use customization::TestCustomization; use persistent_store::{BufferOptions, BufferStorage, Store}; +use rand::rngs::StdRng; +use rand::{Rng, SeedableRng}; mod customization; mod upgrade_storage; pub struct TestEnv { - rng: ThreadRng256, + rng: TestRng256, user_presence: TestUserPresence, store: Store, upgrade_storage: Option, customization: TestCustomization, } +pub struct TestRng256 { + rng: StdRng, +} + +impl TestRng256 { + pub fn seed_rng_from_u64(&mut self, state: u64) { + self.rng = StdRng::seed_from_u64(state); + } +} + +impl Rng256 for TestRng256 { + fn gen_uniform_u8x32(&mut self) -> [u8; 32] { + let mut result = [Default::default(); 32]; + self.rng.fill(&mut result); + result + } +} + pub struct TestUserPresence { check: Box Result<(), Ctap2StatusCode>>, } @@ -48,7 +68,9 @@ fn new_storage() -> BufferStorage { impl TestEnv { pub fn new() -> Self { - let rng = ThreadRng256 {}; + let rng = TestRng256 { + rng: StdRng::seed_from_u64(0), + }; let user_presence = TestUserPresence { check: Box::new(|_| Ok(())), }; @@ -72,6 +94,10 @@ impl TestEnv { pub fn customization_mut(&mut self) -> &mut TestCustomization { &mut self.customization } + + pub fn rng(&mut self) -> &mut TestRng256 { + &mut self.rng + } } impl TestUserPresence { @@ -93,7 +119,7 @@ impl FirmwareProtection for TestEnv { } impl Env for TestEnv { - type Rng = ThreadRng256; + type Rng = TestRng256; type UserPresence = TestUserPresence; type Storage = BufferStorage; type UpgradeStorage = BufferUpgradeStorage;