Rename: PcscClient->PcscCard,PcscTxClient->TxClient
This commit is contained in:
parent
431da53b28
commit
30341d6c4b
10 changed files with 318 additions and 336 deletions
|
@ -8,7 +8,7 @@ use anyhow::Result;
|
||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use openpgp_card_pcsc::PcscClient;
|
use openpgp_card_pcsc::PcscCard;
|
||||||
use openpgp_card_scdc::ScdClient;
|
use openpgp_card_scdc::ScdClient;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -30,14 +30,14 @@ pub struct Config {
|
||||||
|
|
||||||
/// An "opened" card, via one particular backend, with test-metadata
|
/// An "opened" card, via one particular backend, with test-metadata
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TestCardApp {
|
pub struct TestCardData {
|
||||||
name: String,
|
name: String,
|
||||||
tc: TestCard,
|
tc: TestCard,
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestCardApp {
|
impl TestCardData {
|
||||||
pub(crate) fn get_card_client(&self) -> Result<Box<PcscClient>> {
|
pub(crate) fn get_card(&self) -> Result<Box<PcscCard>> {
|
||||||
self.tc.open()
|
self.tc.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ impl TestConfig {
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_cardapps(self) -> Vec<TestCardApp> {
|
pub fn into_cardapps(self) -> Vec<TestCardData> {
|
||||||
let mut cards = vec![];
|
let mut cards = vec![];
|
||||||
|
|
||||||
for (name, card) in self.card {
|
for (name, card) in self.card {
|
||||||
|
@ -69,7 +69,7 @@ impl TestConfig {
|
||||||
_ => panic!("unexpected backend {}", backend),
|
_ => panic!("unexpected backend {}", backend),
|
||||||
};
|
};
|
||||||
|
|
||||||
cards.push(TestCardApp {
|
cards.push(TestCardData {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
tc,
|
tc,
|
||||||
config: card.config.clone(),
|
config: card.config.clone(),
|
||||||
|
@ -88,7 +88,7 @@ pub enum TestCard {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestCard {
|
impl TestCard {
|
||||||
pub fn open(&self) -> Result<Box<PcscClient>> {
|
pub fn open(&self) -> Result<Box<PcscCard>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Pcsc(ident) => {
|
Self::Pcsc(ident) => {
|
||||||
// Attempt to shutdown SCD, if it is running.
|
// Attempt to shutdown SCD, if it is running.
|
||||||
|
@ -96,7 +96,7 @@ 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);
|
||||||
|
|
||||||
Ok(Box::new(PcscClient::open_by_ident(ident)?))
|
Ok(Box::new(PcscCard::open_by_ident(ident)?))
|
||||||
}
|
}
|
||||||
Self::Scdc(serial) => {
|
Self::Scdc(serial) => {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
|
|
|
@ -4,18 +4,19 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use pcsc::Transaction;
|
use pcsc::Transaction;
|
||||||
|
|
||||||
use openpgp_card_pcsc::{PcscClient, PcscTxClient};
|
use openpgp_card_pcsc::{PcscCard, TxClient};
|
||||||
use openpgp_card_sequoia::card::Open;
|
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 mut card in PcscClient::cards()? {
|
for mut card in PcscCard::cards()? {
|
||||||
let cc = card.card_caps();
|
let cc = card.card_caps();
|
||||||
|
let rc = card.reader_caps();
|
||||||
|
|
||||||
let mut tx: Transaction =
|
let mut tx: Transaction =
|
||||||
openpgp_card_pcsc::start_tx!(card.card(), true)?;
|
openpgp_card_pcsc::start_tx!(card.card(), true)?;
|
||||||
let mut txc = PcscTxClient::new(&mut tx, cc);
|
let mut txc = TxClient::new(&mut tx, cc, rc);
|
||||||
|
|
||||||
let open = Open::new(&mut txc)?;
|
let open = Open::new(&mut txc)?;
|
||||||
println!(" {}", open.application_identifier()?.ident());
|
println!(" {}", open.application_identifier()?.ident());
|
||||||
|
|
|
@ -15,14 +15,14 @@ use sequoia_openpgp::Cert;
|
||||||
use openpgp_card;
|
use openpgp_card;
|
||||||
use openpgp_card::algorithm::AlgoSimple;
|
use openpgp_card::algorithm::AlgoSimple;
|
||||||
use openpgp_card::card_do::{KeyGenerationTime, Sex};
|
use openpgp_card::card_do::{KeyGenerationTime, Sex};
|
||||||
use openpgp_card::{CardApp, CardClient, Error, KeyType, StatusBytes};
|
use openpgp_card::{CardClient, Error, KeyType, StatusBytes};
|
||||||
use openpgp_card_pcsc::PcscTxClient;
|
use openpgp_card_pcsc::TxClient;
|
||||||
use openpgp_card_sequoia::card::Open;
|
use openpgp_card_sequoia::card::Open;
|
||||||
use openpgp_card_sequoia::util::{
|
use openpgp_card_sequoia::util::{
|
||||||
make_cert, public_key_material_to_key, public_to_fingerprint,
|
make_cert, public_key_material_to_key, public_to_fingerprint,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::cards::TestCardApp;
|
use crate::cards::TestCardData;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -671,25 +671,25 @@ pub fn test_reset_retry_counter(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_test(
|
pub fn run_test(
|
||||||
card: &mut TestCardApp,
|
card: &mut TestCardData,
|
||||||
t: fn(
|
t: fn(
|
||||||
&mut (dyn CardClient + Send + Sync),
|
&mut (dyn CardClient + Send + Sync),
|
||||||
&[&str],
|
&[&str],
|
||||||
) -> Result<TestOutput, TestError>,
|
) -> Result<TestOutput, TestError>,
|
||||||
param: &[&str],
|
param: &[&str],
|
||||||
) -> Result<TestOutput, TestError> {
|
) -> Result<TestOutput, TestError> {
|
||||||
let mut card_client = card.get_card_client()?;
|
let mut card_client = card.get_card()?;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use openpgp_card::SmartcardError;
|
|
||||||
use pcsc::Transaction;
|
use pcsc::Transaction;
|
||||||
|
|
||||||
let card_caps = card_client.card_caps();
|
let card_caps = card_client.card_caps();
|
||||||
|
let reader_caps = card_client.reader_caps();
|
||||||
|
|
||||||
let mut tx: Transaction =
|
let mut tx: Transaction =
|
||||||
openpgp_card_pcsc::start_tx!(card_client.card(), true)
|
openpgp_card_pcsc::start_tx!(card_client.card(), true)
|
||||||
.map_err(|e| anyhow!(e))?;
|
.map_err(|e| anyhow!(e))?;
|
||||||
let mut txc = PcscTxClient::new(&mut tx, card_caps);
|
let mut txc = TxClient::new(&mut tx, card_caps, reader_caps);
|
||||||
|
|
||||||
t(&mut txc, param)
|
t(&mut txc, param)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz <wiktor@metacode.biz>
|
// SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz <wiktor@metacode.biz>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use openpgp_card_pcsc::PcscClient;
|
use openpgp_card_pcsc::PcscCard;
|
||||||
|
|
||||||
use openpgp_card_sequoia::card::Open;
|
use openpgp_card_sequoia::card::Open;
|
||||||
|
|
||||||
|
@ -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)?;
|
let mut ca = PcscCard::open_by_ident(card_ident)?;
|
||||||
let mut open = Open::new(&mut ca)?;
|
let mut open = Open::new(&mut ca)?;
|
||||||
|
|
||||||
let pin = std::fs::read_to_string(pin_file)?;
|
let pin = std::fs::read_to_string(pin_file)?;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz <wiktor@metacode.biz>
|
// SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz <wiktor@metacode.biz>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use openpgp_card_pcsc::PcscClient;
|
use openpgp_card_pcsc::PcscCard;
|
||||||
|
|
||||||
use openpgp_card_sequoia::card::Open;
|
use openpgp_card_sequoia::card::Open;
|
||||||
|
|
||||||
|
@ -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)?;
|
let mut ca = PcscCard::open_by_ident(card_ident)?;
|
||||||
let mut open = Open::new(&mut ca)?;
|
let mut open = Open::new(&mut ca)?;
|
||||||
|
|
||||||
let pin = std::fs::read_to_string(pin_file)?;
|
let pin = std::fs::read_to_string(pin_file)?;
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
//! With `openpgp-card-pcsc` you can either open all available cards:
|
//! With `openpgp-card-pcsc` you can either open all available cards:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use openpgp_card_pcsc::PcscClient;
|
//! use openpgp_card_pcsc::PcscCard;
|
||||||
//! use openpgp_card_sequoia::card::Open;
|
//! use openpgp_card_sequoia::card::Open;
|
||||||
//!
|
//!
|
||||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//! for mut cc in PcscClient::cards()? {
|
//! for mut cc in PcscCard::cards()? {
|
||||||
//! let open = Open::new(&mut cc)?;
|
//! let open = Open::new(&mut cc)?;
|
||||||
//! println!("Found OpenPGP card with ident '{}'",
|
//! println!("Found OpenPGP card with ident '{}'",
|
||||||
//! open.application_identifier()?.ident());
|
//! open.application_identifier()?.ident());
|
||||||
|
@ -28,11 +28,11 @@
|
||||||
//! Or you can open one particular card, by ident:
|
//! Or you can open one particular card, by ident:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use openpgp_card_pcsc::PcscClient;
|
//! use openpgp_card_pcsc::PcscCard;
|
||||||
//! use openpgp_card_sequoia::card::Open;
|
//! use openpgp_card_sequoia::card::Open;
|
||||||
//!
|
//!
|
||||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//! let mut cc = PcscClient::open_by_ident("abcd:12345678")?;
|
//! let mut cc = PcscCard::open_by_ident("abcd:12345678")?;
|
||||||
//! let mut open = Open::new(&mut cc)?;
|
//! let mut open = Open::new(&mut cc)?;
|
||||||
//! # Ok(())
|
//! # Ok(())
|
||||||
//! # }
|
//! # }
|
||||||
|
@ -48,13 +48,13 @@
|
||||||
//! that corresponds to the private encryption key on the card:
|
//! that corresponds to the private encryption key on the card:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use openpgp_card_pcsc::PcscClient;
|
//! use openpgp_card_pcsc::PcscCard;
|
||||||
//! use openpgp_card_sequoia::card::Open;
|
//! use openpgp_card_sequoia::card::Open;
|
||||||
//!
|
//!
|
||||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//! // Open card via PCSC
|
//! // Open card via PCSC
|
||||||
//! use sequoia_openpgp::policy::StandardPolicy;
|
//! use sequoia_openpgp::policy::StandardPolicy;
|
||||||
//! let mut cc = PcscClient::open_by_ident("abcd:12345678")?;
|
//! let mut cc = PcscCard::open_by_ident("abcd:12345678")?;
|
||||||
//! let mut open = Open::new(&mut cc)?;
|
//! let mut open = Open::new(&mut cc)?;
|
||||||
//!
|
//!
|
||||||
//! // Get authorization for user access to the card with password
|
//! // Get authorization for user access to the card with password
|
||||||
|
@ -89,13 +89,13 @@
|
||||||
//! user password before each signing operation!)
|
//! user password before each signing operation!)
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use openpgp_card_pcsc::PcscClient;
|
//! use openpgp_card_pcsc::PcscCard;
|
||||||
//! use openpgp_card_sequoia::card::Open;
|
//! use openpgp_card_sequoia::card::Open;
|
||||||
//!
|
//!
|
||||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//! // Open card via PCSC
|
//! // Open card via PCSC
|
||||||
//! use sequoia_openpgp::policy::StandardPolicy;
|
//! use sequoia_openpgp::policy::StandardPolicy;
|
||||||
//! let mut cc = PcscClient::open_by_ident("abcd:12345678")?;
|
//! let mut cc = PcscCard::open_by_ident("abcd:12345678")?;
|
||||||
//! let mut open = Open::new(&mut cc)?;
|
//! let mut open = Open::new(&mut cc)?;
|
||||||
//!
|
//!
|
||||||
//! // Get authorization for signing access to the card with password
|
//! // Get authorization for signing access to the card with password
|
||||||
|
@ -120,12 +120,12 @@
|
||||||
//! # Setting up and configuring a card
|
//! # Setting up and configuring a card
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use openpgp_card_pcsc::PcscClient;
|
//! use openpgp_card_pcsc::PcscCard;
|
||||||
//! use openpgp_card_sequoia::card::Open;
|
//! use openpgp_card_sequoia::card::Open;
|
||||||
//!
|
//!
|
||||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//! // Open card via PCSC
|
//! // Open card via PCSC
|
||||||
//! let mut cc = PcscClient::open_by_ident("abcd:12345678")?;
|
//! let mut cc = PcscCard::open_by_ident("abcd:12345678")?;
|
||||||
//! let mut open = Open::new(&mut cc)?;
|
//! let mut open = Open::new(&mut cc)?;
|
||||||
//!
|
//!
|
||||||
//! // Get authorization for admin access to the card with password
|
//! // Get authorization for admin access to the card with password
|
||||||
|
|
|
@ -11,7 +11,7 @@ use sequoia_openpgp::Cert;
|
||||||
|
|
||||||
use openpgp_card::card_do::Sex;
|
use openpgp_card::card_do::Sex;
|
||||||
use openpgp_card::KeyType;
|
use openpgp_card::KeyType;
|
||||||
use openpgp_card_pcsc::PcscClient;
|
use openpgp_card_pcsc::PcscCard;
|
||||||
|
|
||||||
use openpgp_card_sequoia::card::Open;
|
use openpgp_card_sequoia::card::Open;
|
||||||
use openpgp_card_sequoia::sq_util;
|
use openpgp_card_sequoia::sq_util;
|
||||||
|
@ -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)?;
|
let mut card = PcscCard::open_by_ident(&test_card_ident)?;
|
||||||
let mut open = Open::new(&mut card)?;
|
let mut open = Open::new(&mut card)?;
|
||||||
|
|
||||||
// card metadata
|
// card metadata
|
||||||
|
@ -144,7 +144,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)?;
|
let mut card = PcscCard::open_by_ident(&test_card_ident)?;
|
||||||
let mut open = Open::new(&mut card)?;
|
let mut open = Open::new(&mut card)?;
|
||||||
|
|
||||||
// Check that we're still using the expected card
|
// Check that we're still using the expected card
|
||||||
|
@ -183,7 +183,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)?;
|
let mut card = PcscCard::open_by_ident(&test_card_ident)?;
|
||||||
let mut open = Open::new(&mut card)?;
|
let mut open = Open::new(&mut card)?;
|
||||||
|
|
||||||
// Sign
|
// Sign
|
||||||
|
@ -213,7 +213,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 mut card in PcscClient::cards()? {
|
for mut card in PcscCard::cards()? {
|
||||||
let open = Open::new(&mut card)?;
|
let open = Open::new(&mut card)?;
|
||||||
println!(" {}", open.application_identifier()?.ident());
|
println!(" {}", open.application_identifier()?.ident());
|
||||||
}
|
}
|
||||||
|
|
565
pcsc/src/lib.rs
565
pcsc/src/lib.rs
|
@ -1,9 +1,8 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
//! This crate implements the `PcscClient` backend for the `openpgp-card`
|
//! This crate implements a `CardClient` backend for `openpgp-card`. It uses
|
||||||
//! crate, which uses the PCSC lite middleware to access the OpenPGP
|
//! the PCSC middleware to access the OpenPGP application on smart cards.
|
||||||
//! application on smart cards.
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use iso7816_tlv::simple::Tlv;
|
use iso7816_tlv::simple::Tlv;
|
||||||
|
@ -17,12 +16,14 @@ use openpgp_card::{CardCaps, CardClient, Error, SmartcardError};
|
||||||
const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06;
|
const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06;
|
||||||
const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07;
|
const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07;
|
||||||
|
|
||||||
|
/// Get a TxClient from a Card
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! start_tx {
|
macro_rules! start_tx {
|
||||||
($card:expr, $reselect:expr) => {{
|
($card:expr, $reselect:expr) => {{
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use openpgp_card::{Error, SmartcardError};
|
use openpgp_card::{Error, SmartcardError};
|
||||||
use pcsc::{Disposition, Protocols, ShareMode, Transaction};
|
use pcsc::{Disposition, Protocols, ShareMode, Transaction};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
let mut was_reset = false;
|
let mut was_reset = false;
|
||||||
|
|
||||||
|
@ -38,9 +39,17 @@ macro_rules! start_tx {
|
||||||
"start_tx: card was reset, select() openpgp"
|
"start_tx: card was reset, select() openpgp"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut txc = PcscTxClient::new(&mut tx, None);
|
let mut txc =
|
||||||
|
TxClient::new(&mut tx, None, HashMap::default());
|
||||||
|
|
||||||
|
// In contexts where the caller of this macro
|
||||||
|
// expects that the card has already been opened,
|
||||||
|
// re-open the card here.
|
||||||
|
// For initial card-opening, we don't do this, then
|
||||||
|
// the caller always expects a card that has not
|
||||||
|
// been "select"ed yet.
|
||||||
if $reselect {
|
if $reselect {
|
||||||
PcscTxClient::select(&mut txc)?;
|
TxClient::select(&mut txc)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,29 +92,49 @@ macro_rules! start_tx {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An implementation of the CardClient trait that uses the PCSC lite
|
/// An opened PCSC Card (without open transaction).
|
||||||
/// middleware to access the OpenPGP card application on smart cards.
|
///
|
||||||
pub struct PcscClient {
|
/// This struct can be used to hold on to a Card, even while no operations
|
||||||
|
/// are performed on the Card. To perform operations on the card, a
|
||||||
|
/// `TxClient` object needs to be obtained.
|
||||||
|
pub struct PcscCard {
|
||||||
card: Card,
|
card: Card,
|
||||||
card_caps: Option<CardCaps>,
|
card_caps: Option<CardCaps>,
|
||||||
reader_caps: HashMap<u8, Tlv>,
|
reader_caps: HashMap<u8, Tlv>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PcscTxClient<'a, 'b> {
|
/// An implementation of the CardClient trait that uses the PCSC lite
|
||||||
|
/// middleware to access the OpenPGP card application on smart cards, via a
|
||||||
|
/// PCSC "transaction".
|
||||||
|
///
|
||||||
|
/// This struct is created from a PcscRaw card by opening a transaction,
|
||||||
|
/// using the `start_tx!` macro.
|
||||||
|
///
|
||||||
|
/// Transactions on a card cannot be opened and left idle
|
||||||
|
/// (e.g. Microsoft documents that on Windows, they will be closed after
|
||||||
|
/// 5s without a command:
|
||||||
|
/// <https://docs.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardbegintransaction?redirectedfrom=MSDN#remarks>)
|
||||||
|
pub struct TxClient<'a, 'b> {
|
||||||
tx: &'a mut Transaction<'b>,
|
tx: &'a mut Transaction<'b>,
|
||||||
card_caps: Option<CardCaps>,
|
card_caps: Option<CardCaps>, // FIXME: manual copy from PcscCard
|
||||||
|
reader_caps: HashMap<u8, Tlv>, // FIXME: manual copy from PcscCard
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> PcscTxClient<'a, 'b> {
|
impl<'a, 'b> TxClient<'a, 'b> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tx: &'a mut Transaction<'b>,
|
tx: &'a mut Transaction<'b>,
|
||||||
card_caps: Option<CardCaps>,
|
card_caps: Option<CardCaps>,
|
||||||
|
reader_caps: HashMap<u8, Tlv>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
PcscTxClient { tx, card_caps }
|
TxClient {
|
||||||
|
tx,
|
||||||
|
card_caps,
|
||||||
|
reader_caps,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to select the OpenPGP application on a card
|
/// Try to select the OpenPGP application on a card
|
||||||
pub fn select(card_client: &'a mut PcscTxClient) -> Result<(), Error> {
|
pub fn select(card_client: &'a mut TxClient) -> Result<(), Error> {
|
||||||
if <dyn CardClient>::select(card_client).is_ok() {
|
if <dyn CardClient>::select(card_client).is_ok() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -115,7 +144,7 @@ impl<'a, 'b> PcscTxClient<'a, 'b> {
|
||||||
|
|
||||||
/// Get application_related_data from card
|
/// Get application_related_data from card
|
||||||
fn application_related_data(
|
fn application_related_data(
|
||||||
card_client: &mut PcscTxClient,
|
card_client: &mut TxClient,
|
||||||
) -> Result<ApplicationRelatedData, Error> {
|
) -> Result<ApplicationRelatedData, Error> {
|
||||||
<dyn CardClient>::application_related_data(card_client).map_err(|e| {
|
<dyn CardClient>::application_related_data(card_client).map_err(|e| {
|
||||||
Error::Smartcard(SmartcardError::Error(format!(
|
Error::Smartcard(SmartcardError::Error(format!(
|
||||||
|
@ -143,9 +172,30 @@ impl<'a, 'b> PcscTxClient<'a, 'b> {
|
||||||
|
|
||||||
Ok(Tlv::parse_all(res))
|
Ok(Tlv::parse_all(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the minimum pin length for pin_id.
|
||||||
|
fn min_pin_len(&self, pin_id: u8) -> Result<u8> {
|
||||||
|
match pin_id {
|
||||||
|
0x81 | 0x82 => Ok(6),
|
||||||
|
0x83 => Ok(8),
|
||||||
|
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get the maximum pin length for pin_id.
|
||||||
|
fn max_pin_len(&self, pin_id: u8) -> Result<u8> {
|
||||||
|
if let Some(card_caps) = self.card_caps {
|
||||||
|
match pin_id {
|
||||||
|
0x81 | 0x82 => Ok(card_caps.pw1_max_len()),
|
||||||
|
0x83 => Ok(card_caps.pw3_max_len()),
|
||||||
|
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("card_caps is None"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardClient for PcscTxClient<'_, '_> {
|
impl CardClient for TxClient<'_, '_> {
|
||||||
fn transmit(
|
fn transmit(
|
||||||
&mut self,
|
&mut self,
|
||||||
cmd: &[u8],
|
cmd: &[u8],
|
||||||
|
@ -180,23 +230,207 @@ impl CardClient for PcscTxClient<'_, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feature_pinpad_verify(&self) -> bool {
|
fn feature_pinpad_verify(&self) -> bool {
|
||||||
todo!()
|
self.reader_caps.contains_key(&FEATURE_VERIFY_PIN_DIRECT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feature_pinpad_modify(&self) -> bool {
|
fn feature_pinpad_modify(&self) -> bool {
|
||||||
todo!()
|
self.reader_caps.contains_key(&FEATURE_MODIFY_PIN_DIRECT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pinpad_verify(&mut self, _id: u8) -> Result<Vec<u8>> {
|
fn pinpad_verify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
|
||||||
todo!()
|
let pin_min_size = self.min_pin_len(pin_id)?;
|
||||||
|
let pin_max_size = self.max_pin_len(pin_id)?;
|
||||||
|
|
||||||
|
// Default to varlen, for now.
|
||||||
|
// (NOTE: Some readers don't support varlen, and need explicit length
|
||||||
|
// information. Also see https://wiki.gnupg.org/CardReader/PinpadInput)
|
||||||
|
let fixedlen: u8 = 0;
|
||||||
|
|
||||||
|
// APDU: 00 20 00 pin_id <len> (ff)*
|
||||||
|
let mut ab_data = vec![
|
||||||
|
0x00, /* CLA */
|
||||||
|
0x20, /* INS: VERIFY */
|
||||||
|
0x00, /* P1 */
|
||||||
|
pin_id, /* P2 */
|
||||||
|
fixedlen, /* Lc: 'fixedlen' data bytes */
|
||||||
|
];
|
||||||
|
ab_data.extend([0xff].repeat(fixedlen as usize));
|
||||||
|
|
||||||
|
// PC/SC v2.02.05 Part 10 PIN verification data structure
|
||||||
|
let mut send: Vec<u8> = vec![
|
||||||
|
// 0 bTimeOut BYTE timeout in seconds (00 means use default
|
||||||
|
// timeout)
|
||||||
|
0x00,
|
||||||
|
// 1 bTimeOut2 BYTE timeout in seconds after first key stroke
|
||||||
|
0x00,
|
||||||
|
// 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx
|
||||||
|
0x82,
|
||||||
|
// 3 bmPINBlockString BYTE
|
||||||
|
// bits 7-4 bit size of PIN length in APDU
|
||||||
|
// bits 3-0 PIN block size in bytes after justification and formatting
|
||||||
|
fixedlen,
|
||||||
|
// 4 bmPINLengthFormat BYTE
|
||||||
|
// bits 7-5 RFU, bit 4 set if system units are bytes clear if
|
||||||
|
// system units are bits,
|
||||||
|
// bits 3-0 PIN length position in system units
|
||||||
|
0x00,
|
||||||
|
// 5 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size
|
||||||
|
// in digits, YY is maximum
|
||||||
|
pin_max_size,
|
||||||
|
pin_min_size,
|
||||||
|
// 7 bEntryValidationCondition BYTE Conditions under which PIN
|
||||||
|
// entry should be considered complete.
|
||||||
|
//
|
||||||
|
// table for bEntryValidationCondition:
|
||||||
|
// 0x01: Max size reached
|
||||||
|
// 0x02: Validation key pressed
|
||||||
|
// 0x04: Timeout occurred
|
||||||
|
0x07,
|
||||||
|
// 8 bNumberMessage BYTE Number of messages to display for PIN
|
||||||
|
// verification
|
||||||
|
0x01,
|
||||||
|
// 9 wLangIdU SHORT Language for messages
|
||||||
|
0x04,
|
||||||
|
0x09, // US english
|
||||||
|
// 11 bMsgIndex BYTE Message index (should be 00)
|
||||||
|
0x00,
|
||||||
|
// 12 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 15 ulDataLength ULONG length of Data to be sent to the ICC
|
||||||
|
send.extend(&(ab_data.len() as u32).to_le_bytes());
|
||||||
|
|
||||||
|
// 19 abData BYTE[] Data to send to the ICC
|
||||||
|
send.extend(ab_data);
|
||||||
|
|
||||||
|
log::debug!("pcsc pinpad_verify send: {:x?}", send);
|
||||||
|
|
||||||
|
let mut recv = vec![0xAA; 256];
|
||||||
|
|
||||||
|
let verify_ioctl: [u8; 4] = self
|
||||||
|
.reader_caps
|
||||||
|
.get(&FEATURE_VERIFY_PIN_DIRECT)
|
||||||
|
.ok_or_else(|| anyhow!("no reader_capability"))?
|
||||||
|
.value()
|
||||||
|
.try_into()?;
|
||||||
|
|
||||||
|
let res = self.tx.control(
|
||||||
|
u32::from_be_bytes(verify_ioctl).into(),
|
||||||
|
&send,
|
||||||
|
&mut recv,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
log::debug!(" <- pcsc pinpad_verify result: {:x?}", res);
|
||||||
|
|
||||||
|
Ok(res.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pinpad_modify(&mut self, _id: u8) -> Result<Vec<u8>> {
|
fn pinpad_modify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
|
||||||
todo!()
|
let pin_min_size = self.min_pin_len(pin_id)?;
|
||||||
|
let pin_max_size = self.max_pin_len(pin_id)?;
|
||||||
|
|
||||||
|
// Default to varlen, for now.
|
||||||
|
// (NOTE: Some readers don't support varlen, and need explicit length
|
||||||
|
// information. Also see https://wiki.gnupg.org/CardReader/PinpadInput)
|
||||||
|
let fixedlen: u8 = 0;
|
||||||
|
|
||||||
|
// APDU: 00 24 00 pin_id <len> [(ff)* x2]
|
||||||
|
let mut ab_data = vec![
|
||||||
|
0x00, /* CLA */
|
||||||
|
0x24, /* INS: CHANGE_REFERENCE_DATA */
|
||||||
|
0x00, /* P1 */
|
||||||
|
pin_id, /* P2 */
|
||||||
|
fixedlen * 2, /* Lc: 'fixedlen' data bytes */
|
||||||
|
];
|
||||||
|
ab_data.extend([0xff].repeat(fixedlen as usize * 2));
|
||||||
|
|
||||||
|
// PC/SC v2.02.05 Part 10 PIN modification data structure
|
||||||
|
let mut send: Vec<u8> = vec![
|
||||||
|
// 0 bTimeOut BYTE timeout in seconds (00 means use default
|
||||||
|
// timeout)
|
||||||
|
0x00,
|
||||||
|
// 1 bTimeOut2 BYTE timeout in seconds after first key stroke
|
||||||
|
0x00,
|
||||||
|
// 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx
|
||||||
|
0x82,
|
||||||
|
// 3 bmPINBlockString BYTE
|
||||||
|
// bits 7-4 bit size of PIN length in APDU
|
||||||
|
// bits 3-0 PIN block size in bytes after justification and formatting
|
||||||
|
fixedlen,
|
||||||
|
// 4 bmPINLengthFormat BYTE
|
||||||
|
// bits 7-5 RFU, bit 4 set if system units are bytes clear if
|
||||||
|
// system units are bits,
|
||||||
|
// bits 3-0 PIN length position in system units
|
||||||
|
0x00,
|
||||||
|
// 5 bInsertionOffsetOld BYTE Insertion position offset in bytes for
|
||||||
|
// the current PIN
|
||||||
|
0x00,
|
||||||
|
// 6 bInsertionOffsetNew BYTE Insertion position offset in bytes for
|
||||||
|
// the new PIN
|
||||||
|
fixedlen,
|
||||||
|
// 7 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size
|
||||||
|
// in digits, YY is maximum
|
||||||
|
pin_max_size,
|
||||||
|
pin_min_size,
|
||||||
|
// 9 bConfirmPIN
|
||||||
|
0x03, // TODO check?
|
||||||
|
// 10 bEntryValidationCondition BYTE Conditions under which PIN
|
||||||
|
// entry should be considered complete.
|
||||||
|
//
|
||||||
|
// table for bEntryValidationCondition:
|
||||||
|
// 0x01: Max size reached
|
||||||
|
// 0x02: Validation key pressed
|
||||||
|
// 0x04: Timeout occurred
|
||||||
|
0x07,
|
||||||
|
// 11 bNumberMessage BYTE Number of messages to display for PIN
|
||||||
|
// verification
|
||||||
|
0x03, // TODO check? (match with bConfirmPIN?)
|
||||||
|
// 12 wLangId USHORT Language for messages
|
||||||
|
0x04,
|
||||||
|
0x09, // US english
|
||||||
|
// 14 bMsgIndex1-3
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x02,
|
||||||
|
// 17 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 15 ulDataLength ULONG length of Data to be sent to the ICC
|
||||||
|
send.extend(&(ab_data.len() as u32).to_le_bytes());
|
||||||
|
|
||||||
|
// 19 abData BYTE[] Data to send to the ICC
|
||||||
|
send.extend(ab_data);
|
||||||
|
|
||||||
|
log::debug!("pcsc pinpad_modify send: {:x?}", send);
|
||||||
|
|
||||||
|
let mut recv = vec![0xAA; 256];
|
||||||
|
|
||||||
|
let modify_ioctl: [u8; 4] = self
|
||||||
|
.reader_caps
|
||||||
|
.get(&FEATURE_MODIFY_PIN_DIRECT)
|
||||||
|
.ok_or_else(|| anyhow!("no reader_capability"))?
|
||||||
|
.value()
|
||||||
|
.try_into()?;
|
||||||
|
|
||||||
|
let res = self.tx.control(
|
||||||
|
u32::from_be_bytes(modify_ioctl).into(),
|
||||||
|
&send,
|
||||||
|
&mut recv,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
log::debug!(" <- pcsc pinpad_modify result: {:x?}", res);
|
||||||
|
|
||||||
|
Ok(res.to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PcscClient {
|
impl PcscCard {
|
||||||
pub fn card(&mut self) -> &mut Card {
|
pub fn card(&mut self) -> &mut Card {
|
||||||
&mut self.card
|
&mut self.card
|
||||||
}
|
}
|
||||||
|
@ -266,8 +500,8 @@ impl PcscClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cards_filter(ident: Option<&str>) -> Result<Vec<PcscClient>, Error> {
|
fn cards_filter(ident: Option<&str>) -> Result<Vec<PcscCard>, Error> {
|
||||||
let mut cas: Vec<PcscClient> = vec![];
|
let mut cas: Vec<PcscCard> = vec![];
|
||||||
|
|
||||||
for mut card in
|
for mut card in
|
||||||
Self::raw_pcsc_cards().map_err(|sce| Error::Smartcard(sce))?
|
Self::raw_pcsc_cards().map_err(|sce| Error::Smartcard(sce))?
|
||||||
|
@ -283,10 +517,10 @@ impl PcscClient {
|
||||||
log::debug!("1");
|
log::debug!("1");
|
||||||
let mut tx: Transaction = start_tx!(card, false)?;
|
let mut tx: Transaction = start_tx!(card, false)?;
|
||||||
|
|
||||||
let mut txc = PcscTxClient::new(&mut tx, None);
|
let mut txc = TxClient::new(&mut tx, None, HashMap::default());
|
||||||
log::debug!("3");
|
log::debug!("3");
|
||||||
{
|
{
|
||||||
if let Err(e) = PcscTxClient::select(&mut txc) {
|
if let Err(e) = TxClient::select(&mut txc) {
|
||||||
log::debug!("4a");
|
log::debug!("4a");
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"cards_filter: error during select: {:?}",
|
"cards_filter: error during select: {:?}",
|
||||||
|
@ -307,14 +541,13 @@ impl PcscClient {
|
||||||
)))
|
)))
|
||||||
})?;
|
})?;
|
||||||
log::debug!("4b card status: {:x?}", stat);
|
log::debug!("4b card status: {:x?}", stat);
|
||||||
let mut txc = PcscTxClient::new(&mut tx, None);
|
let mut txc =
|
||||||
|
TxClient::new(&mut tx, None, HashMap::default());
|
||||||
// -- /debug: status --
|
// -- /debug: status --
|
||||||
|
|
||||||
if let Some(ident) = ident {
|
if let Some(ident) = ident {
|
||||||
if let Ok(ard) =
|
if let Ok(ard) =
|
||||||
PcscTxClient::application_related_data(
|
TxClient::application_related_data(&mut txc)
|
||||||
&mut txc,
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
let aid = ard.application_id()?;
|
let aid = ard.application_id()?;
|
||||||
|
|
||||||
|
@ -347,14 +580,10 @@ impl PcscClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction ends
|
|
||||||
// drop(txc);
|
|
||||||
// drop(tx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if store_card {
|
if store_card {
|
||||||
let pcsc = PcscClient::new(card);
|
let pcsc = PcscCard::new(card);
|
||||||
cas.push(pcsc.initialize_card()?);
|
cas.push(pcsc.initialize_card()?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,14 +597,14 @@ impl PcscClient {
|
||||||
///
|
///
|
||||||
/// Each card has the OpenPGP application selected, CardCaps have been
|
/// Each card has the OpenPGP application selected, CardCaps have been
|
||||||
/// initialized.
|
/// initialized.
|
||||||
pub fn cards() -> Result<Vec<PcscClient>, Error> {
|
pub fn cards() -> Result<Vec<PcscCard>, Error> {
|
||||||
Self::cards_filter(None)
|
Self::cards_filter(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the OpenPGP card that matches `ident`, if it is available.
|
/// Returns the OpenPGP card that matches `ident`, if it is available.
|
||||||
/// A fully initialized CardApp is returned: the OpenPGP application has
|
/// A fully initialized CardApp is returned: the OpenPGP application has
|
||||||
/// been selected, CardCaps have been set.
|
/// been selected, CardCaps have been set.
|
||||||
pub fn open_by_ident(ident: &str) -> Result<PcscClient, Error> {
|
pub fn open_by_ident(ident: &str) -> Result<PcscCard, Error> {
|
||||||
log::debug!("open_by_ident for {:?}", ident);
|
log::debug!("open_by_ident for {:?}", ident);
|
||||||
|
|
||||||
let mut cards = Self::cards_filter(Some(ident))?;
|
let mut cards = Self::cards_filter(Some(ident))?;
|
||||||
|
@ -399,14 +628,15 @@ impl PcscClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make an initialized CardApp from a PcscClient:
|
/// Initialized a PcscCard:
|
||||||
/// - Obtain and store feature lists from reader (pinpad functionality).
|
/// - Obtain and store feature lists from reader (pinpad functionality).
|
||||||
/// - Get ARD from card, set CardCaps based on ARD.
|
/// - Get ARD from card, set CardCaps based on ARD.
|
||||||
fn initialize_card(mut self) -> Result<Self> {
|
fn initialize_card(mut self) -> Result<Self> {
|
||||||
log::debug!("pcsc initialize_card");
|
log::debug!("pcsc initialize_card");
|
||||||
|
|
||||||
let mut tx: Transaction = start_tx!(self.card, true)?;
|
let mut tx: Transaction = start_tx!(self.card, true)?;
|
||||||
let mut txc = PcscTxClient::new(&mut tx, self.card_caps);
|
let mut txc =
|
||||||
|
TxClient::new(&mut tx, self.card_caps, self.reader_caps.clone());
|
||||||
|
|
||||||
// Get Features from reader (pinpad verify/modify)
|
// Get Features from reader (pinpad verify/modify)
|
||||||
if let Ok(feat) = txc.features() {
|
if let Ok(feat) = txc.features() {
|
||||||
|
@ -427,259 +657,10 @@ impl PcscClient {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the minimum pin length for pin_id.
|
|
||||||
fn min_pin_len(&self, pin_id: u8) -> Result<u8> {
|
|
||||||
match pin_id {
|
|
||||||
0x81 | 0x82 => Ok(6),
|
|
||||||
0x83 => Ok(8),
|
|
||||||
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get the maximum pin length for pin_id.
|
|
||||||
fn max_pin_len(&self, pin_id: u8) -> Result<u8> {
|
|
||||||
if let Some(card_caps) = self.card_caps {
|
|
||||||
match pin_id {
|
|
||||||
0x81 | 0x82 => Ok(card_caps.pw1_max_len()),
|
|
||||||
0x83 => Ok(card_caps.pw3_max_len()),
|
|
||||||
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(anyhow!("card_caps is None"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn card_caps(&self) -> Option<CardCaps> {
|
pub fn card_caps(&self) -> Option<CardCaps> {
|
||||||
self.card_caps
|
self.card_caps
|
||||||
}
|
}
|
||||||
|
pub fn reader_caps(&self) -> HashMap<u8, Tlv> {
|
||||||
|
self.reader_caps.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl CardClient for PcscClient {
|
|
||||||
// fn transmit(
|
|
||||||
// &mut self,
|
|
||||||
// cmd: &[u8],
|
|
||||||
// buf_size: usize,
|
|
||||||
// ) -> Result<Vec<u8>, Error> {
|
|
||||||
// let stat = self.card.status2_owned();
|
|
||||||
// log::debug!("PcscClient transmit - status2: {:x?}", stat);
|
|
||||||
//
|
|
||||||
// let mut tx: Transaction = start_tx!(self.card, true)?;
|
|
||||||
//
|
|
||||||
// log::debug!("PcscClient transmit 2");
|
|
||||||
// let mut txc = PcscTxClient::new(&mut tx, self.card_caps);
|
|
||||||
// log::debug!("PcscClient transmit 3 (got TxClient!)");
|
|
||||||
//
|
|
||||||
// let res = txc.transmit(cmd, buf_size);
|
|
||||||
//
|
|
||||||
// log::debug!("PcscClient transmit res {:x?}", res);
|
|
||||||
//
|
|
||||||
// res
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn init_card_caps(&mut self, caps: CardCaps) {
|
|
||||||
// self.card_caps = Some(caps);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn card_caps(&self) -> Option<&CardCaps> {
|
|
||||||
// self.card_caps.as_ref()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn feature_pinpad_verify(&self) -> bool {
|
|
||||||
// self.reader_caps.contains_key(&FEATURE_VERIFY_PIN_DIRECT)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn feature_pinpad_modify(&self) -> bool {
|
|
||||||
// self.reader_caps.contains_key(&FEATURE_MODIFY_PIN_DIRECT)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn pinpad_verify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
|
|
||||||
// let pin_min_size = self.min_pin_len(pin_id)?;
|
|
||||||
// let pin_max_size = self.max_pin_len(pin_id)?;
|
|
||||||
//
|
|
||||||
// // Default to varlen, for now.
|
|
||||||
// // (NOTE: Some readers don't support varlen, and need explicit length
|
|
||||||
// // information. Also see https://wiki.gnupg.org/CardReader/PinpadInput)
|
|
||||||
// let fixedlen: u8 = 0;
|
|
||||||
//
|
|
||||||
// // APDU: 00 20 00 pin_id <len> (ff)*
|
|
||||||
// let mut ab_data = vec![
|
|
||||||
// 0x00, /* CLA */
|
|
||||||
// 0x20, /* INS: VERIFY */
|
|
||||||
// 0x00, /* P1 */
|
|
||||||
// pin_id, /* P2 */
|
|
||||||
// fixedlen, /* Lc: 'fixedlen' data bytes */
|
|
||||||
// ];
|
|
||||||
// ab_data.extend([0xff].repeat(fixedlen as usize));
|
|
||||||
//
|
|
||||||
// // PC/SC v2.02.05 Part 10 PIN verification data structure
|
|
||||||
// let mut send: Vec<u8> = vec![
|
|
||||||
// // 0 bTimeOut BYTE timeout in seconds (00 means use default
|
|
||||||
// // timeout)
|
|
||||||
// 0x00,
|
|
||||||
// // 1 bTimeOut2 BYTE timeout in seconds after first key stroke
|
|
||||||
// 0x00,
|
|
||||||
// // 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx
|
|
||||||
// 0x82,
|
|
||||||
// // 3 bmPINBlockString BYTE
|
|
||||||
// // bits 7-4 bit size of PIN length in APDU
|
|
||||||
// // bits 3-0 PIN block size in bytes after justification and formatting
|
|
||||||
// fixedlen,
|
|
||||||
// // 4 bmPINLengthFormat BYTE
|
|
||||||
// // bits 7-5 RFU, bit 4 set if system units are bytes clear if
|
|
||||||
// // system units are bits,
|
|
||||||
// // bits 3-0 PIN length position in system units
|
|
||||||
// 0x00,
|
|
||||||
// // 5 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size
|
|
||||||
// // in digits, YY is maximum
|
|
||||||
// pin_max_size,
|
|
||||||
// pin_min_size,
|
|
||||||
// // 7 bEntryValidationCondition BYTE Conditions under which PIN
|
|
||||||
// // entry should be considered complete.
|
|
||||||
// //
|
|
||||||
// // table for bEntryValidationCondition:
|
|
||||||
// // 0x01: Max size reached
|
|
||||||
// // 0x02: Validation key pressed
|
|
||||||
// // 0x04: Timeout occurred
|
|
||||||
// 0x07,
|
|
||||||
// // 8 bNumberMessage BYTE Number of messages to display for PIN
|
|
||||||
// // verification
|
|
||||||
// 0x01,
|
|
||||||
// // 9 wLangIdU SHORT Language for messages
|
|
||||||
// 0x04,
|
|
||||||
// 0x09, // US english
|
|
||||||
// // 11 bMsgIndex BYTE Message index (should be 00)
|
|
||||||
// 0x00,
|
|
||||||
// // 12 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00)
|
|
||||||
// 0x00,
|
|
||||||
// 0x00,
|
|
||||||
// 0x00,
|
|
||||||
// ];
|
|
||||||
//
|
|
||||||
// // 15 ulDataLength ULONG length of Data to be sent to the ICC
|
|
||||||
// send.extend(&(ab_data.len() as u32).to_le_bytes());
|
|
||||||
//
|
|
||||||
// // 19 abData BYTE[] Data to send to the ICC
|
|
||||||
// send.extend(ab_data);
|
|
||||||
//
|
|
||||||
// log::debug!("pcsc pinpad_verify send: {:x?}", send);
|
|
||||||
//
|
|
||||||
// let mut recv = vec![0xAA; 256];
|
|
||||||
//
|
|
||||||
// let verify_ioctl: [u8; 4] = self
|
|
||||||
// .reader_caps
|
|
||||||
// .get(&FEATURE_VERIFY_PIN_DIRECT)
|
|
||||||
// .ok_or_else(|| anyhow!("no reader_capability"))?
|
|
||||||
// .value()
|
|
||||||
// .try_into()?;
|
|
||||||
//
|
|
||||||
// let res = self.card.control(
|
|
||||||
// u32::from_be_bytes(verify_ioctl).into(),
|
|
||||||
// &send,
|
|
||||||
// &mut recv,
|
|
||||||
// )?;
|
|
||||||
//
|
|
||||||
// log::debug!(" <- pcsc pinpad_verify result: {:x?}", res);
|
|
||||||
//
|
|
||||||
// Ok(res.to_vec())
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn pinpad_modify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
|
|
||||||
// let pin_min_size = self.min_pin_len(pin_id)?;
|
|
||||||
// let pin_max_size = self.max_pin_len(pin_id)?;
|
|
||||||
//
|
|
||||||
// // Default to varlen, for now.
|
|
||||||
// // (NOTE: Some readers don't support varlen, and need explicit length
|
|
||||||
// // information. Also see https://wiki.gnupg.org/CardReader/PinpadInput)
|
|
||||||
// let fixedlen: u8 = 0;
|
|
||||||
//
|
|
||||||
// // APDU: 00 24 00 pin_id <len> [(ff)* x2]
|
|
||||||
// let mut ab_data = vec![
|
|
||||||
// 0x00, /* CLA */
|
|
||||||
// 0x24, /* INS: CHANGE_REFERENCE_DATA */
|
|
||||||
// 0x00, /* P1 */
|
|
||||||
// pin_id, /* P2 */
|
|
||||||
// fixedlen * 2, /* Lc: 'fixedlen' data bytes */
|
|
||||||
// ];
|
|
||||||
// ab_data.extend([0xff].repeat(fixedlen as usize * 2));
|
|
||||||
//
|
|
||||||
// // PC/SC v2.02.05 Part 10 PIN modification data structure
|
|
||||||
// let mut send: Vec<u8> = vec![
|
|
||||||
// // 0 bTimeOut BYTE timeout in seconds (00 means use default
|
|
||||||
// // timeout)
|
|
||||||
// 0x00,
|
|
||||||
// // 1 bTimeOut2 BYTE timeout in seconds after first key stroke
|
|
||||||
// 0x00,
|
|
||||||
// // 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx
|
|
||||||
// 0x82,
|
|
||||||
// // 3 bmPINBlockString BYTE
|
|
||||||
// // bits 7-4 bit size of PIN length in APDU
|
|
||||||
// // bits 3-0 PIN block size in bytes after justification and formatting
|
|
||||||
// fixedlen,
|
|
||||||
// // 4 bmPINLengthFormat BYTE
|
|
||||||
// // bits 7-5 RFU, bit 4 set if system units are bytes clear if
|
|
||||||
// // system units are bits,
|
|
||||||
// // bits 3-0 PIN length position in system units
|
|
||||||
// 0x00,
|
|
||||||
// // 5 bInsertionOffsetOld BYTE Insertion position offset in bytes for
|
|
||||||
// // the current PIN
|
|
||||||
// 0x00,
|
|
||||||
// // 6 bInsertionOffsetNew BYTE Insertion position offset in bytes for
|
|
||||||
// // the new PIN
|
|
||||||
// fixedlen,
|
|
||||||
// // 7 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size
|
|
||||||
// // in digits, YY is maximum
|
|
||||||
// pin_max_size,
|
|
||||||
// pin_min_size,
|
|
||||||
// // 9 bConfirmPIN
|
|
||||||
// 0x03, // TODO check?
|
|
||||||
// // 10 bEntryValidationCondition BYTE Conditions under which PIN
|
|
||||||
// // entry should be considered complete.
|
|
||||||
// //
|
|
||||||
// // table for bEntryValidationCondition:
|
|
||||||
// // 0x01: Max size reached
|
|
||||||
// // 0x02: Validation key pressed
|
|
||||||
// // 0x04: Timeout occurred
|
|
||||||
// 0x07,
|
|
||||||
// // 11 bNumberMessage BYTE Number of messages to display for PIN
|
|
||||||
// // verification
|
|
||||||
// 0x03, // TODO check? (match with bConfirmPIN?)
|
|
||||||
// // 12 wLangId USHORT Language for messages
|
|
||||||
// 0x04,
|
|
||||||
// 0x09, // US english
|
|
||||||
// // 14 bMsgIndex1-3
|
|
||||||
// 0x00,
|
|
||||||
// 0x01,
|
|
||||||
// 0x02,
|
|
||||||
// // 17 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00)
|
|
||||||
// 0x00,
|
|
||||||
// 0x00,
|
|
||||||
// 0x00,
|
|
||||||
// ];
|
|
||||||
//
|
|
||||||
// // 15 ulDataLength ULONG length of Data to be sent to the ICC
|
|
||||||
// send.extend(&(ab_data.len() as u32).to_le_bytes());
|
|
||||||
//
|
|
||||||
// // 19 abData BYTE[] Data to send to the ICC
|
|
||||||
// send.extend(ab_data);
|
|
||||||
//
|
|
||||||
// log::debug!("pcsc pinpad_modify send: {:x?}", send);
|
|
||||||
//
|
|
||||||
// let mut recv = vec![0xAA; 256];
|
|
||||||
//
|
|
||||||
// let modify_ioctl: [u8; 4] = self
|
|
||||||
// .reader_caps
|
|
||||||
// .get(&FEATURE_MODIFY_PIN_DIRECT)
|
|
||||||
// .ok_or_else(|| anyhow!("no reader_capability"))?
|
|
||||||
// .value()
|
|
||||||
// .try_into()?;
|
|
||||||
//
|
|
||||||
// let res = self.card.control(
|
|
||||||
// u32::from_be_bytes(modify_ioctl).into(),
|
|
||||||
// &send,
|
|
||||||
// &mut recv,
|
|
||||||
// )?;
|
|
||||||
//
|
|
||||||
// log::debug!(" <- pcsc pinpad_modify result: {:x?}", res);
|
|
||||||
//
|
|
||||||
// Ok(res.to_vec())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use anyhow::Result;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use openpgp_card::{CardClient, Error, StatusBytes};
|
use openpgp_card::{CardClient, Error, StatusBytes};
|
||||||
use openpgp_card_pcsc::PcscClient;
|
use openpgp_card_pcsc::PcscCard;
|
||||||
use openpgp_card_sequoia::card::Open;
|
use openpgp_card_sequoia::card::Open;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
|
@ -15,7 +15,7 @@ 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)?;
|
let mut card = PcscCard::open_by_ident(&cli.ident)?;
|
||||||
let pinpad_verify = card.feature_pinpad_verify();
|
let pinpad_verify = card.feature_pinpad_verify();
|
||||||
let pinpad_modify = card.feature_pinpad_modify();
|
let pinpad_modify = card.feature_pinpad_modify();
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,15 @@ use anyhow::{anyhow, Context, Result};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use openpgp_card::Error;
|
use openpgp_card::Error;
|
||||||
use openpgp_card_pcsc::PcscClient;
|
use openpgp_card_pcsc::PcscCard;
|
||||||
use openpgp_card_sequoia::card::{Admin, Open, Sign, User};
|
use openpgp_card_sequoia::card::{Admin, Open, Sign, User};
|
||||||
|
|
||||||
pub(crate) fn cards() -> Result<Vec<PcscClient>, Error> {
|
pub(crate) fn cards() -> Result<Vec<PcscCard>, Error> {
|
||||||
PcscClient::cards()
|
PcscCard::cards()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn open_card(ident: &str) -> Result<PcscClient, Error> {
|
pub(crate) fn open_card(ident: &str) -> Result<PcscCard, Error> {
|
||||||
PcscClient::open_by_ident(ident)
|
PcscCard::open_by_ident(ident)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify_to_user<'app, 'open>(
|
pub(crate) fn verify_to_user<'app, 'open>(
|
||||||
|
|
Loading…
Reference in a new issue