// SPDX-FileCopyrightText: 2021-2023 Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 //! This crate offers ergonomic abstractions to use //! [OpenPGP card devices](https://en.wikipedia.org/wiki/OpenPGP_card). //! The central abstraction is the [Card] type, which offers access to all card operations. //! //! A [Card] object is always in one of the possible [State]s. The [State] determines which //! operations can be performed on the card. //! //! This crate is a convenient higher-level wrapper around the //! [openpgp-card](https://crates.io/crates/openpgp-card) crate (which exposes low level access //! to OpenPGP card functionality). //! [sequoia-openpgp](https://crates.io/crates/sequoia-openpgp) is used to perform OpenPGP operations. //! //! # Backends //! //! To make use of this crate, you need to use a backend for communication //! with cards. The suggested default backend is `card-backend-pcsc`. //! //! With `card-backend-pcsc` you can either open all available cards: //! //! ```no_run //! use card_backend_pcsc::PcscBackend; //! use openpgp_card_sequoia::{state::Open, Card}; //! //! # fn main() -> Result<(), Box> { //! for backend in PcscBackend::cards(None)? { //! let mut card = Card::::new(backend?)?; //! let mut transaction = card.transaction()?; //! println!( //! "Found OpenPGP card with ident '{}'", //! transaction.application_identifier()?.ident() //! ); //! } //! # Ok(()) //! # } //! ``` //! //! Or you can open one particular card, by ident: //! //! ```no_run //! use card_backend_pcsc::PcscBackend; //! use openpgp_card_sequoia::{state::Open, Card}; //! //! # fn main() -> Result<(), Box> { //! let cards = PcscBackend::card_backends(None)?; //! let mut card = Card::::open_by_ident(cards, "abcd:01234567")?; //! let mut transaction = card.transaction()?; //! # Ok(()) //! # } //! ``` //! //! # Use for cryptographic operations //! //! ## Decryption //! //! To use a card for decryption, it needs to be opened, and user //! authorization needs to be available. //! A [`sequoia_openpgp::crypto::Decryptor`] implementation can then be obtained: //! //! ```no_run //! use card_backend_pcsc::PcscBackend; //! use openpgp_card_sequoia::{state::Open, Card}; //! # fn main() -> Result<(), Box> { //! //! // Open card via PCSC //! let cards = PcscBackend::card_backends(None)?; //! let mut card = Card::::open_by_ident(cards, "abcd:01234567")?; //! let mut transaction = card.transaction()?; //! //! // Get user access to the card (and authorize with the user pin) //! let mut user = transaction.to_user_card("123456")?; //! //! // Get decryptor //! let decryptor = user.decryptor(&|| println!("Touch confirmation needed for decryption")); //! //! // Perform decryption operation(s) //! // .. //! //! # Ok(()) //! # } //! ``` //! //! ## Signing //! //! To use a card for signing, it needs to be opened, and signing //! authorization needs to be available. //! A [`sequoia_openpgp::crypto::Signer`] implementation can then be obtained. //! //! (Note that by default, some OpenPGP Cards will only allow one signing //! operation to be performed after the password has been presented for //! signing. Depending on the card's configuration you need to present the //! user password before each signing operation!) //! //! ```no_run //! use card_backend_pcsc::PcscBackend; //! use openpgp_card_sequoia::{state::Open, Card}; //! //! # fn main() -> Result<(), Box> { //! // Open card via PCSC //! let cards = PcscBackend::card_backends(None)?; //! let mut card = Card::::open_by_ident(cards, "abcd:01234567")?; //! let mut transaction = card.transaction()?; //! //! // Get signing access to the card (and authorize with the user pin) //! let mut user = transaction.to_signing_card("123456")?; //! //! // Get signer //! let signer = user.signer(&|| println!("Touch confirmation needed for signing")); //! //! // Perform signing operation(s) //! // .. //! //! # Ok(()) //! # } //! ``` //! //! # Setting up and configuring a card //! //! ```no_run //! use card_backend_pcsc::PcscBackend; //! use openpgp_card_sequoia::{state::Open, Card}; //! //! # fn main() -> Result<(), Box> { //! // Open card via PCSC //! let cards = PcscBackend::card_backends(None)?; //! let mut card = Card::::open_by_ident(cards, "abcd:01234567")?; //! let mut transaction = card.transaction()?; //! //! // Get admin access to the card (and authorize with the admin pin) //! let mut admin = transaction.to_admin_card("12345678")?; //! //! // Set the Name and URL fields on the card //! admin.set_cardholder_name("Alice Adams")?; //! admin.set_url("https://example.org/openpgp.asc")?; //! //! # Ok(()) //! # } //! ``` use card_backend::{CardBackend, SmartcardError}; use openpgp_card::algorithm::{AlgoSimple, AlgorithmAttributes, AlgorithmInformation}; use openpgp_card::card_do::{ ApplicationIdentifier, CardholderRelatedData, ExtendedCapabilities, ExtendedLengthInfo, Fingerprint, HistoricalBytes, KeyGenerationTime, KeyInformation, KeySet, Lang, PWStatusBytes, Sex, TouchPolicy, UserInteractionFlag, }; use openpgp_card::crypto_data::PublicKeyMaterial; use openpgp_card::{Error, KeyType}; use sequoia_openpgp::cert::prelude::ValidErasedKeyAmalgamation; use sequoia_openpgp::packet::key::SecretParts; use sequoia_openpgp::packet::{key, Key}; use crate::decryptor::CardDecryptor; use crate::signer::CardSigner; use crate::state::{Admin, Open, Sign, State, Transaction, User}; use crate::util::{public_key_material_and_fp_to_key, vka_as_uploadable_key}; mod decryptor; mod privkey; mod signer; pub mod sq_util; pub mod state; pub mod types; pub mod util; /// Shorthand for Sequoia public key data (a single public (sub)key) pub type PublicKey = Key; /// Optional PIN, used as a parameter to `Card::into_*_card`. /// /// Effectively acts like a `Option<&[u8]>`, but with a number of `From` /// implementations for convenience. pub struct OptionalPin<'p>(Option<&'p [u8]>); impl<'p> From> for OptionalPin<'p> { fn from(value: Option<&'p [u8]>) -> Self { OptionalPin(value) } } impl<'p> From<&'p str> for OptionalPin<'p> { fn from(value: &'p str) -> Self { OptionalPin(Some(value.as_bytes())) } } impl<'p> From<&'p Vec> for OptionalPin<'p> { fn from(value: &'p Vec) -> Self { OptionalPin(Some(value)) } } impl<'p, const S: usize> From<&'p [u8; S]> for OptionalPin<'p> { fn from(value: &'p [u8; S]) -> Self { OptionalPin(Some(value)) } } /// Representation of an OpenPGP card. /// /// A card transitions between [`State`]s by starting a transaction (that groups together a number /// of operations into an atomic sequence) and via PIN presentation. /// /// Depending on the [`State`] of the card, and the access privileges that are associated with that /// state, different operations can be performed. In many cases, client software will want to /// transition between states while performing one workflow for the user. pub struct Card where S: State, { state: S, } impl Card { /// Takes an iterator over [`CardBackend`]s, tries to SELECT the OpenPGP card /// application on each of them, and checks if its application id matches /// `ident`. /// Returns a [`Card`] for the first match, if any. pub fn open_by_ident( cards: impl Iterator, SmartcardError>>, ident: &str, ) -> Result { for b in cards.filter_map(|c| c.ok()) { let mut card = Self::new(b)?; let aid = { let tx = card.transaction()?; tx.state.ard().application_id()? }; if aid.ident() == ident.to_ascii_uppercase() { return Ok(card); } } Err(Error::NotFound(format!("Couldn't find card {}", ident))) } /// Returns a [`Card`] based on `backend` (after SELECTing the /// OpenPGP card application). pub fn new(backend: B) -> Result where B: Into>, { let pgp = openpgp_card::Card::new(backend)?; Ok(Card:: { state: Open { pgp }, }) } /// Starts a transaction on the underlying backend (if the backend /// implementation supports transactions, otherwise the backend /// will operate without transaction guarantees). /// /// The resulting [`Card`] object allows performing /// operations on the card. pub fn transaction(&mut self) -> Result, Error> { let opt = self.state.pgp.transaction()?; Card::::new(opt) } /// Retrieve the underlying [`CardBackend`]. /// /// This is useful to take the card object into a different context /// (e.g. to perform operations on the card with the `yubikey-management` /// crate, without closing the connection to the card). pub fn into_backend(self) -> Box { self.state.pgp.into_card() } } impl<'a> Card> { /// Internal constructor fn new(mut opt: openpgp_card::Transaction<'a>) -> Result { let ard = opt.application_related_data()?; Ok(Self { state: Transaction::new(opt, ard), }) } /// Replace cached "application related data" in this instance of Open /// with the current data on the card. /// /// This is needed e.g. after importing or generating keys on a card, to /// see these changes reflected in the internal cached /// [`openpgp_card::card_do::ApplicationRelatedData`]. pub fn reload_ard(&mut self) -> Result<(), Error> { // FIXME: this should be implemented internally, transparent to users let ard = self.state.opt.application_related_data()?; self.state.set_ard(ard); Ok(()) } /// True if the reader for this card supports PIN verification with a pin pad. pub fn feature_pinpad_verify(&mut self) -> bool { self.state.opt.feature_pinpad_verify() } /// True if the reader for this card supports PIN modification with a pin pad. pub fn feature_pinpad_modify(&mut self) -> bool { self.state.opt.feature_pinpad_modify() } /// Verify the User PIN (for operations such as decryption) pub fn verify_user_pin(&mut self, pin: &str) -> Result<(), Error> { self.state.opt.verify_pw1_user(pin.as_bytes())?; self.state.pw1 = true; Ok(()) } /// Verify the User PIN with a physical PIN pad (if available, /// see [`Self::feature_pinpad_verify`]). pub fn verify_user_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pinpad_prompt(); self.state.opt.verify_pw1_user_pinpad()?; self.state.pw1 = true; Ok(()) } /// Verify the User PIN for signing operations. /// /// (Note that depending on the configuration of the card, this may enable /// performing just one signing operation, or an unlimited amount of /// signing operations). pub fn verify_user_signing_pin(&mut self, pin: &str) -> Result<(), Error> { self.state.opt.verify_pw1_sign(pin.as_bytes())?; // FIXME: depending on card mode, pw1_sign is only usable once self.state.pw1_sign = true; Ok(()) } /// Verify the User PIN for signing operations with a physical PIN pad /// (if available, see [`Self::feature_pinpad_verify`]). pub fn verify_user_signing_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pinpad_prompt(); self.state.opt.verify_pw1_sign_pinpad()?; // FIXME: depending on card mode, pw1_sign is only usable once self.state.pw1_sign = true; Ok(()) } /// Verify the Admin PIN. pub fn verify_admin_pin(&mut self, pin: &str) -> Result<(), Error> { self.state.opt.verify_pw3(pin.as_bytes())?; self.state.pw3 = true; Ok(()) } /// Verify the Admin PIN with a physical PIN pad /// (if available, see [`Self::feature_pinpad_verify`]). pub fn verify_admin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pinpad_prompt(); self.state.opt.verify_pw3_pinpad()?; self.state.pw3 = true; Ok(()) } /// Ask the card if the user password has been successfully verified. /// /// NOTE: on some cards this functionality seems broken and may decrease /// the pin's error count! pub fn check_user_verified(&mut self) -> Result<(), Error> { self.state.opt.check_pw1_user() } /// Ask the card if the admin password has been successfully verified. /// /// NOTE: on some cards this functionality seems broken and may decrease /// the pin's error count! pub fn check_admin_verified(&mut self) -> Result<(), Error> { self.state.opt.check_pw3() } /// Change the User PIN, based on the old User PIN. pub fn change_user_pin(&mut self, old: &str, new: &str) -> Result<(), Error> { self.state.opt.change_pw1(old.as_bytes(), new.as_bytes()) } /// Change the User PIN, based on the old User PIN, with a physical PIN /// pad (if available, see [`Self::feature_pinpad_modify`]). pub fn change_user_pin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pinpad_prompt(); self.state.opt.change_pw1_pinpad() } /// Change the User PIN, based on the resetting code `rst`. pub fn reset_user_pin(&mut self, rst: &str, new: &str) -> Result<(), Error> { self.state .opt .reset_retry_counter_pw1(new.as_bytes(), Some(rst.as_bytes())) } /// Change the Admin PIN, based on the old Admin PIN. pub fn change_admin_pin(&mut self, old: &str, new: &str) -> Result<(), Error> { self.state.opt.change_pw3(old.as_bytes(), new.as_bytes()) } /// Change the Admin PIN, based on the old Admin PIN, with a physical PIN /// pad (if available, see [`Self::feature_pinpad_modify`]). pub fn change_admin_pin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pinpad_prompt(); self.state.opt.change_pw3_pinpad() } /// Get a view of the card in the [`Card`] state, and authenticate /// for that state with `pin`, if available. /// /// If `pin` is not None, `verify_user` is called with that pin. pub fn to_user_card<'b, 'p, P>(&'b mut self, pin: P) -> Result>, Error> where P: Into>, { let pin: OptionalPin = pin.into(); if let Some(pin) = pin.0 { self.verify_user_pin(String::from_utf8_lossy(pin).as_ref())?; } Ok(Card:: { state: User { tx: self }, }) } /// Get a view of the card in the [`Card`] state, and authenticate /// for that state with `pin`, if available. /// /// If `pin` is not None, `verify_user_for_signing` is called with that pin. pub fn to_signing_card<'b, 'p, P>(&'b mut self, pin: P) -> Result>, Error> where P: Into>, { let pin: OptionalPin = pin.into(); if let Some(pin) = pin.0 { self.verify_user_signing_pin(String::from_utf8_lossy(pin).as_ref())?; } Ok(Card:: { state: Sign { tx: self }, }) } /// Get a view of the card in the [`Card`] state, and authenticate /// for that state with `pin`, if available. /// /// If `pin` is not None, `verify_admin` is called with that pin. pub fn to_admin_card<'b, 'p, P>(&'b mut self, pin: P) -> Result>, Error> where P: Into>, { let pin: OptionalPin = pin.into(); if let Some(pin) = pin.0 { self.verify_admin_pin(String::from_utf8_lossy(pin).as_ref())?; } Ok(Card:: { state: Admin { tx: self }, }) } // --- application data --- /// The Application Identifier is unique for each card. /// It includes a manufacturer code and serial number. /// /// (This is an immutable field on the card. The value is cached in the /// underlying Card object. It can be retrieved without incurring a call /// to the card) pub fn application_identifier(&self) -> Result { // Use immutable data cache from underlying Card object self.state.opt.application_identifier() } /// The "Extended Capabilities" data object describes features of a card /// to the caller. /// This includes the availability and length of various data fields. /// /// (This is an immutable field on the card. The value is cached in the /// underlying Card object. It can be retrieved without incurring a call /// to the card) pub fn extended_capabilities(&self) -> Result { // Use immutable data cache from underlying Card object self.state.opt.extended_capabilities() } /// The "Historical Bytes" data object describes features of a card /// to the caller. /// The information in this field is probably not relevant for most /// users of this library, however, some of it is used for the internal /// operation of the `openpgp-card` library. /// /// (This is an immutable field on the card. The value is cached in the /// underlying Card object. It can be retrieved without incurring a call /// to the card) pub fn historical_bytes(&self) -> Result { // Use immutable data cache from underlying Card object match self.state.opt.historical_bytes()? { Some(hb) => Ok(hb), None => Err(Error::NotFound( "Card doesn't have historical bytes DO".to_string(), )), } } /// The "Extended Length Information" data object was introduced in /// version 3.0 of the OpenPGP card standard. /// /// The information in this field should not be relevant for /// users of this library. /// However, it is used for the internal operation of the `openpgp-card` /// library. /// /// (This is an immutable field on the card. The value is cached in the /// underlying Card object. It can be retrieved without incurring a call /// to the card) pub fn extended_length_information(&self) -> Result, Error> { // Use immutable data cache from underlying Card object self.state.opt.extended_length_info() } #[allow(dead_code)] fn general_feature_management() -> Option { unimplemented!() } #[allow(dead_code)] fn discretionary_data_objects() { unimplemented!() } /// PW Status Bytes pub fn pw_status_bytes(&self) -> Result { self.state.ard().pw_status_bytes() } /// Get algorithm attributes for a key slot. pub fn algorithm_attributes(&self, key_type: KeyType) -> Result { self.state.ard().algorithm_attributes(key_type) } /// Get the Fingerprints for the three basic [`KeyType`]s. /// /// (The fingerprints for the three basic key slots are stored in a /// shared field on the card, thus they can be retrieved in one go) pub fn fingerprints(&self) -> Result, Error> { self.state.ard().fingerprints() } /// Get the Fingerprint for one [`KeyType`]. /// /// This function allows retrieval for all slots, including /// [`KeyType::Attestation`], if available. pub fn fingerprint(&mut self, key_type: KeyType) -> Result, Error> { let fp = match key_type { KeyType::Signing => self.fingerprints()?.signature().cloned(), KeyType::Decryption => self.fingerprints()?.decryption().cloned(), KeyType::Authentication => self.fingerprints()?.authentication().cloned(), KeyType::Attestation => self.state.ard().attestation_key_fingerprint()?, _ => { return Err(Error::UnsupportedFeature(format!( "Can't get fingerprint for key_type {:?}", key_type, ))) } }; Ok(fp) } /// Get the Key Creation Times for the three basic [`KeyType`]s. /// /// (The creation time for the three basic key slots are stored in a /// shared field on the card, thus they can be retrieved in one go) pub fn key_generation_times(&self) -> Result, Error> { self.state.ard().key_generation_times() } /// Get the Key Creation Time for one [`KeyType`]. /// /// This function allows retrieval for all slots, including /// [`KeyType::Attestation`], if available. pub fn key_generation_time( &mut self, key_type: KeyType, ) -> Result, Error> { let ts = match key_type { KeyType::Signing => self.key_generation_times()?.signature().cloned(), KeyType::Decryption => self.key_generation_times()?.decryption().cloned(), KeyType::Authentication => self.key_generation_times()?.authentication().cloned(), KeyType::Attestation => self.state.ard().attestation_key_generation_time()?, _ => { return Err(Error::UnsupportedFeature(format!( "Can't get creation time for key_type {:?}", key_type, ))) } }; Ok(ts) } pub fn key_information(&self) -> Result, Error> { self.state.ard().key_information() } /// Get the [`UserInteractionFlag`] for a key slot. /// This includes the [`TouchPolicy`], if the card supports touch /// confirmation. pub fn user_interaction_flag( &self, key_type: KeyType, ) -> Result, Error> { match key_type { KeyType::Signing => self.state.ard().uif_pso_cds(), KeyType::Decryption => self.state.ard().uif_pso_dec(), KeyType::Authentication => self.state.ard().uif_pso_aut(), KeyType::Attestation => self.state.ard().uif_attestation(), _ => Err(Error::UnsupportedFeature(format!( "Can't get UIF for key_type {:?}", key_type, ))), } } /// List of CA-Fingerprints of “Ultimately Trusted Keys”. /// May be used to verify Public Keys from servers. pub fn ca_fingerprints(&self) -> Result<[Option; 3], Error> { self.state.ard().ca_fingerprints() } /// Get optional "Private use" data from the card. /// /// The presence and maximum length of these DOs is announced /// in [`ExtendedCapabilities`]. /// /// If available, there are 4 data fields for private use: /// /// - `1`: read accessible without PIN verification /// - `2`: read accessible without PIN verification /// - `3`: read accessible with User PIN verification /// - `4`: read accessible with Admin PIN verification pub fn private_use_do(&mut self, num: u8) -> Result, Error> { self.state.opt.private_use_do(num) } /// Login Data /// /// This DO can be used to store any information used for the Log-In /// process in a client/server authentication (e.g. user name of a /// network). /// The maximum length of this DO is announced in Extended Capabilities. pub fn login_data(&mut self) -> Result, Error> { self.state.opt.login_data() } // --- URL (5f50) --- /// Get "cardholder" URL from the card. /// /// "The URL should contain a link to a set of public keys in OpenPGP format, related to /// the card." pub fn url(&mut self) -> Result { Ok(String::from_utf8_lossy(&self.state.opt.url()?).to_string()) } /// Cardholder related data (contains the fields: Name, Language preferences and Sex) pub fn cardholder_related_data(&mut self) -> Result { self.state.opt.cardholder_related_data() } // Unicode codepoints are a superset of iso-8859-1 characters fn latin1_to_string(s: &[u8]) -> String { s.iter().map(|&c| c as char).collect() } /// Get cardholder name. /// /// This is an ISO 8859-1 (Latin 1) String of up to 39 characters. /// /// Note that the standard specifies that this field should be encoded /// according to ISO/IEC 7501-1: /// /// "The data element consists of surname (e. g. family name and given /// name(s)) and forename(s) (including name suffix, e. g., Jr. and number). /// Each item is separated by a ´<´ filler character (3C), the family- and /// fore-name(s) are separated by two ´<<´ filler characters." /// /// This library doesn't perform this encoding. pub fn cardholder_name(&mut self) -> Result { let crd = self.state.opt.cardholder_related_data()?; match crd.name() { Some(name) => Ok(Self::latin1_to_string(name)), None => Ok("".to_string()), } } /// Get the current digital signature count (how many signatures have been issued by the card) pub fn digital_signature_count(&mut self) -> Result { Ok(self .state .opt .security_support_template()? .signature_count()) } /// SELECT DATA ("select a DO in the current template"). pub fn select_data(&mut self, num: u8, tag: &[u8]) -> Result<(), Error> { self.state.opt.select_data(num, tag) } /// Get cardholder certificate. /// /// 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, Error> { self.state.opt.cardholder_certificate() } /// "GET NEXT DATA" for the DO cardholder certificate. /// /// Cardholder certificate data for multiple slots can be read from the card by first calling /// cardholder_certificate(), followed by up to two calls to next_cardholder_certificate(). pub fn next_cardholder_certificate(&mut self) -> Result, Error> { self.state.opt.next_cardholder_certificate() } /// Algorithm Information (list of supported Algorithm attributes). pub fn algorithm_information(&mut self) -> Result, Error> { // The DO "Algorithm Information" (Tag FA) shall be present if // Algorithm attributes can be changed let ec = self.extended_capabilities()?; if !ec.algo_attrs_changeable() { // Algorithm attributes can not be changed, // list_supported_algo is not supported return Ok(None); } self.state.opt.algorithm_information() } /// "MANAGE SECURITY ENVIRONMENT". /// Make `key_ref` usable for the operation normally done by the key /// designated by `for_operation` pub fn manage_security_environment( &mut self, for_operation: KeyType, key_ref: KeyType, ) -> Result<(), Error> { self.state .opt .manage_security_environment(for_operation, key_ref) } // ---------- /// Get "Attestation Certificate (Yubico)" pub fn attestation_certificate(&mut self) -> Result, Error> { self.state.opt.attestation_certificate() } /// Firmware Version, YubiKey specific (?) pub fn firmware_version(&mut self) -> Result, Error> { self.state.opt.firmware_version() } /// Set "identity", Nitrokey Start specific (possible values: 0, 1, 2). /// /// /// A Nitrokey Start can present as 3 different virtual OpenPGP cards. /// This command enables one of those virtual cards. /// /// Each virtual card identity behaves like a separate, independent OpenPGP card. pub fn set_identity(&mut self, id: u8) -> Result<(), Error> { // FIXME: what is in the returned data - is it ever useful? let _ = self.state.opt.set_identity(id)?; Ok(()) } // ---------- /// Get the raw public key material for a key slot on the card /// (also see [`Self::public_key`] for getting a Sequoia PGP key object) pub fn public_key_material(&mut self, key_type: KeyType) -> Result { self.state.opt.public_key(key_type) } /// Get a sequoia public key representation /// ([`Key`]) /// for a key slot on the card pub fn public_key(&mut self, kt: KeyType) -> Result, Error> { let fps = self.fingerprints()?; let ts = self.key_generation_time(kt)?; let fp = match kt { KeyType::Signing => fps.signature(), KeyType::Decryption => fps.decryption(), KeyType::Authentication => fps.authentication(), _ => None, }; match kt { KeyType::Signing => { if let Ok(pkm) = self.public_key_material(kt) { if let Some(ts) = ts { return Ok(Some(public_key_material_and_fp_to_key( &pkm, KeyType::Signing, &ts, fp.expect("Signature fingerprint is unset"), )?)); } } Ok(None) } KeyType::Decryption => { if let Ok(pkm) = self.public_key_material(KeyType::Decryption) { if let Some(ts) = ts { return Ok(Some(public_key_material_and_fp_to_key( &pkm, KeyType::Decryption, &ts, fp.expect("Decryption fingerprint is unset"), )?)); } } Ok(None) } KeyType::Authentication => { if let Ok(pkm) = self.public_key_material(KeyType::Authentication) { if let Some(ts) = ts { return Ok(Some(public_key_material_and_fp_to_key( &pkm, KeyType::Authentication, &ts, fp.expect("Authentication fingerprint is unset"), )?)); } } Ok(None) } _ => unimplemented!(), } } // ---------- /// Reset all state on this OpenPGP card pub fn factory_reset(&mut self) -> Result<(), Error> { self.state.opt.factory_reset() } } impl<'app, 'open> Card> { /// Helper fn to easily access underlying openpgp_card object fn card(&mut self) -> &mut openpgp_card::Transaction<'app> { &mut self.state.tx.state.opt } pub fn decryptor( &mut self, touch_prompt: &'open (dyn Fn() + Send + Sync), ) -> Result, Error> { let pk = self .state .tx .public_key(KeyType::Decryption)? .expect("Couldn't get decryption pubkey from card"); Ok(CardDecryptor::with_pubkey(self.card(), pk, touch_prompt)) } pub fn decryptor_from_public( &mut self, pubkey: PublicKey, touch_prompt: &'open (dyn Fn() + Send + Sync), ) -> CardDecryptor<'_, 'app> { CardDecryptor::with_pubkey(self.card(), pubkey, touch_prompt) } pub fn authenticator( &mut self, touch_prompt: &'open (dyn Fn() + Send + Sync), ) -> Result, Error> { let pk = self .state .tx .public_key(KeyType::Authentication)? .expect("Couldn't get authentication pubkey from card"); Ok(CardSigner::with_pubkey_for_auth( self.card(), pk, touch_prompt, )) } pub fn authenticator_from_public( &mut self, pubkey: PublicKey, touch_prompt: &'open (dyn Fn() + Send + Sync), ) -> CardSigner<'_, 'app> { CardSigner::with_pubkey_for_auth(self.card(), pubkey, touch_prompt) } /// Set optional "Private use" data on the card. /// /// The presence and maximum length of these DOs is announced /// in [`ExtendedCapabilities`]. /// /// If available, there are 4 data fields for private use: /// /// - `1`: write accessible with User PIN verification /// - `2`: write accessible with Admin PIN verification /// - `3`: write accessible with User PIN verification /// - `4`: write accessible with Admin PIN verification pub fn set_private_use_do(&mut self, num: u8, data: Vec) -> Result<(), Error> { self.card().set_private_use_do(num, data) } } impl<'app, 'open> Card> { /// Helper fn to easily access underlying openpgp_card object fn card(&mut self) -> &mut openpgp_card::Transaction<'app> { &mut self.state.tx.state.opt } pub fn signer( &mut self, touch_prompt: &'open (dyn Fn() + Send + Sync), ) -> Result, Error> { // FIXME: depending on the setting in "PW1 Status byte", only one // signature can be made after verification for signing let pk = self .state .tx .public_key(KeyType::Signing)? .expect("Couldn't get signing pubkey from card"); Ok(CardSigner::with_pubkey(self.card(), pk, touch_prompt)) } pub fn signer_from_public( &mut self, pubkey: PublicKey, touch_prompt: &'open (dyn Fn() + Send + Sync), ) -> CardSigner<'_, 'app> { // FIXME: depending on the setting in "PW1 Status byte", only one // signature can be made after verification for signing CardSigner::with_pubkey(self.card(), pubkey, touch_prompt) } /// Generate Attestation (Yubico) pub fn generate_attestation( &mut self, key_type: KeyType, touch_prompt: &'open (dyn Fn() + Send + Sync), ) -> Result<(), Error> { // Touch is required if: // - the card supports the feature // - and the policy is set to a value other than 'Off' if let Some(uif) = self.state.tx.state.ard().uif_attestation()? { if uif.touch_policy().touch_required() { (touch_prompt)(); } } self.card().generate_attestation(key_type) } } impl<'app, 'open> Card> { pub fn as_transaction(&'_ mut self) -> &mut Card> { self.state.tx } /// Helper fn to easily access underlying openpgp_card object fn card(&mut self) -> &mut openpgp_card::Transaction<'app> { &mut self.state.tx.state.opt } } impl Card> { /// Set cardholder name. /// /// This is an ISO 8859-1 (Latin 1) String of max. 39 characters. /// /// Note that the standard specifies that this field should be encoded according /// to ISO/IEC 7501-1: /// /// "The data element consists of surname (e. g. family name and given /// name(s)) and forename(s) (including name suffix, e. g., Jr. and number). /// Each item is separated by a ´<´ filler character (3C), the family- and /// fore-name(s) are separated by two ´<<´ filler characters." /// /// This library doesn't perform this encoding. pub fn set_cardholder_name(&mut self, name: &str) -> Result<(), Error> { // All chars must be in ASCII7 if name.chars().any(|c| !c.is_ascii()) { return Err(Error::InternalError("Invalid char in name".into())); }; // FIXME: encode spaces and do ordering if name.len() >= 40 { return Err(Error::InternalError("name too long".into())); } self.card().set_name(name.as_bytes()) } pub fn set_lang(&mut self, lang: &[Lang]) -> Result<(), Error> { if lang.len() > 8 { return Err(Error::InternalError("lang too long".into())); } self.card().set_lang(lang) } pub fn set_sex(&mut self, sex: Sex) -> Result<(), Error> { self.card().set_sex(sex) } /// Set optional "Private use" data on the card. /// /// The presence and maximum length of these DOs is announced /// in [`ExtendedCapabilities`]. /// /// If available, there are 4 data fields for private use: /// /// - `1`: write accessible with User PIN verification /// - `2`: write accessible with Admin PIN verification /// - `3`: write accessible with User PIN verification /// - `4`: write accessible with Admin PIN verification pub fn set_private_use_do(&mut self, num: u8, data: Vec) -> Result<(), Error> { self.card().set_private_use_do(num, data) } pub fn set_login_data(&mut self, login_data: &[u8]) -> Result<(), Error> { self.card().set_login(login_data) } /// Set "hardholder" URL on the card. /// /// "The URL should contain a link to a set of public keys in OpenPGP format, related to /// the card." pub fn set_url(&mut self, url: &str) -> Result<(), Error> { if url.chars().any(|c| !c.is_ascii()) { return Err(Error::InternalError("Invalid char in url".into())); } // Check for max len let ec = self.state.tx.extended_capabilities()?; if ec.max_len_special_do().is_none() || url.len() <= ec.max_len_special_do().unwrap() as usize { // If we don't know the max length for URL ("special DO"), // or if it's within the acceptable length: // send the url update to the card. self.card().set_url(url.as_bytes()) } else { Err(Error::InternalError("URL too long".into())) } } /// Set PW Status Bytes. /// /// According to the spec, length information should not be changed. /// /// If `long` is false, sends 1 byte to the card, otherwise 4. /// /// 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. pub fn set_pw_status_bytes( &mut self, pw_status: &PWStatusBytes, long: bool, ) -> Result<(), Error> { self.card().set_pw_status_bytes(pw_status, long) } /// Configure the "only valid for one PSO:CDS" setting in PW Status Bytes. /// /// If `once` is `true`, the User PIN must be verified before each /// signing operation on the card. /// If `once` is `false`, one User PIN verification is good for an /// unlimited number of signing operations. pub fn set_user_pin_signing_validity(&mut self, once: bool) -> Result<(), Error> { let mut pws = self.as_transaction().pw_status_bytes()?; pws.set_pw1_cds_valid_once(once); self.set_pw_status_bytes(&pws, false) } /// Set the touch policy for a key slot (if the card supports this /// feature). /// /// Note that the current touch policy setting (if available) can be read /// via [`Card::user_interaction_flag`]. pub fn set_touch_policy(&mut self, key: KeyType, policy: TouchPolicy) -> Result<(), Error> { let uif = match key { KeyType::Signing => self.state.tx.state.ard().uif_pso_cds()?, KeyType::Decryption => self.state.tx.state.ard().uif_pso_dec()?, KeyType::Authentication => self.state.tx.state.ard().uif_pso_aut()?, KeyType::Attestation => self.state.tx.state.ard().uif_attestation()?, _ => unimplemented!(), }; if let Some(mut uif) = uif { uif.set_touch_policy(policy); match key { KeyType::Signing => self.card().set_uif_pso_cds(&uif)?, KeyType::Decryption => self.card().set_uif_pso_dec(&uif)?, KeyType::Authentication => self.card().set_uif_pso_aut(&uif)?, KeyType::Attestation => self.card().set_uif_attestation(&uif)?, _ => unimplemented!(), } } else { return Err(Error::UnsupportedFeature( "User Interaction Flag not available".into(), )); }; Ok(()) } /// Set the User PIN on the card (also resets the User PIN error count) pub fn reset_user_pin(&mut self, new: &str) -> Result<(), Error> { self.card().reset_retry_counter_pw1(new.as_bytes(), None) } /// Define the "resetting code" on the card pub fn set_resetting_code(&mut self, pin: &str) -> Result<(), Error> { self.card().set_resetting_code(pin.as_bytes()) } /// Set optional AES encryption/decryption key /// (32 bytes for AES256, or 16 bytes for AES128), /// if the card supports this feature. /// /// The availability of this feature is announced in /// [`Card::extended_capabilities`]. pub fn set_pso_enc_dec_key(&mut self, key: &[u8]) -> Result<(), Error> { self.card().set_pso_enc_dec_key(key) } /// Upload a Sequoia PGP [`ValidErasedKeyAmalgamation`] to the card into /// a specific key slot. /// /// (The caller needs to make sure that `vka` is suitable as `key_type`) pub fn upload_key( &mut self, vka: ValidErasedKeyAmalgamation, key_type: KeyType, password: Option, ) -> Result<(), Error> { let key = vka_as_uploadable_key(vka, password); self.card().key_import(key, key_type) } /// Configure the `algorithm_attributes` for key slot `key_type` based on /// the algorithm `algo`. /// This can be useful in preparation for [`Self::generate_key`]. /// /// This is a convenience wrapper for [`Self::set_algorithm_attributes`] /// that determines the exact appropriate [`AlgorithmAttributes`] by /// reading information from the card. pub fn set_algorithm(&mut self, key_type: KeyType, algo: AlgoSimple) -> Result<(), Error> { let attr = algo.matching_algorithm_attributes(self.card(), key_type)?; self.set_algorithm_attributes(key_type, &attr) } /// Configure the key slot `key_type` to `algorithm_attributes`. /// This can be useful in preparation for [`Self::generate_key`]. /// /// Note that legal values for [`AlgorithmAttributes`] are card-specific. /// Different OpenPGP card implementations may support different /// algorithms, sometimes with differing requirements for the encoding /// (e.g. field sizes) /// /// See [`Self::set_algorithm`] for a convenience function that sets /// the algorithm attributes based on an [`AlgoSimple`]. pub fn set_algorithm_attributes( &mut self, key_type: KeyType, algorithm_attributes: &AlgorithmAttributes, ) -> Result<(), Error> { self.card() .set_algorithm_attributes(key_type, algorithm_attributes) } /// Generate a new cryptographic key in slot `key_type`, with the currently /// configured cryptographic algorithm /// (see [`Self::set_algorithm`] for changing the algorithm setting). pub fn generate_key( &mut self, key_type: KeyType, ) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> { self.card() .generate_key(crate::util::public_to_fingerprint, key_type) } }