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<String> 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 <cretin@google.com>
This commit is contained in:
hcyang
2022-04-20 15:49:17 +08:00
committed by GitHub
parent 1e123ab3c3
commit aca1f35170
11 changed files with 71 additions and 20 deletions

View File

@@ -23,12 +23,13 @@ subtle = { version = "2.2", default-features = false, features = ["nightly"] }
serde_json = { version = "=1.0.69", default-features = false, features = ["alloc"] } serde_json = { version = "=1.0.69", default-features = false, features = ["alloc"] }
embedded-time = "0.12.1" embedded-time = "0.12.1"
arbitrary = { version = "0.4.7", features = ["derive"], optional = true } arbitrary = { version = "0.4.7", features = ["derive"], optional = true }
rand = { version = "0.8.4", optional = true }
[features] [features]
debug_allocations = ["lang_items/debug_allocations"] debug_allocations = ["lang_items/debug_allocations"]
debug_ctap = ["libtock_drivers/debug_ctap"] debug_ctap = ["libtock_drivers/debug_ctap"]
panic_console = ["lang_items/panic_console"] 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"] verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
with_ctap1 = ["crypto/with_ctap1"] with_ctap1 = ["crypto/with_ctap1"]
with_nfc = ["libtock_drivers/with_nfc"] with_nfc = ["libtock_drivers/with_nfc"]

View File

@@ -128,25 +128,40 @@ fn process_message(data: &[u8], ctap: &mut Ctap<TestEnv>) {
// Interprets the raw data as any ctap command (including the command byte) and // 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 // invokes message splitting, packet processing at CTAP HID level and response assembling
// using an initialized and allocated channel. // 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. // 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); let cid = initialize(&mut ctap);
// Wrap input as message with the allocated cid. // Wrap input as message with the allocated cid.
let mut command = cid.to_vec(); let mut command = cid.to_vec();
command.extend(data); command.extend(data);
process_message(&command, &mut ctap); process_message(&command, &mut ctap);
Ok(())
} }
// Interprets the raw data as of the given input type and // Interprets the raw data as of the given input type and
// invokes message splitting, packet processing at CTAP HID level and response assembling // invokes message splitting, packet processing at CTAP HID level and response assembling
// using an initialized and allocated channel. // 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) { if !is_type(data, input_type) {
return; return Ok(());
} }
// Initialize ctap state and hid and get the allocated cid. // 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); let cid = initialize(&mut ctap);
// Wrap input as message with allocated cid and command type. // Wrap input as message with allocated cid and command type.
let mut command = cid.to_vec(); let mut command = cid.to_vec();
@@ -166,12 +181,15 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
} }
command.extend(data); command.extend(data);
process_message(&command, &mut ctap); process_message(&command, &mut ctap);
Ok(())
} }
pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> arbitrary::Result<()> { pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> arbitrary::Result<()> {
let unstructured = &mut Unstructured::new(data); let unstructured = &mut Unstructured::new(data);
let mut env = TestEnv::new(); 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 mut state = CtapState::new(&mut env, CtapInstant::new(0));
let command = match input_type { 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. // 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(); 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); let message = raw_to_message(data);
if let Some(hid_packet_iterator) = HidPacketIterator::new(message.clone()) { if let Some(hid_packet_iterator) = HidPacketIterator::new(message.clone()) {
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
@@ -221,4 +245,5 @@ pub fn split_assemble_hid_packets(data: &[u8]) {
); );
} }
} }
Ok(())
} }

View File

@@ -7,5 +7,5 @@ use libfuzzer_sys::fuzz_target;
// For a more generic fuzz target including all CTAP commands, you can use // For a more generic fuzz target including all CTAP commands, you can use
// fuzz_target_process_ctap_command. // fuzz_target_process_ctap_command.
fuzz_target!(|data: &[u8]| { fuzz_target!(|data: &[u8]| {
process_ctap_specific_type(data, InputType::Ctap1); process_ctap_specific_type(data, InputType::Ctap1).ok();
}); });

View File

@@ -7,5 +7,5 @@ use libfuzzer_sys::fuzz_target;
// For a more generic fuzz target including all CTAP commands, you can use // For a more generic fuzz target including all CTAP commands, you can use
// fuzz_target_process_ctap_command. // fuzz_target_process_ctap_command.
fuzz_target!(|data: &[u8]| { fuzz_target!(|data: &[u8]| {
process_ctap_specific_type(data, InputType::CborClientPinParameter); process_ctap_specific_type(data, InputType::CborClientPinParameter).ok();
}); });

View File

@@ -7,5 +7,5 @@ use libfuzzer_sys::fuzz_target;
// For a more generic fuzz target including all CTAP commands, you can use // For a more generic fuzz target including all CTAP commands, you can use
// fuzz_target_process_ctap_command. // fuzz_target_process_ctap_command.
fuzz_target!(|data: &[u8]| { fuzz_target!(|data: &[u8]| {
process_ctap_specific_type(data, InputType::CborGetAssertionParameter); process_ctap_specific_type(data, InputType::CborGetAssertionParameter).ok();
}); });

