diff --git a/card-functionality/config/test-cards-example.toml b/card-functionality/config/test-cards-example.toml index 33a415f..51fa35d 100644 --- a/card-functionality/config/test-cards-example.toml +++ b/card-functionality/config/test-cards-example.toml @@ -10,22 +10,27 @@ # You should configure your set of test-card identifiers and store your # configuration in that location. # -# Usually you will want to test cards via the pcsc transport, which talks +# Usually you will want to test cards via the pcsc backend, which talks # to smartcards via the standard PCSC lite middleware # (https://pcsclite.apdu.fr/). # -# An alternative scdc transport is provided. It talks to smartcards via +# An alternative scdc backend is provided. It talks to smartcards via # scdaemon (which is part of the GnuPG system). -# The scdc transport is not recommended for use by default, it is offered +# The scdc backend is not recommended for use by default, it is offered # for experimental use only. # # (However, note that emulated Gnuk can currently only be accessed via scdc) -pcsc = [ - "0006:12345678", # Yubikey 5 - "0005:0000A835", # FLOSS Card 3.4 -] +[card.gnuk_emu] +#backend.pcsc = "FFFE:F1420A7A" +backend.scdc = "D276000124010200FFFEF1420A7A0000" +config.keygen = ["RSA2k/32", "NIST256", "Curve25519"] -scdc = [ - "D276000124010200FFFEF1420A7A0000", # Gnuk emulated -] +[card.yubikey5] +backend.pcsc = "0006:16019180" +#backend.scdc = "D2760001240103040006160191800000" +config.keygen = [ + "RSA2k/17", "RSA3k/17", "RSA4k/17", + "NIST256", "NIST384", "NIST521", + "Curve25519" +] \ No newline at end of file diff --git a/card-functionality/src/cards.rs b/card-functionality/src/cards.rs index 65b2cc7..1289b7b 100644 --- a/card-functionality/src/cards.rs +++ b/card-functionality/src/cards.rs @@ -6,12 +6,78 @@ use anyhow::{anyhow, Result}; use serde_derive::Deserialize; +use std::collections::BTreeMap; use openpgp_card::apdu::PcscClient; use openpgp_card::card_app::CardApp; use openpgp_card::CardClientBox; use openpgp_card_scdc::ScdClient; +#[derive(Debug, Deserialize)] +pub struct TestConfig { + card: BTreeMap, +} + +#[derive(Debug, Deserialize)] +pub struct Card { + backend: BTreeMap, + config: Config, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Config { + pub keygen: Vec, +} + +/// An "opened" card, via one particular backend, with test-metadata +#[derive(Debug)] +pub struct TestCardApp { + name: String, + tc: TestCard, + config: Config, +} + +impl TestCardApp { + pub(crate) fn get_card_app(&mut self) -> Result { + self.tc.open() + } + + pub fn get_config(&self) -> &Config { + &self.config + } +} + +impl TestConfig { + pub fn load(file: &str) -> Result { + let config_file = std::fs::read_to_string(file)?; + + let config: Self = toml::from_str(&config_file)?; + Ok(config) + } + + pub fn get_cards(self) -> Vec { + let mut cards = vec![]; + + for (name, card) in self.card { + for (backend, id) in &card.backend { + let tc: TestCard = match backend.as_str() { + "pcsc" => TestCard::Pcsc(id.to_string()), + "scdc" => TestCard::Scdc(id.to_string()), + _ => panic!("unexpected backend {}", backend), + }; + + cards.push(TestCardApp { + name: name.clone(), + tc, + config: card.config.clone(), + }) + } + } + + cards + } +} + #[derive(Debug)] pub enum TestCard { Pcsc(String), @@ -65,36 +131,3 @@ impl TestCard { } } } - -#[derive(Debug, Deserialize)] -pub struct TestConfig { - pcsc: Option>, - scdc: Option>, -} - -impl TestConfig { - pub fn open(file: &str) -> Result { - let config_file = std::fs::read_to_string(file)?; - - let config: Self = toml::from_str(&config_file)?; - Ok(config) - } - - pub fn get_cards(&self) -> Vec { - let mut cards = vec![]; - - if let Some(pcsc) = &self.pcsc { - for card in pcsc { - cards.push(TestCard::Pcsc(card.to_string())); - } - } - - if let Some(scdc) = &self.scdc { - for card in scdc { - cards.push(TestCard::Scdc(card.to_string())); - } - } - - cards - } -} diff --git a/card-functionality/src/import.rs b/card-functionality/src/import.rs index 1d5c0f4..6eda1ff 100644 --- a/card-functionality/src/import.rs +++ b/card-functionality/src/import.rs @@ -9,7 +9,7 @@ use card_functionality::tests::*; fn main() -> Result<()> { env_logger::init(); - let config = TestConfig::open("config/test-cards.toml")?; + let config = TestConfig::load("config/test-cards.toml")?; let cards = config.get_cards(); diff --git a/card-functionality/src/keygen.rs b/card-functionality/src/keygen.rs index 94d10e8..2a121d0 100644 --- a/card-functionality/src/keygen.rs +++ b/card-functionality/src/keygen.rs @@ -12,7 +12,7 @@ use sequoia_openpgp::Cert; fn main() -> Result<()> { env_logger::init(); - let config = TestConfig::open("config/test-cards.toml")?; + let config = TestConfig::load("config/test-cards.toml")?; let cards = config.get_cards(); @@ -31,34 +31,42 @@ 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"); let _ = run_test(&mut card, test_set_user_data, &[])?; - println!("Generate key"); - let res = run_test(&mut card, test_keygen, &[])?; - - if let TestResult::Text(cert) = &res[0] { - // sign - print!(" Sign"); - let sign_out = run_test(&mut card, test_sign, &[cert])?; - println!(" {:x?}", sign_out); - - // 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"); + let algos = { + let config = card.get_config(); + config.keygen.clone() }; + for algo in algos { + println!("Generate key [{}]", algo); + + let res = run_test(&mut card, test_keygen, &[&algo])?; + + if let TestResult::Text(cert) = &res[0] { + // sign + print!(" Sign"); + let sign_out = run_test(&mut card, test_sign, &[cert])?; + println!(" {:x?}", sign_out); + + // 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/other.rs b/card-functionality/src/other.rs index e0940cd..3eaa72c 100644 --- a/card-functionality/src/other.rs +++ b/card-functionality/src/other.rs @@ -9,7 +9,7 @@ use card_functionality::tests::*; fn main() -> Result<()> { env_logger::init(); - let config = TestConfig::open("config/test-cards.toml")?; + let config = TestConfig::load("config/test-cards.toml")?; let cards = config.get_cards(); diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index d5b03ba..5473979 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -2,31 +2,23 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 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, PublicParts, SubordinateRole, UnspecifiedRole, -}; -use sequoia_openpgp::packet::signature::SignatureBuilder; -use sequoia_openpgp::packet::{Key, UserID}; use sequoia_openpgp::parse::Parse; use sequoia_openpgp::serialize::SerializeInto; -use sequoia_openpgp::types::{KeyFlags, SignatureType, Timestamp}; -use sequoia_openpgp::{Cert, Packet}; +use sequoia_openpgp::types::Timestamp; +use sequoia_openpgp::Cert; use openpgp_card::card_app::CardApp; use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError}; -use openpgp_card::{ - Algo, Curve, EccAttrs, EccType, KeyType, PublicKeyMaterial, RsaAttrs, Sex, +use openpgp_card::{AlgoSimple, KeyType, Sex}; +use openpgp_card_sequoia::{ + make_cert, public_key_material_to_key, public_to_fingerprint, }; -use openpgp_card_sequoia::{make_cert, signer::CardSigner}; -use crate::cards::TestCard; +use crate::cards::TestCardApp; use crate::util; #[derive(Debug)] @@ -216,151 +208,48 @@ pub fn test_upload_keys( /// Generate keys for each of the three KeyTypes pub fn test_keygen( ca: &mut CardApp, - _param: &[&str], + param: &[&str], ) -> Result { let verify = ca.verify_pw3("12345678")?; verify.check_ok()?; - // --------------- + // Generate all three subkeys on card + let algo = param[0]; - // RSA 1024, e=17 - let rsa1k = Algo::Rsa(RsaAttrs { - len_n: 1024, - len_e: 17, - import_format: 0, - }); + let alg = AlgoSimple::from(algo); - // RSA 2048, e=17 - 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, - len_e: 17, - 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_32 = Algo::Rsa(RsaAttrs { - len_n: 4096, - len_e: 32, - import_format: 0, - }); - - // ed25519 (sign) - let ed25519 = Algo::Ecc(EccAttrs { - ecc_type: EccType::EdDSA, - curve: Curve::Ed25519, - import_format: None, - }); - - // cv25519 (dec) - let cv25519 = Algo::Ecc(EccAttrs { - ecc_type: EccType::ECDH, - curve: Curve::Cv25519, - import_format: None, - }); - - // nist256 sig, auth - let nist256_ecdsa = Algo::Ecc(EccAttrs { - ecc_type: EccType::ECDSA, - curve: Curve::NistP256r1, - import_format: None, - }); - - // nist256 dec - let nist256_ecdh = Algo::Ecc(EccAttrs { - ecc_type: EccType::ECDH, - curve: Curve::NistP256r1, - import_format: None, - }); - - // --------------- - - // -- RSA (e=17) [YK4, YK5] - // let dec_algo = &rsa2k_17; - // let sig_algo = &rsa2k_17; - - // let dec_algo = &rsa4k_17; - // let sig_algo = &rsa4k_17; - - // -- 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(sig_algo))?; - let key_sig = openpgp_card_sequoia::public_key_material_to_key( + let (pkm, ts) = + ca.generate_key_simple(public_to_fingerprint, KeyType::Signing, alg)?; + let key_sig = public_key_material_to_key( &pkm, KeyType::Signing, - SystemTime::from(Timestamp::from(ts)), + Timestamp::from(ts).into(), )?; - // ------ - - let (pkm, ts) = - ca.generate_key(fp, KeyType::Decryption, Some(dec_algo))?; - let key_dec = openpgp_card_sequoia::public_key_material_to_key( + let (pkm, ts) = ca.generate_key_simple( + public_to_fingerprint, + KeyType::Decryption, + alg, + )?; + let key_dec = public_key_material_to_key( &pkm, KeyType::Decryption, - SystemTime::from(Timestamp::from(ts)), + Timestamp::from(ts).into(), )?; - // ------ - - let (pkm, ts) = - ca.generate_key(fp, KeyType::Authentication, Some(sig_algo))?; - let key_aut = openpgp_card_sequoia::public_key_material_to_key( + let (pkm, ts) = ca.generate_key_simple( + public_to_fingerprint, + KeyType::Authentication, + alg, + )?; + let key_aut = public_key_material_to_key( &pkm, KeyType::Authentication, - SystemTime::from(Timestamp::from(ts)), + Timestamp::from(ts).into(), )?; + // Generate a Cert for this set of generated keys + let cert = make_cert(ca, key_sig, key_dec, key_aut)?; let armored = String::from_utf8(cert.armored().to_vec()?)?; @@ -525,11 +414,11 @@ pub fn test_verify( } pub fn run_test( - card: &mut TestCard, + card: &mut TestCardApp, t: fn(&mut CardApp, &[&str]) -> Result, param: &[&str], ) -> Result { - let mut ca = card.open()?; + let mut ca = card.get_card_app()?; let ard = ca.get_app_data()?; let _app_id = CardApp::get_aid(&ard)?; diff --git a/openpgp-card-sequoia/src/lib.rs b/openpgp-card-sequoia/src/lib.rs index a4d511b..d756944 100644 --- a/openpgp-card-sequoia/src/lib.rs +++ b/openpgp-card-sequoia/src/lib.rs @@ -531,3 +531,21 @@ pub fn sign( String::from_utf8(buffer).context("Failed to convert signature to utf8") } + +/// Mapping function to get a fingerprint from "PublicKeyMaterial + +/// timestamp + KeyType" (intended for use with `CardApp.generate_key()`). +pub fn public_to_fingerprint( + pkm: &PublicKeyMaterial, + ts: SystemTime, + kt: KeyType, +) -> Result<[u8; 20]> { + // Transform PublicKeyMaterial into a Sequoia Key + let key = public_key_material_to_key(pkm, kt, ts)?; + + // Get fingerprint from the Sequoia Key + let fp = key.fingerprint(); + let fp = fp.as_bytes(); + + assert_eq!(fp.len(), 20); + Ok(fp.try_into()?) +}