From 4b7743a68bce62df646067590dd3df71dad57f6c Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Fri, 6 Oct 2023 10:46:45 +0800 Subject: [PATCH] feat: v1.7.6, add subcommand age-address --- Cargo.lock | 47 +++++++++++++++++++++++++++++++++++++- Cargo.toml | 4 +++- src/cmd_ageaddress.rs | 52 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 6 +++++ 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 src/cmd_ageaddress.rs diff --git a/Cargo.lock b/Cargo.lock index 1027663..388fea2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + [[package]] name = "bindgen" version = "0.63.0" @@ -320,13 +326,15 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.7.5" +version = "1.7.6" dependencies = [ "authenticator", "base64 0.21.4", + "bech32", "chrono", "clap", "digest 0.10.7", + "env_logger", "hex", "openpgp-card", "openpgp-card-pcsc", @@ -778,6 +786,19 @@ dependencies = [ "log", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1007,6 +1028,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -2540,6 +2567,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -3002,6 +3038,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 92d5a9a..cbf2868 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.7.5" +version = "1.7.6" authors = ["Hatter Jiang "] edition = "2018" @@ -34,6 +34,8 @@ ssh-agent = { version = "0.2", features = ["agent"] } p256 = { version = "0.13", features = ["pem", "ecdh"] } spki = { version = "0.7", features = ["pem"] } tabled = "0.14.0" +env_logger = "0.10" +bech32 = "0.9.1" #lazy_static = "1.4.0" #ssh-key = "0.4.0" #ctap-hid-fido2 = "2.1.3" diff --git a/src/cmd_ageaddress.rs b/src/cmd_ageaddress.rs new file mode 100644 index 0000000..a256f06 --- /dev/null +++ b/src/cmd_ageaddress.rs @@ -0,0 +1,52 @@ +use bech32::{ToBase32, Variant}; +use clap::{App, ArgMatches, SubCommand}; +use openpgp_card::{KeyType, OpenPgp}; +use openpgp_card::algorithm::{Algo, Curve}; +use openpgp_card::crypto_data::{EccType, PublicKeyMaterial}; +use openpgp_card_pcsc::PcscBackend; +use rust_util::util_clap::{Command, CommandError}; + +const AGE_PUBLIC_KEY_PREFIX: &str = "age"; + +pub struct CommandImpl; + +impl Command for CommandImpl { + fn name(&self) -> &str { "age-address" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("OpenPGP Card Encryption key to age address") + } + + fn run(&self, _arg_matches: &ArgMatches, _sub_arg_matches: &ArgMatches) -> CommandError { + let cards = opt_result!(PcscBackend::cards(None), "Failed to list OpenPGP cards: {}"); + + information!("Found {} card(s)", cards.len()); + for (i, card) in cards.into_iter().enumerate() { + let mut pgp = OpenPgp::new(card); + let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}"); + if let Ok(application_related_data) = trans.application_related_data() { + success!("Found card #{}: {:?}", i, application_related_data.application_id()); + } + let encryption_public_key = match trans.public_key(KeyType::Decryption) { + Ok(pub_key) => pub_key, + Err(e) => return simple_error!("Get decryption public key failed: {}", e), + }; + if let PublicKeyMaterial::E(ecc_pub) = &encryption_public_key { + if let Algo::Ecc(ecc) = ecc_pub.algo() { + if let (EccType::ECDH, Curve::Cv25519) = (ecc.ecc_type(), ecc.curve()) { + let pub_key_bytes = ecc_pub.data(); + let age_address = opt_result!(bech32::encode( + AGE_PUBLIC_KEY_PREFIX, + pub_key_bytes.to_base32(), + Variant::Bech32, + ), "Generate age address failed: {}"); + success!("Age address: {}", age_address); + return Ok(None); + } + } + } + return simple_error!("Not supported encryption key: {}", encryption_public_key); + } + Ok(None) + } +} diff --git a/src/main.rs b/src/main.rs index 5bec406..f69c831 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,7 @@ mod cmd_pivgenerate; mod cmd_chall; mod cmd_challconfig; mod cmd_sshagent; +mod cmd_ageaddress; pub struct DefaultCommandImpl; @@ -49,6 +50,10 @@ impl DefaultCommandImpl { } fn main() { + // Run with: RUST_LOG=debug, for more: https://docs.rs/env_logger/0.10.0/env_logger/ + #[cfg(debug_assertions)] + env_logger::init(); + match inner_main() { Err(e) => failure_and_exit!("Run cli error: {}", e), Ok(Some(code)) => std::process::exit(code), @@ -81,6 +86,7 @@ fn inner_main() -> CommandError { Box::new(cmd_u2fregister::CommandImpl), Box::new(cmd_u2fsign::CommandImpl), Box::new(cmd_sshagent::CommandImpl), + Box::new(cmd_ageaddress::CommandImpl), ]; let mut app = App::new(env!("CARGO_PKG_NAME")) .version(env!("CARGO_PKG_VERSION"))