From 986ad2f782cf944990e4eda8bf88ea1821233302 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 11 Dec 2018 23:50:45 +0100 Subject: Add nitrokey as a dependency to nitrocli The nitrokey crate provides a simple interface to the Nitrokey Storage and the Nitrokey Pro based on the libnitrokey library developed by Nitrokey UG. The low-level bindings to this library are available in the nitrokey-sys crate. This patch adds version v0.2.1 of the nitrokey crate as a dependency for nitrocli. It includes the indirect dependencies nitrokey-sys (version 3.4.1) and rand (version 0.4.3). Import subrepo nitrokey/:nitrokey at 2eccc96ceec2282b868891befe9cda7f941fbe7b Import subrepo nitrokey-sys/:nitrokey-sys at f1a11ebf72610fb9cf80ac7f9f147b4ba1a5336f Import subrepo rand/:rand at d7d5da49daf7ceb3e5940072940d495cced3a1b3 --- nitrokey/src/util.rs | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 nitrokey/src/util.rs (limited to 'nitrokey/src/util.rs') diff --git a/nitrokey/src/util.rs b/nitrokey/src/util.rs new file mode 100644 index 0000000..6f4fbb0 --- /dev/null +++ b/nitrokey/src/util.rs @@ -0,0 +1,171 @@ +use libc::{c_void, free}; +use nitrokey_sys; +use rand::{OsRng, Rng}; +use std; +use std::ffi::{CStr, CString}; +use std::fmt; +use std::os::raw::{c_char, c_int}; + +/// Error types returned by Nitrokey device or by the library. +#[derive(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 occured 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 occured. + Unknown, + /// You passed a string containing a null byte. + InvalidString, + /// You passed an invalid slot. + InvalidSlot, + /// An error occured during random number generation. + RngError, +} + +/// 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(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 owned_str_from_ptr(ptr: *const c_char) -> String { + unsafe { + return CStr::from_ptr(ptr).to_string_lossy().into_owned(); + } +} + +pub fn result_from_string(ptr: *const c_char) -> Result { + if ptr.is_null() { + return Err(CommandError::Unknown); + } + unsafe { + let s = owned_str_from_ptr(ptr); + free(ptr as *mut c_void); + if s.is_empty() { + return Err(get_last_error()); + } + return Ok(s); + } +} + +pub fn get_command_result(value: c_int) -> Result<(), CommandError> { + match value { + 0 => Ok(()), + other => Err(CommandError::from(other)), + } +} + +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::Unknown, + Err(err) => err, + }; +} + +pub fn generate_password(length: usize) -> std::io::Result> { + let mut rng = match OsRng::new() { + Ok(rng) => rng, + Err(err) => return Err(err), + }; + let mut data = vec![0u8; length]; + rng.fill_bytes(&mut data[..]); + return Ok(data); +} + +pub fn get_cstring>>(s: T) -> Result { + CString::new(s).or(Err(CommandError::InvalidString)) +} + +impl fmt::Display for CommandError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match *self { + CommandError::WrongCrc => "A packet with a wrong checksum has been sent or received", + CommandError::WrongSlot => "The given OTP slot does not exist", + CommandError::SlotNotProgrammed => "The given OTP slot is not programmed", + CommandError::WrongPassword => "The given password is wrong", + CommandError::NotAuthorized => { + "You are not authorized for this command or provided a wrong temporary password" + } + CommandError::Timestamp => "An error occured when getting or setting the time", + CommandError::NoName => "You did not provide a name for the OTP slot", + CommandError::NotSupported => "This command is not supported by this device", + CommandError::UnknownCommand => "This command is unknown", + CommandError::AesDecryptionFailed => "AES decryption failed", + CommandError::Unknown => "An unknown error occured", + CommandError::InvalidString => "You passed a string containing a null byte", + CommandError::InvalidSlot => "The given slot is invalid", + CommandError::RngError => "An error occured during random number generation", + }; + write!(f, "{}", msg) + } +} + +impl From 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, + 201 => CommandError::InvalidSlot, + _ => CommandError::Unknown, + } + } +} + +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, + } + } +} -- cgit v1.2.1