Add the crate card-functionality, which implements a test suite to test the openpgp-card crate against a set of OpenPGP cards
This commit is contained in:
parent
4b7b4a2ab6
commit
77e32e02c2
5 changed files with 481 additions and 0 deletions
|
@ -7,4 +7,5 @@ members = [
|
|||
"openpgp-card",
|
||||
"openpgp-card-sequoia",
|
||||
"scdc",
|
||||
"card-functionality",
|
||||
]
|
||||
|
|
16
card-functionality/Cargo.toml
Normal file
16
card-functionality/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
# SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||
# SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
[package]
|
||||
name = "card-functionality"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
openpgp-card = { path = "../openpgp-card" }
|
||||
openpgp-card-sequoia = { path = "../openpgp-card-sequoia" }
|
||||
sequoia-openpgp = "1.3"
|
||||
anyhow = "1"
|
||||
env_logger = "0.8"
|
83
card-functionality/data/rsa2k.sec
Normal file
83
card-functionality/data/rsa2k.sec
Normal file
|
@ -0,0 +1,83 @@
|
|||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
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
|
||||
7aYYBzgV/of68E1HMFmY7A==
|
||||
=JojA
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
309
card-functionality/src/main.rs
Normal file
309
card-functionality/src/main.rs
Normal file
|
@ -0,0 +1,309 @@
|
|||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
//! These tests rely mainly on the card-app abstraction layer in
|
||||
//! openpgp-card. However, for crypto-operations, higher level APIs and
|
||||
//! Sequoia PGP are used.
|
||||
//!
|
||||
//! The main purpose of this test suite is to be able to test the behavior
|
||||
//! of different OpenPGP card implementation.
|
||||
//!
|
||||
//! These tests assert (and fail) in cases where a certain behavior is
|
||||
//! expected from all cards, and a card doesn't conform.
|
||||
//! However, in some aspects, card behavior is expected to diverge, and
|
||||
//! it's not ok for us to just fail and reject the card's output.
|
||||
//! Even when it contradicts the OpenPGP card spec.
|
||||
//!
|
||||
//! For such cases, these tests return a TestOutput, which is a
|
||||
//! Vec<TestResult>, to document the return values of the card in question.
|
||||
//!
|
||||
//! e.g.: the Yubikey 5 fails to handle the VERIFY command with empty data
|
||||
//! (see OpenPGP card spec, 7.2.2: "If the command is called
|
||||
//! without data, the actual access status of the addressed password is
|
||||
//! returned or the access status is set to 'not verified'").
|
||||
//!
|
||||
//! The Yubikey 5 erroneously returns Status 0x6a80 ("Incorrect parameters in
|
||||
//! the command data field").
|
||||
|
||||
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, KeyType, Sex};
|
||||
|
||||
mod util;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TestResult {
|
||||
Status([u8; 2]),
|
||||
Text(String),
|
||||
}
|
||||
|
||||
type TestOutput = Vec<TestResult>;
|
||||
|
||||
/// Map: Card ident -> TestOutput
|
||||
type TestsOutput = HashMap<String, TestOutput>;
|
||||
|
||||
/// Run after each "upload keys", if key *was* uploaded (?)
|
||||
fn test_decrypt() {
|
||||
// FIXME
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Run after each "upload keys", if key *was* uploaded (?)
|
||||
fn test_sign() {
|
||||
// FIXME
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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 check_key_upload_algo_attrs() -> Result<()> {
|
||||
// get_algorithm_attributes
|
||||
// FIXME
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_upload_keys_rsa_2k(ca: &mut CardApp) -> Result<TestOutput> {
|
||||
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() {
|
||||
// FIXME
|
||||
unimplemented!()
|
||||
|
||||
// check if card supports 25519, if not that's ok, return this
|
||||
// information and don't try upload.
|
||||
|
||||
// upload key
|
||||
|
||||
// test upload general - checks
|
||||
}
|
||||
|
||||
fn test_keygen() {
|
||||
// FIXME
|
||||
// (implementation of this functionality is still missing in openpgp-card)
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn test_reset(ca: &mut CardApp) -> Result<TestOutput> {
|
||||
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.
|
||||
///
|
||||
/// Returns an empty TestOutput, throws errors for unexpected Status codes
|
||||
/// and for unequal field values.
|
||||
fn test_set_user_data(ca: &mut CardApp) -> Result<TestOutput> {
|
||||
let res = ca.verify_pw3("12345678")?;
|
||||
res.check_ok()?;
|
||||
|
||||
// name
|
||||
let res = ca.set_name("Bar<<Foo")?;
|
||||
res.check_ok()?;
|
||||
|
||||
// lang
|
||||
let res = ca.set_lang("deen")?;
|
||||
res.check_ok()?;
|
||||
|
||||
// sex
|
||||
let res = ca.set_sex(Sex::Female)?;
|
||||
res.check_ok()?;
|
||||
|
||||
// url
|
||||
let res = ca.set_url("https://duckduckgo.com/")?;
|
||||
res.check_ok()?;
|
||||
|
||||
// read all the fields back again, expect equal data
|
||||
let ch = ca.get_cardholder_related_data()?;
|
||||
|
||||
assert_eq!(ch.name, Some("Bar<<Foo".to_string()));
|
||||
assert_eq!(ch.lang, Some(vec![['d', 'e'], ['e', 'n']]));
|
||||
assert_eq!(ch.sex, Some(Sex::Female));
|
||||
|
||||
let url = ca.get_url()?;
|
||||
assert_eq!(url, "https://duckduckgo.com/".to_string());
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
/// Outputs:
|
||||
/// - verify pw3 (check) -> Status
|
||||
/// - verify pw1 (check) -> Status
|
||||
fn test_verify(ca: &mut CardApp) -> Result<TestOutput> {
|
||||
// Steps:
|
||||
//
|
||||
// - try to set name without verify, assert result is not ok
|
||||
// - verify pw3 + pin -> Status
|
||||
// - verify pw3 (check) -> Status
|
||||
// - set name -> Status
|
||||
// - get name -> Text(name)
|
||||
// - verify pw1 + pin -> Status
|
||||
// - verify pw1 (check) -> Status
|
||||
// - set name -> Status
|
||||
// - get name -> Text(name)
|
||||
|
||||
let mut out = vec![];
|
||||
|
||||
// try to set name without verify, assert result is not ok!
|
||||
let res = ca.set_name("Notverified<<Hello")?;
|
||||
assert_eq!(res.status(), [0x69, 0x82]); // "Security status not satisfied"
|
||||
|
||||
let res = ca.verify_pw3("12345678")?;
|
||||
res.check_ok()?;
|
||||
|
||||
let check = ca.check_pw3()?;
|
||||
// don't "check_ok()" - yubikey5 returns an error code!
|
||||
out.push(TestResult::Status(check.status()));
|
||||
|
||||
let res = ca.set_name("Admin<<Hello")?;
|
||||
res.check_ok()?;
|
||||
|
||||
let cardholder = ca.get_cardholder_related_data()?;
|
||||
assert_eq!(cardholder.name, Some("Admin<<Hello".to_string()));
|
||||
|
||||
let res = ca.verify_pw1("123456")?;
|
||||
res.check_ok()?;
|
||||
|
||||
let check = ca.check_pw3()?;
|
||||
// don't "check_ok()" - yubikey5 returns an error code
|
||||
out.push(TestResult::Status(check.status()));
|
||||
|
||||
let res = ca.set_name("There<<Hello")?;
|
||||
res.check_ok()?;
|
||||
|
||||
let cardholder = ca.get_cardholder_related_data()?;
|
||||
assert_eq!(cardholder.name, Some("There<<Hello".to_string()));
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn run_test(
|
||||
cards: &[&str],
|
||||
t: fn(&mut CardApp) -> Result<TestOutput>,
|
||||
) -> Result<TestsOutput> {
|
||||
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)?;
|
||||
|
||||
if cards.contains(&app_id.ident().as_str()) {
|
||||
println!("Running Test on {}:", app_id.ident());
|
||||
|
||||
let res = t(&mut ca);
|
||||
|
||||
out.insert(app_id.ident(), res?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
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", /* Gnuk Rysim (green) */
|
||||
|
||||
// "FFFE:4231EB6E", /* Gnuk FST */
|
||||
];
|
||||
|
||||
// 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(())
|
||||
}
|
72
card-functionality/src/util.rs
Normal file
72
card-functionality/src/util.rs
Normal file
|
@ -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<Vec<(String, u32)>> {
|
||||
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<ValidKeyAmalgamation<'_, SecretParts, UnspecifiedRole, bool>> {
|
||||
// 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"))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue