summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 1cfb7fd9c949fa4df27667cd476f4a1f9d610590 (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
//! 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
//! [`UnauthenticatedDevice`][], [`UserAuthenticatedDevice`][] and
//! [`AdminAuthenticatedDevice`][].
//!
//! Use [`connect`][] or [`connect_model`][] to obtain an
//! [`UnauthenticatedDevice`][].  You can then use [`authenticate_user`][] or
//! [`authenticate_admin`][] to get an authenticated device.  You can then 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::CommandError;
//!
//! # fn try_main() -> Result<(), CommandError> {
//! let device = nitrokey::connect()?;
//! println!("{}", device.get_serial_number()?);
//! #     Ok(())
//! # }
//! ```
//!
//! Configure an HOTP slot:
//!
//! ```no_run
//! use nitrokey::{CommandStatus, ConfigureOtp, Device, OtpMode, OtpSlotData};
//! # use nitrokey::CommandError;
//!
//! # fn try_main() -> Result<(), (CommandError)> {
//! 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) {
//!             CommandStatus::Success => println!("Successfully wrote slot."),
//!             CommandStatus::Error(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::CommandError;
//!
//! # fn try_main() -> Result<(), (CommandError)> {
//! 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`]: struct.UnauthenticatedDevice.html#method.authenticate_admin
//! [`authenticate_user`]: struct.UnauthenticatedDevice.html#method.authenticate_user
//! [`connect`]: fn.connect.html
//! [`connect_model`]: fn.connect_model.html
//! [`device`]: struct.AuthenticatedDevice.html#method.device
//! [`get_hotp_code`]: trait.ProvideOtp.html#method.get_hotp_code
//! [`get_totp_code`]: trait.ProvideOtp.html#method.get_totp_code
//! [`AdminAuthenticatedDevice`]: struct.AdminAuthenticatedDevice.html
//! [`UserAuthenticatedDevice`]: struct.UserAuthenticatedDevice.html
//! [`UnauthenticatedDevice`]: struct.UnauthenticatedDevice.html

extern crate libc;
extern crate nitrokey_sys;
extern crate rand;

mod config;
mod device;
mod otp;
mod util;
#[cfg(test)]
mod tests;

pub use config::Config;
pub use device::{AdminAuthenticatedDevice, Device, Model, UnauthenticatedDevice,
                 UserAuthenticatedDevice};
pub use otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData};
pub use util::{CommandError, CommandStatus, LogLevel};

/// Connects to a Nitrokey device.  This method can be used to connect to any
/// connected device, both a Nitrokey Pro and a Nitrokey Storage.
///
/// # Example
///
/// ```
/// use nitrokey::UnauthenticatedDevice;
///
/// fn do_something(device: UnauthenticatedDevice) {}
///
/// match nitrokey::connect() {
///     Ok(device) => do_something(device),
///     Err(err) => println!("Could not connect to a Nitrokey: {:?}", err),
/// }
/// ```
pub fn connect() -> Result<UnauthenticatedDevice, CommandError> {
    unsafe {
        match nitrokey_sys::NK_login_auto() {
            1 => Ok(UnauthenticatedDevice {}),
            _ => Err(CommandError::Unknown),
        }
    }
}

/// Connects to a Nitrokey device of the given model.
///
/// # Example
///
/// ```
/// use nitrokey::{Model, UnauthenticatedDevice};
///
/// fn do_something(device: UnauthenticatedDevice) {}
///
/// match nitrokey::connect_model(Model::Pro) {
///     Ok(device) => do_something(device),
///     Err(err) => println!("Could not connect to a Nitrokey Pro: {:?}", err),
/// }
/// ```
pub fn connect_model(model: Model) -> Result<UnauthenticatedDevice, CommandError> {
    let model = match model {
        Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE,
        Model::Pro => nitrokey_sys::NK_device_model_NK_PRO,
    };
    unsafe {
        return match nitrokey_sys::NK_login_enum(model) {
            1 => Ok(UnauthenticatedDevice {}),
            rv => Err(CommandError::from(rv)),
        };
    }
}

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