diff --git a/tools/src/bin/opgpcard/cli.rs b/tools/src/bin/opgpcard/cli.rs index 8328fd5..714ca23 100644 --- a/tools/src/bin/opgpcard/cli.rs +++ b/tools/src/bin/opgpcard/cli.rs @@ -78,21 +78,7 @@ pub enum Command { Decrypt(commands::decrypt::DecryptCommand), /// Sign data using a card - Sign { - #[clap(name = "card ident", short = 'c', long = "card")] - ident: String, - - /// User PIN file - #[clap(short = 'p', long = "user-pin")] - user_pin: Option, - - #[clap(name = "detached", short = 'd', long = "detached")] - detached: bool, - - /// Input file (stdin if unset) - #[clap(name = "input")] - input: Option, - }, + Sign(commands::sign::SignCommand), /// Attestation management (Yubico) Attestation { diff --git a/tools/src/bin/opgpcard/commands/mod.rs b/tools/src/bin/opgpcard/commands/mod.rs index 7746aec..b692df3 100644 --- a/tools/src/bin/opgpcard/commands/mod.rs +++ b/tools/src/bin/opgpcard/commands/mod.rs @@ -5,5 +5,6 @@ pub mod decrypt; pub mod info; pub mod pubkey; +pub mod sign; pub mod ssh; pub mod status; diff --git a/tools/src/bin/opgpcard/commands/sign.rs b/tools/src/bin/opgpcard/commands/sign.rs new file mode 100644 index 0000000..807fac9 --- /dev/null +++ b/tools/src/bin/opgpcard/commands/sign.rs @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer +// SPDX-FileCopyrightText: 2022 Nora Widdecke +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use anyhow::{anyhow, Result}; +use clap::Parser; + +use std::path::{Path, PathBuf}; + +use sequoia_openpgp::serialize::stream::{Armorer, Message, Signer}; + +use openpgp_card_sequoia::card::Card; + +use crate::util; + +#[derive(Parser, Debug)] +pub struct SignCommand { + #[clap(name = "card ident", short = 'c', long = "card")] + pub ident: String, + + /// User PIN file + #[clap(short = 'p', long = "user-pin")] + pub user_pin: Option, + + #[clap(name = "detached", short = 'd', long = "detached")] + pub detached: bool, + + /// Input file (stdin if unset) + #[clap(name = "input")] + pub input: Option, +} + +pub fn sign(command: SignCommand) -> Result<(), Box> { + if command.detached { + sign_detached(&command.ident, command.user_pin, command.input.as_deref()) + } else { + Err(anyhow::anyhow!("Only detached signatures are supported for now").into()) + } +} + +pub fn sign_detached( + ident: &str, + pin_file: Option, + input: Option<&Path>, +) -> Result<(), Box> { + let mut input = util::open_or_stdin(input)?; + + let backend = util::open_card(ident)?; + let mut card = Card::new(backend); + let mut open = card.transaction()?; + + if open.fingerprints()?.signature().is_none() { + return Err(anyhow!("Can't sign: this card has no key in the signing slot.").into()); + } + + let user_pin = util::get_pin(&mut open, pin_file, crate::ENTER_USER_PIN); + + let mut sign = util::verify_to_sign(&mut open, user_pin.as_deref())?; + let s = sign.signer(&|| println!("Touch confirmation needed for signing"))?; + + let message = Armorer::new(Message::new(std::io::stdout())).build()?; + let mut signer = Signer::new(message, s).detached().build()?; + + std::io::copy(&mut input, &mut signer)?; + signer.finalize()?; + + Ok(()) +} diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index 4087f98..8e24c7d 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -5,14 +5,13 @@ use anyhow::{anyhow, Result}; use clap::Parser; use cli::BaseKeySlot; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use sequoia_openpgp::cert::prelude::ValidErasedKeyAmalgamation; use sequoia_openpgp::packet::key::{SecretParts, UnspecifiedRole}; use sequoia_openpgp::packet::Key; use sequoia_openpgp::parse::Parse; use sequoia_openpgp::policy::{Policy, StandardPolicy}; -use sequoia_openpgp::serialize::stream::{Armorer, Message, Signer}; use sequoia_openpgp::serialize::SerializeInto; use sequoia_openpgp::types::{HashAlgorithm, SymmetricAlgorithm}; use sequoia_openpgp::Cert; @@ -67,19 +66,8 @@ fn main() -> Result<(), Box> { cli::Command::Decrypt(cmd) => { commands::decrypt::decrypt(cmd)?; } - cli::Command::Sign { - ident, - user_pin, - detached, - input, - } => { - if detached { - sign_detached(&ident, user_pin, input.as_deref())?; - } else { - return Err( - anyhow::anyhow!("Only detached signatures are supported for now").into(), - ); - } + cli::Command::Sign(cmd) => { + commands::sign::sign(cmd)?; } cli::Command::Attestation { cmd } => match cmd { cli::AttCommand::Cert { ident } => { @@ -591,35 +579,6 @@ fn pick_card_for_reading(ident: Option) -> Result, - input: Option<&Path>, -) -> Result<(), Box> { - let mut input = util::open_or_stdin(input)?; - - let backend = util::open_card(ident)?; - let mut card = Card::new(backend); - let mut open = card.transaction()?; - - if open.fingerprints()?.signature().is_none() { - return Err(anyhow!("Can't sign: this card has no key in the signing slot.").into()); - } - - let user_pin = util::get_pin(&mut open, pin_file, ENTER_USER_PIN); - - let mut sign = util::verify_to_sign(&mut open, user_pin.as_deref())?; - let s = sign.signer(&|| println!("Touch confirmation needed for signing"))?; - - let message = Armorer::new(Message::new(std::io::stdout())).build()?; - let mut signer = Signer::new(message, s).detached().build()?; - - std::io::copy(&mut input, &mut signer)?; - signer.finalize()?; - - Ok(()) -} - fn factory_reset(ident: &str) -> Result<()> { println!("Resetting Card {}", ident); let card = util::open_card(ident)?;