Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/ctap2/commands/credential_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ pub struct CredentialManagementResponse {
pub cred_protect: Option<u64>,
/// Large blob encryption key.
pub large_blob_key: Option<Vec<u8>>,

// CTAP 2.2
/// Whether the credential is third-party payment enabled, if supported by the authenticator.
pub third_party_payment: Option<bool>,
}

impl CtapResponse for CredentialManagementResponse {}
Expand Down Expand Up @@ -215,6 +219,7 @@ impl<'de> Deserialize<'de> for CredentialManagementResponse {
let mut total_credentials = None; // (0x09) Unsigned Integer Total number of credentials present on the authenticator for the RP in question
let mut cred_protect = None; // (0x0A) Unsigned Integer Credential protection policy.
let mut large_blob_key = None; // (0x0B) Byte string Large blob encryption key.
let mut third_party_payment = None; // (0x0C) bool

while let Some(key) = map.next_key()? {
match key {
Expand Down Expand Up @@ -294,7 +299,12 @@ impl<'de> Deserialize<'de> for CredentialManagementResponse {
// Using into_vec, to avoid any copy of large_blob_key
large_blob_key = Some(map.next_value::<ByteBuf>()?.into_vec());
}

0x0C => {
if third_party_payment.is_some() {
return Err(SerdeError::duplicate_field("third_party_payment"));
}
third_party_payment = Some(map.next_value()?);
}
k => {
warn!("ClientPinResponse: unexpected key: {:?}", k);
let _ = map.next_value::<IgnoredAny>()?;
Expand All @@ -315,6 +325,7 @@ impl<'de> Deserialize<'de> for CredentialManagementResponse {
total_credentials,
cred_protect,
large_blob_key,
third_party_payment,
})
}
}
Expand Down
61 changes: 60 additions & 1 deletion src/ctap2/commands/get_assertion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use serde::{
};
use serde_bytes::ByteBuf;
use serde_cbor::{de::from_slice, ser, Value};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt;
use std::io::Cursor;
Expand Down Expand Up @@ -258,6 +259,8 @@ pub struct GetAssertionExtensions {
pub cred_blob: Option<bool>,
#[serde(rename = "largeBlobKey", skip_serializing_if = "Option::is_none")]
pub large_blob_key: Option<bool>,
#[serde(rename = "thirdPartyPayment", skip_serializing_if = "Option::is_none")]
pub third_party_payment: Option<bool>,
}

impl From<AuthenticationExtensionsClientInputs> for GetAssertionExtensions {
Expand All @@ -281,13 +284,17 @@ impl From<AuthenticationExtensionsClientInputs> for GetAssertionExtensions {
_ => None,
},
large_blob_key: input.large_blob_key,
third_party_payment: input.third_party_payment,
}
}
}

impl GetAssertionExtensions {
fn has_content(&self) -> bool {
self.hmac_secret.is_some() || self.cred_blob.is_some() || self.large_blob_key.is_some()
self.hmac_secret.is_some()
|| self.cred_blob.is_some()
|| self.large_blob_key.is_some()
|| self.third_party_payment.is_some()
}
}

Expand All @@ -306,6 +313,10 @@ pub struct GetAssertion {
pub extensions: GetAssertionExtensions,
pub options: GetAssertionOptions,
pub pin_uv_auth_param: Option<PinUvAuthParam>,

// CTAP 2.2:
pub enterprise_attestation: Option<u64>,
pub attestation_formats_preference: Option<Vec<String>>,
}

impl GetAssertion {
Expand All @@ -323,6 +334,8 @@ impl GetAssertion {
extensions,
options,
pin_uv_auth_param: None,
enterprise_attestation: None,
attestation_formats_preference: None,
}
}

