aboutsummaryrefslogtreecommitdiff
path: root/src/util.rs
blob: 22cccb36ee7884e5087b34246217803f396ce0c8 (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
use libc::{c_void, free};
use nitrokey_sys;
use rand::{OsRng, Rng};
use std;
use std::ffi::CStr;
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 decryptionfailed.
    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,
}

/// Command execution status.
#[derive(Debug, PartialEq)]
pub enum CommandStatus {
    /// The command was successful.
    Success,
    /// An error occured during command execution.
    Error(CommandError),
}

/// 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);
        if s.is_empty() {
            return Err(get_last_error());
        }
        // TODO: move up for newer libnitrokey versions
        free(ptr as *mut c_void);
        return Ok(s);
    }
}

pub fn get_last_status() -> CommandStatus {
    unsafe {
        let status = nitrokey_sys::NK_get_last_command_status();
        return CommandStatus::from(status as c_int);
    }
}

pub fn get_last_error() -> CommandError {
    return match get_last_status() {
        CommandStatus::Success => CommandError::Unknown,
        CommandStatus::Error(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);
}

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 From<c_int> for CommandStatus {
    fn from(value: c_int) -> Self {
        match value {
            0 => CommandStatus::Success,
            other => CommandStatus::Error(CommandError::from(other)),
        }
    }
}

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,
        }
    }
}