openpgp-card-sequoia: Transaction::to_*_card() methods

Allow more ergonomic switching to User/Sign/Admin states by directly providing a PIN, while also allowing a `None` parameter if verification has already happened.
This commit is contained in:
Heiko Schaefer 2023-08-15 18:29:45 +02:00
parent 8d5b1c0563
commit 212e7f335f
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
5 changed files with 88 additions and 34 deletions

View file

@ -24,9 +24,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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();

View file

@ -23,9 +23,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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();

View file

@ -100,7 +100,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
// 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<dyn Error>> {
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)?;

View file

@ -69,9 +69,8 @@
//! let mut card = Card::<Open>::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>::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>::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<key::PublicParts, key::UnspecifiedRole>;
/// Optional PIN, used as a parameter to `Card<Transaction>::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<Option<&'p [u8]>> 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<u8>> for OptionalPin<'p> {
fn from(value: &'p Vec<u8>) -> 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<Transaction<'a>> {
}
/// Get a view of the card authenticated for "User" commands.
pub fn user_card<'b>(&'b mut self) -> Option<Card<User<'a, 'b>>> {
Some(Card::<User> {
///
/// 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<Card<User<'a, 'b>>, Error>
where
P: Into<OptionalPin<'p>>,
{
let pin: OptionalPin = pin.into();
if let Some(pin) = pin.0 {
self.verify_user(pin)?;
}
Ok(Card::<User> {
state: User { tx: self },
})
}
/// Get a view of the card authenticated for Signing.
pub fn signing_card<'b>(&'b mut self) -> Option<Card<Sign<'a, 'b>>> {
Some(Card::<Sign> {
///
/// 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<Card<Sign<'a, 'b>>, Error>
where
P: Into<OptionalPin<'p>>,
{
let pin: OptionalPin = pin.into();
if let Some(pin) = pin.0 {
self.verify_user_for_signing(pin)?;
}
Ok(Card::<Sign> {
state: Sign { tx: self },
})
}
/// Get a view of the card authenticated for "Admin" commands.
pub fn admin_card<'b>(&'b mut self) -> Option<Card<Admin<'a, 'b>>> {
Some(Card::<Admin> {
///
/// 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<Card<Admin<'a, 'b>>, Error>
where
P: Into<OptionalPin<'p>>,
{
let pin: OptionalPin = pin.into();
if let Some(pin) = pin.0 {
self.verify_admin(pin)?;
}
Ok(Card::<Admin> {
state: Admin { tx: self },
})
}

View file

@ -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::<Signature, anyhow::Error>(s)
};
// 1) use the signing key as primary key