aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/arg_defs.rs428
-rw-r--r--src/arg_util.rs4
-rw-r--r--src/args.rs422
-rw-r--r--src/commands.rs55
-rw-r--r--src/main.rs1
-rw-r--r--src/tests/otp.rs6
6 files changed, 470 insertions, 446 deletions
diff --git a/src/arg_defs.rs b/src/arg_defs.rs
new file mode 100644
index 0000000..d195841
--- /dev/null
+++ b/src/arg_defs.rs
@@ -0,0 +1,428 @@
+// arg_defs.rs
+
+// *************************************************************************
+// * Copyright (C) 2020 Daniel Mueller (deso@posteo.net) *
+// * *
+// * This program is free software: you can redistribute it and/or modify *
+// * it under the terms of the GNU General Public License as published by *
+// * the Free Software Foundation, either version 3 of the License, or *
+// * (at your option) any later version. *
+// * *
+// * This program is distributed in the hope that it will be useful, *
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+// * GNU General Public License for more details. *
+// * *
+// * You should have received a copy of the GNU General Public License *
+// * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+// *************************************************************************
+
+use crate::args;
+use crate::commands;
+use crate::error::Error;
+use crate::pinentry;
+
+/// Provides access to a Nitrokey device
+#[derive(structopt::StructOpt)]
+#[structopt(name = "nitrocli")]
+pub struct Args {
+ /// Increases the log level (can be supplied multiple times)
+ #[structopt(short, long, global = true, parse(from_occurrences))]
+ pub verbose: u8,
+ /// Selects the device model to connect to
+ #[structopt(short, long, global = true, possible_values = &DeviceModel::all_str())]
+ pub model: Option<DeviceModel>,
+ #[structopt(subcommand)]
+ pub cmd: Command,
+}
+
+/// The available Nitrokey models.
+#[allow(unused_doc_comments)]
+Enum! {DeviceModel, [
+ Pro => "pro",
+ Storage => "storage",
+]}
+
+impl DeviceModel {
+ pub fn as_user_facing_str(&self) -> &str {
+ match self {
+ DeviceModel::Pro => "Pro",
+ DeviceModel::Storage => "Storage",
+ }
+ }
+}
+
+impl From<DeviceModel> for nitrokey::Model {
+ fn from(model: DeviceModel) -> nitrokey::Model {
+ match model {
+ DeviceModel::Pro => nitrokey::Model::Pro,
+ DeviceModel::Storage => nitrokey::Model::Storage,
+ }
+ }
+}
+
+/// A top-level command for nitrocli.
+#[allow(unused_doc_comments)]
+Command! {Command, [
+ /// Reads or writes the device configuration
+ Config(ConfigArgs) => |ctx, args: ConfigArgs| args.subcmd.execute(ctx),
+ /// Interacts with the device's encrypted volume
+ Encrypted(EncryptedArgs) => |ctx, args: EncryptedArgs| args.subcmd.execute(ctx),
+ /// Interacts with the device's hidden volume
+ Hidden(HiddenArgs) => |ctx, args: HiddenArgs| args.subcmd.execute(ctx),
+ /// Lists the attached Nitrokey devices
+ List(ListArgs) => |ctx, args: ListArgs| commands::list(ctx, args.no_connect),
+ /// Locks the connected Nitrokey device
+ Lock => commands::lock,
+ /// Accesses one-time passwords
+ Otp(OtpArgs) => |ctx, args: OtpArgs| args.subcmd.execute(ctx),
+ /// Manages the Nitrokey PINs
+ Pin(PinArgs) => |ctx, args: PinArgs| args.subcmd.execute(ctx),
+ /// Accesses the password safe
+ Pws(PwsArgs) => |ctx, args: PwsArgs| args.subcmd.execute(ctx),
+ /// Performs a factory reset
+ Reset => commands::reset,
+ /// Prints the status of the connected Nitrokey device
+ Status => commands::status,
+ /// Interacts with the device's unencrypted volume
+ Unencrypted(UnencryptedArgs) => |ctx, args: UnencryptedArgs| args.subcmd.execute(ctx),
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct ConfigArgs {
+ #[structopt(subcommand)]
+ subcmd: ConfigCommand,
+}
+
+Command! {ConfigCommand, [
+ /// Prints the Nitrokey configuration
+ Get => commands::config_get,
+ /// Changes the Nitrokey configuration
+ Set(ConfigSetArgs) => args::config_set,
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct ConfigSetArgs {
+ /// Sets the numlock option to the given HOTP slot
+ #[structopt(short = "n", long)]
+ pub numlock: Option<u8>,
+ /// Unsets the numlock option
+ #[structopt(short = "N", long, conflicts_with("numlock"))]
+ pub no_numlock: bool,
+ /// Sets the capslock option to the given HOTP slot
+ #[structopt(short = "c", long)]
+ pub capslock: Option<u8>,
+ /// Unsets the capslock option
+ #[structopt(short = "C", long, conflicts_with("capslock"))]
+ pub no_capslock: bool,
+ /// Sets the scrollock option to the given HOTP slot
+ #[structopt(short = "s", long)]
+ pub scrollock: Option<u8>,
+ /// Unsets the scrollock option
+ #[structopt(short = "S", long, conflicts_with("scrollock"))]
+ pub no_scrollock: bool,
+ /// Requires the user PIN to generate one-time passwords
+ #[structopt(short = "o", long)]
+ pub otp_pin: bool,
+ /// Allows one-time password generation without PIN
+ #[structopt(short = "O", long, conflicts_with("otp-pin"))]
+ pub no_otp_pin: bool,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum ConfigOption<T> {
+ Enable(T),
+ Disable,
+ Ignore,
+}
+
+impl<T> ConfigOption<T> {
+ pub fn try_from(disable: bool, value: Option<T>, name: &'static str) -> Result<Self, Error> {
+ if disable {
+ if value.is_some() {
+ Err(Error::Error(format!(
+ "--{name} and --no-{name} are mutually exclusive",
+ name = name
+ )))
+ } else {
+ Ok(ConfigOption::Disable)
+ }
+ } else {
+ match value {
+ Some(value) => Ok(ConfigOption::Enable(value)),
+ None => Ok(ConfigOption::Ignore),
+ }
+ }
+ }
+
+ pub fn or(self, default: Option<T>) -> Option<T> {
+ match self {
+ ConfigOption::Enable(value) => Some(value),
+ ConfigOption::Disable => None,
+ ConfigOption::Ignore => default,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct EncryptedArgs {
+ #[structopt(subcommand)]
+ subcmd: EncryptedCommand,
+}
+
+Command! {EncryptedCommand, [
+ /// Closes the encrypted volume on a Nitrokey Storage
+ Close => commands::encrypted_close,
+ /// Opens the encrypted volume on a Nitrokey Storage
+ Open => commands::encrypted_open,
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct HiddenArgs {
+ #[structopt(subcommand)]
+ subcmd: HiddenCommand,
+}
+
+Command! {HiddenCommand, [
+ /// Closes the hidden volume on a Nitrokey Storage
+ Close => commands::hidden_close,
+ /// Creates a hidden volume on a Nitrokey Storage
+ Create(HiddenCreateArgs) => |ctx, args: HiddenCreateArgs| {
+ commands::hidden_create(ctx, args.slot, args.start, args.end)
+ },
+ /// Opens the hidden volume on a Nitrokey Storage
+ Open => commands::hidden_open,
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct HiddenCreateArgs {
+ /// The hidden volume slot to use
+ pub slot: u8,
+ /// The start location of the hidden volume as a percentage of the encrypted volume's size (0-99)
+ pub start: u8,
+ /// The end location of the hidden volume as a percentage of the encrypted volume's size (1-100)
+ pub end: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct ListArgs {
+ /// Only print the information that is available without connecting to a device
+ #[structopt(short, long)]
+ pub no_connect: bool,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpArgs {
+ #[structopt(subcommand)]
+ subcmd: OtpCommand,
+}
+
+Command! {OtpCommand, [
+ /// Clears a one-time password slot
+ Clear(OtpClearArgs) => |ctx, args: OtpClearArgs| {
+ commands::otp_clear(ctx, args.slot, args.algorithm)
+ },
+ /// Generates a one-time password
+ Get(OtpGetArgs) => |ctx, args: OtpGetArgs| {
+ commands::otp_get(ctx, args.slot, args.algorithm, args.time)
+ },
+ /// Configures a one-time password slot
+ Set(OtpSetArgs) => args::otp_set,
+ /// Prints the status of the one-time password slots
+ Status(OtpStatusArgs) => |ctx, args: OtpStatusArgs| commands::otp_status(ctx, args.all),
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpClearArgs {
+ /// The OTP algorithm to use
+ #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
+ possible_values = &OtpAlgorithm::all_str())]
+ pub algorithm: OtpAlgorithm,
+ /// The OTP slot to clear
+ pub slot: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpGetArgs {
+ /// The OTP algorithm to use
+ #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
+ possible_values = &OtpAlgorithm::all_str())]
+ pub algorithm: OtpAlgorithm,
+ /// The time to use for TOTP generation (Unix timestamp) [default: system time]
+ #[structopt(short, long)]
+ pub time: Option<u64>,
+ /// The OTP slot to use
+ pub slot: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpSetArgs {
+ /// The OTP algorithm to use
+ #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
+ possible_values = &OtpAlgorithm::all_str())]
+ pub algorithm: OtpAlgorithm,
+ /// The number of digits to use for the one-time password
+ #[structopt(short, long, default_value = OtpMode::SixDigits.as_ref(),
+ possible_values = &OtpMode::all_str())]
+ pub digits: OtpMode,
+ /// The counter value for HOTP
+ #[structopt(short, long, default_value = "0")]
+ pub counter: u64,
+ /// The time window for TOTP
+ #[structopt(short, long, default_value = "30")]
+ pub time_window: u16,
+ /// The format of the secret
+ #[structopt(short, long, default_value = OtpSecretFormat::Hex.as_ref(),
+ possible_values = &OtpSecretFormat::all_str())]
+ pub format: OtpSecretFormat,
+ /// The OTP slot to use
+ pub slot: u8,
+ /// The name of the slot
+ pub name: String,
+ /// The secret to store on the slot as a hexadecimal string (or in the format set with the
+ /// --format option)
+ pub secret: String,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct OtpStatusArgs {
+ /// Shows slots that are not programmed
+ #[structopt(short, long)]
+ pub all: bool,
+}
+
+Enum! {OtpAlgorithm, [
+ Hotp => "hotp",
+ Totp => "totp",
+]}
+
+Enum! {OtpMode, [
+ SixDigits => "6",
+ EightDigits => "8",
+]}
+
+impl From<OtpMode> for nitrokey::OtpMode {
+ fn from(mode: OtpMode) -> Self {
+ match mode {
+ OtpMode::SixDigits => nitrokey::OtpMode::SixDigits,
+ OtpMode::EightDigits => nitrokey::OtpMode::EightDigits,
+ }
+ }
+}
+
+Enum! {OtpSecretFormat, [
+ Ascii => "ascii",
+ Base32 => "base32",
+ Hex => "hex",
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PinArgs {
+ #[structopt(subcommand)]
+ subcmd: PinCommand,
+}
+
+Command! {PinCommand, [
+ /// Clears the cached PINs
+ Clear => commands::pin_clear,
+ /// Changes a PIN
+ Set(PinSetArgs) => |ctx, args: PinSetArgs| commands::pin_set(ctx, args.pintype),
+ /// Unblocks and resets the user PIN
+ Unblock => commands::pin_unblock,
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PinSetArgs {
+ /// The PIN type to change
+ #[structopt(name = "type", possible_values = &pinentry::PinType::all_str())]
+ pub pintype: pinentry::PinType,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsArgs {
+ #[structopt(subcommand)]
+ subcmd: PwsCommand,
+}
+
+Command! {PwsCommand, [
+ /// Clears a password safe slot
+ Clear(PwsClearArgs) => |ctx, args: PwsClearArgs| commands::pws_clear(ctx, args.slot),
+ /// Reads a password safe slot
+ Get(PwsGetArgs) => |ctx, args: PwsGetArgs| {
+ commands::pws_get(ctx, args.slot, args.name, args.login, args.password, args.quiet)
+ },
+ /// Writes a password safe slot
+ Set(PwsSetArgs) => |ctx, args: PwsSetArgs| {
+ commands::pws_set(ctx, args.slot, &args.name, &args.login, &args.password)
+ },
+ /// Prints the status of the password safe slots
+ Status(PwsStatusArgs) => |ctx, args: PwsStatusArgs| commands::pws_status(ctx, args.all),
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsClearArgs {
+ /// The PWS slot to clear
+ pub slot: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsGetArgs {
+ /// Shows the name stored on the slot
+ #[structopt(short, long)]
+ pub name: bool,
+ /// Shows the login stored on the slot
+ #[structopt(short, long)]
+ pub login: bool,
+ /// Shows the password stored on the slot
+ #[structopt(short, long)]
+ pub password: bool,
+ /// Prints the stored data without description
+ #[structopt(short, long)]
+ pub quiet: bool,
+ /// The PWS slot to read
+ pub slot: u8,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsSetArgs {
+ /// The PWS slot to write
+ pub slot: u8,
+ /// The name to store on the slot
+ pub name: String,
+ /// The login to store on the slot
+ pub login: String,
+ /// The password to store on the slot
+ pub password: String,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct PwsStatusArgs {
+ /// Shows slots that are not programmed
+ #[structopt(short, long)]
+ pub all: bool,
+}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct UnencryptedArgs {
+ #[structopt(subcommand)]
+ subcmd: UnencryptedCommand,
+}
+
+Command! {UnencryptedCommand, [
+ /// Changes the configuration of the unencrypted volume on a Nitrokey Storage
+ Set(UnencryptedSetArgs) => |ctx, args: UnencryptedSetArgs| {
+ commands::unencrypted_set(ctx, args.mode)
+ },
+]}
+
+#[derive(Debug, PartialEq, structopt::StructOpt)]
+pub struct UnencryptedSetArgs {
+ /// The mode to change to
+ #[structopt(name = "type", possible_values = &UnencryptedVolumeMode::all_str())]
+ pub mode: UnencryptedVolumeMode,
+}
+
+Enum! {UnencryptedVolumeMode, [
+ ReadWrite => "read-write",
+ ReadOnly => "read-only",
+]}
diff --git a/src/arg_util.rs b/src/arg_util.rs
index 930e470..c16c326 100644
--- a/src/arg_util.rs
+++ b/src/arg_util.rs
@@ -35,7 +35,7 @@ macro_rules! tr {
macro_rules! Command {
( $name:ident, [ $( $(#[$doc:meta])* $var:ident$(($inner:ty))? => $exec:expr, ) *] ) => {
#[derive(Debug, PartialEq, structopt::StructOpt)]
- enum $name {
+ pub enum $name {
$(
$(#[$doc])*
$var$(($inner))?,
@@ -44,7 +44,7 @@ macro_rules! Command {
#[allow(unused_qualifications)]
impl $name {
- fn execute(
+ pub fn execute(
self,
ctx: &mut crate::args::ExecCtx<'_>,
) -> crate::Result<()> {
diff --git a/src/args.rs b/src/args.rs
index df93814..720fabf 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -20,11 +20,10 @@
use std::ffi;
use std::io;
use std::result;
-use std::str;
+use crate::arg_defs;
use crate::commands;
use crate::error::Error;
-use crate::pinentry;
use crate::RunCtx;
type Result<T> = result::Result<T, Error>;
@@ -51,7 +50,7 @@ where
/// A command execution context that captures additional data pertaining
/// the command execution.
pub struct ExecCtx<'io> {
- pub model: Option<DeviceModel>,
+ pub model: Option<arg_defs::DeviceModel>,
pub stdout: &'io mut dyn io::Write,
pub stderr: &'io mut dyn io::Write,
pub admin_pin: Option<ffi::OsString>,
@@ -69,152 +68,10 @@ impl<'io> Stdio for ExecCtx<'io> {
}
}
-/// Provides access to a Nitrokey device
-#[derive(structopt::StructOpt)]
-#[structopt(name = "nitrocli")]
-struct Args {
- /// Increases the log level (can be supplied multiple times)
- #[structopt(short, long, global = true, parse(from_occurrences))]
- verbose: u8,
- /// Selects the device model to connect to
- #[structopt(short, long, global = true, possible_values = &DeviceModel::all_str())]
- model: Option<DeviceModel>,
- #[structopt(subcommand)]
- cmd: Command,
-}
-
-/// The available Nitrokey models.
-#[allow(unused_doc_comments)]
-Enum! {DeviceModel, [
- Pro => "pro",
- Storage => "storage",
-]}
-
-impl DeviceModel {
- pub fn as_user_facing_str(&self) -> &str {
- match self {
- DeviceModel::Pro => "Pro",
- DeviceModel::Storage => "Storage",
- }
- }
-}
-
-impl From<DeviceModel> for nitrokey::Model {
- fn from(model: DeviceModel) -> nitrokey::Model {
- match model {
- DeviceModel::Pro => nitrokey::Model::Pro,
- DeviceModel::Storage => nitrokey::Model::Storage,
- }
- }
-}
-
-/// A top-level command for nitrocli.
-#[allow(unused_doc_comments)]
-Command! {Command, [
- /// Reads or writes the device configuration
- Config(ConfigArgs) => |ctx, args: ConfigArgs| args.subcmd.execute(ctx),
- /// Interacts with the device's encrypted volume
- Encrypted(EncryptedArgs) => |ctx, args: EncryptedArgs| args.subcmd.execute(ctx),
- /// Interacts with the device's hidden volume
- Hidden(HiddenArgs) => |ctx, args: HiddenArgs| args.subcmd.execute(ctx),
- /// Lists the attached Nitrokey devices
- List(ListArgs) => |ctx, args: ListArgs| commands::list(ctx, args.no_connect),
- /// Locks the connected Nitrokey device
- Lock => commands::lock,
- /// Accesses one-time passwords
- Otp(OtpArgs) => |ctx, args: OtpArgs| args.subcmd.execute(ctx),
- /// Manages the Nitrokey PINs
- Pin(PinArgs) => |ctx, args: PinArgs| args.subcmd.execute(ctx),
- /// Accesses the password safe
- Pws(PwsArgs) => |ctx, args: PwsArgs| args.subcmd.execute(ctx),
- /// Performs a factory reset
- Reset => commands::reset,
- /// Prints the status of the connected Nitrokey device
- Status => commands::status,
- /// Interacts with the device's unencrypted volume
- Unencrypted(UnencryptedArgs) => |ctx, args: UnencryptedArgs| args.subcmd.execute(ctx),
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct ConfigArgs {
- #[structopt(subcommand)]
- subcmd: ConfigCommand,
-}
-
-Command! {ConfigCommand, [
- /// Prints the Nitrokey configuration
- Get => commands::config_get,
- /// Changes the Nitrokey configuration
- Set(ConfigSetArgs) => config_set,
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct ConfigSetArgs {
- /// Sets the numlock option to the given HOTP slot
- #[structopt(short = "n", long)]
- numlock: Option<u8>,
- /// Unsets the numlock option
- #[structopt(short = "N", long, conflicts_with("numlock"))]
- no_numlock: bool,
- /// Sets the capslock option to the given HOTP slot
- #[structopt(short = "c", long)]
- capslock: Option<u8>,
- /// Unsets the capslock option
- #[structopt(short = "C", long, conflicts_with("capslock"))]
- no_capslock: bool,
- /// Sets the scrollock option to the given HOTP slot
- #[structopt(short = "s", long)]
- scrollock: Option<u8>,
- /// Unsets the scrollock option
- #[structopt(short = "S", long, conflicts_with("scrollock"))]
- no_scrollock: bool,
- /// Requires the user PIN to generate one-time passwords
- #[structopt(short = "o", long)]
- otp_pin: bool,
- /// Allows one-time password generation without PIN
- #[structopt(short = "O", long, conflicts_with("otp-pin"))]
- no_otp_pin: bool,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum ConfigOption<T> {
- Enable(T),
- Disable,
- Ignore,
-}
-
-impl<T> ConfigOption<T> {
- fn try_from(disable: bool, value: Option<T>, name: &'static str) -> Result<Self> {
- if disable {
- if value.is_some() {
- Err(Error::Error(format!(
- "--{name} and --no-{name} are mutually exclusive",
- name = name
- )))
- } else {
- Ok(ConfigOption::Disable)
- }
- } else {
- match value {
- Some(value) => Ok(ConfigOption::Enable(value)),
- None => Ok(ConfigOption::Ignore),
- }
- }
- }
-
- pub fn or(self, default: Option<T>) -> Option<T> {
- match self {
- ConfigOption::Enable(value) => Some(value),
- ConfigOption::Disable => None,
- ConfigOption::Ignore => default,
- }
- }
-}
-
-fn config_set(ctx: &mut ExecCtx<'_>, args: ConfigSetArgs) -> Result<()> {
- let numlock = ConfigOption::try_from(args.no_numlock, args.numlock, "numlock")?;
- let capslock = ConfigOption::try_from(args.no_capslock, args.capslock, "capslock")?;
- let scrollock = ConfigOption::try_from(args.no_scrollock, args.scrollock, "scrollock")?;
+pub fn config_set(ctx: &mut ExecCtx<'_>, args: arg_defs::ConfigSetArgs) -> Result<()> {
+ let numlock = arg_defs::ConfigOption::try_from(args.no_numlock, args.numlock, "numlock")?;
+ let capslock = arg_defs::ConfigOption::try_from(args.no_capslock, args.capslock, "capslock")?;
+ let scrollock = arg_defs::ConfigOption::try_from(args.no_scrollock, args.scrollock, "scrollock")?;
let otp_pin = if args.otp_pin {
Some(true)
} else if args.no_otp_pin {
@@ -225,159 +82,7 @@ fn config_set(ctx: &mut ExecCtx<'_>, args: ConfigSetArgs) -> Result<()> {
commands::config_set(ctx, numlock, capslock, scrollock, otp_pin)
}
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct EncryptedArgs {
- #[structopt(subcommand)]
- subcmd: EncryptedCommand,
-}
-
-Command! {EncryptedCommand, [
- /// Closes the encrypted volume on a Nitrokey Storage
- Close => commands::encrypted_close,
- /// Opens the encrypted volume on a Nitrokey Storage
- Open => commands::encrypted_open,
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct HiddenArgs {
- #[structopt(subcommand)]
- subcmd: HiddenCommand,
-}
-
-Command! {HiddenCommand, [
- /// Closes the hidden volume on a Nitrokey Storage
- Close => commands::hidden_close,
- /// Creates a hidden volume on a Nitrokey Storage
- Create(HiddenCreateArgs) => |ctx, args: HiddenCreateArgs| {
- commands::hidden_create(ctx, args.slot, args.start, args.end)
- },
- /// Opens the hidden volume on a Nitrokey Storage
- Open => commands::hidden_open,
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct HiddenCreateArgs {
- /// The hidden volume slot to use
- slot: u8,
- /// The start location of the hidden volume as a percentage of the encrypted volume's size (0-99)
- start: u8,
- /// The end location of the hidden volume as a percentage of the encrypted volume's size (1-100)
- end: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct ListArgs {
- /// Only print the information that is available without connecting to a device
- #[structopt(short, long)]
- no_connect: bool,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct OtpArgs {
- #[structopt(subcommand)]
- subcmd: OtpCommand,
-}
-
-Command! {OtpCommand, [
- /// Clears a one-time password slot
- Clear(OtpClearArgs) => |ctx, args: OtpClearArgs| {
- commands::otp_clear(ctx, args.slot, args.algorithm)
- },
- /// Generates a one-time password
- Get(OtpGetArgs) => |ctx, args: OtpGetArgs| {
- commands::otp_get(ctx, args.slot, args.algorithm, args.time)
- },
- /// Configures a one-time password slot
- Set(OtpSetArgs) => otp_set,
- /// Prints the status of the one-time password slots
- Status(OtpStatusArgs) => |ctx, args: OtpStatusArgs| commands::otp_status(ctx, args.all),
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct OtpClearArgs {
- /// The OTP algorithm to use
- #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
- possible_values = &OtpAlgorithm::all_str())]
- algorithm: OtpAlgorithm,
- /// The OTP slot to clear
- slot: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct OtpGetArgs {
- /// The OTP algorithm to use
- #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
- possible_values = &OtpAlgorithm::all_str())]
- algorithm: OtpAlgorithm,
- /// The time to use for TOTP generation (Unix timestamp) [default: system time]
- #[structopt(short, long)]
- time: Option<u64>,
- /// The OTP slot to use
- slot: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct OtpSetArgs {
- /// The OTP algorithm to use
- #[structopt(short, long, default_value = OtpAlgorithm::Totp.as_ref(),
- possible_values = &OtpAlgorithm::all_str())]
- algorithm: OtpAlgorithm,
- /// The number of digits to use for the one-time password
- #[structopt(short, long, default_value = OtpMode::SixDigits.as_ref(),
- possible_values = &OtpMode::all_str())]
- digits: OtpMode,
- /// The counter value for HOTP
- #[structopt(short, long, default_value = "0")]
- counter: u64,
- /// The time window for TOTP
- #[structopt(short, long, default_value = "30")]
- time_window: u16,
- /// The format of the secret
- #[structopt(short, long, default_value = OtpSecretFormat::Hex.as_ref(),
- possible_values = &OtpSecretFormat::all_str())]
- format: OtpSecretFormat,
- /// The OTP slot to use
- slot: u8,
- /// The name of the slot
- name: String,
- /// The secret to store on the slot as a hexadecimal string (or in the format set with the
- /// --format option)
- secret: String,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct OtpStatusArgs {
- /// Shows slots that are not programmed
- #[structopt(short, long)]
- all: bool,
-}
-
-Enum! {OtpAlgorithm, [
- Hotp => "hotp",
- Totp => "totp",
-]}
-
-Enum! {OtpMode, [
- SixDigits => "6",
- EightDigits => "8",
-]}
-
-impl From<OtpMode> for nitrokey::OtpMode {
- fn from(mode: OtpMode) -> Self {
- match mode {
- OtpMode::SixDigits => nitrokey::OtpMode::SixDigits,
- OtpMode::EightDigits => nitrokey::OtpMode::EightDigits,
- }
- }
-}
-
-Enum! {OtpSecretFormat, [
- Ascii => "ascii",
- Base32 => "base32",
- Hex => "hex",
-]}
-
-fn otp_set(ctx: &mut ExecCtx<'_>, args: OtpSetArgs) -> Result<()> {
+pub fn otp_set(ctx: &mut ExecCtx<'_>, args: arg_defs::OtpSetArgs) -> Result<()> {
let data = nitrokey::OtpSlotData {
number: args.slot,
name: args.name,
@@ -396,122 +101,11 @@ fn otp_set(ctx: &mut ExecCtx<'_>, args: OtpSetArgs) -> Result<()> {
)
}
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct PinArgs {
- #[structopt(subcommand)]
- subcmd: PinCommand,
-}
-
-Command! {PinCommand, [
- /// Clears the cached PINs
- Clear => commands::pin_clear,
- /// Changes a PIN
- Set(PinSetArgs) => |ctx, args: PinSetArgs| commands::pin_set(ctx, args.pintype),
- /// Unblocks and resets the user PIN
- Unblock => commands::pin_unblock,
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct PinSetArgs {
- /// The PIN type to change
- #[structopt(name = "type", possible_values = &pinentry::PinType::all_str())]
- pintype: pinentry::PinType,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct PwsArgs {
- #[structopt(subcommand)]
- subcmd: PwsCommand,
-}
-
-Command! {PwsCommand, [
- /// Clears a password safe slot
- Clear(PwsClearArgs) => |ctx, args: PwsClearArgs| commands::pws_clear(ctx, args.slot),
- /// Reads a password safe slot
- Get(PwsGetArgs) => |ctx, args: PwsGetArgs| {
- commands::pws_get(ctx, args.slot, args.name, args.login, args.password, args.quiet)
- },
- /// Writes a password safe slot
- Set(PwsSetArgs) => |ctx, args: PwsSetArgs| {
- commands::pws_set(ctx, args.slot, &args.name, &args.login, &args.password)
- },
- /// Prints the status of the password safe slots
- Status(PwsStatusArgs) => |ctx, args: PwsStatusArgs| commands::pws_status(ctx, args.all),
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct PwsClearArgs {
- /// The PWS slot to clear
- slot: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct PwsGetArgs {
- /// Shows the name stored on the slot
- #[structopt(short, long)]
- name: bool,
- /// Shows the login stored on the slot
- #[structopt(short, long)]
- login: bool,
- /// Shows the password stored on the slot
- #[structopt(short, long)]
- password: bool,
- /// Prints the stored data without description
- #[structopt(short, long)]
- quiet: bool,
- /// The PWS slot to read
- slot: u8,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct PwsSetArgs {
- /// The PWS slot to write
- slot: u8,
- /// The name to store on the slot
- name: String,
- /// The login to store on the slot
- login: String,
- /// The password to store on the slot
- password: String,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct PwsStatusArgs {
- /// Shows slots that are not programmed
- #[structopt(short, long)]
- all: bool,
-}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct UnencryptedArgs {
- #[structopt(subcommand)]
- subcmd: UnencryptedCommand,
-}
-
-Command! {UnencryptedCommand, [
- /// Changes the configuration of the unencrypted volume on a Nitrokey Storage
- Set(UnencryptedSetArgs) => |ctx, args: UnencryptedSetArgs| {
- commands::unencrypted_set(ctx, args.mode)
- },
-]}
-
-#[derive(Debug, PartialEq, structopt::StructOpt)]
-struct UnencryptedSetArgs {
- /// The mode to change to
- #[structopt(name = "type", possible_values = &UnencryptedVolumeMode::all_str())]
- mode: UnencryptedVolumeMode,
-}
-
-Enum! {UnencryptedVolumeMode, [
- ReadWrite => "read-write",
- ReadOnly => "read-only",
-]}
-
/// Parse the command-line arguments and execute the selected command.
pub(crate) fn handle_arguments(ctx: &mut RunCtx<'_>, args: Vec<String>) -> Result<()> {
use structopt::StructOpt;
- match Args::from_iter_safe(args.iter()) {
+ match arg_defs::Args::from_iter_safe(args.iter()) {
Ok(args) => {
let mut ctx = ExecCtx {
model: args.model,
diff --git a/src/commands.rs b/src/commands.rs
index 08dad04..31f4c26 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -30,6 +30,7 @@ use nitrokey::Device;
use nitrokey::GenerateOtp;
use nitrokey::GetPasswordSafe;
+use crate::arg_defs;
use crate::args;
use crate::error;
use crate::error::Error;
@@ -87,7 +88,7 @@ where
set_log_level(ctx);
if let Some(model) = ctx.model {
- if model != args::DeviceModel::Storage {
+ if model != arg_defs::DeviceModel::Storage {
return Err(Error::from(
"This command is only available on the Nitrokey Storage",
));
@@ -441,13 +442,13 @@ pub fn reset(ctx: &mut args::ExecCtx<'_>) -> Result<()> {
/// Change the configuration of the unencrypted volume.
pub fn unencrypted_set(
ctx: &mut args::ExecCtx<'_>,
- mode: args::UnencryptedVolumeMode,
+ mode: arg_defs::UnencryptedVolumeMode,
) -> Result<()> {
with_storage_device(ctx, |ctx, mut device| {
let pin_entry = pinentry::PinEntry::from(pinentry::PinType::Admin, &device)?;
let mode = match mode {
- args::UnencryptedVolumeMode::ReadWrite => nitrokey::VolumeMode::ReadWrite,
- args::UnencryptedVolumeMode::ReadOnly => nitrokey::VolumeMode::ReadOnly,
+ arg_defs::UnencryptedVolumeMode::ReadWrite => nitrokey::VolumeMode::ReadWrite,
+ arg_defs::UnencryptedVolumeMode::ReadOnly => nitrokey::VolumeMode::ReadOnly,
};
// The unencrypted volume may reconnect, so be sure to flush caches to
@@ -579,9 +580,9 @@ pub fn config_get(ctx: &mut args::ExecCtx<'_>) -> Result<()> {
/// Write the Nitrokey configuration.
pub fn config_set(
ctx: &mut args::ExecCtx<'_>,
- numlock: args::ConfigOption<u8>,
- capslock: args::ConfigOption<u8>,
- scrollock: args::ConfigOption<u8>,
+ numlock: arg_defs::ConfigOption<u8>,
+ capslock: arg_defs::ConfigOption<u8>,
+ scrollock: arg_defs::ConfigOption<u8>,
user_password: Option<bool>,
) -> Result<()> {
with_device(ctx, |ctx, device| {
@@ -610,13 +611,13 @@ pub fn lock(ctx: &mut args::ExecCtx<'_>) -> Result<()> {
})
}
-fn get_otp<T>(slot: u8, algorithm: args::OtpAlgorithm, device: &mut T) -> Result<String>
+fn get_otp<T>(slot: u8, algorithm: arg_defs::OtpAlgorithm, device: &mut T) -> Result<String>
where
T: GenerateOtp,
{
match algorithm {
- args::OtpAlgorithm::Hotp => device.get_hotp_code(slot),
- args::OtpAlgorithm::Totp => device.get_totp_code(slot),
+ arg_defs::OtpAlgorithm::Hotp => device.get_hotp_code(slot),
+ arg_defs::OtpAlgorithm::Totp => device.get_totp_code(slot),
}
.map_err(|err| get_error("Could not generate OTP", err))
}
@@ -632,11 +633,11 @@ fn get_unix_timestamp() -> Result<u64> {
pub fn otp_get(
ctx: &mut args::ExecCtx<'_>,
slot: u8,
- algorithm: args::OtpAlgorithm,
+ algorithm: arg_defs::OtpAlgorithm,
time: Option<u64>,
) -> Result<()> {
with_device(ctx, |ctx, mut device| {
- if algorithm == args::OtpAlgorithm::Totp {
+ if algorithm == arg_defs::OtpAlgorithm::Totp {
device
.set_time(
match time {
@@ -696,16 +697,16 @@ fn prepare_base32_secret(secret: &str) -> Result<String> {
pub fn otp_set(
ctx: &mut args::ExecCtx<'_>,
mut data: nitrokey::OtpSlotData,
- algorithm: args::OtpAlgorithm,
+ algorithm: arg_defs::OtpAlgorithm,
counter: u64,
time_window: u16,
- secret_format: args::OtpSecretFormat,
+ secret_format: arg_defs::OtpSecretFormat,
) -> Result<()> {
with_device(ctx, |ctx, device| {
let secret = match secret_format {
- args::OtpSecretFormat::Ascii => prepare_ascii_secret(&data.secret)?,
- args::OtpSecretFormat::Base32 => prepare_base32_secret(&data.secret)?,
- args::OtpSecretFormat::Hex => {
+ arg_defs::OtpSecretFormat::Ascii => prepare_ascii_secret(&data.secret)?,
+ arg_defs::OtpSecretFormat::Base32 => prepare_base32_secret(&data.secret)?,
+ arg_defs::OtpSecretFormat::Hex => {
// We need to ensure to provide a string with an even number of
// characters in it, just because that's what libnitrokey
// expects. So prepend a '0' if that is not the case.
@@ -721,8 +722,8 @@ pub fn otp_set(
let data = nitrokey::OtpSlotData { secret, ..data };
let mut device = authenticate_admin(ctx, device)?;
match algorithm {
- args::OtpAlgorithm::Hotp => device.write_hotp_slot(data, counter),
- args::OtpAlgorithm::Totp => device.write_totp_slot(data, time_window),
+ arg_defs::OtpAlgorithm::Hotp => device.write_hotp_slot(data, counter),
+ arg_defs::OtpAlgorithm::Totp => device.write_totp_slot(data, time_window),
}
.map_err(|err| get_error("Could not write OTP slot", err))?;
Ok(())
@@ -733,13 +734,13 @@ pub fn otp_set(
pub fn otp_clear(
ctx: &mut args::ExecCtx<'_>,
slot: u8,
- algorithm: args::OtpAlgorithm,
+ algorithm: arg_defs::OtpAlgorithm,
) -> Result<()> {
with_device(ctx, |ctx, device| {
let mut device = authenticate_admin(ctx, device)?;
match algorithm {
- args::OtpAlgorithm::Hotp => device.erase_hotp_slot(slot),
- args::OtpAlgorithm::Totp => device.erase_totp_slot(slot),
+ arg_defs::OtpAlgorithm::Hotp => device.erase_hotp_slot(slot),
+ arg_defs::OtpAlgorithm::Totp => device.erase_totp_slot(slot),
}
.map_err(|err| get_error("Could not clear OTP slot", err))?;
Ok(())
@@ -748,15 +749,15 @@ pub fn otp_clear(
fn print_otp_status(
ctx: &mut args::ExecCtx<'_>,
- algorithm: args::OtpAlgorithm,
+ algorithm: arg_defs::OtpAlgorithm,
device: &nitrokey::DeviceWrapper<'_>,
all: bool,
) -> Result<()> {
let mut slot: u8 = 0;
loop {
let result = match algorithm {
- args::OtpAlgorithm::Hotp => device.get_hotp_slot_name(slot),
- args::OtpAlgorithm::Totp => device.get_totp_slot_name(slot),
+ arg_defs::OtpAlgorithm::Hotp => device.get_hotp_slot_name(slot),
+ arg_defs::OtpAlgorithm::Totp => device.get_totp_slot_name(slot),
};
slot = match slot.checked_add(1) {
Some(slot) => slot,
@@ -784,8 +785,8 @@ fn print_otp_status(
pub fn otp_status(ctx: &mut args::ExecCtx<'_>, all: bool) -> Result<()> {
with_device(ctx, |ctx, device| {
println!(ctx, "alg\tslot\tname")?;
- print_otp_status(ctx, args::OtpAlgorithm::Hotp, &device, all)?;
- print_otp_status(ctx, args::OtpAlgorithm::Totp, &device, all)?;
+ print_otp_status(ctx, arg_defs::OtpAlgorithm::Hotp, &device, all)?;
+ print_otp_status(ctx, arg_defs::OtpAlgorithm::Totp, &device, all)?;
Ok(())
})
}
diff --git a/src/main.rs b/src/main.rs
index e42bf4d..89c9d67 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -68,6 +68,7 @@ mod redefine;
#[macro_use]
mod arg_util;
+mod arg_defs;
mod args;
mod commands;
mod error;
diff --git a/src/tests/otp.rs b/src/tests/otp.rs
index 0ccecf9..4ce1156 100644
--- a/src/tests/otp.rs
+++ b/src/tests/otp.rs
@@ -19,7 +19,7 @@
use super::*;
-use crate::args;
+use crate::arg_defs;
#[test_device]
fn set_invalid_slot_raw(model: nitrokey::Model) {
@@ -101,8 +101,8 @@ fn set_get_totp(model: nitrokey::Model) -> crate::Result<()> {
#[test_device]
fn set_totp_uneven_chars(model: nitrokey::Model) -> crate::Result<()> {
let secrets = [
- (args::OtpSecretFormat::Hex, "123"),
- (args::OtpSecretFormat::Base32, "FBILDWWGA2"),
+ (arg_defs::OtpSecretFormat::Hex, "123"),
+ (arg_defs::OtpSecretFormat::Base32, "FBILDWWGA2"),
];
for (format, secret) in &secrets {