diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 2253711..4d9b534 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -18,6 +18,7 @@ openpgp-card = { path = "../openpgp-card", version = "0.1" } openpgp-card-pcsc = { path = "../pcsc", version = "0.1" } pcsc = "2" openpgp-card-sequoia = { path = "../openpgp-card-sequoia", version = "0.0.7" } +sshkeys = "0.3" rpassword = "5" chrono = "0.4" anyhow = "1" diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index 12d2a45..08880fc 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -178,6 +178,8 @@ fn print_status(ident: Option, verbose: bool) -> Result<()> { let mut open = Open::new(&mut txc)?; + let ident = open.application_identifier()?.ident(); + print!("OpenPGP card {}", open.application_identifier()?.ident()); let ai = open.application_identifier()?; @@ -262,12 +264,20 @@ fn print_status(ident: Option, verbose: bool) -> Result<()> { if let Some(fp) = fps.authentication() { println!(" fingerprint: {}", fp.to_spaced_hex()); } + let pubkey = open.public_key(KeyType::Authentication); + if let Ok(pkm) = &pubkey { + if let Ok(ssh) = util::get_ssh_pubkey_string(pkm, ident) { + // print auth key as openssh public key string + println!(" {}", ssh); + } + } + if let Some(kgt) = kgt.authentication() { println! {" created: {}", kgt.formatted()}; } println! {" algorithm: {}", open.algorithm_attributes(KeyType::Authentication)?}; if verbose { - if let Ok(pkm) = open.public_key(KeyType::Authentication) { + if let Ok(pkm) = pubkey { println! {" public key material: {}", pkm}; } } diff --git a/tools/src/bin/opgpcard/util.rs b/tools/src/bin/opgpcard/util.rs index 2ae51d5..7e8cfc0 100644 --- a/tools/src/bin/opgpcard/util.rs +++ b/tools/src/bin/opgpcard/util.rs @@ -4,6 +4,8 @@ use anyhow::{anyhow, Context, Result}; use std::path::{Path, PathBuf}; +use openpgp_card::algorithm::{Algo, Curve}; +use openpgp_card::crypto_data::{EccType, PublicKeyMaterial}; use openpgp_card::Error; use openpgp_card_pcsc::PcscCard; use openpgp_card_sequoia::card::{Admin, Open, Sign, User}; @@ -110,3 +112,103 @@ pub(crate) fn open_or_stdout( None => Ok(Box::new(std::io::stdout())), } } + +fn get_ssh_pubkey( + pkm: &PublicKeyMaterial, + ident: String, +) -> Result { + let cardno = format!("cardno:{}", ident); + + let (key_type, kind) = match pkm { + PublicKeyMaterial::R(rsa) => { + let key_type = sshkeys::KeyType::from_name("ssh-rsa")?; + + let kind = sshkeys::PublicKeyKind::Rsa(sshkeys::RsaPublicKey { + e: rsa.v().to_vec(), + n: rsa.n().to_vec(), + }); + + Ok((key_type, kind)) + } + PublicKeyMaterial::E(ecc) => { + if let Algo::Ecc(ecc_attrs) = ecc.algo() { + match ecc_attrs.ecc_type() { + EccType::EdDSA => { + let key_type = + sshkeys::KeyType::from_name("ssh-ed25519")?; + + let kind = sshkeys::PublicKeyKind::Ed25519( + sshkeys::Ed25519PublicKey { + key: ecc.data().to_vec(), + }, + ); + + Ok((key_type, kind)) + } + EccType::ECDSA => { + let (curve, name) = match ecc_attrs.curve() { + Curve::NistP256r1 => Ok(( + sshkeys::Curve::from_identifier("nistp256")?, + "ecdsa-sha2-nistp256", + )), + Curve::NistP384r1 => Ok(( + sshkeys::Curve::from_identifier("nistp384")?, + "ecdsa-sha2-nistp384", + )), + Curve::NistP521r1 => Ok(( + sshkeys::Curve::from_identifier("nistp521")?, + "ecdsa-sha2-nistp521", + )), + _ => Err(anyhow!( + "Unexpected ECDSA curve {:?}", + ecc_attrs.curve() + )), + }?; + + let key_type = sshkeys::KeyType::from_name(name)?; + + let kind = sshkeys::PublicKeyKind::Ecdsa( + sshkeys::EcdsaPublicKey { + curve, + key: ecc.data().to_vec(), + }, + ); + + Ok((key_type, kind)) + } + _ => Err(anyhow!( + "Unexpected EccType {:?}", + ecc_attrs.ecc_type() + )), + } + } else { + Err(anyhow!("Unexpected Algo in EccPub {:?}", ecc)) + } + } + _ => Err(anyhow!("Unexpected PublicKeyMaterial type {:?}", pkm)), + }?; + + let pk = sshkeys::PublicKey { + key_type, + comment: Some(cardno), + kind, + }; + + Ok(pk) +} + +/// Return a String representation of an ssh public key, in a form like: +/// "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAuTuxILMTvzTIRvaRqqUM3aRDoEBgz/JAoWKsD1ECxy cardno:FFFE:43194240" +pub(crate) fn get_ssh_pubkey_string( + pkm: &PublicKeyMaterial, + ident: String, +) -> Result { + let pk = get_ssh_pubkey(pkm, ident)?; + + let mut v = vec![]; + pk.write(&mut v)?; + + let s = String::from_utf8_lossy(&v).to_string(); + + Ok(s.trim().into()) +}