diff --git a/reproducible/binaries.sha256sum b/reproducible/binaries.sha256sum deleted file mode 100644 index 6858153d17e1a2491fa968c67cbef248393acce8..0000000000000000000000000000000000000000 --- a/reproducible/binaries.sha256sum +++ /dev/null @@ -1,9 +0,0 @@ -c182bb4902fff51b2f56810fc2a27df3646cd66ba21359162354d53445623ab8 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin -2c2879a0263ebaa6e841db4b352346cc5b4cef5084ce85525cf49669d3b0b41d target/nrf52840dk_merged.hex -0a9929ba8fa57e8a502a49fc7c53177397202e1b11f4c7c3cb6ed68b2b99dd46 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin -652824a90674dc3199070f2f46a791ab4951e367982ecae6f65fc41338a5a856 target/nrf52840_dongle_merged.hex -cca9086c9149c607589b23ffa599a5e4c26db7c20bd3700b79528bd3a5df991d third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin -a030505f5576129954a3977f97957b8b4e023b2b51a29d45d4511566458666ac target/nrf52840_dongle_dfu_merged.hex -8857488ba6a69e366f0da229bbfc012a2ad291d3a88d9494247d600c10bb19b7 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin -82ac4290967ae67a78c986444fd6c2a2aa5668ac254a2af642c98be4a064f913 target/nrf52840_mdk_dfu_merged.hex -c3e901a80fd779b15a6f266e48dcef5140b318f5f62b23a96547498572ac1666 target/tab/ctap2.tab diff --git a/reproducible/reference_binaries_macos-10.15.sha256sum b/reproducible/reference_binaries_macos-10.15.sha256sum index bb62ef986f45ea6808cba8e72debc1bda67b1561..0228740f016945649827a653187b83a37e147d99 100644 --- a/reproducible/reference_binaries_macos-10.15.sha256sum +++ b/reproducible/reference_binaries_macos-10.15.sha256sum @@ -1,9 +1,9 @@ 1003863864e06553e730eec6df4bf8d30c99f697ef9380efdc35eba679b4db78 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin -63dda5b708add5ac72db0c0757de451f95173b0fc7d71fc85c3b25460850e23c target/nrf52840dk_merged.hex +022268c93fa8bbd9e54e082982b87c10a0e7c0486704de8219d1bb374304636a target/nrf52840dk_merged.hex 88f00a5e1dae6ab3f7571c254ac75f5f3e29ebea7f3ca46c16cfdc3708e804fc third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin -2fca47df0053c6750d9f8fe61e8e6a0e9eee2457c908aa1c2225e2aab2690cb4 target/nrf52840_dongle_merged.hex +8d68ecc700527789b8edf318f0872ca8fc3b72fa73236f4e06bec89a3682fcf8 target/nrf52840_dongle_merged.hex 1bc69b48a2c48da55db8b322902e1fe3f2e095c0dd8517db28837d86e0addc85 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin -33b5a96bb427de653440cd8bfa31e518e1a2c269368a6cd103602d2fd6f3e127 target/nrf52840_dongle_dfu_merged.hex +af5465e4209914aaf74ee878d03e883a717827119e47b9295aa279ee21f0c5f4 target/nrf52840_dongle_dfu_merged.hex f38ee31d3a09e7e11848e78b5318f95517b6dcd076afcb37e6e3d3e5e9995cc7 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin -dce8f1f02e2f7e93634b1edd02343547e139445d23c2d17ce6ccdc4895a67c8c target/nrf52840_mdk_dfu_merged.hex -1e216599d58e6c66845845a2ec709f72842e21d48a2185022223d1ffe006de07 target/tab/ctap2.tab +23603386a615e4e8cb2173c5ce4762110e6cbb979efdbb6e8bef9bc1e3988de4 target/nrf52840_mdk_dfu_merged.hex +c2cbcc28b835934be4c3d3e3c5bdaba642a5811d760c1d2cb73d26b6474e4219 target/tab/ctap2.tab diff --git a/reproducible/reference_binaries_ubuntu-18.04.sha256sum b/reproducible/reference_binaries_ubuntu-18.04.sha256sum index d57d20c51f43956b184aadded1a64dc5396e2a2e..666eecd0eb3f10daa9893fe13acb5ccb7bed6970 100644 --- a/reproducible/reference_binaries_ubuntu-18.04.sha256sum +++ b/reproducible/reference_binaries_ubuntu-18.04.sha256sum @@ -1,9 +1,9 @@ c182bb4902fff51b2f56810fc2a27df3646cd66ba21359162354d53445623ab8 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin -d978bbb7d56e54f1d8dc43a8b73e01ede50f404f8640ed8969112acac9efa36e target/nrf52840dk_merged.hex +d8b62ece387a77cc21f2c10a5f5d65d0d57bf4739b47fd86d2c9ecdd90fbfd7e target/nrf52840dk_merged.hex 0a9929ba8fa57e8a502a49fc7c53177397202e1b11f4c7c3cb6ed68b2b99dd46 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin -53f3f390fce878d4d6108d34a0f4f305375ca125a02a4160839eeee3acd55e88 target/nrf52840_dongle_merged.hex +380de1a910b4d9eeb0c814b11b074b2e66334968cc99a4bd34d52a1fce3c5a79 target/nrf52840_dongle_merged.hex cca9086c9149c607589b23ffa599a5e4c26db7c20bd3700b79528bd3a5df991d third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin -68ef4c5d2e5b53761c31e185fa6237eaaa13736f8e0657c68d235f27497efe85 target/nrf52840_dongle_dfu_merged.hex +4edd988b3e37991f1e58fc520e41f7666f8ae3e8d3993e1bb2fb71657a71fa50 target/nrf52840_dongle_dfu_merged.hex 8857488ba6a69e366f0da229bbfc012a2ad291d3a88d9494247d600c10bb19b7 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin -898842aeaf9a7b03f3f5828871fc8a41fff8fc550683c18f9cfd718f8da26ba4 target/nrf52840_mdk_dfu_merged.hex -d7d8e13bd8a183a6868a463512a999e1c3843d9d5b13f1d35909bfa80d24619e target/tab/ctap2.tab +a51aba1cd12e55aa33fd9017af406583ebf14e1c690295b15cf147713dfe2561 target/nrf52840_mdk_dfu_merged.hex +40b413a8b645b4b47fae62a4311acb12cb0c57faff2757e45c18d9e5d441e52d target/tab/ctap2.tab diff --git a/reproducible/reproduced.tar b/reproducible/reproduced.tar deleted file mode 100644 index 96616b5374f432ffd0eec49b727128ee17c52d4b..0000000000000000000000000000000000000000 Binary files a/reproducible/reproduced.tar and /dev/null differ diff --git a/src/ctap/command.rs b/src/ctap/command.rs index 19132bc9d1363aea7ff2839da3a9f86b6726135e..d6dd0fab10085747eff73e306ff61e49c1192f8e 100644 --- a/src/ctap/command.rs +++ b/src/ctap/command.rs @@ -13,10 +13,10 @@ // limitations under the License. use super::data_formats::{ - ok_or_missing, read_array, read_byte_string, read_map, read_text_string, read_unsigned, - ClientPinSubCommand, CoseKey, Extensions, GetAssertionOptions, MakeCredentialOptions, - PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity, - PublicKeyCredentialUserEntity, + extract_array, extract_byte_string, extract_map, extract_text_string, extract_unsigned, + ok_or_missing, ClientPinSubCommand, CoseKey, GetAssertionExtensions, GetAssertionOptions, + MakeCredentialExtensions, MakeCredentialOptions, PublicKeyCredentialDescriptor, + PublicKeyCredentialParameter, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, }; use super::status_code::Ctap2StatusCode; use alloc::string::String; @@ -113,7 +113,7 @@ pub struct AuthenticatorMakeCredentialParameters { pub user: PublicKeyCredentialUserEntity, pub pub_key_cred_params: Vec<PublicKeyCredentialParameter>, pub exclude_list: Option<Vec<PublicKeyCredentialDescriptor>>, - pub extensions: Option<Extensions>, + pub extensions: Option<MakeCredentialExtensions>, // Even though options are optional, we can use the default if not present. pub options: MakeCredentialOptions, pub pin_uv_auth_param: Option<Vec<u8>>, @@ -124,30 +124,32 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters { type Error = Ctap2StatusCode; fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { - let param_map = read_map(&cbor_value)?; + let mut param_map = extract_map(cbor_value)?; - let client_data_hash = read_byte_string(ok_or_missing(param_map.get(&cbor_unsigned!(1)))?)?; + let client_data_hash = + extract_byte_string(ok_or_missing(param_map.remove(&cbor_unsigned!(1)))?)?; let rp = PublicKeyCredentialRpEntity::try_from(ok_or_missing( - param_map.get(&cbor_unsigned!(2)), + param_map.remove(&cbor_unsigned!(2)), )?)?; let user = PublicKeyCredentialUserEntity::try_from(ok_or_missing( - param_map.get(&cbor_unsigned!(3)), + param_map.remove(&cbor_unsigned!(3)), )?)?; - let cred_param_vec = read_array(ok_or_missing(param_map.get(&cbor_unsigned!(4)))?)?; + let cred_param_vec = extract_array(ok_or_missing(param_map.remove(&cbor_unsigned!(4)))?)?; let pub_key_cred_params = cred_param_vec - .iter() + .into_iter() .map(PublicKeyCredentialParameter::try_from) .collect::<Result<Vec<PublicKeyCredentialParameter>, Ctap2StatusCode>>()?; - let exclude_list = match param_map.get(&cbor_unsigned!(5)) { + let exclude_list = match param_map.remove(&cbor_unsigned!(5)) { Some(entry) => { - let exclude_list_vec = read_array(entry)?; + let exclude_list_vec = extract_array(entry)?; + let list_len = MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(exclude_list_vec.len()); let exclude_list = exclude_list_vec - .iter() - .take(MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(exclude_list_vec.len())) + .into_iter() + .take(list_len) .map(PublicKeyCredentialDescriptor::try_from) .collect::<Result<Vec<PublicKeyCredentialDescriptor>, Ctap2StatusCode>>()?; Some(exclude_list) @@ -156,11 +158,11 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters { }; let extensions = param_map - .get(&cbor_unsigned!(6)) - .map(Extensions::try_from) + .remove(&cbor_unsigned!(6)) + .map(MakeCredentialExtensions::try_from) .transpose()?; - let options = match param_map.get(&cbor_unsigned!(7)) { + let options = match param_map.remove(&cbor_unsigned!(7)) { Some(entry) => MakeCredentialOptions::try_from(entry)?, None => MakeCredentialOptions { rk: false, @@ -169,13 +171,13 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters { }; let pin_uv_auth_param = param_map - .get(&cbor_unsigned!(8)) - .map(read_byte_string) + .remove(&cbor_unsigned!(8)) + .map(extract_byte_string) .transpose()?; let pin_uv_auth_protocol = param_map - .get(&cbor_unsigned!(9)) - .map(read_unsigned) + .remove(&cbor_unsigned!(9)) + .map(extract_unsigned) .transpose()?; Ok(AuthenticatorMakeCredentialParameters { @@ -197,7 +199,7 @@ pub struct AuthenticatorGetAssertionParameters { pub rp_id: String, pub client_data_hash: Vec<u8>, pub allow_list: Option<Vec<PublicKeyCredentialDescriptor>>, - pub extensions: Option<Extensions>, + pub extensions: Option<GetAssertionExtensions>, // Even though options are optional, we can use the default if not present. pub options: GetAssertionOptions, pub pin_uv_auth_param: Option<Vec<u8>>, @@ -208,18 +210,20 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters { type Error = Ctap2StatusCode; fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { - let param_map = read_map(&cbor_value)?; + let mut param_map = extract_map(cbor_value)?; - let rp_id = read_text_string(ok_or_missing(param_map.get(&cbor_unsigned!(1)))?)?; + let rp_id = extract_text_string(ok_or_missing(param_map.remove(&cbor_unsigned!(1)))?)?; - let client_data_hash = read_byte_string(ok_or_missing(param_map.get(&cbor_unsigned!(2)))?)?; + let client_data_hash = + extract_byte_string(ok_or_missing(param_map.remove(&cbor_unsigned!(2)))?)?; - let allow_list = match param_map.get(&cbor_unsigned!(3)) { + let allow_list = match param_map.remove(&cbor_unsigned!(3)) { Some(entry) => { - let allow_list_vec = read_array(entry)?; + let allow_list_vec = extract_array(entry)?; + let list_len = MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(allow_list_vec.len()); let allow_list = allow_list_vec - .iter() - .take(MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(allow_list_vec.len())) + .into_iter() + .take(list_len) .map(PublicKeyCredentialDescriptor::try_from) .collect::<Result<Vec<PublicKeyCredentialDescriptor>, Ctap2StatusCode>>()?; Some(allow_list) @@ -228,11 +232,11 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters { }; let extensions = param_map - .get(&cbor_unsigned!(4)) - .map(Extensions::try_from) + .remove(&cbor_unsigned!(4)) + .map(GetAssertionExtensions::try_from) .transpose()?; - let options = match param_map.get(&cbor_unsigned!(5)) { + let options = match param_map.remove(&cbor_unsigned!(5)) { Some(entry) => GetAssertionOptions::try_from(entry)?, None => GetAssertionOptions { up: true, @@ -241,13 +245,13 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters { }; let pin_uv_auth_param = param_map - .get(&cbor_unsigned!(6)) - .map(read_byte_string) + .remove(&cbor_unsigned!(6)) + .map(extract_byte_string) .transpose()?; let pin_uv_auth_protocol = param_map - .get(&cbor_unsigned!(7)) - .map(read_unsigned) + .remove(&cbor_unsigned!(7)) + .map(extract_unsigned) .transpose()?; Ok(AuthenticatorGetAssertionParameters { @@ -276,32 +280,32 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters { type Error = Ctap2StatusCode; fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { - let param_map = read_map(&cbor_value)?; + let mut param_map = extract_map(cbor_value)?; - let pin_protocol = read_unsigned(ok_or_missing(param_map.get(&cbor_unsigned!(1)))?)?; + let pin_protocol = extract_unsigned(ok_or_missing(param_map.remove(&cbor_unsigned!(1)))?)?; let sub_command = - ClientPinSubCommand::try_from(ok_or_missing(param_map.get(&cbor_unsigned!(2)))?)?; + ClientPinSubCommand::try_from(ok_or_missing(param_map.remove(&cbor_unsigned!(2)))?)?; let key_agreement = param_map - .get(&cbor_unsigned!(3)) - .map(read_map) + .remove(&cbor_unsigned!(3)) + .map(extract_map) .transpose()? - .map(|x| CoseKey(x.clone())); + .map(|x| CoseKey(x)); let pin_auth = param_map - .get(&cbor_unsigned!(4)) - .map(read_byte_string) + .remove(&cbor_unsigned!(4)) + .map(extract_byte_string) .transpose()?; let new_pin_enc = param_map - .get(&cbor_unsigned!(5)) - .map(read_byte_string) + .remove(&cbor_unsigned!(5)) + .map(extract_byte_string) .transpose()?; let pin_hash_enc = param_map - .get(&cbor_unsigned!(6)) - .map(read_byte_string) + .remove(&cbor_unsigned!(6)) + .map(extract_byte_string) .transpose()?; Ok(AuthenticatorClientPinParameters { diff --git a/src/ctap/data_formats.rs b/src/ctap/data_formats.rs index d226fca34941c0f0c3a1f485f3d79f14e4c45f9f..33dec12ec62b5d2f4afa4f071997624a880b0df5 100644 --- a/src/ctap/data_formats.rs +++ b/src/ctap/data_formats.rs @@ -14,7 +14,7 @@ use super::status_code::Ctap2StatusCode; use alloc::collections::BTreeMap; -use alloc::string::{String, ToString}; +use alloc::string::String; use alloc::vec::Vec; use core::convert::TryFrom; use crypto::{ecdh, ecdsa}; @@ -27,19 +27,19 @@ pub struct PublicKeyCredentialRpEntity { pub rp_icon: Option<String>, } -impl TryFrom<&cbor::Value> for PublicKeyCredentialRpEntity { +impl TryFrom<cbor::Value> for PublicKeyCredentialRpEntity { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let rp_map = read_map(cbor_value)?; - let rp_id = read_text_string(ok_or_missing(rp_map.get(&cbor_text!("id")))?)?; + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut rp_map = extract_map(cbor_value)?; + let rp_id = extract_text_string(ok_or_missing(rp_map.remove(&cbor_text!("id")))?)?; let rp_name = rp_map - .get(&cbor_text!("name")) - .map(read_text_string) + .remove(&cbor_text!("name")) + .map(extract_text_string) .transpose()?; let rp_icon = rp_map - .get(&cbor_text!("icon")) - .map(read_text_string) + .remove(&cbor_text!("icon")) + .map(extract_text_string) .transpose()?; Ok(Self { rp_id, @@ -58,23 +58,23 @@ pub struct PublicKeyCredentialUserEntity { pub user_icon: Option<String>, } -impl TryFrom<&cbor::Value> for PublicKeyCredentialUserEntity { +impl TryFrom<cbor::Value> for PublicKeyCredentialUserEntity { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let user_map = read_map(cbor_value)?; - let user_id = read_byte_string(ok_or_missing(user_map.get(&cbor_text!("id")))?)?; + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut user_map = extract_map(cbor_value)?; + let user_id = extract_byte_string(ok_or_missing(user_map.remove(&cbor_text!("id")))?)?; let user_name = user_map - .get(&cbor_text!("name")) - .map(read_text_string) + .remove(&cbor_text!("name")) + .map(extract_text_string) .transpose()?; let user_display_name = user_map - .get(&cbor_text!("displayName")) - .map(read_text_string) + .remove(&cbor_text!("displayName")) + .map(extract_text_string) .transpose()?; let user_icon = user_map - .get(&cbor_text!("icon")) - .map(read_text_string) + .remove(&cbor_text!("icon")) + .map(extract_text_string) .transpose()?; Ok(Self { user_id, @@ -117,11 +117,11 @@ impl From<PublicKeyCredentialType> for cbor::Value { } } -impl TryFrom<&cbor::Value> for PublicKeyCredentialType { +impl TryFrom<cbor::Value> for PublicKeyCredentialType { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let cred_type_string = read_text_string(cbor_value)?; + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let cred_type_string = extract_text_string(cbor_value)?; match &cred_type_string[..] { "public-key" => Ok(PublicKeyCredentialType::PublicKey), _ => Ok(PublicKeyCredentialType::Unknown), @@ -137,16 +137,17 @@ pub struct PublicKeyCredentialParameter { pub alg: SignatureAlgorithm, } -impl TryFrom<&cbor::Value> for PublicKeyCredentialParameter { +impl TryFrom<cbor::Value> for PublicKeyCredentialParameter { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let cred_param_map = read_map(cbor_value)?; + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut cred_param_map = extract_map(cbor_value)?; let cred_type = PublicKeyCredentialType::try_from(ok_or_missing( - cred_param_map.get(&cbor_text!("type")), + cred_param_map.remove(&cbor_text!("type")), + )?)?; + let alg = SignatureAlgorithm::try_from(ok_or_missing( + cred_param_map.remove(&cbor_text!("alg")), )?)?; - let alg = - SignatureAlgorithm::try_from(ok_or_missing(cred_param_map.get(&cbor_text!("alg")))?)?; Ok(Self { cred_type, alg }) } } @@ -181,11 +182,11 @@ impl From<AuthenticatorTransport> for cbor::Value { } } -impl TryFrom<&cbor::Value> for AuthenticatorTransport { +impl TryFrom<cbor::Value> for AuthenticatorTransport { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let transport_string = read_text_string(cbor_value)?; + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let transport_string = extract_text_string(cbor_value)?; match &transport_string[..] { "usb" => Ok(AuthenticatorTransport::Usb), "nfc" => Ok(AuthenticatorTransport::Nfc), @@ -204,23 +205,22 @@ pub struct PublicKeyCredentialDescriptor { pub transports: Option<Vec<AuthenticatorTransport>>, } -impl TryFrom<&cbor::Value> for PublicKeyCredentialDescriptor { +impl TryFrom<cbor::Value> for PublicKeyCredentialDescriptor { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let cred_desc_map = read_map(cbor_value)?; + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut cred_desc_map = extract_map(cbor_value)?; let key_type = PublicKeyCredentialType::try_from(ok_or_missing( - cred_desc_map.get(&cbor_text!("type")), + cred_desc_map.remove(&cbor_text!("type")), )?)?; - let key_id = read_byte_string(ok_or_missing(cred_desc_map.get(&cbor_text!("id")))?)?; - let transports = match cred_desc_map.get(&cbor_text!("transports")) { + let key_id = extract_byte_string(ok_or_missing(cred_desc_map.remove(&cbor_text!("id")))?)?; + let transports = match cred_desc_map.remove(&cbor_text!("transports")) { Some(exclude_entry) => { - let transport_vec = read_array(exclude_entry)?; + let transport_vec = extract_array(exclude_entry)?; let transports = transport_vec - .iter() + .into_iter() .map(AuthenticatorTransport::try_from) - .collect::<Result<Vec<AuthenticatorTransport>, Ctap2StatusCode>>( - )?; + .collect::<Result<Vec<AuthenticatorTransport>, Ctap2StatusCode>>()?; Some(transports) } None => None, @@ -243,105 +243,72 @@ impl From<PublicKeyCredentialDescriptor> for cbor::Value { } } -#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))] -pub struct Extensions(BTreeMap<String, cbor::Value>); +#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))] +pub struct MakeCredentialExtensions { + pub hmac_secret: bool, + pub cred_protect: Option<CredentialProtectionPolicy>, +} -impl TryFrom<&cbor::Value> for Extensions { +impl TryFrom<cbor::Value> for MakeCredentialExtensions { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let mut extensions = BTreeMap::new(); - for (extension_key, extension_value) in read_map(cbor_value)? { - if let cbor::KeyType::TextString(extension_key_string) = extension_key { - extensions.insert(extension_key_string.to_string(), extension_value.clone()); - } else { - return Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE); - } - } - Ok(Extensions(extensions)) + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut extensions_map = extract_map(cbor_value)?; + let hmac_secret = extensions_map + .remove(&cbor_text!("hmac-secret")) + .map_or(Ok(false), extract_bool)?; + let cred_protect = extensions_map + .remove(&cbor_text!("credProtect")) + .map(CredentialProtectionPolicy::try_from) + .transpose()?; + Ok(Self { + hmac_secret, + cred_protect, + }) } } -impl From<Extensions> for cbor::Value { - fn from(extensions: Extensions) -> Self { - cbor_map_btree!(extensions - .0 - .into_iter() - .map(|(key, value)| (cbor_text!(key), value)) - .collect()) - } +#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))] +pub struct GetAssertionExtensions { + pub hmac_secret: Option<GetAssertionHmacSecretInput>, } -impl Extensions { - #[cfg(test)] - pub fn new(extension_map: BTreeMap<String, cbor::Value>) -> Self { - Extensions(extension_map) - } - - pub fn has_make_credential_hmac_secret(&self) -> Result<bool, Ctap2StatusCode> { - self.0 - .get("hmac-secret") - .map(read_bool) - .unwrap_or(Ok(false)) - } +impl TryFrom<cbor::Value> for GetAssertionExtensions { + type Error = Ctap2StatusCode; - pub fn get_assertion_hmac_secret( - &self, - ) -> Option<Result<GetAssertionHmacSecretInput, Ctap2StatusCode>> { - self.0 - .get("hmac-secret") + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut extensions_map = extract_map(cbor_value)?; + let hmac_secret = extensions_map + .remove(&cbor_text!("hmac-secret")) .map(GetAssertionHmacSecretInput::try_from) - } - - pub fn make_credential_cred_protect_policy( - &self, - ) -> Option<Result<CredentialProtectionPolicy, Ctap2StatusCode>> { - self.0 - .get("credProtect") - .map(CredentialProtectionPolicy::try_from) + .transpose()?; + Ok(Self { hmac_secret }) } } -#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))] +#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))] pub struct GetAssertionHmacSecretInput { pub key_agreement: CoseKey, pub salt_enc: Vec<u8>, pub salt_auth: Vec<u8>, } -impl TryFrom<&cbor::Value> for GetAssertionHmacSecretInput { +impl TryFrom<cbor::Value> for GetAssertionHmacSecretInput { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let input_map = read_map(cbor_value)?; - let cose_key = read_map(ok_or_missing(input_map.get(&cbor_unsigned!(1)))?)?; - let salt_enc = read_byte_string(ok_or_missing(input_map.get(&cbor_unsigned!(2)))?)?; - let salt_auth = read_byte_string(ok_or_missing(input_map.get(&cbor_unsigned!(3)))?)?; + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut input_map = extract_map(cbor_value)?; + let cose_key = extract_map(ok_or_missing(input_map.remove(&cbor_unsigned!(1)))?)?; + let salt_enc = extract_byte_string(ok_or_missing(input_map.remove(&cbor_unsigned!(2)))?)?; + let salt_auth = extract_byte_string(ok_or_missing(input_map.remove(&cbor_unsigned!(3)))?)?; Ok(Self { - key_agreement: CoseKey(cose_key.clone()), + key_agreement: CoseKey(cose_key), salt_enc, salt_auth, }) } } -#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))] -pub struct GetAssertionHmacSecretOutput(Vec<u8>); - -impl From<GetAssertionHmacSecretOutput> for cbor::Value { - fn from(message: GetAssertionHmacSecretOutput) -> cbor::Value { - cbor_bytes!(message.0) - } -} - -impl TryFrom<&cbor::Value> for GetAssertionHmacSecretOutput { - type Error = Ctap2StatusCode; - - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - Ok(GetAssertionHmacSecretOutput(read_byte_string(cbor_value)?)) - } -} - // Even though options are optional, we can use the default if not present. #[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))] pub struct MakeCredentialOptions { @@ -349,22 +316,22 @@ pub struct MakeCredentialOptions { pub uv: bool, } -impl TryFrom<&cbor::Value> for MakeCredentialOptions { +impl TryFrom<cbor::Value> for MakeCredentialOptions { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let options_map = read_map(cbor_value)?; - let rk = match options_map.get(&cbor_text!("rk")) { - Some(options_entry) => read_bool(options_entry)?, + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut options_map = extract_map(cbor_value)?; + let rk = match options_map.remove(&cbor_text!("rk")) { + Some(options_entry) => extract_bool(options_entry)?, None => false, }; - if let Some(options_entry) = options_map.get(&cbor_text!("up")) { - if !read_bool(options_entry)? { + if let Some(options_entry) = options_map.remove(&cbor_text!("up")) { + if !extract_bool(options_entry)? { return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION); } } - let uv = match options_map.get(&cbor_text!("uv")) { - Some(options_entry) => read_bool(options_entry)?, + let uv = match options_map.remove(&cbor_text!("uv")) { + Some(options_entry) => extract_bool(options_entry)?, None => false, }; Ok(Self { rk, uv }) @@ -377,22 +344,22 @@ pub struct GetAssertionOptions { pub uv: bool, } -impl TryFrom<&cbor::Value> for GetAssertionOptions { +impl TryFrom<cbor::Value> for GetAssertionOptions { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let options_map = read_map(cbor_value)?; - if let Some(options_entry) = options_map.get(&cbor_text!("rk")) { + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let mut options_map = extract_map(cbor_value)?; + if let Some(options_entry) = options_map.remove(&cbor_text!("rk")) { // This is only for returning the correct status code. - read_bool(options_entry)?; + extract_bool(options_entry)?; return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION); } - let up = match options_map.get(&cbor_text!("up")) { - Some(options_entry) => read_bool(options_entry)?, + let up = match options_map.remove(&cbor_text!("up")) { + Some(options_entry) => extract_bool(options_entry)?, None => true, }; - let uv = match options_map.get(&cbor_text!("uv")) { - Some(options_entry) => read_bool(options_entry)?, + let uv = match options_map.remove(&cbor_text!("uv")) { + Some(options_entry) => extract_bool(options_entry)?, None => false, }; Ok(Self { up, uv }) @@ -435,11 +402,11 @@ impl From<SignatureAlgorithm> for cbor::Value { } } -impl TryFrom<&cbor::Value> for SignatureAlgorithm { +impl TryFrom<cbor::Value> for SignatureAlgorithm { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - match read_integer(cbor_value)? { + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + match extract_integer(cbor_value)? { ecdsa::PubKey::ES256_ALGORITHM => Ok(SignatureAlgorithm::ES256), _ => Ok(SignatureAlgorithm::Unknown), } @@ -473,19 +440,6 @@ impl TryFrom<cbor::Value> for CredentialProtectionPolicy { } } -impl TryFrom<&cbor::Value> for CredentialProtectionPolicy { - type Error = Ctap2StatusCode; - - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - match read_integer(cbor_value)? { - 0x01 => Ok(CredentialProtectionPolicy::UserVerificationOptional), - 0x02 => Ok(CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList), - 0x03 => Ok(CredentialProtectionPolicy::UserVerificationRequired), - _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), - } - } -} - // https://www.w3.org/TR/webauthn/#public-key-credential-source // // Note that we only use the WebAuthn definition as an example. This data-structure is not specified @@ -605,7 +559,7 @@ impl PublicKeyCredentialSource { // TODO(kaczmarczyck) we could decide to split this data type up // It depends on the algorithm though, I think. // So before creating a mess, this is my workaround. -#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))] +#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))] pub struct CoseKey(pub BTreeMap<cbor::KeyType, cbor::Value>); // This is the algorithm specifier that is supposed to be used in a COSE key @@ -645,23 +599,24 @@ impl TryFrom<CoseKey> for ecdh::PubKey { type Error = Ctap2StatusCode; fn try_from(cose_key: CoseKey) -> Result<Self, Ctap2StatusCode> { - let key_type = read_integer(ok_or_missing(cose_key.0.get(&cbor_int!(1)))?)?; + let mut cose_map = cose_key.0; + let key_type = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(1)))?)?; if key_type != EC2_KEY_TYPE { return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM); } - let algorithm = read_integer(ok_or_missing(cose_key.0.get(&cbor_int!(3)))?)?; + let algorithm = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(3)))?)?; if algorithm != ECDH_ALGORITHM && algorithm != ES256_ALGORITHM { return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM); } - let curve = read_integer(ok_or_missing(cose_key.0.get(&cbor_int!(-1)))?)?; + let curve = extract_integer(ok_or_missing(cose_map.remove(&cbor_int!(-1)))?)?; if curve != P_256_CURVE { return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM); } - let x_bytes = read_byte_string(ok_or_missing(cose_key.0.get(&cbor_int!(-2)))?)?; + let x_bytes = extract_byte_string(ok_or_missing(cose_map.remove(&cbor_int!(-2)))?)?; if x_bytes.len() != ecdh::NBYTES { return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); } - let y_bytes = read_byte_string(ok_or_missing(cose_key.0.get(&cbor_int!(-3)))?)?; + let y_bytes = extract_byte_string(ok_or_missing(cose_map.remove(&cbor_int!(-3)))?)?; if y_bytes.len() != ecdh::NBYTES { return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); } @@ -698,11 +653,11 @@ impl From<ClientPinSubCommand> for cbor::Value { } } -impl TryFrom<&cbor::Value> for ClientPinSubCommand { +impl TryFrom<cbor::Value> for ClientPinSubCommand { type Error = Ctap2StatusCode; - fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> { - let subcommand_int = read_unsigned(cbor_value)?; + fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> { + let subcommand_int = extract_unsigned(cbor_value)?; match subcommand_int { 0x01 => Ok(ClientPinSubCommand::GetPinRetries), 0x02 => Ok(ClientPinSubCommand::GetKeyAgreement), @@ -717,28 +672,14 @@ impl TryFrom<&cbor::Value> for ClientPinSubCommand { } } -pub(super) fn read_unsigned(cbor_value: &cbor::Value) -> Result<u64, Ctap2StatusCode> { +pub(super) fn extract_unsigned(cbor_value: cbor::Value) -> Result<u64, Ctap2StatusCode> { match cbor_value { - cbor::Value::KeyValue(cbor::KeyType::Unsigned(unsigned)) => Ok(*unsigned), - _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), - } -} - -pub(super) fn read_integer(cbor_value: &cbor::Value) -> Result<i64, Ctap2StatusCode> { - match cbor_value { - cbor::Value::KeyValue(cbor::KeyType::Unsigned(unsigned)) => { - if *unsigned <= core::i64::MAX as u64 { - Ok(*unsigned as i64) - } else { - Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE) - } - } - cbor::Value::KeyValue(cbor::KeyType::Negative(signed)) => Ok(*signed), + cbor::Value::KeyValue(cbor::KeyType::Unsigned(unsigned)) => Ok(unsigned), _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), } } -fn extract_integer(cbor_value: cbor::Value) -> Result<i64, Ctap2StatusCode> { +pub(super) fn extract_integer(cbor_value: cbor::Value) -> Result<i64, Ctap2StatusCode> { match cbor_value { cbor::Value::KeyValue(cbor::KeyType::Unsigned(unsigned)) => { if unsigned <= core::i64::MAX as u64 { @@ -752,53 +693,28 @@ fn extract_integer(cbor_value: cbor::Value) -> Result<i64, Ctap2StatusCode> { } } -pub fn read_byte_string(cbor_value: &cbor::Value) -> Result<Vec<u8>, Ctap2StatusCode> { - match cbor_value { - cbor::Value::KeyValue(cbor::KeyType::ByteString(byte_string)) => Ok(byte_string.to_vec()), - _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), - } -} - -fn extract_byte_string(cbor_value: cbor::Value) -> Result<Vec<u8>, Ctap2StatusCode> { +pub fn extract_byte_string(cbor_value: cbor::Value) -> Result<Vec<u8>, Ctap2StatusCode> { match cbor_value { cbor::Value::KeyValue(cbor::KeyType::ByteString(byte_string)) => Ok(byte_string), _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), } } -pub(super) fn read_text_string(cbor_value: &cbor::Value) -> Result<String, Ctap2StatusCode> { - match cbor_value { - cbor::Value::KeyValue(cbor::KeyType::TextString(text_string)) => { - Ok(text_string.to_string()) - } - _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), - } -} - -fn extract_text_string(cbor_value: cbor::Value) -> Result<String, Ctap2StatusCode> { +pub(super) fn extract_text_string(cbor_value: cbor::Value) -> Result<String, Ctap2StatusCode> { match cbor_value { cbor::Value::KeyValue(cbor::KeyType::TextString(text_string)) => Ok(text_string), _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), } } -pub(super) fn read_array(cbor_value: &cbor::Value) -> Result<&Vec<cbor::Value>, Ctap2StatusCode> { +pub(super) fn extract_array(cbor_value: cbor::Value) -> Result<Vec<cbor::Value>, Ctap2StatusCode> { match cbor_value { cbor::Value::Array(array) => Ok(array), _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), } } -pub(super) fn read_map( - cbor_value: &cbor::Value, -) -> Result<&BTreeMap<cbor::KeyType, cbor::Value>, Ctap2StatusCode> { - match cbor_value { - cbor::Value::Map(map) => Ok(map), - _ => Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE), - } -} - -fn extract_map( +pub(super) fn extract_map( cbor_value: cbor::Value, ) -> Result<BTreeMap<cbor::KeyType, cbor::Value>, Ctap2StatusCode> { match cbor_value { @@ -807,7 +723,7 @@ fn extract_map( } } -pub(super) fn read_bool(cbor_value: &cbor::Value) -> Result<bool, Ctap2StatusCode> { +pub(super) fn extract_bool(cbor_value: cbor::Value) -> Result<bool, Ctap2StatusCode> { match cbor_value { cbor::Value::Simple(cbor::SimpleValue::FalseValue) => Ok(false), cbor::Value::Simple(cbor::SimpleValue::TrueValue) => Ok(true), @@ -827,192 +743,192 @@ mod test { use crypto::rng256::{Rng256, ThreadRng256}; #[test] - fn test_read_unsigned() { - assert_eq!(read_unsigned(&cbor_int!(123)), Ok(123)); + fn test_extract_unsigned() { + assert_eq!(extract_unsigned(cbor_int!(123)), Ok(123)); assert_eq!( - read_unsigned(&cbor_bool!(true)), + extract_unsigned(cbor_bool!(true)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_unsigned(&cbor_text!("foo")), + extract_unsigned(cbor_text!("foo")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_unsigned(&cbor_bytes_lit!(b"bar")), + extract_unsigned(cbor_bytes_lit!(b"bar")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_unsigned(&cbor_array![]), + extract_unsigned(cbor_array![]), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_unsigned(&cbor_map! {}), + extract_unsigned(cbor_map! {}), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); } #[test] - fn test_read_unsigned_limits() { + fn test_extract_unsigned_limits() { assert_eq!( - read_unsigned(&cbor_unsigned!(std::u64::MAX)), + extract_unsigned(cbor_unsigned!(std::u64::MAX)), Ok(std::u64::MAX) ); assert_eq!( - read_unsigned(&cbor_unsigned!((std::i64::MAX as u64) + 1)), + extract_unsigned(cbor_unsigned!((std::i64::MAX as u64) + 1)), Ok((std::i64::MAX as u64) + 1) ); assert_eq!( - read_unsigned(&cbor_int!(std::i64::MAX)), + extract_unsigned(cbor_int!(std::i64::MAX)), Ok(std::i64::MAX as u64) ); - assert_eq!(read_unsigned(&cbor_int!(123)), Ok(123)); - assert_eq!(read_unsigned(&cbor_int!(1)), Ok(1)); - assert_eq!(read_unsigned(&cbor_int!(0)), Ok(0)); + assert_eq!(extract_unsigned(cbor_int!(123)), Ok(123)); + assert_eq!(extract_unsigned(cbor_int!(1)), Ok(1)); + assert_eq!(extract_unsigned(cbor_int!(0)), Ok(0)); assert_eq!( - read_unsigned(&cbor_int!(-1)), + extract_unsigned(cbor_int!(-1)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_unsigned(&cbor_int!(-123)), + extract_unsigned(cbor_int!(-123)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_unsigned(&cbor_int!(std::i64::MIN)), + extract_unsigned(cbor_int!(std::i64::MIN)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); } #[test] - fn test_read_integer() { - assert_eq!(read_integer(&cbor_int!(123)), Ok(123)); - assert_eq!(read_integer(&cbor_int!(-123)), Ok(-123)); + fn test_extract_integer() { + assert_eq!(extract_integer(cbor_int!(123)), Ok(123)); + assert_eq!(extract_integer(cbor_int!(-123)), Ok(-123)); assert_eq!( - read_integer(&cbor_bool!(true)), + extract_integer(cbor_bool!(true)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_integer(&cbor_text!("foo")), + extract_integer(cbor_text!("foo")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_integer(&cbor_bytes_lit!(b"bar")), + extract_integer(cbor_bytes_lit!(b"bar")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_integer(&cbor_array![]), + extract_integer(cbor_array![]), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_integer(&cbor_map! {}), + extract_integer(cbor_map! {}), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); } #[test] - fn test_read_integer_limits() { + fn test_extract_integer_limits() { assert_eq!( - read_integer(&cbor_unsigned!(std::u64::MAX)), + extract_integer(cbor_unsigned!(std::u64::MAX)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_integer(&cbor_unsigned!((std::i64::MAX as u64) + 1)), + extract_integer(cbor_unsigned!((std::i64::MAX as u64) + 1)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); - assert_eq!(read_integer(&cbor_int!(std::i64::MAX)), Ok(std::i64::MAX)); - assert_eq!(read_integer(&cbor_int!(123)), Ok(123)); - assert_eq!(read_integer(&cbor_int!(1)), Ok(1)); - assert_eq!(read_integer(&cbor_int!(0)), Ok(0)); - assert_eq!(read_integer(&cbor_int!(-1)), Ok(-1)); - assert_eq!(read_integer(&cbor_int!(-123)), Ok(-123)); - assert_eq!(read_integer(&cbor_int!(std::i64::MIN)), Ok(std::i64::MIN)); + assert_eq!(extract_integer(cbor_int!(std::i64::MAX)), Ok(std::i64::MAX)); + assert_eq!(extract_integer(cbor_int!(123)), Ok(123)); + assert_eq!(extract_integer(cbor_int!(1)), Ok(1)); + assert_eq!(extract_integer(cbor_int!(0)), Ok(0)); + assert_eq!(extract_integer(cbor_int!(-1)), Ok(-1)); + assert_eq!(extract_integer(cbor_int!(-123)), Ok(-123)); + assert_eq!(extract_integer(cbor_int!(std::i64::MIN)), Ok(std::i64::MIN)); } #[test] - fn test_read_byte_string() { + fn test_extract_byte_string() { assert_eq!( - read_byte_string(&cbor_int!(123)), + extract_byte_string(cbor_int!(123)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_byte_string(&cbor_bool!(true)), + extract_byte_string(cbor_bool!(true)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_byte_string(&cbor_text!("foo")), + extract_byte_string(cbor_text!("foo")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); - assert_eq!(read_byte_string(&cbor_bytes_lit!(b"")), Ok(Vec::new())); + assert_eq!(extract_byte_string(cbor_bytes_lit!(b"")), Ok(Vec::new())); assert_eq!( - read_byte_string(&cbor_bytes_lit!(b"bar")), + extract_byte_string(cbor_bytes_lit!(b"bar")), Ok(b"bar".to_vec()) ); assert_eq!( - read_byte_string(&cbor_array![]), + extract_byte_string(cbor_array![]), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_byte_string(&cbor_map! {}), + extract_byte_string(cbor_map! {}), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); } #[test] - fn test_read_text_string() { + fn test_extract_text_string() { assert_eq!( - read_text_string(&cbor_int!(123)), + extract_text_string(cbor_int!(123)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_text_string(&cbor_bool!(true)), + extract_text_string(cbor_bool!(true)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); - assert_eq!(read_text_string(&cbor_text!("")), Ok(String::new())); + assert_eq!(extract_text_string(cbor_text!("")), Ok(String::new())); assert_eq!( - read_text_string(&cbor_text!("foo")), + extract_text_string(cbor_text!("foo")), Ok(String::from("foo")) ); assert_eq!( - read_text_string(&cbor_bytes_lit!(b"bar")), + extract_text_string(cbor_bytes_lit!(b"bar")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_text_string(&cbor_array![]), + extract_text_string(cbor_array![]), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_text_string(&cbor_map! {}), + extract_text_string(cbor_map! {}), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); } #[test] - fn test_read_array() { + fn test_extract_array() { assert_eq!( - read_array(&cbor_int!(123)), + extract_array(cbor_int!(123)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_array(&cbor_bool!(true)), + extract_array(cbor_bool!(true)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_array(&cbor_text!("foo")), + extract_array(cbor_text!("foo")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_array(&cbor_bytes_lit!(b"bar")), + extract_array(cbor_bytes_lit!(b"bar")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); - assert_eq!(read_array(&cbor_array![]), Ok(&Vec::new())); + assert_eq!(extract_array(cbor_array![]), Ok(Vec::new())); assert_eq!( - read_array(&cbor_array![ + extract_array(cbor_array![ 123, cbor_null!(), "foo", cbor_array![], cbor_map! {}, ]), - Ok(&vec![ + Ok(vec![ cbor_int!(123), cbor_null!(), cbor_text!("foo"), @@ -1021,41 +937,41 @@ mod test { ]) ); assert_eq!( - read_array(&cbor_map! {}), + extract_array(cbor_map! {}), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); } #[test] - fn test_read_map() { + fn test_extract_map() { assert_eq!( - read_map(&cbor_int!(123)), + extract_map(cbor_int!(123)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_map(&cbor_bool!(true)), + extract_map(cbor_bool!(true)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_map(&cbor_text!("foo")), + extract_map(cbor_text!("foo")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_map(&cbor_bytes_lit!(b"bar")), + extract_map(cbor_bytes_lit!(b"bar")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_map(&cbor_array![]), + extract_map(cbor_array![]), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); - assert_eq!(read_map(&cbor_map! {}), Ok(&BTreeMap::new())); + assert_eq!(extract_map(cbor_map! {}), Ok(BTreeMap::new())); assert_eq!( - read_map(&cbor_map! { + extract_map(cbor_map! { 1 => cbor_false!(), "foo" => b"bar", b"bin" => -42, }), - Ok(&[ + Ok([ (cbor_unsigned!(1), cbor_false!()), (cbor_text!("foo"), cbor_bytes_lit!(b"bar")), (cbor_bytes_lit!(b"bin"), cbor_int!(-42)), @@ -1067,27 +983,27 @@ mod test { } #[test] - fn test_read_bool() { + fn test_extract_bool() { assert_eq!( - read_bool(&cbor_int!(123)), + extract_bool(cbor_int!(123)), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); - assert_eq!(read_bool(&cbor_bool!(true)), Ok(true)); - assert_eq!(read_bool(&cbor_bool!(false)), Ok(false)); + assert_eq!(extract_bool(cbor_bool!(true)), Ok(true)); + assert_eq!(extract_bool(cbor_bool!(false)), Ok(false)); assert_eq!( - read_bool(&cbor_text!("foo")), + extract_bool(cbor_text!("foo")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_bool(&cbor_bytes_lit!(b"bar")), + extract_bool(cbor_bytes_lit!(b"bar")), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_bool(&cbor_array![]), + extract_bool(cbor_array![]), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); assert_eq!( - read_bool(&cbor_map! {}), + extract_bool(cbor_map! {}), Err(CTAP2_ERR_CBOR_UNEXPECTED_TYPE) ); } @@ -1099,7 +1015,7 @@ mod test { "name" => "Example", "icon" => "example.com/icon.png", }; - let rp_entity = PublicKeyCredentialRpEntity::try_from(&cbor_rp_entity); + let rp_entity = PublicKeyCredentialRpEntity::try_from(cbor_rp_entity); let expected_rp_entity = PublicKeyCredentialRpEntity { rp_id: "example.com".to_string(), rp_name: Some("Example".to_string()), @@ -1116,7 +1032,7 @@ mod test { "displayName" => "bar", "icon" => "example.com/foo/icon.png", }; - let user_entity = PublicKeyCredentialUserEntity::try_from(&cbor_user_entity); + let user_entity = PublicKeyCredentialUserEntity::try_from(cbor_user_entity.clone()); let expected_user_entity = PublicKeyCredentialUserEntity { user_id: vec![0x1D, 0x1D, 0x1D, 0x1D], user_name: Some("foo".to_string()), @@ -1130,30 +1046,30 @@ mod test { #[test] fn test_from_into_public_key_credential_type() { - let cbor_credential_type = cbor_text!("public-key"); - let credential_type = PublicKeyCredentialType::try_from(&cbor_credential_type); + let cbor_credential_type: cbor::Value = cbor_text!("public-key"); + let credential_type = PublicKeyCredentialType::try_from(cbor_credential_type.clone()); let expected_credential_type = PublicKeyCredentialType::PublicKey; assert_eq!(credential_type, Ok(expected_credential_type)); let created_cbor: cbor::Value = credential_type.unwrap().into(); assert_eq!(created_cbor, cbor_credential_type); - let cbor_unknown_type = cbor_text!("unknown-type"); - let unknown_type = PublicKeyCredentialType::try_from(&cbor_unknown_type); + let cbor_unknown_type: cbor::Value = cbor_text!("unknown-type"); + let unknown_type = PublicKeyCredentialType::try_from(cbor_unknown_type); let expected_unknown_type = PublicKeyCredentialType::Unknown; assert_eq!(unknown_type, Ok(expected_unknown_type)); } #[test] fn test_from_into_signature_algorithm() { - let cbor_signature_algorithm = cbor_int!(ecdsa::PubKey::ES256_ALGORITHM); - let signature_algorithm = SignatureAlgorithm::try_from(&cbor_signature_algorithm); + let cbor_signature_algorithm: cbor::Value = cbor_int!(ecdsa::PubKey::ES256_ALGORITHM); + let signature_algorithm = SignatureAlgorithm::try_from(cbor_signature_algorithm.clone()); let expected_signature_algorithm = SignatureAlgorithm::ES256; assert_eq!(signature_algorithm, Ok(expected_signature_algorithm)); - let created_cbor = cbor::Value::from(signature_algorithm.unwrap()); + let created_cbor: cbor::Value = signature_algorithm.unwrap().into(); assert_eq!(created_cbor, cbor_signature_algorithm); - let cbor_unknown_algorithm = cbor_int!(-1); - let unknown_algorithm = SignatureAlgorithm::try_from(&cbor_unknown_algorithm); + let cbor_unknown_algorithm: cbor::Value = cbor_int!(-1); + let unknown_algorithm = SignatureAlgorithm::try_from(cbor_unknown_algorithm); let expected_unknown_algorithm = SignatureAlgorithm::Unknown; assert_eq!(unknown_algorithm, Ok(expected_unknown_algorithm)); } @@ -1176,24 +1092,24 @@ mod test { #[test] fn test_from_into_cred_protection_policy() { - let cbor_policy = cbor::Value::from(CredentialProtectionPolicy::UserVerificationOptional); - let policy = CredentialProtectionPolicy::try_from(&cbor_policy); + let cbor_policy: cbor::Value = CredentialProtectionPolicy::UserVerificationOptional.into(); + let policy = CredentialProtectionPolicy::try_from(cbor_policy.clone()); let expected_policy = CredentialProtectionPolicy::UserVerificationOptional; assert_eq!(policy, Ok(expected_policy)); - let created_cbor = cbor::Value::from(policy.unwrap()); + let created_cbor: cbor::Value = policy.unwrap().into(); assert_eq!(created_cbor, cbor_policy); - let cbor_policy_error = cbor_int!(-1); - let policy_error = CredentialProtectionPolicy::try_from(&cbor_policy_error); + let cbor_policy_error: cbor::Value = cbor_int!(-1); + let policy_error = CredentialProtectionPolicy::try_from(cbor_policy_error); let expected_error = Err(Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE); assert_eq!(policy_error, expected_error); } #[test] fn test_from_into_authenticator_transport() { - let cbor_authenticator_transport = cbor_text!("usb"); + let cbor_authenticator_transport: cbor::Value = cbor_text!("usb"); let authenticator_transport = - AuthenticatorTransport::try_from(&cbor_authenticator_transport); + AuthenticatorTransport::try_from(cbor_authenticator_transport.clone()); let expected_authenticator_transport = AuthenticatorTransport::Usb; assert_eq!( authenticator_transport, @@ -1210,7 +1126,7 @@ mod test { "alg" => ecdsa::PubKey::ES256_ALGORITHM, }; let credential_parameter = - PublicKeyCredentialParameter::try_from(&cbor_credential_parameter); + PublicKeyCredentialParameter::try_from(cbor_credential_parameter.clone()); let expected_credential_parameter = PublicKeyCredentialParameter { cred_type: PublicKeyCredentialType::PublicKey, alg: SignatureAlgorithm::ES256, @@ -1228,7 +1144,7 @@ mod test { "transports" => cbor_array!["usb"], }; let credential_descriptor = - PublicKeyCredentialDescriptor::try_from(&cbor_credential_descriptor); + PublicKeyCredentialDescriptor::try_from(cbor_credential_descriptor.clone()); let expected_credential_descriptor = PublicKeyCredentialDescriptor { key_type: PublicKeyCredentialType::PublicKey, key_id: vec![0x2D, 0x2D, 0x2D, 0x2D], @@ -1240,44 +1156,21 @@ mod test { } #[test] - fn test_from_into_extensions() { + fn test_from_make_credential_extensions() { let cbor_extensions = cbor_map! { - "the_answer" => 42, + "hmac-secret" => true, + "credProtect" => CredentialProtectionPolicy::UserVerificationRequired, + }; + let extensions = MakeCredentialExtensions::try_from(cbor_extensions); + let expected_extensions = MakeCredentialExtensions { + hmac_secret: true, + cred_protect: Some(CredentialProtectionPolicy::UserVerificationRequired), }; - let extensions = Extensions::try_from(&cbor_extensions); - let mut expected_extensions = Extensions(BTreeMap::new()); - expected_extensions - .0 - .insert("the_answer".to_string(), cbor_int!(42)); assert_eq!(extensions, Ok(expected_extensions)); - let created_cbor: cbor::Value = extensions.unwrap().into(); - assert_eq!(created_cbor, cbor_extensions); } #[test] - fn test_from_into_get_assertion_hmac_secret_output() { - let cbor_output = cbor_bytes![vec![0xC0; 32]]; - let output = GetAssertionHmacSecretOutput::try_from(&cbor_output); - let expected_output = GetAssertionHmacSecretOutput(vec![0xC0; 32]); - assert_eq!(output, Ok(expected_output)); - let created_cbor: cbor::Value = output.unwrap().into(); - assert_eq!(created_cbor, cbor_output); - } - - #[test] - fn test_hmac_secret_extension() { - let cbor_extensions = cbor_map! { - "hmac-secret" => true, - }; - let extensions = Extensions::try_from(&cbor_extensions).unwrap(); - assert!(extensions.has_make_credential_hmac_secret().unwrap()); - - let cbor_extensions = cbor_map! { - "hmac-secret" => false, - }; - let extensions = Extensions::try_from(&cbor_extensions).unwrap(); - assert!(!extensions.has_make_credential_hmac_secret().unwrap()); - + fn test_from_get_assertion_extensions() { let mut rng = ThreadRng256 {}; let sk = crypto::ecdh::SecKey::gensk(&mut rng); let pk = sk.genpk(); @@ -1286,29 +1179,19 @@ mod test { "hmac-secret" => cbor_map! { 1 => cbor::Value::Map(cose_key.0.clone()), 2 => vec![0x02; 32], - 3 => vec![0x03; 32], + 3 => vec![0x03; 16], }, }; - let extensions = Extensions::try_from(&cbor_extensions).unwrap(); - let get_assertion_input = extensions.get_assertion_hmac_secret(); + let extensions = GetAssertionExtensions::try_from(cbor_extensions); let expected_input = GetAssertionHmacSecretInput { key_agreement: cose_key, salt_enc: vec![0x02; 32], - salt_auth: vec![0x03; 32], + salt_auth: vec![0x03; 16], }; - assert_eq!(get_assertion_input, Some(Ok(expected_input))); - } - - #[test] - fn test_cred_protect_extension() { - let cbor_extensions = cbor_map! { - "credProtect" => CredentialProtectionPolicy::UserVerificationRequired, + let expected_extensions = GetAssertionExtensions { + hmac_secret: Some(expected_input), }; - let extensions = Extensions::try_from(&cbor_extensions).unwrap(); - assert_eq!( - extensions.make_credential_cred_protect_policy(), - Some(Ok(CredentialProtectionPolicy::UserVerificationRequired)) - ); + assert_eq!(extensions, Ok(expected_extensions)); } #[test] @@ -1317,7 +1200,7 @@ mod test { "rk" => true, "uv" => false, }; - let make_options = MakeCredentialOptions::try_from(&cbor_make_options); + let make_options = MakeCredentialOptions::try_from(cbor_make_options); let expected_make_options = MakeCredentialOptions { rk: true, uv: false, @@ -1331,7 +1214,7 @@ mod test { "up" => true, "uv" => false, }; - let get_assertion = GetAssertionOptions::try_from(&cbor_get_assertion); + let get_assertion = GetAssertionOptions::try_from(cbor_get_assertion); let expected_get_assertion = GetAssertionOptions { up: true, uv: false, @@ -1370,8 +1253,8 @@ mod test { #[test] fn test_from_into_client_pin_sub_command() { - let cbor_sub_command = cbor_int!(0x01); - let sub_command = ClientPinSubCommand::try_from(&cbor_sub_command); + let cbor_sub_command: cbor::Value = cbor_int!(0x01); + let sub_command = ClientPinSubCommand::try_from(cbor_sub_command.clone()); let expected_sub_command = ClientPinSubCommand::GetPinRetries; assert_eq!(sub_command, Ok(expected_sub_command)); let created_cbor: cbor::Value = sub_command.unwrap().into(); diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 5514fea2402558f071dcb5427175f37367ee7344..a52798b2a3ff1019c7a34b8852ac21a490bfbe61 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -435,16 +435,14 @@ where } let (use_hmac_extension, cred_protect_policy) = if let Some(extensions) = extensions { - let mut cred_protect = extensions - .make_credential_cred_protect_policy() - .transpose()?; + let mut cred_protect = extensions.cred_protect; if cred_protect.unwrap_or(CredentialProtectionPolicy::UserVerificationOptional) < DEFAULT_CRED_PROTECT .unwrap_or(CredentialProtectionPolicy::UserVerificationOptional) { cred_protect = DEFAULT_CRED_PROTECT; } - (extensions.has_make_credential_hmac_secret()?, cred_protect) + (extensions.hmac_secret, cred_protect) } else { (false, None) }; @@ -545,11 +543,11 @@ where auth_data.extend(cose_key); if has_extension_output { let hmac_secret_output = if use_hmac_extension { Some(true) } else { None }; - let extensions = cbor_map_options! { + let extensions_output = cbor_map_options! { "hmac-secret" => hmac_secret_output, "credProtect" => cred_protect_policy, }; - if !cbor::write(extensions, &mut auth_data) { + if !cbor::write(extensions_output, &mut auth_data) { return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_RESPONSE_CANNOT_WRITE_CBOR); } } @@ -639,11 +637,8 @@ where } } - let get_assertion_hmac_secret_input = match extensions { - Some(extensions) => extensions.get_assertion_hmac_secret().transpose()?, - None => None, - }; - if get_assertion_hmac_secret_input.is_some() && !options.up { + let hmac_secret_input = extensions.map(|e| e.hmac_secret).flatten(); + if hmac_secret_input.is_some() && !options.up { // The extension is actually supported, but we need user presence. return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_EXTENSION); } @@ -673,7 +668,7 @@ where if options.up { flags |= UP_FLAG; } - if get_assertion_hmac_secret_input.is_some() { + if hmac_secret_input.is_some() { flags |= ED_FLAG; } @@ -720,12 +715,12 @@ where let mut auth_data = self.generate_auth_data(&rp_id_hash, flags); // Process extensions. - if let Some(get_assertion_hmac_secret_input) = get_assertion_hmac_secret_input { + if let Some(hmac_secret_input) = hmac_secret_input { let GetAssertionHmacSecretInput { key_agreement, salt_enc, salt_auth, - } = get_assertion_hmac_secret_input; + } = hmac_secret_input; let pk: crypto::ecdh::PubKey = CoseKey::try_into(key_agreement)?; let shared_secret = self.key_agreement_key.exchange_x_sha256(&pk); // HMAC-secret does the same 16 byte truncated check. @@ -740,10 +735,10 @@ where None => return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_EXTENSION), }; - let extensions = cbor_map! { + let extensions_output = cbor_map! { "hmac-secret" => encrypted_output, }; - if !cbor::write(extensions, &mut auth_data) { + if !cbor::write(extensions_output, &mut auth_data) { return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_RESPONSE_CANNOT_WRITE_CBOR); } } @@ -1118,8 +1113,8 @@ where #[cfg(test)] mod test { use super::data_formats::{ - Extensions, GetAssertionOptions, MakeCredentialOptions, PublicKeyCredentialRpEntity, - PublicKeyCredentialUserEntity, + GetAssertionExtensions, GetAssertionOptions, MakeCredentialExtensions, + MakeCredentialOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, }; use super::*; use crypto::rng256::ThreadRng256; @@ -1219,9 +1214,10 @@ mod test { fn create_make_credential_parameters_with_cred_protect_policy( policy: CredentialProtectionPolicy, ) -> AuthenticatorMakeCredentialParameters { - let mut extension_map = BTreeMap::new(); - extension_map.insert("credProtect".to_string(), cbor::Value::from(policy)); - let extensions = Some(Extensions::new(extension_map)); + let extensions = Some(MakeCredentialExtensions { + hmac_secret: false, + cred_protect: Some(policy), + }); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = extensions; make_credential_params @@ -1408,9 +1404,10 @@ mod test { let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut rng, user_immediately_present); - let mut extension_map = BTreeMap::new(); - extension_map.insert("hmac-secret".to_string(), cbor_bool!(true)); - let extensions = Some(Extensions::new(extension_map)); + let extensions = Some(MakeCredentialExtensions { + hmac_secret: true, + cred_protect: None, + }); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = extensions; let make_credential_response = @@ -1520,9 +1517,10 @@ mod test { let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut rng, user_immediately_present); - let mut extension_map = BTreeMap::new(); - extension_map.insert("hmac-secret".to_string(), cbor_bool!(true)); - let make_extensions = Some(Extensions::new(extension_map)); + let make_extensions = Some(MakeCredentialExtensions { + hmac_secret: true, + cred_protect: None, + }); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = make_extensions; assert!(ctap_state @@ -1530,15 +1528,15 @@ mod test { .is_ok()); let pk = sk.genpk(); - let hmac_secret_parameters = cbor_map! { - 1 => cbor::Value::Map(CoseKey::from(pk).0), - 2 => vec![0; 32], - 3 => vec![0; 16], + let hmac_secret_input = GetAssertionHmacSecretInput { + key_agreement: CoseKey::from(pk), + salt_enc: vec![0x02; 32], + salt_auth: vec![0x03; 16], }; - let mut extension_map = BTreeMap::new(); - extension_map.insert("hmac-secret".to_string(), hmac_secret_parameters); + let get_extensions = Some(GetAssertionExtensions { + hmac_secret: Some(hmac_secret_input), + }); - let get_extensions = Some(Extensions::new(extension_map)); let get_assertion_params = AuthenticatorGetAssertionParameters { rp_id: String::from("example.com"), client_data_hash: vec![0xCD],