aboutsummaryrefslogtreecommitdiff
path: root/src/util.rs
blob: 2a756349bece1ef9e90ca37ef2babd0752dd2092 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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<String, CommandError> {
    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<Vec<u8>> {
    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<T: Into<Vec<u8>>>(s: T) -> Result<CString, CommandError> {
    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<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,
            201 => CommandError::InvalidSlot,
            _ => CommandError::Unknown,
        }
    }
}

impl Into<i32> 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,
        }
    }
}