// Copyright (C) 2018-2019 Robin Krahl // SPDX-License-Identifier: MIT use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int}; use libc::{c_void, free}; use rand_core::{OsRng, RngCore}; use crate::error::{Error, LibraryError}; /// Log level for libnitrokey. /// /// Setting the log level to a lower level enables all output from higher levels too. Currently, /// only the log levels `Warning`, `DebugL1`, `Debug` and `DebugL2` are actually used. #[derive(Clone, Copy, Debug, PartialEq)] pub enum LogLevel { /// Error messages. Currently not used. Error, /// Warning messages. Warning, /// Informational messages. Currently not used. Info, /// Basic debug messages, especially basic information on the sent and received packets. DebugL1, /// Detailed debug messages, especially detailed information on the sent and received packets. Debug, /// Very detailed debug messages, especially detailed information about the control flow for /// device communication (for example function entries and exits). DebugL2, } pub fn str_from_ptr<'a>(ptr: *const c_char) -> Result<&'a str, Error> { unsafe { CStr::from_ptr(ptr) }.to_str().map_err(Error::from) } pub fn owned_str_from_ptr(ptr: *const c_char) -> Result { str_from_ptr(ptr).map(ToOwned::to_owned) } pub fn run_with_string(ptr: *const c_char, op: F) -> Result where F: FnOnce(&str) -> Result, { if ptr.is_null() { return Err(Error::UnexpectedError( "libnitrokey returned a null pointer".to_owned(), )); } let result = str_from_ptr(ptr).and_then(op); unsafe { free(ptr as *mut c_void) }; result } pub fn result_from_string(ptr: *const c_char) -> Result { run_with_string(ptr, |s| { // An empty string can both indicate an error or be a valid return value. In this case, we // have to check the last command status to decide what to return. if s.is_empty() { get_last_result().map(|_| s.to_owned()) } else { Ok(s.to_owned()) } }) } pub fn result_or_error(value: T) -> Result { get_last_result().and(Ok(value)) } pub fn get_command_result(value: c_int) -> Result<(), Error> { if value == 0 { Ok(()) } else { Err(Error::from(value)) } } pub fn get_last_result() -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_get_last_command_status() }.into()) } pub fn get_last_error() -> Error { get_last_result().err().unwrap_or_else(|| { Error::UnexpectedError("Expected an error, but command status is zero".to_owned()) }) } pub fn generate_password(length: usize) -> Result { loop { // Randomly generate a password until we get a string *without* null bytes. Otherwise // the string would be cut off prematurely due to null-termination in C. let mut data = vec![0u8; length]; OsRng.fill_bytes(&mut data[..]); if let Ok(s) = CString::new(data) { return Ok(s); } } } pub fn get_cstring>>(s: T) -> Result { CString::new(s).or_else(|_| Err(LibraryError::InvalidString.into())) } impl Into for LogLevel { fn into(self) -> i32 { match self { LogLevel::Error => 0, LogLevel::Warning => 1, LogLevel::Info => 2, LogLevel::DebugL1 => 3, LogLevel::Debug => 4, LogLevel::DebugL2 => 5, } } }