WIP: more testing in the context of on-card key generation.

In particular, add a decryption test.
This commit is contained in:
Heiko Schaefer 2021-08-11 19:46:10 +02:00
parent 6904551c7d
commit a0370c5c53
7 changed files with 151 additions and 45 deletions

View file

@ -46,15 +46,23 @@ fn main() -> Result<()> {
let upload_out = upload_res?; let upload_out = upload_res?;
println!(" {:x?}", upload_out); println!(" {:x?}", upload_out);
let key = std::fs::read_to_string(key)
.expect("Unable to read ciphertext");
// decrypt // decrypt
print!(" Decrypt"); print!(" Decrypt");
let dec_out = let msg = std::fs::read_to_string(ciphertext).expect(&format![
run_test(&mut card, test_decrypt, &[key, ciphertext])?; "Unable to read ciphertext from file {}",
ciphertext
]);
let dec_out = run_test(&mut card, test_decrypt, &[&key, &msg])?;
println!(" {:x?}", dec_out); println!(" {:x?}", dec_out);
// sign // sign
print!(" Sign"); print!(" Sign");
let sign_out = run_test(&mut card, test_sign, &[key])?;
let sign_out = run_test(&mut card, test_sign, &[&key])?;
println!(" {:x?}", sign_out); println!(" {:x?}", sign_out);
} }

View file

@ -2,9 +2,12 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::Result; use anyhow::Result;
use std::str::FromStr;
use card_functionality::cards::TestConfig; use card_functionality::cards::TestConfig;
use card_functionality::tests::*; use card_functionality::tests::*;
use card_functionality::util;
use sequoia_openpgp::Cert;
fn main() -> Result<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();
@ -28,8 +31,8 @@ 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");
@ -39,10 +42,22 @@ fn main() -> Result<()> {
let res = run_test(&mut card, test_keygen, &[])?; let res = run_test(&mut card, test_keygen, &[])?;
if let TestResult::Text(cert) = &res[0] { if let TestResult::Text(cert) = &res[0] {
println!("cert\n{}", cert); // sign
}; print!(" Sign");
let sign_out = run_test(&mut card, test_sign, &[cert])?;
println!(" {:x?}", sign_out);
// panic!(); // decrypt
let c = Cert::from_str(cert)?;
let ciphertext = util::encrypt_to("Hello world!\n", &c)?;
print!(" Decrypt");
let dec_out =
run_test(&mut card, test_decrypt, &[cert, &ciphertext])?;
println!(" {:x?}", dec_out);
} else {
panic!("Didn't get back a Cert from test_keygen");
};
println!(); println!();
} }

View file

@ -3,4 +3,4 @@
pub mod cards; pub mod cards;
pub mod tests; pub mod tests;
mod util; pub mod util;

View file

