2
This commit is contained in:
parent
bd29452980
commit
8954de3558
@ -8,13 +8,13 @@ steps:
|
|||||||
- rustup component add rustfmt
|
- rustup component add rustfmt
|
||||||
- cargo fmt --all -- --check
|
- cargo fmt --all -- --check
|
||||||
- name: test
|
- name: test
|
||||||
image: shimun/fido2luks@sha256:c1c9b0fb36f24555bc575dd6f643efd3a83f9db25a7ed88af3e22eacf44a89b8
|
image: shimun/fido2luks@sha256:6d0b4017bffbec5fac8f25d383d68671fcc9930efb02e97ce5ea81acf0060ece
|
||||||
environment:
|
environment:
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
commands:
|
commands:
|
||||||
- cargo test --locked
|
- cargo test --locked
|
||||||
- name: publish
|
- name: publish
|
||||||
image: shimun/fido2luks@sha256:c1c9b0fb36f24555bc575dd6f643efd3a83f9db25a7ed88af3e22eacf44a89b8
|
image: shimun/fido2luks@sha256:6d0b4017bffbec5fac8f25d383d68671fcc9930efb02e97ce5ea81acf0060ece
|
||||||
environment:
|
environment:
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
CARGO_REGISTRY_TOKEN:
|
CARGO_REGISTRY_TOKEN:
|
||||||
|
269
src/cli.rs
269
src/cli.rs
@ -20,17 +20,13 @@ use std::fs::File;
|
|||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
pub use cli_args::Args;
|
pub use cli_args::Args;
|
||||||
|
use failure::ResultExt;
|
||||||
|
use std::collections::hash_map::RandomState;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn read_pin(ap: &AuthenticatorParameters) -> Fido2LuksResult<String> {
|
fn read_pin() -> Fido2LuksResult<String> {
|
||||||
if let Some(src) = ap.pin_source.as_ref() {
|
util::read_password("Authenticator PIN", false)
|
||||||
let mut pin = String::new();
|
|
||||||
File::open(src)?.read_to_string(&mut pin)?;
|
|
||||||
Ok(pin.trim_end_matches("\n").to_string()) //remove trailing newline
|
|
||||||
} else {
|
|
||||||
util::read_password("Authenticator PIN", false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_secret(
|
fn derive_secret(
|
||||||
@ -74,10 +70,15 @@ pub fn extend_creds_device(
|
|||||||
creds: &[HexEncoded],
|
creds: &[HexEncoded],
|
||||||
luks_dev: &mut LuksDevice,
|
luks_dev: &mut LuksDevice,
|
||||||
) -> Fido2LuksResult<Vec<HexEncoded>> {
|
) -> Fido2LuksResult<Vec<HexEncoded>> {
|
||||||
let mut additional = HashSet::from_iter(creds.iter().cloned());
|
let mut additional = HashSet::new();
|
||||||
|
additional.extend(creds.iter().cloned());
|
||||||
for token in luks_dev.tokens()? {
|
for token in luks_dev.tokens()? {
|
||||||
for cred in token?.1.credential {
|
for cred in token?.1.credential {
|
||||||
let parsed = HexEncoded::from_str(cred.as_str())?;
|
let parsed = HexEncoded::from_str(cred.as_str()).map_err(|_e| {
|
||||||
|
Fido2LuksError::HexEncodingError {
|
||||||
|
string: cred.clone(),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
additional.insert(parsed);
|
additional.insert(parsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,7 +108,6 @@ pub fn parse_cmdline() -> Args {
|
|||||||
pub fn run_cli() -> Fido2LuksResult<()> {
|
pub fn run_cli() -> Fido2LuksResult<()> {
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
let args = parse_cmdline();
|
let args = parse_cmdline();
|
||||||
let interactive = args.interactive;
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
Command::Credential {
|
Command::Credential {
|
||||||
authenticator,
|
authenticator,
|
||||||
@ -115,7 +115,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
} => {
|
} => {
|
||||||
let pin_string;
|
let pin_string;
|
||||||
let pin = if authenticator.pin {
|
let pin = if authenticator.pin {
|
||||||
pin_string = read_pin(authenticator)?;
|
pin_string = read_pin()?;
|
||||||
Some(pin_string.as_ref())
|
Some(pin_string.as_ref())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -131,13 +131,6 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
secret,
|
secret,
|
||||||
device,
|
device,
|
||||||
} => {
|
} => {
|
||||||
let pin_string;
|
|
||||||
let pin = if authenticator.pin {
|
|
||||||
pin_string = read_pin(authenticator)?;
|
|
||||||
Some(pin_string.as_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let (pin, salt) = match (
|
let (pin, salt) = match (
|
||||||
&secret.password_helper,
|
&secret.password_helper,
|
||||||
authenticator.pin,
|
authenticator.pin,
|
||||||
@ -147,15 +140,12 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
(Some(phelper), true, true, false) => {
|
(Some(phelper), true, true, false) => {
|
||||||
read_password_pin_prefixed(|| phelper.obtain())?
|
read_password_pin_prefixed(|| phelper.obtain())?
|
||||||
}
|
}
|
||||||
(Some(phelper), false, false, false) => (None, secret.salt.obtain_sha256(&phelper)),
|
(Some(phelper), false, false, false) => {
|
||||||
|
(None, secret.salt.obtain_sha256(&phelper)?)
|
||||||
|
}
|
||||||
|
|
||||||
(phelper, pin, _, true) => (
|
(phelper, pin, _, _) => (
|
||||||
if pin {
|
if pin { Some(read_pin()?) } else { None },
|
||||||
pin_string = read_pin(authenticator)?;
|
|
||||||
Some(pin_string.as_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
match phelper {
|
match phelper {
|
||||||
None | Some(PasswordHelper::Stdin) => {
|
None | Some(PasswordHelper::Stdin) => {
|
||||||
util::read_password_hashed("Password", false)
|
util::read_password_hashed("Password", false)
|
||||||
@ -165,9 +155,17 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
let credentials = if let Some(dev) = device {
|
let credentials = if let Some(dev) = device {
|
||||||
extend_creds_device(credentials.ids.0.as_slice(), &mut LuksDevice::load(dev)?)?
|
extend_creds_device(
|
||||||
|
credentials
|
||||||
|
.ids
|
||||||
|
.clone()
|
||||||
|
.map(|cs| cs.0)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_slice(),
|
||||||
|
&mut LuksDevice::load(dev)?,
|
||||||
|
)?
|
||||||
} else {
|
} else {
|
||||||
credentials.ids.0
|
credentials.ids.clone().map(|cs| cs.0).unwrap_or_default()
|
||||||
};
|
};
|
||||||
let (secret, _cred) = derive_secret(
|
let (secret, _cred) = derive_secret(
|
||||||
credentials.as_slice(),
|
credentials.as_slice(),
|
||||||
@ -189,7 +187,6 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
secret,
|
secret,
|
||||||
luks_mod,
|
luks_mod,
|
||||||
existing_secret: other_secret,
|
existing_secret: other_secret,
|
||||||
auto_credential,
|
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| Command::ReplaceKey {
|
| Command::ReplaceKey {
|
||||||
@ -199,21 +196,54 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
secret,
|
secret,
|
||||||
luks_mod,
|
luks_mod,
|
||||||
replacement: other_secret,
|
replacement: other_secret,
|
||||||
remove_cred,
|
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let pin = if authenticator.pin {
|
let mut luks_dev = LuksDevice::load(&luks.device)?;
|
||||||
Some(read_pin(authenticator)?)
|
|
||||||
|
let luks2 = luks_dev.is_luks2()?;
|
||||||
|
|
||||||
|
let credentials = if !luks.disable_token && luks2 {
|
||||||
|
extend_creds_device(
|
||||||
|
credentials
|
||||||
|
.ids
|
||||||
|
.clone()
|
||||||
|
.map(|cs| cs.0)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_slice(),
|
||||||
|
&mut luks_dev,
|
||||||
|
)?
|
||||||
} else {
|
} else {
|
||||||
None
|
credentials.ids.clone().map(|cs| cs.0).unwrap_or_default()
|
||||||
};
|
};
|
||||||
let salt = |q: &str, verify: bool| -> Fido2LuksResult<[u8; 32]> {
|
|
||||||
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
let inputs = |q: &str, verify: bool| -> Fido2LuksResult<(Option<String>, [u8; 32])> {
|
||||||
util::read_password_hashed(q, verify)
|
Ok(
|
||||||
} else {
|
match (
|
||||||
secret.salt.obtain_sha256(&secret.password_helper)
|
&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),
|
||||||
|
}?,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let other_secret = |salt_q: &str,
|
let other_secret = |salt_q: &str,
|
||||||
verify: bool|
|
verify: bool|
|
||||||
-> Fido2LuksResult<(Vec<u8>, Option<FidoCredential>)> {
|
-> Fido2LuksResult<(Vec<u8>, Option<FidoCredential>)> {
|
||||||
@ -224,38 +254,53 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
} => Ok((util::read_keyfile(file)?, None)),
|
} => Ok((util::read_keyfile(file)?, None)),
|
||||||
OtherSecret {
|
OtherSecret {
|
||||||
fido_device: true, ..
|
fido_device: true, ..
|
||||||
} => Ok(derive_secret(
|
} => {
|
||||||
&credentials.ids.0,
|
let (pin, salt) = inputs(salt_q, verify)?;
|
||||||
&salt(salt_q, verify)?,
|
Ok(derive_secret(
|
||||||
authenticator.await_time,
|
&credentials,
|
||||||
pin.as_deref(),
|
&salt,
|
||||||
)
|
authenticator.await_time,
|
||||||
.map(|(secret, cred)| (secret[..].to_vec(), Some(cred)))?),
|
pin.as_deref(),
|
||||||
|
)
|
||||||
|
.map(|(secret, cred)| (secret[..].to_vec(), Some(cred)))?)
|
||||||
|
}
|
||||||
_ => Ok((
|
_ => Ok((
|
||||||
util::read_password(salt_q, verify)?.as_bytes().to_vec(),
|
util::read_password(salt_q, verify)?.as_bytes().to_vec(),
|
||||||
None,
|
None,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let secret = |verify: bool| -> Fido2LuksResult<([u8; 32], FidoCredential)> {
|
let secret = |verify: bool,
|
||||||
derive_secret(
|
credentials: &[HexEncoded]|
|
||||||
&credentials.ids.0,
|
-> Fido2LuksResult<([u8; 32], FidoCredential)> {
|
||||||
&salt("Password", verify)?,
|
let (pin, salt) = inputs("Password", verify)?;
|
||||||
authenticator.await_time,
|
derive_secret(credentials, &salt, authenticator.await_time, pin.as_deref())
|
||||||
pin.as_deref(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let mut luks_dev = LuksDevice::load(&luks.device)?;
|
|
||||||
// Non overlap
|
// Non overlap
|
||||||
match &args.command {
|
match &args.command {
|
||||||
Command::AddKey { exclusive, .. } => {
|
Command::AddKey {
|
||||||
|
exclusive,
|
||||||
|
auto_credential,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let (existing_secret, _) = other_secret("Current password", false)?;
|
let (existing_secret, _) = other_secret("Current password", false)?;
|
||||||
let (new_secret, cred) = secret(true)?;
|
let (new_secret, cred) = if *auto_credential && luks2 {
|
||||||
|
let cred = make_credential_id(
|
||||||
|
Some(luks.device.display().to_string().as_str()),
|
||||||
|
None,
|
||||||
|
)?; //TODO: do ask for PIN
|
||||||
|
let creds = vec![HexEncoded(cred.id)];
|
||||||
|
secret(true, &creds)
|
||||||
|
} else {
|
||||||
|
secret(true, &credentials)
|
||||||
|
}?;
|
||||||
let added_slot = luks_dev.add_key(
|
let added_slot = luks_dev.add_key(
|
||||||
&new_secret,
|
&new_secret,
|
||||||
&existing_secret[..],
|
&existing_secret[..],
|
||||||
luks_mod.kdf_time.or(Some(10)),
|
luks_mod.kdf_time.or(Some(10)),
|
||||||
Some(&cred.id[..]).filter(|_| *token),
|
Some(&cred.id[..])
|
||||||
|
.filter(|_| !luks.disable_token || *auto_credential)
|
||||||
|
.filter(|_| luks2),
|
||||||
)?;
|
)?;
|
||||||
if *exclusive {
|
if *exclusive {
|
||||||
let destroyed = luks_dev.remove_keyslots(&[added_slot])?;
|
let destroyed = luks_dev.remove_keyslots(&[added_slot])?;
|
||||||
@ -274,23 +319,37 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Command::ReplaceKey { add_password, .. } => {
|
Command::ReplaceKey {
|
||||||
let (existing_secret, _) = secret(false)?;
|
add_password,
|
||||||
|
remove_cred,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let (existing_secret, _prev_cred) = secret(false, &credentials)?;
|
||||||
let (replacement_secret, cred) = other_secret("Replacement password", true)?;
|
let (replacement_secret, cred) = other_secret("Replacement password", true)?;
|
||||||
let slot = if *add_password {
|
let slot = if *add_password {
|
||||||
luks_dev.add_key(
|
luks_dev.add_key(
|
||||||
&replacement_secret[..],
|
&replacement_secret[..],
|
||||||
&existing_secret,
|
&existing_secret,
|
||||||
luks_mod.kdf_time,
|
luks_mod.kdf_time,
|
||||||
cred.as_ref().filter(|_| *token).map(|cred| &cred.id[..]),
|
cred.as_ref()
|
||||||
|
.filter(|_| !luks.disable_token)
|
||||||
|
.filter(|_| luks2)
|
||||||
|
.map(|cred| &cred.id[..]),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
luks_dev.replace_key(
|
let slot = luks_dev.replace_key(
|
||||||
&replacement_secret[..],
|
&replacement_secret[..],
|
||||||
&existing_secret,
|
&existing_secret,
|
||||||
luks_mod.kdf_time,
|
luks_mod.kdf_time,
|
||||||
cred.as_ref().filter(|_| *token).map(|cred| &cred.id[..]),
|
cred.as_ref()
|
||||||
)
|
.filter(|_| !luks.disable_token)
|
||||||
|
.filter(|_| luks2)
|
||||||
|
.map(|cred| &cred.id[..]),
|
||||||
|
)?;
|
||||||
|
if *remove_cred && cred.is_none() {
|
||||||
|
luks_dev.remove_token_slot(slot)?;
|
||||||
|
}
|
||||||
|
Ok(slot)
|
||||||
}?;
|
}?;
|
||||||
println!(
|
println!(
|
||||||
"Added to password to device {}, slot: {}",
|
"Added to password to device {}, slot: {}",
|
||||||
@ -307,48 +366,56 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
authenticator,
|
authenticator,
|
||||||
secret,
|
secret,
|
||||||
name,
|
name,
|
||||||
retries,
|
credentials,
|
||||||
..
|
|
||||||
}
|
|
||||||
| Command::OpenToken {
|
|
||||||
luks,
|
|
||||||
authenticator,
|
|
||||||
secret,
|
|
||||||
name,
|
|
||||||
retries,
|
retries,
|
||||||
} => {
|
} => {
|
||||||
let pin_string;
|
let inputs = |q: &str, verify: bool| -> Fido2LuksResult<(Option<String>, [u8; 32])> {
|
||||||
let pin = if authenticator.pin {
|
Ok(
|
||||||
pin_string = read_pin(authenticator)?;
|
match (
|
||||||
Some(pin_string.as_ref())
|
&secret.password_helper,
|
||||||
} else {
|
authenticator.pin,
|
||||||
None
|
authenticator.pin_prefixed,
|
||||||
};
|
args.interactive,
|
||||||
let salt = |q: &str, verify: bool| -> Fido2LuksResult<[u8; 32]> {
|
) {
|
||||||
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
(Some(phelper), true, true, false) => {
|
||||||
util::read_password_hashed(q, verify)
|
read_password_pin_prefixed(|| phelper.obtain())?
|
||||||
} else {
|
}
|
||||||
secret.salt.obtain_sha256(&secret.password_helper)
|
(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),
|
||||||
|
}?,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cow shouldn't be necessary
|
// Cow shouldn't be necessary
|
||||||
let secret = |credentials: Cow<'_, Vec<HexEncoded>>| {
|
let secret = |credentials: Cow<'_, Vec<HexEncoded>>| {
|
||||||
|
let (pin, salt) = inputs("Password", false)?;
|
||||||
derive_secret(
|
derive_secret(
|
||||||
credentials.as_ref(),
|
credentials.as_ref(),
|
||||||
&salt("Password", false)?,
|
&salt,
|
||||||
authenticator.await_time,
|
authenticator.await_time,
|
||||||
pin,
|
pin.as_deref(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut retries = *retries;
|
let mut retries = *retries;
|
||||||
let mut luks_dev = LuksDevice::load(&luks.device)?;
|
let mut luks_dev = LuksDevice::load(&luks.device)?;
|
||||||
loop {
|
loop {
|
||||||
let secret = match &args.command {
|
let slot = if let Some(ref credentials) = credentials.ids {
|
||||||
Command::Open { credentials, .. } => secret(Cow::Borrowed(&credentials.ids.0))
|
secret(Cow::Borrowed(&credentials.0))
|
||||||
.and_then(|(secret, _cred)| luks_dev.activate(&name, &secret, luks.slot)),
|
.and_then(|(secret, _cred)| luks_dev.activate(&name, &secret, luks.slot))
|
||||||
Command::OpenToken { .. } => luks_dev.activate_token(
|
} else if luks_dev.is_luks2()? {
|
||||||
|
luks_dev.activate_token(
|
||||||
&name,
|
&name,
|
||||||
Box::new(|credentials: Vec<String>| {
|
Box::new(|credentials: Vec<String>| {
|
||||||
let creds = credentials
|
let creds = credentials
|
||||||
@ -359,10 +426,11 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
.map(|(secret, cred)| (secret, hex::encode(&cred.id)))
|
.map(|(secret, cred)| (secret, hex::encode(&cred.id)))
|
||||||
}),
|
}),
|
||||||
luks.slot,
|
luks.slot,
|
||||||
),
|
)
|
||||||
_ => unreachable!(),
|
} else {
|
||||||
|
return Err(Fido2LuksError::WrongSecret); // creds or luks2
|
||||||
};
|
};
|
||||||
match secret {
|
match slot {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
match e {
|
match e {
|
||||||
Fido2LuksError::WrongSecret if retries > 0 => {}
|
Fido2LuksError::WrongSecret if retries > 0 => {}
|
||||||
@ -444,7 +512,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let count = if tokens.is_empty() {
|
let count = if tokens.is_empty() {
|
||||||
dev.add_token(&Fido2LuksToken::with_credentials(&credentials.ids.0, *slot))?;
|
dev.add_token(&Fido2LuksToken::with_credentials(&credentials.0, *slot))?;
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
tokens.len()
|
tokens.len()
|
||||||
@ -452,7 +520,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
for (id, mut token) in tokens {
|
for (id, mut token) in tokens {
|
||||||
token
|
token
|
||||||
.credential
|
.credential
|
||||||
.extend(credentials.ids.0.iter().map(|h| h.to_string()));
|
.extend(credentials.0.iter().map(|h| h.to_string()));
|
||||||
dev.update_token(id, &token)?;
|
dev.update_token(id, &token)?;
|
||||||
}
|
}
|
||||||
println!("Updated {} tokens", count);
|
println!("Updated {} tokens", count);
|
||||||
@ -480,7 +548,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
token.credential = token
|
token.credential = token
|
||||||
.credential
|
.credential
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|cred| !credentials.ids.0.iter().any(|h| &h.to_string() == cred))
|
.filter(|cred| !credentials.0.iter().any(|h| &h.to_string() == cred))
|
||||||
.collect();
|
.collect();
|
||||||
dev.update_token(id, &token)?;
|
dev.update_token(id, &token)?;
|
||||||
}
|
}
|
||||||
@ -512,11 +580,8 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
Command::GenerateCompletions { shell, out_dir } => {
|
Command::GenerateCompletions { shell, out_dir } => {
|
||||||
Args::clap().gen_completions(
|
Args::clap().gen_completions(
|
||||||
env!("CARGO_PKG_NAME"),
|
env!("CARGO_PKG_NAME"),
|
||||||
match shell.as_ref() {
|
Shell::from_str(shell.as_str())
|
||||||
"bash" => Shell::Bash,
|
.expect("structopt shouldn't allow us to reach this point"),
|
||||||
"fish" => Shell::Fish,
|
|
||||||
_ => unreachable!("structopt shouldn't allow us to reach this point"),
|
|
||||||
},
|
|
||||||
&out_dir,
|
&out_dir,
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::fmt::{Display, Error, Formatter};
|
use std::fmt::{Display, Error, Formatter};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use structopt::clap::AppSettings;
|
use structopt::clap::AppSettings;
|
||||||
@ -31,6 +32,12 @@ impl FromStr for HexEncoded {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Hash for HexEncoded {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub struct CommaSeparated<T: FromStr + Display>(pub Vec<T>);
|
pub struct CommaSeparated<T: FromStr + Display>(pub Vec<T>);
|
||||||
|
|
||||||
@ -128,8 +135,7 @@ pub struct SecretParameters {
|
|||||||
#[structopt(
|
#[structopt(
|
||||||
name = "password-helper",
|
name = "password-helper",
|
||||||
env = "FIDO2LUKS_PASSWORD_HELPER",
|
env = "FIDO2LUKS_PASSWORD_HELPER",
|
||||||
long = "password-helper",
|
long = "password-helper"
|
||||||
default_value = "/usr/bin/env systemd-ask-password 'Please enter second factor for LUKS disk encryption!'"
|
|
||||||
)]
|
)]
|
||||||
pub password_helper: Option<PasswordHelper>,
|
pub password_helper: Option<PasswordHelper>,
|
||||||
}
|
}
|
||||||
@ -269,8 +275,14 @@ pub enum TokenCommand {
|
|||||||
Add {
|
Add {
|
||||||
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
||||||
device: PathBuf,
|
device: PathBuf,
|
||||||
#[structopt(flatten)]
|
/// FIDO credential ids, separated by ',' generate using fido2luks credential
|
||||||
credentials: Credentials,
|
#[structopt(
|
||||||
|
name = "credential-ids",
|
||||||
|
env = "FIDO2LUKS_CREDENTIAL_ID",
|
||||||
|
short = "c",
|
||||||
|
long = "creds"
|
||||||
|
)]
|
||||||
|
credentials: CommaSeparated<HexEncoded>,
|
||||||
/// Slot to which the credentials will be added
|
/// Slot to which the credentials will be added
|
||||||
#[structopt(long = "slot", env = "FIDO2LUKS_DEVICE_SLOT")]
|
#[structopt(long = "slot", env = "FIDO2LUKS_DEVICE_SLOT")]
|
||||||
slot: u32,
|
slot: u32,
|
||||||
@ -279,8 +291,14 @@ pub enum TokenCommand {
|
|||||||
Remove {
|
Remove {
|
||||||
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
||||||
device: PathBuf,
|
device: PathBuf,
|
||||||
#[structopt(flatten)]
|
/// FIDO credential ids, separated by ',' generate using fido2luks credential
|
||||||
credentials: Credentials,
|
#[structopt(
|
||||||
|
name = "credential-ids",
|
||||||
|
env = "FIDO2LUKS_CREDENTIAL_ID",
|
||||||
|
short = "c",
|
||||||
|
long = "creds"
|
||||||
|
)]
|
||||||
|
credentials: CommaSeparated<HexEncoded>,
|
||||||
/// Token from which the credentials will be removed
|
/// Token from which the credentials will be removed
|
||||||
#[structopt(long = "token")]
|
#[structopt(long = "token")]
|
||||||
token_id: Option<u32>,
|
token_id: Option<u32>,
|
||||||
|
@ -29,6 +29,8 @@ pub enum Fido2LuksError {
|
|||||||
WrongSecret,
|
WrongSecret,
|
||||||
#[fail(display = "not an utf8 string")]
|
#[fail(display = "not an utf8 string")]
|
||||||
StringEncodingError { cause: FromUtf8Error },
|
StringEncodingError { cause: FromUtf8Error },
|
||||||
|
#[fail(display = "not an hex string: {}", string)]
|
||||||
|
HexEncodingError { string: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fido2LuksError {
|
impl Fido2LuksError {
|
||||||
|
22
src/luks.rs
22
src/luks.rs
@ -103,6 +103,10 @@ impl LuksDevice {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_credential(&mut self, slot: u32, credential: Vec<u8>) -> Fido2LuksResult<()> {
|
||||||
|
self.add_token(&Fido2LuksToken::with_credentials(&[credential], slot))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_token(&mut self, token: u32) -> Fido2LuksResult<()> {
|
pub fn remove_token(&mut self, token: u32) -> Fido2LuksResult<()> {
|
||||||
self.require_luks2()?;
|
self.require_luks2()?;
|
||||||
self.device
|
self.device
|
||||||
@ -111,6 +115,20 @@ impl LuksDevice {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_token_slot(&mut self, slot: u32) -> Fido2LuksResult<()> {
|
||||||
|
let mut remove = HashSet::new();
|
||||||
|
for token in self.tokens()? {
|
||||||
|
let (id, token) = token?;
|
||||||
|
if token.keyslots.contains(&slot.to_string()) {
|
||||||
|
remove.insert(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for rm in remove {
|
||||||
|
self.remove_token(rm)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_token(&mut self, token: u32, data: &Fido2LuksToken) -> Fido2LuksResult<()> {
|
pub fn update_token(&mut self, token: u32, data: &Fido2LuksToken) -> Fido2LuksResult<()> {
|
||||||
self.require_luks2()?;
|
self.require_luks2()?;
|
||||||
self.device
|
self.device
|
||||||
@ -192,7 +210,9 @@ impl LuksDevice {
|
|||||||
old_secret,
|
old_secret,
|
||||||
CryptActivateFlags::empty(),
|
CryptActivateFlags::empty(),
|
||||||
)?;
|
)?;
|
||||||
self.device.keyslot_handle().change_by_passphrase(
|
|
||||||
|
// slot should stay the same but better be safe than sorry
|
||||||
|
let slot = self.device.keyslot_handle().change_by_passphrase(
|
||||||
Some(slot),
|
Some(slot),
|
||||||
Some(slot),
|
Some(slot),
|
||||||
old_secret,
|
old_secret,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user