readers: Initial Readers enumerator for detecting YubiKeys
Adds a `yubikey_piv::Readers` type which opens a PC/SC context and can enumerate detected PC/SC readers with a slightly more ergonomic API than what's provided in the upstream crate. Does not support actually instantiating a `YubiKey` from a `Reader<'_>` yet, but ideally all connections to YubiKeys should go through this API.
This commit is contained in:
+2
-1
@@ -156,6 +156,7 @@ mod metadata;
|
|||||||
pub mod mgm;
|
pub mod mgm;
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
pub mod msroots;
|
pub mod msroots;
|
||||||
|
pub mod readers;
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
mod serialization;
|
mod serialization;
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
@@ -164,7 +165,7 @@ mod transaction;
|
|||||||
pub mod yubikey;
|
pub mod yubikey;
|
||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
pub use self::{key::Key, mgm::MgmKey};
|
pub use self::{key::Key, mgm::MgmKey, readers::Readers};
|
||||||
pub use yubikey::YubiKey;
|
pub use yubikey::YubiKey;
|
||||||
|
|
||||||
/// Object identifiers
|
/// Object identifiers
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
//! Support for enumerating available readers
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
ffi::CStr,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Iterator over connected readers
|
||||||
|
pub type Iter<'ctx> = std::vec::IntoIter<Reader<'ctx>>;
|
||||||
|
|
||||||
|
/// Enumeration support for available readers
|
||||||
|
pub struct Readers {
|
||||||
|
/// PC/SC context
|
||||||
|
ctx: Arc<Mutex<pcsc::Context>>,
|
||||||
|
|
||||||
|
/// Buffer for storing reader names
|
||||||
|
reader_names: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Readers {
|
||||||
|
/// Open a PC/SC context
|
||||||
|
pub fn open() -> Result<Self, Error> {
|
||||||
|
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<'_>, Error> {
|
||||||
|
let Self { ctx, reader_names } = self;
|
||||||
|
|
||||||
|
let reader_cstrs: Vec<_> = {
|
||||||
|
let c = ctx.lock().unwrap();
|
||||||
|
|
||||||
|
// ensure PC/SC context is valid
|
||||||
|
c.is_valid()?;
|
||||||
|
|
||||||
|
c.list_readers(reader_names)?.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
let readers: Vec<_> = reader_cstrs
|
||||||
|
.iter()
|
||||||
|
.map(|name| Reader::new(name, Arc::clone(ctx)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(readers.into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An individual connected 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 the given reader
|
||||||
|
// TODO(tarcieri): return a `YubiKey` here rather than a `pcsc::Card`
|
||||||
|
pub fn open(&self) -> Result<pcsc::Card, Error> {
|
||||||
|
let ctx = self.ctx.lock().unwrap();
|
||||||
|
Ok(ctx.connect(self.name, pcsc::ShareMode::Shared, pcsc::Protocols::T1)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user