Expand Down Expand Up @@ -512,6 +525,8 @@ impl Serialize for GetAssertion {
&5 => self.options.has_some().then_some(&self.options),
&6 => &self.pin_uv_auth_param,
&7 => self.pin_uv_auth_param.as_ref().map(|p| p.pin_protocol.id()),
&8 => &self.enterprise_attestation,
&9 => &self.attestation_formats_preference,
}
}
}
Expand Down Expand Up @@ -759,6 +774,9 @@ pub struct GetAssertionResponse {
pub number_of_credentials: Option<usize>,
pub user_selected: Option<bool>,
pub large_blob_key: Option<Vec<u8>>,
pub unsigned_extension_outputs: Option<HashMap<String, serde_cbor::Value>>,
pub ep_attestation: Option<bool>,
pub att_stmt: Option<HashMap<String, serde_cbor::Value>>,
}

impl CtapResponse for GetAssertionResponse {}
Expand Down Expand Up @@ -788,6 +806,9 @@ impl<'de> Deserialize<'de> for GetAssertionResponse {
let mut number_of_credentials = None;
let mut user_selected = None;
let mut large_blob_key = None;
let mut unsigned_extension_outputs = None;
let mut ep_attestation = None;
let mut att_stmt = None;

while let Some(key) = map.next_key()? {
match key {
Expand Down Expand Up @@ -835,6 +856,26 @@ impl<'de> Deserialize<'de> for GetAssertionResponse {
let large_blob_key_bytes: ByteBuf = map.next_value()?;
large_blob_key = Some(large_blob_key_bytes.into_vec());
}
0x08 => {
if unsigned_extension_outputs.is_some() {
return Err(M::Error::duplicate_field(
"unsigned_extension_outputs",
));
}
unsigned_extension_outputs = Some(map.next_value()?);
}
0x09 => {
if ep_attestation.is_some() {
return Err(M::Error::duplicate_field("ep_attestation"));
}
ep_attestation = Some(map.next_value()?);
}
0x0A => {
if att_stmt.is_some() {
return Err(M::Error::duplicate_field("att_stmt"));
}
att_stmt = Some(map.next_value()?);
}
k => return Err(M::Error::custom(format!("unexpected key: {k:?}"))),
}
}
Expand All @@ -850,6 +891,9 @@ impl<'de> Deserialize<'de> for GetAssertionResponse {
number_of_credentials,
user_selected,
large_blob_key,
unsigned_extension_outputs,
ep_attestation,
att_stmt,
})
}
}
Expand Down Expand Up @@ -1105,12 +1149,15 @@ pub mod test {
)),
cred_blob: None,
large_blob_key: None,
third_party_payment: None,
},
options: GetAssertionOptions {
user_presence: Some(true),
user_verification: None,
},
pin_uv_auth_param: Some(PinUvAuthParam::create_empty()),
enterprise_attestation: None,
attestation_formats_preference: None,
};
let req_serialized = assertion
.wire_format()
Expand Down Expand Up @@ -1164,6 +1211,7 @@ pub mod test {
)),
cred_blob: None,
large_blob_key: None,
third_party_payment: None,
},
options: GetAssertionOptions {
user_presence: None,
Expand All @@ -1174,6 +1222,8 @@ pub mod test {
vec![9; 4],
PinUvAuthTokenPermission::GetAssertion,
)),
enterprise_attestation: None,
attestation_formats_preference: None,
};
let req_serialized = assertion
.wire_format()
Expand Down Expand Up @@ -1211,12 +1261,15 @@ pub mod test {
)),
cred_blob: None,
large_blob_key: None,
third_party_payment: None,
},
options: GetAssertionOptions {
user_presence: None,
user_verification: None,
},
pin_uv_auth_param: None,
enterprise_attestation: None,
attestation_formats_preference: None,
};
assertion
.wire_format()
Expand All @@ -1234,12 +1287,15 @@ pub mod test {
hmac_secret: Some(HmacGetSecretOrPrf::PrfUnmatched),
cred_blob: None,
large_blob_key: None,
third_party_payment: None,
},
options: GetAssertionOptions {
user_presence: None,
user_verification: None,
},
pin_uv_auth_param: None,
enterprise_attestation: None,
attestation_formats_preference: None,
};
let req_serialized = assertion
.wire_format()
Expand Down Expand Up @@ -1640,6 +1696,9 @@ pub mod test {
certifications: None,
remaining_discoverable_credentials: None,
vendor_prototype_config_commands: None,
attestation_formats: None,
uv_count_since_last_pin_entry: None,
long_touch_for_reset: None,
});

