1226 lines
43 KiB
Rust
1226 lines
43 KiB
Rust
// SPDX-FileCopyrightText: 2021-2023 Heiko Schaefer <heiko@schaefer.name>
|
||
// 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<dyn std::error::Error>> {
|
||
//! for backend in PcscBackend::cards(None)? {
|
||
//! let mut card = Card::<Open>::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<dyn std::error::Error>> {
|
||
//! let cards = PcscBackend::card_backends(None)?;
|
||
//! let mut card = Card::<Open>::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<dyn std::error::Error>> {
|
||
//!
|
||
//! // Open card via PCSC
|
||
//! let cards = PcscBackend::card_backends(None)?;
|
||
//! let mut card = Card::<Open>::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<dyn std::error::Error>> {
|
||
//! // Open card via PCSC
|
||
//! let cards = PcscBackend::card_backends(None)?;
|
||
//! let mut card = Card::<Open>::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<dyn std::error::Error>> {
|
||
//! // Open card via PCSC
|
||
//! let cards = PcscBackend::card_backends(None)?;
|
||
//! let mut card = Card::<Open>::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<key::PublicParts, key::UnspecifiedRole>;
|
||
|
||
/// Optional PIN, used as a parameter to `Card<Transaction>::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<Option<&'p [u8]>> 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<u8>> for OptionalPin<'p> {
|
||
fn from(value: &'p Vec<u8>) -> 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<S>
|
||
where
|
||
S: State,
|
||
{
|
||
state: S,
|
||
}
|
||
|
||
impl Card<Open> {
|
||
/// 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<Open>`] for the first match, if any.
|
||
pub fn open_by_ident(
|
||
cards: impl Iterator<Item = Result<Box<dyn CardBackend + Send + Sync>, SmartcardError>>,
|
||
ident: &str,
|
||
) -> Result<Self, Error> {
|
||
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<Open>`] based on `backend` (after SELECTing the
|
||
/// OpenPGP card application).
|
||
pub fn new<B>(backend: B) -> Result<Self, Error>
|
||
where
|
||
B: Into<Box<dyn CardBackend + Send + Sync>>,
|
||
{
|
||
let pgp = openpgp_card::Card::new(backend)?;
|
||
|
||
Ok(Card::<Open> {
|
||
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<Transaction>`] object allows performing
|
||
/// operations on the card.
|
||
pub fn transaction(&mut self) -> Result<Card<Transaction>, Error> {
|
||
let opt = self.state.pgp.transaction()?;
|
||
|
||
Card::<Transaction>::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<dyn CardBackend + Send + Sync> {
|
||
self.state.pgp.into_card()
|
||
}
|
||
}
|
||
|
||
impl<'a> Card<Transaction<'a>> {
|
||
/// Internal constructor
|
||
fn new(mut opt: openpgp_card::Transaction<'a>) -> Result<Self, Error> {
|
||
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<User>`] 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<Card<User<'a, 'b>>, Error>
|
||
where
|
||
P: Into<OptionalPin<'p>>,
|
||
{
|
||
let pin: OptionalPin = pin.into();
|
||
|
||
if let Some(pin) = pin.0 {
|
||
self.verify_user_pin(String::from_utf8_lossy(pin).as_ref())?;
|
||
}
|
||
|
||
Ok(Card::<User> {
|
||
state: User { tx: self },
|
||
})
|
||
}
|
||
|
||
/// Get a view of the card in the [`Card<Sign>`] 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<Card<Sign<'a, 'b>>, Error>
|
||
where
|
||
P: Into<OptionalPin<'p>>,
|
||
{
|
||
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::<Sign> {
|
||
state: Sign { tx: self },
|
||
})
|
||
}
|
||
|
||
/// Get a view of the card in the [`Card<Admin>`] 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<Card<Admin<'a, 'b>>, Error>
|
||
where
|
||
P: Into<OptionalPin<'p>>,
|
||
{
|
||
let pin: OptionalPin = pin.into();
|
||
|
||
if let Some(pin) = pin.0 {
|
||
self.verify_admin_pin(String::from_utf8_lossy(pin).as_ref())?;
|
||
}
|
||
|
||
Ok(Card::<Admin> {
|
||
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<ApplicationIdentifier, Error> {
|
||
// 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<ExtendedCapabilities, Error> {
|
||
// 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<HistoricalBytes, Error> {
|
||
// 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<Option<ExtendedLengthInfo>, Error> {
|
||
// Use immutable data cache from underlying Card object
|
||
self.state.opt.extended_length_info()
|
||
}
|
||
|
||
#[allow(dead_code)]
|
||
fn general_feature_management() -> Option<bool> {
|
||
unimplemented!()
|
||
}
|
||
|
||
#[allow(dead_code)]
|
||
fn discretionary_data_objects() {
|
||
unimplemented!()
|
||
}
|
||
|
||
/// PW Status Bytes
|
||
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> {
|
||
self.state.ard().pw_status_bytes()
|
||
}
|
||
|
||
/// Get algorithm attributes for a key slot.
|
||
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<AlgorithmAttributes, Error> {
|
||
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<KeySet<Fingerprint>, 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<Option<Fingerprint>, 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<KeySet<KeyGenerationTime>, 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<Option<KeyGenerationTime>, 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<Option<KeyInformation>, 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<Option<UserInteractionFlag>, 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<Fingerprint>; 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<Vec<u8>, 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<Vec<u8>, 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<String, Error> {
|
||
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<CardholderRelatedData, Error> {
|
||
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<String, Error> {
|
||
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<u32, Error> {
|
||
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<Vec<u8>, 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<Vec<u8>, Error> {
|
||
self.state.opt.next_cardholder_certificate()
|
||
}
|
||
|
||
/// Algorithm Information (list of supported Algorithm attributes).
|
||
pub fn algorithm_information(&mut self) -> Result<Option<AlgorithmInformation>, 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<Vec<u8>, Error> {
|
||
self.state.opt.attestation_certificate()
|
||
}
|
||
|
||
/// Firmware Version, YubiKey specific (?)
|
||
pub fn firmware_version(&mut self) -> Result<Vec<u8>, Error> {
|
||
self.state.opt.firmware_version()
|
||
}
|
||
|
||
/// Set "identity", Nitrokey Start specific (possible values: 0, 1, 2).
|
||
/// <https://docs.nitrokey.com/start/windows/multiple-identities.html>
|
||
///
|
||
/// 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<PublicKeyMaterial, Error> {
|
||
self.state.opt.public_key(key_type)
|
||
}
|
||
|
||
/// Get a sequoia public key representation
|
||
/// ([`Key<key::PublicParts, key::UnspecifiedRole>`])
|
||
/// for a key slot on the card
|
||
pub fn public_key(&mut self, kt: KeyType) -> Result<Option<PublicKey>, 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<User<'app, 'open>> {
|
||
/// 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<CardDecryptor<'_, 'app>, 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<CardSigner<'_, 'app>, 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<u8>) -> Result<(), Error> {
|
||
self.card().set_private_use_do(num, data)
|
||
}
|
||
}
|
||
|
||
impl<'app, 'open> Card<Sign<'app, 'open>> {
|
||
/// 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<CardSigner<'_, 'app>, 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<Admin<'app, 'open>> {
|
||
pub fn as_transaction(&'_ mut self) -> &mut Card<Transaction<'app>> {
|
||
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<Admin<'_, '_>> {
|
||
/// 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<u8>) -> 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<Transaction>::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<Transaction>::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<SecretParts>,
|
||
key_type: KeyType,
|
||
password: Option<String>,
|
||
) -> 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)
|
||
}
|
||
}
|