~ireas/nitrokey-rs

ref: 4c3b7f9513f136b058eda5ff927599907ddd68ed nitrokey-rs/src/util.rs -rw-r--r-- 3.7 KiB
4c3b7f95Robin Krahl Update deprecated functions in readme 1 year, 1 month ago
                                                                                
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
// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
// 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<String, Error> {
    str_from_ptr(ptr).map(ToOwned::to_owned)
}

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(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<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.to_owned())
        } else {
            Ok(s.to_owned())
        }
    })
}

pub fn result_or_error<T>(value: T) -> Result<T, Error> {
    get_last_result().and(Ok(value))
}

pub fn get_struct<R, T, F>(f: F) -> Result<R, Error>
where
    R: From<T>,
    T: Default,
    F: Fn(&mut T) -> c_int,
{
    let mut out = T::default();
    get_command_result(f(&mut out))?;
    Ok(out.into())
}

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<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);
        }
    }
}

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 {
    fn into(self) -> i32 {
        match self {
            LogLevel::Error => 0,
            LogLevel::Warning => 1,
            LogLevel::Info => 2,
            LogLevel::DebugL1 => 3,
            LogLevel::Debug => 4,
            LogLevel::DebugL2 => 5,
        }
    }
}