yubikey 0.7.0

Pure Rust cross-platform host-side driver for YubiKey devices from Yubico with support for hardware-backed public-key decryption and digital signatures using the Personal Identity Verification (PIV) application. Supports RSA (1024/2048) or ECC (NIST P-256/P-384) algorithms e.g, PKCS#1v1.5, ECDSA
Documentation
//! Support for enumerating available PC/SC card readers.

use crate::{Result, YubiKey};
use std::{
    borrow::Cow,
    ffi::CStr,
    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 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<_> = {
            let c = ctx.lock().unwrap();

            // 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> {
        let ctx = self.ctx.lock().unwrap();
        Ok(ctx.connect(self.name, pcsc::ShareMode::Shared, pcsc::Protocols::T1)?)
    }
}