Fix the assumptions about authorization underlying the card::* types:

Multiple passwords can be validated on a card at the same time.
Rename verify_* fn to be more easily legible ("user" instead of "pw1", ...)
This commit is contained in:
Heiko Schaefer 2021-09-10 21:59:02 +02:00
parent 1613f23ecc
commit 0e2b53feb4
2 changed files with 177 additions and 177 deletions

View file

@ -5,7 +5,6 @@
//! different types, such as `Open`, `User`, `Sign`, `Admin`. //! different types, such as `Open`, `User`, `Sign`, `Admin`.
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use std::ops::{Deref, DerefMut};
use sequoia_openpgp::policy::Policy; use sequoia_openpgp::policy::Policy;
use sequoia_openpgp::Cert; use sequoia_openpgp::Cert;
@ -32,9 +31,28 @@ pub struct Open {
// FIXME: Should be invalidated when changing data on the card! // FIXME: Should be invalidated when changing data on the card!
// (e.g. uploading keys, etc) // (e.g. uploading keys, etc)
ard: ApplicationRelatedData, ard: ApplicationRelatedData,
// verify status of pw1
pw1: bool,
// verify status of pw1 for signing
pw1_sign: bool,
// verify status of pw3
pw3: bool,
} }
impl Open { impl Open {
fn new(card_app: CardApp, ard: ApplicationRelatedData) -> Self {
Self {
card_app,
ard,
pw1: false,
pw1_sign: false,
pw3: false,
}
}
/// Set up connection to a CardClient (read and cache "application /// Set up connection to a CardClient (read and cache "application
/// related data"). /// related data").
/// ///
@ -47,7 +65,68 @@ impl Open {
card_app.init_caps(&ard)?; card_app.init_caps(&ard)?;
Ok(Self { card_app, ard }) Ok(Self::new(card_app, ard))
}
pub fn verify_user(&mut self, pin: &str) -> Result<(), Error> {
let _ = self.card_app.verify_pw1(pin)?;
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)?;
// 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(())
}
/// Ask the card if the user password has been successfully verified.
///
/// NOTE: on some cards this functionality seems broken.
pub fn check_user_verified(&mut self) -> Result<Response, Error> {
self.card_app.check_pw1()
}
/// Ask the card if the admin password has been successfully verified.
///
/// NOTE: on some cards this functionality seems broken.
pub fn check_admin_verified(&mut self) -> Result<Response, Error> {
self.card_app.check_pw3()
}
/// Get a view of the card authenticated for "User" commands.
pub fn get_user(&mut self) -> Option<User> {
if self.pw1 {
Some(User { oc: self })
} else {
None
}
}
/// Get a view of the card authenticated for Signing.
pub fn get_sign(&mut self) -> Option<Sign> {
if self.pw1_sign {
Some(Sign { oc: self })
} else {
None
}
}
/// Get a view of the card authenticated for "Admin" commands.
pub fn get_admin(&mut self) -> Option<Admin> {
if self.pw3 {
Some(Admin { oc: self })
} else {
None
}
} }
// --- application data --- // --- application data ---
@ -172,134 +251,49 @@ impl Open {
pub fn factory_reset(&mut self) -> Result<()> { pub fn factory_reset(&mut self) -> Result<()> {
self.card_app.factory_reset() self.card_app.factory_reset()
} }
pub fn verify_pw1_for_signing(mut self, pin: &str) -> Result<Sign, Open> {
assert!(pin.len() >= 6); // FIXME: Err
if self.card_app.verify_pw1_for_signing(pin).is_ok() {
Ok(Sign { oc: self })
} else {
Err(self)
}
}
pub fn check_pw1(&mut self) -> Result<Response, Error> {
self.card_app.check_pw1()
}
pub fn verify_pw1(mut self, pin: &str) -> Result<User, Open> {
assert!(pin.len() >= 6); // FIXME: Err
if self.card_app.verify_pw1(pin).is_ok() {
Ok(User { oc: self })
} else {
Err(self)
}
}
pub fn check_pw3(&mut self) -> Result<Response, Error> {
self.card_app.check_pw3()
}
pub fn verify_pw3(mut self, pin: &str) -> Result<Admin, Open> {
assert!(pin.len() >= 8); // FIXME: Err
if self.card_app.verify_pw3(pin).is_ok() {
Ok(Admin { oc: self })
} else {
Err(self)
}
}
} }
/// An OpenPGP card after successful verification of PW1 in mode 82 /// An OpenPGP card after successfully verifying PW1 in mode 82
/// (verification for operations other than signing) /// (verification for user operations other than signing)
pub struct User { pub struct User<'a> {
oc: Open, oc: &'a mut Open,
} }
/// Allow access to fn of OpenPGPCard, through OpenPGPCardUser. impl User<'_> {
impl Deref for User {
type Target = Open;
fn deref(&self) -> &Self::Target {
&self.oc
}
}
/// Allow access to fn of CardBase, through CardUser.
impl DerefMut for User {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.oc
}
}
impl User {
pub fn decryptor( pub fn decryptor(
&mut self, &mut self,
cert: &Cert, cert: &Cert,
policy: &dyn Policy, policy: &dyn Policy,
) -> Result<CardDecryptor, Error> { ) -> Result<CardDecryptor, Error> {
CardDecryptor::new(&mut self.card_app, cert, policy) CardDecryptor::new(&mut self.oc.card_app, cert, policy)
} }
} }
/// An OpenPGP card after successful verification of PW1 in mode 81 /// An OpenPGP card after successfully verifying PW1 in mode 81
/// (verification for signing) /// (verification for signing)
pub struct Sign { pub struct Sign<'a> {
oc: Open, oc: &'a mut Open,
} }
/// Allow access to fn of CardBase, through CardSign. impl Sign<'_> {
impl Deref for Sign {
type Target = Open;
fn deref(&self) -> &Self::Target {
&self.oc
}
}
/// Allow access to fn of CardBase, through CardSign.
impl DerefMut for Sign {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.oc
}
}
// FIXME: depending on the setting in "PW1 Status byte", only one
// signature can be made after verification for signing
impl Sign {
pub fn signer( pub fn signer(
&mut self, &mut self,
cert: &Cert, cert: &Cert,
policy: &dyn Policy, policy: &dyn Policy,
) -> std::result::Result<CardSigner, Error> { ) -> std::result::Result<CardSigner, Error> {
CardSigner::new(&mut self.card_app, cert, policy) // FIXME: depending on the setting in "PW1 Status byte", only one
// signature can be made after verification for signing
CardSigner::new(&mut self.oc.card_app, cert, policy)
} }
} }
/// An OpenPGP card after successful verification of PW3 ("Admin privileges") /// An OpenPGP card after successful verification of PW3 ("Admin privileges")
pub struct Admin { pub struct Admin<'a> {
oc: Open, oc: &'a mut Open,
} }
/// Allow access to fn of OpenPGPCard, through OpenPGPCardAdmin. impl Admin<'_> {
impl Deref for Admin {
type Target = Open;
fn deref(&self) -> &Self::Target {
&self.oc
}
}
/// Allow access to fn of OpenPGPCard, through OpenPGPCardAdmin.
impl DerefMut for Admin {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.oc
}
}
impl Admin {
pub fn set_name(&mut self, name: &str) -> Result<Response, Error> { pub fn set_name(&mut self, name: &str) -> Result<Response, Error> {
if name.len() >= 40 { if name.len() >= 40 {
return Err(anyhow!("name too long").into()); return Err(anyhow!("name too long").into());
@ -310,7 +304,7 @@ impl Admin {
return Err(anyhow!("Invalid char in name").into()); return Err(anyhow!("Invalid char in name").into());
}; };
self.card_app.set_name(name) self.oc.card_app.set_name(name)
} }
pub fn set_lang(&mut self, lang: &str) -> Result<Response, Error> { pub fn set_lang(&mut self, lang: &str) -> Result<Response, Error> {
@ -318,11 +312,11 @@ impl Admin {
return Err(anyhow!("lang too long").into()); return Err(anyhow!("lang too long").into());
} }
self.card_app.set_lang(lang) self.oc.card_app.set_lang(lang)
} }
pub fn set_sex(&mut self, sex: Sex) -> Result<Response, Error> { pub fn set_sex(&mut self, sex: Sex) -> Result<Response, Error> {
self.card_app.set_sex(sex) self.oc.card_app.set_sex(sex)
} }
pub fn set_url(&mut self, url: &str) -> Result<Response, Error> { pub fn set_url(&mut self, url: &str) -> Result<Response, Error> {
@ -331,10 +325,10 @@ impl Admin {
} }
// Check for max len // Check for max len
let ec = self.get_extended_capabilities()?; let ec = self.oc.get_extended_capabilities()?;
if url.len() < ec.max_len_special_do() as usize { if url.len() < ec.max_len_special_do() as usize {
self.card_app.set_url(url) self.oc.card_app.set_url(url)
} else { } else {
Err(anyhow!("URL too long").into()) Err(anyhow!("URL too long").into())
} }
@ -345,6 +339,6 @@ impl Admin {
key: Box<dyn CardUploadableKey>, key: Box<dyn CardUploadableKey>,
key_type: KeyType, key_type: KeyType,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.card_app.key_import(key, key_type) self.oc.card_app.key_import(key, key_type)
} }
} }

