1use std::borrow::Borrow;
11use std::collections::HashMap;
12use std::str::FromStr;
13
14use sequoia_openpgp as openpgp;
15use openpgp::packet::key;
16use openpgp::packet::Key;
17
18use sequoia_ipc as ipc;
19use ipc::Keygrip;
20
21use anyhow::Context;
22
23use crate::Result;
24
25#[derive(thiserror::Error, Debug)]
27#[non_exhaustive]
28pub enum Error {
29 #[error("Error parsing keyinfo data: {0}")]
30 ParseError(String),
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35#[non_exhaustive]
36pub enum KeyType {
37 Regular,
39 Smartcard,
41 Unknown,
43 Missing,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49#[non_exhaustive]
50pub enum KeyProtection {
51 Protected,
53 NotProtected,
55 UnknownProtection,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
61#[non_exhaustive]
62enum KeyFlag {
63 Disabled,
65 SSHControl,
67 ConfirmationRequired,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct KeyInfo {
76 keygrip: Keygrip,
77
78 keytype: KeyType,
85
86 serialno: Option<String>,
90
91 idstr: Option<String>,
97
98 passphrase_cached: bool,
100
101 protection: KeyProtection,
107
108 ttl: Option<u64>,
110
111 flags: Vec<KeyFlag>,
113}
114
115impl KeyInfo {
116 pub fn keygrip(&self) -> &ipc::Keygrip {
118 &self.keygrip
119 }
120
121 pub fn keytype(&self) -> KeyType {
123 self.keytype.clone()
124 }
125
126 pub fn serialno(&self) -> Option<&str> {
130 self.serialno.as_deref()
131 }
132
133 pub fn idstr(&self) -> Option<&str> {
138 self.idstr.as_deref()
139 }
140
141 pub fn passphrase_cached(&self) -> bool {
144 self.passphrase_cached
145 }
146
147 pub fn protection(&self) -> &KeyProtection {
149 &self.protection
150 }
151
152 pub fn ttl(&self) -> Option<u64> {
156 self.ttl
157 }
158
159 pub fn key_disabled(&self) -> bool {
161 self.flags.contains(&KeyFlag::Disabled)
162 }
163
164 pub fn in_ssh_control(&self) -> bool {
166 self.flags.contains(&KeyFlag::SSHControl)
167 }
168
169 pub fn confirmation_required(&self) -> bool {
171 self.flags.contains(&KeyFlag::ConfirmationRequired)
172 }
173
174 pub(crate) fn parse(keyinfo: &str) -> Result<Self> {
185 let mut iter = keyinfo.split(' ');
186
187 let Some(keygrip) = iter.next() else {
188 return Err(Error::ParseError("KEYGRIP field missing".into()).into());
189 };
190 let keygrip = Keygrip::from_str(keygrip)
191 .with_context(|| {
192 format!("Parsing {:?} as a keygrip", keygrip)
193 })?;
194
195 let Some(keytype) = iter.next() else {
201 return Err(Error::ParseError("TYPE field missing".into()).into());
202 };
203 let keytype = match keytype {
204 "D" => KeyType::Regular,
205 "T" => KeyType::Smartcard,
206 "-" => KeyType::Missing,
207 "X"|_ => KeyType::Unknown,
208 };
209
210 let Some(serialno) = iter.next() else {
214 return Err(Error::ParseError("SERIALNO field missing".into()).into());
215 };
216 let serialno = if serialno == "-" {
217 None
218 } else {
219 Some(serialno.to_string())
220 };
221
222 let Some(idstr) = iter.next() else {
225 return Err(Error::ParseError("IDSTR field missing".into()).into());
226 };
227 let idstr = if idstr == "-" {
228 None
229 } else {
230 Some(idstr.to_string())
231 };
232
233 let Some(cached) = iter.next() else {
236 return Err(Error::ParseError("CACHED field missing".into()).into());
237 };
238 let passphrase_cached = match cached {
239 "1" => true,
240 _ => false,
241 };
242
243 let Some(protection) = iter.next() else {
248 return Err(Error::ParseError("PROTECTION field missing".into()).into());
249 };
250 let protection = match protection {
251 "P" => KeyProtection::Protected,
252 "C" => KeyProtection::NotProtected,
253 _ => KeyProtection::UnknownProtection,
254 };
255
256 let Some(_ssh_fpr) = iter.next() else {
261 return Err(Error::ParseError("FPR field missing".into()).into());
262 };
263
264 let Some(ttl) = iter.next() else {
266 return Err(Error::ParseError("TTL field missing".into()).into());
267 };
268 let ttl = if ttl == "-" {
269 None
270 } else {
271 Some(u64::from_str_radix(ttl, 10).with_context(|| {
272 format!("Parsing {:?} as a u64", ttl)
273 })?)
274 };
275
276 let Some(flags) = iter.next() else {
282 return Err(Error::ParseError("FLAGS field missing".into()).into());
283 };
284 let flags = flags.chars()
285 .filter_map(|c| {
286 match c {
287 'D' => Some(KeyFlag::Disabled),
288 'S' => Some(KeyFlag::SSHControl),
289 'c' => Some(KeyFlag::ConfirmationRequired),
290 _ => None,
291 }
292 })
293 .collect::<Vec<KeyFlag>>();
294
295 Ok(KeyInfo {
296 keygrip,
297 keytype,
298 serialno,
299 idstr,
300 passphrase_cached,
301 protection,
302 ttl,
303 flags,
304 })
305 }
306}
307
308pub struct KeyInfoList {
310 keys: HashMap<Keygrip, KeyInfo>,
311}
312
313impl FromIterator<KeyInfo> for KeyInfoList {
314 fn from_iter<I>(iter: I) -> Self
315 where I: IntoIterator<Item=KeyInfo>
316 {
317 KeyInfoList {
318 keys: HashMap::from_iter(
319 iter.into_iter().map(|i| (i.keygrip().clone(), i))),
320 }
321 }
322}
323
324impl Default for KeyInfoList {
325 fn default() -> Self {
326 Self {
327 keys: Default::default(),
328 }
329 }
330}
331
332impl KeyInfoList {
333 pub fn empty() -> Self {
335 Self::default()
336 }
337
338 pub fn lookup<K>(&self, keygrip: K) -> Option<&KeyInfo>
340 where K: Borrow<Keygrip>
341 {
342 let keygrip = keygrip.borrow();
343 self.keys.get(keygrip)
344 }
345
346 pub fn lookup_by_key<K, R, P>(&self, key: K) -> Option<&KeyInfo>
348 where K: Borrow<Key<P, R>>,
349 P: key:: KeyParts,
350 R: key:: KeyRole,
351 {
352 let key = key.borrow();
353 let keygrip = Keygrip::of(key.mpis()).ok()?;
359 self.lookup(keygrip)
360 }
361
362 pub fn iter(&self) -> KeyInfoListIter {
364 KeyInfoListIter {
365 iter: self.keys.values(),
366 }
367 }
368
369 pub fn len(&self) -> usize {
371 self.keys.len()
372 }
373}
374
375pub struct KeyInfoListIter<'a> {
377 iter: std::collections::hash_map::Values<'a, Keygrip, KeyInfo>,
378}
379
380impl<'a> IntoIterator for &'a KeyInfoList {
381 type Item = &'a KeyInfo;
382 type IntoIter = KeyInfoListIter<'a>;
383
384 fn into_iter(self) -> Self::IntoIter {
385 KeyInfoListIter {
386 iter: self.keys.values()
387 }
388 }
389}
390
391impl<'a> Iterator for KeyInfoListIter<'a> {
392 type Item = &'a KeyInfo;
393
394 fn next(&mut self) -> Option<Self::Item> {
395 self.iter.next()
396 }
397}
398
399#[cfg(test)]
400mod test {
401 use super::*;
402
403 #[test]
404 fn parse_keyinfo_list() -> Result<()> {
405 let entry = "EF8CE31AE9E310D660C7C9709A028442A8B52112 D - - - C - - -";
406 eprintln!("{}", entry);
407 assert_eq!(
408 KeyInfo::parse(entry).unwrap(),
409 KeyInfo {
410 keygrip: Keygrip::from_str(
411 "EF8CE31AE9E310D660C7C9709A028442A8B52112").unwrap(),
412 keytype: KeyType::Regular,
413 serialno: None,
414 idstr: None,
415 passphrase_cached: false,
416 protection: KeyProtection::NotProtected,
417 ttl: None,
418 flags: vec![ ],
419 });
420
421 let entry = "EAFE58E4F5269129D7233A0FA6307C366A3EA2CC D - - - P - - -";
422 eprintln!("{}", entry);
423 assert_eq!(
424 KeyInfo::parse(entry).unwrap(),
425 KeyInfo {
426 keygrip: Keygrip::from_str(
427 "EAFE58E4F5269129D7233A0FA6307C366A3EA2CC").unwrap(),
428 keytype: KeyType::Regular,
429 serialno: None,
430 idstr: None,
431 passphrase_cached: false,
432 protection: KeyProtection::Protected,
433 ttl: None,
434 flags: vec![ ],
435 });
436
437 let entry = "9483454871CC1239D4C2A1416F2742D39A14DB14 T D2760001240103040006181329630000 OPENPGP.3 - - - - -";
438 eprintln!("{}", entry);
439 assert_eq!(
440 KeyInfo::parse(entry).unwrap(),
441 KeyInfo {
442 keygrip: Keygrip::from_str(
443 "9483454871CC1239D4C2A1416F2742D39A14DB14").unwrap(),
444 keytype: KeyType::Smartcard,
445 serialno: Some("D2760001240103040006181329630000".to_string()),
446 idstr: Some("OPENPGP.3".to_string()),
447 passphrase_cached: false,
448 protection: KeyProtection::UnknownProtection,
449 ttl: None,
450 flags: vec![ ],
451 });
452
453 Ok(())
454 }
455}