Implement --identity command

This commit is contained in:
Jack Grigg
2021-01-01 21:12:52 +00:00
parent babe64da42
commit 7a527b2be6
7 changed files with 287 additions and 2 deletions
+75 -1
View File
@@ -10,11 +10,13 @@ mod error;
mod p256;
mod plugin;
mod util;
mod yubikey;
use error::Error;
const PLUGIN_NAME: &str = "age-plugin-yubikey";
const RECIPIENT_PREFIX: &str = "age1yubikey";
const IDENTITY_PREFIX: &str = "age-plugin-yubikey-";
const USABLE_SLOTS: [RetiredSlotId; 20] = [
RetiredSlotId::R1,
@@ -62,6 +64,78 @@ struct PluginOptions {
#[options(help = "List all YubiKey keys that are compatible with age.", no_short)]
list_all: bool,
#[options(
help = "Specify which YubiKey to use, if more than one is plugged in.",
no_short
)]
serial: Option<u32>,
#[options(
help = "Specify which slot to use. Defaults to first usable slot.",
no_short
)]
slot: Option<u8>,
}
fn identity(opts: PluginOptions) -> Result<(), Error> {
let serial = opts.serial.map(|s| s.into());
let slot = opts
.slot
.map(|slot| {
USABLE_SLOTS
.get(slot as usize - 1)
.cloned()
.ok_or(Error::InvalidSlot(slot))
})
.transpose()?;
let mut yubikey = yubikey::open(serial)?;
let mut keys = Key::list(&mut yubikey)?.into_iter().filter_map(|key| {
// - We only use the retired slots.
// - Only P-256 keys are compatible with us.
match (key.slot(), key.certificate().subject_pki()) {
(SlotId::Retired(slot), PublicKeyInfo::EcP256(pubkey)) => {
p256::Recipient::from_pubkey(*pubkey).map(|r| (key, slot, r))
}
_ => None,
}
});
let (key, slot, recipient) = if let Some(slot) = slot {
keys.find(|(_, s, _)| s == &slot)
.ok_or(Error::SlotHasNoIdentity(slot))
} else {
let mut keys = keys.filter(|(key, _, _)| {
let cert = x509_parser::parse_x509_certificate(key.certificate().as_ref())
.map(|(_, cert)| cert)
.ok();
match cert
.as_ref()
.and_then(|cert| cert.subject().iter_organization().next())
{
Some(org) => org.as_str() == Ok(PLUGIN_NAME),
_ => false,
}
});
match (keys.next(), keys.next()) {
(None, None) => Err(Error::NoIdentities),
(Some(key), None) => Ok(key),
(Some(_), Some(_)) => Err(Error::MultipleIdentities),
(None, Some(_)) => unreachable!(),
}
}?;
let stub = yubikey::Stub::new(yubikey.serial(), slot, &recipient);
let created = x509_parser::parse_x509_certificate(key.certificate().as_ref())
.ok()
.map(|(_, cert)| cert.validity().not_before.to_rfc2822())
.unwrap_or_else(|| "Unknown".to_owned());
util::print_identity(stub, recipient, &created);
Ok(())
}
fn list(all: bool) -> Result<(), Error> {
@@ -141,7 +215,7 @@ fn main() -> Result<(), Error> {
} else if opts.generate {
todo!()
} else if opts.identity {
todo!()
identity(opts)
} else if opts.list {
list(false)
} else if opts.list_all {