diff --git a/tools/src/bin/opgpcard/cli.rs b/tools/src/bin/opgpcard/cli.rs index 248050e..7ed6696 100644 --- a/tools/src/bin/opgpcard/cli.rs +++ b/tools/src/bin/opgpcard/cli.rs @@ -51,17 +51,7 @@ pub enum Command { Ssh(commands::ssh::SshCommand), /// Export the key data on a card as an OpenPGP public key - Pubkey { - #[clap(name = "card ident", short = 'c', long = "card")] - ident: Option, - - #[clap(name = "User PIN file", short = 'p', long = "user-pin")] - user_pin: Option, - - /// User ID to add to the exported certificate representation - #[clap(name = "User ID", short = 'u', long = "userid")] - user_id: Vec, - }, + Pubkey(commands::pubkey::PubkeyCommand), /// Administer data on a card (including keys and metadata) Admin { diff --git a/tools/src/bin/opgpcard/commands/mod.rs b/tools/src/bin/opgpcard/commands/mod.rs index 67deb6e..590a30a 100644 --- a/tools/src/bin/opgpcard/commands/mod.rs +++ b/tools/src/bin/opgpcard/commands/mod.rs @@ -3,5 +3,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pub mod info; +pub mod pubkey; pub mod ssh; pub mod status; diff --git a/tools/src/bin/opgpcard/commands/pubkey.rs b/tools/src/bin/opgpcard/commands/pubkey.rs new file mode 100644 index 0000000..9673643 --- /dev/null +++ b/tools/src/bin/opgpcard/commands/pubkey.rs @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer +// SPDX-FileCopyrightText: 2022 Lars Wirzenius +// SPDX-FileCopyrightText: 2022 Nora Widdecke +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use anyhow::Result; +use clap::Parser; + +use std::path::PathBuf; + +use sequoia_openpgp::serialize::SerializeInto; + +use openpgp_card_sequoia::card::Card; +use openpgp_card_sequoia::types::KeyType; +use openpgp_card_sequoia::util::public_key_material_and_fp_to_key; + +use crate::output; +use crate::pick_card_for_reading; +use crate::util; +use crate::versioned_output::{OutputBuilder, OutputFormat, OutputVersion}; + +#[derive(Parser, Debug)] +pub struct PubkeyCommand { + #[clap(name = "card ident", short = 'c', long = "card")] + ident: Option, + + #[clap(name = "User PIN file", short = 'p', long = "user-pin")] + user_pin: Option, + + /// User ID to add to the exported certificate representation + #[clap(name = "User ID", short = 'u', long = "userid")] + user_ids: Vec, +} + +pub fn print_pubkey( + format: OutputFormat, + output_version: OutputVersion, + command: PubkeyCommand, +) -> Result<()> { + let mut output = output::PublicKey::default(); + + let backend = pick_card_for_reading(command.ident)?; + let mut card = Card::new(backend); + let mut open = card.transaction()?; + + let ident = open.application_identifier()?.ident(); + output.ident(ident); + + let user_pin = util::get_pin(&mut open, command.user_pin, crate::ENTER_USER_PIN); + + let pkm = open.public_key(KeyType::Signing)?; + let times = open.key_generation_times()?; + let fps = open.fingerprints()?; + + let key_sig = public_key_material_and_fp_to_key( + &pkm, + KeyType::Signing, + times.signature().expect("Signature time is unset"), + fps.signature().expect("Signature fingerprint is unset"), + )?; + + let mut key_dec = None; + if let Ok(pkm) = open.public_key(KeyType::Decryption) { + if let Some(ts) = times.decryption() { + key_dec = Some(public_key_material_and_fp_to_key( + &pkm, + KeyType::Decryption, + ts, + fps.decryption().expect("Decryption fingerprint is unset"), + )?); + } + } + + let mut key_aut = None; + if let Ok(pkm) = open.public_key(KeyType::Authentication) { + if let Some(ts) = times.authentication() { + key_aut = Some(public_key_material_and_fp_to_key( + &pkm, + KeyType::Authentication, + ts, + fps.authentication() + .expect("Authentication fingerprint is unset"), + )?); + } + } + + let cert = crate::get_cert( + &mut open, + key_sig, + key_dec, + key_aut, + user_pin.as_deref(), + &command.user_ids, + &|| println!("Enter User PIN on card reader pinpad."), + )?; + + let armored = String::from_utf8(cert.armored().to_vec()?)?; + output.public_key(armored); + + println!("{}", output.print(format, output_version)?); + Ok(()) +} diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index b06d6fe..5e8e848 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -19,9 +19,7 @@ use sequoia_openpgp::Cert; use openpgp_card_sequoia::card::{Admin, Card, Open}; use openpgp_card_sequoia::types::{AlgoSimple, CardBackend, KeyType, TouchPolicy}; -use openpgp_card_sequoia::util::{ - make_cert, public_key_material_and_fp_to_key, public_key_material_to_key, -}; +use openpgp_card_sequoia::util::{make_cert, public_key_material_to_key}; use openpgp_card_sequoia::{sq_util, PublicKey}; use crate::util::{load_pin, print_gnuk_note}; @@ -60,18 +58,8 @@ fn main() -> Result<(), Box> { cli::Command::Ssh(cmd) => { commands::ssh::print_ssh(cli.output_format, cli.output_version, cmd)?; } - cli::Command::Pubkey { - ident, - user_pin, - user_id, - } => { - print_pubkey( - cli.output_format, - cli.output_version, - ident, - user_pin, - user_id, - )?; + cli::Command::Pubkey(cmd) => { + commands::pubkey::print_pubkey(cli.output_format, cli.output_version, cmd)?; } cli::Command::SetIdentity { ident, id } => { set_identity(&ident, id)?; @@ -607,77 +595,6 @@ fn pick_card_for_reading(ident: Option) -> Result, - user_pin: Option, - user_ids: Vec, -) -> Result<()> { - let mut output = output::PublicKey::default(); - - let backend = pick_card_for_reading(ident)?; - let mut card = Card::new(backend); - let mut open = card.transaction()?; - - let ident = open.application_identifier()?.ident(); - output.ident(ident); - - let user_pin = util::get_pin(&mut open, user_pin, ENTER_USER_PIN); - - let pkm = open.public_key(KeyType::Signing)?; - let times = open.key_generation_times()?; - let fps = open.fingerprints()?; - - let key_sig = public_key_material_and_fp_to_key( - &pkm, - KeyType::Signing, - times.signature().expect("Signature time is unset"), - fps.signature().expect("Signature fingerprint is unset"), - )?; - - let mut key_dec = None; - if let Ok(pkm) = open.public_key(KeyType::Decryption) { - if let Some(ts) = times.decryption() { - key_dec = Some(public_key_material_and_fp_to_key( - &pkm, - KeyType::Decryption, - ts, - fps.decryption().expect("Decryption fingerprint is unset"), - )?); - } - } - - let mut key_aut = None; - if let Ok(pkm) = open.public_key(KeyType::Authentication) { - if let Some(ts) = times.authentication() { - key_aut = Some(public_key_material_and_fp_to_key( - &pkm, - KeyType::Authentication, - ts, - fps.authentication() - .expect("Authentication fingerprint is unset"), - )?); - } - } - - let cert = get_cert( - &mut open, - key_sig, - key_dec, - key_aut, - user_pin.as_deref(), - &user_ids, - &|| println!("Enter User PIN on card reader pinpad."), - )?; - - let armored = String::from_utf8(cert.armored().to_vec()?)?; - output.public_key(armored); - - println!("{}", output.print(format, output_version)?); - Ok(()) -} - fn decrypt( ident: &str, pin_file: Option,