Make YubiKey::open() more robust (#504)

On systems with a physical card-reader, the previous implementation falsely
reports "multiple YubiKeys detected!", even if only one YubiKey is connected.
This change attempts to actually open each reader as a YubiKey, and only
reports "multiple YubiKeys" if it can actually open more than one.

Additionally, this change avoids resetting the YubiKeys in case we find more
than one.
This commit is contained in:
hko-s
2023-08-16 00:20:04 +02:00
committed by GitHub
parent 9932d05428
commit 485d49a6c8
+25 -8
View File
@@ -178,28 +178,45 @@ impl fmt::Debug for YubiKey {
impl YubiKey {
/// Open a connection to a YubiKey.
///
/// Returns an error if there is more than one YubiKey detected.
/// Returns an error if more than one YubiKey is detected (or none at all).
///
/// NOTE: If multiple YubiKeys are connected, but we are only able to
/// open one of them (e.g. because the other one is in use, and the
/// connection doesn't allow sharing), the YubiKey that we were able to
/// open is returned.
///
/// If you need to operate in environments with more than one YubiKey
/// attached to the same system, use [`YubiKey::open_by_serial`] or
/// [`yubikey::reader::Context`][`Context`] to select from the available
/// PC/SC readers.
pub fn open() -> Result<Self> {
let mut readers = Context::open()?;
let mut reader_iter = readers.iter()?;
let mut yubikey: Option<Self> = None;
let mut readers = Context::open()?;
for reader in readers.iter()? {
if let Ok(yk_found) = reader.open() {
if let Some(yk_stored) = yubikey {
// We found two YubiKeys, so we won't use either.
// Don't reset them.
let _ = yk_stored.disconnect(pcsc::Disposition::LeaveCard);
let _ = yk_found.disconnect(pcsc::Disposition::LeaveCard);
if let Some(reader) = reader_iter.next() {
if reader_iter.next().is_some() {
error!("multiple YubiKeys detected!");
return Err(Error::PcscError { inner: None });
} else {
yubikey = Some(yk_found);
}
}
}
return reader.open();
}
if let Some(yubikey) = yubikey {
// We found exactly one YubiKey that we could open, so we return it.
Ok(yubikey)
} else {
error!("no YubiKey detected!");
Err(Error::NotFound)
}
}
/// Open a YubiKey with a specific serial number.
pub fn open_by_serial(serial: Serial) -> Result<Self> {