Change the API for interactions between openpgp-card and backends.

The goal of this change is a cleaner structure, and in particular to make it the default for client-code to obtain a CardApp with pre-initialized "capabilities" (that is, init_caps() gets called implicitely).
This commit is contained in:
Heiko Schaefer 2021-11-11 15:28:44 +01:00
parent 288a2a8325
commit d55985807c
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
14 changed files with 147 additions and 173 deletions

View file

@ -97,16 +97,12 @@ impl TestCard {
let res = ScdClient::shutdown_scd(None); let res = ScdClient::shutdown_scd(None);
log::trace!(" Attempt to shutdown scd: {:?}", res); log::trace!(" Attempt to shutdown scd: {:?}", res);
for card_client in PcscClient::cards()? { for mut ca in PcscClient::cards()? {
let mut ca = CardApp::from(card_client);
// Set Card Capabilities (chaining, command length, ..) // Set Card Capabilities (chaining, command length, ..)
let ard = ca.get_application_related_data()?; let ard = ca.get_application_related_data()?;
let app_id = ard.get_application_id()?; let app_id = ard.get_application_id()?;
if app_id.ident().as_str() == ident.to_uppercase() { if app_id.ident().as_str() == ident.to_uppercase() {
ca.init_caps(&ard)?;
// println!("opened pcsc card {}", ident); // println!("opened pcsc card {}", ident);
return Ok(ca); return Ok(ca);
@ -116,8 +112,7 @@ impl TestCard {
Err(anyhow!("Pcsc card {} not found", ident)) Err(anyhow!("Pcsc card {} not found", ident))
} }
Self::Scdc(serial) => { Self::Scdc(serial) => {
let card_client = ScdClient::open_by_serial(None, serial)?; let mut ca = ScdClient::open_by_serial(None, serial)?;
let mut ca = CardApp::from(card_client);
// Set Card Capabilities (chaining, command length, ..) // Set Card Capabilities (chaining, command length, ..)
let ard = ca.get_application_related_data()?; let ard = ca.get_application_related_data()?;

View file

@ -9,8 +9,7 @@ use openpgp_card_sequoia::card::Open;
fn main() -> Result<()> { fn main() -> Result<()> {
println!("The following OpenPGP cards are connected to your system:"); println!("The following OpenPGP cards are connected to your system:");
for ccb in PcscClient::cards()? { for mut ca in PcscClient::cards()? {
let mut ca = ccb.into();
let open = Open::open(&mut ca)?; let open = Open::open(&mut ca)?;
println!(" {}", open.application_identifier()?.ident()); println!(" {}", open.application_identifier()?.ident());
} }

View file

@ -22,7 +22,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let pin_file = &args[1]; let pin_file = &args[1];
let cert_file = &args[2]; let cert_file = &args[2];
let mut ca = PcscClient::open_by_ident(&card_ident)?.into(); let mut ca = PcscClient::open_by_ident(card_ident)?;
let mut open = Open::open(&mut ca)?; let mut open = Open::open(&mut ca)?;
let pin = std::fs::read_to_string(pin_file)?; let pin = std::fs::read_to_string(pin_file)?;

View file

@ -23,7 +23,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let pin_file = &args[1]; let pin_file = &args[1];
let cert_file = &args[2]; let cert_file = &args[2];
let mut ca = PcscClient::open_by_ident(&card_ident)?.into(); let mut ca = PcscClient::open_by_ident(card_ident)?;
let mut open = Open::open(&mut ca)?; let mut open = Open::open(&mut ca)?;
let pin = std::fs::read_to_string(pin_file)?; let pin = std::fs::read_to_string(pin_file)?;

View file

@ -35,7 +35,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let test_card_ident = env::var("TEST_CARD_IDENT"); let test_card_ident = env::var("TEST_CARD_IDENT");
if let Ok(test_card_ident) = test_card_ident { if let Ok(test_card_ident) = test_card_ident {
let mut card = PcscClient::open_by_ident(&test_card_ident)?.into(); let mut card = PcscClient::open_by_ident(&test_card_ident)?;
let mut open = Open::open(&mut card)?; let mut open = Open::open(&mut card)?;
// card metadata // card metadata
@ -150,7 +150,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// ----------------------------- // -----------------------------
// Open fresh Card for decrypt // Open fresh Card for decrypt
// ----------------------------- // -----------------------------
let mut card = PcscClient::open_by_ident(&test_card_ident)?.into(); let mut card = PcscClient::open_by_ident(&test_card_ident)?;
let mut open = Open::open(&mut card)?; let mut open = Open::open(&mut card)?;
// Check that we're still using the expected card // Check that we're still using the expected card
@ -189,7 +189,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// ----------------------------- // -----------------------------
// Open fresh Card for signing // Open fresh Card for signing
// ----------------------------- // -----------------------------
let mut card = PcscClient::open_by_ident(&test_card_ident)?.into(); let mut card = PcscClient::open_by_ident(&test_card_ident)?;
let mut open = Open::open(&mut card)?; let mut open = Open::open(&mut card)?;
// Sign // Sign
@ -219,8 +219,7 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("The following OpenPGP cards are connected to your system:"); println!("The following OpenPGP cards are connected to your system:");
for card in PcscClient::cards()? { for mut card in PcscClient::cards()? {
let mut card = card.into();
let open = Open::open(&mut card)?; let open = Open::open(&mut card)?;
println!(" {}", open.application_identifier()?.ident()); println!(" {}", open.application_identifier()?.ident());
} }

View file

@ -13,7 +13,7 @@ use std::convert::TryFrom;
use crate::apdu::command::Expect; use crate::apdu::command::Expect;
use crate::apdu::{command::Command, response::RawResponse}; use crate::apdu::{command::Command, response::RawResponse};
use crate::{CardClientBox, Error, StatusBytes}; use crate::{CardClient, Error, StatusBytes};
/// "Maximum amount of bytes in a short APDU command or response" (from pcsc) /// "Maximum amount of bytes in a short APDU command or response" (from pcsc)
const MAX_BUFFER_SIZE: usize = 264; const MAX_BUFFER_SIZE: usize = 264;
@ -23,7 +23,7 @@ const MAX_BUFFER_SIZE: usize = 264;
/// If the reply is truncated, this fn assembles all the parts and returns /// If the reply is truncated, this fn assembles all the parts and returns
/// them as one aggregated Response. /// them as one aggregated Response.
pub(crate) fn send_command( pub(crate) fn send_command(
card_client: &mut CardClientBox, card_client: &mut dyn CardClient,
cmd: Command, cmd: Command,
expect_reply: bool, expect_reply: bool,
) -> Result<RawResponse, Error> { ) -> Result<RawResponse, Error> {
@ -82,7 +82,7 @@ pub(crate) fn send_command(
/// If the response is chained, this fn only returns one chunk, the caller /// If the response is chained, this fn only returns one chunk, the caller
/// needs to re-assemble the chained response-parts. /// needs to re-assemble the chained response-parts.
fn send_command_low_level( fn send_command_low_level(
card_client: &mut CardClientBox, card_client: &mut dyn CardClient,
cmd: Command, cmd: Command,
expect_response: Expect, expect_response: Expect,
) -> Result<Vec<u8>, Error> { ) -> Result<Vec<u8>, Error> {

View file

@ -3,7 +3,6 @@
//! CardApp exposes functionality of the "OpenPGP card" application. //! CardApp exposes functionality of the "OpenPGP card" application.
use std::borrow::BorrowMut;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::convert::TryInto; use std::convert::TryInto;
@ -19,7 +18,7 @@ use crate::crypto_data::{
CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial, CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial,
}; };
use crate::tlv::{tag::Tag, value::Value, Tlv}; use crate::tlv::{tag::Tag, value::Value, Tlv};
use crate::{apdu, keys, CardCaps, CardClientBox, KeyType}; use crate::{apdu, keys, CardCaps, CardClient, CardClientBox, KeyType};
use crate::{Error, StatusBytes}; use crate::{Error, StatusBytes};
/// Low-level access to OpenPGP card functionality. /// Low-level access to OpenPGP card functionality.
@ -33,22 +32,26 @@ pub struct CardApp {
card_client: CardClientBox, card_client: CardClientBox,
} }
impl From<CardClientBox> for CardApp {
fn from(card_client: CardClientBox) -> Self {
Self { card_client }
}
}
impl From<CardApp> for CardClientBox {
fn from(card_app: CardApp) -> CardClientBox {
card_app.card_client
}
}
impl CardApp { impl CardApp {
/// Get a CardApp based on a CardClient.
///
/// It is expected that SELECT has already been performed on the card.
///
/// This fn calls CardClient::init_caps(). It should probably only be used
/// by backend implementations, not by user code. User Code should get
/// a fully initialized CardApp from their backend implementation.
pub fn initialize(card_client: CardClientBox) -> Result<Self> {
let mut ca = Self { card_client };
let ard = ca.get_application_related_data()?;
ca.init_caps(&ard)?;
Ok(ca)
}
/// Get the CardClient for this CardApp /// Get the CardClient for this CardApp
pub(crate) fn get_card_client(&mut self) -> &mut CardClientBox { pub(crate) fn card_client(&mut self) -> &mut dyn CardClient {
&mut self.card_client &mut *self.card_client
} }
/// Initialize the CardCaps settings in the underlying CardClient /// Initialize the CardCaps settings in the underlying CardClient
@ -95,15 +98,6 @@ impl CardApp {
Ok(()) Ok(())
} }
// --- select ---
/// Select the OpenPGP card application
pub fn select(&mut self) -> Result<Response, Error> {
let select_openpgp = commands::select_openpgp();
apdu::send_command(&mut self.card_client, select_openpgp, false)?
.try_into()
}
// --- get data --- // --- get data ---
/// Get the "application related data" from the card. /// Get the "application related data" from the card.
@ -115,7 +109,7 @@ impl CardApp {
&mut self, &mut self,
) -> Result<ApplicationRelatedData> { ) -> Result<ApplicationRelatedData> {
let ad = commands::get_application_data(); let ad = commands::get_application_data();
let resp = apdu::send_command(&mut self.card_client, ad, true)?; let resp = apdu::send_command(self.card_client(), ad, true)?;
let value = Value::from(resp.data()?, true)?; let value = Value::from(resp.data()?, true)?;
log::debug!(" App data Value: {:x?}", value); log::debug!(" App data Value: {:x?}", value);
@ -130,7 +124,7 @@ impl CardApp {
assert!((1..=4).contains(&num)); assert!((1..=4).contains(&num));
let cmd = commands::get_private_do(num); let cmd = commands::get_private_do(num);
let resp = apdu::send_command(&mut self.card_client, cmd, true)?; let resp = apdu::send_command(self.card_client(), cmd, true)?;
Ok(resp.data()?.to_vec()) Ok(resp.data()?.to_vec())
} }
@ -166,11 +160,8 @@ impl CardApp {
// --- URL (5f50) --- // --- URL (5f50) ---
pub fn get_url(&mut self) -> Result<String> { pub fn get_url(&mut self) -> Result<String> {
let resp = apdu::send_command( let resp =
&mut self.card_client, apdu::send_command(self.card_client(), commands::get_url(), true)?;
commands::get_url(),
true,
)?;
Ok(String::from_utf8_lossy(resp.data()?).to_string()) Ok(String::from_utf8_lossy(resp.data()?).to_string())
} }
@ -180,7 +171,7 @@ impl CardApp {
&mut self, &mut self,
) -> Result<CardholderRelatedData> { ) -> Result<CardholderRelatedData> {
let crd = commands::cardholder_related_data(); let crd = commands::cardholder_related_data();
let resp = apdu::send_command(&mut self.card_client, crd, true)?; let resp = apdu::send_command(self.card_client(), crd, true)?;
resp.check_ok()?; resp.check_ok()?;
CardholderRelatedData::try_from(resp.data()?) CardholderRelatedData::try_from(resp.data()?)
@ -191,7 +182,7 @@ impl CardApp {
&mut self, &mut self,
) -> Result<SecuritySupportTemplate> { ) -> Result<SecuritySupportTemplate> {
let sst = commands::get_security_support_template(); let sst = commands::get_security_support_template();
let resp = apdu::send_command(&mut self.card_client, sst, true)?; let resp = apdu::send_command(self.card_client(), sst, true)?;
resp.check_ok()?; resp.check_ok()?;
let tlv = Tlv::try_from(resp.data()?)?; let tlv = Tlv::try_from(resp.data()?)?;
@ -219,13 +210,13 @@ impl CardApp {
/// certificate (if the card supports multiple certificates). /// certificate (if the card supports multiple certificates).
pub fn get_cardholder_certificate(&mut self) -> Result<Response, Error> { pub fn get_cardholder_certificate(&mut self) -> Result<Response, Error> {
let cmd = commands::get_cardholder_certificate(); let cmd = commands::get_cardholder_certificate();
apdu::send_command(&mut self.card_client, cmd, true)?.try_into() apdu::send_command(self.card_client(), cmd, true)?.try_into()
} }
/// DO "Algorithm Information" /// DO "Algorithm Information"
pub fn get_algo_info(&mut self) -> Result<Option<AlgoInfo>> { pub fn get_algo_info(&mut self) -> Result<Option<AlgoInfo>> {
let resp = apdu::send_command( let resp = apdu::send_command(
&mut self.card_client, self.card_client(),
commands::get_algo_list(), commands::get_algo_list(),
true, true,
)?; )?;
@ -250,7 +241,7 @@ impl CardApp {
let data = tlv.serialize(); let data = tlv.serialize();
let cmd = commands::select_data(num, data); let cmd = commands::select_data(num, data);
apdu::send_command(&mut self.card_client, cmd, true)?.try_into() apdu::send_command(self.card_client(), cmd, true)?.try_into()
} }
// ---------- // ----------
@ -268,8 +259,7 @@ impl CardApp {
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40] // [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
for _ in 0..4 { for _ in 0..4 {
let verify = commands::verify_pw1_81([0x40; 8].to_vec()); let verify = commands::verify_pw1_81([0x40; 8].to_vec());
let resp = let resp = apdu::send_command(self.card_client(), verify, false)?;
apdu::send_command(&mut self.card_client, verify, false)?;
if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied
|| resp.status() == StatusBytes::AuthenticationMethodBlocked || resp.status() == StatusBytes::AuthenticationMethodBlocked
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_))) || matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
@ -282,8 +272,7 @@ impl CardApp {
// [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40] // [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40]
for _ in 0..4 { for _ in 0..4 {
let verify = commands::verify_pw3([0x40; 8].to_vec()); let verify = commands::verify_pw3([0x40; 8].to_vec());
let resp = let resp = apdu::send_command(self.card_client(), verify, false)?;
apdu::send_command(&mut self.card_client, verify, false)?;
if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied
|| resp.status() == StatusBytes::AuthenticationMethodBlocked || resp.status() == StatusBytes::AuthenticationMethodBlocked
@ -295,12 +284,12 @@ impl CardApp {
// terminate_df [apdu 00 e6 00 00] // terminate_df [apdu 00 e6 00 00]
let term = commands::terminate_df(); let term = commands::terminate_df();
let resp = apdu::send_command(&mut self.card_client, term, false)?; let resp = apdu::send_command(self.card_client(), term, false)?;
resp.check_ok()?; resp.check_ok()?;
// activate_file [apdu 00 44 00 00] // activate_file [apdu 00 44 00 00]
let act = commands::activate_file(); let act = commands::activate_file();
let resp = apdu::send_command(&mut self.card_client, act, false)?; let resp = apdu::send_command(self.card_client(), act, false)?;
resp.check_ok()?; resp.check_ok()?;
Ok(()) Ok(())
@ -317,7 +306,7 @@ impl CardApp {
pin: &str, pin: &str,
) -> Result<Response, Error> { ) -> Result<Response, Error> {
let verify = commands::verify_pw1_81(pin.as_bytes().to_vec()); let verify = commands::verify_pw1_81(pin.as_bytes().to_vec());
apdu::send_command(&mut self.card_client, verify, false)?.try_into() apdu::send_command(self.card_client(), verify, false)?.try_into()
} }
/// Check the current access of PW1 for signing (mode 81). /// Check the current access of PW1 for signing (mode 81).
@ -328,14 +317,14 @@ impl CardApp {
/// e.g. yubikey 5) /// e.g. yubikey 5)
pub fn check_pw1_for_signing(&mut self) -> Result<Response, Error> { pub fn check_pw1_for_signing(&mut self) -> Result<Response, Error> {
let verify = commands::verify_pw1_81(vec![]); let verify = commands::verify_pw1_81(vec![]);
apdu::send_command(&mut self.card_client, verify, false)?.try_into() apdu::send_command(self.card_client(), verify, false)?.try_into()
} }
/// Verify PW1 (user) and set an appropriate access status. /// Verify PW1 (user) and set an appropriate access status.
/// (For operations except signing, mode 82). /// (For operations except signing, mode 82).
pub fn verify_pw1(&mut self, pin: &str) -> Result<Response, Error> { pub fn verify_pw1(&mut self, pin: &str) -> Result<Response, Error> {
let verify = commands::verify_pw1_82(pin.as_bytes().to_vec()); let verify = commands::verify_pw1_82(pin.as_bytes().to_vec());
apdu::send_command(&mut self.card_client, verify, false)?.try_into() apdu::send_command(self.card_client(), verify, false)?.try_into()
} }
/// Check the current access of PW1. /// Check the current access of PW1.
@ -347,13 +336,13 @@ impl CardApp {
/// e.g. yubikey 5) /// e.g. yubikey 5)
pub fn check_pw1(&mut self) -> Result<Response, Error> { pub fn check_pw1(&mut self) -> Result<Response, Error> {
let verify = commands::verify_pw1_82(vec![]); let verify = commands::verify_pw1_82(vec![]);
apdu::send_command(&mut self.card_client, verify, false)?.try_into() apdu::send_command(self.card_client(), verify, false)?.try_into()
} }
/// Verify PW3 (admin) and set an appropriate access status. /// Verify PW3 (admin) and set an appropriate access status.
pub fn verify_pw3(&mut self, pin: &str) -> Result<Response, Error> { pub fn verify_pw3(&mut self, pin: &str) -> Result<Response, Error> {
let verify = commands::verify_pw3(pin.as_bytes().to_vec()); let verify = commands::verify_pw3(pin.as_bytes().to_vec());
apdu::send_command(&mut self.card_client, verify, false)?.try_into() apdu::send_command(self.card_client(), verify, false)?.try_into()
} }
/// Check the current access of PW3 (admin). /// Check the current access of PW3 (admin).
@ -364,7 +353,7 @@ impl CardApp {
/// e.g. yubikey 5) /// e.g. yubikey 5)
pub fn check_pw3(&mut self) -> Result<Response, Error> { pub fn check_pw3(&mut self) -> Result<Response, Error> {
let verify = commands::verify_pw3(vec![]); let verify = commands::verify_pw3(vec![]);
apdu::send_command(&mut self.card_client, verify, false)?.try_into() apdu::send_command(self.card_client(), verify, false)?.try_into()
} }
/// Change the value of PW1 (user password). /// Change the value of PW1 (user password).
@ -380,7 +369,7 @@ impl CardApp {
data.extend(new.as_bytes()); data.extend(new.as_bytes());
let change = commands::change_pw1(data); let change = commands::change_pw1(data);
apdu::send_command(&mut self.card_client, change, false)?.try_into() apdu::send_command(self.card_client(), change, false)?.try_into()
} }
/// Change the value of PW3 (admin password). /// Change the value of PW3 (admin password).
@ -396,7 +385,7 @@ impl CardApp {
data.extend(new.as_bytes()); data.extend(new.as_bytes());
let change = commands::change_pw3(data); let change = commands::change_pw3(data);
apdu::send_command(&mut self.card_client, change, false)?.try_into() apdu::send_command(self.card_client(), change, false)?.try_into()
} }
/// Reset the error counter for PW1 (user password) and set a new value /// Reset the error counter for PW1 (user password) and set a new value
@ -412,7 +401,7 @@ impl CardApp {
resetting_code: Option<Vec<u8>>, resetting_code: Option<Vec<u8>>,
) -> Result<Response, Error> { ) -> Result<Response, Error> {
let reset = commands::reset_retry_counter_pw1(resetting_code, new_pw1); let reset = commands::reset_retry_counter_pw1(resetting_code, new_pw1);
apdu::send_command(&mut self.card_client, reset, false)?.try_into() apdu::send_command(self.card_client(), reset, false)?.try_into()
} }
// --- decrypt --- // --- decrypt ---
@ -460,7 +449,7 @@ impl CardApp {
fn pso_decipher(&mut self, data: Vec<u8>) -> Result<Vec<u8>, Error> { fn pso_decipher(&mut self, data: Vec<u8>) -> Result<Vec<u8>, Error> {
// The OpenPGP card is already connected and PW1 82 has been verified // The OpenPGP card is already connected and PW1 82 has been verified
let dec_cmd = commands::decryption(data); let dec_cmd = commands::decryption(data);
let resp = apdu::send_command(&mut self.card_client, dec_cmd, true)?; let resp = apdu::send_command(self.card_client(), dec_cmd, true)?;
resp.check_ok()?; resp.check_ok()?;
Ok(resp.data().map(|d| d.to_vec())?) Ok(resp.data().map(|d| d.to_vec())?)
@ -522,7 +511,7 @@ impl CardApp {
) -> Result<Vec<u8>, Error> { ) -> Result<Vec<u8>, Error> {
let cds_cmd = commands::signature(data); let cds_cmd = commands::signature(data);
let resp = apdu::send_command(&mut self.card_client, cds_cmd, true)?; let resp = apdu::send_command(self.card_client(), cds_cmd, true)?;
Ok(resp.data().map(|d| d.to_vec())?) Ok(resp.data().map(|d| d.to_vec())?)
} }
@ -553,7 +542,7 @@ impl CardApp {
data: Vec<u8>, data: Vec<u8>,
) -> Result<Vec<u8>, Error> { ) -> Result<Vec<u8>, Error> {
let ia_cmd = commands::internal_authenticate(data); let ia_cmd = commands::internal_authenticate(data);
let resp = apdu::send_command(&mut self.card_client, ia_cmd, true)?; let resp = apdu::send_command(self.card_client(), ia_cmd, true)?;
Ok(resp.data().map(|d| d.to_vec())?) Ok(resp.data().map(|d| d.to_vec())?)
} }
@ -571,31 +560,29 @@ impl CardApp {
assert!((1..=4).contains(&num)); assert!((1..=4).contains(&num));
let cmd = commands::put_private_do(num, data); let cmd = commands::put_private_do(num, data);
let resp = apdu::send_command(&mut self.card_client, cmd, true)?; let resp = apdu::send_command(self.card_client(), cmd, true)?;
Ok(resp.data()?.to_vec()) Ok(resp.data()?.to_vec())
} }
pub fn set_name(&mut self, name: &str) -> Result<Response, Error> { pub fn set_name(&mut self, name: &str) -> Result<Response, Error> {
let put_name = commands::put_name(name.as_bytes().to_vec()); let put_name = commands::put_name(name.as_bytes().to_vec());
apdu::send_command(&mut self.card_client, put_name, false)?.try_into() apdu::send_command(self.card_client(), put_name, false)?.try_into()
} }
pub fn set_lang(&mut self, lang: &str) -> Result<Response, Error> { pub fn set_lang(&mut self, lang: &str) -> Result<Response, Error> {
let put_lang = commands::put_lang(lang.as_bytes().to_vec()); let put_lang = commands::put_lang(lang.as_bytes().to_vec());
apdu::send_command(self.card_client.borrow_mut(), put_lang, false)? apdu::send_command(self.card_client(), put_lang, false)?.try_into()
.try_into()
} }
pub fn set_sex(&mut self, sex: Sex) -> Result<Response, Error> { pub fn set_sex(&mut self, sex: Sex) -> Result<Response, Error> {
let put_sex = commands::put_sex((&sex).into()); let put_sex = commands::put_sex((&sex).into());
apdu::send_command(self.card_client.borrow_mut(), put_sex, false)? apdu::send_command(self.card_client(), put_sex, false)?.try_into()
.try_into()
} }
pub fn set_url(&mut self, url: &str) -> Result<Response, Error> { pub fn set_url(&mut self, url: &str) -> Result<Response, Error> {
let put_url = commands::put_url(url.as_bytes().to_vec()); let put_url = commands::put_url(url.as_bytes().to_vec());
apdu::send_command(&mut self.card_client, put_url, false)?.try_into() apdu::send_command(self.card_client(), put_url, false)?.try_into()
} }
pub fn set_creation_time( pub fn set_creation_time(
@ -617,7 +604,7 @@ impl CardApp {
time_value, time_value,
); );
apdu::send_command(&mut self.card_client, time_cmd, false)?.try_into() apdu::send_command(self.card_client(), time_cmd, false)?.try_into()
} }
pub fn set_fingerprint( pub fn set_fingerprint(
@ -630,7 +617,7 @@ impl CardApp {
fp.as_bytes().to_vec(), fp.as_bytes().to_vec(),
); );
apdu::send_command(self.get_card_client(), fp_cmd, false)?.try_into() apdu::send_command(self.card_client(), fp_cmd, false)?.try_into()
} }
/// Set PW Status Bytes. /// Set PW Status Bytes.
@ -652,7 +639,7 @@ impl CardApp {
let data = pw_status.serialize_for_put(long); let data = pw_status.serialize_for_put(long);
let cmd = commands::put_pw_status(data); let cmd = commands::put_pw_status(data);
apdu::send_command(self.get_card_client(), cmd, false)?.try_into() apdu::send_command(self.card_client(), cmd, false)?.try_into()
} }
/// Set cardholder certificate (for AUT, DEC or SIG). /// Set cardholder certificate (for AUT, DEC or SIG).
@ -664,7 +651,7 @@ impl CardApp {
data: Vec<u8>, data: Vec<u8>,
) -> Result<Response, Error> { ) -> Result<Response, Error> {
let cmd = commands::put_cardholder_certificate(data); let cmd = commands::put_cardholder_certificate(data);
apdu::send_command(&mut self.card_client, cmd, false)?.try_into() apdu::send_command(self.card_client(), cmd, false)?.try_into()
} }
/// Set algorithm attributes /// Set algorithm attributes
@ -680,7 +667,7 @@ impl CardApp {
algo.get_data()?, algo.get_data()?,
); );
apdu::send_command(&mut self.card_client, cmd, false)?.try_into() apdu::send_command(self.card_client(), cmd, false)?.try_into()
} }
/// Set resetting code /// Set resetting code
@ -690,7 +677,7 @@ impl CardApp {
resetting_code: Vec<u8>, resetting_code: Vec<u8>,
) -> Result<Response, Error> { ) -> Result<Response, Error> {
let cmd = commands::put_data(&[0xd3], resetting_code); let cmd = commands::put_data(&[0xd3], resetting_code);
apdu::send_command(&mut self.card_client, cmd, false)?.try_into() apdu::send_command(self.card_client(), cmd, false)?.try_into()
} }
/// Import an existing private key to the card. /// Import an existing private key to the card.

View file

@ -135,9 +135,7 @@ pub(crate) fn generate_asymmetric_key_pair(
let crt = get_crt(key_type)?; let crt = get_crt(key_type)?;
let gen_key_cmd = commands::gen_key(crt.serialize().to_vec()); let gen_key_cmd = commands::gen_key(crt.serialize().to_vec());
let card_client = card_app.get_card_client(); let resp = apdu::send_command(card_app.card_client(), gen_key_cmd, true)?;
let resp = apdu::send_command(card_client, gen_key_cmd, true)?;
resp.check_ok()?; resp.check_ok()?;
let tlv = Tlv::try_from(resp.data()?)?; let tlv = Tlv::try_from(resp.data()?)?;
@ -164,7 +162,7 @@ pub(crate) fn get_pub_key(
let get_pub_key_cmd = commands::get_pub_key(crt.serialize().to_vec()); let get_pub_key_cmd = commands::get_pub_key(crt.serialize().to_vec());
let resp = let resp =
apdu::send_command(card_app.get_card_client(), get_pub_key_cmd, true)?; apdu::send_command(card_app.card_client(), get_pub_key_cmd, true)?;
resp.check_ok()?; resp.check_ok()?;
let tlv = Tlv::try_from(resp.data()?)?; let tlv = Tlv::try_from(resp.data()?)?;
@ -216,8 +214,7 @@ pub(crate) fn key_import(
card_app.set_algorithm_attributes(key_type, &algo)?; card_app.set_algorithm_attributes(key_type, &algo)?;
} }
apdu::send_command(card_app.get_card_client(), key_cmd, false)? apdu::send_command(card_app.card_client(), key_cmd, false)?.check_ok()?;
.check_ok()?;
card_app.set_fingerprint(fp, key_type)?; card_app.set_fingerprint(fp, key_type)?;
card_app.set_creation_time(key.get_ts(), key_type)?; card_app.set_creation_time(key.get_ts(), key_type)?;

View file

@ -25,10 +25,8 @@
//! crate offers a higher level wrapper based on the //! crate offers a higher level wrapper based on the
//! [Sequoia PGP](https://sequoia-pgp.org/) implementation. //! [Sequoia PGP](https://sequoia-pgp.org/) implementation.
use anyhow::Result;
pub mod algorithm; pub mod algorithm;
mod apdu; pub(crate) mod apdu;
mod card_app; mod card_app;
pub mod card_do; pub mod card_do;
pub mod crypto_data; pub mod crypto_data;
@ -40,6 +38,11 @@ pub use crate::apdu::response::Response;
pub use crate::card_app::CardApp; pub use crate::card_app::CardApp;
pub use crate::errors::{Error, SmartcardError, StatusBytes}; pub use crate::errors::{Error, SmartcardError, StatusBytes};
use anyhow::Result;
use std::convert::TryInto;
use crate::apdu::commands;
/// The CardClient trait defines communication with an OpenPGP card via a /// The CardClient trait defines communication with an OpenPGP card via a
/// backend implementation (e.g. the pcsc backend in the crate /// backend implementation (e.g. the pcsc backend in the crate
/// [openpgp-card-pcsc](https://crates.io/crates/openpgp-card-pcsc)). /// [openpgp-card-pcsc](https://crates.io/crates/openpgp-card-pcsc)).
@ -75,6 +78,14 @@ pub trait CardClient {
/// A boxed CardClient (which is Send+Sync). /// A boxed CardClient (which is Send+Sync).
pub type CardClientBox = Box<dyn CardClient + Send + Sync>; pub type CardClientBox = Box<dyn CardClient + Send + Sync>;
impl dyn CardClient {
/// Select the OpenPGP card application
pub fn select(&mut self) -> Result<Response, Error> {
let select_openpgp = commands::select_openpgp();
apdu::send_command(self, select_openpgp, false)?.try_into()
}
}
/// Configuration of the capabilities of the card. /// Configuration of the capabilities of the card.
/// ///
/// This configuration is used to determine e.g. if chaining or extended /// This configuration is used to determine e.g. if chaining or extended

View file

@ -4,9 +4,7 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use pcsc::{Card, Context, Protocols, Scope, ShareMode}; use pcsc::{Card, Context, Protocols, Scope, ShareMode};
use openpgp_card::{ use openpgp_card::{CardApp, CardCaps, CardClient, Error, SmartcardError};
CardApp, CardCaps, CardClient, CardClientBox, Error, SmartcardError,
};
pub struct PcscClient { pub struct PcscClient {
card: Card, card: Card,
@ -21,6 +19,11 @@ impl PcscClient {
} }
} }
/// Get an initialized CardApp from a card
fn into_card_app(self) -> Result<CardApp> {
CardApp::initialize(Box::new(self))
}
/// Opened PCSC Cards without selecting the OpenPGP card application /// Opened PCSC Cards without selecting the OpenPGP card application
fn pcsc_cards() -> Result<Vec<Card>, SmartcardError> { fn pcsc_cards() -> Result<Vec<Card>, SmartcardError> {
let ctx = match Context::establish(Scope::User) { let ctx = match Context::establish(Scope::User) {
@ -84,56 +87,43 @@ impl PcscClient {
/// Return all cards on which the OpenPGP application could be selected. /// Return all cards on which the OpenPGP application could be selected.
/// ///
/// Each card is opened and has the OpenPGP application selected. /// Each card is opened and has the OpenPGP application selected.
pub fn cards() -> Result<Vec<CardClientBox>> { /// Cards are initialized via init_caps().
let cards = Self::unopened_cards()? pub fn cards() -> Result<Vec<CardApp>> {
.into_iter() let mut cards = vec![];
.map(Self::select)
.map(|res| res.ok()) for mut card in Self::unopened_cards()? {
.flatten() if Self::select(&mut card).is_ok() {
.map(|ca| ca.into()) if let Ok(ca) = card.into_card_app() {
.collect(); cards.push(ca);
}
}
}
Ok(cards) Ok(cards)
} }
/// Try to select the OpenPGP application on a card /// Try to select the OpenPGP application on a card
fn select(card_client: PcscClient) -> Result<CardApp, Error> { fn select(card_client: &mut PcscClient) -> Result<(), Error> {
let ccb = Box::new(card_client) as CardClientBox; if <dyn CardClient>::select(card_client).is_ok() {
Ok(())
let mut ca = CardApp::from(ccb);
if ca.select().is_ok() {
Ok(ca)
} else { } else {
Err(Error::Smartcard(SmartcardError::SelectOpenPGPCardFailed)) Err(Error::Smartcard(SmartcardError::SelectOpenPGPCardFailed))
} }
} }
/// Get application related data from the card and check if 'ident'
/// matches
fn match_by_ident(
mut ca: CardApp,
ident: &str,
) -> Result<Option<CardClientBox>, Error> {
let ard = ca.get_application_related_data()?;
let aid = ard.get_application_id()?;
if aid.ident() == ident {
Ok(Some(ca.into()))
} else {
Ok(None)
}
}
/// Returns the OpenPGP card that matches `ident`, if it is available. /// Returns the OpenPGP card that matches `ident`, if it is available.
/// The OpenPGP application of the `CardClientBox` has been selected. /// A fully initialized CardApp is returned: application has been
pub fn open_by_ident(ident: &str) -> Result<CardClientBox, Error> { /// selected, init_caps() has been performed.
for card in Self::unopened_cards()? { pub fn open_by_ident(ident: &str) -> Result<CardApp, Error> {
if let Ok(ca) = Self::select(card) { for mut card in Self::unopened_cards()? {
if let Some(matched_card) = Self::select(&mut card)?;
PcscClient::match_by_ident(ca, ident)? let mut ca = card.into_card_app()?;
{
return Ok(matched_card); let ard = ca.get_application_related_data()?;
} let aid = ard.get_application_id()?;
if aid.ident() == ident {
return Ok(ca);
} }
} }

View file

@ -13,7 +13,7 @@ use sequoia_ipc::gnupg::{Agent, Context};
use std::sync::Mutex; use std::sync::Mutex;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use openpgp_card::Error; use openpgp_card::{CardApp, Error};
use openpgp_card::{CardCaps, CardClient, CardClientBox}; use openpgp_card::{CardCaps, CardClient, CardClientBox};
lazy_static! { lazy_static! {
@ -118,11 +118,11 @@ impl ScdClient {
pub fn open_by_serial( pub fn open_by_serial(
agent: Option<Agent>, agent: Option<Agent>,
serial: &str, serial: &str,
) -> Result<CardClientBox, Error> { ) -> Result<CardApp, Error> {
let mut card = ScdClient::new(agent, true)?; let mut card = ScdClient::new(agent, true)?;
card.select_card(serial)?; card.select_card(serial)?;
Ok(Box::new(card) as CardClientBox) Ok(CardApp::initialize(Box::new(card))?)
} }
/// Ask scdameon to switch to using a specific OpenPGP card, based on /// Ask scdameon to switch to using a specific OpenPGP card, based on

View file

@ -13,7 +13,7 @@ mod cli;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = cli::Cli::from_args(); let cli = cli::Cli::from_args();
let mut card = PcscClient::open_by_ident(&cli.ident)?.into(); let mut card = PcscClient::open_by_ident(&cli.ident)?;
let mut open = Open::open(&mut card)?; let mut open = Open::open(&mut card)?;
match cli.cmd { match cli.cmd {
@ -145,15 +145,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let res = if let Some(rst) = rst { let res = if let Some(rst) = rst {
// reset to new user pin // reset to new user pin
open.reset_user_pin(&rst, &newpin1) open.reset_user_pin(&rst, &newpin1)
} else if let Some(mut admin) = open.admin_card() {
admin.reset_user_pin(&newpin1)
} else { } else {
if let Some(mut admin) = open.admin_card() { return Err(anyhow::anyhow!(
admin.reset_user_pin(&newpin1) "Failed to use card in admin-mode."
} else { )
return Err(anyhow::anyhow!( .into());
"Failed to use card in admin-mode."
)
.into());
}
}; };
if res.is_err() { if res.is_err() {

View file

@ -71,7 +71,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
admin_pin, admin_pin,
cmd, cmd,
} => { } => {
let mut card = util::open_card(&ident)?.into(); let mut card = util::open_card(&ident)?;
let mut open = Open::open(&mut card)?; let mut open = Open::open(&mut card)?;
match cmd { match cmd {
@ -137,8 +137,7 @@ fn list_cards() -> Result<()> {
if !cards.is_empty() { if !cards.is_empty() {
println!("Available OpenPGP cards:"); println!("Available OpenPGP cards:");
for card in cards { for mut card in cards {
let mut card = card.into();
let open = Open::open(&mut card)?; let open = Open::open(&mut card)?;
println!(" {}", open.application_identifier()?.ident()); println!(" {}", open.application_identifier()?.ident());
} }
@ -149,18 +148,17 @@ fn list_cards() -> Result<()> {
} }
fn print_status(ident: Option<String>, verbose: bool) -> Result<()> { fn print_status(ident: Option<String>, verbose: bool) -> Result<()> {
let ccb = if let Some(ident) = ident { let mut ca = if let Some(ident) = ident {
util::open_card(&ident)? util::open_card(&ident)?
} else { } else {
let mut cards = util::cards()?; let mut cards = util::cards()?;
if cards.len() == 1 { if cards.len() == 1 {
cards.pop().unwrap() cards.pop().unwrap()
} else { } else {
return Err(anyhow::anyhow!("Found {} cards", cards.len()).into()); return Err(anyhow::anyhow!("Found {} cards", cards.len()));
} }
}; };
let mut card = ccb.into(); let mut open = Open::open(&mut ca)?;
let mut open = Open::open(&mut card)?;
print!("OpenPGP card {}", open.application_identifier()?.ident()); print!("OpenPGP card {}", open.application_identifier()?.ident());
@ -291,10 +289,10 @@ fn decrypt(
let input = util::open_or_stdin(input.as_deref())?; let input = util::open_or_stdin(input.as_deref())?;
let mut card = util::open_card(&ident)?.into(); let mut card = util::open_card(ident)?;
let mut open = Open::open(&mut card)?; let mut open = Open::open(&mut card)?;
let mut user = util::get_user(&mut open, &pin_file)?; let mut user = util::get_user(&mut open, pin_file)?;
let d = user.decryptor(&cert, &p)?; let d = user.decryptor(&cert, &p)?;
let db = DecryptorBuilder::from_reader(input)?; let db = DecryptorBuilder::from_reader(input)?;
@ -316,10 +314,10 @@ fn sign_detached(
let mut input = util::open_or_stdin(input.as_deref())?; let mut input = util::open_or_stdin(input.as_deref())?;
let mut card = util::open_card(&ident)?.into(); let mut card = util::open_card(ident)?;
let mut open = Open::open(&mut card)?; let mut open = Open::open(&mut card)?;
let mut sign = util::get_sign(&mut open, &pin_file)?; let mut sign = util::get_sign(&mut open, pin_file)?;
let s = sign.signer(&cert, &p)?; let s = sign.signer(&cert, &p)?;
let message = Armorer::new(Message::new(std::io::stdout())).build()?; let message = Armorer::new(Message::new(std::io::stdout())).build()?;
@ -333,7 +331,7 @@ fn sign_detached(
fn factory_reset(ident: &str) -> Result<()> { fn factory_reset(ident: &str) -> Result<()> {
println!("Resetting Card {}", ident); println!("Resetting Card {}", ident);
let mut card = util::open_card(ident)?.into(); let mut card = util::open_card(ident)?;
Open::open(&mut card)?.factory_reset() Open::open(&mut card)?.factory_reset()
} }
@ -341,16 +339,16 @@ fn key_import_yolo(mut admin: Admin, key: &Cert) -> Result<()> {
let p = StandardPolicy::new(); let p = StandardPolicy::new();
let sig = let sig =
openpgp_card_sequoia::sq_util::get_subkey(&key, &p, KeyType::Signing)?; openpgp_card_sequoia::sq_util::get_subkey(key, &p, KeyType::Signing)?;
let dec = openpgp_card_sequoia::sq_util::get_subkey( let dec = openpgp_card_sequoia::sq_util::get_subkey(
&key, key,
&p, &p,
KeyType::Decryption, KeyType::Decryption,
)?; )?;
let auth = openpgp_card_sequoia::sq_util::get_subkey( let auth = openpgp_card_sequoia::sq_util::get_subkey(
&key, key,
&p, &p,
KeyType::Authentication, KeyType::Authentication,
)?; )?;
@ -382,7 +380,7 @@ fn key_import_explicit(
if let Some(sig_fp) = sig_fp { if let Some(sig_fp) = sig_fp {
if let Some(sig) = if let Some(sig) =
sq_util::get_subkey_by_fingerprint(&key, &p, &sig_fp)? sq_util::get_subkey_by_fingerprint(key, &p, &sig_fp)?
{ {
println!("Uploading {} as signing key", sig.fingerprint()); println!("Uploading {} as signing key", sig.fingerprint());
admin.upload_key(sig, KeyType::Signing, None)?; admin.upload_key(sig, KeyType::Signing, None)?;
@ -393,7 +391,7 @@ fn key_import_explicit(
if let Some(dec_fp) = dec_fp { if let Some(dec_fp) = dec_fp {
if let Some(dec) = if let Some(dec) =
sq_util::get_subkey_by_fingerprint(&key, &p, &dec_fp)? sq_util::get_subkey_by_fingerprint(key, &p, &dec_fp)?
{ {
println!("Uploading {} as decryption key", dec.fingerprint()); println!("Uploading {} as decryption key", dec.fingerprint());
admin.upload_key(dec, KeyType::Decryption, None)?; admin.upload_key(dec, KeyType::Decryption, None)?;
@ -404,7 +402,7 @@ fn key_import_explicit(
if let Some(auth_fp) = auth_fp { if let Some(auth_fp) = auth_fp {
if let Some(auth) = if let Some(auth) =
sq_util::get_subkey_by_fingerprint(&key, &p, &auth_fp)? sq_util::get_subkey_by_fingerprint(key, &p, &auth_fp)?
{ {
println!("Uploading {} as authentication key", auth.fingerprint()); println!("Uploading {} as authentication key", auth.fingerprint());
admin.upload_key(auth, KeyType::Authentication, None)?; admin.upload_key(auth, KeyType::Authentication, None)?;
@ -478,7 +476,7 @@ fn generate_keys(
// Write armored certificate to the output file (or stdout) // Write armored certificate to the output file (or stdout)
let mut output = util::open_or_stdout(output.as_deref())?; let mut output = util::open_or_stdout(output.as_deref())?;
output.write(armored.as_bytes())?; output.write_all(armored.as_bytes())?;
Ok(()) Ok(())
} }
@ -519,7 +517,7 @@ fn gen_subkeys(
// If none of them work, fail and return all errors // If none of them work, fail and return all errors
for (n, alg) in algos.iter().enumerate() { for (n, alg) in algos.iter().enumerate() {
let a = Some(alg.clone()); let a = Some(*alg);
log::info!(" Trying key generation with algo {:?}", alg); log::info!(" Trying key generation with algo {:?}", alg);

View file

@ -3,16 +3,16 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use openpgp_card::{CardClientBox, Error}; use openpgp_card::{CardApp, Error};
use openpgp_card_pcsc::PcscClient; use openpgp_card_pcsc::PcscClient;
use openpgp_card_sequoia::card::{Admin, Open, Sign, User}; use openpgp_card_sequoia::card::{Admin, Open, Sign, User};
use std::path::Path; use std::path::Path;
pub(crate) fn cards() -> Result<Vec<CardClientBox>> { pub(crate) fn cards() -> Result<Vec<CardApp>> {
PcscClient::cards() PcscClient::cards()
} }
pub(crate) fn open_card(ident: &str) -> Result<CardClientBox, Error> { pub(crate) fn open_card(ident: &str) -> Result<CardApp, Error> {
PcscClient::open_by_ident(ident) PcscClient::open_by_ident(ident)
} }