#![deny(bad_style,
const_err,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements ,
patterns_in_fns_without_body,
private_in_public,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true,
missing_debug_implementations,
missing_copy_implementations,
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_results)]
pub mod functions;
pub mod objects;
pub mod types;
use crate::types::function::{Rv, RvError};
use crate::types::session::{Session, UserType};
use crate::types::slot_token::Slot;
use derivative::Derivative;
use log::error;
use secrecy::{ExposeSecret, Secret, SecretVec};
use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
use std::ffi::CString;
use std::fmt;
use std::mem;
use std::path::Path;
use std::sync::{Mutex, RwLock};
#[macro_export]
macro_rules! get_pkcs11 {
($pkcs11:expr, $func_name:ident) => {
($pkcs11
.function_list
.$func_name
.ok_or(crate::Error::NullFunctionPointer)?)
};
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Pkcs11 {
#[derivative(Debug = "ignore")]
_pkcs11_lib: cryptoki_sys::Pkcs11,
function_list: cryptoki_sys::_CK_FUNCTION_LIST,
logged_sessions: Mutex<HashMap<Slot, HashSet<cryptoki_sys::CK_SESSION_HANDLE>>>,
#[derivative(Debug = "ignore")]
pins: RwLock<HashMap<Slot, SecretVec<u8>>>,
}
impl Pkcs11 {
pub fn new<P>(filename: P) -> Result<Self>
where
P: AsRef<Path>,
{
unsafe {
let pkcs11_lib =
cryptoki_sys::Pkcs11::new(filename.as_ref()).map_err(Error::LibraryLoading)?;
let mut list = mem::MaybeUninit::uninit();
Rv::from(pkcs11_lib.C_GetFunctionList(list.as_mut_ptr())).into_result()?;
let list_ptr = *list.as_ptr();
Ok(Pkcs11 {
_pkcs11_lib: pkcs11_lib,
function_list: *list_ptr,
logged_sessions: Mutex::new(HashMap::new()),
pins: RwLock::new(HashMap::new()),
})
}
}
pub fn set_pin(&self, slot: Slot, pin: &str) -> Result<()> {
let _ = self
.pins
.write()
.expect("Pins lock poisoned")
.insert(slot, Secret::new(CString::new(pin)?.into_bytes()));
Ok(())
}
pub fn clear_pin(&self, slot: Slot) {
let _ = self.pins.write().expect("Pins lock poisoned").remove(&slot);
}
fn login(&self, session: &Session, user_type: UserType) -> Result<()> {
let pins = self.pins.read().expect("Pins lock poisoned");
let pin = pins
.get(&session.slot())
.ok_or(Error::PinNotSet)?
.expose_secret();
let mut logged_sessions = self
.logged_sessions
.lock()
.expect("Logged sessions mutex poisoned!");
match unsafe {
Rv::from(get_pkcs11!(self, C_Login)(
session.handle(),
user_type.into(),
pin.as_ptr() as *mut u8,
pin.len().try_into()?,
))
} {
Rv::Ok | Rv::Error(RvError::UserAlreadyLoggedIn) => {
if let Some(session_handles) = logged_sessions.get_mut(&session.slot()) {
let _ = session_handles.insert(session.handle());
} else {
let mut new_set = HashSet::new();
let _ = new_set.insert(session.handle());
let _ = logged_sessions.insert(session.slot(), new_set);
}
Ok(())
}
Rv::Error(err) => Err(err.into()),
}
}
fn logout(&self, session: &Session) -> Result<()> {
let mut logged_sessions = self
.logged_sessions
.lock()
.expect("Logged sessions mutex poisoned!");
if let Some(session_handles) = logged_sessions.get_mut(&session.slot()) {
if session_handles.contains(&session.handle()) {
if session_handles.len() == 1 {
unsafe {
Rv::from(get_pkcs11!(self, C_Logout)(session.handle())).into_result()?;
}
}
let _ = session_handles.remove(&session.handle());
}
}
Ok(())
}
}
#[derive(Debug)]
pub enum Error {
LibraryLoading(libloading::Error),
Pkcs11(RvError),
NotSupported,
TryFromInt(std::num::TryFromIntError),
TryFromSlice(std::array::TryFromSliceError),
NulError(std::ffi::NulError),
NullFunctionPointer,
InvalidValue,
PinNotSet,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::LibraryLoading(e) => write!(f, "libloading error ({})", e),
Error::Pkcs11(e) => write!(f, "PKCS11 error: {}", e),
Error::NotSupported => write!(f, "Feature not supported"),
Error::TryFromInt(e) => write!(f, "Conversion between integers failed ({})", e),
Error::TryFromSlice(e) => write!(f, "Error converting slice to array ({})", e),
Error::NulError(e) => write!(f, "An interior nul byte was found ({})", e),
Error::NullFunctionPointer => write!(f, "Calling a NULL function pointer"),
Error::InvalidValue => write!(f, "The value is not one of the expected options"),
Error::PinNotSet => write!(f, "Pin has not been set before trying to log in"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::LibraryLoading(e) => Some(e),
Error::TryFromInt(e) => Some(e),
Error::TryFromSlice(e) => Some(e),
Error::NulError(e) => Some(e),
Error::Pkcs11(_)
| Error::NotSupported
| Error::NullFunctionPointer
| Error::PinNotSet
| Error::InvalidValue => None,
}
}
}
impl From<libloading::Error> for Error {
fn from(err: libloading::Error) -> Error {
Error::LibraryLoading(err)
}
}
impl From<std::num::TryFromIntError> for Error {
fn from(err: std::num::TryFromIntError) -> Error {
Error::TryFromInt(err)
}
}
impl From<std::array::TryFromSliceError> for Error {
fn from(err: std::array::TryFromSliceError) -> Error {
Error::TryFromSlice(err)
}
}
impl From<std::ffi::NulError> for Error {
fn from(err: std::ffi::NulError) -> Error {
Error::NulError(err)
}
}
impl From<std::convert::Infallible> for Error {
fn from(_err: std::convert::Infallible) -> Error {
unreachable!()
}
}
impl Drop for Pkcs11 {
fn drop(&mut self) {
if let Err(e) = self.finalize_private() {
error!("Failed to finalize: {}", e);
}
}
}
pub type Result<T> = core::result::Result<T, Error>;