card-functionality: adjust to backend and openpgp-card-sequoia API changes

This commit is contained in:
Heiko Schaefer 2023-08-17 15:03:18 +02:00
parent ccf605f086
commit 746f2f647d
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
9 changed files with 224 additions and 193 deletions

View file

@ -9,5 +9,5 @@ members = [
"pcsc", "pcsc",
"scdc", "scdc",
"openpgp-card-examples", "openpgp-card-examples",
# "card-functionality", "card-functionality",
] ]

View file

@ -31,8 +31,8 @@ path = "src/list-cards.rs"
[dependencies] [dependencies]
openpgp-card = { path = "../openpgp-card" } openpgp-card = { path = "../openpgp-card" }
openpgp-card-sequoia = { path = "../openpgp-card-sequoia" } openpgp-card-sequoia = { path = "../openpgp-card-sequoia" }
openpgp-card-scdc = { path = "../scdc" } card-backend-scdc = { path = "../scdc" }
openpgp-card-pcsc = { path = "../pcsc" } card-backend-pcsc = { path = "../pcsc" }
pcsc = "2.7" pcsc = "2.7"
sequoia-openpgp = "1.3" sequoia-openpgp = "1.3"
anyhow = "1" anyhow = "1"

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021-2023 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
//! Wrapping of cards for tests. Open a list of cards, based on a //! Wrapping of cards for tests. Open a list of cards, based on a
@ -7,13 +7,13 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use anyhow::Result; use anyhow::Result;
use openpgp_card::{CardBackend, Error}; use card_backend_pcsc::PcscBackend;
use openpgp_card_pcsc::PcscBackend; use card_backend_scdc::ScdBackend;
use openpgp_card_scdc::ScdBackend; use openpgp_card::Error;
use pcsc::ShareMode; use openpgp_card_sequoia::state::Open;
use serde_derive::Deserialize; use serde_derive::Deserialize;
const SHARE_MODE: Option<ShareMode> = Some(ShareMode::Shared); // const SHARE_MODE: Option<ShareMode> = Some(ShareMode::Shared);
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct TestConfig { pub struct TestConfig {
@ -41,7 +41,7 @@ pub struct TestCardData {
} }
impl TestCardData { impl TestCardData {
pub(crate) fn get_card(&self) -> Result<Box<dyn CardBackend + Send + Sync>> { pub fn get_card(&self) -> Result<openpgp_card_sequoia::Card<Open>> {
self.tc.open() self.tc.open()
} }
@ -92,7 +92,7 @@ pub enum TestCard {
} }
impl TestCard { impl TestCard {
pub fn open(&self) -> Result<Box<dyn CardBackend + Send + Sync>> { pub fn open(&self) -> Result<openpgp_card_sequoia::Card<Open>> {
match self { match self {
Self::Pcsc(ident) => { Self::Pcsc(ident) => {
// Attempt to shutdown SCD, if it is running. // Attempt to shutdown SCD, if it is running.
@ -103,24 +103,39 @@ impl TestCard {
// Make three attempts to open the card before failing // Make three attempts to open the card before failing
// (this can be useful in ShareMode::Exclusive) // (this can be useful in ShareMode::Exclusive)
let mut i = 1; let mut i = 1;
let card: Result<Box<dyn CardBackend + Send + Sync>, Error> = loop { let card: Result<openpgp_card_sequoia::Card<Open>, Error> = loop {
let res = PcscBackend::open_by_ident(ident, SHARE_MODE); i += 1;
if i == 3 { let cards = PcscBackend::card_backends(None)?;
if let Ok(res) = res { let res = openpgp_card_sequoia::Card::<Open>::open_by_ident(cards, ident);
break Ok(Box::new(res));
println!("Got result for card: {}", ident);
if let Err(e) = &res {
println!("Result is an error: {:x?}", e);
} else {
println!("Result is a happy card");
} }
if let Ok(res) = res {
break Ok(res);
}
if i > 3 {
break Err(Error::NotFound(format!("Couldn't open card {}", ident)));
} }
// sleep for 100ms // sleep for 100ms
println!("Will sleep for 100ms");
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(100));
i += 1;
}; };
Ok(card?) Ok(card?)
} }
Self::Scdc(serial) => Ok(Box::new(ScdBackend::open_by_serial(None, serial)?)), Self::Scdc(serial) => {
let backend = ScdBackend::open_by_serial(None, serial)?;
Ok(openpgp_card_sequoia::Card::<Open>::new(backend)?)
}
} }
} }
} }

View file

@ -7,6 +7,8 @@ use anyhow::Result;
use card_functionality::cards::TestConfig; use card_functionality::cards::TestConfig;
use card_functionality::tests::*; use card_functionality::tests::*;
use card_functionality::util; use card_functionality::util;
use openpgp_card_sequoia::state::Open;
use openpgp_card_sequoia::Card;
use sequoia_openpgp::Cert; use sequoia_openpgp::Cert;
fn main() -> Result<()> { fn main() -> Result<()> {
@ -21,16 +23,23 @@ fn main() -> Result<()> {
let cards = config.into_cardapps(); let cards = config.into_cardapps();
for mut card in cards { for card in cards {
println!("** Run tests on card '{}' **", card.get_name()); println!("** Run tests on card '{}' **", card.get_name());
let mut c: Card<Open> = card.get_card()?;
println!(" -> Card opened");
println!("Reset"); println!("Reset");
let _ = run_test(&mut card, test_reset, &[])?; let _ = run_test(&mut c, test_reset, &[])?;
print!("Set user data"); print!("Set user data");
let userdata_out = run_test(&mut card, test_set_user_data, &[])?; let userdata_out = run_test(&mut c, test_set_user_data, &[])?;
println!(" {userdata_out:x?}"); println!(" {userdata_out:x?}");
println!("Set login data");
let login_data_out = run_test(&mut c, test_set_login_data, &[])?;
println!(" {login_data_out:x?}");
let key_files = { let key_files = {
let config = card.get_config(); let config = card.get_config();
if let Some(import) = &config.import { if let Some(import) = &config.import {
@ -40,14 +49,10 @@ fn main() -> Result<()> {
} }
}; };
println!("Set login data");
let login_data_out = run_test(&mut card, test_set_login_data, &[])?;
println!(" {login_data_out:x?}");
for key_file in &key_files { for key_file in &key_files {
// upload keys // upload keys
print!("Upload key '{key_file}'"); print!("Upload key '{key_file}'");
let upload_res = run_test(&mut card, test_upload_keys, &[key_file]); let upload_res = run_test(&mut c, test_upload_keys, &[key_file]);
if let Err(TestError::KeyUploadError(_file, err)) = &upload_res { if let Err(TestError::KeyUploadError(_file, err)) = &upload_res {
// The card doesn't support this key type, so skip to the // The card doesn't support this key type, so skip to the
@ -66,16 +71,16 @@ fn main() -> Result<()> {
// decrypt // decrypt
print!(" Decrypt"); print!(" Decrypt");
let c = Cert::from_str(&key)?; let cert = Cert::from_str(&key)?;
let ciphertext = util::encrypt_to("Hello world!\n", &c)?; let ciphertext = util::encrypt_to("Hello world!\n", &cert)?;
let dec_out = run_test(&mut card, test_decrypt, &[&key, &ciphertext])?; let dec_out = run_test(&mut c, test_decrypt, &[&key, &ciphertext])?;
println!(" {dec_out:x?}"); println!(" {dec_out:x?}");
// sign // sign
print!(" Sign"); print!(" Sign");
let sign_out = run_test(&mut card, test_sign, &[&key])?; let sign_out = run_test(&mut c, test_sign, &[&key])?;
println!(" {sign_out:x?}"); println!(" {sign_out:x?}");
} }

View file

@ -7,6 +7,8 @@ use anyhow::Result;
use card_functionality::cards::TestConfig; use card_functionality::cards::TestConfig;
use card_functionality::tests::*; use card_functionality::tests::*;
use card_functionality::util; use card_functionality::util;
use openpgp_card_sequoia::state::Open;
use openpgp_card_sequoia::Card;
use sequoia_openpgp::Cert; use sequoia_openpgp::Cert;
fn main() -> Result<()> { fn main() -> Result<()> {
@ -21,9 +23,12 @@ fn main() -> Result<()> {
let cards = config.into_cardapps(); let cards = config.into_cardapps();
for mut card in cards { for card in cards {
println!("** Run tests on card {} **", card.get_name()); println!("** Run tests on card {} **", card.get_name());
let mut c: Card<Open> = card.get_card()?;
println!(" -> Card opened");
// println!("Get pubkey"); // println!("Get pubkey");
// let _ = run_test(&mut card, test_get_pub, &[])?; // let _ = run_test(&mut card, test_get_pub, &[])?;
// //
@ -34,14 +39,14 @@ fn main() -> Result<()> {
// // continue; // only print caps // // continue; // only print caps
println!("Reset"); println!("Reset");
let _ = run_test(&mut card, test_reset, &[])?; let _ = run_test(&mut c, test_reset, &[])?;
// println!("Algo info"); // println!("Algo info");
// let _ = run_test(&mut card, test_print_algo_info, &[])?; // let _ = run_test(&mut card, test_print_algo_info, &[])?;
// Set user data because keygen expects a name (for the user id) // Set user data because keygen expects a name (for the user id)
println!("Set user data"); println!("Set user data");
let _ = run_test(&mut card, test_set_user_data, &[])?; let _ = run_test(&mut c, test_set_user_data, &[])?;
let algos = { let algos = {
let config = card.get_config(); let config = card.get_config();
@ -55,20 +60,20 @@ fn main() -> Result<()> {
for algo in algos { for algo in algos {
println!("Generate key [{algo}]"); println!("Generate key [{algo}]");
let res = run_test(&mut card, test_keygen, &[&algo])?; let res = run_test(&mut c, test_keygen, &[&algo])?;
if let TestResult::Text(cert) = &res[0] { if let TestResult::Text(cert_str) = &res[0] {
// sign // sign
print!(" Sign"); print!(" Sign");
let sign_out = run_test(&mut card, test_sign, &[cert])?; let sign_out = run_test(&mut c, test_sign, &[cert_str])?;
println!(" {sign_out:x?}"); println!(" {sign_out:x?}");
// decrypt // decrypt
let c = Cert::from_str(cert)?; let cert = Cert::from_str(cert_str)?;
let ciphertext = util::encrypt_to("Hello world!\n", &c)?; let ciphertext = util::encrypt_to("Hello world!\n", &cert)?;
print!(" Decrypt"); print!(" Decrypt");
let dec_out = run_test(&mut card, test_decrypt, &[cert, &ciphertext])?; let dec_out = run_test(&mut c, test_decrypt, &[cert_str, &ciphertext])?;
println!(" {dec_out:x?}"); println!(" {dec_out:x?}");
} else { } else {
panic!("Didn't get back a Cert from test_keygen"); panic!("Didn't get back a Cert from test_keygen");

View file

@ -2,14 +2,15 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::Result; use anyhow::Result;
use openpgp_card_pcsc::PcscBackend; use card_backend_pcsc::PcscBackend;
use openpgp_card_sequoia::{state::Open, Card}; use openpgp_card_sequoia::{state::Open, Card};
fn main() -> Result<()> { fn main() -> Result<()> {
println!("The following OpenPGP cards are connected to your system:"); println!("The following OpenPGP cards are connected to your system:");
for backend in PcscBackend::cards(None)? { for backend in PcscBackend::cards(None)? {
let mut card: Card<Open> = backend.into(); let mut card: Card<Open> = Card::<Open>::new(backend?)?;
println!(" {}", card.transaction()?.application_identifier()?.ident()); println!(" {}", card.transaction()?.application_identifier()?.ident());
} }

View file

@ -4,6 +4,8 @@
use anyhow::Result; use anyhow::Result;
use card_functionality::cards::TestConfig; use card_functionality::cards::TestConfig;
use card_functionality::tests::*; use card_functionality::tests::*;
use openpgp_card_sequoia::state::Open;
use openpgp_card_sequoia::Card;
fn main() -> Result<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();
@ -12,9 +14,11 @@ fn main() -> Result<()> {
let cards = config.into_cardapps(); let cards = config.into_cardapps();
for mut card in cards { for card in cards {
println!("** Run tests on card '{}' **", card.get_name()); println!("** Run tests on card '{}' **", card.get_name());
let mut c: Card<Open> = card.get_card()?;
// println!("Caps"); // println!("Caps");
// let _ = run_test(&mut card, test_print_caps, &[])?; // let _ = run_test(&mut card, test_print_caps, &[])?;
// continue; // only print caps // continue; // only print caps
@ -23,7 +27,7 @@ fn main() -> Result<()> {
// let _ = run_test(&mut card, test_print_algo_info, &[])?; // let _ = run_test(&mut card, test_print_algo_info, &[])?;
println!("Reset"); println!("Reset");
let _ = run_test(&mut card, test_reset, &[])?; let _ = run_test(&mut c, test_reset, &[])?;
// --- // ---

View file

@ -8,12 +8,13 @@ use std::string::FromUtf8Error;
use anyhow::Result; use anyhow::Result;
use openpgp_card::algorithm::AlgoSimple; use openpgp_card::algorithm::AlgoSimple;
use openpgp_card::card_do::{KeyGenerationTime, Sex}; use openpgp_card::card_do::{KeyGenerationTime, Sex};
use openpgp_card::{Error, KeyType, OpenPgp, OpenPgpTransaction, StatusBytes}; use openpgp_card::{Error, KeyType, OpenPgp, StatusBytes};
use openpgp_card_sequoia::sq_util; use openpgp_card_sequoia::sq_util;
use openpgp_card_sequoia::state::{Admin, Open};
use openpgp_card_sequoia::util::{ use openpgp_card_sequoia::util::{
make_cert, public_key_material_and_fp_to_key, public_key_material_to_key, make_cert, public_key_material_and_fp_to_key, public_key_material_to_key,
}; };
use openpgp_card_sequoia::{state::Transaction, Card}; use openpgp_card_sequoia::Card;
use sequoia_openpgp::parse::Parse; use sequoia_openpgp::parse::Parse;
use sequoia_openpgp::policy::StandardPolicy; use sequoia_openpgp::policy::StandardPolicy;
use sequoia_openpgp::serialize::SerializeInto; use sequoia_openpgp::serialize::SerializeInto;
@ -21,7 +22,6 @@ use sequoia_openpgp::types::{HashAlgorithm, SymmetricAlgorithm};
use sequoia_openpgp::Cert; use sequoia_openpgp::Cert;
use thiserror; use thiserror;
use crate::cards::TestCardData;
use crate::util; use crate::util;
#[derive(Debug)] #[derive(Debug)]
@ -52,9 +52,7 @@ pub enum TestError {
} }
/// Run after each "upload keys", if key *was* uploaded (?) /// Run after each "upload keys", if key *was* uploaded (?)
pub fn test_decrypt(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_decrypt(card: &mut Card<Open>, param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?;
assert_eq!( assert_eq!(
param.len(), param.len(),
2, 2,
@ -63,13 +61,11 @@ pub fn test_decrypt(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput, Tes
let msg = param[1].to_string(); let msg = param[1].to_string();
pgpt.verify_pw1_user(b"123456")?;
let p = StandardPolicy::new(); let p = StandardPolicy::new();
let mut transaction = Card::<Transaction>::new(pgpt)?; let mut transaction = card.transaction()?;
let mut user = transaction.user_card().unwrap(); let mut user = transaction.to_user_card("123456")?;
let d = user.decryptor(&|| {})?; let d = user.decryptor(&|| {})?;
let res = sq_util::decrypt(d, msg.into_bytes(), &p)?; let res = sq_util::decrypt(d, msg.into_bytes(), &p)?;
@ -81,18 +77,14 @@ pub fn test_decrypt(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput, Tes
} }
/// Run after each "upload keys", if key *was* uploaded (?) /// Run after each "upload keys", if key *was* uploaded (?)
pub fn test_sign(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_sign(card: &mut Card<Open>, param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?;
assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'"); assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'");
pgpt.verify_pw1_sign(b"123456")?;
let cert = Cert::from_str(param[0])?; let cert = Cert::from_str(param[0])?;
let mut transaction = Card::<Transaction>::new(pgpt)?; let mut transaction = card.transaction()?;
let mut sign = transaction.signing_card().unwrap(); let mut sign = transaction.to_signing_card("123456").unwrap();
let s = sign.signer(&|| {})?; let s = sign.signer(&|| {})?;
let msg = "Hello world, I am signed."; let msg = "Hello world, I am signed.";
@ -105,13 +97,13 @@ pub fn test_sign(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput, TestEr
} }
fn check_key_upload_metadata( fn check_key_upload_metadata(
pgpt: &mut OpenPgpTransaction, admin: &mut Card<Admin>,
meta: &[(String, KeyGenerationTime)], meta: &[(String, KeyGenerationTime)],
) -> Result<()> { ) -> Result<()> {
let ard = pgpt.application_related_data()?; admin.as_transaction().reload_ard()?;
// check fingerprints // check fingerprints
let card_fp = ard.fingerprints()?; let card_fp = admin.as_transaction().fingerprints()?;
let sig = card_fp.signature().expect("signature fingerprint"); let sig = card_fp.signature().expect("signature fingerprint");
assert_eq!(format!("{sig:X}"), meta[0].0); assert_eq!(format!("{sig:X}"), meta[0].0);
@ -125,7 +117,7 @@ fn check_key_upload_metadata(
assert_eq!(format!("{auth:X}"), meta[2].0); assert_eq!(format!("{auth:X}"), meta[2].0);
// get_key_generation_times // get_key_generation_times
let card_kg = ard.key_generation_times()?; let card_kg = admin.as_transaction().key_generation_times()?;
let sig = card_kg.signature().expect("signature creation time"); let sig = card_kg.signature().expect("signature creation time");
assert_eq!(sig, &meta[0].1); assert_eq!(sig, &meta[0].1);
@ -186,8 +178,8 @@ pub fn test_print_algo_info(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOu
Ok(vec![]) Ok(vec![])
} }
pub fn test_upload_keys(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_upload_keys(card: &mut Card<Open>, param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
assert_eq!( assert_eq!(
param.len(), param.len(),
@ -195,16 +187,15 @@ pub fn test_upload_keys(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput,
"test_upload_keys needs a filename for 'cert'" "test_upload_keys needs a filename for 'cert'"
); );
pgpt.verify_pw3(b"12345678")?;
let cert = Cert::from_file(param[0])?; let cert = Cert::from_file(param[0])?;
let p = StandardPolicy::new(); let p = StandardPolicy::new();
let meta = util::upload_subkeys(&mut pgpt, &cert, &p) let mut admin = transaction.to_admin_card(b"12345678")?;
let meta = util::upload_subkeys(&mut admin, &cert, &p)
.map_err(|e| TestError::KeyUploadError(param[0].to_string(), e))?; .map_err(|e| TestError::KeyUploadError(param[0].to_string(), e))?;
check_key_upload_metadata(&mut pgpt, &meta)?; check_key_upload_metadata(&mut admin, &meta)?;
// FIXME: implement // FIXME: implement
check_key_upload_algo_attrs()?; check_key_upload_algo_attrs()?;
@ -213,12 +204,11 @@ pub fn test_upload_keys(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput,
} }
/// Generate keys for each of the three KeyTypes /// Generate keys for each of the three KeyTypes
pub fn test_keygen(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_keygen(card: &mut Card<Open>, param: &[&str]) -> Result<TestOutput, TestError> {
let pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
let mut admin = transaction
let mut transaction = Card::<Transaction>::new(pgpt)?; .to_admin_card("12345678")
transaction.verify_admin(b"12345678")?; .expect("Couldn't get Admin card");
let mut admin = transaction.admin_card().expect("Couldn't get Admin card");
// Generate all three subkeys on card // Generate all three subkeys on card
let algo = param[0]; let algo = param[0];
@ -262,16 +252,15 @@ pub fn test_keygen(pgp: &mut OpenPgp, param: &[&str]) -> Result<TestOutput, Test
} }
/// Construct public key based on data from the card /// Construct public key based on data from the card
pub fn test_get_pub(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_get_pub(mut card: Card<Open>, _param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
let ard = pgpt.application_related_data()?; let times = transaction.key_generation_times()?;
let times = ard.key_generation_times()?; let fps = transaction.fingerprints()?;
let fps = ard.fingerprints()?;
// -- // --
let sig = pgpt.public_key(KeyType::Signing)?; let sig = transaction.public_key_material(KeyType::Signing)?;
let ts = times.signature().unwrap().get().into(); let ts = times.signature().unwrap().get().into();
let key = let key =
public_key_material_and_fp_to_key(&sig, KeyType::Signing, &ts, fps.signature().unwrap())?; public_key_material_and_fp_to_key(&sig, KeyType::Signing, &ts, fps.signature().unwrap())?;
@ -280,7 +269,7 @@ pub fn test_get_pub(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, Te
// -- // --
let dec = pgpt.public_key(KeyType::Decryption)?; let dec = transaction.public_key_material(KeyType::Decryption)?;
let ts = times.decryption().unwrap().get().into(); let ts = times.decryption().unwrap().get().into();
let key = public_key_material_and_fp_to_key( let key = public_key_material_and_fp_to_key(
&dec, &dec,
@ -293,7 +282,7 @@ pub fn test_get_pub(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, Te
// -- // --
let auth = pgpt.public_key(KeyType::Authentication)?; let auth = transaction.public_key_material(KeyType::Authentication)?;
let ts = times.authentication().unwrap().get().into(); let ts = times.authentication().unwrap().get().into();
let key = public_key_material_and_fp_to_key( let key = public_key_material_and_fp_to_key(
&auth, &auth,
@ -307,10 +296,10 @@ pub fn test_get_pub(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, Te
Ok(vec![]) Ok(vec![])
} }
pub fn test_reset(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_reset(card: &mut Card<Open>, _param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
pgpt.factory_reset()?; transaction.factory_reset()?;
Ok(vec![]) Ok(vec![])
} }
@ -319,25 +308,28 @@ pub fn test_reset(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, Test
/// ///
/// Returns an empty TestOutput, throws errors for unexpected Status codes /// Returns an empty TestOutput, throws errors for unexpected Status codes
/// and for unequal field values. /// and for unequal field values.
pub fn test_set_user_data(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_set_user_data(card: &mut Card<Open>, _param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
pgpt.verify_pw3(b"12345678")?; let mut admin = transaction.to_admin_card("12345678")?;
// name // name
pgpt.set_name(b"Bar<<Foo")?; admin.set_name("Bar<<Foo")?;
// lang // lang
pgpt.set_lang(&[['d', 'e'].into(), ['e', 'n'].into()])?; admin.set_lang(&[['d', 'e'].into(), ['e', 'n'].into()])?;
// sex // sex
pgpt.set_sex(Sex::Female)?; admin.set_sex(Sex::Female)?;
// url // url
pgpt.set_url(b"https://duckduckgo.com/")?; admin.set_url("https://duckduckgo.com/")?;
// read all the fields back again, expect equal data // reload application releated data
let ch = pgpt.cardholder_related_data()?; transaction.reload_ard()?;
// compare the reloaded fields, expect equal data
let ch = transaction.cardholder_related_data()?;
assert_eq!(ch.name(), Some("Bar<<Foo".as_bytes())); assert_eq!(ch.name(), Some("Bar<<Foo".as_bytes()));
assert_eq!( assert_eq!(
@ -346,59 +338,62 @@ pub fn test_set_user_data(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutp
); );
assert_eq!(ch.sex(), Some(Sex::Female)); assert_eq!(ch.sex(), Some(Sex::Female));
let url = pgpt.url()?; let url = transaction.url()?;
assert_eq!(&url, b"https://duckduckgo.com/"); assert_eq!(&url, "https://duckduckgo.com/");
Ok(vec![]) Ok(vec![])
} }
pub fn test_set_login_data(pgp: &mut OpenPgp, _params: &[&str]) -> Result<TestOutput, TestError> { pub fn test_set_login_data(
let mut pgpt = pgp.transaction()?; card: &mut Card<Open>,
_params: &[&str],
) -> std::result::Result<TestOutput, TestError> {
let mut transaction = card.transaction()?;
pgpt.verify_pw3(b"12345678")?; let mut admin = transaction.to_admin_card("12345678")?;
let test_login = b"someone@somewhere.com"; let test_login = "someone@somewhere.com";
pgpt.set_login(test_login)?; admin.set_login_data(test_login)?;
// Read the previously set login data // Read the previously set login data
let read_login_data = pgpt.login_data()?; let read_login_data = transaction.login_data()?;
assert_eq!(read_login_data, test_login.to_vec()); assert_eq!(read_login_data, test_login);
Ok(vec![]) Ok(vec![])
} }
pub fn test_private_data(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, TestError> { // pub fn test_private_data(mut card: Card<Open>, _param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; // let mut transaction = card.transaction()?;
//
let out = vec![]; // let out = vec![];
//
println!(); // println!();
//
let d = pgpt.private_use_do(1)?; // let d = transaction.private_use_do(1)?;
println!("data 1 {d:?}"); // println!("data 1 {d:?}");
//
pgpt.verify_pw1_user(b"123456")?; // transaction.verify_pw1_user(b"123456")?;
//
pgpt.set_private_use_do(1, "Foo bar1!".as_bytes().to_vec())?; // transaction.set_private_use_do(1, "Foo bar1!".as_bytes().to_vec())?;
pgpt.set_private_use_do(3, "Foo bar3!".as_bytes().to_vec())?; // transaction.set_private_use_do(3, "Foo bar3!".as_bytes().to_vec())?;
//
pgpt.verify_pw3(b"12345678")?; // transaction.verify_pw3(b"12345678")?;
//
pgpt.set_private_use_do(2, "Foo bar2!".as_bytes().to_vec())?; // transaction.set_private_use_do(2, "Foo bar2!".as_bytes().to_vec())?;
pgpt.set_private_use_do(4, "Foo bar4!".as_bytes().to_vec())?; // transaction.set_private_use_do(4, "Foo bar4!".as_bytes().to_vec())?;
//
let d = pgpt.private_use_do(1)?; // let d = transaction.private_use_do(1)?;
println!("data 1 {d:?}"); // println!("data 1 {d:?}");
let d = pgpt.private_use_do(2)?; // let d = transaction.private_use_do(2)?;
println!("data 2 {d:?}"); // println!("data 2 {d:?}");
let d = pgpt.private_use_do(3)?; // let d = transaction.private_use_do(3)?;
println!("data 3 {d:?}"); // println!("data 3 {d:?}");
let d = pgpt.private_use_do(4)?; // let d = transaction.private_use_do(4)?;
println!("data 4 {d:?}"); // println!("data 4 {d:?}");
//
Ok(out) // Ok(out)
} // }
// pub fn test_cardholder_cert( // pub fn test_cardholder_cert(
// card_tx: &mut CardApp, // card_tx: &mut CardApp,
@ -460,25 +455,25 @@ pub fn test_private_data(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutpu
// Ok(out) // Ok(out)
// } // }
pub fn test_pw_status(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_pw_status(mut card: Card<Open>, _param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
let out = vec![]; let out = vec![];
let ard = pgpt.application_related_data()?; let mut pws = transaction.pw_status_bytes()?;
let mut pws = ard.pw_status_bytes()?;
println!("pws {pws:?}"); println!("pws {pws:?}");
pgpt.verify_pw3(b"12345678")?; let mut admin = transaction.to_admin_card("12345678")?;
pws.set_pw1_cds_valid_once(false); pws.set_pw1_cds_valid_once(false);
pws.set_pw1_pin_block(true); pws.set_pw1_pin_block(true);
pgpt.set_pw_status_bytes(&pws, false)?; admin.set_pw_status_bytes(&pws, false)?;
let ard = pgpt.application_related_data()?; transaction.reload_ard()?;
let pws = ard.pw_status_bytes()?;
let pws = transaction.pw_status_bytes()?;
println!("pws {pws:?}"); println!("pws {pws:?}");
Ok(out) Ok(out)
@ -487,8 +482,8 @@ pub fn test_pw_status(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput,
/// Outputs: /// Outputs:
/// - verify pw3 (check) -> Status /// - verify pw3 (check) -> Status
/// - verify pw1 (check) -> Status /// - verify pw1 (check) -> Status
pub fn test_verify(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_verify(mut card: Card<Open>, _param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
// Steps: // Steps:
// //
@ -505,7 +500,8 @@ pub fn test_verify(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, Tes
let mut out = vec![]; let mut out = vec![];
// try to set name without verify, assert result is not ok! // try to set name without verify, assert result is not ok!
let res = pgpt.set_name("Notverified<<Hello".as_bytes()); let mut admin = transaction.to_admin_card(None)?;
let res = admin.set_name("Notverified<<Hello");
if let Err(Error::CardStatus(s)) = res { if let Err(Error::CardStatus(s)) = res {
assert_eq!(s, StatusBytes::SecurityStatusNotSatisfied); assert_eq!(s, StatusBytes::SecurityStatusNotSatisfied);
@ -513,9 +509,9 @@ pub fn test_verify(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, Tes
panic!("Status should be 'SecurityStatusNotSatisfied'"); panic!("Status should be 'SecurityStatusNotSatisfied'");
} }
pgpt.verify_pw3(b"12345678")?; transaction.verify_admin(b"12345678")?;
match pgpt.check_pw3() { match transaction.check_admin_verified() {
Err(Error::CardStatus(s)) => { Err(Error::CardStatus(s)) => {
// e.g. yubikey5 returns an error status! // e.g. yubikey5 returns an error status!
out.push(TestResult::Status(s)); out.push(TestResult::Status(s));
@ -526,14 +522,18 @@ pub fn test_verify(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, Tes
Ok(_) => out.push(TestResult::StatusOk), Ok(_) => out.push(TestResult::StatusOk),
} }
pgpt.set_name(b"Admin<<Hello")?; let mut admin = transaction.to_admin_card(None)?;
let cardholder = pgpt.cardholder_related_data()?; admin.set_name("Admin<<Hello")?;
transaction.reload_ard()?;
let cardholder = transaction.cardholder_related_data()?;
assert_eq!(cardholder.name(), Some("Admin<<Hello".as_bytes())); assert_eq!(cardholder.name(), Some("Admin<<Hello".as_bytes()));
pgpt.verify_pw1_user(b"123456")?; transaction.verify_user(b"123456")?;
match pgpt.check_pw3() { match transaction.check_user_verified() {
Err(Error::CardStatus(s)) => { Err(Error::CardStatus(s)) => {
// e.g. yubikey5 returns an error status! // e.g. yubikey5 returns an error status!
out.push(TestResult::Status(s)); out.push(TestResult::Status(s));
@ -544,36 +544,39 @@ pub fn test_verify(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, Tes
Ok(_) => out.push(TestResult::StatusOk), Ok(_) => out.push(TestResult::StatusOk),
} }
pgpt.set_name(b"There<<Hello")?; let mut admin = transaction.to_admin_card(None)?;
let cardholder = pgpt.cardholder_related_data()?; admin.set_name("There<<Hello")?;
transaction.reload_ard()?;
let cardholder = transaction.cardholder_related_data()?;
assert_eq!(cardholder.name(), Some("There<<Hello".as_bytes())); assert_eq!(cardholder.name(), Some("There<<Hello".as_bytes()));
Ok(out) Ok(out)
} }
pub fn test_change_pw(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput, TestError> { pub fn test_change_pw(mut card: Card<Open>, _param: &[&str]) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
let out = vec![]; let out = vec![];
// first do admin-less pw1 on gnuk // first do admin-less pw1 on gnuk
// (NOTE: Gnuk requires a key to be loaded before allowing pw changes!) // (NOTE: Gnuk requires a key to be loaded before allowing pw changes!)
println!("change pw1"); println!("change pw1");
pgpt.change_pw1(b"123456", b"abcdef00")?; transaction.change_user_pin(b"123456", b"abcdef00")?;
// also set admin pw, which means pw1 is now only user-pw again, on gnuk // also set admin pw, which means pw1 is now only user-pw again, on gnuk
println!("change pw3"); println!("change pw3");
// ca.change_pw3("abcdef00", "abcdefgh")?; // gnuk // ca.change_pw3("abcdef00", "abcdefgh")?; // gnuk
pgpt.change_pw3(b"12345678", b"abcdefgh")?; transaction.change_admin_pin(b"12345678", b"abcdefgh")?;
println!("change pw1"); println!("change pw1");
pgpt.change_pw1(b"abcdef00", b"abcdef")?; // gnuk transaction.change_user_pin(b"abcdef00", b"abcdef")?; // gnuk
// ca.change_pw1("123456", "abcdef")?; // ca.change_pw1("123456", "abcdef")?;
println!("verify bad pw1"); println!("verify bad pw1");
match pgpt.verify_pw1_user(b"123456ab") { match transaction.verify_user(b"123456ab") {
Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => { Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => {
// this is expected // this is expected
} }
@ -584,10 +587,10 @@ pub fn test_change_pw(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput,
} }
println!("verify good pw1"); println!("verify good pw1");
pgpt.verify_pw1_user(b"abcdef")?; transaction.verify_user(b"abcdef")?;
println!("verify bad pw3"); println!("verify bad pw3");
match pgpt.verify_pw3(b"00000000") { match transaction.verify_admin(b"00000000") {
Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => { Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => {
// this is expected // this is expected
} }
@ -598,36 +601,36 @@ pub fn test_change_pw(pgp: &mut OpenPgp, _param: &[&str]) -> Result<TestOutput,
} }
println!("verify good pw3"); println!("verify good pw3");
pgpt.verify_pw3(b"abcdefgh")?; transaction.verify_admin(b"abcdefgh")?;
println!("change pw3 back to default"); println!("change pw3 back to default");
pgpt.change_pw3(b"abcdefgh", b"12345678")?; transaction.change_admin_pin(b"abcdefgh", b"12345678")?;
println!("change pw1 back to default"); println!("change pw1 back to default");
pgpt.change_pw1(b"abcdef", b"123456")?; transaction.change_user_pin(b"abcdef", b"123456")?;
Ok(out) Ok(out)
} }
pub fn test_reset_retry_counter( pub fn test_reset_retry_counter(
pgp: &mut OpenPgp, mut card: Card<Open>,
_param: &[&str], _param: &[&str],
) -> Result<TestOutput, TestError> { ) -> Result<TestOutput, TestError> {
let mut pgpt = pgp.transaction()?; let mut transaction = card.transaction()?;
let out = vec![]; let out = vec![];
// set pw3, then pw1 (to bring gnuk into non-admin mode) // set pw3, then pw1 (to bring gnuk into non-admin mode)
println!("set pw3"); println!("set pw3");
pgpt.change_pw3(b"12345678", b"12345678")?; transaction.change_admin_pin(b"12345678", b"12345678")?;
println!("set pw1"); println!("set pw1");
pgpt.change_pw1(b"123456", b"123456")?; transaction.change_user_pin(b"123456", b"123456")?;
println!("break pw1"); println!("break pw1");
let _ = pgpt.verify_pw1_user(b"wrong0"); let _ = transaction.verify_user(b"wrong0");
let _ = pgpt.verify_pw1_user(b"wrong0"); let _ = transaction.verify_user(b"wrong0");
let _ = pgpt.verify_pw1_user(b"wrong0"); let _ = transaction.verify_user(b"wrong0");
let res = pgpt.verify_pw1_user(b"wrong0"); let res = transaction.verify_user(b"wrong0");
match res { match res {
Err(Error::CardStatus(StatusBytes::AuthenticationMethodBlocked)) => { Err(Error::CardStatus(StatusBytes::AuthenticationMethodBlocked)) => {
@ -646,20 +649,21 @@ pub fn test_reset_retry_counter(
} }
println!("verify pw3"); println!("verify pw3");
pgpt.verify_pw3(b"12345678")?; transaction.verify_admin(b"12345678")?;
println!("set resetting code"); println!("set resetting code");
pgpt.set_resetting_code(b"abcdefgh")?; let mut admin = transaction.to_admin_card(None)?;
admin.set_resetting_code(b"abcdefgh")?;
println!("reset retry counter"); println!("reset retry counter");
// ca.reset_retry_counter_pw1("abcdef".as_bytes().to_vec(), None)?; // ca.reset_retry_counter_pw1("abcdef".as_bytes().to_vec(), None)?;
let _res = pgpt.reset_retry_counter_pw1(b"abcdef", Some(b"abcdefgh")); let _res = transaction.reset_user_pin(b"abcdef", b"abcdefgh");
println!("verify good pw1"); println!("verify good pw1");
pgpt.verify_pw1_user(b"abcdef")?; transaction.verify_user(b"abcdef")?;
println!("verify bad pw1"); println!("verify bad pw1");
match pgpt.verify_pw1_user(b"00000000") { match transaction.verify_user(b"00000000") {
Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => { Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => {
// this is expected // this is expected
} }
@ -673,11 +677,9 @@ pub fn test_reset_retry_counter(
} }
pub fn run_test( pub fn run_test(
tc: &mut TestCardData, card: &mut Card<Open>,
t: fn(&mut OpenPgp, &[&str]) -> Result<TestOutput, TestError>, t: fn(&mut Card<Open>, &[&str]) -> Result<TestOutput, TestError>,
param: &[&str], param: &[&str],
) -> Result<TestOutput, TestError> { ) -> Result<TestOutput, TestError> {
let card = tc.get_card()?; t(card, param)
let mut pgp = OpenPgp::new(card);
t(&mut pgp, param)
} }

View file

@ -6,9 +6,9 @@ use std::time::SystemTime;
use anyhow::Result; use anyhow::Result;
use openpgp_card::card_do::KeyGenerationTime; use openpgp_card::card_do::KeyGenerationTime;
use openpgp_card::{KeyType, OpenPgpTransaction}; use openpgp_card::KeyType;
use openpgp_card_sequoia::sq_util; use openpgp_card_sequoia::state::Admin;
use openpgp_card_sequoia::util::vka_as_uploadable_key; use openpgp_card_sequoia::{sq_util, Card};
use sequoia_openpgp::parse::stream::{ use sequoia_openpgp::parse::stream::{
DetachedVerifierBuilder, MessageLayer, MessageStructure, VerificationHelper, DetachedVerifierBuilder, MessageLayer, MessageStructure, VerificationHelper,
}; };
@ -20,7 +20,7 @@ use sequoia_openpgp::Cert;
pub const SP: &StandardPolicy = &StandardPolicy::new(); pub const SP: &StandardPolicy = &StandardPolicy::new();
pub(crate) fn upload_subkeys( pub(crate) fn upload_subkeys(
pgpt: &mut OpenPgpTransaction, admin: &mut Card<Admin>,
cert: &Cert, cert: &Cert,
policy: &dyn Policy, policy: &dyn Policy,
) -> Result<Vec<(String, KeyGenerationTime)>> { ) -> Result<Vec<(String, KeyGenerationTime)>> {
@ -44,8 +44,7 @@ pub(crate) fn upload_subkeys(
out.push((fp, creation.into())); out.push((fp, creation.into()));
// upload key // upload key
let cuk = vka_as_uploadable_key(vka, None); admin.upload_key(vka, *kt, None)?;
pgpt.key_import(cuk, *kt)?;
} }
} }