diff --git a/card-functionality/src/import.rs b/card-functionality/src/import.rs index 3a9d52a..0ab02e0 100644 --- a/card-functionality/src/import.rs +++ b/card-functionality/src/import.rs @@ -46,15 +46,23 @@ fn main() -> Result<()> { let upload_out = upload_res?; println!(" {:x?}", upload_out); + let key = std::fs::read_to_string(key) + .expect("Unable to read ciphertext"); + // decrypt print!(" Decrypt"); - let dec_out = - run_test(&mut card, test_decrypt, &[key, ciphertext])?; + let msg = std::fs::read_to_string(ciphertext).expect(&format![ + "Unable to read ciphertext from file {}", + ciphertext + ]); + + let dec_out = run_test(&mut card, test_decrypt, &[&key, &msg])?; println!(" {:x?}", dec_out); // sign print!(" Sign"); - let sign_out = run_test(&mut card, test_sign, &[key])?; + + let sign_out = run_test(&mut card, test_sign, &[&key])?; println!(" {:x?}", sign_out); } diff --git a/card-functionality/src/keygen.rs b/card-functionality/src/keygen.rs index 18a8544..94d10e8 100644 --- a/card-functionality/src/keygen.rs +++ b/card-functionality/src/keygen.rs @@ -2,9 +2,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use anyhow::Result; +use std::str::FromStr; use card_functionality::cards::TestConfig; use card_functionality::tests::*; +use card_functionality::util; +use sequoia_openpgp::Cert; fn main() -> Result<()> { env_logger::init(); @@ -28,8 +31,8 @@ fn main() -> Result<()> { println!("Reset"); let _ = run_test(&mut card, test_reset, &[])?; - // println!("Algo info"); - // let _ = run_test(&mut card, test_print_algo_info, &[])?; + println!("Algo info"); + let _ = run_test(&mut card, test_print_algo_info, &[])?; // Set user data because keygen expects a name (for the user id) println!("Set user data"); @@ -39,10 +42,22 @@ fn main() -> Result<()> { let res = run_test(&mut card, test_keygen, &[])?; if let TestResult::Text(cert) = &res[0] { - println!("cert\n{}", cert); - }; + // sign + print!(" Sign"); + let sign_out = run_test(&mut card, test_sign, &[cert])?; + println!(" {:x?}", sign_out); - // panic!(); + // decrypt + let c = Cert::from_str(cert)?; + let ciphertext = util::encrypt_to("Hello world!\n", &c)?; + + print!(" Decrypt"); + let dec_out = + run_test(&mut card, test_decrypt, &[cert, &ciphertext])?; + println!(" {:x?}", dec_out); + } else { + panic!("Didn't get back a Cert from test_keygen"); + }; println!(); } diff --git a/card-functionality/src/lib.rs b/card-functionality/src/lib.rs index eb6a235..5b0fbb1 100644 --- a/card-functionality/src/lib.rs +++ b/card-functionality/src/lib.rs @@ -3,4 +3,4 @@ pub mod cards; pub mod tests; -mod util; +pub mod util; diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index c17e62e..ca42b87 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -4,12 +4,17 @@ use anyhow::{Error, Result}; use std::convert::TryFrom; use std::convert::TryInto; +use std::str::FromStr; +use std::string::FromUtf8Error; use std::time::SystemTime; use thiserror::Error; +use sequoia_openpgp::packet::key::{KeyRole, PrimaryRole, SubordinateRole}; +use sequoia_openpgp::packet::signature::SignatureBuilder; +use sequoia_openpgp::packet::UserID; use sequoia_openpgp::parse::Parse; use sequoia_openpgp::serialize::SerializeInto; -use sequoia_openpgp::types::{SignatureType, Timestamp}; +use sequoia_openpgp::types::{KeyFlags, SignatureType, Timestamp}; use sequoia_openpgp::{Cert, Packet}; use openpgp_card::card_app::CardApp; @@ -17,14 +22,10 @@ use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError}; use openpgp_card::{ Algo, Curve, EccAttrs, EccType, KeyType, PublicKeyMaterial, RsaAttrs, Sex, }; +use openpgp_card_sequoia::signer::CardSigner; use crate::cards::TestCard; use crate::util; -use openpgp_card_sequoia::signer::CardSigner; -use sequoia_openpgp::packet::key::{KeyRole, PrimaryRole, SubordinateRole}; -use sequoia_openpgp::packet::signature::SignatureBuilder; -use sequoia_openpgp::packet::UserID; -use std::string::FromUtf8Error; #[derive(Debug)] pub enum TestResult { @@ -63,9 +64,8 @@ pub fn test_decrypt( "test_decrypt needs filenames for 'cert' and 'encrypted'" ); - let cert = Cert::from_file(param[0])?; - let msg = - std::fs::read_to_string(param[1]).expect("Unable to read ciphertext"); + let cert = Cert::from_str(param[0])?; + let msg = param[1].to_string(); let res = ca.verify_pw1("123456")?; res.check_ok()?; @@ -88,7 +88,7 @@ pub fn test_sign( let res = ca.verify_pw1_for_signing("123456")?; res.check_ok()?; - let cert = Cert::from_file(param[0])?; + let cert = Cert::from_str(param[0])?; let msg = "Hello world, I am signed."; let sig = openpgp_card_sequoia::sign(&mut ca, &cert, &mut msg.as_bytes())?; @@ -219,6 +219,8 @@ pub fn test_keygen( let verify = ca.verify_pw3("12345678")?; verify.check_ok()?; + // --------------- + // RSA 1024, e=17 let rsa1k = Algo::Rsa(RsaAttrs { len_n: 1024, @@ -227,12 +229,19 @@ pub fn test_keygen( }); // RSA 2048, e=17 - let rsa2k = Algo::Rsa(RsaAttrs { + let rsa2k_17 = Algo::Rsa(RsaAttrs { len_n: 2048, len_e: 17, import_format: 0, }); + // RSA 2048, e=32 + let rsa2k_32 = Algo::Rsa(RsaAttrs { + len_n: 2048, + len_e: 32, + import_format: 0, + }); + // RSA 3072, e=17 let rsa3k = Algo::Rsa(RsaAttrs { len_n: 3072, @@ -240,21 +249,28 @@ pub fn test_keygen( import_format: 0, }); + // RSA 4096, e=17 + let rsa4k_17 = Algo::Rsa(RsaAttrs { + len_n: 4096, + len_e: 17, + import_format: 0, + }); + // RSA 4096, e=32 - let rsa4k = Algo::Rsa(RsaAttrs { + let rsa4k_32 = Algo::Rsa(RsaAttrs { len_n: 4096, len_e: 32, import_format: 0, }); - // ed25519 sign + // ed25519 (sign) let ed25519 = Algo::Ecc(EccAttrs { ecc_type: EccType::EdDSA, curve: Curve::Ed25519, import_format: None, }); - // cv25519 dec + // cv25519 (dec) let cv25519 = Algo::Ecc(EccAttrs { ecc_type: EccType::ECDH, curve: Curve::Cv25519, @@ -275,24 +291,48 @@ pub fn test_keygen( import_format: None, }); - let fp = - |pkm: &PublicKeyMaterial, ts: SystemTime, kt: KeyType, algo: &Algo| { - // FIXME: store creation timestamp + // --------------- - let key = - openpgp_card_sequoia::public_key_material_to_key(pkm, kt, ts)?; + // -- RSA (e=17) [YK4, YK5] + // let dec_algo = &rsa2k_17; + // let sig_algo = &rsa2k_17; - let fp = key.fingerprint(); - let fp = fp.as_bytes(); - assert_eq!(fp.len(), 20); + // let dec_algo = &rsa4k_17; + // let sig_algo = &rsa4k_17; - Ok(fp.try_into().unwrap()) - }; + // -- RSA (e=32) [YK5, Floss3.4, Gnuk1.2] + // let dec_algo = &rsa2k_32; + // let sig_algo = &rsa2k_32; + + let dec_algo = &rsa4k_32; + let sig_algo = &rsa4k_32; + + // -- NIST 256 + // let dec_algo = &nist256_ecdh; + // let sig_algo = &nist256_ecdsa; + + // -- Curve 25519 + // let dec_algo = &cv25519; + // let sig_algo = &ed25519; + + // --------------- + + let fp = |pkm: &PublicKeyMaterial, ts: SystemTime, kt: KeyType| { + // FIXME: store creation timestamp + + let key = + openpgp_card_sequoia::public_key_material_to_key(pkm, kt, ts)?; + + let fp = key.fingerprint(); + let fp = fp.as_bytes(); + assert_eq!(fp.len(), 20); + + Ok(fp.try_into().unwrap()) + }; // ------ - let (pkm, ts) = - ca.generate_key(fp, KeyType::Signing, Some(&nist256_ecdsa))?; + let (pkm, ts) = ca.generate_key(fp, KeyType::Signing, Some(sig_algo))?; let key_sig = openpgp_card_sequoia::public_key_material_to_key( &pkm, KeyType::Signing, @@ -302,7 +342,7 @@ pub fn test_keygen( // ------ let (pkm, ts) = - ca.generate_key(fp, KeyType::Decryption, Some(&nist256_ecdh))?; + ca.generate_key(fp, KeyType::Decryption, Some(dec_algo))?; let key_dec = openpgp_card_sequoia::public_key_material_to_key( &pkm, KeyType::Decryption, @@ -312,7 +352,7 @@ pub fn test_keygen( // ------ let (pkm, ts) = - ca.generate_key(fp, KeyType::Authentication, Some(&nist256_ecdsa))?; + ca.generate_key(fp, KeyType::Authentication, Some(sig_algo))?; let key_aut = openpgp_card_sequoia::public_key_material_to_key( &pkm, KeyType::Authentication, diff --git a/card-functionality/src/util.rs b/card-functionality/src/util.rs index 7f18415..9ee9ef4 100644 --- a/card-functionality/src/util.rs +++ b/card-functionality/src/util.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use anyhow::{anyhow, Result}; +use std::io::Write; use std::time::SystemTime; -use sequoia_openpgp as openpgp; use sequoia_openpgp::cert::amalgamation::key::ValidKeyAmalgamation; use sequoia_openpgp::packet::key::{SecretParts, UnspecifiedRole}; use sequoia_openpgp::parse::stream::{ @@ -13,6 +13,9 @@ use sequoia_openpgp::parse::stream::{ }; use sequoia_openpgp::parse::Parse; use sequoia_openpgp::policy::StandardPolicy; +use sequoia_openpgp::serialize::stream::{ + Armorer, Encryptor, LiteralWriter, Message, +}; use sequoia_openpgp::Cert; use openpgp_card::card_app::CardApp; @@ -94,20 +97,23 @@ impl<'a> VHelper<'a> { impl<'a> VerificationHelper for VHelper<'a> { fn get_certs( &mut self, - _ids: &[openpgp::KeyHandle], - ) -> openpgp::Result> { + _ids: &[sequoia_openpgp::KeyHandle], + ) -> sequoia_openpgp::Result> { // Hand out our single Cert Ok(vec![self.cert.clone()]) } - fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> { + fn check( + &mut self, + structure: MessageStructure, + ) -> sequoia_openpgp::Result<()> { // We are interested in signatures over the data (level 0 signatures) if let Some(MessageLayer::SignatureGroup { results }) = structure.into_iter().next() { match results.into_iter().next() { Some(Ok(_)) => Ok(()), // Good signature. - Some(Err(e)) => Err(openpgp::Error::from(e).into()), + Some(Err(e)) => Err(sequoia_openpgp::Error::from(e).into()), None => Err(anyhow::anyhow!("No signature")), } } else { @@ -123,3 +129,42 @@ pub fn verify_sig(cert: &Cert, msg: &[u8], sig: &[u8]) -> Result { Ok(dv.verify_bytes(msg).is_ok()) } + +pub fn encrypt_to(cleartext: &str, cert: &Cert) -> Result { + let p = &StandardPolicy::new(); + + let mut recipients = Vec::new(); + + // Make sure we add at least one subkey from every + // certificate. + let mut found_one = false; + for key in cert + .keys() + .with_policy(p, None) + .supported() + .alive() + .revoked(false) + .for_transport_encryption() + { + recipients.push(key); + found_one = true; + } + + if !found_one { + return Err(anyhow::anyhow!( + "No suitable encryption subkey for {}", + cert + )); + } + + let mut sink = vec![]; + + let message = Message::new(&mut sink); + let message = Armorer::new(message).build()?; + let message = Encryptor::for_recipients(message, recipients).build()?; + let mut w = LiteralWriter::new(message).build()?; + w.write_all(cleartext.as_bytes())?; + w.finalize()?; + + Ok(String::from_utf8(sink).unwrap()) +} diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index a58e138..1217b03 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -643,7 +643,6 @@ impl CardApp { &PublicKeyMaterial, SystemTime, KeyType, - &Algo, ) -> Result<[u8; 20]>, key_type: KeyType, algo: Option<&Algo>, diff --git a/openpgp-card/src/keys.rs b/openpgp-card/src/keys.rs index e24d966..aa2d71f 100644 --- a/openpgp-card/src/keys.rs +++ b/openpgp-card/src/keys.rs @@ -26,7 +26,6 @@ pub(crate) fn gen_key_with_metadata( &PublicKeyMaterial, SystemTime, KeyType, - &Algo, ) -> Result<[u8; 20]>, key_type: KeyType, algo: Option<&Algo>, @@ -62,7 +61,7 @@ pub(crate) fn gen_key_with_metadata( card_app.set_creation_time(ts, key_type)?.check_ok()?; // calculate/store fingerprint - let fp = fp_from_pub(&pubkey, time, key_type, &algo)?; + let fp = fp_from_pub(&pubkey, time, key_type)?; card_app.set_fingerprint(fp, key_type)?.check_ok()?; Ok((pubkey, ts))