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 # You should configure your set of test-card identifiers and store your
# configuration in that location. # 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 # to smartcards via the standard PCSC lite middleware
# (https://pcsclite.apdu.fr/). # (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). # 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. # for experimental use only.
# #
# (However, note that emulated Gnuk can currently only be accessed via scdc) # (However, note that emulated Gnuk can currently only be accessed via scdc)
pcsc = [ [card.gnuk_emu]
"0006:12345678", # Yubikey 5 #backend.pcsc = "FFFE:F1420A7A"
"0005:0000A835", # FLOSS Card 3.4 backend.scdc = "D276000124010200FFFEF1420A7A0000"
] config.keygen = ["RSA2k/32", "NIST256", "Curve25519"]
scdc = [ [card.yubikey5]
"D276000124010200FFFEF1420A7A0000", # Gnuk emulated 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 anyhow::{anyhow, Result};
use serde_derive::Deserialize; use serde_derive::Deserialize;
use std::collections::BTreeMap;
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::CardClientBox; use openpgp_card::CardClientBox;
use openpgp_card_scdc::ScdClient; 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)] #[derive(Debug)]
pub enum TestCard { pub enum TestCard {
Pcsc(String), 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<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();
let config = TestConfig::open("config/test-cards.toml")?; let config = TestConfig::load("config/test-cards.toml")?;
let cards = config.get_cards(); let cards = config.get_cards();

View file

@ -12,7 +12,7 @@ use sequoia_openpgp::Cert;
fn main() -> Result<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();
let config = TestConfig::open("config/test-cards.toml")?; let config = TestConfig::load("config/test-cards.toml")?;
let cards = config.get_cards(); let cards = config.get_cards();
@ -31,15 +31,22 @@ fn main() -> Result<()> {
println!("Reset"); println!("Reset");
let _ = run_test(&mut card, test_reset, &[])?; let _ = run_test(&mut card, 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 card, test_set_user_data, &[])?;
println!("Generate key"); let algos = {
let res = run_test(&mut card, test_keygen, &[])?; 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] { if let TestResult::Text(cert) = &res[0] {
// sign // sign
@ -58,6 +65,7 @@ fn main() -> Result<()> {
} else { } else {
panic!("Didn't get back a Cert from test_keygen"); panic!("Didn't get back a Cert from test_keygen");
}; };
}
println!(); println!();
} }

View file

@ -9,7 +9,7 @@ use card_functionality::tests::*;
fn main() -> Result<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();
let config = TestConfig::open("config/test-cards.toml")?; let config = TestConfig::load("config/test-cards.toml")?;
let cards = config.get_cards(); let cards = config.get_cards();

View file

@ -2,31 +2,23 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use std::convert::TryFrom;
use std::convert::TryInto;
use std::str::FromStr; use std::str::FromStr;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use std::time::SystemTime;
use thiserror::Error; 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::parse::Parse;
use sequoia_openpgp::serialize::SerializeInto; use sequoia_openpgp::serialize::SerializeInto;
use sequoia_openpgp::types::{KeyFlags, SignatureType, Timestamp}; use sequoia_openpgp::types::Timestamp;
use sequoia_openpgp::{Cert, Packet}; use sequoia_openpgp::Cert;
use openpgp_card::card_app::CardApp; use openpgp_card::card_app::CardApp;
use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError}; use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError};
use openpgp_card::{ use openpgp_card::{AlgoSimple, KeyType, Sex};
Algo, Curve, EccAttrs, EccType, KeyType, PublicKeyMaterial, RsaAttrs, 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; use crate::util;
#[derive(Debug)] #[derive(Debug)]
@ -216,151 +208,48 @@ pub fn test_upload_keys(
/// Generate keys for each of the three KeyTypes /// Generate keys for each of the three KeyTypes
pub fn test_keygen( pub fn test_keygen(
ca: &mut CardApp, ca: &mut CardApp,
_param: &[&str], param: &[&str],
) -> Result<TestOutput, TestError> { ) -> Result<TestOutput, TestError> {
let verify = ca.verify_pw3("12345678")?; let verify = ca.verify_pw3("12345678")?;
verify.check_ok()?; verify.check_ok()?;
// --------------- // Generate all three subkeys on card
let algo = param[0];
// RSA 1024, e=17 let alg = AlgoSimple::from(algo);
let rsa1k = Algo::Rsa(RsaAttrs {
len_n: 1024,
len_e: 17,
import_format: 0,
});
// RSA 2048, e=17 let (pkm, ts) =
let rsa2k_17 = Algo::Rsa(RsaAttrs { ca.generate_key_simple(public_to_fingerprint, KeyType::Signing, alg)?;
len_n: 2048, let key_sig = public_key_material_to_key(
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(
&pkm, &pkm,
KeyType::Signing, KeyType::Signing,
SystemTime::from(Timestamp::from(ts)), Timestamp::from(ts).into(),
)?; )?;
// ------ let (pkm, ts) = ca.generate_key_simple(
public_to_fingerprint,
let (pkm, ts) = KeyType::Decryption,
ca.generate_key(fp, KeyType::Decryption, Some(dec_algo))?; alg,
let key_dec = openpgp_card_sequoia::public_key_material_to_key( )?;
let key_dec = public_key_material_to_key(
&pkm, &pkm,
KeyType::Decryption, KeyType::Decryption,
SystemTime::from(Timestamp::from(ts)), Timestamp::from(ts).into(),
)?; )?;
// ------ let (pkm, ts) = ca.generate_key_simple(
public_to_fingerprint,
let (pkm, ts) = KeyType::Authentication,
ca.generate_key(fp, KeyType::Authentication, Some(sig_algo))?; alg,
let key_aut = openpgp_card_sequoia::public_key_material_to_key( )?;
let key_aut = public_key_material_to_key(
&pkm, &pkm,
KeyType::Authentication, 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 cert = make_cert(ca, key_sig, key_dec, key_aut)?;
let armored = String::from_utf8(cert.armored().to_vec()?)?; let armored = String::from_utf8(cert.armored().to_vec()?)?;
@ -525,11 +414,11 @@ pub fn test_verify(
} }
pub fn run_test( pub fn run_test(
card: &mut TestCard, card: &mut TestCardApp,
t: fn(&mut CardApp, &[&str]) -> Result<TestOutput, TestError>, t: fn(&mut CardApp, &[&str]) -> Result<TestOutput, TestError>,
param: &[&str], param: &[&str],
) -> Result<TestOutput, TestError> { ) -> Result<TestOutput, TestError> {
let mut ca = card.open()?; let mut ca = card.get_card_app()?;
let ard = ca.get_app_data()?; let ard = ca.get_app_data()?;
let _app_id = CardApp::get_aid(&ard)?; 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") 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()?)
}