Merge pull request #441 from hcyang-google/fuzz
Generate valid structure for MakeCredential params
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
|
fuzz/artifacts
|
||||||
fuzz/corpus
|
fuzz/corpus
|
||||||
|
fuzz/coverage
|
||||||
target/
|
target/
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ subtle = { version = "2.2", default-features = false, features = ["nightly"] }
|
|||||||
# This import explicitly locks the version.
|
# This import explicitly locks the version.
|
||||||
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 }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
debug_allocations = ["lang_items/debug_allocations"]
|
debug_allocations = ["lang_items/debug_allocations"]
|
||||||
@@ -32,6 +33,7 @@ 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"]
|
||||||
vendor_hid = []
|
vendor_hid = []
|
||||||
|
fuzz = ["arbitrary", "std"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
enum-iterator = "0.6.0"
|
enum-iterator = "0.6.0"
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ path = "fuzz_targets/fuzz_target_process_ctap2_make_credential.rs"
|
|||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz_target_process_ctap2_make_credential_structured"
|
||||||
|
path = "fuzz_targets/fuzz_target_process_ctap2_make_credential_structured.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "fuzz_target_split_assemble"
|
name = "fuzz_target_split_assemble"
|
||||||
path = "fuzz_targets/fuzz_target_split_assemble.rs"
|
path = "fuzz_targets/fuzz_target_split_assemble.rs"
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ embedded-time = "0.12.1"
|
|||||||
libtock_drivers = { path = "../../third_party/libtock-drivers" }
|
libtock_drivers = { path = "../../third_party/libtock-drivers" }
|
||||||
crypto = { path = "../../libraries/crypto", features = ['std'] }
|
crypto = { path = "../../libraries/crypto", features = ['std'] }
|
||||||
sk-cbor = { path = "../../libraries/cbor" }
|
sk-cbor = { path = "../../libraries/cbor" }
|
||||||
ctap2 = { path = "../..", features = ['std'] }
|
ctap2 = { path = "../..", features = ["fuzz"] }
|
||||||
lang_items = { path = "../../third_party/lang-items", features = ['std'] }
|
lang_items = { path = "../../third_party/lang-items", features = ['std'] }
|
||||||
|
arbitrary = { version = "0.4.7", features = ["derive"] }
|
||||||
|
|||||||
@@ -16,17 +16,18 @@
|
|||||||
// `libtock_alloc_init` symbol.
|
// `libtock_alloc_init` symbol.
|
||||||
extern crate lang_items;
|
extern crate lang_items;
|
||||||
|
|
||||||
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use ctap2::clock::CtapInstant;
|
use ctap2::clock::CtapInstant;
|
||||||
use ctap2::ctap::cbor_read;
|
|
||||||
use ctap2::ctap::command::{
|
use ctap2::ctap::command::{
|
||||||
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
||||||
AuthenticatorMakeCredentialParameters,
|
AuthenticatorMakeCredentialParameters, Command,
|
||||||
};
|
};
|
||||||
use ctap2::ctap::hid::{
|
use ctap2::ctap::hid::{
|
||||||
ChannelID, CtapHidCommand, HidPacket, HidPacketIterator, Message, MessageAssembler,
|
ChannelID, CtapHidCommand, HidPacket, HidPacketIterator, Message, MessageAssembler,
|
||||||
};
|
};
|
||||||
|
use ctap2::ctap::{cbor_read, Channel, CtapState};
|
||||||
use ctap2::env::test::TestEnv;
|
use ctap2::env::test::TestEnv;
|
||||||
use ctap2::{Ctap, Transport};
|
use ctap2::{Ctap, Transport};
|
||||||
|
|
||||||
@@ -166,6 +167,39 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
|
|||||||
process_message(&command, &mut ctap);
|
process_message(&command, &mut ctap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> arbitrary::Result<()> {
|
||||||
|
let unstructured = &mut Unstructured::new(data);
|
||||||
|
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
let mut state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||||
|
|
||||||
|
let command = match input_type {
|
||||||
|
InputType::CborMakeCredentialParameter => Command::AuthenticatorMakeCredential(
|
||||||
|
AuthenticatorMakeCredentialParameters::arbitrary(unstructured)?,
|
||||||
|
),
|
||||||
|
InputType::CborGetAssertionParameter => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
InputType::CborClientPinParameter => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
InputType::Ctap1 => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state
|
||||||
|
.process_parsed_command(
|
||||||
|
&mut env,
|
||||||
|
command,
|
||||||
|
Channel::MainHid(ChannelID::arbitrary(unstructured)?),
|
||||||
|
CtapInstant::new(0),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// 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]) {
|
||||||
let message = raw_to_message(data);
|
let message = raw_to_message(data);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use fuzz_helper::{process_ctap_structured, InputType};
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
|
||||||
|
// Fuzz inputs as CTAP2 make credential command parameters.
|
||||||
|
// The inputs will used to construct arbitrary make credential parameters.
|
||||||
|
fuzz_target!(|data: &[u8]| {
|
||||||
|
process_ctap_structured(data, InputType::CborMakeCredentialParameter).ok();
|
||||||
|
});
|
||||||
@@ -225,6 +225,12 @@ impl From<&str> for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Value>> for Value {
|
||||||
|
fn from(array: Vec<Value>) -> Self {
|
||||||
|
Value::Array(array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Vec<(Value, Value)>> for Value {
|
impl From<Vec<(Value, Value)>> for Value {
|
||||||
fn from(map: Vec<(Value, Value)>) -> Self {
|
fn from(map: Vec<(Value, Value)>) -> Self {
|
||||||
Value::Map(map)
|
Value::Map(map)
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ use super::status_code::Ctap2StatusCode;
|
|||||||
use super::{cbor_read, key_material};
|
use super::{cbor_read, key_material};
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
#[cfg(feature = "fuzz")]
|
||||||
|
use arbitrary::Arbitrary;
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use sk_cbor as cbor;
|
use sk_cbor as cbor;
|
||||||
@@ -155,6 +157,7 @@ impl Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub struct AuthenticatorMakeCredentialParameters {
|
pub struct AuthenticatorMakeCredentialParameters {
|
||||||
pub client_data_hash: Vec<u8>,
|
pub client_data_hash: Vec<u8>,
|
||||||
pub rp: PublicKeyCredentialRpEntity,
|
pub rp: PublicKeyCredentialRpEntity,
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
#[cfg(feature = "fuzz")]
|
||||||
|
use arbitrary::Arbitrary;
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use crypto::{ecdh, ecdsa};
|
use crypto::{ecdh, ecdsa};
|
||||||
@@ -28,6 +30,7 @@ const ES256_ALGORITHM: i64 = -7;
|
|||||||
|
|
||||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrpentity
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrpentity
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub struct PublicKeyCredentialRpEntity {
|
pub struct PublicKeyCredentialRpEntity {
|
||||||
pub rp_id: String,
|
pub rp_id: String,
|
||||||
pub rp_name: Option<String>,
|
pub rp_name: Option<String>,
|
||||||
@@ -70,6 +73,7 @@ impl From<PublicKeyCredentialRpEntity> for cbor::Value {
|
|||||||
|
|
||||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialuserentity
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialuserentity
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub struct PublicKeyCredentialUserEntity {
|
pub struct PublicKeyCredentialUserEntity {
|
||||||
pub user_id: Vec<u8>,
|
pub user_id: Vec<u8>,
|
||||||
pub user_name: Option<String>,
|
pub user_name: Option<String>,
|
||||||
@@ -117,6 +121,7 @@ impl From<PublicKeyCredentialUserEntity> for cbor::Value {
|
|||||||
|
|
||||||
// https://www.w3.org/TR/webauthn/#enumdef-publickeycredentialtype
|
// https://www.w3.org/TR/webauthn/#enumdef-publickeycredentialtype
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub enum PublicKeyCredentialType {
|
pub enum PublicKeyCredentialType {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
// This is the default for all strings not covered above.
|
// This is the default for all strings not covered above.
|
||||||
@@ -149,6 +154,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialType {
|
|||||||
|
|
||||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialparameters
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialparameters
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub struct PublicKeyCredentialParameter {
|
pub struct PublicKeyCredentialParameter {
|
||||||
pub cred_type: PublicKeyCredentialType,
|
pub cred_type: PublicKeyCredentialType,
|
||||||
pub alg: SignatureAlgorithm,
|
pub alg: SignatureAlgorithm,
|
||||||
@@ -183,6 +189,7 @@ impl From<PublicKeyCredentialParameter> for cbor::Value {
|
|||||||
// https://www.w3.org/TR/webauthn/#enumdef-authenticatortransport
|
// https://www.w3.org/TR/webauthn/#enumdef-authenticatortransport
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(test, derive(IntoEnumIterator))]
|
#[cfg_attr(test, derive(IntoEnumIterator))]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub enum AuthenticatorTransport {
|
pub enum AuthenticatorTransport {
|
||||||
Usb,
|
Usb,
|
||||||
Nfc,
|
Nfc,
|
||||||
@@ -219,6 +226,7 @@ impl TryFrom<cbor::Value> for AuthenticatorTransport {
|
|||||||
|
|
||||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialdescriptor
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialdescriptor
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub struct PublicKeyCredentialDescriptor {
|
pub struct PublicKeyCredentialDescriptor {
|
||||||
pub key_type: PublicKeyCredentialType,
|
pub key_type: PublicKeyCredentialType,
|
||||||
pub key_id: Vec<u8>,
|
pub key_id: Vec<u8>,
|
||||||
@@ -270,6 +278,7 @@ impl From<PublicKeyCredentialDescriptor> for cbor::Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub struct MakeCredentialExtensions {
|
pub struct MakeCredentialExtensions {
|
||||||
pub hmac_secret: bool,
|
pub hmac_secret: bool,
|
||||||
pub cred_protect: Option<CredentialProtectionPolicy>,
|
pub cred_protect: Option<CredentialProtectionPolicy>,
|
||||||
@@ -388,6 +397,7 @@ impl TryFrom<cbor::Value> for GetAssertionHmacSecretInput {
|
|||||||
|
|
||||||
// Even though options are optional, we can use the default if not present.
|
// Even though options are optional, we can use the default if not present.
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub struct MakeCredentialOptions {
|
pub struct MakeCredentialOptions {
|
||||||
pub rk: bool,
|
pub rk: bool,
|
||||||
pub uv: bool,
|
pub uv: bool,
|
||||||
@@ -488,6 +498,7 @@ impl From<PackedAttestationStatement> for cbor::Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub enum SignatureAlgorithm {
|
pub enum SignatureAlgorithm {
|
||||||
ES256 = ES256_ALGORITHM as isize,
|
ES256 = ES256_ALGORITHM as isize,
|
||||||
// This is the default for all numbers not covered above.
|
// This is the default for all numbers not covered above.
|
||||||
@@ -516,6 +527,7 @@ impl TryFrom<cbor::Value> for SignatureAlgorithm {
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||||
#[cfg_attr(test, derive(IntoEnumIterator))]
|
#[cfg_attr(test, derive(IntoEnumIterator))]
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub enum CredentialProtectionPolicy {
|
pub enum CredentialProtectionPolicy {
|
||||||
/// The credential is always discoverable, as if it had no protection level.
|
/// The credential is always discoverable, as if it had no protection level.
|
||||||
UserVerificationOptional = 0x01,
|
UserVerificationOptional = 0x01,
|
||||||
@@ -884,6 +896,7 @@ impl TryFrom<CoseSignature> for ecdsa::Signature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||||
pub enum PinUvAuthProtocol {
|
pub enum PinUvAuthProtocol {
|
||||||
V1 = 1,
|
V1 = 1,
|
||||||
V2 = 2,
|
V2 = 2,
|
||||||
|
|||||||
@@ -532,7 +532,7 @@ impl CtapState {
|
|||||||
///
|
///
|
||||||
/// This function contains the logic of `parse_command`, minus all CBOR encoding and decoding.
|
/// This function contains the logic of `parse_command`, minus all CBOR encoding and decoding.
|
||||||
/// It should make command parsing easier to test.
|
/// It should make command parsing easier to test.
|
||||||
fn process_parsed_command(
|
pub fn process_parsed_command(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut impl Env,
|
||||||
command: Command,
|
command: Command,
|
||||||
|
|||||||
Reference in New Issue
Block a user