// Sending first GetAssertion with first allow_list-entry, that will return an error
Expand Down
30 changes: 29 additions & 1 deletion src/ctap2/commands/get_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ pub struct AuthenticatorInfo {
pub certifications: Option<BTreeMap<String, u64>>,
pub remaining_discoverable_credentials: Option<u64>,
pub vendor_prototype_config_commands: Option<Vec<u64>>,
// CTAP 2.2
pub attestation_formats: Option<Vec<String>>,
pub uv_count_since_last_pin_entry: Option<u64>,
pub long_touch_for_reset: Option<bool>,
}

impl AuthenticatorInfo {
Expand Down Expand Up @@ -420,6 +424,9 @@ impl<'de> Deserialize<'de> for AuthenticatorInfo {
let mut certifications = None;
let mut remaining_discoverable_credentials = None;
let mut vendor_prototype_config_commands = None;
let mut attestation_formats = None;
let mut uv_count_since_last_pin_entry = None;
let mut long_touch_for_reset = None;
while let Some(key) = map.next_key()? {
match key {
0x01 => {
Expand Down Expand Up @@ -491,6 +498,15 @@ impl<'de> Deserialize<'de> for AuthenticatorInfo {
0x15 => {
parse_next_optional_value!(vendor_prototype_config_commands, map);
}
0x16 => {
parse_next_optional_value!(attestation_formats, map);
}
0x17 => {
parse_next_optional_value!(uv_count_since_last_pin_entry, map);
}
0x18 => {
parse_next_optional_value!(long_touch_for_reset, map);
}
k => {
warn!("GetInfo: unexpected key: {:?}", k);
let _ = map.next_value::<IgnoredAny>()?;
Expand Down Expand Up @@ -537,6 +553,9 @@ impl<'de> Deserialize<'de> for AuthenticatorInfo {
certifications,
remaining_discoverable_credentials,
vendor_prototype_config_commands,
attestation_formats,
uv_count_since_last_pin_entry,
long_touch_for_reset,
})
} else {
Err(M::Error::custom("No AAGuid specified".to_string()))
Expand Down Expand Up @@ -812,6 +831,9 @@ pub mod tests {
certifications: None,
remaining_discoverable_credentials: None,
vendor_prototype_config_commands: None,
attestation_formats: None,
uv_count_since_last_pin_entry: None,
long_touch_for_reset: None,
};

assert_eq!(authenticator_info, expected);
Expand All @@ -822,7 +844,7 @@ pub mod tests {
broken_payload[0] += 1;
// Add the additional entry at the back with an invalid key
broken_payload.extend_from_slice(&[
0x17, // unsigned(23) -> invalid key-number. CTAP2.1 goes only to 0x15
0x27, // unsigned(39) -> invalid key-number. CTAP2.2 goes only to 0x18
0x6B, // text(11)
0x69, 0x6E, 0x76, 0x61, 0x6C, 0x69, 0x64, 0x5F, 0x6B, 0x65, 0x79, // "invalid_key"
]);
Expand Down Expand Up @@ -899,6 +921,9 @@ pub mod tests {
certifications: None,
remaining_discoverable_credentials: Some(24),
vendor_prototype_config_commands: None,
attestation_formats: None,
uv_count_since_last_pin_entry: None,
long_touch_for_reset: None,
};

assert_eq!(authenticator_info, expected);
Expand Down Expand Up @@ -992,6 +1017,9 @@ pub mod tests {
certifications: None,
remaining_discoverable_credentials: None,
vendor_prototype_config_commands: None,
attestation_formats: None,
uv_count_since_last_pin_entry: None,
long_touch_for_reset: None,
};

assert_eq!(result, &expected);
Expand Down
Loading