new client pin subcommands

This commit is contained in:
Fabian Kaczmarczyck
2020-06-18 16:10:08 +02:00
parent a4becf9aca
commit 63aef3bd76
3 changed files with 161 additions and 15 deletions

View File

@@ -278,6 +278,14 @@ pub struct AuthenticatorClientPinParameters {
pub pin_auth: Option<Vec<u8>>,
pub new_pin_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 {
@@ -305,6 +313,39 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
let new_pin_enc = new_pin_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 {
pin_protocol,
sub_command,
@@ -312,6 +353,14 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
pin_auth,
new_pin_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]
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! {
1 => 1,
2 => ClientPinSubCommand::GetPinRetries,
@@ -442,6 +494,19 @@ mod test {
5 => vec! [0xCC],
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 =
AuthenticatorClientPinParameters::try_from(cbor_value).unwrap();
@@ -452,6 +517,14 @@ mod test {
pin_auth: Some(vec![0xBB]),
new_pin_enc: Some(vec![0xCC]),
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!(

View File

@@ -690,9 +690,15 @@ pub enum ClientPinSubCommand {
GetKeyAgreement = 0x02,
SetPin = 0x03,
ChangePin = 0x04,
GetPinUvAuthTokenUsingPin = 0x05,
GetPinUvAuthTokenUsingUv = 0x06,
GetPinToken = 0x05,
#[cfg(feature = "with_ctap2_1")]
GetPinUvAuthTokenUsingUvWithPermissions = 0x06,
#[cfg(feature = "with_ctap2_1")]
GetUvRetries = 0x07,
#[cfg(feature = "with_ctap2_1")]
SetMinPinLength = 0x08,
#[cfg(feature = "with_ctap2_1")]
GetPinUvAuthTokenUsingPinWithPermissions = 0x09,
}
impl From<ClientPinSubCommand> for cbor::Value {
@@ -712,10 +718,16 @@ impl TryFrom<cbor::Value> for ClientPinSubCommand {
0x02 => Ok(GetKeyAgreement),
0x03 => Ok(SetPin),
0x04 => Ok(ChangePin),
0x05 => Ok(GetPinUvAuthTokenUsingPin),
0x06 => Ok(GetPinUvAuthTokenUsingUv),
0x05 => Ok(GetPinToken),
#[cfg(feature = "with_ctap2_1")]
0x06 => Ok(GetPinUvAuthTokenUsingUvWithPermissions),
#[cfg(feature = "with_ctap2_1")]
0x07 => Ok(GetUvRetries),
#[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),
#[cfg(not(feature = "with_ctap2_1"))]
_ => Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),

View File

@@ -849,6 +849,7 @@ where
}
}
if pin.len() < 4 || pin.len() == PIN_PADDED_LENGTH {
// TODO(kaczmarczyck) check 4 code point minimum instead
return false;
}
let mut pin_hash = [0; 16];
@@ -973,7 +974,7 @@ where
Ok(())
}
fn process_get_pin_uv_auth_token_using_pin(
fn process_get_pin_token(
&mut self,
key_agreement: CoseKey,
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,
_: CoseKey,
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
@@ -1019,6 +1021,7 @@ where
})
}
#[cfg(feature = "with_ctap2_1")]
fn process_get_uv_retries(&self) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
// User verifications is only supported through PIN currently.
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(
&mut self,
client_pin_params: AuthenticatorClientPinParameters,
@@ -1039,6 +1073,14 @@ where
pin_auth,
new_pin_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;
if pin_protocol != 1 {
@@ -1065,18 +1107,37 @@ where
)?;
None
}
ClientPinSubCommand::GetPinUvAuthTokenUsingPin => {
Some(self.process_get_pin_uv_auth_token_using_pin(
ClientPinSubCommand::GetPinToken => Some(self.process_get_pin_token(
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
pin_hash_enc.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
)?),
#[cfg(feature = "with_ctap2_1")]
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some(
self.process_get_pin_uv_auth_token_using_uv_with_permissions(
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
)?,
),
#[cfg(feature = "with_ctap2_1")]
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
}
ClientPinSubCommand::GetPinUvAuthTokenUsingUv => {
Some(self.process_get_pin_uv_auth_token_using_uv(
key_agreement.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
)?)
}
ClientPinSubCommand::GetUvRetries => Some(self.process_get_uv_retries()?),
};
Ok(ResponseData::AuthenticatorClientPin(response))
}