Implement pinpad feature detection and pinpad support for verify/modify (of pw1 and pw3) in pcsc backend.
Extend CardCaps to contain pw1_max_len and pw3_max_len (and initialize these values from ARD). Add pinpad_verify(), pinpad_modify(), feature_verify()/feature_modify() to CardClient API. Expose in card_app (and openpgp-card-sequoia card API). Adjust opgpcard, opgpcard-pin to ue pinpad reader when available.
This commit is contained in:
parent
a7fb5b2b2c
commit
2709b4ad39
14 changed files with 655 additions and 107 deletions
|
@ -255,8 +255,14 @@ pub fn test_keygen(
|
|||
|
||||
// Generate a Cert for this set of generated keys
|
||||
let mut open = Open::new(&mut ca)?;
|
||||
let cert =
|
||||
make_cert(&mut open, key_sig, Some(key_dec), Some(key_aut), "123456")?;
|
||||
let cert = make_cert(
|
||||
&mut open,
|
||||
key_sig,
|
||||
Some(key_dec),
|
||||
Some(key_aut),
|
||||
Some("123456".to_string()),
|
||||
&|| {},
|
||||
)?;
|
||||
let armored = String::from_utf8(cert.armored().to_vec()?)?;
|
||||
|
||||
let res = TestResult::Text(armored);
|
||||
|
|
|
@ -58,6 +58,13 @@ impl<'a> Open<'a> {
|
|||
pw3: false,
|
||||
})
|
||||
}
|
||||
pub fn feature_pinpad_verify(&self) -> bool {
|
||||
self.card_app.feature_pinpad_verify()
|
||||
}
|
||||
|
||||
pub fn feature_pinpad_modify(&self) -> bool {
|
||||
self.card_app.feature_pinpad_modify()
|
||||
}
|
||||
|
||||
pub fn verify_user(&mut self, pin: &str) -> Result<(), Error> {
|
||||
let _ = self.card_app.verify_pw1(pin)?;
|
||||
|
@ -65,6 +72,17 @@ impl<'a> Open<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn verify_user_pinpad(
|
||||
&mut self,
|
||||
prompt: &dyn Fn(),
|
||||
) -> Result<(), Error> {
|
||||
prompt();
|
||||
|
||||
let _ = self.card_app.verify_pw1_pinpad()?;
|
||||
self.pw1 = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn verify_user_for_signing(&mut self, pin: &str) -> Result<(), Error> {
|
||||
let _ = self.card_app.verify_pw1_for_signing(pin)?;
|
||||
|
||||
|
@ -74,12 +92,37 @@ impl<'a> Open<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn verify_user_for_signing_pinpad(
|
||||
&mut self,
|
||||
prompt: &dyn Fn(),
|
||||
) -> Result<(), Error> {
|
||||
prompt();
|
||||
|
||||
let _ = self.card_app.verify_pw1_for_signing_pinpad()?;
|
||||
|
||||
// FIXME: depending on card mode, pw1_sign is only usable once
|
||||
|
||||
self.pw1_sign = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn verify_admin(&mut self, pin: &str) -> Result<(), Error> {
|
||||
let _ = self.card_app.verify_pw3(pin)?;
|
||||
self.pw3 = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn verify_admin_pinpad(
|
||||
&mut self,
|
||||
prompt: &dyn Fn(),
|
||||
) -> Result<(), Error> {
|
||||
prompt();
|
||||
|
||||
let _ = self.card_app.verify_pw3_pinpad()?;
|
||||
self.pw3 = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ask the card if the user password has been successfully verified.
|
||||
///
|
||||
/// NOTE: on some cards this functionality seems broken.
|
||||
|
@ -102,6 +145,14 @@ impl<'a> Open<'a> {
|
|||
self.card_app.change_pw1(old, new)
|
||||
}
|
||||
|
||||
pub fn change_user_pin_pinpad(
|
||||
&mut self,
|
||||
prompt: &dyn Fn(),
|
||||
) -> Result<Response, Error> {
|
||||
prompt();
|
||||
self.card_app.change_pw1_pinpad()
|
||||
}
|
||||
|
||||
pub fn reset_user_pin(
|
||||
&mut self,
|
||||
rst: &str,
|
||||
|
@ -119,6 +170,14 @@ impl<'a> Open<'a> {
|
|||
self.card_app.change_pw3(old, new)
|
||||
}
|
||||
|
||||
pub fn change_admin_pin_pinpad(
|
||||
&mut self,
|
||||
prompt: &dyn Fn(),
|
||||
) -> Result<Response, Error> {
|
||||
prompt();
|
||||
self.card_app.change_pw3_pinpad()
|
||||
}
|
||||
|
||||
/// Get a view of the card authenticated for "User" commands.
|
||||
pub fn user_card<'b>(&'a mut self) -> Option<User<'a, 'b>> {
|
||||
if self.pw1 {
|
||||
|
|
|
@ -37,13 +37,18 @@ use crate::{decryptor, signer, PublicKey};
|
|||
/// Create a Cert from the three subkeys on a card.
|
||||
/// (Calling this multiple times will result in different Certs!)
|
||||
///
|
||||
/// When pw1 is None, attempt to verify via pinpad.
|
||||
///
|
||||
/// `prompt` notifies the user when a pinpad needs the user pin as input.
|
||||
///
|
||||
/// FIXME: accept optional metadata for user_id(s)?
|
||||
pub fn make_cert<'a, 'app>(
|
||||
open: &'a mut Open<'app>,
|
||||
key_sig: PublicKey,
|
||||
key_dec: Option<PublicKey>,
|
||||
key_aut: Option<PublicKey>,
|
||||
pw1: &str,
|
||||
pw1: Option<String>,
|
||||
prompt: &dyn Fn(),
|
||||
) -> Result<Cert> {
|
||||
let mut pp = vec![];
|
||||
|
||||
|
@ -72,7 +77,11 @@ pub fn make_cert<'a, 'app>(
|
|||
)?;
|
||||
|
||||
// Allow signing on the card
|
||||
open.verify_user_for_signing(pw1)?;
|
||||
if let Some(pw1) = pw1.clone() {
|
||||
open.verify_user_for_signing(&pw1)?;
|
||||
} else {
|
||||
open.verify_user_for_signing_pinpad(prompt)?;
|
||||
}
|
||||
if let Some(mut sign) = open.signing_card() {
|
||||
// Card-backed signer for bindings
|
||||
let mut card_signer = sign.signer_from_pubkey(key_sig.clone());
|
||||
|
@ -100,7 +109,11 @@ pub fn make_cert<'a, 'app>(
|
|||
.set_key_flags(KeyFlags::empty().set_authentication())?;
|
||||
|
||||
// Allow signing on the card
|
||||
open.verify_user_for_signing(pw1)?;
|
||||
if let Some(pw1) = pw1.clone() {
|
||||
open.verify_user_for_signing(&pw1)?;
|
||||
} else {
|
||||
open.verify_user_for_signing_pinpad(prompt)?;
|
||||
}
|
||||
if let Some(mut sign) = open.signing_card() {
|
||||
// Card-backed signer for bindings
|
||||
let mut card_signer = sign.signer_from_pubkey(key_sig.clone());
|
||||
|
@ -141,7 +154,11 @@ pub fn make_cert<'a, 'app>(
|
|||
)?;
|
||||
|
||||
// Allow signing on the card
|
||||
open.verify_user_for_signing(pw1)?;
|
||||
if let Some(pw1) = pw1 {
|
||||
open.verify_user_for_signing(&pw1)?;
|
||||
} else {
|
||||
open.verify_user_for_signing_pinpad(prompt)?;
|
||||
}
|
||||
|
||||
if let Some(mut sign) = open.signing_card() {
|
||||
// Card-backed signer for bindings
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::convert::TryInto;
|
|||
use anyhow::{anyhow, Result};
|
||||
|
||||
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple};
|
||||
use crate::apdu::response::RawResponse;
|
||||
use crate::apdu::{commands, response::Response};
|
||||
use crate::card_do::{
|
||||
ApplicationRelatedData, CardholderRelatedData, Fingerprint,
|
||||
|
@ -89,11 +90,17 @@ impl CardApp {
|
|||
(255, 255)
|
||||
};
|
||||
|
||||
let pw_status = ard.get_pw_status_bytes()?;
|
||||
let pw1_max = pw_status.get_pw1_max_len();
|
||||
let pw3_max = pw_status.get_pw3_max_len();
|
||||
|
||||
let caps = CardCaps {
|
||||
ext_support,
|
||||
chaining_support,
|
||||
max_cmd_bytes,
|
||||
max_rsp_bytes,
|
||||
pw1_max_len: pw1_max,
|
||||
pw3_max_len: pw3_max,
|
||||
};
|
||||
|
||||
self.card_client.init_caps(caps);
|
||||
|
@ -335,6 +342,18 @@ impl CardApp {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
||||
/// Does the cardreader support direct pinpad verify?
|
||||
pub fn feature_pinpad_verify(&self) -> bool {
|
||||
self.card_client.feature_pinpad_verify()
|
||||
}
|
||||
|
||||
/// Does the cardreader support direct pinpad modify?
|
||||
pub fn feature_pinpad_modify(&self) -> bool {
|
||||
self.card_client.feature_pinpad_modify()
|
||||
}
|
||||
|
||||
/// Verify pw1 (user) for signing operation (mode 81) and set an
|
||||
/// appropriate access status.
|
||||
///
|
||||
|
@ -349,6 +368,20 @@ impl CardApp {
|
|||
apdu::send_command(self.card_client(), verify, false)?.try_into()
|
||||
}
|
||||
|
||||
/// Verify pw1 (user) for signing operation (mode 81) and set an
|
||||
/// appropriate access status. This fn uses a pinpad on the card reader,
|
||||
/// if no usable pinpad is found, an error is returned.
|
||||
///
|
||||
/// Depending on the PW1 status byte (see Extended Capabilities) this
|
||||
/// access condition is only valid for one PSO:CDS command or remains
|
||||
/// valid for several attempts.
|
||||
pub fn verify_pw1_for_signing_pinpad(
|
||||
&mut self,
|
||||
) -> Result<Response, Error> {
|
||||
let res = self.card_client.pinpad_verify(0x81)?;
|
||||
RawResponse::try_from(res)?.try_into()
|
||||
}
|
||||
|
||||
/// Check the current access of PW1 for signing (mode 81).
|
||||
///
|
||||
/// If verification is not required, an empty Ok Response is returned.
|
||||
|
@ -367,6 +400,15 @@ impl CardApp {
|
|||
apdu::send_command(self.card_client(), verify, false)?.try_into()
|
||||
}
|
||||
|
||||
/// Verify PW1 (user) and set an appropriate access status.
|
||||
/// (For operations except signing, mode 82).
|
||||
/// This fn uses a pinpad on the card reader, if no usable pinpad is
|
||||
/// found, an error is returned.
|
||||
pub fn verify_pw1_pinpad(&mut self) -> Result<Response, Error> {
|
||||
let res = self.card_client.pinpad_verify(0x82)?;
|
||||
RawResponse::try_from(res)?.try_into()
|
||||
}
|
||||
|
||||
/// Check the current access of PW1.
|
||||
/// (For operations except signing, mode 82).
|
||||
///
|
||||
|
@ -385,6 +427,14 @@ impl CardApp {
|
|||
apdu::send_command(self.card_client(), verify, false)?.try_into()
|
||||
}
|
||||
|
||||
/// Verify PW3 (admin) and set an appropriate access status.
|
||||
/// This fn uses a pinpad on the card reader, if no usable pinpad is
|
||||
/// found, an error is returned.
|
||||
pub fn verify_pw3_pinpad(&mut self) -> Result<Response, Error> {
|
||||
let res = self.card_client.pinpad_verify(0x83)?;
|
||||
RawResponse::try_from(res)?.try_into()
|
||||
}
|
||||
|
||||
/// Check the current access of PW3 (admin).
|
||||
///
|
||||
/// If verification is not required, an empty Ok Response is returned.
|
||||
|
@ -412,6 +462,14 @@ impl CardApp {
|
|||
apdu::send_command(self.card_client(), change, false)?.try_into()
|
||||
}
|
||||
|
||||
/// Change the value of PW1 (user password).
|
||||
/// This fn uses a pinpad on the card reader, if no usable pinpad is
|
||||
/// found, an error is returned.
|
||||
pub fn change_pw1_pinpad(&mut self) -> Result<Response, Error> {
|
||||
let res = self.card_client.pinpad_modify(0x81)?;
|
||||
RawResponse::try_from(res)?.try_into()
|
||||
}
|
||||
|
||||
/// Change the value of PW3 (admin password).
|
||||
///
|
||||
/// The current value of PW3 must be presented in `old` for authorization.
|
||||
|
@ -428,6 +486,14 @@ impl CardApp {
|
|||
apdu::send_command(self.card_client(), change, false)?.try_into()
|
||||
}
|
||||
|
||||
/// Change the value of PW3 (admin password).
|
||||
/// This fn uses a pinpad on the card reader, if no usable pinpad is
|
||||
/// found, an error is returned.
|
||||
pub fn change_pw3_pinpad(&mut self) -> Result<Response, Error> {
|
||||
let res = self.card_client.pinpad_modify(0x83)?;
|
||||
RawResponse::try_from(res)?.try_into()
|
||||
}
|
||||
|
||||
/// Reset the error counter for PW1 (user password) and set a new value
|
||||
/// for PW1.
|
||||
///
|
||||
|
|
|
@ -325,10 +325,10 @@ impl From<u8> for Sex {
|
|||
pub struct PWStatusBytes {
|
||||
pub(crate) pw1_cds_valid_once: bool,
|
||||
pub(crate) pw1_pin_block: bool,
|
||||
pub(crate) pw1_len: u8,
|
||||
pub(crate) pw1_len_format: u8,
|
||||
pub(crate) rc_len: u8,
|
||||
pub(crate) pw3_pin_block: bool,
|
||||
pub(crate) pw3_len: u8,
|
||||
pub(crate) pw3_len_format: u8,
|
||||
pub(crate) err_count_pw1: u8,
|
||||
pub(crate) err_count_rst: u8,
|
||||
pub(crate) err_count_pw3: u8,
|
||||
|
@ -349,6 +349,18 @@ impl PWStatusBytes {
|
|||
self.pw1_cds_valid_once
|
||||
}
|
||||
|
||||
pub fn get_pw1_max_len(&self) -> u8 {
|
||||
self.pw1_len_format & 0x7f
|
||||
}
|
||||
|
||||
pub fn get_rc_max_len(&self) -> u8 {
|
||||
self.rc_len
|
||||
}
|
||||
|
||||
pub fn get_pw3_max_len(&self) -> u8 {
|
||||
self.pw3_len_format & 0x7f
|
||||
}
|
||||
|
||||
pub fn get_err_count_pw1(&self) -> u8 {
|
||||
self.err_count_pw1
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ impl PWStatusBytes {
|
|||
data.push(if self.pw1_cds_valid_once { 0 } else { 1 });
|
||||
|
||||
if long {
|
||||
let mut b2 = self.pw1_len;
|
||||
let mut b2 = self.pw1_len_format;
|
||||
if self.pw1_pin_block {
|
||||
b2 |= 0x80;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ impl PWStatusBytes {
|
|||
|
||||
data.push(self.rc_len);
|
||||
|
||||
let mut b4 = self.pw3_len;
|
||||
let mut b4 = self.pw3_len_format;
|
||||
if self.pw3_pin_block {
|
||||
b4 |= 0x80;
|
||||
}
|
||||
|
@ -58,10 +58,10 @@ impl TryFrom<&[u8]> for PWStatusBytes {
|
|||
Ok(Self {
|
||||
pw1_cds_valid_once,
|
||||
pw1_pin_block,
|
||||
pw1_len,
|
||||
pw1_len_format: pw1_len,
|
||||
rc_len,
|
||||
pw3_pin_block,
|
||||
pw3_len,
|
||||
pw3_len_format: pw3_len,
|
||||
err_count_pw1,
|
||||
err_count_rst,
|
||||
err_count_pw3,
|
||||
|
@ -92,10 +92,10 @@ mod test {
|
|||
PWStatusBytes {
|
||||
pw1_cds_valid_once: true,
|
||||
pw1_pin_block: false,
|
||||
pw1_len: 0x40,
|
||||
pw1_len_format: 0x40,
|
||||
rc_len: 0x40,
|
||||
pw3_pin_block: false,
|
||||
pw3_len: 0x40,
|
||||
pw3_len_format: 0x40,
|
||||
err_count_pw1: 3,
|
||||
err_count_rst: 0,
|
||||
err_count_pw3: 3
|
||||
|
|
|
@ -77,6 +77,18 @@ pub trait CardClient {
|
|||
fn max_cmd_len(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Does the reader support FEATURE_VERIFY_PIN_DIRECT?
|
||||
fn feature_pinpad_verify(&self) -> bool;
|
||||
|
||||
/// Does the reader support FEATURE_MODIFY_PIN_DIRECT?
|
||||
fn feature_pinpad_modify(&self) -> bool;
|
||||
|
||||
/// Verify the PIN 'id' via the reader pinpad
|
||||
fn pinpad_verify(&mut self, id: u8) -> Result<Vec<u8>>;
|
||||
|
||||
/// Modify the PIN 'id' via the reader pinpad
|
||||
fn pinpad_modify(&mut self, id: u8) -> Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
/// A boxed CardClient (which is Send+Sync).
|
||||
|
@ -96,7 +108,7 @@ impl dyn CardClient {
|
|||
/// length can be used when communicating with the card.
|
||||
///
|
||||
/// (This configuration is retrieved from card metadata, specifically from
|
||||
/// "Card Capabilities" and "Extended length information")
|
||||
/// "Card Capabilities", "Extended length information" and "PWStatus")
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CardCaps {
|
||||
/// Extended Lc and Le fields
|
||||
|
@ -110,6 +122,12 @@ pub struct CardCaps {
|
|||
|
||||
/// Maximum number of bytes in a response APDU
|
||||
max_rsp_bytes: u16,
|
||||
|
||||
/// Maximum length of pw1
|
||||
pw1_max_len: u8,
|
||||
|
||||
/// Maximum length of pw3
|
||||
pw3_max_len: u8,
|
||||
}
|
||||
|
||||
impl CardCaps {
|
||||
|
@ -120,6 +138,14 @@ impl CardCaps {
|
|||
pub fn get_max_rsp_bytes(&self) -> u16 {
|
||||
self.max_rsp_bytes
|
||||
}
|
||||
|
||||
pub fn get_pw1_max_len(&self) -> u8 {
|
||||
self.pw1_max_len
|
||||
}
|
||||
|
||||
pub fn get_pw3_max_len(&self) -> u8 {
|
||||
self.pw3_max_len
|
||||
}
|
||||
}
|
||||
|
||||
/// Identify a Key slot on an OpenPGP card
|
||||
|
|
|
@ -13,6 +13,7 @@ documentation = "https://docs.rs/crate/openpgp-card-pcsc"
|
|||
|
||||
[dependencies]
|
||||
openpgp-card = { path = "../openpgp-card", version = "0.0.5" }
|
||||
iso7816-tlv = "0.4"
|
||||
pcsc = "2"
|
||||
anyhow = "1"
|
||||
log = "0.4"
|
||||
|
|
261
pcsc/src/lib.rs
261
pcsc/src/lib.rs
|
@ -2,13 +2,20 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use iso7816_tlv::simple::Tlv;
|
||||
use pcsc::{Card, Context, Protocols, Scope, ShareMode};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use openpgp_card::{CardApp, CardCaps, CardClient, Error, SmartcardError};
|
||||
|
||||
const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06;
|
||||
const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07;
|
||||
|
||||
pub struct PcscClient {
|
||||
card: Card,
|
||||
card_caps: Option<CardCaps>,
|
||||
reader_caps: HashMap<u8, Tlv>,
|
||||
}
|
||||
|
||||
impl PcscClient {
|
||||
|
@ -56,11 +63,21 @@ impl PcscClient {
|
|||
Self {
|
||||
card,
|
||||
card_caps: None,
|
||||
reader_caps: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Make an initialized CardApp from a PcscClient
|
||||
fn into_card_app(self) -> Result<CardApp> {
|
||||
/// Make an initialized CardApp from a PcscClient.
|
||||
/// Obtain and store feature lists from reader (pinpad functionality).
|
||||
fn into_card_app(mut self) -> Result<CardApp> {
|
||||
// Get Features from reader (pinpad verify/modify)
|
||||
let feat = self.features()?;
|
||||
for tlv in feat {
|
||||
log::debug!("Found reader feature {:?}", tlv);
|
||||
self.reader_caps.insert(tlv.tag().into(), tlv);
|
||||
}
|
||||
|
||||
// Get initalized CardApp
|
||||
CardApp::initialize(Box::new(self))
|
||||
}
|
||||
|
||||
|
@ -136,6 +153,46 @@ impl PcscClient {
|
|||
Err(Error::Smartcard(SmartcardError::SelectOpenPGPCardFailed))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the minimum pin length for pin_id.
|
||||
fn get_min_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 get_max_len(&self, pin_id: u8) -> Result<u8> {
|
||||
if let Some(card_caps) = self.card_caps {
|
||||
match pin_id {
|
||||
0x81 | 0x82 => Ok(card_caps.get_pw1_max_len()),
|
||||
0x83 => Ok(card_caps.get_pw3_max_len()),
|
||||
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!("card_caps is None"))
|
||||
}
|
||||
}
|
||||
|
||||
/// GET_FEATURE_REQUEST
|
||||
/// (see http://pcscworkgroup.com/Download/Specifications/pcsc10_v2.02.09.pdf)
|
||||
fn features(&mut self) -> Result<Vec<Tlv>, Error> {
|
||||
let mut recv = vec![0; 1024];
|
||||
|
||||
let cm_ioctl_get_feature_request = 0x42000000 + 3400;
|
||||
let res = self
|
||||
.card
|
||||
.control(cm_ioctl_get_feature_request, &[], &mut recv)
|
||||
.map_err(|e| {
|
||||
Error::Smartcard(SmartcardError::Error(format!(
|
||||
"GET_FEATURE_REQUEST control call failed: {:?}",
|
||||
e
|
||||
)))
|
||||
})?;
|
||||
|
||||
Ok(Tlv::parse_all(res))
|
||||
}
|
||||
}
|
||||
|
||||
impl CardClient for PcscClient {
|
||||
|
@ -171,4 +228,204 @@ impl CardClient for PcscClient {
|
|||
fn get_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.get_min_len(pin_id)?;
|
||||
let pin_max_size = self.get_max_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) as u64,
|
||||
&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.get_min_len(pin_id)?;
|
||||
let pin_max_size = self.get_max_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) as u64,
|
||||
&send,
|
||||
&mut recv,
|
||||
)?;
|
||||
|
||||
log::debug!(" <- pcsc pinpad_modify result: {:x?}", res);
|
||||
|
||||
Ok(res.to_vec())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -262,4 +262,20 @@ impl CardClient for ScdClient {
|
|||
fn max_cmd_len(&self) -> Option<usize> {
|
||||
Some(APDU_CMD_BYTES_MAX)
|
||||
}
|
||||
|
||||
fn feature_pinpad_verify(&self) -> bool {
|
||||
false // FIXME
|
||||
}
|
||||
|
||||
fn feature_pinpad_modify(&self) -> bool {
|
||||
false // FIXME
|
||||
}
|
||||
|
||||
fn pinpad_verify(&mut self, _id: u8) -> Result<Vec<u8>> {
|
||||
unimplemented!() // FIXME
|
||||
}
|
||||
|
||||
fn pinpad_modify(&mut self, _id: u8) -> Result<Vec<u8>> {
|
||||
unimplemented!() // FIXME
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,35 +11,52 @@ use openpgp_card_sequoia::card::Open;
|
|||
mod cli;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
|
||||
let cli = cli::Cli::from_args();
|
||||
|
||||
let mut card = PcscClient::open_by_ident(&cli.ident)?;
|
||||
let pinpad_verify = card.feature_pinpad_verify();
|
||||
let pinpad_modify = card.feature_pinpad_modify();
|
||||
|
||||
let mut open = Open::new(&mut card)?;
|
||||
|
||||
match cli.cmd {
|
||||
cli::Command::SetUserPin {} => {
|
||||
// get current user pin
|
||||
let pin =
|
||||
rpassword::read_password_from_tty(Some("Enter user PIN: "))?;
|
||||
let res = if !pinpad_modify {
|
||||
// get current user pin
|
||||
let pin = rpassword::read_password_from_tty(Some(
|
||||
"Enter user PIN: ",
|
||||
))?;
|
||||
|
||||
// verify pin
|
||||
open.verify_user(&pin)?;
|
||||
println!("PIN was accepted by the card.\n");
|
||||
// verify pin
|
||||
open.verify_user(&pin)?;
|
||||
println!("PIN was accepted by the card.\n");
|
||||
|
||||
// get new user pin
|
||||
let newpin1 = rpassword::read_password_from_tty(Some(
|
||||
"Enter new user PIN: ",
|
||||
))?;
|
||||
let newpin2 = rpassword::read_password_from_tty(Some(
|
||||
"Repeat the new user PIN: ",
|
||||
))?;
|
||||
// get new user pin
|
||||
let newpin1 = rpassword::read_password_from_tty(Some(
|
||||
"Enter new user PIN: ",
|
||||
))?;
|
||||
let newpin2 = rpassword::read_password_from_tty(Some(
|
||||
"Repeat the new user PIN: ",
|
||||
))?;
|
||||
|
||||
if newpin1 != newpin2 {
|
||||
return Err(anyhow::anyhow!("PINs do not match.").into());
|
||||
}
|
||||
if newpin1 != newpin2 {
|
||||
return Err(anyhow::anyhow!("PINs do not match.").into());
|
||||
}
|
||||
|
||||
// set new user pin
|
||||
open.change_user_pin(&pin, &newpin1)
|
||||
} else {
|
||||
// set new user pin via pinpad
|
||||
open.change_user_pin_pinpad(&|| {
|
||||
println!(
|
||||
"Enter old user PIN on card reader pinpad, \
|
||||
then new user PIN (twice)."
|
||||
)
|
||||
})
|
||||
};
|
||||
|
||||
// set new user pin
|
||||
let res = open.change_user_pin(&pin, &newpin1);
|
||||
if res.is_err() {
|
||||
println!("\nFailed to change the user PIN!");
|
||||
if let Err(err) = res {
|
||||
|
@ -50,37 +67,55 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
}
|
||||
cli::Command::SetAdminPin {} => {
|
||||
// get current admin pin
|
||||
let pin =
|
||||
rpassword::read_password_from_tty(Some("Enter admin PIN: "))?;
|
||||
if !pinpad_modify {
|
||||
// get current admin pin
|
||||
let pin = rpassword::read_password_from_tty(Some(
|
||||
"Enter admin PIN: ",
|
||||
))?;
|
||||
|
||||
// verify pin
|
||||
open.verify_admin(&pin)?;
|
||||
// verify pin
|
||||
open.verify_admin(&pin)?;
|
||||
|
||||
// get new admin pin
|
||||
let newpin1 = rpassword::read_password_from_tty(Some(
|
||||
"Enter new admin PIN: ",
|
||||
))?;
|
||||
let newpin2 = rpassword::read_password_from_tty(Some(
|
||||
"Repeat the new admin PIN: ",
|
||||
))?;
|
||||
// get new admin pin
|
||||
let newpin1 = rpassword::read_password_from_tty(Some(
|
||||
"Enter new admin PIN: ",
|
||||
))?;
|
||||
let newpin2 = rpassword::read_password_from_tty(Some(
|
||||
"Repeat the new admin PIN: ",
|
||||
))?;
|
||||
|
||||
if newpin1 != newpin2 {
|
||||
return Err(anyhow::anyhow!("PINs do not match.").into());
|
||||
if newpin1 != newpin2 {
|
||||
return Err(anyhow::anyhow!("PINs do not match.").into());
|
||||
}
|
||||
|
||||
// set new admin pin from input
|
||||
open.change_admin_pin(&pin, &newpin1)?;
|
||||
} else {
|
||||
// set new admin pin with pinpad
|
||||
open.change_admin_pin_pinpad(&|| {
|
||||
println!(
|
||||
"Enter old admin PIN on card reader pinpad, \
|
||||
then new admin PIN (twice)."
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
// set new user pin
|
||||
open.change_admin_pin(&pin, &newpin1)?;
|
||||
|
||||
println!("\nAdmin PIN has been set.");
|
||||
}
|
||||
cli::Command::SetResetCode {} => {
|
||||
// get current admin pin
|
||||
let pin =
|
||||
rpassword::read_password_from_tty(Some("Enter admin PIN: "))?;
|
||||
|
||||
// verify admin pin
|
||||
open.verify_admin(&pin)?;
|
||||
if !pinpad_verify {
|
||||
// get current admin pin
|
||||
let pin = rpassword::read_password_from_tty(Some(
|
||||
"Enter admin PIN: ",
|
||||
))?;
|
||||
|
||||
open.verify_admin(&pin)?;
|
||||
} else {
|
||||
open.verify_admin_pinpad(&|| {
|
||||
println!("Enter admin PIN on card reader pinpad.")
|
||||
})?;
|
||||
}
|
||||
println!("PIN was accepted by the card.\n");
|
||||
|
||||
if let Some(mut admin) = open.admin_card() {
|
||||
|
@ -93,7 +128,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
))?;
|
||||
|
||||
if newpin1 == newpin2 {
|
||||
admin.set_resetting_code(&pin)?;
|
||||
admin.set_resetting_code(&newpin1)?;
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("PINs do not match.").into());
|
||||
}
|
||||
|
@ -109,13 +144,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
// either with resetting code, or by presenting pw3
|
||||
|
||||
let rst = if admin {
|
||||
// get current admin pin
|
||||
let pin = rpassword::read_password_from_tty(Some(
|
||||
"Enter admin PIN: ",
|
||||
))?;
|
||||
if !pinpad_verify {
|
||||
// get current admin pin
|
||||
let pin = rpassword::read_password_from_tty(Some(
|
||||
"Enter admin PIN: ",
|
||||
))?;
|
||||
|
||||
// verify pin
|
||||
open.verify_admin(&pin)?;
|
||||
// verify pin
|
||||
open.verify_admin(&pin)?;
|
||||
} else {
|
||||
open.verify_admin_pinpad(&|| {
|
||||
println!("Enter admin PIN on card reader pinpad.")
|
||||
})?;
|
||||
}
|
||||
println!("PIN was accepted by the card.\n");
|
||||
|
||||
None
|
||||
|
|
|
@ -43,7 +43,7 @@ pub enum Command {
|
|||
ident: String,
|
||||
|
||||
#[structopt(name = "Admin PIN file", short = "P", long = "admin-pin")]
|
||||
admin_pin: PathBuf,
|
||||
admin_pin: Option<PathBuf>,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
cmd: AdminCommand,
|
||||
|
@ -53,7 +53,7 @@ pub enum Command {
|
|||
ident: String,
|
||||
|
||||
#[structopt(name = "User PIN file", short = "p", long = "user-pin")]
|
||||
user_pin: PathBuf,
|
||||
user_pin: Option<PathBuf>,
|
||||
|
||||
#[structopt(
|
||||
name = "recipient-cert-file",
|
||||
|
@ -70,7 +70,7 @@ pub enum Command {
|
|||
ident: String,
|
||||
|
||||
#[structopt(name = "User PIN file", short = "p", long = "user-pin")]
|
||||
user_pin: PathBuf,
|
||||
user_pin: Option<PathBuf>,
|
||||
|
||||
#[structopt(name = "detached", short = "d", long = "detached")]
|
||||
detached: bool,
|
||||
|
@ -129,7 +129,7 @@ pub enum AdminCommand {
|
|||
/// are optional.
|
||||
Generate {
|
||||
#[structopt(name = "User PIN file", short = "p", long = "user-pin")]
|
||||
user_pin: PathBuf,
|
||||
user_pin: Option<PathBuf>,
|
||||
|
||||
#[structopt(
|
||||
about = "Output file (stdout if unset)",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::path::{Path, PathBuf};
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
@ -43,7 +43,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
cert_file,
|
||||
input,
|
||||
} => {
|
||||
decrypt(&ident, &user_pin, &cert_file, input.as_deref())?;
|
||||
decrypt(&ident, user_pin, &cert_file, input.as_deref())?;
|
||||
}
|
||||
cli::Command::Sign {
|
||||
ident,
|
||||
|
@ -53,12 +53,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
input,
|
||||
} => {
|
||||
if detached {
|
||||
sign_detached(
|
||||
&ident,
|
||||
&user_pin,
|
||||
&cert_file,
|
||||
input.as_deref(),
|
||||
)?;
|
||||
sign_detached(&ident, user_pin, &cert_file, input.as_deref())?;
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Only detached signatures are supported for now"
|
||||
|
@ -79,12 +74,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
match cmd {
|
||||
cli::AdminCommand::Name { name } => {
|
||||
let mut admin = util::get_admin(&mut open, &admin_pin)?;
|
||||
let mut admin = util::get_admin(&mut open, admin_pin)?;
|
||||
|
||||
let _ = admin.set_name(&name)?;
|
||||
}
|
||||
cli::AdminCommand::Url { url } => {
|
||||
let mut admin = util::get_admin(&mut open, &admin_pin)?;
|
||||
let mut admin = util::get_admin(&mut open, admin_pin)?;
|
||||
|
||||
let _ = admin.set_url(&url)?;
|
||||
}
|
||||
|
@ -94,8 +89,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
dec_fp,
|
||||
auth_fp,
|
||||
} => {
|
||||
let admin = util::get_admin(&mut open, &admin_pin)?;
|
||||
let key = Cert::from_file(keyfile)?;
|
||||
let admin = util::get_admin(&mut open, admin_pin)?;
|
||||
|
||||
if (&sig_fp, &dec_fp, &auth_fp) == (&None, &None, &None) {
|
||||
// If no fingerprint has been provided, we check if
|
||||
|
@ -115,13 +110,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
no_auth,
|
||||
algo,
|
||||
} => {
|
||||
let pw3 = util::get_pin(&admin_pin)?;
|
||||
let pw1 = util::get_pin(&user_pin)?;
|
||||
|
||||
generate_keys(
|
||||
open,
|
||||
&pw3,
|
||||
&pw1,
|
||||
admin_pin,
|
||||
user_pin,
|
||||
output,
|
||||
!no_decrypt,
|
||||
!no_auth,
|
||||
|
@ -318,7 +310,7 @@ fn print_status(ident: Option<String>, verbose: bool) -> Result<()> {
|
|||
|
||||
fn decrypt(
|
||||
ident: &str,
|
||||
pin_file: &Path,
|
||||
pin_file: Option<PathBuf>,
|
||||
cert_file: &Path,
|
||||
input: Option<&Path>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
@ -343,7 +335,7 @@ fn decrypt(
|
|||
|
||||
fn sign_detached(
|
||||
ident: &str,
|
||||
pin_file: &Path,
|
||||
pin_file: Option<PathBuf>,
|
||||
cert_file: &Path,
|
||||
input: Option<&Path>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
@ -445,8 +437,8 @@ fn key_import_explicit(
|
|||
|
||||
fn generate_keys(
|
||||
mut open: Open,
|
||||
pw3: &str,
|
||||
pw1: &str,
|
||||
pw3_path: Option<PathBuf>,
|
||||
pw1_path: Option<PathBuf>,
|
||||
output: Option<PathBuf>,
|
||||
decrypt: bool,
|
||||
auth: bool,
|
||||
|
@ -484,23 +476,37 @@ fn generate_keys(
|
|||
|
||||
// 2) Then, generate keys on the card.
|
||||
// We need "admin" access to the card for this).
|
||||
|
||||
open.verify_admin(pw3)?;
|
||||
|
||||
let (key_sig, key_dec, key_aut) = {
|
||||
if let Some(mut admin) = open.admin_card() {
|
||||
if let Ok(mut admin) = util::get_admin(&mut open, pw3_path) {
|
||||
gen_subkeys(&mut admin, decrypt, auth, algos)?
|
||||
} else {
|
||||
// FIXME: couldn't get admin mode
|
||||
unimplemented!()
|
||||
return Err(anyhow!("Failed to open card in admin mode."));
|
||||
}
|
||||
};
|
||||
|
||||
// 3) Generate a Cert from the generated keys. For this, we
|
||||
// need "signing" access to the card (to make binding signatures within
|
||||
// the Cert).
|
||||
let pin = if let Some(pw1) = pw1_path {
|
||||
Some(util::get_pin(&pw1)?)
|
||||
} else {
|
||||
if open.feature_pinpad_verify() {
|
||||
println!();
|
||||
println!(
|
||||
"Next: generating your public cert. You will need to enter \
|
||||
your user PIN multiple times to make binding signatures."
|
||||
);
|
||||
} else {
|
||||
return Err(anyhow!(
|
||||
"No user PIN file provided, and no pinpad found"
|
||||
));
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
let cert = make_cert(&mut open, key_sig, key_dec, key_aut, pw1)?;
|
||||
let cert = make_cert(&mut open, key_sig, key_dec, key_aut, pin, &|| {
|
||||
println!("Enter user PIN on card reader pinpad.")
|
||||
})?;
|
||||
let armored = String::from_utf8(cert.armored().to_vec()?)?;
|
||||
|
||||
// Write armored certificate to the output file (or stdout)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use openpgp_card::{CardApp, Error};
|
||||
use openpgp_card_pcsc::PcscClient;
|
||||
use openpgp_card_sequoia::card::{Admin, Open, Sign, User};
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) fn cards() -> Result<Vec<CardApp>> {
|
||||
PcscClient::cards()
|
||||
|
@ -18,29 +18,70 @@ pub(crate) fn open_card(ident: &str) -> Result<CardApp, Error> {
|
|||
|
||||
pub(crate) fn get_user<'app, 'open>(
|
||||
open: &'app mut Open<'app>,
|
||||
pin_file: &Path,
|
||||
pin_file: Option<PathBuf>,
|
||||
) -> Result<User<'app, 'open>, Box<dyn std::error::Error>> {
|
||||
open.verify_user(&get_pin(pin_file)?)?;
|
||||
if let Some(path) = pin_file {
|
||||
open.verify_user(&get_pin(&path)?)?;
|
||||
} else {
|
||||
if !open.feature_pinpad_verify() {
|
||||
return Err(anyhow!(
|
||||
"No user PIN file provided, and no pinpad found"
|
||||
)
|
||||
.into());
|
||||
};
|
||||
|
||||
open.verify_user_pinpad(&|| {
|
||||
println!("Enter user PIN on card reader pinpad.")
|
||||
})?;
|
||||
}
|
||||
|
||||
open.user_card()
|
||||
.ok_or_else(|| anyhow::anyhow!("Couldn't get user access").into())
|
||||
.ok_or_else(|| anyhow!("Couldn't get user access").into())
|
||||
}
|
||||
|
||||
pub(crate) fn get_sign<'app, 'open>(
|
||||
open: &'app mut Open<'app>,
|
||||
pin_file: &Path,
|
||||
pin_file: Option<PathBuf>,
|
||||
) -> Result<Sign<'app, 'open>, Box<dyn std::error::Error>> {
|
||||
open.verify_user_for_signing(&get_pin(pin_file)?)?;
|
||||
if let Some(path) = pin_file {
|
||||
open.verify_user_for_signing(&get_pin(&path)?)?;
|
||||
} else {
|
||||
if !open.feature_pinpad_verify() {
|
||||
return Err(anyhow!(
|
||||
"No user PIN file provided, and no pinpad found"
|
||||
)
|
||||
.into());
|
||||
}
|
||||
open.verify_user_for_signing_pinpad(&|| {
|
||||
println!("Enter user PIN on card reader pinpad.")
|
||||
})?;
|
||||
}
|
||||
open.signing_card()
|
||||
.ok_or_else(|| anyhow::anyhow!("Couldn't get sign access").into())
|
||||
.ok_or_else(|| anyhow!("Couldn't get sign access").into())
|
||||
}
|
||||
|
||||
// pub fn admin_card<'b>(&'b mut self) -> Option<Admin<'a, 'b>> {
|
||||
|
||||
pub(crate) fn get_admin<'app, 'open>(
|
||||
open: &'app mut Open<'app>,
|
||||
pin_file: &Path,
|
||||
open: &'open mut Open<'app>,
|
||||
pin_file: Option<PathBuf>,
|
||||
) -> Result<Admin<'app, 'open>, Box<dyn std::error::Error>> {
|
||||
open.verify_admin(&get_pin(pin_file)?)?;
|
||||
if let Some(path) = pin_file {
|
||||
open.verify_admin(&get_pin(&path)?)?;
|
||||
} else {
|
||||
if !open.feature_pinpad_verify() {
|
||||
return Err(anyhow!(
|
||||
"No admin PIN file provided, and no pinpad found"
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
open.verify_admin_pinpad(&|| {
|
||||
println!("Enter admin PIN on card reader pinpad.")
|
||||
})?;
|
||||
}
|
||||
open.admin_card()
|
||||
.ok_or_else(|| anyhow::anyhow!("Couldn't get admin access").into())
|
||||
.ok_or_else(|| anyhow!("Couldn't get admin access").into())
|
||||
}
|
||||
|
||||
pub(crate) fn get_pin(pin_file: &Path) -> Result<String> {
|
||||
|
|
Loading…
Reference in a new issue