diff --git a/src/cli.rs b/src/cli.rs index 5698574..6607946 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,26 +2,19 @@ use crate::error::*; use crate::luks::{Fido2LuksToken, LuksDevice}; use crate::util::sha256; use crate::*; +pub use cli_args::Args; use cli_args::*; - -use structopt::clap::Shell; -use structopt::StructOpt; - use ctap::{FidoCredential, FidoErrorKind}; - +use std::borrow::Cow; +use std::collections::HashSet; use std::io::Write; +use std::iter::FromIterator; use std::str::FromStr; use std::thread; use std::time::Duration; - -use std::borrow::Cow; -use std::collections::HashSet; - use std::time::SystemTime; - -pub use cli_args::Args; - -use std::iter::FromIterator; +use structopt::clap::Shell; +use structopt::StructOpt; fn read_pin() -> Fido2LuksResult { util::read_password("Authenticator PIN", false) @@ -86,6 +79,43 @@ pub fn extend_creds_device( Ok(Vec::from_iter(additional.into_iter())) } +pub fn get_input( + secret: &SecretParameters, + authenticator: &AuthenticatorParameters, + interactive: bool, + q: &str, + verify: bool, +) -> Fido2LuksResult<(Option, [u8; 32])> { + let password_helper = secret + .password_helper + .as_ref() + .map(|helper| move || helper.obtain()); + let salt = &secret.salt; + Ok(if interactive { + ( + if authenticator.pin { + Some(util::read_password("PIN", false)?) + } else { + None + }, + salt.obtain_sha256(Some(|| util::read_password(q, verify)))?, + ) + } else { + match (authenticator.pin, authenticator.pin_prefixed) { + (true, false) => ( + Some(util::read_password("PIN", false)?), + salt.obtain_sha256(password_helper)?, + ), + (true, true) => read_password_pin_prefixed(|| { + salt.obtain(password_helper).and_then(|secret| { + String::from_utf8(secret).map_err(|e| Fido2LuksError::from(e)) + }) + })?, + (false, _) => (None, salt.obtain_sha256(password_helper)?), + } + }) +} + pub fn read_password_pin_prefixed( prefixed: impl Fn() -> Fido2LuksResult, ) -> Fido2LuksResult<(Option, [u8; 32])> { @@ -136,29 +166,8 @@ pub fn run_cli() -> Fido2LuksResult<()> { secret, device, } => { - let (pin, salt) = match ( - &secret.password_helper, - authenticator.pin, - authenticator.pin_prefixed, - args.interactive, - ) { - (Some(phelper), true, true, false) => { - read_password_pin_prefixed(|| phelper.obtain())? - } - (Some(phelper), false, false, false) => { - (None, secret.salt.obtain_sha256(&phelper)?) - } - - (phelper, pin, _, _) => ( - if pin { Some(read_pin()?) } else { None }, - match phelper { - None | Some(PasswordHelper::Stdin) => { - util::read_password_hashed("Password", false) - } - Some(phelper) => secret.salt.obtain_sha256(&phelper), - }?, - ), - }; + let (pin, salt) = + get_input(&secret, &authenticator, args.interactive, "Password", false)?; let credentials = if let Some(dev) = device { extend_creds_device( credentials @@ -222,31 +231,7 @@ pub fn run_cli() -> Fido2LuksResult<()> { }; let inputs = |q: &str, verify: bool| -> Fido2LuksResult<(Option, [u8; 32])> { - Ok( - match ( - &secret.password_helper, - authenticator.pin, - authenticator.pin_prefixed, - args.interactive, - ) { - (Some(phelper), true, true, false) => { - read_password_pin_prefixed(|| phelper.obtain())? - } - (Some(phelper), false, false, false) => { - (None, secret.salt.obtain_sha256(&phelper)?) - } - - (_phelper, pin, _, _) => ( - if pin { Some(read_pin()?) } else { None }, - match &secret.password_helper { - None | Some(PasswordHelper::Stdin) => { - util::read_password_hashed(q, verify) - } - Some(phelper) => secret.salt.obtain_sha256(&phelper), - }?, - ), - }, - ) + get_input(&secret, &authenticator, args.interactive, q, verify) }; let other_secret = |salt_q: &str, @@ -375,31 +360,7 @@ pub fn run_cli() -> Fido2LuksResult<()> { retries, } => { let inputs = |q: &str, verify: bool| -> Fido2LuksResult<(Option, [u8; 32])> { - Ok( - match ( - &secret.password_helper, - authenticator.pin, - authenticator.pin_prefixed, - args.interactive, - ) { - (Some(phelper), true, true, false) => { - read_password_pin_prefixed(|| phelper.obtain())? - } - (Some(phelper), false, false, false) => { - (None, secret.salt.obtain_sha256(&phelper)?) - } - - (phelper, pin, _, _) => ( - if pin { Some(read_pin()?) } else { None }, - match &phelper { - None | Some(PasswordHelper::Stdin) => { - util::read_password_hashed(q, verify) - } - Some(phelper) => secret.salt.obtain_sha256(&phelper), - }?, - ), - }, - ) + get_input(&secret, &authenticator, args.interactive, q, verify) }; // Cow shouldn't be necessary diff --git a/src/cli_args/config.rs b/src/cli_args/config.rs index cde093f..00bdbbc 100644 --- a/src/cli_args/config.rs +++ b/src/cli_args/config.rs @@ -55,11 +55,17 @@ impl fmt::Display for SecretInput { } impl SecretInput { - pub fn obtain_string(&self, password_helper: &PasswordHelper) -> Fido2LuksResult { + pub fn obtain_string( + &self, + password_helper: Option Fido2LuksResult>, + ) -> Fido2LuksResult { Ok(String::from_utf8(self.obtain(password_helper)?)?) } - pub fn obtain(&self, password_helper: &PasswordHelper) -> Fido2LuksResult> { + pub fn obtain( + &self, + password_helper: Option Fido2LuksResult>, + ) -> Fido2LuksResult> { let mut secret = Vec::new(); match self { SecretInput::File { path } => { @@ -67,16 +73,22 @@ impl SecretInput { let mut do_io = || File::open(path)?.read_to_end(&mut secret); do_io().map_err(|cause| Fido2LuksError::KeyfileError { cause })?; } - SecretInput::AskPassword => { - secret.extend_from_slice(password_helper.obtain()?.as_bytes()) - } + SecretInput::AskPassword => secret.extend_from_slice( + password_helper.ok_or_else(|| Fido2LuksError::AskPassError { + cause: AskPassError::FailedHelper, + })?()? + .as_bytes(), + ), SecretInput::String(s) => secret.extend_from_slice(s.as_bytes()), } Ok(secret) } - pub fn obtain_sha256(&self, password_helper: &PasswordHelper) -> Fido2LuksResult<[u8; 32]> { + pub fn obtain_sha256( + &self, + password_helper: Option Fido2LuksResult>, + ) -> Fido2LuksResult<[u8; 32]> { let mut digest = digest::Context::new(&digest::SHA256); match self { SecretInput::File { path } => { @@ -198,7 +210,7 @@ mod test { fn input_salt_obtain() { assert_eq!( SecretInput::String("abc".into()) - .obtain_sha256(&PasswordHelper::Stdin) + .obtain_sha256(None) .unwrap(), [ 186, 120, 22, 191, 143, 1, 207, 234, 65, 65, 64, 222, 93, 174, 34, 35, 176, 3, 97, diff --git a/src/error.rs b/src/error.rs index dc29d3d..f451f1c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -54,6 +54,8 @@ pub enum AskPassError { IO(io::Error), #[fail(display = "provided passwords don't match")] Mismatch, + #[fail(display = "failed to call password helper")] + FailedHelper, } #[derive(Debug, Fail)]