Signing
This commit is contained in:
parent
aef6c781ed
commit
97d4880118
7 changed files with 154 additions and 33 deletions
|
@ -11,6 +11,7 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
openpgp-card = { path = "../openpgp-card" }
|
openpgp-card = { path = "../openpgp-card" }
|
||||||
openpgp-card-sequoia = { path = "../openpgp-card-sequoia" }
|
openpgp-card-sequoia = { path = "../openpgp-card-sequoia" }
|
||||||
|
openpgp-card-scdc = { path = "../scdc" }
|
||||||
sequoia-openpgp = "1.3"
|
sequoia-openpgp = "1.3"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
|
@ -33,7 +33,8 @@ 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::{CardBase, CardClientBox, Sex};
|
use openpgp_card::{CardClientBox, Sex};
|
||||||
|
use openpgp_card_scdc::ScdClient;
|
||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
@ -72,9 +73,21 @@ 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() {
|
fn test_sign(mut ca: &mut CardApp, param: &[&str]) -> Result<TestOutput> {
|
||||||
// FIXME
|
assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'");
|
||||||
unimplemented!()
|
|
||||||
|
let res = ca.verify_pw1_for_signing("123456")?;
|
||||||
|
res.check_ok()?;
|
||||||
|
|
||||||
|
let cert = Cert::from_file(param[0])?;
|
||||||
|
|
||||||
|
let msg = "Hello world, I am signed.";
|
||||||
|
let sig = openpgp_card_sequoia::sign(&mut ca, &cert, &mut msg.as_bytes())?;
|
||||||
|
|
||||||
|
// validate sig
|
||||||
|
assert!(util::verify_sig(&cert, msg.as_bytes(), sig.as_bytes())?);
|
||||||
|
|
||||||
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_key_upload_metadata(
|
fn check_key_upload_metadata(
|
||||||
|
@ -251,7 +264,7 @@ fn test_verify(ca: &mut CardApp, _param: &[&str]) -> Result<TestOutput> {
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_test(
|
fn run_test_pcsc(
|
||||||
cards: &[&str],
|
cards: &[&str],
|
||||||
t: fn(&mut CardApp, &[&str]) -> Result<TestOutput>,
|
t: fn(&mut CardApp, &[&str]) -> Result<TestOutput>,
|
||||||
param: &[&str],
|
param: &[&str],
|
||||||
|
@ -260,7 +273,6 @@ fn run_test(
|
||||||
|
|
||||||
for card in PcscClient::list_cards()? {
|
for card in PcscClient::list_cards()? {
|
||||||
let card_client = Box::new(card) as CardClientBox;
|
let card_client = Box::new(card) as CardClientBox;
|
||||||
|
|
||||||
let mut ca = CardApp::new(card_client);
|
let mut ca = CardApp::new(card_client);
|
||||||
|
|
||||||
// Select OpenPGP applet
|
// Select OpenPGP applet
|
||||||
|
@ -271,11 +283,47 @@ fn run_test(
|
||||||
let ard = ca.get_app_data()?;
|
let ard = ca.get_app_data()?;
|
||||||
ca = ca.init_caps(&ard)?;
|
ca = ca.init_caps(&ard)?;
|
||||||
|
|
||||||
let ard = ca.get_app_data()?;
|
|
||||||
let app_id = CardApp::get_aid(&ard)?;
|
let app_id = CardApp::get_aid(&ard)?;
|
||||||
|
|
||||||
if cards.contains(&app_id.ident().as_str()) {
|
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);
|
||||||
|
|
||||||
|
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);
|
let res = t(&mut ca, param);
|
||||||
|
|
||||||
|
@ -290,7 +338,7 @@ fn main() -> Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
// list of card idents to runs the tests on
|
// list of card idents to runs the tests on
|
||||||
let cards = vec![
|
let pcsc_cards = vec![
|
||||||
"0006:16019180", /* Yubikey 5 */
|
"0006:16019180", /* Yubikey 5 */
|
||||||
"0005:0000A835", /* FLOSS Card 3.4 */
|
"0005:0000A835", /* FLOSS Card 3.4 */
|
||||||
"FFFE:57183146", /* Gnuk Rysim (green) */
|
"FFFE:57183146", /* Gnuk Rysim (green) */
|
||||||
|
@ -298,6 +346,15 @@ fn main() -> Result<()> {
|
||||||
// "FFFE:4231EB6E", /* Gnuk FST */
|
// "FFFE:4231EB6E", /* Gnuk FST */
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// list of scdc card serial to runs the tests on
|
||||||
|
let scdc_cards = vec![
|
||||||
|
// "D2760001240103040006160191800000", /* Yubikey 5 */
|
||||||
|
"D27600012401030400050000A8350000", /* FLOSS Card 3.4 */
|
||||||
|
"D276000124010200FFFE571831460000", /* Gnuk Rysim (green) */
|
||||||
|
|
||||||
|
// "D276000124010200FFFE4231EB6E0000", /* Gnuk FST */
|
||||||
|
];
|
||||||
|
|
||||||
// println!("reset");
|
// println!("reset");
|
||||||
// let _ = run_test(&cards, test_reset)?;
|
// let _ = run_test(&cards, test_reset)?;
|
||||||
//
|
//
|
||||||
|
@ -314,19 +371,26 @@ fn main() -> Result<()> {
|
||||||
("data/rsa4k.sec", "data/encrypted_to_rsa4k.asc"),
|
("data/rsa4k.sec", "data/encrypted_to_rsa4k.asc"),
|
||||||
] {
|
] {
|
||||||
// upload keys
|
// upload keys
|
||||||
println!("upload key");
|
println!("Upload key '{}'", key);
|
||||||
let upload_out = run_test(&cards, test_upload_keys, &vec![key])?;
|
let upload_out =
|
||||||
|
run_test_pcsc(&pcsc_cards, test_upload_keys, &vec![key])?;
|
||||||
println!("{:x?}", upload_out);
|
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?
|
||||||
|
|
||||||
// decrypt
|
// decrypt
|
||||||
println!("decrypt");
|
println!("Decrypt");
|
||||||
let dec_out = run_test(&cards, test_decrypt, &vec![key, ciphertext])?;
|
let dec_out =
|
||||||
|
run_test_pcsc(&cards, test_decrypt, &vec![key, ciphertext])?;
|
||||||
println!("{:x?}", dec_out);
|
println!("{:x?}", dec_out);
|
||||||
|
|
||||||
// sign
|
// sign
|
||||||
|
println!("Sign");
|
||||||
|
let sign_out = run_test_pcsc(&pcsc_cards, test_sign, &vec![key])?;
|
||||||
|
println!("{:x?}", sign_out);
|
||||||
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload some key with pw
|
// upload some key with pw
|
||||||
|
|
|
@ -3,14 +3,20 @@
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
|
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::Parse;
|
||||||
use sequoia_openpgp::policy::StandardPolicy;
|
use sequoia_openpgp::policy::StandardPolicy;
|
||||||
use sequoia_openpgp::Cert;
|
use sequoia_openpgp::Cert;
|
||||||
|
|
||||||
use openpgp_card::card_app::CardApp;
|
use openpgp_card::card_app::CardApp;
|
||||||
use openpgp_card::KeyType;
|
use openpgp_card::KeyType;
|
||||||
use openpgp_card_sequoia::vka_as_uploadable_key;
|
use openpgp_card_sequoia::vka_as_uploadable_key;
|
||||||
|
use sequoia_openpgp::parse::stream::{
|
||||||
|
DetachedVerifierBuilder, MessageLayer, MessageStructure,
|
||||||
|
VerificationHelper,
|
||||||
|
};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
pub const SP: &StandardPolicy = &StandardPolicy::new();
|
pub const SP: &StandardPolicy = &StandardPolicy::new();
|
||||||
|
@ -72,3 +78,48 @@ fn get_subkey(
|
||||||
Err(anyhow!("No suitable (sub)key found"))
|
Err(anyhow!("No suitable (sub)key found"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform signature verification for one Cert and a simple signature
|
||||||
|
/// over a message.
|
||||||
|
struct VHelper<'a> {
|
||||||
|
cert: &'a Cert,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> VHelper<'a> {
|
||||||
|
fn new(cert: &'a Cert) -> Self {
|
||||||
|
Self { cert }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> VerificationHelper for VHelper<'a> {
|
||||||
|
fn get_certs(
|
||||||
|
&mut self,
|
||||||
|
_ids: &[openpgp::KeyHandle],
|
||||||
|
) -> openpgp::Result<Vec<openpgp::Cert>> {
|
||||||
|
// Hand out our single Cert
|
||||||
|
Ok(vec![self.cert.clone()])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> {
|
||||||
|
// We are interested in signatures over the data (level 0 signatures)
|
||||||
|
if let Some(MessageLayer::SignatureGroup { results }) =
|
||||||
|
structure.into_iter().next()
|
||||||
|
{
|
||||||
|
match results.into_iter().next() {
|
||||||
|
Some(Ok(_)) => Ok(()), // Good signature.
|
||||||
|
Some(Err(e)) => Err(openpgp::Error::from(e).into()),
|
||||||
|
None => Err(anyhow::anyhow!("No signature")),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("Unexpected message structure"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_sig(cert: &Cert, msg: &[u8], sig: &[u8]) -> Result<bool> {
|
||||||
|
let vh = VHelper::new(cert);
|
||||||
|
let mut dv = DetachedVerifierBuilder::from_bytes(&sig[..])?
|
||||||
|
.with_policy(SP, None, vh)?;
|
||||||
|
|
||||||
|
Ok(dv.verify_bytes(msg).is_ok())
|
||||||
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ use sequoia_openpgp as openpgp;
|
||||||
|
|
||||||
use openpgp_card::card_app::CardApp;
|
use openpgp_card::card_app::CardApp;
|
||||||
use openpgp_card::{
|
use openpgp_card::{
|
||||||
errors::OpenpgpCardError, CardAdmin, CardSign, CardUploadableKey, EccKey,
|
errors::OpenpgpCardError, CardAdmin, CardUploadableKey, EccKey, EccType,
|
||||||
EccType, KeyType, PrivateKeyMaterial, RSAKey,
|
KeyType, PrivateKeyMaterial, RSAKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod decryptor;
|
mod decryptor;
|
||||||
|
@ -292,14 +292,14 @@ pub fn decrypt(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(
|
pub fn sign(
|
||||||
ocu: CardSign,
|
ca: &mut CardApp,
|
||||||
cert: &sequoia_openpgp::Cert,
|
cert: &sequoia_openpgp::Cert,
|
||||||
input: &mut dyn io::Read,
|
input: &mut dyn io::Read,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let mut armorer = armor::Writer::new(vec![], armor::Kind::Signature)?;
|
let mut armorer = armor::Writer::new(vec![], armor::Kind::Signature)?;
|
||||||
{
|
{
|
||||||
let p = StandardPolicy::new();
|
let p = StandardPolicy::new();
|
||||||
let s = signer::CardSigner::new(ocu, cert, &p)?;
|
let s = signer::CardSigner::new(ca, cert, &p)?;
|
||||||
|
|
||||||
let message = Message::new(&mut armorer);
|
let message = Message::new(&mut armorer);
|
||||||
let mut message = Signer::new(message, s).detached().build()?;
|
let mut message = Signer::new(message, s).detached().build()?;
|
||||||
|
|
|
@ -195,14 +195,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
// Sign
|
// Sign
|
||||||
match oc.verify_pw1_for_signing("123456") {
|
match oc.verify_pw1_for_signing("123456") {
|
||||||
Ok(oc_user) => {
|
Ok(mut oc_user) => {
|
||||||
println!("pw1 81 verify ok");
|
println!("pw1 81 verify ok");
|
||||||
|
|
||||||
let cert = Cert::from_file(TEST_KEY_PATH)?;
|
let cert = Cert::from_file(TEST_KEY_PATH)?;
|
||||||
|
|
||||||
let text = "Hello world, I am signed.";
|
let text = "Hello world, I am signed.";
|
||||||
let res = openpgp_card_sequoia::sign(
|
let res = openpgp_card_sequoia::sign(
|
||||||
oc_user,
|
oc_user.get_card_app(),
|
||||||
&cert,
|
&cert,
|
||||||
&mut text.as_bytes(),
|
&mut text.as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,32 +10,33 @@ use openpgp::policy::Policy;
|
||||||
use openpgp::types::PublicKeyAlgorithm;
|
use openpgp::types::PublicKeyAlgorithm;
|
||||||
use sequoia_openpgp as openpgp;
|
use sequoia_openpgp as openpgp;
|
||||||
|
|
||||||
|
use openpgp_card::card_app::CardApp;
|
||||||
use openpgp_card::errors::OpenpgpCardError;
|
use openpgp_card::errors::OpenpgpCardError;
|
||||||
use openpgp_card::CardSign;
|
|
||||||
use openpgp_card::Hash;
|
use openpgp_card::Hash;
|
||||||
|
|
||||||
use crate::PublicKey;
|
use crate::PublicKey;
|
||||||
|
|
||||||
pub(crate) struct CardSigner {
|
pub(crate) struct CardSigner<'a> {
|
||||||
/// The OpenPGP card (authenticated to allow signing operations)
|
/// The OpenPGP card (authenticated to allow signing operations)
|
||||||
ocu: CardSign,
|
ca: &'a mut CardApp,
|
||||||
|
|
||||||
/// The matching public key for the card's signing key
|
/// The matching public key for the card's signing key
|
||||||
public: PublicKey,
|
public: PublicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardSigner {
|
impl<'a> CardSigner<'a> {
|
||||||
/// Try to create a CardSigner.
|
/// Try to create a CardSigner.
|
||||||
///
|
///
|
||||||
/// An Error is returned if no match between the card's signing
|
/// An Error is returned if no match between the card's signing
|
||||||
/// key and a (sub)key of `cert` can be made.
|
/// key and a (sub)key of `cert` can be made.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
cs: CardSign,
|
ca: &'a mut CardApp,
|
||||||
cert: &openpgp::Cert,
|
cert: &openpgp::Cert,
|
||||||
policy: &dyn Policy,
|
policy: &dyn Policy,
|
||||||
) -> Result<CardSigner, OpenpgpCardError> {
|
) -> Result<CardSigner<'a>, OpenpgpCardError> {
|
||||||
// Get the fingerprint for the signing key from the card.
|
// Get the fingerprint for the signing key from the card.
|
||||||
let fps = cs.get_fingerprints()?;
|
let ard = ca.get_app_data()?;
|
||||||
|
let fps = CardApp::get_fingerprints(&ard)?;
|
||||||
let fp = fps.signature();
|
let fp = fps.signature();
|
||||||
|
|
||||||
if let Some(fp) = fp {
|
if let Some(fp) = fp {
|
||||||
|
@ -58,7 +59,7 @@ impl CardSigner {
|
||||||
let public = keys[0].clone();
|
let public = keys[0].clone();
|
||||||
|
|
||||||
Ok(CardSigner {
|
Ok(CardSigner {
|
||||||
ocu: cs,
|
ca,
|
||||||
public: public.role_as_unspecified().clone(),
|
public: public.role_as_unspecified().clone(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -75,7 +76,7 @@ impl CardSigner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> crypto::Signer for CardSigner {
|
impl<'a> crypto::Signer for CardSigner<'a> {
|
||||||
fn public(&self) -> &PublicKey {
|
fn public(&self) -> &PublicKey {
|
||||||
&self.public
|
&self.public
|
||||||
}
|
}
|
||||||
|
@ -122,7 +123,7 @@ impl<'a> crypto::Signer for CardSigner {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let sig = self.ocu.signature_for_hash(hash)?;
|
let sig = self.ca.signature_for_hash(hash)?;
|
||||||
|
|
||||||
let mpi = mpi::MPI::new(&sig[..]);
|
let mpi = mpi::MPI::new(&sig[..]);
|
||||||
Ok(mpi::Signature::RSA { s: mpi })
|
Ok(mpi::Signature::RSA { s: mpi })
|
||||||
|
@ -130,7 +131,7 @@ impl<'a> crypto::Signer for CardSigner {
|
||||||
(PublicKeyAlgorithm::EdDSA, mpi::PublicKey::EdDSA { .. }) => {
|
(PublicKeyAlgorithm::EdDSA, mpi::PublicKey::EdDSA { .. }) => {
|
||||||
let hash = Hash::EdDSA(digest);
|
let hash = Hash::EdDSA(digest);
|
||||||
|
|
||||||
let sig = self.ocu.signature_for_hash(hash)?;
|
let sig = self.ca.signature_for_hash(hash)?;
|
||||||
|
|
||||||
let r = mpi::MPI::new(&sig[..32]);
|
let r = mpi::MPI::new(&sig[..32]);
|
||||||
let s = mpi::MPI::new(&sig[32..]);
|
let s = mpi::MPI::new(&sig[32..]);
|
||||||
|
|
|
@ -50,20 +50,24 @@ impl ScdClient {
|
||||||
Ok(Self { client })
|
Ok(Self { client })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_card(&mut self, serial: &str) -> Result<()> {
|
pub fn select_card(&mut self, serial: &str) -> Result<()> {
|
||||||
let send = format!("SERIALNO --demand={}\n", serial);
|
let send = format!("SERIALNO --demand={}\n", serial);
|
||||||
self.client.send(send)?;
|
self.client.send(send)?;
|
||||||
|
|
||||||
let mut rt = RT.lock().unwrap();
|
let mut rt = RT.lock().unwrap();
|
||||||
|
|
||||||
while let Some(response) = rt.block_on(self.client.next()) {
|
while let Some(response) = rt.block_on(self.client.next()) {
|
||||||
|
log::trace!("select res: {:x?}", response);
|
||||||
|
|
||||||
if response.is_err() {
|
if response.is_err() {
|
||||||
return Err(anyhow!("Card not found"));
|
return Err(anyhow!("Card not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Response::Status { .. }) = response {
|
if let Ok(Response::Status { .. }) = response {
|
||||||
// drop remaining lines
|
// drop remaining lines
|
||||||
while let Some(_drop) = rt.block_on(self.client.next()) {}
|
while let Some(_drop) = rt.block_on(self.client.next()) {
|
||||||
|
log::trace!("select drop: {:x?}", _drop);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -78,7 +82,7 @@ impl CardClient for ScdClient {
|
||||||
let hex = hex::encode(cmd);
|
let hex = hex::encode(cmd);
|
||||||
|
|
||||||
let send = format!("APDU {}\n", hex);
|
let send = format!("APDU {}\n", hex);
|
||||||
println!("send: '{}'", send);
|
log::trace!("send: '{}'", send);
|
||||||
self.client.send(send)?;
|
self.client.send(send)?;
|
||||||
|
|
||||||
let mut rt = RT.lock().unwrap();
|
let mut rt = RT.lock().unwrap();
|
||||||
|
|
Loading…
Reference in a new issue