From 1dc178a7b2a3a133de688ff5b7a4ef127921d3f2 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Thu, 2 Dec 2021 17:27:27 +0100 Subject: [PATCH] Documentation edits. --- openpgp-card/src/algorithm.rs | 1 + openpgp-card/src/card_app.rs | 79 +++++++++++++++++++---------------- openpgp-card/src/lib.rs | 12 +++--- pcsc/src/lib.rs | 6 +++ scdc/src/lib.rs | 43 +++++++++++-------- 5 files changed, 80 insertions(+), 61 deletions(-) diff --git a/openpgp-card/src/algorithm.rs b/openpgp-card/src/algorithm.rs index cc709ce..fe3045b 100644 --- a/openpgp-card/src/algorithm.rs +++ b/openpgp-card/src/algorithm.rs @@ -311,6 +311,7 @@ impl EccAttrs { } } +/// Enum for naming ECC curves, and mapping them to/from their OIDs. #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub enum Curve { diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index 48c6076..e44ce2d 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -258,8 +258,8 @@ impl CardApp { /// Set identity (Nitrokey Start specific (?)). /// [see: - /// https://docs.nitrokey.com/start/linux/multiple-identities.html - /// https://github.com/Nitrokey/nitrokey-start-firmware/pull/33/] + /// + /// ] pub fn set_identity(&mut self, id: u8) -> Result> { let resp = apdu::send_command( self.card_client(), @@ -276,8 +276,8 @@ impl CardApp { } } - /// SELECT DATA "select a DO in the current template" - /// (e.g. for cardholder certificate) + /// SELECT DATA ("select a DO in the current template", + /// e.g. for cardholder certificate) pub fn select_data( &mut self, num: u8, @@ -299,11 +299,18 @@ impl CardApp { /// Reset all state on this OpenPGP card. /// /// Note: the "factory reset" operation is not directly offered by the - /// card. It is implemented as a series of OpenPGP card commands: - /// - send 4 bad requests to verify pw1 - /// - send 4 bad requests to verify pw3 - /// - terminate_df - /// - activate_file + /// 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 pw3, + /// - terminate_df, + /// - 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<()> { // send 4 bad requests to verify pw1 // [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40] @@ -345,7 +352,7 @@ impl CardApp { Ok(()) } - // ---------- + // --- verify/modify --- /// Does the cardreader support direct pinpad verify? pub fn feature_pinpad_verify(&self) -> bool { @@ -357,8 +364,7 @@ impl CardApp { self.card_client.feature_pinpad_modify() } - /// Verify pw1 (user) for signing operation (mode 81) and set an - /// appropriate access status. + /// Verify pw1 (user) for signing operation (mode 81). /// /// Depending on the PW1 status byte (see Extended Capabilities) this /// 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() } - /// 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. + /// Verify pw1 (user) for signing operation (mode 81) using 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 @@ -390,23 +396,23 @@ impl CardApp { /// If verification is not required, an empty Ok Response is returned. /// /// (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 { let verify = commands::verify_pw1_81(vec![]); 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). pub fn verify_pw1(&mut self, pin: &str) -> Result { let verify = commands::verify_pw1_82(pin.as_bytes().to_vec()); 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. + /// Verify PW1 (user) for operations except signing (mode 82), + /// using a pinpad on the card reader. If no usable pinpad is found, + /// an error is returned. + pub fn verify_pw1_pinpad(&mut self) -> Result { let res = self.card_client.pinpad_verify(0x82)?; RawResponse::try_from(res)?.try_into() @@ -418,21 +424,20 @@ impl CardApp { /// If verification is not required, an empty Ok Response is returned. /// /// (Note: some cards don't correctly implement this feature, - /// e.g. yubikey 5) + /// e.g. YubiKey 5) pub fn check_pw1(&mut self) -> Result { let verify = commands::verify_pw1_82(vec![]); 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 { let verify = commands::verify_pw3(pin.as_bytes().to_vec()); 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. + /// Verify PW3 (admin) using a pinpad on the card reader. If no usable + /// pinpad is found, an error is returned. pub fn verify_pw3_pinpad(&mut self) -> Result { let res = self.card_client.pinpad_verify(0x83)?; RawResponse::try_from(res)?.try_into() @@ -443,7 +448,7 @@ impl CardApp { /// If verification is not required, an empty Ok Response is returned. /// /// (Note: some cards don't correctly implement this feature, - /// e.g. yubikey 5) + /// e.g. YubiKey 5) pub fn check_pw3(&mut self) -> Result { let verify = commands::verify_pw3(vec![]); 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() } - /// 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. + /// Change the value of PW1 (user password) using a pinpad on the + /// card reader. If no usable pinpad is found, an error is returned. pub fn change_pw1_pinpad(&mut self) -> Result { let res = self.card_client.pinpad_modify(0x81)?; RawResponse::try_from(res)?.try_into() @@ -489,9 +493,8 @@ 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. + /// Change the value of PW3 (admin password) using a pinpad on the + /// card reader. If no usable pinpad is found, an error is returned. pub fn change_pw3_pinpad(&mut self) -> Result { let res = self.card_client.pinpad_modify(0x83)?; RawResponse::try_from(res)?.try_into() @@ -851,10 +854,12 @@ impl CardApp { /// Get public key material from the card. /// - /// Note: this fn returns an uninterpreted set of raw values (not an - /// OpenPGP key data structure). - /// This data from the card is insufficient to create a typical - /// full public key. + /// Note: this fn returns a set of raw public key data (not an + /// OpenPGP data structure). + /// + /// 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( &mut self, key_type: KeyType, diff --git a/openpgp-card/src/lib.rs b/openpgp-card/src/lib.rs index c2da8ed..180fedb 100644 --- a/openpgp-card/src/lib.rs +++ b/openpgp-card/src/lib.rs @@ -12,9 +12,9 @@ //! - without relying on a particular //! [OpenPGP implementation](https://www.openpgp.org/software/developer/). //! -//! This library doesn't itself implement a means to access cards. Instead, -//! users need to supply an implementation of the [`CardClient`] trait, for -//! access to cards. +//! This library can't directly access cards by itself. Instead, users +//! need to supply an implementation of the [`CardClient`] trait, to +//! access cards. //! //! The companion crate //! [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? 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>; - /// Modify the PIN 'id' via the reader pinpad + /// Modify the PIN `id` via the reader pinpad fn pinpad_modify(&mut self, id: u8) -> Result>; } @@ -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 /// length can be used when communicating with the card. diff --git a/pcsc/src/lib.rs b/pcsc/src/lib.rs index e02d0fc..837bb43 100644 --- a/pcsc/src/lib.rs +++ b/pcsc/src/lib.rs @@ -1,6 +1,10 @@ // SPDX-FileCopyrightText: 2021 Heiko Schaefer // 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 iso7816_tlv::simple::Tlv; 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_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 { card: Card, card_caps: Option, diff --git a/scdc/src/lib.rs b/scdc/src/lib.rs index cfe91eb..1eba7d8 100644 --- a/scdc/src/lib.rs +++ b/scdc/src/lib.rs @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: 2021 Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 -//! This crate provides `ScdClient`, which is an implementation of the -//! CardClient trait that uses GnuPG's scdaemon to access OpenPGP cards. -//! To access scdaemon, GnuPG Agent is used. +//! This crate implements the experimental `ScdClient` backend for the +//! `openpgp-card` crate. +//! It uses GnuPG's scdaemon (via GnuPG Agent) to access OpenPGP cards. use anyhow::{anyhow, Result}; use futures::StreamExt; @@ -49,23 +49,14 @@ const ASSUAN_LINELENGTH: usize = 1000; /// In particular, uploading rsa4096 keys fails via scdaemon, with such cards. 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 { agent: Agent, card_caps: Option, } 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) -> Result { - let card = ScdClient::new(agent, true)?; - - Ok(CardApp::initialize(Box::new(card))?) - } - /// Open a CardApp that uses an scdaemon instance as its backend. /// The specific card with AID `serial` is requested from scdaemon. pub fn open_by_serial( @@ -78,6 +69,18 @@ impl ScdClient { 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) -> Result { + let card = ScdClient::new(agent, true)?; + + Ok(CardApp::initialize(Box::new(card))?) + } + /// Helper fn that shuts down scdaemon via GnuPG Agent. /// This may be useful to obtain access to a Smard card via PCSC. pub fn shutdown_scd(agent: Option) -> Result<()> { @@ -263,19 +266,23 @@ impl CardClient for ScdClient { Some(APDU_CMD_BYTES_MAX) } + /// FIXME: not implemented yet fn feature_pinpad_verify(&self) -> bool { - false // FIXME + false } + /// FIXME: not implemented yet fn feature_pinpad_modify(&self) -> bool { - false // FIXME + false } + /// FIXME: not implemented yet fn pinpad_verify(&mut self, _id: u8) -> Result> { - unimplemented!() // FIXME + unimplemented!() } + /// FIXME: not implemented yet fn pinpad_modify(&mut self, _id: u8) -> Result> { - unimplemented!() // FIXME + unimplemented!() } }