Skip to content

Commit

Permalink
New struct members added in CTAP 2.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Sirringhaus authored and msirringhaus committed Jul 30, 2024
1 parent 77572e2 commit 8eaa0fd
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 8 deletions.
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 @@ -508,6 +521,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 @@ -755,6 +770,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 @@ -784,6 +802,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 @@ -831,6 +852,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 @@ -846,6 +887,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 @@ -1101,12 +1145,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 @@ -1160,6 +1207,7 @@ pub mod test {
)),
cred_blob: None,
large_blob_key: None,
third_party_payment: None,
},
options: GetAssertionOptions {
user_presence: None,
Expand All @@ -1170,6 +1218,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 @@ -1207,12 +1257,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 @@ -1230,12 +1283,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 @@ -1636,6 +1692,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 @@ -337,6 +337,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 @@ -418,6 +422,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 @@ -489,6 +496,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 @@ -535,6 +551,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 @@ -776,6 +795,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 @@ -786,7 +808,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 @@ -863,6 +885,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 @@ -956,6 +981,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

0 comments on commit 8eaa0fd

Please sign in to comment.