diff --git a/openpgp-card-examples/src/bin/decrypt.rs b/openpgp-card-examples/src/bin/decrypt.rs index a0d5910..7e5c174 100644 --- a/openpgp-card-examples/src/bin/decrypt.rs +++ b/openpgp-card-examples/src/bin/decrypt.rs @@ -24,9 +24,7 @@ fn main() -> Result<(), Box> { let pin = std::fs::read(pin_file)?; - transaction.verify_user(&pin)?; - - let mut user = transaction.user_card().unwrap(); + let mut user = transaction.to_user_card(&pin)?; let p = StandardPolicy::new(); diff --git a/openpgp-card-examples/src/bin/detach-sign.rs b/openpgp-card-examples/src/bin/detach-sign.rs index f0acc11..01fc84d 100644 --- a/openpgp-card-examples/src/bin/detach-sign.rs +++ b/openpgp-card-examples/src/bin/detach-sign.rs @@ -23,9 +23,7 @@ fn main() -> Result<(), Box> { let pin = std::fs::read(pin_file)?; - transaction.verify_user_for_signing(&pin)?; - - let mut sign = transaction.signing_card().unwrap(); + let mut sign = transaction.to_signing_card(&pin)?; let s = sign.signer(&|| println!("Touch confirmation needed for signing"))?; let stdout = std::io::stdout(); diff --git a/openpgp-card-sequoia/examples/test.rs b/openpgp-card-sequoia/examples/test.rs index bbc521d..4c3059c 100644 --- a/openpgp-card-sequoia/examples/test.rs +++ b/openpgp-card-sequoia/examples/test.rs @@ -100,7 +100,7 @@ fn main() -> Result<(), Box> { println!("has user (pw1/82) been verified yet? {check:x?}"); // Use Admin access to card - let mut admin = transaction.admin_card().expect("just verified"); + let mut admin = transaction.to_admin_card(None).expect("just verified"); println!(); @@ -160,7 +160,7 @@ fn main() -> Result<(), Box> { // Use User access to card let mut user = transaction - .user_card() + .to_user_card(None) .expect("We just validated, this should not fail"); let _cert = Cert::from_file(TEST_KEY_PATH)?; @@ -191,7 +191,7 @@ fn main() -> Result<(), Box> { println!("verify for sign (pw1/81) ok\n"); // Use Sign access to card - let mut sign = transaction.signing_card().expect("just verified"); + let mut sign = transaction.to_signing_card(None).expect("just verified"); let _cert = Cert::from_file(TEST_KEY_PATH)?; diff --git a/openpgp-card-sequoia/src/lib.rs b/openpgp-card-sequoia/src/lib.rs index 2a3accc..98ff55f 100644 --- a/openpgp-card-sequoia/src/lib.rs +++ b/openpgp-card-sequoia/src/lib.rs @@ -69,9 +69,8 @@ //! let mut card = Card::::open_by_ident(cards, "abcd:01234567")?; //! let mut transaction = card.transaction()?; //! -//! // Get authorization for user access to the card with password -//! transaction.verify_user(b"123456")?; -//! let mut user = transaction.user_card().expect("This should not fail"); +//! // Get user access to the card (and authorize with the user pin) +//! let mut user = transaction.to_user_card("123456")?; //! //! // Get decryptor //! let decryptor = user.decryptor(&|| println!("Touch confirmation needed for decryption")); @@ -104,9 +103,8 @@ //! let mut card = Card::::open_by_ident(cards, "abcd:01234567")?; //! let mut transaction = card.transaction()?; //! -//! // Get authorization for signing access to the card with password -//! transaction.verify_user_for_signing(b"123456")?; -//! let mut user = transaction.signing_card().expect("This should not fail"); +//! // Get signing access to the card (and authorize with the user pin) +//! let mut user = transaction.to_signing_card("123456")?; //! //! // Get signer //! let signer = user.signer(&|| println!("Touch confirmation needed for signing")); @@ -130,9 +128,8 @@ //! let mut card = Card::::open_by_ident(cards, "abcd:01234567")?; //! let mut transaction = card.transaction()?; //! -//! // Get authorization for admin access to the card with password -//! transaction.verify_admin(b"12345678")?; -//! let mut admin = transaction.admin_card().expect("This should not fail"); +//! // Get admin access to the card (and authorize with the admin pin) +//! let mut admin = transaction.to_admin_card("12345678")?; //! //! // Set the Name and URL fields on the card //! admin.set_name("Alice Adams")?; @@ -174,6 +171,36 @@ pub mod util; /// Shorthand for Sequoia public key data (a single public (sub)key) pub type PublicKey = Key; +/// Optional PIN, used as a parameter to `Card::into_*_card`. +/// +/// Effectively acts like a `Option<&[u8]>`, but with a number of `From` +/// implementations for convenience. +pub struct OptionalPin<'p>(Option<&'p [u8]>); + +impl<'p> From> for OptionalPin<'p> { + fn from(value: Option<&'p [u8]>) -> Self { + OptionalPin(value) + } +} + +impl<'p> From<&'p str> for OptionalPin<'p> { + fn from(value: &'p str) -> Self { + OptionalPin(Some(value.as_bytes())) + } +} + +impl<'p> From<&'p Vec> for OptionalPin<'p> { + fn from(value: &'p Vec) -> Self { + OptionalPin(Some(value)) + } +} + +impl<'p, const S: usize> From<&'p [u8; S]> for OptionalPin<'p> { + fn from(value: &'p [u8; S]) -> Self { + OptionalPin(Some(value)) + } +} + /// Representation of an OpenPGP card. /// /// A card transitions between `State`s by starting a transaction (that groups together a number @@ -362,22 +389,55 @@ impl<'a> Card> { } /// Get a view of the card authenticated for "User" commands. - pub fn user_card<'b>(&'b mut self) -> Option>> { - Some(Card:: { + /// + /// If `pin` is not None, `verify_user` is called with that pin. + pub fn to_user_card<'b, 'p, P>(&'b mut self, pin: P) -> Result>, Error> + where + P: Into>, + { + let pin: OptionalPin = pin.into(); + + if let Some(pin) = pin.0 { + self.verify_user(pin)?; + } + + Ok(Card:: { state: User { tx: self }, }) } /// Get a view of the card authenticated for Signing. - pub fn signing_card<'b>(&'b mut self) -> Option>> { - Some(Card:: { + /// + /// If `pin` is not None, `verify_user_for_signing` is called with that pin. + pub fn to_signing_card<'b, 'p, P>(&'b mut self, pin: P) -> Result>, Error> + where + P: Into>, + { + let pin: OptionalPin = pin.into(); + + if let Some(pin) = pin.0 { + self.verify_user_for_signing(pin)?; + } + + Ok(Card:: { state: Sign { tx: self }, }) } /// Get a view of the card authenticated for "Admin" commands. - pub fn admin_card<'b>(&'b mut self) -> Option>> { - Some(Card:: { + /// + /// If `pin` is not None, `verify_admin` is called with that pin. + pub fn to_admin_card<'b, 'p, P>(&'b mut self, pin: P) -> Result>, Error> + where + P: Into>, + { + let pin: OptionalPin = pin.into(); + + if let Some(pin) = pin.0 { + self.verify_admin(pin)?; + } + + Ok(Card:: { state: Admin { tx: self }, }) } diff --git a/openpgp-card-sequoia/src/util.rs b/openpgp-card-sequoia/src/util.rs index 5583187..f51415d 100644 --- a/openpgp-card-sequoia/src/util.rs +++ b/openpgp-card-sequoia/src/util.rs @@ -6,7 +6,7 @@ use std::convert::TryFrom; use std::convert::TryInto; -use anyhow::{anyhow, Result}; +use anyhow::Result; use openpgp_card::algorithm::{Algo, Curve}; use openpgp_card::card_do::{Fingerprint, KeyGenerationTime}; use openpgp_card::crypto_data::{CardUploadableKey, PublicKeyMaterial}; @@ -58,16 +58,14 @@ pub fn make_cert( } else { open.verify_user_for_signing_pinpad(pinpad_prompt)?; } - if let Some(mut sign) = open.signing_card() { - // Card-backed signer for bindings - let mut card_signer = sign.signer_from_public(key_sig.clone(), touch_prompt); + let mut sign = open.to_signing_card(None)?; - // Make signature, return it - let s = op(&mut card_signer)?; - Ok(s) - } else { - Err(anyhow!("Failed to open card for signing")) - } + // Card-backed signer for bindings + let mut card_signer = sign.signer_from_public(key_sig.clone(), touch_prompt); + + // Make signature, return it + let s = op(&mut card_signer)?; + Ok::(s) }; // 1) use the signing key as primary key