summaryrefslogtreecommitdiff
path: root/src/args.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/args.rs')
-rw-r--r--src/args.rs422
1 files changed, 8 insertions, 414 deletions
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,