- more explicit error handling

- add abstraction for types of card
This commit is contained in:
Heiko Schaefer 2021-07-20 20:25:45 +02:00
parent 6c563b230b
commit c789c90b9e
2 changed files with 168 additions and 126 deletions

View file

@ -14,4 +14,5 @@ openpgp-card-sequoia = { path = "../openpgp-card-sequoia" }
openpgp-card-scdc = { path = "../scdc" } openpgp-card-scdc = { path = "../scdc" }
sequoia-openpgp = "1.3" sequoia-openpgp = "1.3"
anyhow = "1" anyhow = "1"
thiserror = "1.0"
env_logger = "0.8" env_logger = "0.8"

View file

@ -25,19 +25,76 @@
//! The Yubikey 5 erroneously returns Status 0x6a80 ("Incorrect parameters in //! The Yubikey 5 erroneously returns Status 0x6a80 ("Incorrect parameters in
//! the command data field"). //! the command data field").
use anyhow::Result; use anyhow::{anyhow, Error, Result};
use std::collections::HashMap; use thiserror::Error;
use sequoia_openpgp::parse::Parse; use sequoia_openpgp::parse::Parse;
use sequoia_openpgp::Cert; use sequoia_openpgp::Cert;
use openpgp_card::apdu::PcscClient; use openpgp_card::apdu::PcscClient;
use openpgp_card::card_app::CardApp; use openpgp_card::card_app::CardApp;
use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError};
use openpgp_card::{CardClientBox, Sex}; use openpgp_card::{CardClientBox, Sex};
use openpgp_card_scdc::ScdClient; use openpgp_card_scdc::ScdClient;
mod util; mod util;
#[derive(Debug)]
enum TestCard {
Pcsc(&'static str),
Scdc(&'static str),
}
impl TestCard {
fn open(&self) -> Result<CardApp> {
match self {
Self::Pcsc(ident) => {
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 app_id = CardApp::get_aid(&ard)?;
if &app_id.ident().as_str() == ident {
// println!("opened pcsc card {}", ident);
return Ok(ca);
}
}
Err(anyhow!("Pcsc card {} not found", ident))
}
Self::Scdc(serial) => {
// FIXME
const SOCKET: &str = "/run/user/1000/gnupg/S.scdaemon";
let mut card = ScdClient::new(SOCKET)?;
card.select_card(serial)?;
let card_client = Box::new(card) as CardClientBox;
let mut ca = CardApp::new(card_client);
// Set Card Capabilities (chaining, command length, ..)
let ard = ca.get_app_data()?;
ca = ca.init_caps(&ard)?;
// println!("opened scdc card {}", serial);
Ok(ca)
}
}
}
}
#[derive(Debug)] #[derive(Debug)]
enum TestResult { enum TestResult {
Status([u8; 2]), Status([u8; 2]),
@ -46,11 +103,26 @@ enum TestResult {
type TestOutput = Vec<TestResult>; type TestOutput = Vec<TestResult>;
/// Map: Card ident -> TestOutput #[derive(Error, Debug)]
type TestsOutput = HashMap<String, TestOutput>; pub enum TestError {
#[error("Failed to upload key {0} ({1})")]
KeyUploadError(String, Error),
#[error(transparent)]
OPGP(#[from] OpenpgpCardError),
#[error(transparent)]
OCard(#[from] OcErrorStatus),
#[error(transparent)]
Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
}
/// Run after each "upload keys", if key *was* uploaded (?) /// Run after each "upload keys", if key *was* uploaded (?)
fn test_decrypt(mut ca: &mut CardApp, param: &[&str]) -> Result<TestOutput> { fn test_decrypt(
mut ca: &mut CardApp,
param: &[&str],
) -> Result<TestOutput, TestError> {
assert_eq!( assert_eq!(
param.len(), param.len(),
2, 2,
@ -73,7 +145,10 @@ fn test_decrypt(mut ca: &mut CardApp, param: &[&str]) -> Result<TestOutput> {
} }
/// Run after each "upload keys", if key *was* uploaded (?) /// Run after each "upload keys", if key *was* uploaded (?)
fn test_sign(mut ca: &mut CardApp, param: &[&str]) -> Result<TestOutput> { fn test_sign(
mut ca: &mut CardApp,
param: &[&str],
) -> Result<TestOutput, TestError> {
assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'"); assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'");
let res = ca.verify_pw1_for_signing("123456")?; let res = ca.verify_pw1_for_signing("123456")?;
@ -139,7 +214,10 @@ fn check_key_upload_algo_attrs() -> Result<()> {
Ok(()) Ok(())
} }
fn test_upload_keys(ca: &mut CardApp, param: &[&str]) -> Result<TestOutput> { fn test_upload_keys(
ca: &mut CardApp,
param: &[&str],
) -> Result<TestOutput, TestError> {
assert_eq!( assert_eq!(
param.len(), param.len(),
1, 1,
@ -153,9 +231,12 @@ fn test_upload_keys(ca: &mut CardApp, param: &[&str]) -> Result<TestOutput> {
// FIXME: check if card supports the algo in question? // FIXME: check if card supports the algo in question?
let meta = util::upload_subkeys(ca, &cert)?; let meta = util::upload_subkeys(ca, &cert)
.map_err(|e| TestError::KeyUploadError(param[0].to_string(), e))?;
check_key_upload_metadata(ca, &meta)?; check_key_upload_metadata(ca, &meta)?;
// FIXME: implement
check_key_upload_algo_attrs()?; check_key_upload_algo_attrs()?;
Ok(vec![]) Ok(vec![])
@ -167,7 +248,10 @@ fn test_keygen() {
unimplemented!() unimplemented!()
} }
fn test_reset(ca: &mut CardApp, param: &[&str]) -> Result<TestOutput> { fn test_reset(
ca: &mut CardApp,
param: &[&str],
) -> Result<TestOutput, TestError> {
let _res = ca.factory_reset()?; let _res = ca.factory_reset()?;
Ok(vec![]) Ok(vec![])
} }
@ -180,7 +264,7 @@ fn test_reset(ca: &mut CardApp, param: &[&str]) -> Result<TestOutput> {
fn test_set_user_data( fn test_set_user_data(
ca: &mut CardApp, ca: &mut CardApp,
_param: &[&str], _param: &[&str],
) -> Result<TestOutput> { ) -> Result<TestOutput, TestError> {
let res = ca.verify_pw3("12345678")?; let res = ca.verify_pw3("12345678")?;
res.check_ok()?; res.check_ok()?;
@ -216,7 +300,10 @@ fn test_set_user_data(
/// Outputs: /// Outputs:
/// - verify pw3 (check) -> Status /// - verify pw3 (check) -> Status
/// - verify pw1 (check) -> Status /// - verify pw1 (check) -> Status
fn test_verify(ca: &mut CardApp, _param: &[&str]) -> Result<TestOutput> { fn test_verify(
ca: &mut CardApp,
_param: &[&str],
) -> Result<TestOutput, TestError> {
// Steps: // Steps:
// //
// - try to set name without verify, assert result is not ok // - try to set name without verify, assert result is not ok
@ -264,110 +351,53 @@ fn test_verify(ca: &mut CardApp, _param: &[&str]) -> Result<TestOutput> {
Ok(out) Ok(out)
} }
fn run_test_pcsc( fn run_test(
cards: &[&str], card: &mut TestCard,
t: fn(&mut CardApp, &[&str]) -> Result<TestOutput>, t: fn(&mut CardApp, &[&str]) -> Result<TestOutput, TestError>,
param: &[&str], param: &[&str],
) -> Result<TestsOutput> { ) -> Result<TestOutput, TestError> {
let mut out = HashMap::new(); println!();
println!("Test card {:?}", card);
for card in PcscClient::list_cards()? { let mut ca = card.open()?;
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()?; let ard = ca.get_app_data()?;
ca = ca.init_caps(&ard)?;
let app_id = CardApp::get_aid(&ard)?; let app_id = CardApp::get_aid(&ard)?;
if cards.contains(&app_id.ident().as_str()) { println!("Running Test on {}:", app_id.ident());
// println!("Running Test on {}:", app_id.ident());
let res = t(&mut ca, param); t(&mut ca, param)
out.insert(app_id.ident(), res?);
}
}
Ok(out)
}
fn run_test_scdc(
cards: &[&str],
t: fn(&mut CardApp, &[&str]) -> Result<TestOutput>,
param: &[&str],
) -> Result<TestsOutput> {
let mut out = HashMap::new();
const SOCKET: &str = "/run/user/1000/gnupg/S.scdaemon";
for serial in cards {
let mut card = ScdClient::new(SOCKET)?;
card.select_card(serial)?;
let card_client = Box::new(card) as CardClientBox;
let mut ca = CardApp::new(card_client);
// Set Card Capabilities (chaining, command length, ..)
let ard = ca.get_app_data()?;
ca = ca.init_caps(&ard)?;
println!("XXX");
let app_id = CardApp::get_aid(&ard)?;
if cards.contains(&app_id.ident().as_str()) {
// println!("Running Test on {}:", app_id.ident());
let res = t(&mut ca, param);
out.insert(app_id.ident(), res?);
}
}
Ok(out)
} }
fn main() -> Result<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();
// list of card idents to runs the tests on let cards = vec![
let pcsc_cards = vec![ // TestCard::Scdc("D276000124010200FFFEF1420A7A0000"), /* Gnuk emulated */
"0006:16019180", /* Yubikey 5 */ // TestCard::Scdc("D2760001240103040006160191800000"), /* Yubikey 5 */
"0005:0000A835", /* FLOSS Card 3.4 */ // TestCard::Scdc("D27600012401030400050000A8350000"), /* FLOSS Card 3.4 */
"FFFE:57183146", /* Gnuk Rysim (green) */ // TestCard::Scdc("D276000124010200FFFE571831460000"), /* Gnuk Rysim (green) */
// TestCard::Scdc("D276000124010200FFFE4231EB6E0000"), /* Gnuk FST */
// "FFFE:4231EB6E", /* Gnuk FST */ TestCard::Pcsc("0006:16019180"), /* Yubikey 5 */
TestCard::Pcsc("0005:0000A835"), /* FLOSS Card 3.4 */
TestCard::Pcsc("FFFE:57183146"), /* Gnuk Rysim (green) */
TestCard::Pcsc("FFFE:4231EB6E"), /* Gnuk FST */
]; ];
// list of scdc card serial to runs the tests on for mut card in cards {
let scdc_cards = vec![ println!("reset");
// "D2760001240103040006160191800000", /* Yubikey 5 */ let _ = run_test(&mut card, test_reset, &[])?;
"D27600012401030400050000A8350000", /* FLOSS Card 3.4 */
"D276000124010200FFFE571831460000", /* Gnuk Rysim (green) */
// "D276000124010200FFFE4231EB6E0000", /* Gnuk FST */ println!("verify");
]; let verify_out = run_test(&mut card, test_verify, &[])?;
println!("{:x?}", verify_out);
// println!("reset"); println!("set user data");
// let _ = run_test_pcsc(&pcsc_cards, test_reset, &vec![])?; let userdata_out = run_test(&mut card, test_set_user_data, &[])?;
// println!("{:x?}", userdata_out);
// println!("verify");
// let verify_out = run_test_pcsc(&pcsc_cards, test_verify, &vec![])?;
// println!("{:x?}", verify_out);
//
// println!("set user data");
// let userdata_out =
// run_test_pcsc(&pcsc_cards, test_set_user_data, &vec![])?;
// println!("{:x?}", userdata_out);
for (key, ciphertext) in vec![ for (key, ciphertext) in [
("data/rsa2k.sec", "data/encrypted_to_rsa2k.asc"), ("data/rsa2k.sec", "data/encrypted_to_rsa2k.asc"),
("data/rsa4k.sec", "data/encrypted_to_rsa4k.asc"), ("data/rsa4k.sec", "data/encrypted_to_rsa4k.asc"),
("data/25519.sec", "data/encrypted_to_25519.asc"), ("data/25519.sec", "data/encrypted_to_25519.asc"),
@ -376,29 +406,40 @@ fn main() -> Result<()> {
] { ] {
// upload keys // upload keys
println!("Upload key '{}'", key); println!("Upload key '{}'", key);
let upload_out = let upload_res = run_test(&mut card, test_upload_keys, &[key]);
run_test_pcsc(&pcsc_cards, test_upload_keys, &vec![key])?;
println!("{:x?}", upload_out);
println!();
// FIXME: if this card doesn't support the key type, skip the // FIXME: if this card doesn't support the key type, skip the
// following tests? // following tests?
if let Err(TestError::KeyUploadError(s, _)) = upload_res {
println!(
"upload of key {} to card {:?} failed -> skip",
key, card
);
continue; // FIXME: add label?
}
let upload_out = upload_res?;
println!("{:x?}", upload_out);
println!();
// decrypt // decrypt
println!("Decrypt"); println!("Decrypt");
let dec_out = let dec_out =
run_test_pcsc(&pcsc_cards, test_decrypt, &vec![key, ciphertext])?; run_test(&mut card, test_decrypt, &[key, ciphertext])?;
println!("{:x?}", dec_out); println!("{:x?}", dec_out);
println!(); println!();
// sign // sign
println!("Sign"); println!("Sign");
let sign_out = run_test_pcsc(&pcsc_cards, test_sign, &vec![key])?; let sign_out = run_test(&mut card, test_sign, &[key])?;
println!("{:x?}", sign_out); println!("{:x?}", sign_out);
println!(); println!();
} }
// upload some key with pw // FIXME: generate keys
// FIXME: upload key with password
}
Ok(()) Ok(())
} }