diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index a80ee51..46181ba 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -66,7 +66,7 @@ pub fn test_decrypt( let cert = Cert::from_str(param[0])?; let msg = param[1].to_string(); - CardApp::verify_pw1(card_client, "123456")?; + card_client.verify_pw1("123456")?; let p = StandardPolicy::new(); @@ -90,7 +90,7 @@ pub fn test_sign( ) -> Result { assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'"); - CardApp::verify_pw1_for_signing(card_client, "123456")?; + card_client.verify_pw1_for_signing("123456")?; let cert = Cert::from_str(param[0])?; @@ -111,7 +111,7 @@ fn check_key_upload_metadata( card_client: &mut (dyn CardClient + Send + Sync), meta: &[(String, KeyGenerationTime)], ) -> Result<()> { - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; // check fingerprints let card_fp = ard.fingerprints()?; @@ -155,7 +155,7 @@ pub fn test_print_caps( card_client: &mut (dyn CardClient + Send + Sync), _param: &[&str], ) -> Result { - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; let aid = ard.application_id()?; println!("aid: {:#x?}", aid); @@ -176,14 +176,14 @@ pub fn test_print_algo_info( card_client: &mut (dyn CardClient + Send + Sync), _param: &[&str], ) -> Result { - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; let dec = ard.algorithm_attributes(KeyType::Decryption)?; println!("Current algorithm for the decrypt slot: {}", dec); println!(); - let algo = CardApp::algorithm_information(card_client); + let algo = card_client.algorithm_information(); if let Ok(Some(algo)) = algo { println!("Card algorithm list:\n{}", algo); } @@ -201,7 +201,7 @@ pub fn test_upload_keys( "test_upload_keys needs a filename for 'cert'" ); - CardApp::verify_pw3(card_client, "12345678")?; + card_client.verify_pw3("12345678")?; let cert = Cert::from_file(param[0])?; @@ -223,7 +223,7 @@ pub fn test_keygen( card_client: &mut (dyn CardClient + Send + Sync), param: &[&str], ) -> Result { - CardApp::verify_pw3(card_client, "12345678")?; + card_client.verify_pw3("12345678")?; // Generate all three subkeys on card let algo = param[0]; @@ -231,8 +231,7 @@ pub fn test_keygen( let alg = AlgoSimple::try_from(algo)?; println!(" Generate subkey for Signing"); - let (pkm, ts) = CardApp::generate_key_simple( - card_client, + let (pkm, ts) = card_client.generate_key_simple( public_to_fingerprint, KeyType::Signing, alg, @@ -240,8 +239,7 @@ pub fn test_keygen( let key_sig = public_key_material_to_key(&pkm, KeyType::Signing, ts)?; println!(" Generate subkey for Decryption"); - let (pkm, ts) = CardApp::generate_key_simple( - card_client, + let (pkm, ts) = card_client.generate_key_simple( public_to_fingerprint, KeyType::Decryption, alg, @@ -249,8 +247,7 @@ pub fn test_keygen( let key_dec = public_key_material_to_key(&pkm, KeyType::Decryption, ts)?; println!(" Generate subkey for Authentication"); - let (pkm, ts) = CardApp::generate_key_simple( - card_client, + let (pkm, ts) = card_client.generate_key_simple( public_to_fingerprint, KeyType::Authentication, alg, @@ -280,12 +277,12 @@ pub fn test_get_pub( card_client: &mut (dyn CardClient + Send + Sync), _param: &[&str], ) -> Result { - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; let key_gen = ard.key_generation_times()?; // -- - let sig = CardApp::public_key(card_client, KeyType::Signing)?; + let sig = card_client.public_key(KeyType::Signing)?; let ts = key_gen.signature().unwrap().get().into(); let key = public_key_material_to_key(&sig, KeyType::Signing, ts)?; @@ -293,7 +290,7 @@ pub fn test_get_pub( // -- - let dec = CardApp::public_key(card_client, KeyType::Decryption)?; + let dec = card_client.public_key(KeyType::Decryption)?; let ts = key_gen.decryption().unwrap().get().into(); let key = public_key_material_to_key(&dec, KeyType::Decryption, ts)?; @@ -301,7 +298,7 @@ pub fn test_get_pub( // -- - let auth = CardApp::public_key(card_client, KeyType::Authentication)?; + let auth = card_client.public_key(KeyType::Authentication)?; let ts = key_gen.authentication().unwrap().get().into(); let key = public_key_material_to_key(&auth, KeyType::Authentication, ts)?; @@ -319,7 +316,7 @@ pub fn test_reset( card_client: &mut (dyn CardClient + Send + Sync), _param: &[&str], ) -> Result { - let _res = CardApp::factory_reset(card_client)?; + let _res = card_client.factory_reset()?; Ok(vec![]) } @@ -332,22 +329,22 @@ pub fn test_set_user_data( card_client: &mut (dyn CardClient + Send + Sync), _param: &[&str], ) -> Result { - CardApp::verify_pw3(card_client, "12345678")?; + card_client.verify_pw3("12345678")?; // name - CardApp::set_name(card_client, "Bar< { out.push(TestResult::Text(format!("got cert {:x?}", res.data()))) } @@ -432,11 +413,11 @@ pub fn test_cardholder_cert( } }; - CardApp::verify_pw3(card_client, "12345678")?; + card_client.verify_pw3("12345678")?; let data = "Foo bar baz!".as_bytes(); - match CardApp::set_cardholder_certificate(card_client, data.to_vec()) { + match card_client.set_cardholder_certificate(data.to_vec()) { Ok(_resp) => out.push(TestResult::Text("set cert ok".to_string())), Err(e) => { out.push(TestResult::Text(format!( @@ -447,7 +428,7 @@ pub fn test_cardholder_cert( } } - let res = CardApp::cardholder_certificate(card_client)?; + let res = card_client.cardholder_certificate()?; out.push(TestResult::Text("get cert ok".to_string())); if res.data() != data { @@ -460,7 +441,7 @@ pub fn test_cardholder_cert( // try using slot 2 - match CardApp::select_data(card_client, 2, &[0x7F, 0x21]) { + match card_client.select_data(2, &[0x7F, 0x21]) { Ok(_res) => out.push(TestResult::Text("select_data ok".to_string())), Err(e) => { out.push(TestResult::Text(format!("select_data: {:?}", e))); @@ -477,19 +458,19 @@ pub fn test_pw_status( ) -> Result { let out = vec![]; - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; let mut pws = ard.pw_status_bytes()?; println!("pws {:?}", pws); - CardApp::verify_pw3(card_client, "12345678")?; + card_client.verify_pw3("12345678")?; pws.set_pw1_cds_valid_once(false); pws.set_pw1_pin_block(true); - CardApp::set_pw_status_bytes(card_client, &pws, false)?; + card_client.set_pw_status_bytes(&pws, false)?; - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; let pws = ard.pw_status_bytes()?; println!("pws {:?}", pws); @@ -518,7 +499,7 @@ pub fn test_verify( let mut out = vec![]; // try to set name without verify, assert result is not ok! - let res = CardApp::set_name(card_client, "Notverified< { // e.g. yubikey5 returns an error status! out.push(TestResult::Status(s)); @@ -539,14 +520,14 @@ pub fn test_verify( Ok(_) => out.push(TestResult::StatusOk), } - CardApp::set_name(card_client, "Admin< { // e.g. yubikey5 returns an error status! out.push(TestResult::Status(s)); @@ -557,9 +538,9 @@ pub fn test_verify( Ok(_) => out.push(TestResult::StatusOk), } - CardApp::set_name(card_client, "There< { // this is expected } @@ -598,10 +579,10 @@ pub fn test_change_pw( } println!("verify good pw1"); - CardApp::verify_pw1(card_client, "abcdef")?; + card_client.verify_pw1("abcdef")?; println!("verify bad pw3"); - match CardApp::verify_pw3(card_client, "00000000") { + match card_client.verify_pw3("00000000") { Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => { // this is expected } @@ -612,13 +593,13 @@ pub fn test_change_pw( } println!("verify good pw3"); - CardApp::verify_pw3(card_client, "abcdefgh")?; + card_client.verify_pw3("abcdefgh")?; println!("change pw3 back to default"); - CardApp::change_pw3(card_client, "abcdefgh", "12345678")?; + card_client.change_pw3("abcdefgh", "12345678")?; println!("change pw1 back to default"); - CardApp::change_pw1(card_client, "abcdef", "123456")?; + card_client.change_pw1("abcdef", "123456")?; Ok(out) } @@ -631,15 +612,15 @@ pub fn test_reset_retry_counter( // set pw3, then pw1 (to bring gnuk into non-admin mode) println!("set pw3"); - CardApp::change_pw3(card_client, "12345678", "12345678")?; + card_client.change_pw3("12345678", "12345678")?; println!("set pw1"); - CardApp::change_pw1(card_client, "123456", "123456")?; + card_client.change_pw1("123456", "123456")?; println!("break pw1"); - let _ = CardApp::verify_pw1(card_client, "wrong0"); - let _ = CardApp::verify_pw1(card_client, "wrong0"); - let _ = CardApp::verify_pw1(card_client, "wrong0"); - let res = CardApp::verify_pw1(card_client, "wrong0"); + let _ = card_client.verify_pw1("wrong0"); + let _ = card_client.verify_pw1("wrong0"); + let _ = card_client.verify_pw1("wrong0"); + let res = card_client.verify_pw1("wrong0"); match res { Err(Error::CardStatus(StatusBytes::AuthenticationMethodBlocked)) => { @@ -660,24 +641,23 @@ pub fn test_reset_retry_counter( } println!("verify pw3"); - CardApp::verify_pw3(card_client, "12345678")?; + card_client.verify_pw3("12345678")?; println!("set resetting code"); - CardApp::set_resetting_code(card_client, "abcdefgh".as_bytes().to_vec())?; + card_client.set_resetting_code("abcdefgh".as_bytes().to_vec())?; println!("reset retry counter"); // ca.reset_retry_counter_pw1("abcdef".as_bytes().to_vec(), None)?; - let _res = CardApp::reset_retry_counter_pw1( - card_client, + let _res = card_client.reset_retry_counter_pw1( "abcdef".as_bytes().to_vec(), Some("abcdefgh".as_bytes().to_vec()), ); println!("verify good pw1"); - CardApp::verify_pw1(card_client, "abcdef")?; + card_client.verify_pw1("abcdef")?; println!("verify bad pw1"); - match CardApp::verify_pw1(card_client, "00000000") { + match card_client.verify_pw1("00000000") { Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => { // this is expected } diff --git a/card-functionality/src/util.rs b/card-functionality/src/util.rs index 0049910..ca720c0 100644 --- a/card-functionality/src/util.rs +++ b/card-functionality/src/util.rs @@ -49,7 +49,7 @@ pub(crate) fn upload_subkeys( // upload key let cuk = vka_as_uploadable_key(vka, None); - CardApp::key_import(card_client, cuk, *kt)?; + card_client.key_import(cuk, *kt)?; } } diff --git a/openpgp-card-sequoia/src/card.rs b/openpgp-card-sequoia/src/card.rs index cb11936..bfd259d 100644 --- a/openpgp-card-sequoia/src/card.rs +++ b/openpgp-card-sequoia/src/card.rs @@ -49,7 +49,7 @@ impl<'a> Open<'a> { pub fn new( card_client: &'a mut (dyn CardClient + Send + Sync), ) -> Result { - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; Ok(Self { card_client, @@ -60,15 +60,15 @@ impl<'a> Open<'a> { }) } pub fn feature_pinpad_verify(&mut self) -> bool { - CardApp::feature_pinpad_verify(self.card_client) + self.card_client.feature_pinpad_verify() } pub fn feature_pinpad_modify(&mut self) -> bool { - CardApp::feature_pinpad_modify(self.card_client) + self.card_client.feature_pinpad_modify() } pub fn verify_user(&mut self, pin: &str) -> Result<(), Error> { - let _ = CardApp::verify_pw1(self.card_client, pin)?; + let _ = self.card_client.verify_pw1(pin)?; self.pw1 = true; Ok(()) } @@ -79,13 +79,13 @@ impl<'a> Open<'a> { ) -> Result<(), Error> { prompt(); - let _ = CardApp::verify_pw1_pinpad(self.card_client)?; + let _ = self.card_client.verify_pw1_pinpad()?; self.pw1 = true; Ok(()) } pub fn verify_user_for_signing(&mut self, pin: &str) -> Result<(), Error> { - let _ = CardApp::verify_pw1_for_signing(self.card_client, pin)?; + let _ = self.card_client.verify_pw1_for_signing(pin)?; // FIXME: depending on card mode, pw1_sign is only usable once @@ -99,7 +99,7 @@ impl<'a> Open<'a> { ) -> Result<(), Error> { prompt(); - let _ = CardApp::verify_pw1_for_signing_pinpad(self.card_client)?; + let _ = self.card_client.verify_pw1_for_signing_pinpad()?; // FIXME: depending on card mode, pw1_sign is only usable once @@ -108,7 +108,7 @@ impl<'a> Open<'a> { } pub fn verify_admin(&mut self, pin: &str) -> Result<(), Error> { - let _ = CardApp::verify_pw3(self.card_client, pin)?; + let _ = self.card_client.verify_pw3(pin)?; self.pw3 = true; Ok(()) } @@ -119,7 +119,7 @@ impl<'a> Open<'a> { ) -> Result<(), Error> { prompt(); - let _ = CardApp::verify_pw3_pinpad(self.card_client)?; + let _ = self.card_client.verify_pw3_pinpad()?; self.pw3 = true; Ok(()) } @@ -128,14 +128,14 @@ impl<'a> Open<'a> { /// /// NOTE: on some cards this functionality seems broken. pub fn check_user_verified(&mut self) -> Result { - CardApp::check_pw1(self.card_client) + self.card_client.check_pw1() } /// Ask the card if the admin password has been successfully verified. /// /// NOTE: on some cards this functionality seems broken. pub fn check_admin_verified(&mut self) -> Result { - CardApp::check_pw3(self.card_client) + self.card_client.check_pw3() } pub fn change_user_pin( @@ -143,7 +143,7 @@ impl<'a> Open<'a> { old: &str, new: &str, ) -> Result { - CardApp::change_pw1(self.card_client, old, new) + self.card_client.change_pw1(old, new) } pub fn change_user_pin_pinpad( @@ -151,7 +151,7 @@ impl<'a> Open<'a> { prompt: &dyn Fn(), ) -> Result { prompt(); - CardApp::change_pw1_pinpad(self.card_client) + self.card_client.change_pw1_pinpad() } pub fn reset_user_pin( @@ -159,11 +159,8 @@ impl<'a> Open<'a> { rst: &str, new: &str, ) -> Result { - CardApp::reset_retry_counter_pw1( - self.card_client, - new.into(), - Some(rst.into()), - ) + self.card_client + .reset_retry_counter_pw1(new.into(), Some(rst.into())) } pub fn change_admin_pin( @@ -171,7 +168,7 @@ impl<'a> Open<'a> { old: &str, new: &str, ) -> Result { - CardApp::change_pw3(self.card_client, old, new) + self.card_client.change_pw3(old, new) } pub fn change_admin_pin_pinpad( @@ -179,7 +176,7 @@ impl<'a> Open<'a> { prompt: &dyn Fn(), ) -> Result { prompt(); - CardApp::change_pw3_pinpad(self.card_client) + self.card_client.change_pw3_pinpad() } /// Get a view of the card authenticated for "User" commands. @@ -299,21 +296,21 @@ impl<'a> Open<'a> { // --- URL (5f50) --- pub fn url(&mut self) -> Result { - CardApp::url(self.card_client) + self.card_client.url() } // --- cardholder related data (65) --- pub fn cardholder_related_data( &mut self, ) -> Result { - CardApp::cardholder_related_data(self.card_client) + self.card_client.cardholder_related_data() } // --- security support template (7a) --- pub fn security_support_template( &mut self, ) -> Result { - CardApp::security_support_template(self.card_client) + self.card_client.security_support_template() } // DO "Algorithm Information" (0xFA) @@ -327,12 +324,12 @@ impl<'a> Open<'a> { return Ok(None); } - CardApp::algorithm_information(self.card_client) + self.card_client.algorithm_information() } /// Firmware Version, YubiKey specific (?) pub fn firmware_version(&mut self) -> Result> { - CardApp::firmware_version(self.card_client) + self.card_client.firmware_version() } // ---------- @@ -341,14 +338,14 @@ impl<'a> Open<'a> { &mut self, key_type: KeyType, ) -> Result { - CardApp::public_key(self.card_client, key_type).map_err(|e| e.into()) + self.card_client.public_key(key_type).map_err(|e| e.into()) } // ---------- /// Delete all state on this OpenPGP card pub fn factory_reset(&mut self) -> Result<()> { - CardApp::factory_reset(self.card_client) + self.card_client.factory_reset() } } @@ -411,7 +408,7 @@ impl Admin<'_, '_> { return Err(anyhow!("Invalid char in name").into()); }; - CardApp::set_name(self.oc.card_client, name) + self.oc.card_client.set_name(name) } pub fn set_lang(&mut self, lang: &str) -> Result { @@ -419,11 +416,11 @@ impl Admin<'_, '_> { return Err(anyhow!("lang too long").into()); } - CardApp::set_lang(self.oc.card_client, lang) + self.oc.card_client.set_lang(lang) } pub fn set_sex(&mut self, sex: Sex) -> Result { - CardApp::set_sex(self.oc.card_client, sex) + self.oc.card_client.set_sex(sex) } pub fn set_url(&mut self, url: &str) -> Result { @@ -441,7 +438,7 @@ impl Admin<'_, '_> { // or if it's within the acceptable length: // send the url update to the card. - CardApp::set_url(self.oc.card_client, url) + self.oc.card_client.set_url(url) } else { Err(anyhow!("URL too long").into()) } @@ -451,11 +448,13 @@ impl Admin<'_, '_> { &mut self, pin: &str, ) -> Result { - CardApp::set_resetting_code(self.oc.card_client, pin.into()) + self.oc.card_client.set_resetting_code(pin.into()) } pub fn reset_user_pin(&mut self, new: &str) -> Result { - CardApp::reset_retry_counter_pw1(self.oc.card_client, new.into(), None) + self.oc + .card_client + .reset_retry_counter_pw1(new.into(), None) } /// Upload a ValidErasedKeyAmalgamation to the card as a specific KeyType. @@ -468,7 +467,7 @@ impl Admin<'_, '_> { password: Option, ) -> Result<(), Error> { let key = vka_as_uploadable_key(vka, password); - CardApp::key_import(self.oc.card_client, key, key_type) + self.oc.card_client.key_import(key, key_type) } pub fn generate_key_simple( @@ -477,14 +476,12 @@ impl Admin<'_, '_> { algo: Option, ) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> { match algo { - Some(algo) => CardApp::generate_key_simple( - self.oc.card_client, + Some(algo) => self.oc.card_client.generate_key_simple( public_to_fingerprint, key_type, algo, ), - None => CardApp::generate_key( - self.oc.card_client, + None => self.oc.card_client.generate_key( public_to_fingerprint, key_type, None, diff --git a/openpgp-card-sequoia/src/decryptor.rs b/openpgp-card-sequoia/src/decryptor.rs index 1ebcdbf..5085fbf 100644 --- a/openpgp-card-sequoia/src/decryptor.rs +++ b/openpgp-card-sequoia/src/decryptor.rs @@ -38,7 +38,7 @@ impl<'a> CardDecryptor<'a> { cert: &Cert, ) -> Result, Error> { // Get the fingerprint for the decryption key from the card. - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; let fps = ard.fingerprints()?; let fp = fps.decryption(); @@ -85,7 +85,7 @@ impl<'a> crypto::Decryptor for CardDecryptor<'a> { match (ciphertext, self.public.mpis()) { (mpi::Ciphertext::RSA { c: ct }, mpi::PublicKey::RSA { .. }) => { let dm = Cryptogram::RSA(ct.value()); - let dec = CardApp::decipher(self.card_client, dm)?; + let dec = self.card_client.decipher(dm)?; let sk = openpgp::crypto::SessionKey::from(&dec[..]); Ok(sk) @@ -109,7 +109,7 @@ impl<'a> crypto::Decryptor for CardDecryptor<'a> { }; // Decryption operation on the card - let mut dec = CardApp::decipher(self.card_client, dm)?; + let mut dec = self.card_client.decipher(dm)?; // Specifically handle return value format like Gnuk's // (Gnuk returns a leading '0x04' byte and diff --git a/openpgp-card-sequoia/src/signer.rs b/openpgp-card-sequoia/src/signer.rs index 69c627f..92ca700 100644 --- a/openpgp-card-sequoia/src/signer.rs +++ b/openpgp-card-sequoia/src/signer.rs @@ -34,7 +34,7 @@ impl<'a> CardSigner<'a> { cert: &openpgp::Cert, ) -> Result, Error> { // Get the fingerprint for the signing key from the card. - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; let fps = ard.fingerprints()?; let fp = fps.signature(); @@ -117,7 +117,7 @@ impl<'a> crypto::Signer for CardSigner<'a> { } }; - let sig = CardApp::signature_for_hash(self.card_client, hash)?; + let sig = self.card_client.signature_for_hash(hash)?; let mpi = mpi::MPI::new(&sig[..]); Ok(mpi::Signature::RSA { s: mpi }) @@ -125,7 +125,7 @@ impl<'a> crypto::Signer for CardSigner<'a> { (PublicKeyAlgorithm::EdDSA, mpi::PublicKey::EdDSA { .. }) => { let hash = Hash::EdDSA(digest); - let sig = CardApp::signature_for_hash(self.card_client, hash)?; + let sig = self.card_client.signature_for_hash(hash)?; let r = mpi::MPI::new(&sig[..32]); let s = mpi::MPI::new(&sig[32..]); @@ -143,7 +143,7 @@ impl<'a> crypto::Signer for CardSigner<'a> { _ => Hash::ECDSA(digest), }; - let sig = CardApp::signature_for_hash(self.card_client, hash)?; + let sig = self.card_client.signature_for_hash(hash)?; let len_2 = sig.len() / 2; let r = mpi::MPI::new(&sig[..len_2]); diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index d7eba3a..d527668 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -3,25 +3,6 @@ //! CardApp exposes functionality of the "OpenPGP card" application. -use std::convert::TryFrom; -use std::convert::TryInto; - -use anyhow::{anyhow, Result}; - -use crate::algorithm::{Algo, AlgoInfo, AlgoSimple}; -use crate::apdu::response::RawResponse; -use crate::apdu::{commands, response::Response}; -use crate::card_do::{ - ApplicationRelatedData, CardholderRelatedData, Fingerprint, - KeyGenerationTime, PWStatusBytes, SecuritySupportTemplate, Sex, -}; -use crate::crypto_data::{ - CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial, -}; -use crate::tlv::{tag::Tag, value::Value, Tlv}; -use crate::{apdu, keys, CardCaps, CardClient, KeyType, SmartcardError}; -use crate::{Error, StatusBytes}; - /// Low-level access to OpenPGP card functionality. /// /// Not many checks are performed here (e.g. for valid data lengths). @@ -31,888 +12,4 @@ use crate::{Error, StatusBytes}; /// be done on a higher layer. pub struct CardApp {} -impl CardApp { - /// Get a CardApp based on a CardClient. - /// - /// It is expected that SELECT has already been performed on the card - /// beforehand. - /// - /// This fn initializes the CardCaps by requesting - /// application_related_data from the card, and setting the - /// capabilities accordingly. - pub fn initialize(card_client: &mut dyn CardClient) -> Result<()> { - let ard = Self::application_related_data(card_client)?; - Self::init_caps(card_client, &ard)?; - - Ok(()) - } - - /// Initialize the CardCaps settings in the underlying CardClient - /// from the data in `ard`. - /// - /// This should be done at an early point, soon after opening the card. - fn init_caps( - card_client: &mut dyn CardClient, - ard: &ApplicationRelatedData, - ) -> Result<()> { - // Determine chaining/extended length support from card - // metadata and cache this information in CardApp (as a - // CardCaps) - - let mut ext_support = false; - let mut chaining_support = false; - - if let Ok(hist) = ard.historical_bytes() { - if let Some(cc) = hist.card_capabilities() { - chaining_support = cc.command_chaining(); - ext_support = cc.extended_lc_le(); - } - } - - let ext_cap = ard.extended_capabilities()?; - - // Get max command/response byte sizes from card - let (max_cmd_bytes, max_rsp_bytes) = - if let Ok(Some(eli)) = ard.extended_length_information() { - // In card 3.x, max lengths come from ExtendedLengthInfo - (eli.max_command_bytes(), eli.max_response_bytes()) - } else if let (Some(cmd), Some(rsp)) = - (ext_cap.max_cmd_len(), ext_cap.max_resp_len()) - { - // In card 2.x, max lengths come from ExtendedCapabilities - (cmd, rsp) - } else { - // Fallback: use 255 if we have no information from the card - (255, 255) - }; - - let pw_status = ard.pw_status_bytes()?; - let pw1_max = pw_status.pw1_max_len(); - let pw3_max = pw_status.pw3_max_len(); - - let caps = CardCaps { - ext_support, - chaining_support, - max_cmd_bytes, - max_rsp_bytes, - pw1_max_len: pw1_max, - pw3_max_len: pw3_max, - }; - - log::debug!("init_card_caps to: {:x?}", caps); - - card_client.init_card_caps(caps); - - Ok(()) - } - - // --- get data --- - - /// Get the "application related data" from the card. - /// - /// (This data should probably be cached in a higher layer. Some parts of - /// it are needed regularly, and it does not usually change during - /// normal use of a card.) - pub fn application_related_data( - card_client: &mut dyn CardClient, - ) -> Result { - let ad = commands::application_related_data(); - let resp = apdu::send_command(card_client, ad, true)?; - let value = Value::from(resp.data()?, true)?; - - log::debug!(" App data Value: {:x?}", value); - - Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value))) - } - - #[allow(dead_code)] - fn ca_fingerprints() { - unimplemented!() - } - - #[allow(dead_code)] - fn key_information() { - unimplemented!() - } - - #[allow(dead_code)] - fn uif_pso_cds() { - unimplemented!() - } - - #[allow(dead_code)] - fn uif_pso_dec() { - unimplemented!() - } - - #[allow(dead_code)] - fn uif_pso_aut() { - unimplemented!() - } - - #[allow(dead_code)] - fn uif_attestation() { - unimplemented!() - } - - // --- login data (5e) --- - - /// Get URL (5f50) - pub fn url(card_client: &mut dyn CardClient) -> Result { - let resp = apdu::send_command(card_client, commands::url(), true)?; - - Ok(String::from_utf8_lossy(resp.data()?).to_string()) - } - - /// Get cardholder related data (65) - pub fn cardholder_related_data( - card_client: &mut dyn CardClient, - ) -> Result { - let crd = commands::cardholder_related_data(); - let resp = apdu::send_command(card_client, crd, true)?; - resp.check_ok()?; - - CardholderRelatedData::try_from(resp.data()?) - } - - /// Get security support template (7a) - pub fn security_support_template( - card_client: &mut dyn CardClient, - ) -> Result { - let sst = commands::security_support_template(); - let resp = apdu::send_command(card_client, sst, true)?; - resp.check_ok()?; - - let tlv = Tlv::try_from(resp.data()?)?; - let res = tlv.find(&[0x93].into()).ok_or_else(|| { - anyhow!("Couldn't get SecuritySupportTemplate DO") - })?; - - if let Value::S(data) = res { - let mut data = data.to_vec(); - assert_eq!(data.len(), 3); - - data.insert(0, 0); // prepend a zero - let data: [u8; 4] = data.try_into().unwrap(); - - let dsc: u32 = u32::from_be_bytes(data); - Ok(SecuritySupportTemplate { dsc }) - } else { - Err(anyhow!("Failed to process SecuritySupportTemplate")) - } - } - - /// Get cardholder certificate (each for AUT, DEC and SIG). - /// - /// Call select_data() before calling this fn, to select a particular - /// certificate (if the card supports multiple certificates). - pub fn cardholder_certificate( - card_client: &mut dyn CardClient, - ) -> Result { - let cmd = commands::cardholder_certificate(); - apdu::send_command(card_client, cmd, true)?.try_into() - } - - /// Get "Algorithm Information" - pub fn algorithm_information( - card_client: &mut dyn CardClient, - ) -> Result> { - let resp = - apdu::send_command(card_client, commands::algo_info(), true)?; - resp.check_ok()?; - - let ai = AlgoInfo::try_from(resp.data()?)?; - Ok(Some(ai)) - } - - /// Firmware Version (YubiKey specific (?)) - pub fn firmware_version( - card_client: &mut dyn CardClient, - ) -> Result> { - let resp = apdu::send_command( - card_client, - commands::firmware_version(), - true, - )?; - - Ok(resp.data()?.into()) - } - - /// Set identity (Nitrokey Start specific (?)). - /// [see: - /// - /// ] - pub fn set_identity( - card_client: &mut dyn CardClient, - id: u8, - ) -> Result> { - let resp = - apdu::send_command(card_client, commands::set_identity(id), false); - - // Apparently it's normal to get "NotTransacted" from pcsclite when - // the identity switch was successful. - if let Err(Error::Smartcard(SmartcardError::NotTransacted)) = resp { - Ok(vec![]) - } else { - Ok(resp?.data()?.into()) - } - } - - /// SELECT DATA ("select a DO in the current template", - /// e.g. for cardholder certificate) - pub fn select_data( - card_client: &mut dyn CardClient, - num: u8, - tag: &[u8], - ) -> Result { - let tlv = Tlv::new( - [0x60], - Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]), - ); - - let data = tlv.serialize(); - - let cmd = commands::select_data(num, data); - apdu::send_command(card_client, cmd, true)?.try_into() - } - - // --- optional private DOs (0101 - 0104) --- - - /// Get data from "private use" DO. - /// - /// `num` must be between 1 and 4. - pub fn private_use_do( - card_client: &mut dyn CardClient, - num: u8, - ) -> Result> { - assert!((1..=4).contains(&num)); - - let cmd = commands::private_use_do(num); - let resp = apdu::send_command(card_client, cmd, true)?; - - Ok(resp.data()?.to_vec()) - } - - /// Set data of "private use" DO. - /// - /// `num` must be between 1 and 4. - /// - /// Access condition: - /// - 1/3 need PW1 (82) - /// - 2/4 need PW3 - pub fn set_private_use_do( - card_client: &mut dyn CardClient, - num: u8, - data: Vec, - ) -> Result> { - assert!((1..=4).contains(&num)); - - let cmd = commands::put_private_use_do(num, data); - let resp = apdu::send_command(card_client, cmd, true)?; - - Ok(resp.data()?.to_vec()) - } - - // ---------- - - /// Reset all state on this OpenPGP card. - /// - /// Note: the "factory reset" operation is not directly offered by the - /// card spec. It is implemented as a series of OpenPGP card commands: - /// - send 4 bad requests to verify pw1, - /// - send 4 bad requests to verify pw3, - /// - terminate_df, - /// - activate_file. - /// - /// With most cards, this sequence of operations causes the card - /// to revert to a "blank" state. - /// - /// (However, e.g. vanilla Gnuk doesn't support this functionality. - /// Gnuk needs to be built with the `--enable-factory-reset` - /// option to the `configure` script to enable this functionality). - pub fn factory_reset(card_client: &mut dyn CardClient) -> Result<()> { - // send 4 bad requests to verify pw1 - // [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40] - for _ in 0..4 { - let verify = commands::verify_pw1_81([0x40; 8].to_vec()); - let resp = apdu::send_command(card_client, verify, false)?; - if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied - || resp.status() == StatusBytes::AuthenticationMethodBlocked - || matches!(resp.status(), StatusBytes::PasswordNotChecked(_))) - { - return Err(anyhow!("Unexpected status for reset, at pw1.")); - } - } - - // send 4 bad requests to verify pw3 - // [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40] - for _ in 0..4 { - let verify = commands::verify_pw3([0x40; 8].to_vec()); - let resp = apdu::send_command(card_client, verify, false)?; - - if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied - || resp.status() == StatusBytes::AuthenticationMethodBlocked - || matches!(resp.status(), StatusBytes::PasswordNotChecked(_))) - { - return Err(anyhow!("Unexpected status for reset, at pw3.")); - } - } - - // terminate_df [apdu 00 e6 00 00] - let term = commands::terminate_df(); - let resp = apdu::send_command(card_client, term, false)?; - resp.check_ok()?; - - // activate_file [apdu 00 44 00 00] - let act = commands::activate_file(); - let resp = apdu::send_command(card_client, act, false)?; - resp.check_ok()?; - - Ok(()) - } - - // --- verify/modify --- - - /// Does the cardreader support direct pinpad verify? - pub fn feature_pinpad_verify(card_client: &mut dyn CardClient) -> bool { - card_client.feature_pinpad_verify() - } - - /// Does the cardreader support direct pinpad modify? - pub fn feature_pinpad_modify(card_client: &mut dyn CardClient) -> bool { - card_client.feature_pinpad_modify() - } - - /// Verify pw1 (user) for signing operation (mode 81). - /// - /// Depending on the PW1 status byte (see Extended Capabilities) this - /// access condition is only valid for one PSO:CDS command or remains - /// valid for several attempts. - pub fn verify_pw1_for_signing( - card_client: &mut dyn CardClient, - pin: &str, - ) -> Result { - let verify = commands::verify_pw1_81(pin.as_bytes().to_vec()); - apdu::send_command(card_client, verify, false)?.try_into() - } - - /// Verify pw1 (user) for signing operation (mode 81) using a - /// pinpad on the card reader. If no usable pinpad is found, an error - /// is returned. - /// - /// Depending on the PW1 status byte (see Extended Capabilities) this - /// access condition is only valid for one PSO:CDS command or remains - /// valid for several attempts. - pub fn verify_pw1_for_signing_pinpad( - card_client: &mut dyn CardClient, - ) -> Result { - let res = card_client.pinpad_verify(0x81)?; - RawResponse::try_from(res)?.try_into() - } - - /// Check the current access of PW1 for signing (mode 81). - /// - /// If verification is not required, an empty Ok Response is returned. - /// - /// (Note: some cards don't correctly implement this feature, - /// e.g. YubiKey 5) - pub fn check_pw1_for_signing( - card_client: &mut dyn CardClient, - ) -> Result { - let verify = commands::verify_pw1_81(vec![]); - apdu::send_command(card_client, verify, false)?.try_into() - } - - /// Verify PW1 (user). - /// (For operations except signing, mode 82). - pub fn verify_pw1( - card_client: &mut dyn CardClient, - pin: &str, - ) -> Result { - let verify = commands::verify_pw1_82(pin.as_bytes().to_vec()); - apdu::send_command(card_client, verify, false)?.try_into() - } - - /// Verify PW1 (user) for operations except signing (mode 82), - /// using a pinpad on the card reader. If no usable pinpad is found, - /// an error is returned. - - pub fn verify_pw1_pinpad( - card_client: &mut dyn CardClient, - ) -> Result { - let res = card_client.pinpad_verify(0x82)?; - RawResponse::try_from(res)?.try_into() - } - - /// Check the current access of PW1. - /// (For operations except signing, mode 82). - /// - /// If verification is not required, an empty Ok Response is returned. - /// - /// (Note: some cards don't correctly implement this feature, - /// e.g. YubiKey 5) - pub fn check_pw1( - card_client: &mut dyn CardClient, - ) -> Result { - let verify = commands::verify_pw1_82(vec![]); - apdu::send_command(card_client, verify, false)?.try_into() - } - - /// Verify PW3 (admin). - pub fn verify_pw3( - card_client: &mut dyn CardClient, - pin: &str, - ) -> Result { - let verify = commands::verify_pw3(pin.as_bytes().to_vec()); - apdu::send_command(card_client, verify, false)?.try_into() - } - - /// Verify PW3 (admin) using a pinpad on the card reader. If no usable - /// pinpad is found, an error is returned. - pub fn verify_pw3_pinpad( - card_client: &mut dyn CardClient, - ) -> Result { - let res = card_client.pinpad_verify(0x83)?; - RawResponse::try_from(res)?.try_into() - } - - /// Check the current access of PW3 (admin). - /// - /// If verification is not required, an empty Ok Response is returned. - /// - /// (Note: some cards don't correctly implement this feature, - /// e.g. YubiKey 5) - pub fn check_pw3( - card_client: &mut dyn CardClient, - ) -> Result { - let verify = commands::verify_pw3(vec![]); - apdu::send_command(card_client, verify, false)?.try_into() - } - - /// Change the value of PW1 (user password). - /// - /// The current value of PW1 must be presented in `old` for authorization. - pub fn change_pw1( - card_client: &mut dyn CardClient, - old: &str, - new: &str, - ) -> Result { - let mut data = vec![]; - data.extend(old.as_bytes()); - data.extend(new.as_bytes()); - - let change = commands::change_pw1(data); - apdu::send_command(card_client, change, false)?.try_into() - } - - /// Change the value of PW1 (user password) using a pinpad on the - /// card reader. If no usable pinpad is found, an error is returned. - pub fn change_pw1_pinpad( - card_client: &mut dyn CardClient, - ) -> Result { - let res = card_client.pinpad_modify(0x81)?; - RawResponse::try_from(res)?.try_into() - } - - /// Change the value of PW3 (admin password). - /// - /// The current value of PW3 must be presented in `old` for authorization. - pub fn change_pw3( - card_client: &mut dyn CardClient, - old: &str, - new: &str, - ) -> Result { - let mut data = vec![]; - data.extend(old.as_bytes()); - data.extend(new.as_bytes()); - - let change = commands::change_pw3(data); - apdu::send_command(card_client, change, false)?.try_into() - } - - /// Change the value of PW3 (admin password) using a pinpad on the - /// card reader. If no usable pinpad is found, an error is returned. - pub fn change_pw3_pinpad( - card_client: &mut dyn CardClient, - ) -> Result { - let res = card_client.pinpad_modify(0x83)?; - RawResponse::try_from(res)?.try_into() - } - - /// Reset the error counter for PW1 (user password) and set a new value - /// for PW1. - /// - /// For authorization, either: - /// - PW3 must have been verified previously, - /// - secure messaging must be currently used, - /// - the resetting_code must be presented. - pub fn reset_retry_counter_pw1( - card_client: &mut dyn CardClient, - new_pw1: Vec, - resetting_code: Option>, - ) -> Result { - let reset = commands::reset_retry_counter_pw1(resetting_code, new_pw1); - apdu::send_command(card_client, reset, false)?.try_into() - } - - // --- decrypt --- - - /// Decrypt the ciphertext in `dm`, on the card. - /// - /// (This is a wrapper around the low-level pso_decipher - /// operation, it builds the required `data` field from `dm`) - pub fn decipher( - card_client: &mut dyn CardClient, - dm: Cryptogram, - ) -> Result, Error> { - match dm { - Cryptogram::RSA(message) => { - // "Padding indicator byte (00) for RSA" (pg. 69) - let mut data = vec![0x0]; - data.extend_from_slice(message); - - // Call the card to decrypt `data` - Self::pso_decipher(card_client, data) - } - Cryptogram::ECDH(eph) => { - // "In case of ECDH the card supports a partial decrypt - // only. The input is a cipher DO with the following data:" - // A6 xx Cipher DO - // -> 7F49 xx Public Key DO - // -> 86 xx External Public Key - - // External Public Key - let epk = Tlv::new([0x86], Value::S(eph.to_vec())); - - // Public Key DO - let pkdo = Tlv::new([0x7f, 0x49], Value::C(vec![epk])); - - // Cipher DO - let cdo = Tlv::new([0xa6], Value::C(vec![pkdo])); - - Self::pso_decipher(card_client, cdo.serialize()) - } - } - } - - /// Run decryption operation on the smartcard (low level operation) - /// (7.2.11 PSO: DECIPHER) - fn pso_decipher( - card_client: &mut dyn CardClient, - data: Vec, - ) -> Result, Error> { - // The OpenPGP card is already connected and PW1 82 has been verified - let dec_cmd = commands::decryption(data); - let resp = apdu::send_command(card_client, dec_cmd, true)?; - resp.check_ok()?; - - Ok(resp.data().map(|d| d.to_vec())?) - } - - // --- sign --- - - fn digestinfo(hash: Hash) -> Vec { - match hash { - Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => { - let tlv = Tlv::new( - [0x30], - Value::C(vec![ - Tlv::new( - [0x30], - Value::C(vec![ - Tlv::new( - [0x06], - // unwrapping is ok, for SHA* - Value::S(hash.oid().unwrap().to_vec()), - ), - Tlv::new([0x05], Value::S(vec![])), - ]), - ), - Tlv::new([0x04], Value::S(hash.digest().to_vec())), - ]), - ); - - tlv.serialize() - } - Hash::EdDSA(d) => d.to_vec(), - Hash::ECDSA(d) => d.to_vec(), - } - } - - /// Sign `hash`, on the card. - /// - /// This is a wrapper around the low-level - /// pso_compute_digital_signature operation. - /// It builds the required `data` field from `hash`. - /// - /// For RSA, this means a "DigestInfo" data structure is generated. - /// (see 7.2.10.2 DigestInfo for RSA). - /// - /// With ECC the hash data is processed as is, using - /// pso_compute_digital_signature. - pub fn signature_for_hash( - card_client: &mut dyn CardClient, - hash: Hash, - ) -> Result, Error> { - Self::pso_compute_digital_signature( - card_client, - Self::digestinfo(hash), - ) - } - - /// Run signing operation on the smartcard (low level operation) - /// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE) - pub fn pso_compute_digital_signature( - card_client: &mut dyn CardClient, - data: Vec, - ) -> Result, Error> { - let cds_cmd = commands::signature(data); - - let resp = apdu::send_command(card_client, cds_cmd, true)?; - - Ok(resp.data().map(|d| d.to_vec())?) - } - - // --- internal authenticate --- - - /// Auth-sign `hash`, on the card. - /// - /// This is a wrapper around the low-level - /// internal_authenticate operation. - /// It builds the required `data` field from `hash`. - /// - /// For RSA, this means a "DigestInfo" data structure is generated. - /// (see 7.2.10.2 DigestInfo for RSA). - /// - /// With ECC the hash data is processed as is. - pub fn authenticate_for_hash( - card_client: &mut dyn CardClient, - hash: Hash, - ) -> Result, Error> { - Self::internal_authenticate(card_client, Self::digestinfo(hash)) - } - - /// Run signing operation on the smartcard (low level operation) - /// (7.2.13 INTERNAL AUTHENTICATE) - pub fn internal_authenticate( - card_client: &mut dyn CardClient, - data: Vec, - ) -> Result, Error> { - let ia_cmd = commands::internal_authenticate(data); - let resp = apdu::send_command(card_client, ia_cmd, true)?; - - Ok(resp.data().map(|d| d.to_vec())?) - } - - // --- admin --- - - pub fn set_name( - card_client: &mut dyn CardClient, - name: &str, - ) -> Result { - let put_name = commands::put_name(name.as_bytes().to_vec()); - apdu::send_command(card_client, put_name, false)?.try_into() - } - - pub fn set_lang( - card_client: &mut dyn CardClient, - lang: &str, - ) -> Result { - let put_lang = commands::put_lang(lang.as_bytes().to_vec()); - apdu::send_command(card_client, put_lang, false)?.try_into() - } - - pub fn set_sex( - card_client: &mut dyn CardClient, - sex: Sex, - ) -> Result { - let put_sex = commands::put_sex((&sex).into()); - apdu::send_command(card_client, put_sex, false)?.try_into() - } - - pub fn set_url( - card_client: &mut dyn CardClient, - url: &str, - ) -> Result { - let put_url = commands::put_url(url.as_bytes().to_vec()); - apdu::send_command(card_client, put_url, false)?.try_into() - } - - pub fn set_creation_time( - card_client: &mut dyn CardClient, - time: KeyGenerationTime, - key_type: KeyType, - ) -> Result { - // Timestamp update - let time_value: Vec = time - .get() - .to_be_bytes() - .iter() - .skip_while(|&&e| e == 0) - .copied() - .collect(); - - let time_cmd = - commands::put_data(&[key_type.timestamp_put_tag()], time_value); - - apdu::send_command(card_client, time_cmd, false)?.try_into() - } - - pub fn set_fingerprint( - card_client: &mut dyn CardClient, - fp: Fingerprint, - key_type: KeyType, - ) -> Result { - let fp_cmd = commands::put_data( - &[key_type.fingerprint_put_tag()], - fp.as_bytes().to_vec(), - ); - - apdu::send_command(card_client, fp_cmd, false)?.try_into() - } - - /// Set PW Status Bytes. - /// - /// If `long` is false, send 1 byte to the card, otherwise 4. - /// According to the spec, length information should not be changed. - /// - /// So, effectively, with 'long == false' the setting `pw1_cds_multi` - /// can be changed. - /// With 'long == true', the settings `pw1_pin_block` and `pw3_pin_block` - /// can also be changed. - /// - /// (See OpenPGP card spec, pg. 28) - pub fn set_pw_status_bytes( - card_client: &mut dyn CardClient, - pw_status: &PWStatusBytes, - long: bool, - ) -> Result { - let data = pw_status.serialize_for_put(long); - - let cmd = commands::put_pw_status(data); - apdu::send_command(card_client, cmd, false)?.try_into() - } - - /// Set cardholder certificate (for AUT, DEC or SIG). - /// - /// Call select_data() before calling this fn, to select a particular - /// certificate (if the card supports multiple certificates). - pub fn set_cardholder_certificate( - card_client: &mut dyn CardClient, - data: Vec, - ) -> Result { - let cmd = commands::put_cardholder_certificate(data); - apdu::send_command(card_client, cmd, false)?.try_into() - } - - /// Set algorithm attributes - /// (4.4.3.9 Algorithm Attributes) - pub fn set_algorithm_attributes( - card_client: &mut dyn CardClient, - key_type: KeyType, - algo: &Algo, - ) -> Result { - // Command to PUT the algorithm attributes - let cmd = commands::put_data( - &[key_type.algorithm_tag()], - algo.to_data_object()?, - ); - - apdu::send_command(card_client, cmd, false)?.try_into() - } - - /// Set resetting code - /// (4.3.4 Resetting Code) - pub fn set_resetting_code( - card_client: &mut dyn CardClient, - resetting_code: Vec, - ) -> Result { - let cmd = commands::put_data(&[0xd3], resetting_code); - apdu::send_command(card_client, cmd, false)?.try_into() - } - - /// Import an existing private key to the card. - /// (This implicitly sets the algorithm info, fingerprint and timestamp) - pub fn key_import( - card_client: &mut dyn CardClient, - key: Box, - key_type: KeyType, - ) -> Result<(), Error> { - let algo_info = Self::algorithm_information(card_client); - - // An error is ok - it's fine if a card doesn't offer a list of - // supported algorithms - let algo_info = algo_info.unwrap_or(None); - - keys::key_import(card_client, key, key_type, algo_info) - } - - /// Generate a key on the card. - /// (7.2.14 GENERATE ASYMMETRIC KEY PAIR) - /// - /// If the `algo` parameter is Some, then this algorithm will be set on - /// the card for "key_type". - /// - /// Note: `algo` needs to precisely specify the RSA bitsize of e (if - /// applicable), and import format, with values that the current card - /// supports. - pub fn generate_key( - card_client: &mut dyn CardClient, - fp_from_pub: fn( - &PublicKeyMaterial, - KeyGenerationTime, - KeyType, - ) -> Result, - key_type: KeyType, - algo: Option<&Algo>, - ) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> { - keys::gen_key_with_metadata(card_client, fp_from_pub, key_type, algo) - } - - /// Generate a key on the card. - /// (7.2.14 GENERATE ASYMMETRIC KEY PAIR) - /// - /// This is a wrapper around generate_key() which allows - /// using the simplified `AlgoSimple` algorithm selector enum. - /// - /// Note: AlgoSimple doesn't specify card specific details (such as - /// bitsize of e for RSA, and import format). This function determines - /// these values based on information from the card. - pub fn generate_key_simple( - card_client: &mut dyn CardClient, - fp_from_pub: fn( - &PublicKeyMaterial, - KeyGenerationTime, - KeyType, - ) -> Result, - key_type: KeyType, - simple: AlgoSimple, - ) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> { - let ard = Self::application_related_data(card_client)?; - let algo_info = - if let Ok(ai) = Self::algorithm_information(card_client) { - ai - } else { - None - }; - - let algo = simple.determine_algo(key_type, &ard, algo_info)?; - - Self::generate_key(card_client, fp_from_pub, key_type, Some(&algo)) - } - - /// Get public key material from the card. - /// - /// Note: this fn returns a set of raw public key data (not an - /// OpenPGP data structure). - /// - /// Note also that the information from the card is insufficient to - /// reconstruct a pre-existing OpenPGP public key that corresponds to - /// the private key on the card. - pub fn public_key( - card_client: &mut dyn CardClient, - key_type: KeyType, - ) -> Result { - keys::public_key(card_client, key_type) - } -} +impl CardApp {} diff --git a/openpgp-card/src/keys.rs b/openpgp-card/src/keys.rs index 01a7b7d..3f75f07 100644 --- a/openpgp-card/src/keys.rs +++ b/openpgp-card/src/keys.rs @@ -10,7 +10,6 @@ use std::time::{SystemTime, UNIX_EPOCH}; use crate::algorithm::{Algo, AlgoInfo, Curve, EccAttrs, RsaAttrs}; use crate::apdu::command::Command; use crate::apdu::commands; -use crate::card_app::CardApp; use crate::card_do::{ApplicationRelatedData, Fingerprint, KeyGenerationTime}; use crate::crypto_data::{ CardUploadableKey, EccKey, EccPub, EccType, PrivateKeyMaterial, @@ -43,16 +42,12 @@ pub(crate) fn gen_key_with_metadata( // Set algo on card if it's Some if let Some(target_algo) = algo { // FIXME: caching - let ard = CardApp::application_related_data(card_client)?; // no caching, here! + let ard = card_client.application_related_data()?; // no caching, here! let ecap = ard.extended_capabilities()?; // Only set algo if card supports setting of algo attr if ecap.algo_attrs_changeable() { - CardApp::set_algorithm_attributes( - card_client, - key_type, - target_algo, - )?; + card_client.set_algorithm_attributes(key_type, target_algo)?; } else { // Check if the current algo on the card is the one we want, if // not we return an error. @@ -69,7 +64,7 @@ pub(crate) fn gen_key_with_metadata( } // get current (possibly updated) state of algo - let ard = CardApp::application_related_data(card_client)?; // no caching, here! + let ard = card_client.application_related_data()?; // no caching, here! let cur_algo = ard.algorithm_attributes(key_type)?; // generate key @@ -91,11 +86,11 @@ pub(crate) fn gen_key_with_metadata( let ts = ts.into(); - CardApp::set_creation_time(card_client, ts, key_type)?; + card_client.set_creation_time(ts, key_type)?; // calculate/store fingerprint let fp = fp_from_pub(&pubkey, ts, key_type)?; - CardApp::set_fingerprint(card_client, fp, key_type)?; + card_client.set_fingerprint(fp, key_type)?; Ok((pubkey, ts)) } @@ -158,7 +153,7 @@ pub(crate) fn public_key( key_type: KeyType, ) -> Result { // get current algo - let ard = CardApp::application_related_data(card_client)?; // FIXME: caching + let ard = card_client.application_related_data()?; // FIXME: caching let algo = ard.algorithm_attributes(key_type)?; // get public key @@ -186,7 +181,7 @@ pub(crate) fn key_import( algo_info: Option, ) -> Result<(), Error> { // FIXME: caching? - let ard = CardApp::application_related_data(card_client)?; + let ard = card_client.application_related_data()?; let (algo, key_cmd) = match key.private_key()? { PrivateKeyMaterial::R(rsa_key) => { @@ -222,12 +217,12 @@ pub(crate) fn key_import( // Only set algo attrs if "Extended Capabilities" lists the feature if ard.extended_capabilities()?.algo_attrs_changeable() { - CardApp::set_algorithm_attributes(card_client, key_type, &algo)?; + card_client.set_algorithm_attributes(key_type, &algo)?; } apdu::send_command(card_client, key_cmd, false)?.check_ok()?; - CardApp::set_fingerprint(card_client, fp, key_type)?; - CardApp::set_creation_time(card_client, key.timestamp(), key_type)?; + card_client.set_fingerprint(fp, key_type)?; + card_client.set_creation_time(key.timestamp(), key_type)?; Ok(()) } diff --git a/openpgp-card/src/lib.rs b/openpgp-card/src/lib.rs index 87fdab6..7404244 100644 --- a/openpgp-card/src/lib.rs +++ b/openpgp-card/src/lib.rs @@ -38,11 +38,21 @@ pub use crate::apdu::response::Response; pub use crate::card_app::CardApp; pub use crate::errors::{Error, SmartcardError, StatusBytes}; -use anyhow::Result; +use anyhow::{anyhow, Result}; +use std::convert::TryFrom; use std::convert::TryInto; +use std::ops::{Deref, DerefMut}; +use crate::algorithm::{Algo, AlgoInfo, AlgoSimple}; use crate::apdu::commands; -use crate::card_do::ApplicationRelatedData; +use crate::apdu::response::RawResponse; +use crate::card_do::{ + ApplicationRelatedData, CardholderRelatedData, Fingerprint, + KeyGenerationTime, PWStatusBytes, SecuritySupportTemplate, Sex, +}; +use crate::crypto_data::{ + CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial, +}; use crate::tlv::tag::Tag; use crate::tlv::value::Value; use crate::tlv::Tlv; @@ -95,8 +105,18 @@ pub trait CardClient { fn pinpad_modify(&mut self, id: u8) -> Result>; } -/// A boxed CardClient (which is Send+Sync). -pub type CardClientBox = Box; +impl<'a> Deref for dyn CardClient + Send + Sync + 'a { + type Target = dyn CardClient + 'a; + + fn deref(&self) -> &Self::Target { + self + } +} +impl<'a> DerefMut for dyn CardClient + Send + Sync + 'a { + fn deref_mut(&mut self) -> &mut Self::Target { + self + } +} impl<'a> dyn CardClient + 'a { /// Select the OpenPGP card application @@ -105,7 +125,83 @@ impl<'a> dyn CardClient + 'a { apdu::send_command(self, select_openpgp, false)?.try_into() } - // FIXME: this is a duplicate from card_app + /// Get a CardApp based on a CardClient. + /// + /// It is expected that SELECT has already been performed on the card + /// beforehand. + /// + /// This fn initializes the CardCaps by requesting + /// application_related_data from the card, and setting the + /// capabilities accordingly. + pub fn initialize(&mut self) -> Result<()> { + let ard = self.application_related_data()?; + self.init_caps(&ard)?; + + Ok(()) + } + + /// Initialize the CardCaps settings from the data in `ard`. + /// + /// This should be done at an early point, soon after opening the card. + fn init_caps(&mut self, ard: &ApplicationRelatedData) -> Result<()> { + // Determine chaining/extended length support from card + // metadata and cache this information in CardApp (as a + // CardCaps) + + let mut ext_support = false; + let mut chaining_support = false; + + if let Ok(hist) = ard.historical_bytes() { + if let Some(cc) = hist.card_capabilities() { + chaining_support = cc.command_chaining(); + ext_support = cc.extended_lc_le(); + } + } + + let ext_cap = ard.extended_capabilities()?; + + // Get max command/response byte sizes from card + let (max_cmd_bytes, max_rsp_bytes) = + if let Ok(Some(eli)) = ard.extended_length_information() { + // In card 3.x, max lengths come from ExtendedLengthInfo + (eli.max_command_bytes(), eli.max_response_bytes()) + } else if let (Some(cmd), Some(rsp)) = + (ext_cap.max_cmd_len(), ext_cap.max_resp_len()) + { + // In card 2.x, max lengths come from ExtendedCapabilities + (cmd, rsp) + } else { + // Fallback: use 255 if we have no information from the card + (255, 255) + }; + + let pw_status = ard.pw_status_bytes()?; + let pw1_max = pw_status.pw1_max_len(); + let pw3_max = pw_status.pw3_max_len(); + + let caps = CardCaps { + ext_support, + chaining_support, + max_cmd_bytes, + max_rsp_bytes, + pw1_max_len: pw1_max, + pw3_max_len: pw3_max, + }; + + log::debug!("init_card_caps to: {:x?}", caps); + + self.init_card_caps(caps); + + Ok(()) + } + + // --- get data --- + + /// Get the "application related data" from the card. + /// + /// (This data should probably be cached in a higher layer. Some parts of + /// it are needed regularly, and it does not usually change during + /// normal use of a card.) pub fn application_related_data( &mut self, ) -> Result { @@ -113,10 +209,742 @@ impl<'a> dyn CardClient + 'a { let resp = apdu::send_command(self, ad, true)?; let value = Value::from(resp.data()?, true)?; - log::debug!(" App data Value: {:x?}", value); + log::debug!(" ARD value: {:x?}", value); Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value))) } + + #[allow(dead_code)] + fn ca_fingerprints() { + unimplemented!() + } + + #[allow(dead_code)] + fn key_information() { + unimplemented!() + } + + #[allow(dead_code)] + fn uif_pso_cds() { + unimplemented!() + } + + #[allow(dead_code)] + fn uif_pso_dec() { + unimplemented!() + } + + #[allow(dead_code)] + fn uif_pso_aut() { + unimplemented!() + } + + #[allow(dead_code)] + fn uif_attestation() { + unimplemented!() + } + + // --- login data (5e) --- + + /// Get URL (5f50) + pub fn url(&mut self) -> Result { + let resp = apdu::send_command(self, commands::url(), true)?; + + Ok(String::from_utf8_lossy(resp.data()?).to_string()) + } + + /// Get cardholder related data (65) + pub fn cardholder_related_data( + &mut self, + ) -> Result { + let crd = commands::cardholder_related_data(); + let resp = apdu::send_command(self, crd, true)?; + resp.check_ok()?; + + CardholderRelatedData::try_from(resp.data()?) + } + + /// Get security support template (7a) + pub fn security_support_template( + &mut self, + ) -> Result { + let sst = commands::security_support_template(); + let resp = apdu::send_command(self, sst, true)?; + resp.check_ok()?; + + let tlv = Tlv::try_from(resp.data()?)?; + let res = tlv.find(&[0x93].into()).ok_or_else(|| { + anyhow!("Couldn't get SecuritySupportTemplate DO") + })?; + + if let Value::S(data) = res { + let mut data = data.to_vec(); + assert_eq!(data.len(), 3); + + data.insert(0, 0); // prepend a zero + let data: [u8; 4] = data.try_into().unwrap(); + + let dsc: u32 = u32::from_be_bytes(data); + Ok(SecuritySupportTemplate { dsc }) + } else { + Err(anyhow!("Failed to process SecuritySupportTemplate")) + } + } + + /// Get cardholder certificate (each for AUT, DEC and SIG). + /// + /// Call select_data() before calling this fn, to select a particular + /// certificate (if the card supports multiple certificates). + pub fn cardholder_certificate(&mut self) -> Result { + let cmd = commands::cardholder_certificate(); + apdu::send_command(self, cmd, true)?.try_into() + } + + /// Get "Algorithm Information" + pub fn algorithm_information(&mut self) -> Result> { + let resp = apdu::send_command(self, commands::algo_info(), true)?; + resp.check_ok()?; + + let ai = AlgoInfo::try_from(resp.data()?)?; + Ok(Some(ai)) + } + + /// Firmware Version (YubiKey specific (?)) + pub fn firmware_version(&mut self) -> Result> { + let resp = + apdu::send_command(self, commands::firmware_version(), true)?; + + Ok(resp.data()?.into()) + } + + /// Set identity (Nitrokey Start specific (?)). + /// [see: + /// + /// ] + pub fn set_identity(&mut self, id: u8) -> Result> { + let resp = apdu::send_command(self, commands::set_identity(id), false); + + // Apparently it's normal to get "NotTransacted" from pcsclite when + // the identity switch was successful. + if let Err(Error::Smartcard(SmartcardError::NotTransacted)) = resp { + Ok(vec![]) + } else { + Ok(resp?.data()?.into()) + } + } + + /// SELECT DATA ("select a DO in the current template", + /// e.g. for cardholder certificate) + pub fn select_data( + &mut self, + num: u8, + tag: &[u8], + ) -> Result { + let tlv = Tlv::new( + [0x60], + Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]), + ); + + let data = tlv.serialize(); + + let cmd = commands::select_data(num, data); + apdu::send_command(self, cmd, true)?.try_into() + } + + // --- optional private DOs (0101 - 0104) --- + + /// Get data from "private use" DO. + /// + /// `num` must be between 1 and 4. + pub fn private_use_do(&mut self, num: u8) -> Result> { + assert!((1..=4).contains(&num)); + + let cmd = commands::private_use_do(num); + let resp = apdu::send_command(self, cmd, true)?; + + Ok(resp.data()?.to_vec()) + } + + /// Set data of "private use" DO. + /// + /// `num` must be between 1 and 4. + /// + /// Access condition: + /// - 1/3 need PW1 (82) + /// - 2/4 need PW3 + pub fn set_private_use_do( + &mut self, + num: u8, + data: Vec, + ) -> Result> { + assert!((1..=4).contains(&num)); + + let cmd = commands::put_private_use_do(num, data); + let resp = apdu::send_command(self, cmd, true)?; + + Ok(resp.data()?.to_vec()) + } + + // ---------- + + /// Reset all state on this OpenPGP card. + /// + /// Note: the "factory reset" operation is not directly offered by the + /// card spec. It is implemented as a series of OpenPGP card commands: + /// - send 4 bad requests to verify pw1, + /// - send 4 bad requests to verify pw3, + /// - terminate_df, + /// - activate_file. + /// + /// With most cards, this sequence of operations causes the card + /// to revert to a "blank" state. + /// + /// (However, e.g. vanilla Gnuk doesn't support this functionality. + /// Gnuk needs to be built with the `--enable-factory-reset` + /// option to the `configure` script to enable this functionality). + pub fn factory_reset(&mut self) -> Result<()> { + // send 4 bad requests to verify pw1 + // [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40] + for _ in 0..4 { + let verify = commands::verify_pw1_81([0x40; 8].to_vec()); + let resp = apdu::send_command(self, verify, false)?; + if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied + || resp.status() == StatusBytes::AuthenticationMethodBlocked + || matches!(resp.status(), StatusBytes::PasswordNotChecked(_))) + { + return Err(anyhow!("Unexpected status for reset, at pw1.")); + } + } + + // send 4 bad requests to verify pw3 + // [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40] + for _ in 0..4 { + let verify = commands::verify_pw3([0x40; 8].to_vec()); + let resp = apdu::send_command(self, verify, false)?; + + if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied + || resp.status() == StatusBytes::AuthenticationMethodBlocked + || matches!(resp.status(), StatusBytes::PasswordNotChecked(_))) + { + return Err(anyhow!("Unexpected status for reset, at pw3.")); + } + } + + // terminate_df [apdu 00 e6 00 00] + let term = commands::terminate_df(); + let resp = apdu::send_command(self, term, false)?; + resp.check_ok()?; + + // activate_file [apdu 00 44 00 00] + let act = commands::activate_file(); + let resp = apdu::send_command(self, act, false)?; + resp.check_ok()?; + + Ok(()) + } + + // --- verify/modify --- + + /// Does the cardreader support direct pinpad verify? + pub fn feature_pinpad_verify(card_client: &mut dyn CardClient) -> bool { + card_client.feature_pinpad_verify() + } + + /// Does the cardreader support direct pinpad modify? + pub fn feature_pinpad_modify(card_client: &mut dyn CardClient) -> bool { + card_client.feature_pinpad_modify() + } + + /// Verify pw1 (user) for signing operation (mode 81). + /// + /// Depending on the PW1 status byte (see Extended Capabilities) this + /// access condition is only valid for one PSO:CDS command or remains + /// valid for several attempts. + pub fn verify_pw1_for_signing( + &mut self, + pin: &str, + ) -> Result { + let verify = commands::verify_pw1_81(pin.as_bytes().to_vec()); + apdu::send_command(self, verify, false)?.try_into() + } + + /// Verify pw1 (user) for signing operation (mode 81) using a + /// pinpad on the card reader. If no usable pinpad is found, an error + /// is returned. + /// + /// Depending on the PW1 status byte (see Extended Capabilities) this + /// access condition is only valid for one PSO:CDS command or remains + /// valid for several attempts. + pub fn verify_pw1_for_signing_pinpad( + &mut self, + ) -> Result { + let res = self.pinpad_verify(0x81)?; + RawResponse::try_from(res)?.try_into() + } + + /// Check the current access of PW1 for signing (mode 81). + /// + /// If verification is not required, an empty Ok Response is returned. + /// + /// (Note: some cards don't correctly implement this feature, + /// e.g. YubiKey 5) + pub fn check_pw1_for_signing(&mut self) -> Result { + let verify = commands::verify_pw1_81(vec![]); + apdu::send_command(self, verify, false)?.try_into() + } + + /// Verify PW1 (user). + /// (For operations except signing, mode 82). + pub fn verify_pw1(&mut self, pin: &str) -> Result { + let verify = commands::verify_pw1_82(pin.as_bytes().to_vec()); + apdu::send_command(self, verify, false)?.try_into() + } + + /// Verify PW1 (user) for operations except signing (mode 82), + /// using a pinpad on the card reader. If no usable pinpad is found, + /// an error is returned. + + pub fn verify_pw1_pinpad(&mut self) -> Result { + let res = self.pinpad_verify(0x82)?; + RawResponse::try_from(res)?.try_into() + } + + /// Check the current access of PW1. + /// (For operations except signing, mode 82). + /// + /// If verification is not required, an empty Ok Response is returned. + /// + /// (Note: some cards don't correctly implement this feature, + /// e.g. YubiKey 5) + pub fn check_pw1(&mut self) -> Result { + let verify = commands::verify_pw1_82(vec![]); + apdu::send_command(self, verify, false)?.try_into() + } + + /// Verify PW3 (admin). + pub fn verify_pw3(&mut self, pin: &str) -> Result { + let verify = commands::verify_pw3(pin.as_bytes().to_vec()); + apdu::send_command(self, verify, false)?.try_into() + } + + /// Verify PW3 (admin) using a pinpad on the card reader. If no usable + /// pinpad is found, an error is returned. + pub fn verify_pw3_pinpad(&mut self) -> Result { + let res = self.pinpad_verify(0x83)?; + RawResponse::try_from(res)?.try_into() + } + + /// Check the current access of PW3 (admin). + /// + /// If verification is not required, an empty Ok Response is returned. + /// + /// (Note: some cards don't correctly implement this feature, + /// e.g. YubiKey 5) + pub fn check_pw3(&mut self) -> Result { + let verify = commands::verify_pw3(vec![]); + apdu::send_command(self, verify, false)?.try_into() + } + + /// Change the value of PW1 (user password). + /// + /// The current value of PW1 must be presented in `old` for authorization. + pub fn change_pw1( + &mut self, + old: &str, + new: &str, + ) -> Result { + let mut data = vec![]; + data.extend(old.as_bytes()); + data.extend(new.as_bytes()); + + let change = commands::change_pw1(data); + apdu::send_command(self, change, false)?.try_into() + } + + /// Change the value of PW1 (user password) using a pinpad on the + /// card reader. If no usable pinpad is found, an error is returned. + pub fn change_pw1_pinpad(&mut self) -> Result { + let res = self.pinpad_modify(0x81)?; + RawResponse::try_from(res)?.try_into() + } + + /// Change the value of PW3 (admin password). + /// + /// The current value of PW3 must be presented in `old` for authorization. + pub fn change_pw3( + &mut self, + old: &str, + new: &str, + ) -> Result { + let mut data = vec![]; + data.extend(old.as_bytes()); + data.extend(new.as_bytes()); + + let change = commands::change_pw3(data); + apdu::send_command(self, change, false)?.try_into() + } + + /// Change the value of PW3 (admin password) using a pinpad on the + /// card reader. If no usable pinpad is found, an error is returned. + pub fn change_pw3_pinpad(&mut self) -> Result { + let res = self.pinpad_modify(0x83)?; + RawResponse::try_from(res)?.try_into() + } + + /// Reset the error counter for PW1 (user password) and set a new value + /// for PW1. + /// + /// For authorization, either: + /// - PW3 must have been verified previously, + /// - secure messaging must be currently used, + /// - the resetting_code must be presented. + pub fn reset_retry_counter_pw1( + &mut self, + new_pw1: Vec, + resetting_code: Option>, + ) -> Result { + let reset = commands::reset_retry_counter_pw1(resetting_code, new_pw1); + apdu::send_command(self, reset, false)?.try_into() + } + + // --- decrypt --- + + /// Decrypt the ciphertext in `dm`, on the card. + /// + /// (This is a wrapper around the low-level pso_decipher + /// operation, it builds the required `data` field from `dm`) + pub fn decipher(&mut self, dm: Cryptogram) -> Result, Error> { + match dm { + Cryptogram::RSA(message) => { + // "Padding indicator byte (00) for RSA" (pg. 69) + let mut data = vec![0x0]; + data.extend_from_slice(message); + + // Call the card to decrypt `data` + self.pso_decipher(data) + } + Cryptogram::ECDH(eph) => { + // "In case of ECDH the card supports a partial decrypt + // only. The input is a cipher DO with the following data:" + // A6 xx Cipher DO + // -> 7F49 xx Public Key DO + // -> 86 xx External Public Key + + // External Public Key + let epk = Tlv::new([0x86], Value::S(eph.to_vec())); + + // Public Key DO + let pkdo = Tlv::new([0x7f, 0x49], Value::C(vec![epk])); + + // Cipher DO + let cdo = Tlv::new([0xa6], Value::C(vec![pkdo])); + + self.pso_decipher(cdo.serialize()) + } + } + } + + /// Run decryption operation on the smartcard (low level operation) + /// (7.2.11 PSO: DECIPHER) + fn pso_decipher(&mut self, data: Vec) -> Result, Error> { + // The OpenPGP card is already connected and PW1 82 has been verified + let dec_cmd = commands::decryption(data); + let resp = apdu::send_command(self, dec_cmd, true)?; + resp.check_ok()?; + + Ok(resp.data().map(|d| d.to_vec())?) + } + + // --- sign --- + + fn digestinfo(hash: Hash) -> Vec { + match hash { + Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => { + let tlv = Tlv::new( + [0x30], + Value::C(vec![ + Tlv::new( + [0x30], + Value::C(vec![ + Tlv::new( + [0x06], + // unwrapping is ok, for SHA* + Value::S(hash.oid().unwrap().to_vec()), + ), + Tlv::new([0x05], Value::S(vec![])), + ]), + ), + Tlv::new([0x04], Value::S(hash.digest().to_vec())), + ]), + ); + + tlv.serialize() + } + Hash::EdDSA(d) => d.to_vec(), + Hash::ECDSA(d) => d.to_vec(), + } + } + + /// Sign `hash`, on the card. + /// + /// This is a wrapper around the low-level + /// pso_compute_digital_signature operation. + /// It builds the required `data` field from `hash`. + /// + /// For RSA, this means a "DigestInfo" data structure is generated. + /// (see 7.2.10.2 DigestInfo for RSA). + /// + /// With ECC the hash data is processed as is, using + /// pso_compute_digital_signature. + pub fn signature_for_hash( + &mut self, + hash: Hash, + ) -> Result, Error> { + self.pso_compute_digital_signature(Self::digestinfo(hash)) + } + + /// Run signing operation on the smartcard (low level operation) + /// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE) + pub fn pso_compute_digital_signature( + &mut self, + data: Vec, + ) -> Result, Error> { + let cds_cmd = commands::signature(data); + + let resp = apdu::send_command(self, cds_cmd, true)?; + + Ok(resp.data().map(|d| d.to_vec())?) + } + + // --- internal authenticate --- + + /// Auth-sign `hash`, on the card. + /// + /// This is a wrapper around the low-level + /// internal_authenticate operation. + /// It builds the required `data` field from `hash`. + /// + /// For RSA, this means a "DigestInfo" data structure is generated. + /// (see 7.2.10.2 DigestInfo for RSA). + /// + /// With ECC the hash data is processed as is. + pub fn authenticate_for_hash( + &mut self, + hash: Hash, + ) -> Result, Error> { + self.internal_authenticate(Self::digestinfo(hash)) + } + + /// Run signing operation on the smartcard (low level operation) + /// (7.2.13 INTERNAL AUTHENTICATE) + pub fn internal_authenticate( + &mut self, + data: Vec, + ) -> Result, Error> { + let ia_cmd = commands::internal_authenticate(data); + let resp = apdu::send_command(self, ia_cmd, true)?; + + Ok(resp.data().map(|d| d.to_vec())?) + } + + // --- admin --- + + pub fn set_name(&mut self, name: &str) -> Result { + let put_name = commands::put_name(name.as_bytes().to_vec()); + apdu::send_command(self, put_name, false)?.try_into() + } + + pub fn set_lang(&mut self, lang: &str) -> Result { + let put_lang = commands::put_lang(lang.as_bytes().to_vec()); + apdu::send_command(self, put_lang, false)?.try_into() + } + + pub fn set_sex(&mut self, sex: Sex) -> Result { + let put_sex = commands::put_sex((&sex).into()); + apdu::send_command(self, put_sex, false)?.try_into() + } + + pub fn set_url(&mut self, url: &str) -> Result { + let put_url = commands::put_url(url.as_bytes().to_vec()); + apdu::send_command(self, put_url, false)?.try_into() + } + + pub fn set_creation_time( + &mut self, + time: KeyGenerationTime, + key_type: KeyType, + ) -> Result { + // Timestamp update + let time_value: Vec = time + .get() + .to_be_bytes() + .iter() + .skip_while(|&&e| e == 0) + .copied() + .collect(); + + let time_cmd = + commands::put_data(&[key_type.timestamp_put_tag()], time_value); + + apdu::send_command(self, time_cmd, false)?.try_into() + } + + pub fn set_fingerprint( + &mut self, + fp: Fingerprint, + key_type: KeyType, + ) -> Result { + let fp_cmd = commands::put_data( + &[key_type.fingerprint_put_tag()], + fp.as_bytes().to_vec(), + ); + + apdu::send_command(self, fp_cmd, false)?.try_into() + } + + /// Set PW Status Bytes. + /// + /// If `long` is false, send 1 byte to the card, otherwise 4. + /// According to the spec, length information should not be changed. + /// + /// So, effectively, with 'long == false' the setting `pw1_cds_multi` + /// can be changed. + /// With 'long == true', the settings `pw1_pin_block` and `pw3_pin_block` + /// can also be changed. + /// + /// (See OpenPGP card spec, pg. 28) + pub fn set_pw_status_bytes( + &mut self, + pw_status: &PWStatusBytes, + long: bool, + ) -> Result { + let data = pw_status.serialize_for_put(long); + + let cmd = commands::put_pw_status(data); + apdu::send_command(self, cmd, false)?.try_into() + } + + /// Set cardholder certificate (for AUT, DEC or SIG). + /// + /// Call select_data() before calling this fn, to select a particular + /// certificate (if the card supports multiple certificates). + pub fn set_cardholder_certificate( + &mut self, + data: Vec, + ) -> Result { + let cmd = commands::put_cardholder_certificate(data); + apdu::send_command(self, cmd, false)?.try_into() + } + + /// Set algorithm attributes + /// (4.4.3.9 Algorithm Attributes) + pub fn set_algorithm_attributes( + &mut self, + key_type: KeyType, + algo: &Algo, + ) -> Result { + // Command to PUT the algorithm attributes + let cmd = commands::put_data( + &[key_type.algorithm_tag()], + algo.to_data_object()?, + ); + + apdu::send_command(self, cmd, false)?.try_into() + } + + /// Set resetting code + /// (4.3.4 Resetting Code) + pub fn set_resetting_code( + &mut self, + resetting_code: Vec, + ) -> Result { + let cmd = commands::put_data(&[0xd3], resetting_code); + apdu::send_command(self, cmd, false)?.try_into() + } + + /// Import an existing private key to the card. + /// (This implicitly sets the algorithm info, fingerprint and timestamp) + pub fn key_import( + &mut self, + key: Box, + key_type: KeyType, + ) -> Result<(), Error> { + let algo_info = self.algorithm_information(); + + // An error is ok - it's fine if a card doesn't offer a list of + // supported algorithms + let algo_info = algo_info.unwrap_or(None); + + keys::key_import(self, key, key_type, algo_info) + } + + /// Generate a key on the card. + /// (7.2.14 GENERATE ASYMMETRIC KEY PAIR) + /// + /// If the `algo` parameter is Some, then this algorithm will be set on + /// the card for "key_type". + /// + /// Note: `algo` needs to precisely specify the RSA bitsize of e (if + /// applicable), and import format, with values that the current card + /// supports. + pub fn generate_key( + &mut self, + fp_from_pub: fn( + &PublicKeyMaterial, + KeyGenerationTime, + KeyType, + ) -> Result, + key_type: KeyType, + algo: Option<&Algo>, + ) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> { + keys::gen_key_with_metadata(self, fp_from_pub, key_type, algo) + } + + /// Generate a key on the card. + /// (7.2.14 GENERATE ASYMMETRIC KEY PAIR) + /// + /// This is a wrapper around generate_key() which allows + /// using the simplified `AlgoSimple` algorithm selector enum. + /// + /// Note: AlgoSimple doesn't specify card specific details (such as + /// bitsize of e for RSA, and import format). This function determines + /// these values based on information from the card. + pub fn generate_key_simple( + &mut self, + fp_from_pub: fn( + &PublicKeyMaterial, + KeyGenerationTime, + KeyType, + ) -> Result, + key_type: KeyType, + simple: AlgoSimple, + ) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> { + let ard = self.application_related_data()?; + let algo_info = if let Ok(ai) = self.algorithm_information() { + ai + } else { + None + }; + + let algo = simple.determine_algo(key_type, &ard, algo_info)?; + + Self::generate_key(self, fp_from_pub, key_type, Some(&algo)) + } + + /// Get public key material from the card. + /// + /// Note: this fn returns a set of raw public key data (not an + /// OpenPGP data structure). + /// + /// Note also that the information from the card is insufficient to + /// reconstruct a pre-existing OpenPGP public key that corresponds to + /// the private key on the card. + pub fn public_key( + &mut self, + key_type: KeyType, + ) -> Result { + keys::public_key(self, key_type) + } } /// Configuration of the capabilities of a card. diff --git a/pcsc/src/lib.rs b/pcsc/src/lib.rs index 108e323..9e5bedf 100644 --- a/pcsc/src/lib.rs +++ b/pcsc/src/lib.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; use std::convert::TryInto; use openpgp_card::card_do::ApplicationRelatedData; -use openpgp_card::{CardApp, CardCaps, CardClient, Error, SmartcardError}; +use openpgp_card::{CardCaps, CardClient, Error, SmartcardError}; const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06; const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07; @@ -393,8 +393,8 @@ impl PcscClient { } } - // Get initalized CardApp - CardApp::initialize(&mut self)?; + // Initalize CardClient (set CardCaps from ARD) + ::initialize(&mut self)?; Ok(self) } diff --git a/scdc/src/lib.rs b/scdc/src/lib.rs index 82481ee..2a89dfe 100644 --- a/scdc/src/lib.rs +++ b/scdc/src/lib.rs @@ -13,8 +13,7 @@ use sequoia_ipc::gnupg::{Agent, Context}; use std::sync::Mutex; use tokio::runtime::Runtime; -use openpgp_card::{CardApp, Error}; -use openpgp_card::{CardCaps, CardClient}; +use openpgp_card::{CardCaps, CardClient, Error}; lazy_static! { static ref RT: Mutex = @@ -66,7 +65,7 @@ impl ScdClient { let mut card = ScdClient::new(agent, true)?; card.select_card(serial)?; - CardApp::initialize(&mut card)?; + ::initialize(&mut card)?; Ok(card) } @@ -80,7 +79,7 @@ impl ScdClient { pub fn open_yolo(agent: Option) -> Result { let mut card = ScdClient::new(agent, true)?; - CardApp::initialize(&mut card)?; + ::initialize(&mut card)?; Ok(card) } diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index 1d32ceb..8318a8f 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -12,7 +12,7 @@ use sequoia_openpgp::serialize::SerializeInto; use sequoia_openpgp::Cert; use openpgp_card::algorithm::AlgoSimple; -use openpgp_card::{card_do::Sex, CardApp, KeyType}; +use openpgp_card::{card_do::Sex, CardApp, CardClient, KeyType}; use openpgp_card_sequoia::card::{Admin, Open}; use openpgp_card_sequoia::util::{make_cert, public_key_material_to_key}; @@ -151,7 +151,7 @@ fn set_identity( ) -> Result<(), Box> { let mut card = util::open_card(ident)?; - CardApp::set_identity(&mut card, id)?; + ::set_identity(&mut card, id)?; Ok(()) }