rudimentary pin support
This commit is contained in:
parent
fcdd2a2d3d
commit
b8ae9d91f0
136
src/cli.rs
136
src/cli.rs
@ -57,10 +57,17 @@ impl<T: Display + FromStr> FromStr for CommaSeparated<T> {
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct AuthenticatorParameters {
|
||||
pub struct Credentials {
|
||||
/// FIDO credential ids, seperated by ',' generate using fido2luks credential
|
||||
#[structopt(name = "credential-id", env = "FIDO2LUKS_CREDENTIAL_ID")]
|
||||
pub credential_ids: CommaSeparated<HexEncoded>,
|
||||
pub ids: CommaSeparated<HexEncoded>,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct AuthenticatorParameters {
|
||||
/// Request a PIN to unlock the authenticator
|
||||
#[structopt(short = "P", long = "pin")]
|
||||
pub pin: bool,
|
||||
|
||||
/// Await for an authenticator to be connected, timeout after n seconds
|
||||
#[structopt(
|
||||
@ -77,8 +84,9 @@ pub struct LuksParameters {
|
||||
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
||||
device: PathBuf,
|
||||
|
||||
/// Try to unlock the device using a specifc keyslot, ignore all other slots
|
||||
#[structopt(long = "slot", env = "FIDO2LUKS_DEVICE_SLOT")]
|
||||
slot_hint: Option<u32>,
|
||||
slot: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt, Clone)]
|
||||
@ -117,6 +125,7 @@ fn derive_secret(
|
||||
credentials: &[HexEncoded],
|
||||
salt: &[u8; 32],
|
||||
timeout: u64,
|
||||
pin: Option<&str>,
|
||||
) -> Fido2LuksResult<[u8; 32]> {
|
||||
let timeout = Duration::from_secs(timeout);
|
||||
let start = SystemTime::now();
|
||||
@ -147,11 +156,16 @@ fn derive_secret(
|
||||
.collect::<Vec<_>>()[..],
|
||||
salt,
|
||||
timeout - start.elapsed().unwrap(),
|
||||
pin,
|
||||
)?[..],
|
||||
salt,
|
||||
]))
|
||||
}
|
||||
|
||||
fn read_pin() -> Fido2LuksResult<String> {
|
||||
util::read_password("Authenticator PIN", false)
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Args {
|
||||
/// Request passwords via Stdin instead of using the password helper
|
||||
@ -161,50 +175,6 @@ pub struct Args {
|
||||
pub command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt, Clone)]
|
||||
pub struct SecretGeneration {
|
||||
/// FIDO credential ids, seperated by ',' generate using fido2luks credential
|
||||
#[structopt(name = "credential-id", env = "FIDO2LUKS_CREDENTIAL_ID")]
|
||||
pub credential_ids: CommaSeparated<HexEncoded>,
|
||||
/// Salt for secret generation, defaults to 'ask'
|
||||
///
|
||||
/// Options:{n}
|
||||
/// - ask : Prompt user using password helper{n}
|
||||
/// - file:<PATH> : Will read <FILE>{n}
|
||||
/// - string:<STRING> : Will use <STRING>, which will be handled like a password provided to the 'ask' option{n}
|
||||
#[structopt(
|
||||
name = "salt",
|
||||
long = "salt",
|
||||
env = "FIDO2LUKS_SALT",
|
||||
default_value = "ask"
|
||||
)]
|
||||
pub salt: InputSalt,
|
||||
/// Script used to obtain passwords, overridden by --interactive flag
|
||||
#[structopt(
|
||||
name = "password-helper",
|
||||
env = "FIDO2LUKS_PASSWORD_HELPER",
|
||||
default_value = "/usr/bin/env systemd-ask-password 'Please enter second factor for LUKS disk encryption!'"
|
||||
)]
|
||||
pub password_helper: PasswordHelper,
|
||||
|
||||
/// Await for an authenticator to be connected, timeout after n seconds
|
||||
#[structopt(
|
||||
long = "await-dev",
|
||||
name = "await-dev",
|
||||
env = "FIDO2LUKS_DEVICE_AWAIT",
|
||||
default_value = "15"
|
||||
)]
|
||||
pub await_authenticator: u64,
|
||||
|
||||
/// Request the password twice to ensure it being correct
|
||||
#[structopt(
|
||||
long = "verify-password",
|
||||
env = "FIDO2LUKS_VERIFY_PASSWORD",
|
||||
hidden = true
|
||||
)]
|
||||
pub verify_password: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt, Clone)]
|
||||
pub struct OtherSecret {
|
||||
/// Use a keyfile instead of a password
|
||||
@ -224,6 +194,8 @@ pub enum Command {
|
||||
#[structopt(short = "b", long = "bin")]
|
||||
binary: bool,
|
||||
#[structopt(flatten)]
|
||||
credentials: Credentials,
|
||||
#[structopt(flatten)]
|
||||
authenticator: AuthenticatorParameters,
|
||||
#[structopt(flatten)]
|
||||
secret: SecretParameters,
|
||||
@ -234,6 +206,8 @@ pub enum Command {
|
||||
#[structopt(flatten)]
|
||||
luks: LuksParameters,
|
||||
#[structopt(flatten)]
|
||||
credentials: Credentials,
|
||||
#[structopt(flatten)]
|
||||
authenticator: AuthenticatorParameters,
|
||||
#[structopt(flatten)]
|
||||
secret: SecretParameters,
|
||||
@ -251,6 +225,8 @@ pub enum Command {
|
||||
#[structopt(flatten)]
|
||||
luks: LuksParameters,
|
||||
#[structopt(flatten)]
|
||||
credentials: Credentials,
|
||||
#[structopt(flatten)]
|
||||
authenticator: AuthenticatorParameters,
|
||||
#[structopt(flatten)]
|
||||
secret: SecretParameters,
|
||||
@ -268,6 +244,8 @@ pub enum Command {
|
||||
#[structopt(flatten)]
|
||||
luks: LuksParameters,
|
||||
#[structopt(flatten)]
|
||||
credentials: Credentials,
|
||||
#[structopt(flatten)]
|
||||
authenticator: AuthenticatorParameters,
|
||||
#[structopt(flatten)]
|
||||
secret: SecretParameters,
|
||||
@ -279,6 +257,8 @@ pub enum Command {
|
||||
/// Generate a new FIDO credential
|
||||
#[structopt(name = "credential")]
|
||||
Credential {
|
||||
#[structopt(flatten)]
|
||||
authenticator: AuthenticatorParameters,
|
||||
/// Name to be displayed on the authenticator if it has a display
|
||||
#[structopt(env = "FIDO2LUKS_CREDENTIAL_NAME")]
|
||||
name: Option<String>,
|
||||
@ -297,25 +277,44 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
let args = parse_cmdline();
|
||||
let interactive = args.interactive;
|
||||
match &args.command {
|
||||
Command::Credential { name } => {
|
||||
let cred = make_credential_id(name.as_ref().map(|n| n.as_ref()))?;
|
||||
Command::Credential {
|
||||
authenticator,
|
||||
name,
|
||||
} => {
|
||||
let pin_string;
|
||||
let pin = if authenticator.pin {
|
||||
pin_string = read_pin()?;
|
||||
Some(pin_string.as_ref())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let cred = make_credential_id(name.as_ref().map(|n| n.as_ref()), pin)?;
|
||||
println!("{}", hex::encode(&cred.id));
|
||||
Ok(())
|
||||
}
|
||||
Command::PrintSecret {
|
||||
binary,
|
||||
authenticator,
|
||||
credentials,
|
||||
secret,
|
||||
} => {
|
||||
let pin_string;
|
||||
let pin = if authenticator.pin {
|
||||
pin_string = read_pin()?;
|
||||
Some(pin_string.as_ref())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let salt = if interactive || secret.password_helper == PasswordHelper::Stdin {
|
||||
util::read_password_hashed("Password", false)
|
||||
} else {
|
||||
secret.salt.obtain(&secret.password_helper)
|
||||
}?;
|
||||
let secret = derive_secret(
|
||||
authenticator.credential_ids.0.as_slice(),
|
||||
credentials.ids.0.as_slice(),
|
||||
&salt,
|
||||
authenticator.await_time,
|
||||
pin,
|
||||
)?;
|
||||
if *binary {
|
||||
stdout.write_all(&secret[..])?;
|
||||
@ -327,11 +326,17 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
Command::AddKey {
|
||||
luks,
|
||||
authenticator,
|
||||
credentials,
|
||||
secret,
|
||||
exclusive,
|
||||
existing_secret,
|
||||
luks_mod,
|
||||
} => {
|
||||
let pin = if authenticator.pin {
|
||||
Some(read_pin()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let salt = |q: &str, verify: bool| -> Fido2LuksResult<[u8; 32]> {
|
||||
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
||||
util::read_password_hashed(q, verify)
|
||||
@ -347,9 +352,10 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
OtherSecret {
|
||||
fido_device: true, ..
|
||||
} => derive_secret(
|
||||
&authenticator.credential_ids.0,
|
||||
&credentials.ids.0,
|
||||
&salt("Existing password", false)?,
|
||||
authenticator.await_time,
|
||||
pin.as_deref(),
|
||||
)?[..]
|
||||
.to_vec(),
|
||||
_ => util::read_password("Existing password", false)?
|
||||
@ -357,9 +363,10 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
.to_vec(),
|
||||
};
|
||||
let secret = derive_secret(
|
||||
&authenticator.credential_ids.0,
|
||||
&credentials.ids.0,
|
||||
&salt("Password", false)?,
|
||||
authenticator.await_time,
|
||||
pin.as_deref(),
|
||||
)?;
|
||||
let added_slot = luks::add_key(
|
||||
&luks.device,
|
||||
@ -387,11 +394,17 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
Command::ReplaceKey {
|
||||
luks,
|
||||
authenticator,
|
||||
credentials,
|
||||
secret,
|
||||
add_password,
|
||||
replacement,
|
||||
luks_mod,
|
||||
} => {
|
||||
let pin = if authenticator.pin {
|
||||
Some(read_pin()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let salt = |q: &str, verify: bool| -> Fido2LuksResult<[u8; 32]> {
|
||||
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
||||
util::read_password_hashed(q, verify)
|
||||
@ -407,9 +420,10 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
OtherSecret {
|
||||
fido_device: true, ..
|
||||
} => derive_secret(
|
||||
&authenticator.credential_ids.0,
|
||||
&credentials.ids.0,
|
||||
&salt("Replacement password", true)?,
|
||||
authenticator.await_time,
|
||||
pin.as_deref(),
|
||||
)?[..]
|
||||
.to_vec(),
|
||||
_ => util::read_password("Replacement password", true)?
|
||||
@ -417,9 +431,10 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
.to_vec(),
|
||||
};
|
||||
let existing_secret = derive_secret(
|
||||
&authenticator.credential_ids.0,
|
||||
&credentials.ids.0,
|
||||
&salt("Password", false)?,
|
||||
authenticator.await_time,
|
||||
pin.as_deref(),
|
||||
)?;
|
||||
let slot = if *add_password {
|
||||
luks::add_key(
|
||||
@ -446,10 +461,18 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
Command::Open {
|
||||
luks,
|
||||
authenticator,
|
||||
credentials,
|
||||
secret,
|
||||
name,
|
||||
retries,
|
||||
} => {
|
||||
let pin_string;
|
||||
let pin = if authenticator.pin {
|
||||
pin_string = read_pin()?;
|
||||
Some(pin_string.as_ref())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let salt = |q: &str, verify: bool| -> Fido2LuksResult<[u8; 32]> {
|
||||
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
||||
util::read_password_hashed(q, verify)
|
||||
@ -460,11 +483,12 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
||||
let mut retries = *retries;
|
||||
loop {
|
||||
match derive_secret(
|
||||
&authenticator.credential_ids.0,
|
||||
&credentials.ids.0,
|
||||
&salt("Password", false)?,
|
||||
authenticator.await_time,
|
||||
pin,
|
||||
)
|
||||
.and_then(|secret| luks::open_container(&luks.device, &name, &secret, luks.slot_hint))
|
||||
.and_then(|secret| luks::open_container(&luks.device, &name, &secret, luks.slot))
|
||||
{
|
||||
Err(e) => {
|
||||
match e {
|
||||
|
@ -9,13 +9,21 @@ use std::time::Duration;
|
||||
|
||||
const RP_ID: &str = "fido2luks";
|
||||
|
||||
pub fn make_credential_id(name: Option<&str>) -> Fido2LuksResult<FidoCredential> {
|
||||
pub fn make_credential_id(
|
||||
name: Option<&str>,
|
||||
pin: Option<&str>,
|
||||
) -> Fido2LuksResult<FidoCredential> {
|
||||
let mut request = FidoCredentialRequestBuilder::default().rp_id(RP_ID);
|
||||
if let Some(user_name) = name {
|
||||
request = request.user_name(user_name);
|
||||
}
|
||||
let request = request.build().unwrap();
|
||||
let make_credential = |device: &mut FidoDevice| device.make_hmac_credential(&request);
|
||||
let make_credential = |device: &mut FidoDevice| {
|
||||
if let Some(pin) = pin {
|
||||
device.unlock(pin)?;
|
||||
}
|
||||
device.make_hmac_credential(&request)
|
||||
};
|
||||
Ok(request_multiple_devices(
|
||||
get_devices()?
|
||||
.iter_mut()
|
||||
@ -28,6 +36,7 @@ pub fn perform_challenge(
|
||||
credentials: &[&FidoCredential],
|
||||
salt: &[u8; 32],
|
||||
timeout: Duration,
|
||||
pin: Option<&str>,
|
||||
) -> Fido2LuksResult<[u8; 32]> {
|
||||
let request = FidoAssertionRequestBuilder::default()
|
||||
.rp_id(RP_ID)
|
||||
@ -35,6 +44,9 @@ pub fn perform_challenge(
|
||||
.build()
|
||||
.unwrap();
|
||||
let get_assertion = |device: &mut FidoDevice| {
|
||||
if let Some(pin) = pin {
|
||||
device.unlock(pin)?;
|
||||
}
|
||||
device.get_hmac_assertion(&request, &util::sha256(&[&salt[..]]), None)
|
||||
};
|
||||
let (_, (secret, _)) = request_multiple_devices(
|
||||
|
Loading…
x
Reference in New Issue
Block a user