diff --git a/openpgp-card-sequoia/src/lib.rs b/openpgp-card-sequoia/src/lib.rs index 38d7a8f..e23c5db 100644 --- a/openpgp-card-sequoia/src/lib.rs +++ b/openpgp-card-sequoia/src/lib.rs @@ -4,31 +4,32 @@ //! This library supports using openpgp-card functionality with //! sequoia_openpgp data structures. +use anyhow::{anyhow, Context, Result}; use std::convert::TryFrom; use std::convert::TryInto; use std::error::Error; use std::io; use std::time::SystemTime; -use anyhow::{anyhow, Context, Result}; use openpgp::armor; use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation; use openpgp::crypto::mpi; use openpgp::crypto::mpi::{ProtectedMPI, MPI}; +use openpgp::packet::key::{Key4, PublicParts}; use openpgp::packet::key::{SecretParts, UnspecifiedRole}; use openpgp::packet::{key, Key}; use openpgp::parse::{stream::DecryptorBuilder, Parse}; use openpgp::policy::StandardPolicy; use openpgp::serialize::stream::{Message, Signer}; +use openpgp::types::Timestamp; use sequoia_openpgp as openpgp; use openpgp_card::card_app::CardApp; use openpgp_card::{ - errors::OpenpgpCardError, CardAdmin, CardUploadableKey, EccKey, EccType, - KeyType, PrivateKeyMaterial, PublicKeyMaterial, RSAKey, + errors::OpenpgpCardError, Algo, CardAdmin, CardUploadableKey, Curve, + EccKey, EccType, KeyType, PrivateKeyMaterial, PublicKeyMaterial, RSAKey, }; -use sequoia_openpgp::packet::key::{Key4, PublicParts}; -use sequoia_openpgp::types::Timestamp; +use sequoia_openpgp::types::PublicKeyAlgorithm; mod decryptor; mod signer; @@ -74,6 +75,7 @@ pub fn vka_as_uploadable_key( /// Helper fn: get a Key for a PublicKeyMaterial pub fn public_key_material_to_key( pkm: &PublicKeyMaterial, + key_type: KeyType, time: SystemTime, ) -> Result> { match pkm { @@ -83,7 +85,73 @@ pub fn public_key_material_to_key( Ok(Key::from(k4)) } - _ => unimplemented!("ECC not implemented yet"), + PublicKeyMaterial::E(ecc) => { + let algo = ecc.algo.clone(); // FIXME? + if let Algo::Ecc(algo_ecc) = algo { + match key_type { + KeyType::Authentication | KeyType::Signing => { + if algo_ecc.curve == Curve::Ed25519 { + // EdDSA + let k4: Key4< + key::PublicParts, + key::UnspecifiedRole, + > = Key4::import_public_ed25519(&ecc.data, time)?; + + println!("k4 {:?}", k4); + + Ok(Key::from(k4)) + } else { + // ECDSA + + // The public key for ECDSA/DH consists of of two raw + // big-endian integers with the same length as a field element + // each. In compliance with EN 419212 the format is 04 || x || y + // where the first byte (04) indicates an uncompressed raw format. + + let ec = &ecc.data; + + assert_eq!(ec[0], 0x4); + + let len = ec.len(); + assert_eq!(len % 2, 1); // odd number of bytes + + // --- + + let curve = match algo_ecc.curve { + Curve::NistP256r1 => { + openpgp::types::Curve::NistP256 + } + Curve::NistP384r1 => { + openpgp::types::Curve::NistP384 + } + Curve::NistP521r1 => { + openpgp::types::Curve::NistP521 + } + _ => unimplemented!(), + }; + + // NIST + let k4 = Key4::new( + time, + PublicKeyAlgorithm::ECDSA, + mpi::PublicKey::ECDSA { + curve, + q: mpi::MPI::new(&ec), + }, + )?; + + Ok(Key::from(k4)) + } + } + KeyType::Decryption => { + unimplemented!("Decryption keys not implemented yet") + } + _ => unimplemented!("Unsupported KeyType"), + } + } else { + panic!("unexpected algo {:?}", algo); + } + } } } diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index dca0367..a58e138 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -18,16 +18,15 @@ use anyhow::{anyhow, Result}; use crate::apdu::{commands, response::Response}; use crate::errors::OpenpgpCardError; use crate::parse::{ - algo_attrs::Algo, algo_attrs::RsaAttrs, algo_info::AlgoInfo, - application_id::ApplicationId, cardholder::CardHolder, - extended_cap::ExtendedCap, extended_length_info::ExtendedLengthInfo, - fingerprint, historical::Historical, key_generation_times, - pw_status::PWStatus, KeySet, + algo_info::AlgoInfo, application_id::ApplicationId, + cardholder::CardHolder, extended_cap::ExtendedCap, + extended_length_info::ExtendedLengthInfo, fingerprint, + historical::Historical, key_generation_times, pw_status::PWStatus, KeySet, }; use crate::tlv::{tag::Tag, Tlv, TlvEntry}; use crate::{ - apdu, keys, CardCaps, CardClientBox, CardUploadableKey, DecryptMe, - EccType, Hash, KeyGeneration, KeyType, PublicKeyMaterial, Sex, + apdu, keys, Algo, CardCaps, CardClientBox, CardUploadableKey, DecryptMe, + EccType, Hash, KeyGeneration, KeyType, PublicKeyMaterial, RsaAttrs, Sex, }; pub struct CardApp { @@ -573,7 +572,7 @@ impl CardApp { let data = match algo { Algo::Rsa(rsa) => Self::rsa_algo_attrs(rsa)?, - Algo::Ecc(ecc) => Self::ecc_algo_attrs(&ecc.oid, ecc.ecc_type), + Algo::Ecc(ecc) => Self::ecc_algo_attrs(ecc.oid(), ecc.ecc_type), _ => unimplemented!(), }; @@ -635,14 +634,22 @@ impl CardApp { keys::upload_key(self, key, key_type, algo_list) } - // FIXME: use subset of CardUploadableKey to specify algo? + /// Generate a key on the card. + /// If the `algo` parameter is Some, then this algorithm will be set on + /// the card for "key_type". pub fn generate_key( &mut self, - fp_from_pub: fn(&PublicKeyMaterial, SystemTime) -> Result<[u8; 20]>, + fp_from_pub: fn( + &PublicKeyMaterial, + SystemTime, + KeyType, + &Algo, + ) -> Result<[u8; 20]>, key_type: KeyType, - ) -> Result<(), OpenpgpCardError> { + algo: Option<&Algo>, + ) -> Result<(PublicKeyMaterial, u32), OpenpgpCardError> { // FIXME: specify algo; pass in algo list? - keys::gen_key_with_metadata(self, fp_from_pub, key_type) + keys::gen_key_with_metadata(self, fp_from_pub, key_type, algo) } pub fn get_pub_key( diff --git a/openpgp-card/src/keys.rs b/openpgp-card/src/keys.rs index 0b68c81..5cf3818 100644 --- a/openpgp-card/src/keys.rs +++ b/openpgp-card/src/keys.rs @@ -10,22 +10,47 @@ use crate::apdu::command::Command; use crate::apdu::commands; use crate::card_app::CardApp; use crate::errors::OpenpgpCardError; -use crate::parse::algo_attrs::{Algo, EccAttrs, RsaAttrs}; use crate::parse::algo_info::AlgoInfo; use crate::tlv::{tag::Tag, Tlv, TlvEntry}; -use crate::{apdu, EccPub, PublicKeyMaterial, RSAPub}; +use crate::{apdu, Curve, EccPub, PublicKeyMaterial, RSAPub}; use crate::{ - tlv, CardUploadableKey, EccKey, KeyType, PrivateKeyMaterial, RSAKey, + tlv, Algo, CardUploadableKey, EccAttrs, EccKey, KeyType, + PrivateKeyMaterial, RSAKey, RsaAttrs, }; /// `gen_key_with_metadata` calculates the fingerprint for a public key /// data object pub(crate) fn gen_key_with_metadata( card_app: &mut CardApp, - fp_from_pub: fn(&PublicKeyMaterial, SystemTime) -> Result<[u8; 20]>, + fp_from_pub: fn( + &PublicKeyMaterial, + SystemTime, + KeyType, + &Algo, + ) -> Result<[u8; 20]>, key_type: KeyType, -) -> Result<(), OpenpgpCardError> { - let pubkey = gen_key(card_app, key_type)?; + algo: Option<&Algo>, +) -> Result<(PublicKeyMaterial, u32), OpenpgpCardError> { + // set algo on card if it's Some + if let Some(algo) = algo { + println!("set algo {:?}", algo); + card_app + .set_algorithm_attributes(key_type, algo)? + .check_ok()?; + println!("set algo done"); + } + + // algo + let ard = card_app.get_app_data()?; // no caching, here! + let algo = CardApp::get_algorithm_attributes(&ard, key_type)?; + + // generate key + let tlv = gen_key(card_app, key_type)?; + + // derive pubkey + let pubkey = tlv_to_pubkey(&tlv, &algo)?; + + log::trace!("public {:x?}", pubkey); // set creation time let time = SystemTime::now(); @@ -39,13 +64,13 @@ pub(crate) fn gen_key_with_metadata( card_app.set_creation_time(ts, key_type)?.check_ok()?; // calculate/store fingerprint - let fp = fp_from_pub(&pubkey, time)?; + let fp = fp_from_pub(&pubkey, time, key_type, &algo)?; card_app.set_fingerprint(fp, key_type)?.check_ok()?; - Ok(()) + Ok((pubkey, ts)) } -fn tlv_to_pubkey(tlv: &Tlv) -> Result { +fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result { let n = tlv.find(&Tag::new(vec![0x81])); let v = tlv.find(&Tag::new(vec![0x82])); @@ -61,26 +86,13 @@ fn tlv_to_pubkey(tlv: &Tlv) -> Result { Ok(PublicKeyMaterial::R(rsa)) } (None, None, Some(ec)) => { - let ec = ec.serialize(); + let data = ec.serialize(); + println!("EC --- len {}, data {:x?}", data.len(), data); - // The public key for ECDSA/DH consists of of two raw - // big-endian integers with the same length as a field element - // each. In compliance with EN 419212 the format is 04 || x || y - // where the first byte (04) indicates an uncompressed raw format. - - assert_eq!(ec[0], 0x4); - - let len = ec.len(); - assert_eq!(len % 2, 1); // odd number of bytes - - // len 3 -> 4/2 = 2 - let middle = (len + 1) / 2; - let x = ec[1..middle].to_vec(); - let y = ec[middle..].to_vec(); - - let ecc = EccPub { x, y }; - - Ok(PublicKeyMaterial::E(ecc)) + Ok(PublicKeyMaterial::E(EccPub { + data, + algo: algo.clone(), + })) } (_, _, _) => { @@ -92,7 +104,7 @@ fn tlv_to_pubkey(tlv: &Tlv) -> Result { pub(crate) fn gen_key( card_app: &mut CardApp, key_type: KeyType, -) -> Result { +) -> Result { println!("gen key for {:?}", key_type); // generate key @@ -106,11 +118,7 @@ pub(crate) fn gen_key( let tlv = Tlv::try_from(resp.data()?)?; - let pubkey = tlv_to_pubkey(&tlv)?; - - log::trace!("public {:x?}", pubkey); - - Ok(pubkey) + Ok(tlv) } pub(crate) fn get_pub_key( @@ -119,17 +127,19 @@ pub(crate) fn get_pub_key( ) -> Result { println!("get pub key for {:?}", key_type); - let card_client = card_app.card(); + // algo + let ard = card_app.get_app_data()?; // FIXME: caching + let algo = CardApp::get_algorithm_attributes(&ard, key_type)?; // get public key let crt = get_crt(key_type)?; let get_pub_key_cmd = commands::get_pub_key(crt.serialize().to_vec()); - let resp = apdu::send_command(card_client, get_pub_key_cmd, true)?; + let resp = apdu::send_command(card_app.card(), get_pub_key_cmd, true)?; resp.check_ok()?; let tlv = Tlv::try_from(resp.data()?)?; - let pubkey = tlv_to_pubkey(&tlv)?; + let pubkey = tlv_to_pubkey(&tlv, &algo)?; Ok(pubkey) } @@ -192,7 +202,7 @@ pub(crate) fn upload_key( let algo = Algo::Ecc(EccAttrs { ecc_type: ecc_key.get_type(), - oid: ecc_key.get_oid().to_vec(), + curve: Curve::from(ecc_key.get_oid()).expect("unepected oid"), import_format: None, }); @@ -264,7 +274,7 @@ fn check_card_algo_ecdh( .collect(); // Check if this OID exists in the supported algorithms - ecdh_algos.iter().any(|e| e.oid == oid) + ecdh_algos.iter().any(|e| e.oid() == oid) } // FIXME: refactor, these checks currently pointlessly duplicate code @@ -288,7 +298,7 @@ fn check_card_algo_ecdsa( .collect(); // Check if this OID exists in the supported algorithms - ecdsa_algos.iter().any(|e| e.oid == oid) + ecdsa_algos.iter().any(|e| e.oid() == oid) } // FIXME: refactor, these checks currently pointlessly duplicate code @@ -312,7 +322,7 @@ fn check_card_algo_eddsa( .collect(); // Check if this OID exists in the supported algorithms - eddsa_algos.iter().any(|e| e.oid == oid) + eddsa_algos.iter().any(|e| e.oid() == oid) } fn ecc_key_cmd( diff --git a/openpgp-card/src/lib.rs b/openpgp-card/src/lib.rs index ca5d7a9..801fb51 100644 --- a/openpgp-card/src/lib.rs +++ b/openpgp-card/src/lib.rs @@ -265,14 +265,6 @@ pub trait CardUploadableKey { pub enum PublicKeyMaterial { R(RSAPub), E(EccPub), - T(Tffon), // 25519 -} - -/// ed25519/cv25519 -#[derive(Debug)] -pub struct Tffon { - /// Public key - pub pk: Vec, } /// RSA-specific container for public key material from an OpenPGP card. @@ -288,8 +280,8 @@ pub struct RSAPub { /// ECC-specific container for public key material from an OpenPGP card. #[derive(Debug)] pub struct EccPub { - pub x: Vec, - pub y: Vec, + pub data: Vec, + pub algo: Algo, } /// Algorithm-independent container for private key material to upload to