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:
@@ -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"]
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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
34
src/env/test/mod.rs
vendored
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user