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 committed Feb 21, 2024
1 parent 30a9f76 commit 6e63811
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 15 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 @@ -169,6 +169,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 @@ -248,6 +252,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 @@ -327,7 +332,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 @@ -348,6 +358,7 @@ impl<'de> Deserialize<'de> for CredentialManagementResponse {
total_credentials,
cred_protect,
large_blob_key,
third_party_payment,
})
}
}
Expand Down
73 changes: 65 additions & 8 deletions src/ctap2/commands/get_assertion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use serde::{
};
use serde_bytes::ByteBuf;
use serde_cbor::{de::from_slice, ser, Value};
use std::collections::HashMap;
use std::fmt;
use std::io::Cursor;

Expand Down Expand Up @@ -145,6 +146,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 @@ -156,14 +159,18 @@ impl From<AuthenticationExtensionsClientInputs> for GetAssertionExtensions {
_ => None,
},
large_blob_key: input.large_blob_key,
third_party_payment: input.third_party_payment,
..Default::default()
}
}
}

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 @@ -182,6 +189,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 @@ -199,6 +210,8 @@ impl GetAssertion {
extensions,
options,
pin_uv_auth_param: None,
enterprise_attestation: None,
attestation_formats_preference: None,
}
}

Expand Down Expand Up @@ -287,22 +300,34 @@ impl Serialize for GetAssertion {
if self.pin_uv_auth_param.is_some() {
map_len += 2;
}
if self.enterprise_attestation.is_some() {
map_len += 1;
}
if self.attestation_formats_preference.is_some() {
map_len += 1;
}

let mut map = serializer.serialize_map(Some(map_len))?;
map.serialize_entry(&1, &self.rp.id)?;
map.serialize_entry(&2, &self.client_data_hash)?;
map.serialize_entry(&0x01, &self.rp.id)?;
map.serialize_entry(&0x02, &self.client_data_hash)?;
if !self.allow_list.is_empty() {
map.serialize_entry(&3, &self.allow_list)?;
map.serialize_entry(&0x03, &self.allow_list)?;
}
if self.extensions.has_content() {
map.serialize_entry(&4, &self.extensions)?;
map.serialize_entry(&0x04, &self.extensions)?;
}
if self.options.has_some() {
map.serialize_entry(&5, &self.options)?;
map.serialize_entry(&0x05, &self.options)?;
}
if let Some(pin_uv_auth_param) = &self.pin_uv_auth_param {
map.serialize_entry(&6, &pin_uv_auth_param)?;
map.serialize_entry(&7, &pin_uv_auth_param.pin_protocol.id())?;
map.serialize_entry(&0x06, &pin_uv_auth_param)?;
map.serialize_entry(&0x07, &pin_uv_auth_param.pin_protocol.id())?;
}
if let Some(enterprise_attestation) = &self.enterprise_attestation {
map.serialize_entry(&0x08, &enterprise_attestation)?;
}
if let Some(attestation_formats_preference) = &self.attestation_formats_preference {
map.serialize_entry(&0x09, &attestation_formats_preference)?;
}
map.end()
}
Expand Down Expand Up @@ -551,6 +576,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 @@ -580,6 +608,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 @@ -627,6 +658,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 @@ -642,6 +693,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 @@ -1228,6 +1282,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 6e63811

Please sign in to comment.