implements alwaysUv and makeCredUvNotRqd

This commit is contained in:
Fabian Kaczmarczyck
2021-02-05 18:43:27 +01:00
parent c293708649
commit f90d43a6a1
8 changed files with 262 additions and 12 deletions

View File

@@ -341,6 +341,14 @@ where
Ok(())
}
// Returns whether CTAP1 commands are currently supported.
// If alwaysUv is enabled and the authenticator does not support internal UV,
// CTAP1 needs to be disabled.
#[cfg(feature = "with_ctap1")]
pub fn allows_ctap1(&self) -> Result<bool, Ctap2StatusCode> {
Ok(!self.persistent_store.has_always_uv()?)
}
// Encrypts the private key and relying party ID hash into a credential ID. Other
// information, such as a user name, are not stored, because encrypted credential IDs
// are used for credentials stored server-side. Also, we want the key handle to be
@@ -642,7 +650,11 @@ where
UP_FLAG | UV_FLAG | AT_FLAG | ed_flag
}
None => {
if self.persistent_store.pin_hash()?.is_some() {
if self.persistent_store.has_always_uv()? {
return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED);
}
// Corresponds to makeCredUvNotRqd set to true.
if options.rk && self.persistent_store.pin_hash()?.is_some() {
return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED);
}
if options.uv {
@@ -927,8 +939,10 @@ where
UV_FLAG
}
None => {
if self.persistent_store.has_always_uv()? {
return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED);
}
if options.uv {
// The specification (inconsistently) wants CTAP2_ERR_UNSUPPORTED_OPTION.
return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION);
}
0x00
@@ -1017,6 +1031,23 @@ where
}
fn process_get_info(&self) -> Result<ResponseData, Ctap2StatusCode> {
let has_always_uv = self.persistent_store.has_always_uv()?;
#[cfg(feature = "with_ctap1")]
let mut versions = vec![
String::from(FIDO2_VERSION_STRING),
String::from(FIDO2_1_VERSION_STRING),
];
#[cfg(feature = "with_ctap1")]
{
if !has_always_uv {
versions.insert(0, String::from(U2F_VERSION_STRING))
}
}
#[cfg(not(feature = "with_ctap1"))]
let versions = vec![
String::from(FIDO2_VERSION_STRING),
String::from(FIDO2_1_VERSION_STRING),
];
let mut options_map = BTreeMap::new();
options_map.insert(String::from("rk"), true);
options_map.insert(
@@ -1029,15 +1060,11 @@ where
options_map.insert(String::from("authnrCfg"), true);
options_map.insert(String::from("credMgmt"), true);
options_map.insert(String::from("setMinPINLength"), true);
options_map.insert(String::from("makeCredUvNotRqd"), true);
options_map.insert(String::from("makeCredUvNotRqd"), !has_always_uv);
options_map.insert(String::from("alwaysUv"), has_always_uv);
Ok(ResponseData::AuthenticatorGetInfo(
AuthenticatorGetInfoResponse {
versions: vec![
#[cfg(feature = "with_ctap1")]
String::from(U2F_VERSION_STRING),
String::from(FIDO2_VERSION_STRING),
String::from(FIDO2_1_VERSION_STRING),
],
versions,
extensions: Some(vec![
String::from("hmac-secret"),
String::from("credProtect"),
@@ -1286,6 +1313,7 @@ mod test {
"credMgmt" => true,
"setMinPINLength" => true,
"makeCredUvNotRqd" => true,
"alwaysUv" => false,
},
0x05 => MAX_MSG_SIZE as u64,
0x06 => cbor_array_vec![vec![1]],
@@ -1727,6 +1755,70 @@ mod test {
assert_eq!(stored_credential.large_blob_key.unwrap(), large_blob_key);
}
#[test]
fn test_non_resident_process_make_credential_with_pin() {
let mut rng = ThreadRng256 {};
let user_immediately_present = |_| Ok(());
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.persistent_store.set_pin(&[0x88; 16], 4).unwrap();
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.options.rk = false;
let make_credential_response =
ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID);
check_make_response(
make_credential_response,
0x41,
&ctap_state.persistent_store.aaguid().unwrap(),
0x70,
&[],
);
}
#[test]
fn test_resident_process_make_credential_with_pin() {
let mut rng = ThreadRng256 {};
let user_immediately_present = |_| Ok(());
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.persistent_store.set_pin(&[0x88; 16], 4).unwrap();
let make_credential_params = create_minimal_make_credential_parameters();
let make_credential_response =
ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID);
assert_eq!(
make_credential_response,
Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)
);
}
#[test]
fn test_process_make_credential_with_pin_always_uv() {
let mut rng = ThreadRng256 {};
let user_immediately_present = |_| Ok(());
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.persistent_store.toggle_always_uv().unwrap();
let make_credential_params = create_minimal_make_credential_parameters();
let make_credential_response =
ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID);
assert_eq!(
make_credential_response,
Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)
);
ctap_state.persistent_store.set_pin(&[0x88; 16], 4).unwrap();
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.pin_uv_auth_param = Some(vec![0xA4; 16]);
make_credential_params.pin_uv_auth_protocol = Some(1);
let make_credential_response =
ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID);
assert_eq!(
make_credential_response,
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
);
}
#[test]
fn test_process_make_credential_cancelled() {
let mut rng = ThreadRng256 {};