diff --git a/src/cli.rs b/src/cli.rs index ef6f334..20cd88c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -312,7 +312,7 @@ pub enum TokenCommand { credentials: Credentials, /// Token from which the credentials will be removed #[structopt(long = "token")] - token: Option, + token_id: Option, }, /// Remove all unassigned tokens GC { @@ -599,7 +599,7 @@ pub fn run_cli() -> Fido2LuksResult<()> { if token.keyslots.is_empty() { "None".into() } else { - token.keyslots.join(",") + token.keyslots.iter().cloned().collect::>().join(",") }, token .credential @@ -615,6 +615,67 @@ pub fn run_cli() -> Fido2LuksResult<()> { } Ok(()) } + TokenCommand::Add { + device, + credentials, + slot, + } => { + let mut dev = LuksDevice::load(device)?; + let mut tokens = Vec::new(); + for token in dev.tokens()? { + let (id, token) = token?; + if token.keyslots.contains(&slot.to_string()) { + tokens.push((id, token)); + } + } + if tokens.is_empty() { + unimplemented!("// TODO: create new token") + } + let count = tokens.len(); + for (id, mut token) in tokens { + token + .credential + .extend(credentials.ids.0.iter().map(|h| h.to_string())); + dev.update_token(id, &token)?; + } + println!("Updated {} tokens", count); + Ok(()) + } + TokenCommand::Remove { + device, + credentials, + token_id, + } => { + let mut dev = LuksDevice::load(device)?; + let mut tokens = Vec::new(); + for token in dev.tokens()? { + let (id, token) = token?; + if let Some(token_id) = token_id { + if id == *token_id { + tokens.push((id, token)); + } + } else { + tokens.push((id, token)); + } + } + let count = tokens.len(); + for (id, mut token) in tokens { + token.credential = token + .credential + .into_iter() + .filter(|cred| { + !credentials + .ids + .0 + .iter() + .any(|h| &h.0[..] == cred.as_bytes()) + }) + .collect(); + dev.update_token(id, &token)?; + } + println!("Updated {} tokens", count); + Ok(()) + } TokenCommand::GC { device } => { let mut dev = LuksDevice::load(device)?; let mut creds: HashSet = HashSet::new(); @@ -636,7 +697,6 @@ pub fn run_cli() -> Fido2LuksResult<()> { ); Ok(()) } - _ => unimplemented!(), }, } } diff --git a/src/luks.rs b/src/luks.rs index 387b81b..ce96c48 100644 --- a/src/luks.rs +++ b/src/luks.rs @@ -94,6 +94,16 @@ impl LuksDevice { Ok(()) } + pub fn update_token(&mut self, token: u32, data: &Fido2LuksToken) -> Fido2LuksResult<()> { + self.device + .token_handle() + .json_set(TokenInput::ReplaceToken( + token, + &serde_json::to_value(&data).unwrap(), + ))?; + Ok(()) + } + pub fn add_key( &mut self, secret: &[u8], @@ -259,15 +269,15 @@ impl LuksDevice { pub struct Fido2LuksToken { #[serde(rename = "type")] pub type_: String, - pub credential: Vec, - pub keyslots: Vec, + pub credential: HashSet, + pub keyslots: HashSet, } impl Fido2LuksToken { fn new(credential_id: impl AsRef<[u8]>, slot: u32) -> Self { Self { - credential: vec![hex::encode(credential_id)], - keyslots: vec![slot.to_string()], + credential: vec![hex::encode(credential_id)].into_iter().collect(), + keyslots: vec![slot.to_string()].into_iter().collect(), ..Default::default() } } @@ -280,8 +290,8 @@ impl Default for Fido2LuksToken { fn default() -> Self { Self { type_: Self::default_type().into(), - credential: Vec::new(), - keyslots: Vec::new(), + credential: HashSet::new(), + keyslots: HashSet::new(), } } }