View File

@@ -7,5 +7,5 @@ use libfuzzer_sys::fuzz_target;
// For a more generic fuzz target including all CTAP commands, you can use // For a more generic fuzz target including all CTAP commands, you can use
// fuzz_target_process_ctap_command. // fuzz_target_process_ctap_command.
fuzz_target!(|data: &[u8]| { fuzz_target!(|data: &[u8]| {
process_ctap_specific_type(data, InputType::CborMakeCredentialParameter); process_ctap_specific_type(data, InputType::CborMakeCredentialParameter).ok();
}); });

View File

@@ -5,5 +5,5 @@ use libfuzzer_sys::fuzz_target;
// Generically fuzz inputs as CTAP commands. // Generically fuzz inputs as CTAP commands.
fuzz_target!(|data: &[u8]| { fuzz_target!(|data: &[u8]| {
process_ctap_any_type(data); process_ctap_any_type(data).ok();
}); });

View File

@@ -5,5 +5,5 @@ use libfuzzer_sys::fuzz_target;
// Fuzzing HID packets splitting and assembling functions. // Fuzzing HID packets splitting and assembling functions.
fuzz_target!(|data: &[u8]| { fuzz_target!(|data: &[u8]| {
split_assemble_hid_packets(data); split_assemble_hid_packets(data).ok();
}); });

View File

@@ -364,7 +364,6 @@ mod test {
use super::super::CtapState; use super::super::CtapState;
use super::*; use super::*;
use crate::env::test::TestEnv; use crate::env::test::TestEnv;
use crate::env::Env;
use crypto::rng256::Rng256; use crypto::rng256::Rng256;
const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]); const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]);

View File

@@ -726,10 +726,10 @@ mod test {
use super::*; use super::*;
use crate::ctap::data_formats::{PublicKeyCredentialSource, PublicKeyCredentialType}; use crate::ctap::data_formats::{PublicKeyCredentialSource, PublicKeyCredentialType};
use crate::env::test::TestEnv; use crate::env::test::TestEnv;
use crypto::rng256::{Rng256, ThreadRng256}; use crypto::rng256::Rng256;
fn create_credential_source( fn create_credential_source(
rng: &mut ThreadRng256, rng: &mut impl Rng256,
rp_id: &str, rp_id: &str,
user_handle: Vec<u8>, user_handle: Vec<u8>,
) -> PublicKeyCredentialSource { ) -> PublicKeyCredentialSource {

34
src/env/test/mod.rs vendored
View File

@@ -4,21 +4,41 @@ use crate::api::firmware_protection::FirmwareProtection;
use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::status_code::Ctap2StatusCode;
use crate::ctap::Channel; use crate::ctap::Channel;
use crate::env::{Env, UserPresence}; use crate::env::{Env, UserPresence};
use crypto::rng256::ThreadRng256; use crypto::rng256::Rng256;
use customization::TestCustomization; use customization::TestCustomization;
use persistent_store::{BufferOptions, BufferStorage, Store}; use persistent_store::{BufferOptions, BufferStorage, Store};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
mod customization; mod customization;
mod upgrade_storage; mod upgrade_storage;
pub struct TestEnv { pub struct TestEnv {
rng: ThreadRng256, rng: TestRng256,
user_presence: TestUserPresence, user_presence: TestUserPresence,
store: Store<BufferStorage>, store: Store<BufferStorage>,
upgrade_storage: Option<BufferUpgradeStorage>, upgrade_storage: Option<BufferUpgradeStorage>,
customization: TestCustomization, 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 { pub struct TestUserPresence {
check: Box<dyn Fn(Channel) -> Result<(), Ctap2StatusCode>>, check: Box<dyn Fn(Channel) -> Result<(), Ctap2StatusCode>>,
} }
@@ -48,7 +68,9 @@ fn new_storage() -> BufferStorage {
impl TestEnv { impl TestEnv {
pub fn new() -> Self { pub fn new() -> Self {
let rng = ThreadRng256 {}; let rng = TestRng256 {
rng: StdRng::seed_from_u64(0),
};
let user_presence = TestUserPresence { let user_presence = TestUserPresence {
check: Box::new(|_| Ok(())), check: Box::new(|_| Ok(())),
}; };
@@ -72,6 +94,10 @@ impl TestEnv {
pub fn customization_mut(&mut self) -> &mut TestCustomization { pub fn customization_mut(&mut self) -> &mut TestCustomization {
&mut self.customization &mut self.customization
} }
pub fn rng(&mut self) -> &mut TestRng256 {
&mut self.rng
}
} }
impl TestUserPresence { impl TestUserPresence {
@@ -93,7 +119,7 @@ impl FirmwareProtection for TestEnv {
} }
impl Env for TestEnv { impl Env for TestEnv {
type Rng = ThreadRng256; type Rng = TestRng256;
type UserPresence = TestUserPresence; type UserPresence = TestUserPresence;
type Storage = BufferStorage; type Storage = BufferStorage;
type UpgradeStorage = BufferUpgradeStorage; type UpgradeStorage = BufferUpgradeStorage;