diff --git a/Cargo.toml b/Cargo.toml index 96401de..4c9c4b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ [workspace] members = [ + "openpgp-card-apps", "openpgp-card", "openpgp-card-sequoia", "pcsc", diff --git a/openpgp-card-apps/Cargo.toml b/openpgp-card-apps/Cargo.toml new file mode 100644 index 0000000..bc165ad --- /dev/null +++ b/openpgp-card-apps/Cargo.toml @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz +# SPDX-License-Identifier: MIT OR Apache-2.0 + +[package] +name = "openpgp-card-apps" +description = "Examples and demos of apps utilizing openpgp-card-sequoia" +license = "MIT OR Apache-2.0" +version = "0.0.1" +authors = ["Wiktor Kwapisiewicz "] +edition = "2018" +repository = "https://gitlab.com/hkos/openpgp-card" +documentation = "https://docs.rs/crate/openpgp-card-apps" + +[dependencies] +sequoia-openpgp = "1.3" +nettle = "7" +openpgp-card = { path = "../openpgp-card", version = "0.0.4" } +openpgp-card-pcsc = { path = "../pcsc", version = "0.0.4" } +openpgp-card-scdc = { path = "../scdc", version = "0.0.2" } +openpgp-card-sequoia = { path = "../openpgp-card-sequoia", version = "0.0.3" } +chrono = "0.4" +anyhow = "1" +thiserror = "1" +env_logger = "0.8" +log = "0.4" diff --git a/openpgp-card-apps/README.md b/openpgp-card-apps/README.md new file mode 100644 index 0000000..1379c3c --- /dev/null +++ b/openpgp-card-apps/README.md @@ -0,0 +1,43 @@ + + +**OpenPGP card usage with Sequoia PGP: Example apps** + +*Small GnuPG replacements* + +This crate can be used to decrypt OpenPGP data and to sign data +producing OpenPGP data. + +First export the certificate that holds keys stored on the card: + +``` +$ gpg --export --armor $KEYID > cert.asc +``` + +Then create a test data, encrypted with GnuPG (as an example): + +``` +$ echo example data | gpg -ear $KEYID > encrypted.asc +``` + +And put the card PIN in a file called `pin`. + +And then use the crate for decryption: + +``` +$ cargo run --example decrypt $CARD_ID pin cert.asc < encrypted.asc +``` + +The `$CARD_ID` holds card ident that can be printed using `cargo +run`. It's a string that looks like `0006:12345678`. Remember that if +the GnuPG agent is holding an exclusive access to the card it will not +show up. Unplugging and plugging the card again will relinquish +GnuPG's agent's hold on the card. + +Signing works the same way: + +``` +$ echo data to be signed | cargo run --example detach-sign $CARD_ID pin cert.asc > signature.asc +``` diff --git a/openpgp-card-apps/src/bin/decrypt.rs b/openpgp-card-apps/src/bin/decrypt.rs new file mode 100644 index 0000000..9fa80e4 --- /dev/null +++ b/openpgp-card-apps/src/bin/decrypt.rs @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use openpgp_card_pcsc::PcscClient; + +use openpgp_card_sequoia::card::Open; + +use openpgp::parse::{stream::DecryptorBuilder, Parse}; +use openpgp::policy::StandardPolicy; +use openpgp::Cert; +use sequoia_openpgp as openpgp; + +fn main() -> Result<(), Box> { + let args = std::env::args().collect::>(); + + if args.len() < 3 { + eprintln!("Usage: decrypt card-ident pin-file cert-file"); + return Ok(()); + } + + let card_ident = &args[0]; + let pin_file = &args[1]; + let cert_file = &args[2]; + + let mut open = Open::open_card(PcscClient::open_by_ident(&card_ident)?)?; + + let pin = std::fs::read_to_string(pin_file)?; + + open.verify_user(&pin)?; + + let mut user = open.user_card().unwrap(); + + let p = StandardPolicy::new(); + let cert = Cert::from_file(cert_file)?; + let d = user.decryptor(&cert, &p)?; + let stdin = std::io::stdin(); + + let mut stdout = std::io::stdout(); + + let db = DecryptorBuilder::from_reader(stdin)?; + let mut decryptor = db.with_policy(&p, None, d)?; + + std::io::copy(&mut decryptor, &mut stdout)?; + + Ok(()) +} diff --git a/openpgp-card-apps/src/bin/detach-sign.rs b/openpgp-card-apps/src/bin/detach-sign.rs new file mode 100644 index 0000000..f48ebcc --- /dev/null +++ b/openpgp-card-apps/src/bin/detach-sign.rs @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use openpgp_card_pcsc::PcscClient; + +use openpgp_card_sequoia::card::Open; + +use openpgp::parse::Parse; +use openpgp::policy::StandardPolicy; +use openpgp::serialize::stream::{Armorer, Message, Signer}; +use openpgp::Cert; +use sequoia_openpgp as openpgp; + +fn main() -> Result<(), Box> { + let args = std::env::args().collect::>(); + + if args.len() < 3 { + eprintln!("Usage: detach-sign card-ident pin-file cert-file"); + return Ok(()); + } + + let test_card_ident = &args[0]; + let pin_file = &args[1]; + let cert_file = &args[2]; + + let mut open = + Open::open_card(PcscClient::open_by_ident(&test_card_ident)?)?; + + let pin = std::fs::read_to_string(pin_file)?; + + open.verify_user_for_signing(&pin)?; + + let mut user = open.signing_card().unwrap(); + + let p = StandardPolicy::new(); + let cert = Cert::from_file(cert_file)?; + let s = user.signer(&cert, &p)?; + + let stdout = std::io::stdout(); + + let message = Message::new(stdout); + + let message = Armorer::new(message).build()?; + + let signer = Signer::new(message, s); + + let mut signer = signer.detached().build()?; + + std::io::copy(&mut std::io::stdin(), &mut signer)?; + + signer.finalize()?; + + Ok(()) +}