Use an "ident" to specify a card.

The ident is a combination of manufacturer and serial number.

The OpenPGP card spec stipulates:
"Each OpenPGP application on a card from a manufacturer/personaliser has a unique serial number"
This commit is contained in:
Heiko Schaefer 2021-07-03 18:17:30 +02:00
parent 920da0442b
commit 01fab2d91c
4 changed files with 39 additions and 19 deletions

View file

@ -24,12 +24,12 @@ const TEST_ENC_MSG: &str = "example/encrypted_to_25519.asc";
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
env_logger::init(); env_logger::init();
// Serial number of the OpenPGP Card that will be used for tests. // Ident of the OpenPGP Card that will be used for tests.
let test_card_serial = env::var("TEST_CARD_SERIAL"); let test_card_ident = env::var("TEST_CARD_IDENT");
if let Ok(test_card_serial) = test_card_serial { if let Ok(test_card_ident) = test_card_ident {
println!("** get card"); println!("** get card");
let oc = CardBase::open_by_serial(&test_card_serial)?; let oc = CardBase::open_by_ident(&test_card_ident)?;
// card metadata // card metadata
@ -37,7 +37,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let app_id = oc.get_aid()?; let app_id = oc.get_aid()?;
println!("app id: {:x?}\n\n", app_id); println!("app id: {:x?}\n\n", app_id);
println!(" serial: {:?}\n\n", app_id.serial()); println!(" ident: {:?}\n\n", app_id.ident());
let eli = oc.get_extended_length_information()?; let eli = oc.get_extended_length_information()?;
println!("extended_length_info: {:?}\n\n", eli); println!("extended_length_info: {:?}\n\n", eli);
@ -75,7 +75,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// CAUTION: Write commands ahead! // CAUTION: Write commands ahead!
// Try not to overwrite your production cards. // Try not to overwrite your production cards.
// --------------------------------------------- // ---------------------------------------------
assert_eq!(app_id.serial(), test_card_serial); assert_eq!(app_id.ident(), test_card_ident);
oc.factory_reset()?; oc.factory_reset()?;
@ -127,11 +127,11 @@ fn main() -> Result<(), Box<dyn Error>> {
// Open fresh Card for decrypt // Open fresh Card for decrypt
// ----------------------------- // -----------------------------
let oc = CardBase::open_by_serial(&test_card_serial)?; let oc = CardBase::open_by_ident(&test_card_ident)?;
let app_id = oc.get_aid()?; let app_id = oc.get_aid()?;
// Check that we're still using the expected card // Check that we're still using the expected card
assert_eq!(app_id.serial(), test_card_serial); assert_eq!(app_id.ident(), test_card_ident);
match oc.verify_pw1("123456") { match oc.verify_pw1("123456") {
Ok(oc_user) => { Ok(oc_user) => {
@ -160,7 +160,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// ----------------------------- // -----------------------------
// Open fresh Card for signing // Open fresh Card for signing
// ----------------------------- // -----------------------------
let oc = CardBase::open_by_serial(&test_card_serial)?; let oc = CardBase::open_by_ident(&test_card_ident)?;
// Sign // Sign
match oc.verify_pw1_for_signing("123456") { match oc.verify_pw1_for_signing("123456") {
@ -185,7 +185,7 @@ fn main() -> Result<(), Box<dyn Error>> {
_ => panic!("verify pw1 failed"), _ => panic!("verify pw1 failed"),
} }
} else { } else {
println!("Please set environment variable TEST_CARD_SERIAL."); println!("Please set environment variable TEST_CARD_IDENT.");
println!(); println!();
println!("NOTE: the configured card will get overwritten!"); println!("NOTE: the configured card will get overwritten!");
@ -196,7 +196,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let cards = openpgp_card::CardBase::list_cards()?; let cards = openpgp_card::CardBase::list_cards()?;
for c in cards { for c in cards {
println!(" '{}'", c.get_aid()?.serial()); println!(" '{}'", c.get_aid()?.ident());
} }
} }

View file

@ -145,8 +145,8 @@ pub enum SmartcardError {
#[error("No reader found.")] #[error("No reader found.")]
NoReaderFoundError, NoReaderFoundError,
#[error("The requested card was not found.")] #[error("The requested card '{0}' was not found.")]
CardNotFound, CardNotFound(String),
#[error("Failed to connect to the card: {0}")] #[error("Failed to connect to the card: {0}")]
SmartCardConnectionError(String), SmartCardConnectionError(String),

View file

@ -233,8 +233,11 @@ impl CardBase {
Ok(ocs) Ok(ocs)
} }
/// Find an OpenPGP card by serial number, open and return it. /// Find an OpenPGP card by "ident", open and return it.
pub fn open_by_serial(serial: &str) -> Result<Self, OpenpgpCardError> { ///
/// The ident is constructed as a concatenation of manufacturer
/// id, a colon, and the card serial. Example: "1234:5678ABCD".
pub fn open_by_ident(ident: &str) -> Result<Self, OpenpgpCardError> {
let cards = card::get_cards().map_err(|e| { let cards = card::get_cards().map_err(|e| {
OpenpgpCardError::Smartcard(SmartcardError::Error(format!( OpenpgpCardError::Smartcard(SmartcardError::Error(format!(
"{:?}", "{:?}",
@ -247,14 +250,16 @@ impl CardBase {
if let Ok(opened_card) = res { if let Ok(opened_card) = res {
let res = opened_card.get_aid(); let res = opened_card.get_aid();
if let Ok(aid) = res { if let Ok(aid) = res {
if aid.serial() == serial { if aid.ident() == ident {
return Ok(opened_card); return Ok(opened_card);
} }
} }
} }
} }
Err(OpenpgpCardError::Smartcard(SmartcardError::CardNotFound)) Err(OpenpgpCardError::Smartcard(SmartcardError::CardNotFound(
ident.to_string(),
)))
} }
/// Open connection to some card and select the openpgp applet /// Open connection to some card and select the openpgp applet

View file

@ -55,7 +55,22 @@ impl TryFrom<&[u8]> for ApplicationId {
} }
impl ApplicationId { impl ApplicationId {
pub fn serial(&self) -> String { pub fn serial(&self) -> u32 {
format!("{:08X}", self.serial) self.serial
}
pub fn manufacturer(&self) -> u16 {
self.manufacturer
}
/// This ident is constructed as the concatenation of manufacturer
/// id, a colon, and the card serial (in hexadecimal representation).
///
/// It is a more easily human-readable, shorter form of the full
/// 16-byte AID ("Application Identifier").
///
/// Example: "1234:5678ABCD".
pub fn ident(&self) -> String {
format!("{:04X}:{:08X}", self.manufacturer, self.serial)
} }
} }