use super::{Command, CommandError, PinUvAuthCommand, RequestCtap2, StatusCode};
use crate::{
crypto::{PinUvAuthParam, PinUvAuthToken},
ctap2::server::UserVerificationRequirement,
errors::AuthenticatorError,
transport::errors::HIDError,
AuthenticatorInfo, FidoDevice,
};
use serde::{Deserialize, Serialize, Serializer};
use serde_cbor::{de::from_slice, to_vec, Value};
#[derive(Debug, Clone, Deserialize)]
pub struct SetMinPINLength {
pub new_min_pin_length: Option<u64>,
pub min_pin_length_rpids: Option<Vec<String>>,
pub force_change_pin: Option<bool>,
}
impl Serialize for SetMinPINLength {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_map_optional!(
serializer,
&0x01 => self.new_min_pin_length,
&0x02 => &self.min_pin_length_rpids,
&0x03 => self.force_change_pin,
)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum AuthConfigCommand {
EnableEnterpriseAttestation,
ToggleAlwaysUv,
SetMinPINLength(SetMinPINLength),
}
impl AuthConfigCommand {
fn has_params(&self) -> bool {
match self {
AuthConfigCommand::EnableEnterpriseAttestation => false,
AuthConfigCommand::ToggleAlwaysUv => false,
AuthConfigCommand::SetMinPINLength(..) => true,
}
}
}
#[derive(Debug, Serialize)]
pub enum AuthConfigResult {
Success(AuthenticatorInfo),
}
#[derive(Debug)]
pub struct AuthenticatorConfig {
subcommand: AuthConfigCommand, pin_uv_auth_param: Option<PinUvAuthParam>, }
impl AuthenticatorConfig {
pub(crate) fn new(subcommand: AuthConfigCommand) -> Self {
Self {
subcommand,
pin_uv_auth_param: None,
}
}
}
impl Serialize for AuthenticatorConfig {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let (entry01, entry02) = match &self.subcommand {
AuthConfigCommand::EnableEnterpriseAttestation => (&0x01, None),
AuthConfigCommand::ToggleAlwaysUv => (&0x02, None),
AuthConfigCommand::SetMinPINLength(params) => (&0x03, Some(params)),
};
serialize_map_optional!(
serializer,
&0x01 => Some(entry01),
&0x02 => entry02,
&0x03 => self.pin_uv_auth_param.as_ref().map(|p| p.pin_protocol.id()),
&0x04 => &self.pin_uv_auth_param,
)
}
}
impl RequestCtap2 for AuthenticatorConfig {
type Output = ();
fn command(&self) -> Command {
Command::AuthenticatorConfig
}
fn wire_format(&self) -> Result<Vec<u8>, HIDError> {
let output = to_vec(&self).map_err(CommandError::Serializing)?;
trace!("client subcommmand: {:04X?}", &output);
Ok(output)
}
fn handle_response_ctap2<Dev>(
&self,
_dev: &mut Dev,
input: &[u8],
) -> Result<Self::Output, HIDError>
where
Dev: FidoDevice,
{
if input.is_empty() {
return Err(CommandError::InputTooSmall.into());
}
let status: StatusCode = input[0].into();
if status.is_ok() {
Ok(())
} else {
let msg = if input.len() > 1 {
let data: Value = from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
Some(data)
} else {
None
};
Err(CommandError::StatusCode(status, msg).into())
}
}
fn send_to_virtual_device<Dev: crate::VirtualFidoDevice>(
&self,
_dev: &mut Dev,
) -> Result<Self::Output, HIDError> {
unimplemented!()
}
}
impl PinUvAuthCommand for AuthenticatorConfig {
fn set_pin_uv_auth_param(
&mut self,
pin_uv_auth_token: Option<PinUvAuthToken>,
) -> Result<(), AuthenticatorError> {
let mut param = None;
if let Some(token) = pin_uv_auth_token {
let mut data = vec![0xff; 32];
data.push(0x0D);
match &self.subcommand {
AuthConfigCommand::EnableEnterpriseAttestation => {
data.push(0x01);
}
AuthConfigCommand::ToggleAlwaysUv => {
data.push(0x02);
}
AuthConfigCommand::SetMinPINLength(params) => {
data.push(0x03);
data.extend(to_vec(params).map_err(CommandError::Serializing)?);
}
}
param = Some(token.derive(&data).map_err(CommandError::Crypto)?);
}
self.pin_uv_auth_param = param;
Ok(())
}
fn can_skip_user_verification(
&mut self,
authinfo: &AuthenticatorInfo,
_uv_req: UserVerificationRequirement,
) -> bool {
!authinfo.device_is_protected()
}
fn set_uv_option(&mut self, _uv: Option<bool>) {
}
fn get_pin_uv_auth_param(&self) -> Option<&PinUvAuthParam> {
self.pin_uv_auth_param.as_ref()
}
fn get_rp_id(&self) -> Option<&String> {
None
}
fn hmac_requested(&self) -> bool {
false
}
}
#[cfg(test)]
mod test {
use crate::{crypto::PinUvAuthParam, ctap2::commands::client_pin::PinUvAuthTokenPermission};
use super::{AuthConfigCommand, AuthenticatorConfig, SetMinPINLength};
#[test]
fn test_serialize_set_min_pin_length() {
let set_min_pin_length = SetMinPINLength {
new_min_pin_length: Some(42),
min_pin_length_rpids: Some(vec![
"example.org".to_string(),
"www.example.org".to_string(),
]),
force_change_pin: Some(true),
};
let serialized =
serde_cbor::ser::to_vec(&set_min_pin_length).expect("Failed to serialize to CBOR");
assert_eq!(
serialized,
[
163, 1, 24, 42, 2, 130, 107, 101, 120, 97, 109, 112, 108, 101, 46, 111, 114, 103,
111, 119, 119, 119, 46, 101, 120, 97, 109, 112, 108, 101, 46, 111, 114, 103, 3,
245
]
);
}
#[test]
fn test_serialize_authenticator_config() {
let pin_uv_auth_param = Some(PinUvAuthParam::create_test(
2,
vec![1, 2, 3, 4],
PinUvAuthTokenPermission::CredentialManagement,
));
{
let authenticator_config = AuthenticatorConfig {
subcommand: AuthConfigCommand::EnableEnterpriseAttestation,
pin_uv_auth_param: pin_uv_auth_param.clone(),
};
let serialized = serde_cbor::ser::to_vec(&authenticator_config)
.expect("Failed to serialize to CBOR");
assert_eq!(
serialized,
[
163, 1, 1, 3, 2, 4, 68, 1, 2, 3, 4
]
);
}
{
let authenticator_config = AuthenticatorConfig {
subcommand: AuthConfigCommand::ToggleAlwaysUv,
pin_uv_auth_param: pin_uv_auth_param.clone(),
};
let serialized = serde_cbor::ser::to_vec(&authenticator_config)
.expect("Failed to serialize to CBOR");
assert_eq!(
serialized,
[
163, 1, 2, 3, 2, 4, 68, 1, 2, 3, 4
]
);
}
{
let authenticator_config = AuthenticatorConfig {
subcommand: AuthConfigCommand::SetMinPINLength(SetMinPINLength {
new_min_pin_length: Some(42),
min_pin_length_rpids: Some(vec![
"example.org".to_string(),
"www.example.org".to_string(),
]),
force_change_pin: Some(true),
}),
pin_uv_auth_param,
};
let serialized = serde_cbor::ser::to_vec(&authenticator_config)
.expect("Failed to serialize to CBOR");
assert_eq!(
serialized,
[
164, 1, 3, 2, 163, 1, 24, 42, 2, 130, 107, 101, 120, 97, 109, 112, 108, 101, 46,
111, 114, 103, 111, 119, 119, 119, 46, 101, 120, 97, 109, 112, 108, 101, 46,
111, 114, 103, 3, 245, 3, 2, 4, 68, 1, 2, 3, 4
]
);
}
}
}