Documentation edits.

This commit is contained in:
Heiko Schaefer 2021-12-02 17:27:27 +01:00
parent ddcd888834
commit 1dc178a7b2
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
5 changed files with 80 additions and 61 deletions

View file

@ -311,6 +311,7 @@ impl EccAttrs {
} }
} }
/// Enum for naming ECC curves, and mapping them to/from their OIDs.
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[non_exhaustive] #[non_exhaustive]
pub enum Curve { pub enum Curve {

View file

@ -258,8 +258,8 @@ impl CardApp {
/// Set identity (Nitrokey Start specific (?)). /// Set identity (Nitrokey Start specific (?)).
/// [see: /// [see:
/// https://docs.nitrokey.com/start/linux/multiple-identities.html /// <https://docs.nitrokey.com/start/linux/multiple-identities.html>
/// https://github.com/Nitrokey/nitrokey-start-firmware/pull/33/] /// <https://github.com/Nitrokey/nitrokey-start-firmware/pull/33/>]
pub fn set_identity(&mut self, id: u8) -> Result<Vec<u8>> { pub fn set_identity(&mut self, id: u8) -> Result<Vec<u8>> {
let resp = apdu::send_command( let resp = apdu::send_command(
self.card_client(), self.card_client(),
@ -276,8 +276,8 @@ impl CardApp {
} }
} }
/// SELECT DATA "select a DO in the current template" /// SELECT DATA ("select a DO in the current template",
/// (e.g. for cardholder certificate) /// e.g. for cardholder certificate)
pub fn select_data( pub fn select_data(
&mut self, &mut self,
num: u8, num: u8,
@ -299,11 +299,18 @@ impl CardApp {
/// Reset all state on this OpenPGP card. /// Reset all state on this OpenPGP card.
/// ///
/// Note: the "factory reset" operation is not directly offered by the /// Note: the "factory reset" operation is not directly offered by the
/// card. It is implemented as a series of OpenPGP card commands: /// card spec. It is implemented as a series of OpenPGP card commands:
/// - send 4 bad requests to verify pw1 /// - send 4 bad requests to verify pw1,
/// - send 4 bad requests to verify pw3 /// - send 4 bad requests to verify pw3,
/// - terminate_df /// - terminate_df,
/// - activate_file /// - activate_file.
///
/// With most cards, this sequence of operations causes the card
/// to revert to a "blank" state.
///
/// (However, e.g. vanilla Gnuk doesn't support this functionality.
/// Gnuk needs to be built with the `--enable-factory-reset`
/// option to the `configure` script to enable this functionality).
pub fn factory_reset(&mut self) -> Result<()> { pub fn factory_reset(&mut self) -> Result<()> {
// send 4 bad requests to verify pw1 // send 4 bad requests to verify pw1
// [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]
@ -345,7 +352,7 @@ impl CardApp {
Ok(()) Ok(())
} }
// ---------- // --- verify/modify ---
/// Does the cardreader support direct pinpad verify? /// Does the cardreader support direct pinpad verify?
pub fn feature_pinpad_verify(&self) -> bool { pub fn feature_pinpad_verify(&self) -> bool {
@ -357,8 +364,7 @@ impl CardApp {
self.card_client.feature_pinpad_modify() self.card_client.feature_pinpad_modify()
} }
/// Verify pw1 (user) for signing operation (mode 81) and set an /// Verify pw1 (user) for signing operation (mode 81).
/// appropriate access status.
/// ///
/// Depending on the PW1 status byte (see Extended Capabilities) this /// Depending on the PW1 status byte (see Extended Capabilities) this
/// access condition is only valid for one PSO:CDS command or remains /// access condition is only valid for one PSO:CDS command or remains
@ -371,9 +377,9 @@ impl CardApp {
apdu::send_command(self.card_client(), verify, false)?.try_into() apdu::send_command(self.card_client(), verify, false)?.try_into()
} }
/// Verify pw1 (user) for signing operation (mode 81) and set an /// Verify pw1 (user) for signing operation (mode 81) using a
/// appropriate access status. This fn uses a pinpad on the card reader, /// pinpad on the card reader. If no usable pinpad is found, an error
/// if no usable pinpad is found, an error is returned. /// is returned.
/// ///
/// Depending on the PW1 status byte (see Extended Capabilities) this /// Depending on the PW1 status byte (see Extended Capabilities) this
/// access condition is only valid for one PSO:CDS command or remains /// access condition is only valid for one PSO:CDS command or remains
@ -390,23 +396,23 @@ impl CardApp {
/// If verification is not required, an empty Ok Response is returned. /// If verification is not required, an empty Ok Response is returned.
/// ///
/// (Note: some cards don't correctly implement this feature, /// (Note: some cards don't correctly implement this feature,
/// 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(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).
/// (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(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) for operations except signing (mode 82),
/// (For operations except signing, mode 82). /// using a pinpad on the card reader. If no usable pinpad is found,
/// This fn uses a pinpad on the card reader, if no usable pinpad is /// an error is returned.
/// found, an error is returned.
pub fn verify_pw1_pinpad(&mut self) -> Result<Response, Error> { pub fn verify_pw1_pinpad(&mut self) -> Result<Response, Error> {
let res = self.card_client.pinpad_verify(0x82)?; let res = self.card_client.pinpad_verify(0x82)?;
RawResponse::try_from(res)?.try_into() RawResponse::try_from(res)?.try_into()
@ -418,21 +424,20 @@ impl CardApp {
/// If verification is not required, an empty Ok Response is returned. /// If verification is not required, an empty Ok Response is returned.
/// ///
/// (Note: some cards don't correctly implement this feature, /// (Note: some cards don't correctly implement this feature,
/// 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(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).
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(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) using a pinpad on the card reader. If no usable
/// This fn uses a pinpad on the card reader, if no usable pinpad is /// pinpad is found, an error is returned.
/// found, an error is returned.
pub fn verify_pw3_pinpad(&mut self) -> Result<Response, Error> { pub fn verify_pw3_pinpad(&mut self) -> Result<Response, Error> {
let res = self.card_client.pinpad_verify(0x83)?; let res = self.card_client.pinpad_verify(0x83)?;
RawResponse::try_from(res)?.try_into() RawResponse::try_from(res)?.try_into()
@ -443,7 +448,7 @@ impl CardApp {
/// If verification is not required, an empty Ok Response is returned. /// If verification is not required, an empty Ok Response is returned.
/// ///
/// (Note: some cards don't correctly implement this feature, /// (Note: some cards don't correctly implement this feature,
/// 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(self.card_client(), verify, false)?.try_into() apdu::send_command(self.card_client(), verify, false)?.try_into()
@ -465,9 +470,8 @@ impl CardApp {
apdu::send_command(self.card_client(), change, false)?.try_into() apdu::send_command(self.card_client(), change, false)?.try_into()
} }
/// Change the value of PW1 (user password). /// Change the value of PW1 (user password) using a pinpad on the
/// This fn uses a pinpad on the card reader, if no usable pinpad is /// card reader. If no usable pinpad is found, an error is returned.
/// found, an error is returned.
pub fn change_pw1_pinpad(&mut self) -> Result<Response, Error> { pub fn change_pw1_pinpad(&mut self) -> Result<Response, Error> {
let res = self.card_client.pinpad_modify(0x81)?; let res = self.card_client.pinpad_modify(0x81)?;
RawResponse::try_from(res)?.try_into() RawResponse::try_from(res)?.try_into()
@ -489,9 +493,8 @@ impl CardApp {
apdu::send_command(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) using a pinpad on the
/// This fn uses a pinpad on the card reader, if no usable pinpad is /// card reader. If no usable pinpad is found, an error is returned.
/// found, an error is returned.
pub fn change_pw3_pinpad(&mut self) -> Result<Response, Error> { pub fn change_pw3_pinpad(&mut self) -> Result<Response, Error> {
let res = self.card_client.pinpad_modify(0x83)?; let res = self.card_client.pinpad_modify(0x83)?;
RawResponse::try_from(res)?.try_into() RawResponse::try_from(res)?.try_into()
@ -851,10 +854,12 @@ impl CardApp {
/// Get public key material from the card. /// Get public key material from the card.
/// ///
/// Note: this fn returns an uninterpreted set of raw values (not an /// Note: this fn returns a set of raw public key data (not an
/// OpenPGP key data structure). /// OpenPGP data structure).
/// This data from the card is insufficient to create a typical ///
/// full public key. /// Note also that the information from the card is insufficient to
/// reconstruct a pre-existing OpenPGP public key that corresponds to
/// the private key on the card.
pub fn public_key( pub fn public_key(
&mut self, &mut self,
key_type: KeyType, key_type: KeyType,

View file

@ -12,9 +12,9 @@
//! - without relying on a particular //! - without relying on a particular
//! [OpenPGP implementation](https://www.openpgp.org/software/developer/). //! [OpenPGP implementation](https://www.openpgp.org/software/developer/).
//! //!
//! This library doesn't itself implement a means to access cards. Instead, //! This library can't directly access cards by itself. Instead, users
//! users need to supply an implementation of the [`CardClient`] trait, for //! need to supply an implementation of the [`CardClient`] trait, to
//! access to cards. //! access cards.
//! //!
//! The companion crate //! The companion crate
//! [openpgp-card-pcsc](https://crates.io/crates/openpgp-card-pcsc) //! [openpgp-card-pcsc](https://crates.io/crates/openpgp-card-pcsc)
@ -84,10 +84,10 @@ pub trait CardClient {
/// Does the reader support FEATURE_MODIFY_PIN_DIRECT? /// Does the reader support FEATURE_MODIFY_PIN_DIRECT?
fn feature_pinpad_modify(&self) -> bool; fn feature_pinpad_modify(&self) -> bool;
/// Verify the PIN 'id' via the reader pinpad /// Verify the PIN `id` via the reader pinpad
fn pinpad_verify(&mut self, id: u8) -> Result<Vec<u8>>; fn pinpad_verify(&mut self, id: u8) -> Result<Vec<u8>>;
/// Modify the PIN 'id' via the reader pinpad /// Modify the PIN `id` via the reader pinpad
fn pinpad_modify(&mut self, id: u8) -> Result<Vec<u8>>; fn pinpad_modify(&mut self, id: u8) -> Result<Vec<u8>>;
} }
@ -102,7 +102,7 @@ impl dyn CardClient {
} }
} }
/// Configuration of the capabilities of the card. /// Configuration of the capabilities of a 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
/// length can be used when communicating with the card. /// length can be used when communicating with the card.

View file

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021 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`
//! crate, which uses the PCSC lite middleware to access the OpenPGP
//! application on smart cards.
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use iso7816_tlv::simple::Tlv; use iso7816_tlv::simple::Tlv;
use pcsc::{Card, Context, Protocols, Scope, ShareMode}; use pcsc::{Card, Context, Protocols, Scope, ShareMode};
@ -12,6 +16,8 @@ use openpgp_card::{CardApp, 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;
/// An implementation of the CardClient trait that uses the PCSC lite
/// middleware to access the OpenPGP card application on smart cards.
pub struct PcscClient { pub struct PcscClient {
card: Card, card: Card,
card_caps: Option<CardCaps>, card_caps: Option<CardCaps>,

View file

@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
//! This crate provides `ScdClient`, which is an implementation of the //! This crate implements the experimental `ScdClient` backend for the
//! CardClient trait that uses GnuPG's scdaemon to access OpenPGP cards. //! `openpgp-card` crate.
//! To access scdaemon, GnuPG Agent is used. //! It uses GnuPG's scdaemon (via GnuPG Agent) to access OpenPGP cards.
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use futures::StreamExt; use futures::StreamExt;
@ -49,23 +49,14 @@ const ASSUAN_LINELENGTH: usize = 1000;
/// In particular, uploading rsa4096 keys fails via scdaemon, with such cards. /// In particular, uploading rsa4096 keys fails via scdaemon, with such cards.
const APDU_CMD_BYTES_MAX: usize = (ASSUAN_LINELENGTH - 25) / 2; const APDU_CMD_BYTES_MAX: usize = (ASSUAN_LINELENGTH - 25) / 2;
/// An implementation of the CardClient trait that uses GnuPG's scdaemon
/// (via GnuPG Agent) to access OpenPGP card devices.
pub struct ScdClient { pub struct ScdClient {
agent: Agent, agent: Agent,
card_caps: Option<CardCaps>, card_caps: Option<CardCaps>,
} }
impl ScdClient { impl ScdClient {
/// Open a CardApp that uses an scdaemon instance as its backend.
///
/// If multiple cards are available, scdaemon implicitly selects one.
/// (NOTE: implicitly picking some card seems like a bad idea. You might
/// want to avoid using this fn.)
pub fn open_yolo(agent: Option<Agent>) -> Result<CardApp, Error> {
let card = ScdClient::new(agent, true)?;
Ok(CardApp::initialize(Box::new(card))?)
}
/// Open a CardApp that uses an scdaemon instance as its backend. /// Open a CardApp that uses an scdaemon instance as its backend.
/// The specific card with AID `serial` is requested from scdaemon. /// The specific card with AID `serial` is requested from scdaemon.
pub fn open_by_serial( pub fn open_by_serial(
@ -78,6 +69,18 @@ impl ScdClient {
Ok(CardApp::initialize(Box::new(card))?) Ok(CardApp::initialize(Box::new(card))?)
} }
/// Open a CardApp that uses an scdaemon instance as its backend.
///
/// If multiple cards are available, scdaemon implicitly selects one.
///
/// (NOTE: implicitly picking an unspecified card might be a bad idea.
/// You might want to avoid using this function.)
pub fn open_yolo(agent: Option<Agent>) -> Result<CardApp, Error> {
let card = ScdClient::new(agent, true)?;
Ok(CardApp::initialize(Box::new(card))?)
}
/// Helper fn that shuts down scdaemon via GnuPG Agent. /// Helper fn that shuts down scdaemon via GnuPG Agent.
/// This may be useful to obtain access to a Smard card via PCSC. /// This may be useful to obtain access to a Smard card via PCSC.
pub fn shutdown_scd(agent: Option<Agent>) -> Result<()> { pub fn shutdown_scd(agent: Option<Agent>) -> Result<()> {
@ -263,19 +266,23 @@ impl CardClient for ScdClient {
Some(APDU_CMD_BYTES_MAX) Some(APDU_CMD_BYTES_MAX)
} }
/// FIXME: not implemented yet
fn feature_pinpad_verify(&self) -> bool { fn feature_pinpad_verify(&self) -> bool {
false // FIXME false
} }
/// FIXME: not implemented yet
fn feature_pinpad_modify(&self) -> bool { fn feature_pinpad_modify(&self) -> bool {
false // FIXME false
} }
/// FIXME: not implemented yet
fn pinpad_verify(&mut self, _id: u8) -> Result<Vec<u8>> { fn pinpad_verify(&mut self, _id: u8) -> Result<Vec<u8>> {
unimplemented!() // FIXME unimplemented!()
} }
/// FIXME: not implemented yet
fn pinpad_modify(&mut self, _id: u8) -> Result<Vec<u8>> { fn pinpad_modify(&mut self, _id: u8) -> Result<Vec<u8>> {
unimplemented!() // FIXME unimplemented!()
} }
} }