new client pin subcommands
This commit is contained in:
@@ -278,6 +278,14 @@ pub struct AuthenticatorClientPinParameters {
|
|||||||
pub pin_auth: Option<Vec<u8>>,
|
pub pin_auth: Option<Vec<u8>>,
|
||||||
pub new_pin_enc: Option<Vec<u8>>,
|
pub new_pin_enc: Option<Vec<u8>>,
|
||||||
pub pin_hash_enc: Option<Vec<u8>>,
|
pub pin_hash_enc: Option<Vec<u8>>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
pub min_pin_length: Option<u64>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
pub min_pin_length_rp_ids: Option<Vec<String>>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
pub permissions: Option<u8>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
pub permissions_rp_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
||||||
@@ -305,6 +313,39 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
|||||||
let new_pin_enc = new_pin_enc.map(extract_byte_string).transpose()?;
|
let new_pin_enc = new_pin_enc.map(extract_byte_string).transpose()?;
|
||||||
let pin_hash_enc = pin_hash_enc.map(extract_byte_string).transpose()?;
|
let pin_hash_enc = pin_hash_enc.map(extract_byte_string).transpose()?;
|
||||||
|
|
||||||
|
// TODO(kaczmarczyck) merge with new map destructuring (and use hex!)
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
let min_pin_length = param_map
|
||||||
|
.remove(&cbor_unsigned!(7))
|
||||||
|
.map(extract_unsigned)
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
let min_pin_length_rp_ids = match param_map.remove(&cbor_unsigned!(8)) {
|
||||||
|
Some(entry) => Some(
|
||||||
|
extract_array(entry)?
|
||||||
|
.into_iter()
|
||||||
|
.map(extract_text_string)
|
||||||
|
.collect::<Result<Vec<String>, Ctap2StatusCode>>()?,
|
||||||
|
),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
// We expect a bit field of 8 bits, and drop everything else.
|
||||||
|
// This means we ignore extensions in future versions.
|
||||||
|
let permissions = param_map
|
||||||
|
.remove(&cbor_unsigned!(9))
|
||||||
|
.map(extract_unsigned)
|
||||||
|
.transpose()?
|
||||||
|
.map(|p| p as u8);
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
let permissions_rp_id = param_map
|
||||||
|
.remove(&cbor_unsigned!(10))
|
||||||
|
.map(extract_text_string)
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
Ok(AuthenticatorClientPinParameters {
|
Ok(AuthenticatorClientPinParameters {
|
||||||
pin_protocol,
|
pin_protocol,
|
||||||
sub_command,
|
sub_command,
|
||||||
@@ -312,6 +353,14 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
|
|||||||
pin_auth,
|
pin_auth,
|
||||||
new_pin_enc,
|
new_pin_enc,
|
||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
min_pin_length,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
min_pin_length_rp_ids,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
permissions,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
permissions_rp_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -434,6 +483,9 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_cbor_client_pin_parameters() {
|
fn test_from_cbor_client_pin_parameters() {
|
||||||
|
// TODO(kaczmarczyck) inline the #cfg when the ICE is resolved:
|
||||||
|
// https://github.com/rust-lang/rust/issues/73663
|
||||||
|
#[cfg(not(feature = "with_ctap2_1"))]
|
||||||
let cbor_value = cbor_map! {
|
let cbor_value = cbor_map! {
|
||||||
1 => 1,
|
1 => 1,
|
||||||
2 => ClientPinSubCommand::GetPinRetries,
|
2 => ClientPinSubCommand::GetPinRetries,
|
||||||
@@ -442,6 +494,19 @@ mod test {
|
|||||||
5 => vec! [0xCC],
|
5 => vec! [0xCC],
|
||||||
6 => vec! [0xDD],
|
6 => vec! [0xDD],
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
let cbor_value = cbor_map! {
|
||||||
|
1 => 1,
|
||||||
|
2 => ClientPinSubCommand::GetPinRetries,
|
||||||
|
3 => cbor_map!{},
|
||||||
|
4 => vec! [0xBB],
|
||||||
|
5 => vec! [0xCC],
|
||||||
|
6 => vec! [0xDD],
|
||||||
|
7 => 4,
|
||||||
|
8 => cbor_array!["example.com"],
|
||||||
|
9 => 0x03,
|
||||||
|
10 => "example.com",
|
||||||
|
};
|
||||||
let returned_pin_protocol_parameters =
|
let returned_pin_protocol_parameters =
|
||||||
AuthenticatorClientPinParameters::try_from(cbor_value).unwrap();
|
AuthenticatorClientPinParameters::try_from(cbor_value).unwrap();
|
||||||
|
|
||||||
@@ -452,6 +517,14 @@ mod test {
|
|||||||
pin_auth: Some(vec![0xBB]),
|
pin_auth: Some(vec![0xBB]),
|
||||||
new_pin_enc: Some(vec![0xCC]),
|
new_pin_enc: Some(vec![0xCC]),
|
||||||
pin_hash_enc: Some(vec![0xDD]),
|
pin_hash_enc: Some(vec![0xDD]),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
min_pin_length: Some(4),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
min_pin_length_rp_ids: Some(vec!["example.com".to_string()]),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
permissions: Some(0x03),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
permissions_rp_id: Some("example.com".to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -690,9 +690,15 @@ pub enum ClientPinSubCommand {
|
|||||||
GetKeyAgreement = 0x02,
|
GetKeyAgreement = 0x02,
|
||||||
SetPin = 0x03,
|
SetPin = 0x03,
|
||||||
ChangePin = 0x04,
|
ChangePin = 0x04,
|
||||||
GetPinUvAuthTokenUsingPin = 0x05,
|
GetPinToken = 0x05,
|
||||||
GetPinUvAuthTokenUsingUv = 0x06,
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
GetPinUvAuthTokenUsingUvWithPermissions = 0x06,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
GetUvRetries = 0x07,
|
GetUvRetries = 0x07,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
SetMinPinLength = 0x08,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
GetPinUvAuthTokenUsingPinWithPermissions = 0x09,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ClientPinSubCommand> for cbor::Value {
|
impl From<ClientPinSubCommand> for cbor::Value {
|
||||||
@@ -712,10 +718,16 @@ impl TryFrom<cbor::Value> for ClientPinSubCommand {
|
|||||||
0x02 => Ok(GetKeyAgreement),
|
0x02 => Ok(GetKeyAgreement),
|
||||||
0x03 => Ok(SetPin),
|
0x03 => Ok(SetPin),
|
||||||
0x04 => Ok(ChangePin),
|
0x04 => Ok(ChangePin),
|
||||||
0x05 => Ok(GetPinUvAuthTokenUsingPin),
|
0x05 => Ok(GetPinToken),
|
||||||
0x06 => Ok(GetPinUvAuthTokenUsingUv),
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
0x06 => Ok(GetPinUvAuthTokenUsingUvWithPermissions),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
0x07 => Ok(GetUvRetries),
|
0x07 => Ok(GetUvRetries),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
0x08 => Ok(SetMinPinLength),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
0x09 => Ok(GetPinUvAuthTokenUsingPinWithPermissions),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND),
|
_ => Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND),
|
||||||
#[cfg(not(feature = "with_ctap2_1"))]
|
#[cfg(not(feature = "with_ctap2_1"))]
|
||||||
_ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
_ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
||||||
|
|||||||
@@ -849,6 +849,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pin.len() < 4 || pin.len() == PIN_PADDED_LENGTH {
|
if pin.len() < 4 || pin.len() == PIN_PADDED_LENGTH {
|
||||||
|
// TODO(kaczmarczyck) check 4 code point minimum instead
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let mut pin_hash = [0; 16];
|
let mut pin_hash = [0; 16];
|
||||||
@@ -973,7 +974,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_get_pin_uv_auth_token_using_pin(
|
fn process_get_pin_token(
|
||||||
&mut self,
|
&mut self,
|
||||||
key_agreement: CoseKey,
|
key_agreement: CoseKey,
|
||||||
pin_hash_enc: Vec<u8>,
|
pin_hash_enc: Vec<u8>,
|
||||||
@@ -1007,7 +1008,8 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_get_pin_uv_auth_token_using_uv(
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
fn process_get_pin_uv_auth_token_using_uv_with_permissions(
|
||||||
&self,
|
&self,
|
||||||
_: CoseKey,
|
_: CoseKey,
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
@@ -1019,6 +1021,7 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
fn process_get_uv_retries(&self) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
fn process_get_uv_retries(&self) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
// User verifications is only supported through PIN currently.
|
// User verifications is only supported through PIN currently.
|
||||||
Ok(AuthenticatorClientPinResponse {
|
Ok(AuthenticatorClientPinResponse {
|
||||||
@@ -1028,6 +1031,37 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
fn process_set_min_pin_length(
|
||||||
|
&mut self,
|
||||||
|
_min_pin_length: u64,
|
||||||
|
_min_pin_length_rp_ids: Vec<String>,
|
||||||
|
_pin_auth: Vec<u8>,
|
||||||
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
|
// TODO
|
||||||
|
Ok(AuthenticatorClientPinResponse {
|
||||||
|
key_agreement: None,
|
||||||
|
pin_token: None,
|
||||||
|
retries: Some(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||||
|
&mut self,
|
||||||
|
_key_agreement: CoseKey,
|
||||||
|
_pin_hash_enc: Vec<u8>,
|
||||||
|
_permissions: u8,
|
||||||
|
_permissions_rp_id: String,
|
||||||
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
|
// TODO
|
||||||
|
Ok(AuthenticatorClientPinResponse {
|
||||||
|
key_agreement: None,
|
||||||
|
pin_token: None,
|
||||||
|
retries: Some(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn process_client_pin(
|
fn process_client_pin(
|
||||||
&mut self,
|
&mut self,
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
@@ -1039,6 +1073,14 @@ where
|
|||||||
pin_auth,
|
pin_auth,
|
||||||
new_pin_enc,
|
new_pin_enc,
|
||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
min_pin_length,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
min_pin_length_rp_ids,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
permissions,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
permissions_rp_id,
|
||||||
} = client_pin_params;
|
} = client_pin_params;
|
||||||
|
|
||||||
if pin_protocol != 1 {
|
if pin_protocol != 1 {
|
||||||
@@ -1065,18 +1107,37 @@ where
|
|||||||
)?;
|
)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingPin => {
|
ClientPinSubCommand::GetPinToken => Some(self.process_get_pin_token(
|
||||||
Some(self.process_get_pin_uv_auth_token_using_pin(
|
|
||||||
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
pin_hash_enc.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
pin_hash_enc.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
)?)
|
)?),
|
||||||
}
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingUv => {
|
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some(
|
||||||
Some(self.process_get_pin_uv_auth_token_using_uv(
|
self.process_get_pin_uv_auth_token_using_uv_with_permissions(
|
||||||
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
)?)
|
)?,
|
||||||
}
|
),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
ClientPinSubCommand::GetUvRetries => Some(self.process_get_uv_retries()?),
|
ClientPinSubCommand::GetUvRetries => Some(self.process_get_uv_retries()?),
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
ClientPinSubCommand::SetMinPinLength => {
|
||||||
|
self.process_set_min_pin_length(
|
||||||
|
min_pin_length.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
|
min_pin_length_rp_ids.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
|
pin_auth.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
|
)?;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions => {
|
||||||
|
self.process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||||
|
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
|
pin_hash_enc.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
|
permissions.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
|
permissions_rp_id.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
|
)?;
|
||||||
|
None
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(ResponseData::AuthenticatorClientPin(response))
|
Ok(ResponseData::AuthenticatorClientPin(response))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user