use elliptic_curve::rand_core::{CryptoRng, Error, RngCore}; use p256::{ecdh::EphemeralSecret, PublicKey}; use p256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; use rust_util::{failure_and_exit, information, warning, XResult}; use yubikey::certificate::PublicKeyInfo; use yubikey::Context; use yubikey::piv::{AlgorithmId, decrypt_data, metadata, RetiredSlotId, SlotId}; // const EPK_BYTES: usize = 33; #[derive(Debug)] pub(crate) struct EphemeralKeyBytes(p256::EncodedPoint); impl EphemeralKeyBytes { // fn from_bytes(bytes: [u8; EPK_BYTES]) -> Option { // let encoded = p256::EncodedPoint::from_bytes(&bytes).ok()?; // if encoded.is_compressed() // && p256::PublicKey::from_encoded_point(&encoded) // .is_some() // .into() // { // Some(EphemeralKeyBytes(encoded)) // } else { // None // } // } fn from_public_key(epk: &p256::PublicKey) -> Self { EphemeralKeyBytes(epk.to_encoded_point(true)) } // pub(crate) fn as_bytes(&self) -> &[u8] { // self.0.as_bytes() // } pub(crate) fn decompress(&self) -> p256::EncodedPoint { // EphemeralKeyBytes is a valid compressed encoding by construction. let p = p256::PublicKey::from_encoded_point(&self.0).unwrap(); p.to_encoded_point(false) } } struct FakeRandom; impl CryptoRng for FakeRandom {} impl RngCore for FakeRandom { fn next_u32(&mut self) -> u32 { todo!() } fn next_u64(&mut self) -> u64 { todo!() } fn fill_bytes(&mut self, dest: &mut [u8]) { println!("Fill random dest len: {}", dest.len()); for i in 0..dest.len() { dest[i]= 0x01; } } fn try_fill_bytes(&mut self, _dest: &mut [u8]) -> Result<(), Error> { todo!() } } fn main() -> XResult<()> { let mut readers = Context::open()?; let reader = readers.iter()?.next().unwrap_or_else(|| failure_and_exit!("No reader!")); let mut yubikey = reader.open()?; information!("Yubikey serial: {}", yubikey.serial().0); // let esk = EphemeralSecret::random(&mut rand::rngs::OsRng); let esk = EphemeralSecret::random(&mut FakeRandom); let epk = esk.public_key(); let epk_bytes = EphemeralKeyBytes::from_public_key(&epk); let public_key_pem = r#"-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3T7r2QbJzwCwjsKfftYYBNHMHRNS 2SV7YoGR4I/DcXxPrjKYzVxIKc7IvzqUbn22C3hX4Sh/aguuaz8jQvAH0A== -----END PUBLIC KEY-----"#; let public_key = public_key_pem.parse::().unwrap(); // let encoded_point = p256::EncodedPoint::from_bytes(&hex::decode( // "04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0" // )?)?; // let public_key = PublicKey::from_encoded_point(&encoded_point).unwrap(); let shared_secret = esk.diffie_hellman(&public_key); information!("Shared secret: {}", hex::encode(shared_secret.raw_secret_bytes())); let meta_result = metadata(&mut yubikey, SlotId::Retired(RetiredSlotId::R1)); match meta_result { Ok(meta) => { information!("{:?}", meta); if let Some(public_key) = &meta.public { match public_key { PublicKeyInfo::Rsa { algorithm, pubkey } => { information!("RSA, {:?}, {:?}", algorithm, pubkey); } PublicKeyInfo::EcP256(pubkey) => { information!("EC-P256, {}", hex::encode(pubkey.as_bytes())); } PublicKeyInfo::EcP384(pubkey) => { information!("EC-P384, {}", hex::encode(pubkey.as_bytes())); } } } } Err(e) => warning!("Get slot meta failed: {}", e) } yubikey.verify_pin(b"123456").expect("Verify pin!"); let decrypted_shared_secret = decrypt_data( &mut yubikey, epk_bytes.decompress().as_bytes(), AlgorithmId::EccP256, SlotId::Retired(RetiredSlotId::R1), )?; information!("Decrypted shared secret: {}", hex::encode(&decrypted_shared_secret.to_vec())); Ok(()) }