Skip to content

Commit 92e413d

Browse files
author
shimun
committed
refactored luks operations
1 parent 023399b commit 92e413d

File tree

3 files changed

+484
-232
lines changed

3 files changed

+484
-232
lines changed

src/cli.rs

Lines changed: 190 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::error::*;
2-
use crate::luks;
32
use crate::*;
43

54
use structopt::StructOpt;
@@ -12,8 +11,10 @@ use std::io::Write;
1211
use std::process::exit;
1312
use std::thread;
1413

14+
use crate::luks::{Fido2LuksToken, LuksDevice};
1515
use crate::util::sha256;
1616
use std::borrow::Cow;
17+
use std::collections::HashSet;
1718
use std::time::SystemTime;
1819

1920
#[derive(Debug, Eq, PartialEq, Clone)]
@@ -25,6 +26,12 @@ impl Display for HexEncoded {
2526
}
2627
}
2728

29+
impl AsRef<[u8]> for HexEncoded {
30+
fn as_ref(&self) -> &[u8] {
31+
&self.0[..]
32+
}
33+
}
34+
2835
impl FromStr for HexEncoded {
2936
type Err = hex::FromHexError;
3037

@@ -59,7 +66,7 @@ impl<T: Display + FromStr> FromStr for CommaSeparated<T> {
5966

6067
#[derive(Debug, StructOpt)]
6168
pub struct Credentials {
62-
/// FIDO credential ids, seperated by ',' generate using fido2luks credential
69+
/// FIDO credential ids, separated by ',' generate using fido2luks credential
6370
#[structopt(name = "credential-id", env = "FIDO2LUKS_CREDENTIAL_ID")]
6471
pub ids: CommaSeparated<HexEncoded>,
6572
}
@@ -282,6 +289,45 @@ pub enum Command {
282289
/// Check if an authenticator is connected
283290
#[structopt(name = "connected")]
284291
Connected,
292+
Token(TokenCommand),
293+
}
294+
295+
///LUKS2 token related operations
296+
#[derive(Debug, StructOpt)]
297+
pub enum TokenCommand {
298+
/// List all tokens associated with the specified device
299+
List {
300+
#[structopt(env = "FIDO2LUKS_DEVICE")]
301+
device: PathBuf,
302+
/// Dump all credentials as CSV
303+
#[structopt(long = "csv")]
304+
csv: bool,
305+
},
306+
/// Add credential to a keyslot
307+
Add {
308+
#[structopt(env = "FIDO2LUKS_DEVICE")]
309+
device: PathBuf,
310+
#[structopt(flatten)]
311+
credentials: Credentials,
312+
/// Slot to which the credentials will be added
313+
#[structopt(long = "slot", env = "FIDO2LUKS_DEVICE_SLOT")]
314+
slot: u32,
315+
},
316+
/// Remove credentials from token(s)
317+
Remove {
318+
#[structopt(env = "FIDO2LUKS_DEVICE")]
319+
device: PathBuf,
320+
#[structopt(flatten)]
321+
credentials: Credentials,
322+
/// Token from which the credentials will be removed
323+
#[structopt(long = "token")]
324+
token_id: Option<u32>,
325+
},
326+
/// Remove all unassigned tokens
327+
GC {
328+
#[structopt(env = "FIDO2LUKS_DEVICE")]
329+
device: PathBuf,
330+
},
285331
}
286332

287333
pub fn parse_cmdline() -> Args {
@@ -371,7 +417,9 @@ pub fn run_cli() -> Fido2LuksResult<()> {
371417
secret.salt.obtain(&secret.password_helper)
372418
}
373419
};
374-
let other_secret = |salt_q: &str, verify: bool| -> Fido2LuksResult<(Vec<u8>, Option<FidoCredential>)> {
420+
let other_secret = |salt_q: &str,
421+
verify: bool|
422+
-> Fido2LuksResult<(Vec<u8>, Option<FidoCredential>)> {
375423
match other_secret {
376424
OtherSecret {
377425
keyfile: Some(file),
@@ -386,7 +434,10 @@ pub fn run_cli() -> Fido2LuksResult<()> {
386434
pin.as_deref(),
387435
)
388436
.map(|(secret, cred)| (secret[..].to_vec(), Some(cred)))?),
389-
_ => Ok((util::read_password(salt_q, verify)?.as_bytes().to_vec(), None)),
437+
_ => Ok((
438+
util::read_password(salt_q, verify)?.as_bytes().to_vec(),
439+
None,
440+
)),
390441
}
391442
};
392443
let secret = |verify: bool| -> Fido2LuksResult<([u8; 32], FidoCredential)> {
@@ -397,22 +448,20 @@ pub fn run_cli() -> Fido2LuksResult<()> {
397448
pin.as_deref(),
398449
)
399450
};
451+
let mut luks_dev = LuksDevice::load(&luks.device)?;
400452
// Non overlap
401453
match &args.command {
402-
Command::AddKey {
403-
exclusive, ..
404-
} => {
454+
Command::AddKey { exclusive, .. } => {
405455
let (existing_secret, _) = other_secret("Current password", false)?;
406456
let (new_secret, cred) = secret(true)?;
407-
let added_slot = luks::add_key(
408-
&luks.device,
457+
let added_slot = luks_dev.add_key(
409458
&new_secret,
410459
&existing_secret[..],
411460
luks_mod.kdf_time.or(Some(10)),
412461
Some(&cred.id[..]).filter(|_| *token),
413462
)?;
414463
if *exclusive {
415-
let destroyed = luks::remove_keyslots(&luks.device, &[added_slot])?;
464+
let destroyed = luks_dev.remove_keyslots(&[added_slot])?;
416465
println!(
417466
"Added to key to device {}, slot: {}\nRemoved {} old keys",
418467
luks.device.display(),
@@ -432,16 +481,14 @@ pub fn run_cli() -> Fido2LuksResult<()> {
432481
let (existing_secret, _) = secret(false)?;
433482
let (replacement_secret, cred) = other_secret("Replacement password", true)?;
434483
let slot = if *add_password {
435-
luks::add_key(
436-
&luks.device,
484+
luks_dev.add_key(
437485
&replacement_secret[..],
438486
&existing_secret,
439487
luks_mod.kdf_time,
440488
cred.as_ref().filter(|_| *token).map(|cred| &cred.id[..]),
441489
)
442490
} else {
443-
luks::replace_key(
444-
&luks.device,
491+
luks_dev.replace_key(
445492
&replacement_secret[..],
446493
&existing_secret,
447494
luks_mod.kdf_time,
@@ -499,14 +546,12 @@ pub fn run_cli() -> Fido2LuksResult<()> {
499546
};
500547

501548
let mut retries = *retries;
549+
let mut luks_dev = LuksDevice::load(&luks.device)?;
502550
loop {
503551
let secret = match &args.command {
504552
Command::Open { credentials, .. } => secret(Cow::Borrowed(&credentials.ids.0))
505-
.and_then(|(secret, _cred)| {
506-
luks::open_container(&luks.device, &name, &secret, luks.slot)
507-
}),
508-
Command::OpenToken { .. } => luks::open_container_token(
509-
&luks.device,
553+
.and_then(|(secret, _cred)| luks_dev.activate(&name, &secret, luks.slot)),
554+
Command::OpenToken { .. } => luks_dev.activate_token(
510555
&name,
511556
Box::new(|credentials: Vec<String>| {
512557
let creds = credentials
@@ -516,6 +561,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
516561
secret(Cow::Owned(creds))
517562
.map(|(secret, cred)| (secret, hex::encode(&cred.id)))
518563
}),
564+
luks.slot,
519565
),
520566
_ => unreachable!(),
521567
};
@@ -531,7 +577,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
531577
retries -= 1;
532578
eprintln!("{}", e);
533579
}
534-
res => break res,
580+
res => break res.map(|_| ()),
535581
}
536582
}
537583
}
@@ -542,5 +588,129 @@ pub fn run_cli() -> Fido2LuksResult<()> {
542588
}
543589
_ => exit(1),
544590
},
591+
Command::Token(cmd) => match cmd {
592+
TokenCommand::List {
593+
device,
594+
csv: dump_credentials,
595+
} => {
596+
let mut dev = LuksDevice::load(device)?;
597+
let mut creds = Vec::new();
598+
for token in dev.tokens()? {
599+
let (id, token) = token?;
600+
for cred in token.credential.iter() {
601+
if !creds.contains(cred) {
602+
creds.push(cred.clone());
603+
if *dump_credentials {
604+
print!("{}{}", if creds.len() == 1 { "" } else { "," }, cred);
605+
}
606+
}
607+
}
608+
if *dump_credentials {
609+
continue;
610+
}
611+
println!(
612+
"{}:\n\tSlots: {}\n\tCredentials: {}",
613+
id,
614+
if token.keyslots.is_empty() {
615+
"None".into()
616+
} else {
617+
token.keyslots.iter().cloned().collect::<Vec<_>>().join(",")
618+
},
619+
token
620+
.credential
621+
.iter()
622+
.map(|cred| format!(
623+
"{} ({})",
624+
cred,
625+
creds.iter().position(|c| c == cred).unwrap().to_string()
626+
))
627+
.collect::<Vec<_>>()
628+
.join(",")
629+
);
630+
}
631+
if *dump_credentials {
632+
println!();
633+
}
634+
Ok(())
635+
}
636+
TokenCommand::Add {
637+
device,
638+
credentials,
639+
slot,
640+
} => {
641+
let mut dev = LuksDevice::load(device)?;
642+
let mut tokens = Vec::new();
643+
for token in dev.tokens()? {
644+
let (id, token) = token?;
645+
if token.keyslots.contains(&slot.to_string()) {
646+
tokens.push((id, token));
647+
}
648+
}
649+
let count = if tokens.is_empty() {
650+
dev.add_token(&Fido2LuksToken::with_credentials(&credentials.ids.0, *slot))?;
651+
1
652+
} else {
653+
tokens.len()
654+
};
655+
for (id, mut token) in tokens {
656+
token
657+
.credential
658+
.extend(credentials.ids.0.iter().map(|h| h.to_string()));
659+
dev.update_token(id, &token)?;
660+
}
661+
println!("Updated {} tokens", count);
662+
Ok(())
663+
}
664+
TokenCommand::Remove {
665+
device,
666+
credentials,
667+
token_id,
668+
} => {
669+
let mut dev = LuksDevice::load(device)?;
670+
let mut tokens = Vec::new();
671+
for token in dev.tokens()? {
672+
let (id, token) = token?;
673+
if let Some(token_id) = token_id {
674+
if id == *token_id {
675+
tokens.push((id, token));
676+
}
677+
} else {
678+
tokens.push((id, token));
679+
}
680+
}
681+
let count = tokens.len();
682+
for (id, mut token) in tokens {
683+
token.credential = token
684+
.credential
685+
.into_iter()
686+
.filter(|cred| !credentials.ids.0.iter().any(|h| &h.to_string() == cred))
687+
.collect();
688+
dev.update_token(id, &token)?;
689+
}
690+
println!("Updated {} tokens", count);
691+
Ok(())
692+
}
693+
TokenCommand::GC { device } => {
694+
let mut dev = LuksDevice::load(device)?;
695+
let mut creds: HashSet<String> = HashSet::new();
696+
let mut remove = Vec::new();
697+
for token in dev.tokens()? {
698+
let (id, token) = token?;
699+
if token.keyslots.is_empty() || token.credential.is_empty() {
700+
creds.extend(token.credential);
701+
remove.push(id);
702+
}
703+
}
704+
for id in remove.iter().rev() {
705+
dev.remove_token(*id)?;
706+
}
707+
println!(
708+
"Removed {} tokens, affected credentials: {}",
709+
remove.len(),
710+
creds.into_iter().collect::<Vec<_>>().join(",")
711+
);
712+
Ok(())
713+
}
714+
},
545715
}
546716
}

src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ impl LuksError {
7575
}
7676
}
7777

78+
impl From<LuksError> for Fido2LuksError {
79+
fn from(e: LuksError) -> Self {
80+
Fido2LuksError::LuksError { cause: e }
81+
}
82+
}
83+
7884
use libcryptsetup_rs::LibcryptErr;
7985
use std::io::ErrorKind;
8086
use std::string::FromUtf8Error;

0 commit comments

Comments
 (0)