78313360a1
Lints for usages of `unwrap()` in the `yubikey` crate (not CLI yet). Replaces them with `?` or `expect()` as the situation warrants.
99 lines
2.8 KiB
Rust
99 lines
2.8 KiB
Rust
//! Support for enumerating available PC/SC card readers.
|
|
|
|
use crate::{Error, Result, YubiKey};
|
|
use std::{
|
|
borrow::Cow,
|
|
ffi::CStr,
|
|
fmt,
|
|
sync::{Arc, Mutex},
|
|
};
|
|
|
|
/// Iterator over connected readers
|
|
pub type Iter<'ctx> = std::vec::IntoIter<Reader<'ctx>>;
|
|
|
|
/// PC/SC reader context: used to enumerate available PC/SC [`Reader`]s.
|
|
pub struct Context {
|
|
/// PC/SC context
|
|
ctx: Arc<Mutex<pcsc::Context>>,
|
|
|
|
/// Buffer for storing reader names
|
|
reader_names: Vec<u8>,
|
|
}
|
|
|
|
impl fmt::Debug for Context {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("Context").finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
impl Context {
|
|
/// Open a PC/SC context, which can be used to enumerate available PC/SC
|
|
/// readers (which can be used to connect to YubiKeys).
|
|
pub fn open() -> Result<Self> {
|
|
let ctx = pcsc::Context::establish(pcsc::Scope::System)?;
|
|
let reader_names = vec![0u8; ctx.list_readers_len()?];
|
|
Ok(Self {
|
|
ctx: Arc::new(Mutex::new(ctx)),
|
|
reader_names,
|
|
})
|
|
}
|
|
|
|
/// Iterate over the available readers.
|
|
pub fn iter(&mut self) -> Result<Iter<'_>> {
|
|
let Self { ctx, reader_names } = self;
|
|
|
|
let reader_cstrs: Vec<_> = {
|
|
// TODO(tarcieri): better error?
|
|
let c = ctx.lock().map_err(|_| Error::GenericError)?;
|
|
|
|
// ensure PC/SC context is valid
|
|
c.is_valid()?;
|
|
|
|
c.list_readers(reader_names)?.collect()
|
|
};
|
|
|
|
#[allow(clippy::needless_collect)]
|
|
let readers: Vec<_> = reader_cstrs
|
|
.iter()
|
|
.map(|name| Reader::new(name, Arc::clone(ctx)))
|
|
.collect();
|
|
|
|
Ok(readers.into_iter())
|
|
}
|
|
}
|
|
|
|
/// An individual connected PC/SC card reader.
|
|
pub struct Reader<'ctx> {
|
|
/// Name of this reader
|
|
name: &'ctx CStr,
|
|
|
|
/// PC/SC context
|
|
ctx: Arc<Mutex<pcsc::Context>>,
|
|
}
|
|
|
|
impl<'ctx> Reader<'ctx> {
|
|
/// Create a new reader from its name and context.
|
|
fn new(name: &'ctx CStr, ctx: Arc<Mutex<pcsc::Context>>) -> Self {
|
|
// TODO(tarcieri): open devices, determine they're YubiKeys, get serial?
|
|
Self { name, ctx }
|
|
}
|
|
|
|
/// Get this reader's name.
|
|
pub fn name(&self) -> Cow<'_, str> {
|
|
// TODO(tarcieri): is lossy ok here? try to avoid lossiness?
|
|
self.name.to_string_lossy()
|
|
}
|
|
|
|
/// Open a connection to this reader, returning a `YubiKey` if successful.
|
|
pub fn open(&self) -> Result<YubiKey> {
|
|
self.try_into()
|
|
}
|
|
|
|
/// Connect to this reader, returning its `pcsc::Card`.
|
|
pub(crate) fn connect(&self) -> Result<pcsc::Card> {
|
|
// TODO(tarcieri): better error?
|
|
let ctx = self.ctx.lock().map_err(|_| Error::GenericError)?;
|
|
Ok(ctx.connect(self.name, pcsc::ShareMode::Shared, pcsc::Protocols::T1)?)
|
|
}
|
|
}
|