// Check if the device is actually a token fn is_u2f(&mutself) -> bool; fn should_try_ctap2(&self) -> bool; fn get_authenticator_info(&self) -> Option<&AuthenticatorInfo>; fn set_authenticator_info(&mutself, authenticator_info: AuthenticatorInfo); fn refresh_authenticator_info(&mutself) -> Option<&AuthenticatorInfo> { let command = GetInfo::default(); iflet Ok(info) = self.send_cbor(&command) {
debug!("Refreshed authenticator info: {:?}", info); self.set_authenticator_info(info);
} self.get_authenticator_info()
}
// `get_protocol()` indicates whether we're using CTAP1 or CTAP2. // Prior to initializing the device, `get_protocol()` should return CTAP2 unless // there's a reason to believe that the device does not support CTAP2 (e.g. if // it's a HID device and it does not have the CBOR capability). fn get_protocol(&self) -> FidoProtocol;
// We do not provide a generic `set_protocol(..)` function as this would have complicated // interactions with the AuthenticatorInfo state. fn downgrade_to_ctap1(&mutself);
self.downgrade_to_ctap1(); // We want to return an error here if this device doesn't support CTAP1, // so we send a U2F_VERSION command. let command = GetVersion::default(); self.send_ctap1(&command)?;
Ok(())
}
fn block_and_blink(&mutself, keep_alive: &dynFn() -> bool) -> BlinkResult { let supports_select_cmd = self.get_protocol() == FidoProtocol::CTAP2
&& self.get_authenticator_info().map_or(false, |i| {
i.versions.contains(&AuthenticatorVersion::FIDO_2_1)
}); let resp = if supports_select_cmd { let msg = Selection {}; self.send_cbor_cancellable(&msg, keep_alive)
} else { // We need to fake a blink-request, because FIDO2.0 forgot to specify one // See: https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorMakeCredential let msg = dummy_make_credentials_cmd();
info!("Trying to blink: {:?}", &msg); // We don't care about the Ok-value, just if it is Ok or not self.send_msg_cancellable(&msg, keep_alive).map(|_| ())
};
match resp { // Spec only says PinInvalid or PinNotSet should be returned on the fake touch-request, // but Yubikeys for example return PinAuthInvalid. A successful return is also possible // for CTAP1-tokens so we catch those here as well.
Ok(_)
| Err(HIDError::Command(CommandError::StatusCode(StatusCode::PinInvalid, _)))
| Err(HIDError::Command(CommandError::StatusCode(StatusCode::PinAuthInvalid, _)))
| Err(HIDError::Command(CommandError::StatusCode(StatusCode::PinNotSet, _))) => {
BlinkResult::DeviceSelected
} // We cancelled the receive, because another device was selected.
Err(HIDError::Command(CommandError::StatusCode(StatusCode::KeepaliveCancel, _)))
| Err(HIDError::Command(CommandError::StatusCode(StatusCode::OperationDenied, _)))
| Err(HIDError::Command(CommandError::StatusCode(StatusCode::UserActionTimeout, _))) => { // TODO: Repeat the request, if it is a UserActionTimeout?
debug!("Device {:?} got cancelled", &self);
BlinkResult::Cancelled
} // Something unexpected happened, so we assume this device is not usable and // interpreting this equivalent to being cancelled.
e => {
info!("Device {:?} received unexpected answer, so we assume an error occurred and we are NOT using this device (assuming the request was cancelled): {:?}", &self, e);
BlinkResult::Cancelled
}
}
}
fn establish_shared_secret(
&mutself,
alive: &dynFn() -> bool,
) -> Result<SharedSecret, HIDError> { // CTAP1 devices don't support establishing a shared secret let info = match (self.get_protocol(), self.get_authenticator_info()) {
(FidoProtocol::CTAP2, Some(info)) => info,
_ => return Err(HIDError::UnsupportedCommand),
};
let pin_protocol = PinUvAuthProtocol::try_from(info)?;
// Not reusing the shared secret here, if it exists, since we might start again // with a different PIN (e.g. if the last one was wrong) let pin_command = GetKeyAgreement::new(pin_protocol.clone()); let resp = self.send_cbor_cancellable(&pin_command, alive)?; iflet Some(device_key_agreement_key) = resp.key_agreement { let shared_secret = pin_protocol
.encapsulate(&device_key_agreement_key)
.map_err(CommandError::from)?; self.set_shared_secret(shared_secret.clone());
Ok(shared_secret)
} else {
Err(HIDError::Command(CommandError::MissingRequiredField( "key_agreement",
)))
}
}
/// CTAP 2.0-only version: /// "Getting pinUvAuthToken using getPinToken (superseded)" fn get_pin_token(
&mutself,
pin: &Option<Pin>,
alive: &dynFn() -> bool,
) -> Result<PinUvAuthToken, HIDError> { // Asking the user for PIN before establishing the shared secret let pin = pin
.as_ref()
.ok_or(CommandError::StatusCode(StatusCode::PinRequired, None))?;
// Not reusing the shared secret here, if it exists, since we might start again // with a different PIN (e.g. if the last one was wrong) let shared_secret = self.establish_shared_secret(alive)?;
let pin_command = GetPinToken::new(&shared_secret, pin); let resp = self.send_cbor_cancellable(&pin_command, alive)?; iflet Some(encrypted_pin_token) = resp.pin_token { // CTAP 2.1 spec: // If authenticatorClientPIN's getPinToken subcommand is invoked, default permissions // of `mc` and `ga` (value 0x03) are granted for the returned pinUvAuthToken. let default_permissions = PinUvAuthTokenPermission::default(); let pin_token = shared_secret
.decrypt_pin_token(default_permissions, encrypted_pin_token.as_ref())
.map_err(CommandError::from)?;
Ok(pin_token)
} else {
Err(HIDError::Command(CommandError::MissingRequiredField( "pin_token",
)))
}
}
fn get_pin_uv_auth_token_using_uv_with_permissions(
&mutself,
permission: PinUvAuthTokenPermission,
rp_id: Option<&String>,
alive: &dynFn() -> bool,
) -> Result<PinUvAuthToken, HIDError> { // Explicitly not reusing the shared secret here let shared_secret = self.establish_shared_secret(alive)?; let pin_command = GetPinUvAuthTokenUsingUvWithPermissions::new(
&shared_secret,
permission,
rp_id.cloned(),
);
let resp = self.send_cbor_cancellable(&pin_command, alive)?;
fn get_pin_uv_auth_token_using_pin_with_permissions(
&mutself,
pin: &Option<Pin>,
permission: PinUvAuthTokenPermission,
rp_id: Option<&String>,
alive: &dynFn() -> bool,
) -> Result<PinUvAuthToken, HIDError> { // Asking the user for PIN before establishing the shared secret let pin = pin
.as_ref()
.ok_or(CommandError::StatusCode(StatusCode::PinRequired, None))?;
// Not reusing the shared secret here, if it exists, since we might start again // with a different PIN (e.g. if the last one was wrong) let shared_secret = self.establish_shared_secret(alive)?; let pin_command = GetPinUvAuthTokenUsingPinWithPermissions::new(
&shared_secret,
pin,
permission,
rp_id.cloned(),
);
let resp = self.send_cbor_cancellable(&pin_command, alive)?;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.