diff --git a/Cargo.toml b/Cargo.toml index de151e1..ede3775 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,7 @@ rand = "0.8.4" rand_chacha = "0.3" sha2 = "0.10" serde_test = "1.0.176" -trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "ad577412599156ac98f29ee969e76537e506f2bc", features = ["virt"] } +trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "b2492feada78ee17b936c99c13df480a4fc6d2f5", features = ["virt"] } trussed-staging = { git = "https://github.com/trussed-dev/trussed-staging.git", tag = "v0.4.0", features = ["chunked", "hkdf", "virt", "fs-info"] } trussed-usbip = { git = "https://github.com/trussed-dev/pc-usbip-runner.git", rev = "017921df0930707c4af68882ccb1f8b3f1bbf7c5", default-features = false, features = ["ctaphid"] } usbd-ctaphid = "0.4" @@ -79,7 +79,7 @@ x509-parser = "0.16" features = ["chunked", "dispatch"] [patch.crates-io] -trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "ad577412599156ac98f29ee969e76537e506f2bc" } +trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "b2492feada78ee17b936c99c13df480a4fc6d2f5" } [profile.test] opt-level = 2 diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index b7153ab..6cf4efa 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -24,5 +24,5 @@ doc = false bench = false [patch.crates-io] -trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "ad577412599156ac98f29ee969e76537e506f2bc" } +trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "b2492feada78ee17b936c99c13df480a4fc6d2f5" } trussed-staging = { git = "https://github.com/trussed-dev/trussed-staging.git", tag = "v0.4.0" } diff --git a/tests/basic.rs b/tests/basic.rs index 48b941b..387ccea 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -16,13 +16,51 @@ use rand::RngCore as _; use fs::list_fs; use virt::{Ctap2, Ctap2Error, Options}; use webauthn::{ - exhaustive_struct, AttStmtFormat, AuthenticatorConfig, AuthenticatorConfigParams, ClientPin, - CredentialManagement, CredentialManagementParams, Exhaustive, GetAssertion, + exhaustive_struct, iter_map, AttStmtFormat, AuthenticatorConfig, AuthenticatorConfigParams, + ClientPin, CredentialManagement, CredentialManagementParams, Exhaustive, GetAssertion, GetAssertionExtensionsInput, GetAssertionOptions, GetInfo, GetNextAssertion, HmacSecretInput, KeyAgreementKey, MakeCredential, MakeCredentialExtensionsInput, MakeCredentialOptions, PinToken, PubKeyCredDescriptor, PubKeyCredParam, PublicKey, Rp, SharedSecret, Test, User, }; +macro_rules! run_tests { + ($test:ident { + $(exhaustive = [ + $($exh_field:ident: $exh_type:ty,)* + ],)? + $(iter = [ + $($iter_field:ident: $iter:expr,)* + ],)? + $(random = [ + $($random_field:ident: $random_type:ty,)* + ],)? + $(fixed = [ + $($fixed_field:ident: $fixed_value:expr,)* + ],)? + }) => {{ + $( + let mut rng = rand::thread_rng(); + $( + let $random_field: Vec<$random_type> = <$random_type as Exhaustive>::iter_exhaustive().collect(); + )* + )? + let tests = iter_map! { + [ + $($($exh_field: <$exh_type as Exhaustive>::iter_exhaustive(),)*)? + $($($iter_field: $iter,)*)? + ] => $test { + $($($exh_field,)*)? + $($($iter_field,)*)? + $($($fixed_field: $fixed_value,)*)? + $($($random_field: *::rand::seq::SliceRandom::choose($random_field.as_slice(), &mut rng).unwrap(),)*)? + } + }; + for test in tests { + test.run(); + } + }} +} + #[test] fn test_ping() { virt::run_ctaphid(|device| { @@ -551,6 +589,18 @@ enum PinAuth { PinToken(RequestPinToken), } +impl PinAuth { + fn iter_valid() -> impl Iterator + Clone { + [ + Self::NoPin, + Self::PinNoToken, + Self::PinToken(RequestPinToken::ValidRpId), + Self::PinToken(RequestPinToken::NoRpId), + ] + .into_iter() + } +} + impl Exhaustive for PinAuth { fn iter_exhaustive() -> impl Iterator + Clone { [Self::NoPin, Self::PinNoToken] @@ -707,21 +757,107 @@ impl Test for TestMakeCredential { } } -impl Exhaustive for TestMakeCredential { - fn iter_exhaustive() -> impl Iterator + Clone { - exhaustive_struct! { - pin_auth: PinAuth, - options: Option, - valid_pub_key_alg: bool, - attestation_formats_preference: Option, - hmac_secret: bool, +#[test] +fn test_make_credential_pub_key_alg() { + run_tests! { + TestMakeCredential { + exhaustive = [ + valid_pub_key_alg: bool, + ], + iter = [ + pin_auth: PinAuth::iter_valid(), + options: MakeCredentialOptions::iter_valid().map(Some), + ], + random = [ + attestation_formats_preference: Option, + hmac_secret: bool, + ], } } } #[test] -fn test_make_credential() { - TestMakeCredential::run_all(); +fn test_make_credential_hmac_secret() { + run_tests! { + TestMakeCredential { + exhaustive = [ + hmac_secret: bool, + ], + iter = [ + pin_auth: PinAuth::iter_valid(), + options: MakeCredentialOptions::iter_valid().map(Some), + ], + random = [ + attestation_formats_preference: Option, + ], + fixed = [ + valid_pub_key_alg: true, + ], + } + } +} + +#[test] +fn test_make_credential_pin_auth() { + run_tests! { + TestMakeCredential { + exhaustive = [ + pin_auth: PinAuth, + ], + iter = [ + options: MakeCredentialOptions::iter_valid().map(Some), + ], + random = [ + attestation_formats_preference: Option, + hmac_secret: bool, + ], + fixed = [ + valid_pub_key_alg: true, + ], + } + } +} + +#[test] +fn test_make_credential_options() { + run_tests! { + TestMakeCredential { + exhaustive = [ + options: Option, + ], + iter = [ + pin_auth: PinAuth::iter_valid(), + ], + random = [ + attestation_formats_preference: Option, + hmac_secret: bool, + ], + fixed = [ + valid_pub_key_alg: true, + ], + } + } +} + +#[test] +fn test_make_credential_attestation_formats_preference() { + run_tests! { + TestMakeCredential { + exhaustive = [ + attestation_formats_preference: Option, + ], + iter = [ + pin_auth: PinAuth::iter_valid(), + options: MakeCredentialOptions::iter_valid().map(Some), + ], + random = [ + hmac_secret: bool, + ], + fixed = [ + valid_pub_key_alg: true, + ], + } + } } #[derive(Clone, Copy, Debug, Default)] @@ -922,8 +1058,85 @@ impl Exhaustive for TestGetAssertion { } #[test] -fn test_get_assertion() { - TestGetAssertion::run_all(); +fn test_get_assertion_options() { + run_tests! { + TestGetAssertion { + exhaustive = [ + rk: bool, + allow_list: bool, + options: Option, + ], + random = [ + mc_extensions: Option, + ga_hmac_secret: bool, + ga_third_party_payment: Option, + ga_cred_blob: bool, + ], + } + } +} + +#[test] +fn test_get_assertion_hmac_secret() { + run_tests! { + TestGetAssertion { + exhaustive = [ + rk: bool, + allow_list: bool, + mc_extensions: Option, + ga_hmac_secret: bool, + ], + iter = [ + options: GetAssertionOptions::iter_valid().map(Some), + ], + random = [ + ga_third_party_payment: Option, + ga_cred_blob: bool, + ], + } + } +} + +#[test] +fn test_get_assertion_third_party_payment() { + run_tests! { + TestGetAssertion { + exhaustive = [ + rk: bool, + allow_list: bool, + mc_extensions: Option, + ga_third_party_payment: Option, + ], + iter = [ + options: GetAssertionOptions::iter_valid().map(Some), + ], + random = [ + ga_hmac_secret: bool, + ga_cred_blob: bool, + ], + } + } +} + +#[test] +fn test_get_assertion_cred_blob() { + run_tests! { + TestGetAssertion { + exhaustive = [ + rk: bool, + allow_list: bool, + mc_extensions: Option, + ga_cred_blob: bool, + ], + iter = [ + options: GetAssertionOptions::iter_valid().map(Some), + ], + random = [ + ga_hmac_secret: bool, + ga_third_party_payment: Option, + ], + } + } } fn run_test_get_next_assertion(device: &Ctap2) { @@ -1155,18 +1368,16 @@ impl Test for TestListCredentials { } } -impl Exhaustive for TestListCredentials { - fn iter_exhaustive() -> impl Iterator + Clone { - exhaustive_struct! { - pin_token_rp_id: bool, - third_party_payment: Option, - } - } -} - #[test] fn test_list_credentials() { - TestListCredentials::run_all(); + run_tests! { + TestListCredentials { + exhaustive = [ + pin_token_rp_id: bool, + third_party_payment: Option, + ], + } + } } // ============================================================================ diff --git a/tests/webauthn/mod.rs b/tests/webauthn/mod.rs index 0666afe..9b3d67c 100644 --- a/tests/webauthn/mod.rs +++ b/tests/webauthn/mod.rs @@ -23,12 +23,24 @@ impl Exhaustive for Option { } } +macro_rules! iter_map { + ([ $($name:ident: $iter:expr,)* ] => $map:expr) => { + ::itertools::iproduct!( + $($iter,)* + ).map(|($($name,)*)| { $map }) + } +} + +#[allow(unused_imports)] +pub(crate) use iter_map; + macro_rules! exhaustive_struct { ($($field:ident: $type:ty,)*) => { - ::itertools::iproduct!( - $(<$type as Exhaustive>::iter_exhaustive(),)* - ) - .map(|($($field,)*)| Self { $($field,)* }) + $crate::webauthn::iter_map! { + [ + $($field: <$type as Exhaustive>::iter_exhaustive(),)* + ] => Self { $($field,)* } + } } } @@ -537,6 +549,21 @@ impl MakeCredentialOptions { } } +impl MakeCredentialOptions { + pub fn iter_valid() -> impl Iterator + Clone { + iter_map! { + [ + rk: [true, false], + uv: [true, false], + ] => Self { + rk: rk.then_some(true), + up: None, + uv: uv.then_some(true), + } + } + } +} + impl From for Value { fn from(options: MakeCredentialOptions) -> Value { let mut map = Map::default(); @@ -777,6 +804,18 @@ impl GetAssertionOptions { self.uv = Some(uv); self } + + pub fn iter_valid() -> impl Iterator + Clone { + iter_map! { + [ + up: [true, false], + uv: [true, false], + ] => Self { + up: Some(up), + uv: uv.then_some(true), + } + } + } } impl From for Value {