View file

@ -102,56 +102,58 @@ fn main() -> Result<(), Box<dyn Error>> {
// --------------------------------------------- // ---------------------------------------------
assert_eq!(app_id.ident(), test_card_ident); assert_eq!(app_id.ident(), test_card_ident);
let check = oc.check_pw3(); let check = oc.check_admin_verified();
println!("has pw3 been verified yet? {:x?}\n", check); println!("has pw3 been verified yet? {:x?}\n", check);
println!("factory reset"); println!("factory reset");
oc.factory_reset()?; oc.factory_reset()?;
match oc.verify_pw3("12345678") { if oc.verify_admin("12345678").is_ok() {
Ok(mut oc_admin) => { println!("pw3 verify ok");
println!("pw3 verify ok");
let check = oc_admin.check_pw3(); let check = oc.check_user_verified();
println!("has pw3 been verified yet? {:x?}", check); println!("has pw1/82 been verified yet? {:x?}", check);
let res = oc_admin.set_name("Bar<<Foo")?; // actually take Admin
println!("set name {:x?}", res); let mut oc_admin = oc.get_admin().expect("just verified");
let res = oc_admin.set_sex(Sex::NotApplicable)?; let res = oc_admin.set_name("Bar<<Foo")?;
println!("set sex {:x?}", res); println!("set name {:x?}", res);
let res = oc_admin.set_lang("en")?; let res = oc_admin.set_sex(Sex::NotApplicable)?;
println!("set lang {:x?}", res); println!("set sex {:x?}", res);
let res = oc_admin.set_url("https://keys.openpgp.org")?; let res = oc_admin.set_lang("en")?;
println!("set url {:x?}", res); println!("set lang {:x?}", res);
let cert = Cert::from_file(TEST_KEY_PATH)?; let res = oc_admin.set_url("https://keys.openpgp.org")?;
println!("set url {:x?}", res);
openpgp_card_sequoia::util::upload_from_cert_yolo( let cert = Cert::from_file(TEST_KEY_PATH)?;
&mut oc_admin,
&cert,
KeyType::Decryption,
None,
)?;
openpgp_card_sequoia::util::upload_from_cert_yolo( openpgp_card_sequoia::util::upload_from_cert_yolo(
&mut oc_admin, &mut oc_admin,
&cert, &cert,
KeyType::Signing, KeyType::Decryption,
None, None,
)?; )?;
// TODO: test keys currently have no auth-capable key openpgp_card_sequoia::util::upload_from_cert_yolo(
// openpgp_card_sequoia::upload_from_cert( &mut oc_admin,
// &oc_admin, &cert,
// &cert, KeyType::Signing,
// KeyType::Authentication, None,
// None, )?;
// )?;
} // TODO: test keys currently have no auth-capable key
_ => panic!(), // openpgp_card_sequoia::upload_from_cert(
// &oc_admin,
// &cert,
// KeyType::Authentication,
// None,
// )?;
} else {
panic!()
} }
// ----------------------------- // -----------------------------
@ -171,40 +173,42 @@ fn main() -> Result<(), Box<dyn Error>> {
// Check that we're still using the expected card // Check that we're still using the expected card
assert_eq!(app_id.ident(), test_card_ident); assert_eq!(app_id.ident(), test_card_ident);
let check = oc.check_pw1(); let check = oc.check_user_verified();
println!("has pw1/82 been verified yet? {:x?}", check); println!("has pw1/82 been verified yet? {:x?}", check);
match oc.verify_pw1("123456") { if oc.verify_user("123456").is_ok() {
Ok(mut oc_user) => { println!("pw1 82 verify ok");
println!("pw1 82 verify ok");
let check = oc_user.check_pw1(); let check = oc.check_user_verified();
println!("has pw1/82 been verified yet? {:x?}", check); println!("has pw1/82 been verified yet? {:x?}", check);
let cert = Cert::from_file(TEST_KEY_PATH)?; // actually take User
let msg = std::fs::read_to_string(TEST_ENC_MSG) let mut oc_user = oc.get_user().expect("just verified");
.expect("Unable to read file");
println!("{:?}", msg); let cert = Cert::from_file(TEST_KEY_PATH)?;
let msg = std::fs::read_to_string(TEST_ENC_MSG)
.expect("Unable to read file");
let sp = StandardPolicy::new(); println!("{:?}", msg);
let d = oc_user.decryptor(&cert, &sp)?; let sp = StandardPolicy::new();
let res = decryption_helper(d, msg.into_bytes(), &sp)?; let d = oc_user.decryptor(&cert, &sp)?;
let plain = String::from_utf8_lossy(&res); let res = decryption_helper(d, msg.into_bytes(), &sp)?;
println!("decrypted plaintext: {}", plain);
assert_eq!(plain, "Hello world!\n"); let plain = String::from_utf8_lossy(&res);
} println!("decrypted plaintext: {}", plain);
_ => panic!("verify pw1 failed"),
assert_eq!(plain, "Hello world!\n");
} else {
panic!("verify pw1 failed");
} }
// ----------------------------- // -----------------------------
// Open fresh Card for signing // Open fresh Card for signing
// ----------------------------- // -----------------------------
let oc = let mut oc =
Open::open_card(PcscClient::open_by_ident(&test_card_ident)?)?; Open::open_card(PcscClient::open_by_ident(&test_card_ident)?)?;
// let oc = CardBase::open_card(ScdClient::open_by_serial( // let oc = CardBase::open_card(ScdClient::open_by_serial(
@ -213,24 +217,26 @@ fn main() -> Result<(), Box<dyn Error>> {
// )?)?; // )?)?;
// Sign // Sign
match oc.verify_pw1_for_signing("123456") { if oc.verify_user_for_signing("123456").is_ok() {
Ok(mut oc_sign) => { println!("pw1 81 verify ok");
println!("pw1 81 verify ok");
let cert = Cert::from_file(TEST_KEY_PATH)?; // actually take Sign
let mut oc_sign = oc.get_sign().expect("just verified");
let text = "Hello world, I am signed."; let cert = Cert::from_file(TEST_KEY_PATH)?;
let signer = oc_sign.signer(&cert, &StandardPolicy::new())?; let text = "Hello world, I am signed.";
let res = sign_helper(signer, &mut text.as_bytes());
println!("res sign {:?}", res); let signer = oc_sign.signer(&cert, &StandardPolicy::new())?;
let res = sign_helper(signer, &mut text.as_bytes());
println!("res: {}", res?) println!("res sign {:?}", res);
// FIXME: validate sig println!("res: {}", res?)
}
_ => panic!("verify pw1 failed"), // FIXME: validate sig
} else {
panic!("verify pw1 failed");
} }
} else { } else {
println!("Please set environment variable TEST_CARD_IDENT."); println!("Please set environment variable TEST_CARD_IDENT.");