diff --git a/Cargo.lock b/Cargo.lock index 1c46d27..129874c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,7 +47,6 @@ dependencies = [ "chrono", "console", "dialoguer", - "elliptic-curve", "env_logger", "gumdrop", "hex", diff --git a/Cargo.toml b/Cargo.toml index b9fc361..4914055 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ bech32 = "0.8" chrono = "0.4" console = "0.14" dialoguer = "0.8" -elliptic-curve = "0.8" env_logger = "0.8" gumdrop = "0.8" hex = "0.4" diff --git a/src/builder.rs b/src/builder.rs index 7f05680..2176fe0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -78,13 +78,9 @@ impl IdentityBuilder { let keys = Key::list(yubikey)?; USABLE_SLOTS .iter() - .find(|&&slot| { - keys.iter() - .find(|key| key.slot() == SlotId::Retired(slot)) - .is_none() - }) + .find(|&&slot| !keys.iter().any(|key| key.slot() == SlotId::Retired(slot))) .cloned() - .ok_or(Error::NoEmptySlots(yubikey.serial()))? + .ok_or_else(|| Error::NoEmptySlots(yubikey.serial()))? } }; @@ -113,7 +109,7 @@ impl IdentityBuilder { let recipient = match &generated { PublicKeyInfo::EcP256(pubkey) => { - Recipient::from_pubkey(*pubkey).expect("YubiKey generates a valid pubkey") + Recipient::from_encoded(pubkey).expect("YubiKey generates a valid pubkey") } _ => unreachable!(), }; diff --git a/src/main.rs b/src/main.rs index 37db34f..55543e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -137,7 +137,7 @@ fn identity(opts: PluginOptions) -> Result<(), Error> { // - 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)) + p256::Recipient::from_encoded(pubkey).map(|r| (key, slot, r)) } _ => None, } @@ -205,7 +205,7 @@ fn list(all: bool) -> Result<(), Error> { // Only P-256 keys are compatible with us. let recipient = match key.certificate().subject_pki() { - PublicKeyInfo::EcP256(pubkey) => match p256::Recipient::from_pubkey(*pubkey) { + PublicKeyInfo::EcP256(pubkey) => match p256::Recipient::from_encoded(pubkey) { Some(recipient) => recipient, None => continue, }, @@ -265,8 +265,8 @@ fn main() -> Result<(), Error> { if let Some(state_machine) = opts.age_plugin { run_state_machine( &state_machine, - || plugin::RecipientPlugin::default(), - || plugin::IdentityPlugin::default(), + plugin::RecipientPlugin::default, + plugin::IdentityPlugin::default, )?; Ok(()) } else if opts.generate { @@ -279,17 +279,17 @@ fn main() -> Result<(), Error> { list(true) } else { eprintln!("✨ Let's get your YubiKey set up for age! ✨"); - eprintln!(""); + eprintln!(); eprintln!("This tool can create a new age identity in a free slot of your YubiKey."); eprintln!("It will generate an identity file that you can use with an age client,"); eprintln!("along with the corresponding recipient."); - eprintln!(""); + eprintln!(); eprintln!("If you are already using a YubiKey with age, you can select an existing"); eprintln!("slot to recreate its corresponding identity file and recipient."); - eprintln!(""); + eprintln!(); eprintln!("When asked below to select an option, use the up/down arrow keys to"); eprintln!("make your choice, or press [Esc] or [q] to quit."); - eprintln!(""); + eprintln!(); if Readers::open()?.iter()?.len() == 0 { eprintln!("⏳ Please insert the YubiKey you want to set up."); @@ -299,8 +299,8 @@ fn main() -> Result<(), Error> { // Filter out readers we can't connect to. let readers_list: Vec<_> = readers .iter()? - .filter_map(|reader| match reader.open() { - Ok(_) => Some(reader), + .filter(|reader| match reader.open() { + Ok(_) => true, Err(e) => { use std::error::Error; let reason = if let Some(inner) = e.source() { @@ -309,7 +309,7 @@ fn main() -> Result<(), Error> { e.to_string() }; warn!("Ignoring {}: {}", reader.name(), reason); - None + false } }) .collect(); @@ -342,7 +342,7 @@ fn main() -> Result<(), Error> { .find(|key| key.slot() == SlotId::Retired(slot)) .map(|key| match key.certificate().subject_pki() { PublicKeyInfo::EcP256(pubkey) => { - p256::Recipient::from_pubkey(*pubkey).map(|_| { + p256::Recipient::from_encoded(pubkey).map(|_| { // Cache the details we need to display to the user. let (_, cert) = x509_parser::parse_x509_certificate(key.certificate().as_ref()) @@ -394,7 +394,7 @@ fn main() -> Result<(), Error> { if let Some(key) = keys.iter().find(|key| key.slot() == SlotId::Retired(slot)) { let recipient = match key.certificate().subject_pki() { PublicKeyInfo::EcP256(pubkey) => { - p256::Recipient::from_pubkey(*pubkey).expect("We checked this above") + p256::Recipient::from_encoded(pubkey).expect("We checked this above") } _ => unreachable!(), }; diff --git a/src/p256.rs b/src/p256.rs index 53e5954..583403c 100644 --- a/src/p256.rs +++ b/src/p256.rs @@ -1,6 +1,5 @@ use bech32::{ToBase32, Variant}; -use elliptic_curve::sec1::EncodedPoint; -use p256::NistP256; +use p256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; use sha2::{Digest, Sha256}; use std::convert::TryInto; use std::fmt; @@ -11,11 +10,11 @@ pub(crate) const TAG_BYTES: usize = 4; /// Wrapper around a compressed secp256r1 curve point. #[derive(Clone)] -pub struct Recipient(EncodedPoint); +pub struct Recipient(p256::PublicKey); impl fmt::Debug for Recipient { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Recipient({:?})", self.as_bytes()) + write!(f, "Recipient({:?})", self.to_encoded().as_bytes()) } } @@ -24,7 +23,7 @@ impl fmt::Display for Recipient { f.write_str( bech32::encode( RECIPIENT_PREFIX, - self.as_bytes().to_base32(), + self.to_encoded().as_bytes().to_base32(), Variant::Bech32, ) .expect("HRP is valid") @@ -34,22 +33,17 @@ impl fmt::Display for Recipient { } impl Recipient { - /// Attempts to parse a valid secp256r1 public key from its SEC-1 encoding. - pub(crate) fn from_pubkey(pubkey: EncodedPoint) -> Option { - if pubkey.is_compressed() { - if pubkey.decompress().is_some().into() { - Some(Recipient(pubkey)) - } else { - None - } - } else { - Some(Recipient(pubkey.compress())) - } + /// Attempts to parse a valid YubiKey recipient from its SEC-1 encoding. + /// + /// This accepts both compressed (as used by the plugin) and uncompressed (as used in + /// the YubiKey certificate) encodings. + pub(crate) fn from_encoded(encoded: &p256::EncodedPoint) -> Option { + p256::PublicKey::from_encoded_point(&encoded).map(Recipient) } - /// Returns the compressed SEC-1 encoding of this public key. - pub(crate) fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() + /// Returns the compressed SEC-1 encoding of this recipient. + pub(crate) fn to_encoded(&self) -> p256::EncodedPoint { + self.0.to_encoded_point(true) } pub(crate) fn tag(&self) -> [u8; TAG_BYTES] { diff --git a/src/yubikey.rs b/src/yubikey.rs index 43c84d9..8d45963 100644 --- a/src/yubikey.rs +++ b/src/yubikey.rs @@ -71,7 +71,7 @@ pub(crate) fn open(serial: Option) -> Result { } pub(crate) fn manage(yubikey: &mut YubiKey) -> Result<(), Error> { - eprintln!(""); + eprintln!(); let pin = Password::new() .with_prompt(&format!( "Enter PIN for YubiKey with serial {} (default is 123456)", @@ -82,15 +82,15 @@ pub(crate) fn manage(yubikey: &mut YubiKey) -> Result<(), Error> { // If the user is using the default PIN, help them to change it. if pin == "123456" { - eprintln!(""); + eprintln!(); eprintln!("✨ Your key is using the default PIN. Let's change it!"); eprintln!("✨ We'll also set the PUK equal to the PIN."); - eprintln!(""); + eprintln!(); eprintln!("🔐 The PIN is up to 8 numbers, letters, or symbols. Not just numbers!"); eprintln!( "❌ Your keys will be lost if the PIN and PUK are locked after 3 incorrect tries." ); - eprintln!(""); + eprintln!(); let current_puk = Password::new() .with_prompt("Enter current PUK (default is 12345678)") .interact()?;