summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 9d15d0379c1ec6be5d18fdf61b56c816c401b77f (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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT

//! Provides access to a Nitrokey device using the native libnitrokey API.
//!
//! # Usage
//!
//! Operations on the Nitrokey require different authentication levels.  Some operations can be
//! performed without authentication, some require user access, and some require admin access.
//! This is modelled using the types [`User`][] and [`Admin`][].
//!
//! Use [`connect`][] to connect to any Nitrokey device.  The method will return a
//! [`DeviceWrapper`][] that abstracts over the supported Nitrokey devices.  You can also use
//! [`Pro::connect`][] or [`Storage::connect`][] to connect to a specific device.
//!
//! You can then use [`authenticate_user`][] or [`authenticate_admin`][] to get an authenticated
//! device that can perform operations that require authentication.  You can use [`device`][] to go
//! back to the unauthenticated device.
//!
//! This makes sure that you can only execute a command if you have the required access rights.
//! Otherwise, your code will not compile.  The only exception are the methods to generate one-time
//! passwords – [`get_hotp_code`][] and [`get_totp_code`][].  Depending on the stick configuration,
//! these operations are available without authentication or with user authentication.
//!
//! # Examples
//!
//! Connect to any Nitrokey and print its serial number:
//!
//! ```no_run
//! use nitrokey::Device;
//! # use nitrokey::Error;
//!
//! # fn try_main() -> Result<(), Error> {
//! let device = nitrokey::connect()?;
//! println!("{}", device.get_serial_number()?);
//! #     Ok(())
//! # }
//! ```
//!
//! Configure an HOTP slot:
//!
//! ```no_run
//! use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData};
//! # use nitrokey::Error;
//!
//! # fn try_main() -> Result<(), Error> {
//! let device = nitrokey::connect()?;
//! let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits);
//! match device.authenticate_admin("12345678") {
//!     Ok(admin) => {
//!         match admin.write_hotp_slot(slot_data, 0) {
//!             Ok(()) => println!("Successfully wrote slot."),
//!             Err(err) => println!("Could not write slot: {}", err),
//!         }
//!     },
//!     Err((_, err)) => println!("Could not authenticate as admin: {}", err),
//! }
//! #     Ok(())
//! # }
//! ```
//!
//! Generate an HOTP one-time password:
//!
//! ```no_run
//! use nitrokey::{Device, GenerateOtp};
//! # use nitrokey::Error;
//!
//! # fn try_main() -> Result<(), Error> {
//! let device = nitrokey::connect()?;
//! match device.get_hotp_code(1) {
//!     Ok(code) => println!("Generated HOTP code: {}", code),
//!     Err(err) => println!("Could not generate HOTP code: {}", err),
//! }
//! #     Ok(())
//! # }
//! ```
//!
//! [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin
//! [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user
//! [`connect`]: fn.connect.html
//! [`Pro::connect`]: struct.Pro.html#fn.connect.html
//! [`Storage::connect`]: struct.Storage.html#fn.connect.html
//! [`device`]: struct.User.html#method.device
//! [`get_hotp_code`]: trait.GenerateOtp.html#method.get_hotp_code
//! [`get_totp_code`]: trait.GenerateOtp.html#method.get_totp_code
//! [`Admin`]: struct.Admin.html
//! [`DeviceWrapper`]: enum.DeviceWrapper.html
//! [`User`]: struct.User.html

#![warn(missing_docs, rust_2018_compatibility, rust_2018_idioms, unused)]

mod auth;
mod config;
mod device;
mod error;
mod otp;
mod pws;
mod util;

use std::fmt;

use nitrokey_sys;

pub use crate::auth::{Admin, Authenticate, User};
pub use crate::config::Config;
pub use crate::device::{
    connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage,
    StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus,
};
pub use crate::error::{CommandError, CommunicationError, Error, LibraryError, Result};
pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData};
pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT};
pub use crate::util::LogLevel;

/// The default admin PIN for all Nitrokey devices.
pub const DEFAULT_ADMIN_PIN: &'static str = "12345678";
/// The default user PIN for all Nitrokey devices.
pub const DEFAULT_USER_PIN: &'static str = "123456";

/// A version of the libnitrokey library.
///
/// Use the [`get_library_version`](fn.get_library_version.html) function to query the library
/// version.
#[derive(Clone, Debug, PartialEq)]
pub struct Version {
    /// The Git library version as a string.
    ///
    /// The library version is the output of `git describe --always` at compile time, for example
    /// `v3.3` or `v3.4.1`.  If the library has not been built from a release, the version string
    /// contains the number of commits since the last release and the hash of the current commit, for
    /// example `v3.3-19-gaee920b`.  If the library has not been built from a Git checkout, this
    /// string may be empty.
    pub git: String,
    /// The major library version.
    pub major: u32,
    /// The minor library version.
    pub minor: u32,
}

impl fmt::Display for Version {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.git.is_empty() {
            write!(f, "v{}.{}", self.major, self.minor)
        } else {
            f.write_str(&self.git)
        }
    }
}

/// Enables or disables debug output.  Calling this method with `true` is equivalent to setting the
/// log level to `Debug`; calling it with `false` is equivalent to the log level `Error` (see
/// [`set_log_level`][]).
///
/// If debug output is enabled, detailed information about the communication with the Nitrokey
/// device is printed to the standard output.
///
/// [`set_log_level`]: fn.set_log_level.html
pub fn set_debug(state: bool) {
    unsafe {
        nitrokey_sys::NK_set_debug(state);
    }
}

/// Sets the log level for libnitrokey.  All log messages are written to the standard error stream.
/// Setting the log level enables all log messages on the same or on a higher log level.
pub fn set_log_level(level: LogLevel) {
    unsafe {
        nitrokey_sys::NK_set_debug_level(level.into());
    }
}

/// Returns the libnitrokey library version.
///
/// # Errors
///
/// - [`Utf8Error`][] if libnitrokey returned an invalid UTF-8 string
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), nitrokey::Error> {
/// let version = nitrokey::get_library_version()?;
/// println!("Using libnitrokey {}", version.git);
/// #    Ok(())
/// # }
/// ```
///
/// [`Utf8Error`]: enum.Error.html#variant.Utf8Error
pub fn get_library_version() -> Result<Version> {
    // NK_get_library_version returns a static string, so we don’t have to free the pointer.
    let git = unsafe { nitrokey_sys::NK_get_library_version() };
    let git = if git.is_null() {
        String::new()
    } else {
        util::owned_str_from_ptr(git)?
    };
    let major = unsafe { nitrokey_sys::NK_get_major_library_version() };
    let minor = unsafe { nitrokey_sys::NK_get_minor_library_version() };
    Ok(Version { git, major, minor })
}