allow another fido device to be used as previous secret

This commit is contained in:
shimun 2020-04-06 20:18:00 +02:00
parent ad2451f548
commit d5c0d48f03
Signed by: shimun
GPG Key ID: E81D8382DC2F971B

View File

@ -4,7 +4,7 @@ use crate::*;
use structopt::StructOpt; use structopt::StructOpt;
use ctap::FidoCredential; use ctap::{FidoCredential, FidoErrorKind};
use failure::_core::fmt::{Display, Error, Formatter}; use failure::_core::fmt::{Display, Error, Formatter};
use failure::_core::str::FromStr; use failure::_core::str::FromStr;
use failure::_core::time::Duration; use failure::_core::time::Duration;
@ -65,7 +65,7 @@ pub struct Args {
#[derive(Debug, StructOpt, Clone)] #[derive(Debug, StructOpt, Clone)]
pub struct SecretGeneration { pub struct SecretGeneration {
/// FIDO credential ids, generate using fido2luks credential /// FIDO credential ids, seperated by ',' generate using fido2luks credential
#[structopt(name = "credential-id", env = "FIDO2LUKS_CREDENTIAL_ID")] #[structopt(name = "credential-id", env = "FIDO2LUKS_CREDENTIAL_ID")]
pub credential_ids: CommaSeparated<HexEncoded>, pub credential_ids: CommaSeparated<HexEncoded>,
/// Salt for secret generation, defaults to 'ask' /// Salt for secret generation, defaults to 'ask'
@ -99,14 +99,6 @@ pub struct SecretGeneration {
pub await_authenticator: u64, pub await_authenticator: u64,
} }
#[derive(Debug, StructOpt, Clone)]
pub struct LuksSettings {
/// Number of milliseconds required to derive the volume decryption key
/// Defaults to 10ms when using an authenticator or the default by cryptsetup when using a password
#[structopt(long = "kdf-time", name = "kdf-time")]
kdf_time: Option<u64>,
}
impl SecretGeneration { impl SecretGeneration {
pub fn patch(&self, args: &Args) -> Self { pub fn patch(&self, args: &Args) -> Self {
let mut me = self.clone(); let mut me = self.clone();
@ -150,6 +142,41 @@ impl SecretGeneration {
} }
} }
#[derive(Debug, StructOpt, Clone)]
pub struct LuksSettings {
/// Number of milliseconds required to derive the volume decryption key
/// Defaults to 10ms when using an authenticator or the default by cryptsetup when using a password
#[structopt(long = "kdf-time", name = "kdf-time")]
kdf_time: Option<u64>,
}
#[derive(Debug, StructOpt, Clone)]
pub struct OtherSecret {
/// Use a keyfile instead of a password
#[structopt(short = "d", long = "keyfile", conflicts_with = "fido_device")]
keyfile: Option<PathBuf>,
/// Use another fido device instead of a password
/// Note: this requires for the credential fot the other device to be passed as argument as well
#[structopt(short = "f", long = "fido-device", conflicts_with = "keyfile")]
fido_device: bool,
}
impl OtherSecret {
pub fn obtain(
&self,
secret_gen: &SecretGeneration,
verify_password: bool,
password_question: &str,
) -> Fido2LuksResult<Vec<u8>> {
match &self.keyfile {
Some(keyfile) => util::read_keyfile(keyfile.clone()),
None if self.fido_device => Ok(Vec::from(&secret_gen.obtain_secret()?[..])),
None => util::read_password(password_question, verify_password)
.map(|p| p.as_bytes().to_vec()),
}
}
}
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
pub enum Command { pub enum Command {
#[structopt(name = "print-secret")] #[structopt(name = "print-secret")]
@ -168,9 +195,8 @@ pub enum Command {
/// Will wipe all other keys /// Will wipe all other keys
#[structopt(short = "e", long = "exclusive")] #[structopt(short = "e", long = "exclusive")]
exclusive: bool, exclusive: bool,
/// Use a keyfile instead of typing a previous password #[structopt(flatten)]
#[structopt(short = "d", long = "keyfile")] existing_secret: OtherSecret,
keyfile: Option<PathBuf>,
#[structopt(flatten)] #[structopt(flatten)]
secret_gen: SecretGeneration, secret_gen: SecretGeneration,
#[structopt(flatten)] #[structopt(flatten)]
@ -184,9 +210,8 @@ pub enum Command {
/// Add the password and keep the key /// Add the password and keep the key
#[structopt(short = "a", long = "add-password")] #[structopt(short = "a", long = "add-password")]
add_password: bool, add_password: bool,
/// Use a keyfile instead of typing a previous password #[structopt(flatten)]
#[structopt(short = "d", long = "keyfile")] replacement: OtherSecret,
keyfile: Option<PathBuf>,
#[structopt(flatten)] #[structopt(flatten)]
secret_gen: SecretGeneration, secret_gen: SecretGeneration,
#[structopt(flatten)] #[structopt(flatten)]
@ -244,16 +269,13 @@ pub fn run_cli() -> Fido2LuksResult<()> {
Command::AddKey { Command::AddKey {
device, device,
exclusive, exclusive,
keyfile, existing_secret,
ref secret_gen, ref secret_gen,
luks_settings, luks_settings,
} => { } => {
let secret = secret_gen.patch(&args).obtain_secret()?; let secret_gen = secret_gen.patch(&args);
let old_secret = if let Some(keyfile) = keyfile.clone() { let old_secret = existing_secret.obtain(&secret_gen, false, "Existing password")?;
util::read_keyfile(keyfile.clone()) let secret = secret_gen.obtain_secret()?;
} else {
util::read_password("Old password", false).map(|p| p.as_bytes().to_vec())
}?;
let added_slot = luks::add_key( let added_slot = luks::add_key(
device.clone(), device.clone(),
&secret, &secret,
@ -280,16 +302,13 @@ pub fn run_cli() -> Fido2LuksResult<()> {
Command::ReplaceKey { Command::ReplaceKey {
device, device,
add_password, add_password,
keyfile, replacement,
ref secret_gen, ref secret_gen,
luks_settings, luks_settings,
} => { } => {
let secret_gen = secret_gen.patch(&args);
let secret = secret_gen.patch(&args).obtain_secret()?; let secret = secret_gen.patch(&args).obtain_secret()?;
let new_secret = if let Some(keyfile) = keyfile.clone() { let new_secret = replacement.obtain(&secret_gen, true, "Replacement password")?;
util::read_keyfile(keyfile.clone())
} else {
util::read_password("Password to add", *add_password).map(|p| p.as_bytes().to_vec())
}?;
let slot = if *add_password { let slot = if *add_password {
luks::add_key(device, &new_secret[..], &secret, luks_settings.kdf_time) luks::add_key(device, &new_secret[..], &secret, luks_settings.kdf_time)
} else { } else {
@ -310,16 +329,25 @@ pub fn run_cli() -> Fido2LuksResult<()> {
} => { } => {
let mut retries = *retries; let mut retries = *retries;
loop { loop {
let secret = secret_gen.patch(&args).obtain_secret()?; match secret_gen
match luks::open_container(&device, &name, &secret) { .patch(&args)
Err(e) => match e { .obtain_secret()
Fido2LuksError::WrongSecret if retries > 0 => { .and_then(|secret| luks::open_container(&device, &name, &secret))
retries -= 1; {
eprintln!("{}", e); Err(e) => {
continue; match e {
Fido2LuksError::WrongSecret if retries > 0 => (),
Fido2LuksError::AuthenticatorError { ref cause }
if cause.kind() == FidoErrorKind::Timeout && retries > 0 =>
{
()
}
e => break Err(e)?,
} }
e => Err(e)?, retries -= 1;
}, eprintln!("{}", e);
}
res => break res, res => break res,
} }
} }