diff --git a/card-functionality/Cargo.toml b/card-functionality/Cargo.toml index 2430f20..d2b8a5f 100644 --- a/card-functionality/Cargo.toml +++ b/card-functionality/Cargo.toml @@ -10,4 +10,7 @@ edition = "2018" [dependencies] openpgp-card = { path = "../openpgp-card" } -anyhow = "1" \ No newline at end of file +openpgp-card-sequoia = { path = "../openpgp-card-sequoia" } +sequoia-openpgp = "1.3" +anyhow = "1" +env_logger = "0.8" \ No newline at end of file diff --git a/card-functionality/data/rsa2k.sec b/card-functionality/data/rsa2k.sec new file mode 100644 index 0000000..a9398f5 --- /dev/null +++ b/card-functionality/data/rsa2k.sec @@ -0,0 +1,83 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +7aYYBzgV/of68E1HMFmY7A== +=JojA +-----END PGP PRIVATE KEY BLOCK----- diff --git a/card-functionality/src/main.rs b/card-functionality/src/main.rs index 040b00a..198e3de 100644 --- a/card-functionality/src/main.rs +++ b/card-functionality/src/main.rs @@ -28,9 +28,14 @@ use anyhow::Result; use std::collections::HashMap; +use sequoia_openpgp::parse::Parse; +use sequoia_openpgp::Cert; + use openpgp_card::apdu::PcscClient; use openpgp_card::card_app::CardApp; -use openpgp_card::{CardClientBox, Sex}; +use openpgp_card::{CardClientBox, KeyType, Sex}; + +mod util; #[derive(Debug)] enum TestResult { @@ -40,33 +45,81 @@ enum TestResult { type TestOutput = Vec; -/// run after each "upload keys", if key *was* uploaded (?) +/// Map: Card ident -> TestOutput +type TestsOutput = HashMap; + +/// Run after each "upload keys", if key *was* uploaded (?) fn test_decrypt() { // FIXME unimplemented!() } -/// run after each "upload keys", if key *was* uploaded (?) +/// Run after each "upload keys", if key *was* uploaded (?) fn test_sign() { // FIXME unimplemented!() } -fn test_upload_keys_general() { - // FIXME +fn check_key_upload_metadata( + ca: &mut CardApp, + meta: &[(String, u32)], +) -> Result<()> { + let ard = ca.get_app_data()?; + + // check fingerprints + let card_fp = CardApp::get_fingerprints(&ard)?; + + let sig = card_fp.signature().expect("signature fingerprint"); + assert_eq!(format!("{:X}", sig), meta[0].0); + + let dec = card_fp.decryption().expect("decryption fingerprint"); + assert_eq!(format!("{:X}", dec), meta[1].0); + + let auth = card_fp + .authentication() + .expect("authentication fingerprint"); + assert_eq!(format!("{:X}", auth), meta[2].0); - // check fingerprint - // get_algorithm_attributes // get_key_generation_times + let card_kg = CardApp::get_key_generation_times(&ard)?; + + let sig: u32 = + card_kg.signature().expect("signature creation time").into(); + assert_eq!(sig, meta[0].1); + + let dec: u32 = card_kg + .decryption() + .expect("decryption creation time") + .into(); + assert_eq!(dec, meta[1].1); + + let auth: u32 = card_kg + .authentication() + .expect("authentication creation time") + .into(); + assert_eq!(auth, meta[2].1); + + Ok(()) } -fn test_upload_keys_rsa() { +fn check_key_upload_algo_attrs() -> Result<()> { + // get_algorithm_attributes // FIXME - unimplemented!() - // upload key + Ok(()) +} - // test upload general - checks +fn test_upload_keys_rsa_2k(ca: &mut CardApp) -> Result { + let verify = ca.verify_pw3("12345678")?; + verify.check_ok()?; + + let cert = Cert::from_file("data/rsa2k.sec")?; + let meta = util::upload_subkeys(ca, &cert)?; + + check_key_upload_metadata(ca, &meta)?; + check_key_upload_algo_attrs()?; + + Ok(vec![]) } fn test_upload_keys_25519() { @@ -87,6 +140,11 @@ fn test_keygen() { unimplemented!() } +fn test_reset(ca: &mut CardApp) -> Result { + let res = ca.factory_reset()?; + Ok(vec![]) +} + /// Sets name, lang, sex, url; then reads the fields from the card and /// compares the values with the expected values. /// @@ -179,16 +237,22 @@ fn test_verify(ca: &mut CardApp) -> Result { fn run_test( cards: &[&str], t: fn(&mut CardApp) -> Result, -) -> Result> { +) -> Result { let mut out = HashMap::new(); for card in PcscClient::list_cards()? { let card_client = Box::new(card) as CardClientBox; + let mut ca = CardApp::new(card_client); + // Select OpenPGP applet let res = ca.select()?; res.check_ok()?; + // Set Card Capabilities (chaining, command length, ..) + let ard = ca.get_app_data()?; + ca = ca.init_caps(&ard)?; + let ard = ca.get_app_data()?; let app_id = CardApp::get_aid(&ard)?; @@ -196,7 +260,6 @@ fn run_test( println!("Running Test on {}:", app_id.ident()); let res = t(&mut ca); - println!("{:x?}", res); out.insert(app_id.ident(), res?); } @@ -206,15 +269,41 @@ fn run_test( } fn main() -> Result<()> { + env_logger::init(); + // list of card idents to runs the tests on let cards = vec![ - "0006:16019180", // Yubikey 5 - "0005:0000A835", // FLOSS Card 3.4 - "FFFE:57183146", // Rysim Gnuk (green) + "0006:16019180", /* Yubikey 5 */ + "0005:0000A835", /* FLOSS Card 3.4 */ + "FFFE:57183146", /* Gnuk Rysim (green) */ + + // "FFFE:4231EB6E", /* Gnuk FST */ ]; - let _verify_res = run_test(&cards, test_verify)?; - let _userdata_res = run_test(&cards, test_set_user_data)?; + // println!("reset"); + // let _ = run_test(&cards, test_reset)?; + // + // println!("verify"); + // let verify_out = run_test(&cards, test_verify)?; + // println!("{:x?}", verify_out); + // + // println!("set user data"); + // let userdata_out = run_test(&cards, test_set_user_data)?; + // println!("{:x?}", userdata_out); + + // upload RSA keys + println!("upload RSA2k key"); + let upload_out = run_test(&cards, test_upload_keys_rsa_2k)?; + println!("{:x?}", upload_out); + + // sign + // decrypt + + // upload 25519 keys + // sign + // decrypt + + // upload some key with pw Ok(()) } diff --git a/card-functionality/src/util.rs b/card-functionality/src/util.rs new file mode 100644 index 0000000..2286104 --- /dev/null +++ b/card-functionality/src/util.rs @@ -0,0 +1,72 @@ +use anyhow::{anyhow, Result}; + +use sequoia_openpgp::cert::amalgamation::key::ValidKeyAmalgamation; +use sequoia_openpgp::packet::key::{SecretParts, UnspecifiedRole}; +use sequoia_openpgp::policy::StandardPolicy; +use sequoia_openpgp::Cert; + +use openpgp_card::card_app::CardApp; +use openpgp_card::KeyType; +use openpgp_card_sequoia::vka_as_uploadable_key; +use std::time::SystemTime; + +pub const SP: &StandardPolicy = &StandardPolicy::new(); + +pub(crate) fn upload_subkeys( + ca: &mut CardApp, + cert: &Cert, +) -> Result> { + let mut out = vec![]; + let mut gentime = 0; + + for kt in [ + KeyType::Signing, + KeyType::Decryption, + KeyType::Authentication, + ] { + let vka = get_subkey(cert, kt)?; + + // store fingerprint as return-value + let fp = vka.fingerprint().to_hex(); + // store key creation time as return-value + let creation = vka + .creation_time() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() as u32; + + out.push((fp, creation)); + + // upload key + let cuk = vka_as_uploadable_key(vka, None); + let res = ca.upload_key(cuk, kt)?; + } + + Ok(out) +} + +fn get_subkey( + cert: &Cert, + key_type: KeyType, +) -> Result> { + // Find all suitable (sub)keys for key_type. + let mut valid_ka = cert + .keys() + .with_policy(SP, None) + .secret() + .alive() + .revoked(false); + valid_ka = match key_type { + KeyType::Decryption => valid_ka.for_storage_encryption(), + KeyType::Signing => valid_ka.for_signing(), + KeyType::Authentication => valid_ka.for_authentication(), + _ => return Err(anyhow!("Unexpected KeyType")), + }; + + // FIXME: for now, we just pick the first (sub)key from the list + if let Some(vka) = valid_ka.next() { + Ok(vka) + } else { + Err(anyhow!("No suitable (sub)key found")) + } +}