diff --git a/crates/ctap2-proto/src/authenticator/client_pin/mod.rs b/crates/ctap2-proto/src/authenticator/client_pin/mod.rs index 5cf7006..675fe96 100644 --- a/crates/ctap2-proto/src/authenticator/client_pin/mod.rs +++ b/crates/ctap2-proto/src/authenticator/client_pin/mod.rs @@ -11,7 +11,7 @@ pub mod auth_protocol; mod raw; #[cfg(feature = "serde")] -use raw::RawRequest; +use raw::{RawRequest, RawResponse}; pub type PinUvAuthParam = [u8; 16]; @@ -112,6 +112,12 @@ pub enum Request<'a> { }, } +#[derive(Clone, Debug)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(into = "RawResponse", try_from = "RawResponse") +)] pub enum Response { GetPinRetries { pin_retries: usize, diff --git a/crates/ctap2-proto/src/authenticator/client_pin/raw/mod.rs b/crates/ctap2-proto/src/authenticator/client_pin/raw/mod.rs index 2a0f3c8..b023cc6 100644 --- a/crates/ctap2-proto/src/authenticator/client_pin/raw/mod.rs +++ b/crates/ctap2-proto/src/authenticator/client_pin/raw/mod.rs @@ -183,6 +183,127 @@ impl<'a> TryFrom> for Request<'a> { } } +#[serde_as] +#[derive(Serialize, Deserialize)] +pub(crate) struct RawResponse { + #[serde( + rename = 0x01, + default, // Allows for None variant to be deserialized when 0x01 is not present, required + // because of deserialize_with + deserialize_with = "public_key::deserialize", + skip_serializing_if = "Option::is_none", + )] + pub key_agreement: Option, + #[serde_as(as = "Option")] + #[serde(rename = 0x02, skip_serializing_if = "Option::is_none")] + pub pin_uv_auth_token: Option, + #[serde(rename = 0x03, skip_serializing_if = "Option::is_none")] + pub pin_retries: Option, + #[serde(rename = 0x04, skip_serializing_if = "Option::is_none")] + pub power_cycle_state: Option, + #[serde(rename = 0x05, skip_serializing_if = "Option::is_none")] + pub uv_retries: Option, +} + +impl From for RawResponse { + fn from(value: Response) -> Self { + match value { + Response::GetPinRetries { + pin_retries, + power_cycle_state, + } => Self { + key_agreement: None, + pin_uv_auth_token: None, + pin_retries: Some(pin_retries), + power_cycle_state, + uv_retries: None, + }, + Response::GetKeyAgreement { key_agreement } => Self { + key_agreement: Some(key_agreement), + pin_uv_auth_token: None, + pin_retries: None, + power_cycle_state: None, + uv_retries: None, + }, + Response::SetPin => Self { + key_agreement: None, + pin_uv_auth_token: None, + pin_retries: None, + power_cycle_state: None, + uv_retries: None, + }, + Response::ChangePin => Self { + key_agreement: None, + pin_uv_auth_token: None, + pin_retries: None, + power_cycle_state: None, + uv_retries: None, + }, + Response::GetPinToken { pin_uv_auth_token } => Self { + key_agreement: None, + pin_uv_auth_token: Some(pin_uv_auth_token), + pin_retries: None, + power_cycle_state: None, + uv_retries: None, + }, + Response::GetPinUvAuthTokenUsingUvWithPermissions { pin_uv_auth_token } => Self { + key_agreement: None, + pin_uv_auth_token: Some(pin_uv_auth_token), + pin_retries: None, + power_cycle_state: None, + uv_retries: None, + }, + Response::GetUvRetries { uv_retries } => Self { + key_agreement: None, + pin_uv_auth_token: None, + pin_retries: None, + power_cycle_state: None, + uv_retries: Some(uv_retries.get()), + }, + Response::GetPinUvAuthTokenUsingPinWithPermissions { pin_uv_auth_token } => Self { + key_agreement: None, + pin_uv_auth_token: Some(pin_uv_auth_token), + pin_retries: None, + power_cycle_state: None, + uv_retries: None, + }, + } + } +} + +impl TryFrom for Response { + type Error = Error; + + fn try_from(value: RawResponse) -> Result { + Ok(match value { + RawResponse { + key_agreement: None, + pin_uv_auth_token: None, + pin_retries: Some(pin_retries), + power_cycle_state, + uv_retries: None, + } => Response::GetPinRetries { + pin_retries, + power_cycle_state, + }, + RawResponse { + key_agreement: Some(key_agreement), + pin_uv_auth_token: None, + pin_retries: None, + power_cycle_state: None, + uv_retries: None, + } => Response::GetKeyAgreement { key_agreement }, + RawResponse { + key_agreement: None, + pin_uv_auth_token: Some(pin_uv_auth_token), + pin_retries: None, + power_cycle_state: None, + uv_retries: None, + } => Response::GetPinToken { pin_uv_auth_token }, + _ => todo!(), + }) + } +} flags! { #[derive(Serialize, Deserialize)]