Generate valid structure for MakeCredential params
* Add crate arbitrary as ctap's optional dependency, when feature "fuzz" is activated. * Derive Arbitrary for all the necessary types in order to generate the concrete types from random bytes. * Add a fuzz target that transforms the input to valid format for MakeCredential.
This commit is contained in:
@@ -22,6 +22,7 @@ subtle = { version = "2.2", default-features = false, features = ["nightly"] }
|
||||
# This import explicitly locks the version.
|
||||
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 }
|
||||
|
||||
[features]
|
||||
debug_allocations = ["lang_items/debug_allocations"]
|
||||
@@ -32,6 +33,7 @@ verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
|
||||
with_ctap1 = ["crypto/with_ctap1"]
|
||||
with_nfc = ["libtock_drivers/with_nfc"]
|
||||
vendor_hid = []
|
||||
fuzz = ["arbitrary", "std"]
|
||||
|
||||
[dev-dependencies]
|
||||
enum-iterator = "0.6.0"
|
||||
|
||||
@@ -48,6 +48,12 @@ path = "fuzz_targets/fuzz_target_process_ctap2_make_credential.rs"
|
||||
test = 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]]
|
||||
name = "fuzz_target_split_assemble"
|
||||
path = "fuzz_targets/fuzz_target_split_assemble.rs"
|
||||
|
||||
@@ -11,5 +11,6 @@ embedded-time = "0.12.1"
|
||||
libtock_drivers = { path = "../../third_party/libtock-drivers" }
|
||||
crypto = { path = "../../libraries/crypto", features = ['std'] }
|
||||
sk-cbor = { path = "../../libraries/cbor" }
|
||||
ctap2 = { path = "../..", features = ['std'] }
|
||||
ctap2 = { path = "../..", features = ["fuzz"] }
|
||||
lang_items = { path = "../../third_party/lang-items", features = ['std'] }
|
||||
arbitrary = { version = "0.4.7", features = ["derive"] }
|
||||
|
||||
@@ -16,17 +16,18 @@
|
||||
// `libtock_alloc_init` symbol.
|
||||
extern crate lang_items;
|
||||
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use arrayref::array_ref;
|
||||
use core::convert::TryFrom;
|
||||
use ctap2::clock::CtapInstant;
|
||||
use ctap2::ctap::cbor_read;
|
||||
use ctap2::ctap::command::{
|
||||
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
||||
AuthenticatorMakeCredentialParameters,
|
||||
AuthenticatorMakeCredentialParameters, Command,
|
||||
};
|
||||
use ctap2::ctap::hid::{
|
||||
ChannelID, CtapHidCommand, HidPacket, HidPacketIterator, Message, MessageAssembler,
|
||||
};
|
||||
use ctap2::ctap::{cbor_read, Channel, CtapState};
|
||||
use ctap2::env::test::TestEnv;
|
||||
use ctap2::{Ctap, Transport};
|
||||
|
||||
@@ -166,6 +167,39 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
|
||||
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.
|
||||
pub fn split_assemble_hid_packets(data: &[u8]) {
|
||||
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 {
|
||||
fn from(map: Vec<(Value, Value)>) -> Self {
|
||||
Value::Map(map)
|
||||
|
||||
@@ -26,6 +26,8 @@ use super::status_code::Ctap2StatusCode;
|
||||
use super::{cbor_read, key_material};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
#[cfg(feature = "fuzz")]
|
||||
use arbitrary::Arbitrary;
|
||||
use arrayref::array_ref;
|
||||
use core::convert::TryFrom;
|
||||
use sk_cbor as cbor;
|
||||
@@ -155,6 +157,7 @@ impl Command {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub struct AuthenticatorMakeCredentialParameters {
|
||||
pub client_data_hash: Vec<u8>,
|
||||
pub rp: PublicKeyCredentialRpEntity,
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
#[cfg(feature = "fuzz")]
|
||||
use arbitrary::Arbitrary;
|
||||
use arrayref::array_ref;
|
||||
use core::convert::TryFrom;
|
||||
use crypto::{ecdh, ecdsa};
|
||||
@@ -28,6 +30,7 @@ const ES256_ALGORITHM: i64 = -7;
|
||||
|
||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrpentity
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub struct PublicKeyCredentialRpEntity {
|
||||
pub rp_id: String,
|
||||
pub rp_name: Option<String>,
|
||||
@@ -70,6 +73,7 @@ impl From<PublicKeyCredentialRpEntity> for cbor::Value {
|
||||
|
||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialuserentity
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub struct PublicKeyCredentialUserEntity {
|
||||
pub user_id: Vec<u8>,
|
||||
pub user_name: Option<String>,
|
||||
@@ -117,6 +121,7 @@ impl From<PublicKeyCredentialUserEntity> for cbor::Value {
|
||||
|
||||
// https://www.w3.org/TR/webauthn/#enumdef-publickeycredentialtype
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub enum PublicKeyCredentialType {
|
||||
PublicKey,
|
||||
// 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
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub struct PublicKeyCredentialParameter {
|
||||
pub cred_type: PublicKeyCredentialType,
|
||||
pub alg: SignatureAlgorithm,
|
||||
@@ -183,6 +189,7 @@ impl From<PublicKeyCredentialParameter> for cbor::Value {
|
||||
// https://www.w3.org/TR/webauthn/#enumdef-authenticatortransport
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(test, derive(IntoEnumIterator))]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub enum AuthenticatorTransport {
|
||||
Usb,
|
||||
Nfc,
|
||||
@@ -219,6 +226,7 @@ impl TryFrom<cbor::Value> for AuthenticatorTransport {
|
||||
|
||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialdescriptor
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub struct PublicKeyCredentialDescriptor {
|
||||
pub key_type: PublicKeyCredentialType,
|
||||
pub key_id: Vec<u8>,
|
||||
@@ -270,6 +278,7 @@ impl From<PublicKeyCredentialDescriptor> for cbor::Value {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub struct MakeCredentialExtensions {
|
||||
pub hmac_secret: bool,
|
||||
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.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub struct MakeCredentialOptions {
|
||||
pub rk: bool,
|
||||
pub uv: bool,
|
||||
@@ -488,6 +498,7 @@ impl From<PackedAttestationStatement> for cbor::Value {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub enum SignatureAlgorithm {
|
||||
ES256 = ES256_ALGORITHM as isize,
|
||||
// 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)]
|
||||
#[cfg_attr(test, derive(IntoEnumIterator))]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub enum CredentialProtectionPolicy {
|
||||
/// The credential is always discoverable, as if it had no protection level.
|
||||
UserVerificationOptional = 0x01,
|
||||
@@ -884,6 +896,7 @@ impl TryFrom<CoseSignature> for ecdsa::Signature {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub enum PinUvAuthProtocol {
|
||||
V1 = 1,
|
||||
V2 = 2,
|
||||
|
||||
@@ -532,7 +532,7 @@ impl CtapState {
|
||||
///
|
||||
/// This function contains the logic of `parse_command`, minus all CBOR encoding and decoding.
|
||||
/// It should make command parsing easier to test.
|
||||
fn process_parsed_command(
|
||||
pub fn process_parsed_command(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
command: Command,
|
||||
|
||||
Reference in New Issue
Block a user