From 485d49a6c8d64d4ebbeecb275e928269a32c672d Mon Sep 17 00:00:00 2001 From: hko-s <59601023+hko-s@users.noreply.github.com> Date: Wed, 16 Aug 2023 00:20:04 +0200 Subject: [PATCH] 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. --- src/yubikey.rs | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/yubikey.rs b/src/yubikey.rs index 6a699e3..2c36800 100644 --- a/src/yubikey.rs +++ b/src/yubikey.rs @@ -178,27 +178,44 @@ 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 { + let mut yubikey: Option = None; + let mut readers = Context::open()?; - let mut reader_iter = readers.iter()?; + 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 }); + error!("multiple YubiKeys detected!"); + return Err(Error::PcscError { inner: None }); + } else { + yubikey = Some(yk_found); + } } - - return reader.open(); } - error!("no YubiKey detected!"); - Err(Error::NotFound) + 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.