Extend Test configuration file format.

Run key generation tests for the algorithms specified in the configuration.
This commit is contained in:
Heiko Schaefer 2021-08-13 19:49:11 +02:00
parent 2ef6e0442d
commit 3361c8b79d
7 changed files with 163 additions and 210 deletions

View file

@ -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"
]

View file

@ -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<String, Card>,
}
#[derive(Debug, Deserialize)]
pub struct Card {
backend: BTreeMap<String, String>,
config: Config,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Config {
pub keygen: Vec<String>,
}
/// 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<CardApp> {
self.tc.open()
}
pub fn get_config(&self) -> &Config {
&self.config
}
}
impl TestConfig {
pub fn load(file: &str) -> Result<Self> {
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<TestCardApp> {
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<Vec<String>>,
scdc: Option<Vec<String>>,
}
impl TestConfig {
pub fn open(file: &str) -> Result<Self> {
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<TestCard> {
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
}
}

View file

@ -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();

View file

@ -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,15 +31,22 @@ 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, &[])?;
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
@ -58,6 +65,7 @@ fn main() -> Result<()> {
} else {
panic!("Didn't get back a Cert from test_keygen");
};
}
println!();
}

View file

@ -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();

View file

@ -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<TestOutput, TestError> {
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<TestOutput, TestError>,
param: &[&str],
) -> Result<TestOutput, TestError> {
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)?;

View file

@ -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()?)
}