diff options
Diffstat (limited to 'src/util.rs')
-rw-r--r-- | src/util.rs | 194 |
1 files changed, 52 insertions, 142 deletions
diff --git a/src/util.rs b/src/util.rs index 567c478..26942cf 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,53 +1,13 @@ -use std::borrow; +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + use std::ffi::{CStr, CString}; -use std::fmt; use std::os::raw::{c_char, c_int}; use libc::{c_void, free}; -use rand_core::RngCore; -use rand_os::OsRng; +use rand_core::{OsRng, RngCore}; -/// Error types returned by Nitrokey device or by the library. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CommandError { - /// A packet with a wrong checksum has been sent or received. - WrongCrc, - /// A command tried to access an OTP slot that does not exist. - WrongSlot, - /// A command tried to generate an OTP on a slot that is not configured. - SlotNotProgrammed, - /// The provided password is wrong. - WrongPassword, - /// You are not authorized for this command or provided a wrong temporary - /// password. - NotAuthorized, - /// An error occurred when getting or setting the time. - Timestamp, - /// You did not provide a name for the OTP slot. - NoName, - /// This command is not supported by this device. - NotSupported, - /// This command is unknown. - UnknownCommand, - /// AES decryption failed. - AesDecryptionFailed, - /// An unknown error occurred. - Unknown(i64), - /// An unspecified error occurred. - Undefined, - /// You passed a string containing a null byte. - InvalidString, - /// A supplied string exceeded a length limit. - StringTooLong, - /// You passed an invalid slot. - InvalidSlot, - /// The supplied string was not in hexadecimal format. - InvalidHexString, - /// The target buffer was smaller than the source. - TargetBufferTooSmall, - /// An error occurred during random number generation. - RngError, -} +use crate::error::{Error, LibraryError}; /// Log level for libnitrokey. /// @@ -70,126 +30,76 @@ pub enum LogLevel { DebugL2, } -pub fn owned_str_from_ptr(ptr: *const c_char) -> String { - unsafe { - return CStr::from_ptr(ptr).to_string_lossy().into_owned(); - } +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<String, Error> { + str_from_ptr(ptr).map(ToOwned::to_owned) } -pub fn result_from_string(ptr: *const c_char) -> Result<String, CommandError> { +pub fn run_with_string<R, F>(ptr: *const c_char, op: F) -> Result<R, Error> +where + F: FnOnce(&str) -> Result<R, Error>, +{ if ptr.is_null() { - return Err(CommandError::Undefined); + return Err(Error::UnexpectedError( + "libnitrokey returned a null pointer".to_owned(), + )); } - unsafe { - let s = owned_str_from_ptr(ptr); - free(ptr as *mut c_void); + 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<String, Error> { + 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) + get_last_result().map(|_| s.to_owned()) } else { - Ok(s) + Ok(s.to_owned()) } - } + }) } -pub fn get_command_result(value: c_int) -> Result<(), CommandError> { - match value { - 0 => Ok(()), - other => Err(CommandError::from(other)), - } +pub fn result_or_error<T>(value: T) -> Result<T, Error> { + get_last_result().and(Ok(value)) } -pub fn get_last_result() -> Result<(), CommandError> { - let value = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int; - get_command_result(value) -} - -pub fn get_last_error() -> CommandError { - return match get_last_result() { - Ok(()) => CommandError::Undefined, - Err(err) => err, - }; -} - -pub fn generate_password(length: usize) -> Result<Vec<u8>, CommandError> { - let mut rng = OsRng::new()?; - let mut data = vec![0u8; length]; - rng.fill_bytes(&mut data[..]); - Ok(data) -} - -pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, CommandError> { - CString::new(s).or(Err(CommandError::InvalidString)) +pub fn get_command_result(value: c_int) -> Result<(), Error> { + if value == 0 { + Ok(()) + } else { + Err(Error::from(value)) + } } -impl CommandError { - fn as_str(&self) -> borrow::Cow<'static, str> { - match *self { - CommandError::WrongCrc => { - "A packet with a wrong checksum has been sent or received".into() - } - CommandError::WrongSlot => "The given OTP slot does not exist".into(), - CommandError::SlotNotProgrammed => "The given OTP slot is not programmed".into(), - CommandError::WrongPassword => "The given password is wrong".into(), - CommandError::NotAuthorized => { - "You are not authorized for this command or provided a wrong temporary \ - password" - .into() - } - CommandError::Timestamp => "An error occurred when getting or setting the time".into(), - CommandError::NoName => "You did not provide a name for the OTP slot".into(), - CommandError::NotSupported => "This command is not supported by this device".into(), - CommandError::UnknownCommand => "This command is unknown".into(), - CommandError::AesDecryptionFailed => "AES decryption failed".into(), - CommandError::Unknown(x) => { - borrow::Cow::from(format!("An unknown error occurred ({})", x)) - } - CommandError::Undefined => "An unspecified error occurred".into(), - CommandError::InvalidString => "You passed a string containing a null byte".into(), - CommandError::StringTooLong => "The supplied string is too long".into(), - CommandError::InvalidSlot => "The given slot is invalid".into(), - CommandError::InvalidHexString => { - "The supplied string is not in hexadecimal format".into() - } - CommandError::TargetBufferTooSmall => "The target buffer is too small".into(), - CommandError::RngError => "An error occurred during random number generation".into(), - } - } +pub fn get_last_result() -> Result<(), Error> { + get_command_result(unsafe { nitrokey_sys::NK_get_last_command_status() }.into()) } -impl fmt::Display for CommandError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) - } +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()) + }) } -impl From<c_int> for CommandError { - fn from(value: c_int) -> Self { - match value { - 1 => CommandError::WrongCrc, - 2 => CommandError::WrongSlot, - 3 => CommandError::SlotNotProgrammed, - 4 => CommandError::WrongPassword, - 5 => CommandError::NotAuthorized, - 6 => CommandError::Timestamp, - 7 => CommandError::NoName, - 8 => CommandError::NotSupported, - 9 => CommandError::UnknownCommand, - 10 => CommandError::AesDecryptionFailed, - 200 => CommandError::StringTooLong, - 201 => CommandError::InvalidSlot, - 202 => CommandError::InvalidHexString, - 203 => CommandError::TargetBufferTooSmall, - x => CommandError::Unknown(x.into()), +pub fn generate_password(length: usize) -> Result<CString, Error> { + 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); } } } -impl From<rand_core::Error> for CommandError { - fn from(_error: rand_core::Error) -> Self { - CommandError::RngError - } +pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, Error> { + CString::new(s).map_err(|_| LibraryError::InvalidString.into()) } impl Into<i32> for LogLevel { |