@ -4,12 +4,17 @@
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::convert::TryInto; use std::convert::TryInto;
use std::str::FromStr;
use std::string::FromUtf8Error;
use std::time::SystemTime; use std::time::SystemTime;
use thiserror::Error; use thiserror::Error;
use sequoia_openpgp::packet::key::{KeyRole, PrimaryRole, SubordinateRole};
use sequoia_openpgp::packet::signature::SignatureBuilder;
use sequoia_openpgp::packet::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::{SignatureType, Timestamp}; use sequoia_openpgp::types::{KeyFlags, SignatureType, Timestamp};
use sequoia_openpgp::{Cert, Packet}; use sequoia_openpgp::{Cert, Packet};
use openpgp_card::card_app::CardApp; use openpgp_card::card_app::CardApp;
@ -17,14 +22,10 @@ use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError};
use openpgp_card::{ use openpgp_card::{
Algo, Curve, EccAttrs, EccType, KeyType, PublicKeyMaterial, RsaAttrs, Sex, Algo, Curve, EccAttrs, EccType, KeyType, PublicKeyMaterial, RsaAttrs, Sex,
}; };
use openpgp_card_sequoia::signer::CardSigner;
use crate::cards::TestCard; use crate::cards::TestCard;
use crate::util; use crate::util;
use openpgp_card_sequoia::signer::CardSigner;
use sequoia_openpgp::packet::key::{KeyRole, PrimaryRole, SubordinateRole};
use sequoia_openpgp::packet::signature::SignatureBuilder;
use sequoia_openpgp::packet::UserID;
use std::string::FromUtf8Error;
#[derive(Debug)] #[derive(Debug)]
pub enum TestResult { pub enum TestResult {
@ -63,9 +64,8 @@ pub fn test_decrypt(
"test_decrypt needs filenames for 'cert' and 'encrypted'" "test_decrypt needs filenames for 'cert' and 'encrypted'"
); );
let cert = Cert::from_file(param[0])?; let cert = Cert::from_str(param[0])?;
let msg = let msg = param[1].to_string();
std::fs::read_to_string(param[1]).expect("Unable to read ciphertext");
let res = ca.verify_pw1("123456")?; let res = ca.verify_pw1("123456")?;
res.check_ok()?; res.check_ok()?;
@ -88,7 +88,7 @@ pub fn test_sign(
let res = ca.verify_pw1_for_signing("123456")?; let res = ca.verify_pw1_for_signing("123456")?;
res.check_ok()?; res.check_ok()?;
let cert = Cert::from_file(param[0])?; let cert = Cert::from_str(param[0])?;
let msg = "Hello world, I am signed."; let msg = "Hello world, I am signed.";
let sig = openpgp_card_sequoia::sign(&mut ca, &cert, &mut msg.as_bytes())?; let sig = openpgp_card_sequoia::sign(&mut ca, &cert, &mut msg.as_bytes())?;
@ -219,6 +219,8 @@ pub fn test_keygen(
let verify = ca.verify_pw3("12345678")?; let verify = ca.verify_pw3("12345678")?;
verify.check_ok()?; verify.check_ok()?;
// ---------------
// RSA 1024, e=17 // RSA 1024, e=17
let rsa1k = Algo::Rsa(RsaAttrs { let rsa1k = Algo::Rsa(RsaAttrs {
len_n: 1024, len_n: 1024,
@ -227,12 +229,19 @@ pub fn test_keygen(
}); });
// RSA 2048, e=17 // RSA 2048, e=17
let rsa2k = Algo::Rsa(RsaAttrs { let rsa2k_17 = Algo::Rsa(RsaAttrs {
len_n: 2048, len_n: 2048,
len_e: 17, len_e: 17,
import_format: 0, 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 // RSA 3072, e=17
let rsa3k = Algo::Rsa(RsaAttrs { let rsa3k = Algo::Rsa(RsaAttrs {
len_n: 3072, len_n: 3072,
@ -240,21 +249,28 @@ pub fn test_keygen(
import_format: 0, 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 // RSA 4096, e=32
let rsa4k = Algo::Rsa(RsaAttrs { let rsa4k_32 = Algo::Rsa(RsaAttrs {
len_n: 4096, len_n: 4096,
len_e: 32, len_e: 32,
import_format: 0, import_format: 0,
}); });
// ed25519 sign // ed25519 (sign)
let ed25519 = Algo::Ecc(EccAttrs { let ed25519 = Algo::Ecc(EccAttrs {
ecc_type: EccType::EdDSA, ecc_type: EccType::EdDSA,
curve: Curve::Ed25519, curve: Curve::Ed25519,
import_format: None, import_format: None,
}); });
// cv25519 dec // cv25519 (dec)
let cv25519 = Algo::Ecc(EccAttrs { let cv25519 = Algo::Ecc(EccAttrs {
ecc_type: EccType::ECDH, ecc_type: EccType::ECDH,
curve: Curve::Cv25519, curve: Curve::Cv25519,
@ -275,8 +291,33 @@ pub fn test_keygen(
import_format: None, import_format: None,
}); });
let fp = // ---------------
|pkm: &PublicKeyMaterial, ts: SystemTime, kt: KeyType, algo: &Algo| {
// -- 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 // FIXME: store creation timestamp
let key = let key =
@ -291,8 +332,7 @@ pub fn test_keygen(
// ------ // ------
let (pkm, ts) = let (pkm, ts) = ca.generate_key(fp, KeyType::Signing, Some(sig_algo))?;
ca.generate_key(fp, KeyType::Signing, Some(&nist256_ecdsa))?;
let key_sig = openpgp_card_sequoia::public_key_material_to_key( let key_sig = openpgp_card_sequoia::public_key_material_to_key(
&pkm, &pkm,
KeyType::Signing, KeyType::Signing,
@ -302,7 +342,7 @@ pub fn test_keygen(
// ------ // ------
let (pkm, ts) = let (pkm, ts) =
ca.generate_key(fp, KeyType::Decryption, Some(&nist256_ecdh))?; ca.generate_key(fp, KeyType::Decryption, Some(dec_algo))?;
let key_dec = openpgp_card_sequoia::public_key_material_to_key( let key_dec = openpgp_card_sequoia::public_key_material_to_key(
&pkm, &pkm,
KeyType::Decryption, KeyType::Decryption,
@ -312,7 +352,7 @@ pub fn test_keygen(
// ------ // ------
let (pkm, ts) = let (pkm, ts) =
ca.generate_key(fp, KeyType::Authentication, Some(&nist256_ecdsa))?; ca.generate_key(fp, KeyType::Authentication, Some(sig_algo))?;
let key_aut = openpgp_card_sequoia::public_key_material_to_key( let key_aut = openpgp_card_sequoia::public_key_material_to_key(
&pkm, &pkm,
KeyType::Authentication, KeyType::Authentication,

View file

@ -2,9 +2,9 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use std::io::Write;
use std::time::SystemTime; use std::time::SystemTime;
use sequoia_openpgp as openpgp;
use sequoia_openpgp::cert::amalgamation::key::ValidKeyAmalgamation; use sequoia_openpgp::cert::amalgamation::key::ValidKeyAmalgamation;
use sequoia_openpgp::packet::key::{SecretParts, UnspecifiedRole}; use sequoia_openpgp::packet::key::{SecretParts, UnspecifiedRole};
use sequoia_openpgp::parse::stream::{ use sequoia_openpgp::parse::stream::{
@ -13,6 +13,9 @@ use sequoia_openpgp::parse::stream::{
}; };
use sequoia_openpgp::parse::Parse; use sequoia_openpgp::parse::Parse;
use sequoia_openpgp::policy::StandardPolicy; use sequoia_openpgp::policy::StandardPolicy;
use sequoia_openpgp::serialize::stream::{
Armorer, Encryptor, LiteralWriter, Message,
};
use sequoia_openpgp::Cert; use sequoia_openpgp::Cert;
use openpgp_card::card_app::CardApp; use openpgp_card::card_app::CardApp;
@ -94,20 +97,23 @@ impl<'a> VHelper<'a> {
impl<'a> VerificationHelper for VHelper<'a> { impl<'a> VerificationHelper for VHelper<'a> {
fn get_certs( fn get_certs(
&mut self, &mut self,
_ids: &[openpgp::KeyHandle], _ids: &[sequoia_openpgp::KeyHandle],
) -> openpgp::Result<Vec<openpgp::Cert>> { ) -> sequoia_openpgp::Result<Vec<Cert>> {
// Hand out our single Cert // Hand out our single Cert
Ok(vec![self.cert.clone()]) Ok(vec![self.cert.clone()])
} }
fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> { fn check(
&mut self,
structure: MessageStructure,
) -> sequoia_openpgp::Result<()> {
// We are interested in signatures over the data (level 0 signatures) // We are interested in signatures over the data (level 0 signatures)
if let Some(MessageLayer::SignatureGroup { results }) = if let Some(MessageLayer::SignatureGroup { results }) =
structure.into_iter().next() structure.into_iter().next()
{ {
match results.into_iter().next() { match results.into_iter().next() {
Some(Ok(_)) => Ok(()), // Good signature. Some(Ok(_)) => Ok(()), // Good signature.
Some(Err(e)) => Err(openpgp::Error::from(e).into()), Some(Err(e)) => Err(sequoia_openpgp::Error::from(e).into()),
None => Err(anyhow::anyhow!("No signature")), None => Err(anyhow::anyhow!("No signature")),
} }
} else { } else {
@ -123,3 +129,42 @@ pub fn verify_sig(cert: &Cert, msg: &[u8], sig: &[u8]) -> Result<bool> {
Ok(dv.verify_bytes(msg).is_ok()) Ok(dv.verify_bytes(msg).is_ok())
} }
pub fn encrypt_to(cleartext: &str, cert: &Cert) -> Result<String> {
let p = &StandardPolicy::new();
let mut recipients = Vec::new();
// Make sure we add at least one subkey from every
// certificate.
let mut found_one = false;
for key in cert
.keys()
.with_policy(p, None)
.supported()
.alive()
.revoked(false)
.for_transport_encryption()
{
recipients.push(key);
found_one = true;
}
if !found_one {
return Err(anyhow::anyhow!(
"No suitable encryption subkey for {}",
cert
));
}
let mut sink = vec![];
let message = Message::new(&mut sink);
let message = Armorer::new(message).build()?;
let message = Encryptor::for_recipients(message, recipients).build()?;
let mut w = LiteralWriter::new(message).build()?;
w.write_all(cleartext.as_bytes())?;
w.finalize()?;
Ok(String::from_utf8(sink).unwrap())
}

View file

@ -643,7 +643,6 @@ impl CardApp {
&PublicKeyMaterial, &PublicKeyMaterial,
SystemTime, SystemTime,
KeyType, KeyType,
&Algo,
) -> Result<[u8; 20]>, ) -> Result<[u8; 20]>,
key_type: KeyType, key_type: KeyType,
algo: Option<&Algo>, algo: Option<&Algo>,

View file

@ -26,7 +26,6 @@ pub(crate) fn gen_key_with_metadata(
&PublicKeyMaterial, &PublicKeyMaterial,
SystemTime, SystemTime,
KeyType, KeyType,
&Algo,
) -> Result<[u8; 20]>, ) -> Result<[u8; 20]>,
key_type: KeyType, key_type: KeyType,
algo: Option<&Algo>, algo: Option<&Algo>,
@ -62,7 +61,7 @@ pub(crate) fn gen_key_with_metadata(
card_app.set_creation_time(ts, key_type)?.check_ok()?; card_app.set_creation_time(ts, key_type)?.check_ok()?;
// calculate/store fingerprint // calculate/store fingerprint
let fp = fp_from_pub(&pubkey, time, key_type, &algo)?; let fp = fp_from_pub(&pubkey, time, key_type)?;
card_app.set_fingerprint(fp, key_type)?.check_ok()?; card_app.set_fingerprint(fp, key_type)?.check_ok()?;
Ok((pubkey, ts)) Ok((pubkey, ts))