Move implementation of low-level OpenPGP functionality from CardApp to CardClient.

This commit is contained in:
Heiko Schaefer 2022-01-17 15:11:03 +01:00
parent 111f9e9631
commit 2480745088
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
11 changed files with 968 additions and 1072 deletions

View file

@ -66,7 +66,7 @@ pub fn test_decrypt(
let cert = Cert::from_str(param[0])?;
let msg = param[1].to_string();
CardApp::verify_pw1(card_client, "123456")?;
card_client.verify_pw1("123456")?;
let p = StandardPolicy::new();
@ -90,7 +90,7 @@ pub fn test_sign(
) -> Result<TestOutput, TestError> {
assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'");
CardApp::verify_pw1_for_signing(card_client, "123456")?;
card_client.verify_pw1_for_signing("123456")?;
let cert = Cert::from_str(param[0])?;
@ -111,7 +111,7 @@ fn check_key_upload_metadata(
card_client: &mut (dyn CardClient + Send + Sync),
meta: &[(String, KeyGenerationTime)],
) -> Result<()> {
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
// check fingerprints
let card_fp = ard.fingerprints()?;
@ -155,7 +155,7 @@ pub fn test_print_caps(
card_client: &mut (dyn CardClient + Send + Sync),
_param: &[&str],
) -> Result<TestOutput, TestError> {
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
let aid = ard.application_id()?;
println!("aid: {:#x?}", aid);
@ -176,14 +176,14 @@ pub fn test_print_algo_info(
card_client: &mut (dyn CardClient + Send + Sync),
_param: &[&str],
) -> Result<TestOutput, TestError> {
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
let dec = ard.algorithm_attributes(KeyType::Decryption)?;
println!("Current algorithm for the decrypt slot: {}", dec);
println!();
let algo = CardApp::algorithm_information(card_client);
let algo = card_client.algorithm_information();
if let Ok(Some(algo)) = algo {
println!("Card algorithm list:\n{}", algo);
}
@ -201,7 +201,7 @@ pub fn test_upload_keys(
"test_upload_keys needs a filename for 'cert'"
);
CardApp::verify_pw3(card_client, "12345678")?;
card_client.verify_pw3("12345678")?;
let cert = Cert::from_file(param[0])?;
@ -223,7 +223,7 @@ pub fn test_keygen(
card_client: &mut (dyn CardClient + Send + Sync),
param: &[&str],
) -> Result<TestOutput, TestError> {
CardApp::verify_pw3(card_client, "12345678")?;
card_client.verify_pw3("12345678")?;
// Generate all three subkeys on card
let algo = param[0];
@ -231,8 +231,7 @@ pub fn test_keygen(
let alg = AlgoSimple::try_from(algo)?;
println!(" Generate subkey for Signing");
let (pkm, ts) = CardApp::generate_key_simple(
card_client,
let (pkm, ts) = card_client.generate_key_simple(
public_to_fingerprint,
KeyType::Signing,
alg,
@ -240,8 +239,7 @@ pub fn test_keygen(
let key_sig = public_key_material_to_key(&pkm, KeyType::Signing, ts)?;
println!(" Generate subkey for Decryption");
let (pkm, ts) = CardApp::generate_key_simple(
card_client,
let (pkm, ts) = card_client.generate_key_simple(
public_to_fingerprint,
KeyType::Decryption,
alg,
@ -249,8 +247,7 @@ pub fn test_keygen(
let key_dec = public_key_material_to_key(&pkm, KeyType::Decryption, ts)?;
println!(" Generate subkey for Authentication");
let (pkm, ts) = CardApp::generate_key_simple(
card_client,
let (pkm, ts) = card_client.generate_key_simple(
public_to_fingerprint,
KeyType::Authentication,
alg,
@ -280,12 +277,12 @@ pub fn test_get_pub(
card_client: &mut (dyn CardClient + Send + Sync),
_param: &[&str],
) -> Result<TestOutput, TestError> {
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
let key_gen = ard.key_generation_times()?;
// --
let sig = CardApp::public_key(card_client, KeyType::Signing)?;
let sig = card_client.public_key(KeyType::Signing)?;
let ts = key_gen.signature().unwrap().get().into();
let key = public_key_material_to_key(&sig, KeyType::Signing, ts)?;
@ -293,7 +290,7 @@ pub fn test_get_pub(
// --
let dec = CardApp::public_key(card_client, KeyType::Decryption)?;
let dec = card_client.public_key(KeyType::Decryption)?;
let ts = key_gen.decryption().unwrap().get().into();
let key = public_key_material_to_key(&dec, KeyType::Decryption, ts)?;
@ -301,7 +298,7 @@ pub fn test_get_pub(
// --
let auth = CardApp::public_key(card_client, KeyType::Authentication)?;
let auth = card_client.public_key(KeyType::Authentication)?;
let ts = key_gen.authentication().unwrap().get().into();
let key = public_key_material_to_key(&auth, KeyType::Authentication, ts)?;
@ -319,7 +316,7 @@ pub fn test_reset(
card_client: &mut (dyn CardClient + Send + Sync),
_param: &[&str],
) -> Result<TestOutput, TestError> {
let _res = CardApp::factory_reset(card_client)?;
let _res = card_client.factory_reset()?;
Ok(vec![])
}
@ -332,22 +329,22 @@ pub fn test_set_user_data(
card_client: &mut (dyn CardClient + Send + Sync),
_param: &[&str],
) -> Result<TestOutput, TestError> {
CardApp::verify_pw3(card_client, "12345678")?;
card_client.verify_pw3("12345678")?;
// name
CardApp::set_name(card_client, "Bar<<Foo")?;
card_client.set_name("Bar<<Foo")?;
// lang
CardApp::set_lang(card_client, "deen")?;
card_client.set_lang("deen")?;
// sex
CardApp::set_sex(card_client, Sex::Female)?;
card_client.set_sex(Sex::Female)?;
// url
CardApp::set_url(card_client, "https://duckduckgo.com/")?;
card_client.set_url("https://duckduckgo.com/")?;
// read all the fields back again, expect equal data
let ch = CardApp::cardholder_related_data(card_client)?;
let ch = card_client.cardholder_related_data()?;
assert_eq!(ch.name(), Some("Bar<<Foo"));
assert_eq!(
@ -356,7 +353,7 @@ pub fn test_set_user_data(
);
assert_eq!(ch.sex(), Some(Sex::Female));
let url = CardApp::url(card_client)?;
let url = card_client.url()?;
assert_eq!(url, "https://duckduckgo.com/".to_string());
Ok(vec![])
@ -370,42 +367,26 @@ pub fn test_private_data(
println!();
let d = CardApp::private_use_do(card_client, 1)?;
let d = card_client.private_use_do(1)?;
println!("data 1 {:?}", d);
CardApp::verify_pw1(card_client, "123456")?;
card_client.verify_pw1("123456")?;
CardApp::set_private_use_do(
card_client,
1,
"Foo bar1!".as_bytes().to_vec(),
)?;
CardApp::set_private_use_do(
card_client,
3,
"Foo bar3!".as_bytes().to_vec(),
)?;
card_client.set_private_use_do(1, "Foo bar1!".as_bytes().to_vec())?;
card_client.set_private_use_do(3, "Foo bar3!".as_bytes().to_vec())?;
CardApp::verify_pw3(card_client, "12345678")?;
card_client.verify_pw3("12345678")?;
CardApp::set_private_use_do(
card_client,
2,
"Foo bar2!".as_bytes().to_vec(),
)?;
CardApp::set_private_use_do(
card_client,
4,
"Foo bar4!".as_bytes().to_vec(),
)?;
card_client.set_private_use_do(2, "Foo bar2!".as_bytes().to_vec())?;
card_client.set_private_use_do(4, "Foo bar4!".as_bytes().to_vec())?;
let d = CardApp::private_use_do(card_client, 1)?;
let d = card_client.private_use_do(1)?;
println!("data 1 {:?}", d);
let d = CardApp::private_use_do(card_client, 2)?;
let d = card_client.private_use_do(2)?;
println!("data 2 {:?}", d);
let d = CardApp::private_use_do(card_client, 3)?;
let d = card_client.private_use_do(3)?;
println!("data 3 {:?}", d);
let d = CardApp::private_use_do(card_client, 4)?;
let d = card_client.private_use_do(4)?;
println!("data 4 {:?}", d);
Ok(out)
@ -419,7 +400,7 @@ pub fn test_cardholder_cert(
println!();
match CardApp::cardholder_certificate(card_client) {
match card_client.cardholder_certificate() {
Ok(res) => {
out.push(TestResult::Text(format!("got cert {:x?}", res.data())))
}
@ -432,11 +413,11 @@ pub fn test_cardholder_cert(
}
};
CardApp::verify_pw3(card_client, "12345678")?;
card_client.verify_pw3("12345678")?;
let data = "Foo bar baz!".as_bytes();
match CardApp::set_cardholder_certificate(card_client, data.to_vec()) {
match card_client.set_cardholder_certificate(data.to_vec()) {
Ok(_resp) => out.push(TestResult::Text("set cert ok".to_string())),
Err(e) => {
out.push(TestResult::Text(format!(
@ -447,7 +428,7 @@ pub fn test_cardholder_cert(
}
}
let res = CardApp::cardholder_certificate(card_client)?;
let res = card_client.cardholder_certificate()?;
out.push(TestResult::Text("get cert ok".to_string()));
if res.data() != data {
@ -460,7 +441,7 @@ pub fn test_cardholder_cert(
// try using slot 2
match CardApp::select_data(card_client, 2, &[0x7F, 0x21]) {
match card_client.select_data(2, &[0x7F, 0x21]) {
Ok(_res) => out.push(TestResult::Text("select_data ok".to_string())),
Err(e) => {
out.push(TestResult::Text(format!("select_data: {:?}", e)));
@ -477,19 +458,19 @@ pub fn test_pw_status(
) -> Result<TestOutput, TestError> {
let out = vec![];
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
let mut pws = ard.pw_status_bytes()?;
println!("pws {:?}", pws);
CardApp::verify_pw3(card_client, "12345678")?;
card_client.verify_pw3("12345678")?;
pws.set_pw1_cds_valid_once(false);
pws.set_pw1_pin_block(true);
CardApp::set_pw_status_bytes(card_client, &pws, false)?;
card_client.set_pw_status_bytes(&pws, false)?;
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
let pws = ard.pw_status_bytes()?;
println!("pws {:?}", pws);
@ -518,7 +499,7 @@ pub fn test_verify(
let mut out = vec![];
// try to set name without verify, assert result is not ok!
let res = CardApp::set_name(card_client, "Notverified<<Hello");
let res = card_client.set_name("Notverified<<Hello");
if let Err(Error::CardStatus(s)) = res {
assert_eq!(s, StatusBytes::SecurityStatusNotSatisfied);
@ -526,9 +507,9 @@ pub fn test_verify(
panic!("Status should be 'SecurityStatusNotSatisfied'");
}
CardApp::verify_pw3(card_client, "12345678")?;
card_client.verify_pw3("12345678")?;
match CardApp::check_pw3(card_client) {
match card_client.check_pw3() {
Err(Error::CardStatus(s)) => {
// e.g. yubikey5 returns an error status!
out.push(TestResult::Status(s));
@ -539,14 +520,14 @@ pub fn test_verify(
Ok(_) => out.push(TestResult::StatusOk),
}
CardApp::set_name(card_client, "Admin<<Hello")?;
card_client.set_name("Admin<<Hello")?;
let cardholder = CardApp::cardholder_related_data(card_client)?;
let cardholder = card_client.cardholder_related_data()?;
assert_eq!(cardholder.name(), Some("Admin<<Hello"));
CardApp::verify_pw1(card_client, "123456")?;
card_client.verify_pw1("123456")?;
match CardApp::check_pw3(card_client) {
match card_client.check_pw3() {
Err(Error::CardStatus(s)) => {
// e.g. yubikey5 returns an error status!
out.push(TestResult::Status(s));
@ -557,9 +538,9 @@ pub fn test_verify(
Ok(_) => out.push(TestResult::StatusOk),
}
CardApp::set_name(card_client, "There<<Hello")?;
card_client.set_name("There<<Hello")?;
let cardholder = CardApp::cardholder_related_data(card_client)?;
let cardholder = card_client.cardholder_related_data()?;
assert_eq!(cardholder.name(), Some("There<<Hello"));
Ok(out)
@ -574,20 +555,20 @@ pub fn test_change_pw(
// first do admin-less pw1 on gnuk
// (NOTE: Gnuk requires a key to be loaded before allowing pw changes!)
println!("change pw1");
CardApp::change_pw1(card_client, "123456", "abcdef00")?;
card_client.change_pw1("123456", "abcdef00")?;
// also set admin pw, which means pw1 is now only user-pw again, on gnuk
println!("change pw3");
// ca.change_pw3("abcdef00", "abcdefgh")?; // gnuk
CardApp::change_pw3(card_client, "12345678", "abcdefgh")?;
card_client.change_pw3("12345678", "abcdefgh")?;
println!("change pw1");
CardApp::change_pw1(card_client, "abcdef00", "abcdef")?; // gnuk
card_client.change_pw1("abcdef00", "abcdef")?; // gnuk
// ca.change_pw1("123456", "abcdef")?;
println!("verify bad pw1");
match CardApp::verify_pw1(card_client, "123456ab") {
match card_client.verify_pw1("123456ab") {
Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => {
// this is expected
}
@ -598,10 +579,10 @@ pub fn test_change_pw(
}
println!("verify good pw1");
CardApp::verify_pw1(card_client, "abcdef")?;
card_client.verify_pw1("abcdef")?;
println!("verify bad pw3");
match CardApp::verify_pw3(card_client, "00000000") {
match card_client.verify_pw3("00000000") {
Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => {
// this is expected
}
@ -612,13 +593,13 @@ pub fn test_change_pw(
}
println!("verify good pw3");
CardApp::verify_pw3(card_client, "abcdefgh")?;
card_client.verify_pw3("abcdefgh")?;
println!("change pw3 back to default");
CardApp::change_pw3(card_client, "abcdefgh", "12345678")?;
card_client.change_pw3("abcdefgh", "12345678")?;
println!("change pw1 back to default");
CardApp::change_pw1(card_client, "abcdef", "123456")?;
card_client.change_pw1("abcdef", "123456")?;
Ok(out)
}
@ -631,15 +612,15 @@ pub fn test_reset_retry_counter(
// set pw3, then pw1 (to bring gnuk into non-admin mode)
println!("set pw3");
CardApp::change_pw3(card_client, "12345678", "12345678")?;
card_client.change_pw3("12345678", "12345678")?;
println!("set pw1");
CardApp::change_pw1(card_client, "123456", "123456")?;
card_client.change_pw1("123456", "123456")?;
println!("break pw1");
let _ = CardApp::verify_pw1(card_client, "wrong0");
let _ = CardApp::verify_pw1(card_client, "wrong0");
let _ = CardApp::verify_pw1(card_client, "wrong0");
let res = CardApp::verify_pw1(card_client, "wrong0");
let _ = card_client.verify_pw1("wrong0");
let _ = card_client.verify_pw1("wrong0");
let _ = card_client.verify_pw1("wrong0");
let res = card_client.verify_pw1("wrong0");
match res {
Err(Error::CardStatus(StatusBytes::AuthenticationMethodBlocked)) => {
@ -660,24 +641,23 @@ pub fn test_reset_retry_counter(
}
println!("verify pw3");
CardApp::verify_pw3(card_client, "12345678")?;
card_client.verify_pw3("12345678")?;
println!("set resetting code");
CardApp::set_resetting_code(card_client, "abcdefgh".as_bytes().to_vec())?;
card_client.set_resetting_code("abcdefgh".as_bytes().to_vec())?;
println!("reset retry counter");
// ca.reset_retry_counter_pw1("abcdef".as_bytes().to_vec(), None)?;
let _res = CardApp::reset_retry_counter_pw1(
card_client,
let _res = card_client.reset_retry_counter_pw1(
"abcdef".as_bytes().to_vec(),
Some("abcdefgh".as_bytes().to_vec()),
);
println!("verify good pw1");
CardApp::verify_pw1(card_client, "abcdef")?;
card_client.verify_pw1("abcdef")?;
println!("verify bad pw1");
match CardApp::verify_pw1(card_client, "00000000") {
match card_client.verify_pw1("00000000") {
Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => {
// this is expected
}

View file

@ -49,7 +49,7 @@ pub(crate) fn upload_subkeys(
// upload key
let cuk = vka_as_uploadable_key(vka, None);
CardApp::key_import(card_client, cuk, *kt)?;
card_client.key_import(cuk, *kt)?;
}
}

View file

@ -49,7 +49,7 @@ impl<'a> Open<'a> {
pub fn new(
card_client: &'a mut (dyn CardClient + Send + Sync),
) -> Result<Self, Error> {
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
Ok(Self {
card_client,
@ -60,15 +60,15 @@ impl<'a> Open<'a> {
})
}
pub fn feature_pinpad_verify(&mut self) -> bool {
CardApp::feature_pinpad_verify(self.card_client)
self.card_client.feature_pinpad_verify()
}
pub fn feature_pinpad_modify(&mut self) -> bool {
CardApp::feature_pinpad_modify(self.card_client)
self.card_client.feature_pinpad_modify()
}
pub fn verify_user(&mut self, pin: &str) -> Result<(), Error> {
let _ = CardApp::verify_pw1(self.card_client, pin)?;
let _ = self.card_client.verify_pw1(pin)?;
self.pw1 = true;
Ok(())
}
@ -79,13 +79,13 @@ impl<'a> Open<'a> {
) -> Result<(), Error> {
prompt();
let _ = CardApp::verify_pw1_pinpad(self.card_client)?;
let _ = self.card_client.verify_pw1_pinpad()?;
self.pw1 = true;
Ok(())
}
pub fn verify_user_for_signing(&mut self, pin: &str) -> Result<(), Error> {
let _ = CardApp::verify_pw1_for_signing(self.card_client, pin)?;
let _ = self.card_client.verify_pw1_for_signing(pin)?;
// FIXME: depending on card mode, pw1_sign is only usable once
@ -99,7 +99,7 @@ impl<'a> Open<'a> {
) -> Result<(), Error> {
prompt();
let _ = CardApp::verify_pw1_for_signing_pinpad(self.card_client)?;
let _ = self.card_client.verify_pw1_for_signing_pinpad()?;
// FIXME: depending on card mode, pw1_sign is only usable once
@ -108,7 +108,7 @@ impl<'a> Open<'a> {
}
pub fn verify_admin(&mut self, pin: &str) -> Result<(), Error> {
let _ = CardApp::verify_pw3(self.card_client, pin)?;
let _ = self.card_client.verify_pw3(pin)?;
self.pw3 = true;
Ok(())
}
@ -119,7 +119,7 @@ impl<'a> Open<'a> {
) -> Result<(), Error> {
prompt();
let _ = CardApp::verify_pw3_pinpad(self.card_client)?;
let _ = self.card_client.verify_pw3_pinpad()?;
self.pw3 = true;
Ok(())
}
@ -128,14 +128,14 @@ impl<'a> Open<'a> {
///
/// NOTE: on some cards this functionality seems broken.
pub fn check_user_verified(&mut self) -> Result<Response, Error> {
CardApp::check_pw1(self.card_client)
self.card_client.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> {
CardApp::check_pw3(self.card_client)
self.card_client.check_pw3()
}
pub fn change_user_pin(
@ -143,7 +143,7 @@ impl<'a> Open<'a> {
old: &str,
new: &str,
) -> Result<Response, Error> {
CardApp::change_pw1(self.card_client, old, new)
self.card_client.change_pw1(old, new)
}
pub fn change_user_pin_pinpad(
@ -151,7 +151,7 @@ impl<'a> Open<'a> {
prompt: &dyn Fn(),
) -> Result<Response, Error> {
prompt();
CardApp::change_pw1_pinpad(self.card_client)
self.card_client.change_pw1_pinpad()
}
pub fn reset_user_pin(
@ -159,11 +159,8 @@ impl<'a> Open<'a> {
rst: &str,
new: &str,
) -> Result<Response, Error> {
CardApp::reset_retry_counter_pw1(
self.card_client,
new.into(),
Some(rst.into()),
)
self.card_client
.reset_retry_counter_pw1(new.into(), Some(rst.into()))
}
pub fn change_admin_pin(
@ -171,7 +168,7 @@ impl<'a> Open<'a> {
old: &str,
new: &str,
) -> Result<Response, Error> {
CardApp::change_pw3(self.card_client, old, new)
self.card_client.change_pw3(old, new)
}
pub fn change_admin_pin_pinpad(
@ -179,7 +176,7 @@ impl<'a> Open<'a> {
prompt: &dyn Fn(),
) -> Result<Response, Error> {
prompt();
CardApp::change_pw3_pinpad(self.card_client)
self.card_client.change_pw3_pinpad()
}
/// Get a view of the card authenticated for "User" commands.
@ -299,21 +296,21 @@ impl<'a> Open<'a> {
// --- URL (5f50) ---
pub fn url(&mut self) -> Result<String> {
CardApp::url(self.card_client)
self.card_client.url()
}
// --- cardholder related data (65) ---
pub fn cardholder_related_data(
&mut self,
) -> Result<CardholderRelatedData> {
CardApp::cardholder_related_data(self.card_client)
self.card_client.cardholder_related_data()
}
// --- security support template (7a) ---
pub fn security_support_template(
&mut self,
) -> Result<SecuritySupportTemplate> {
CardApp::security_support_template(self.card_client)
self.card_client.security_support_template()
}
// DO "Algorithm Information" (0xFA)
@ -327,12 +324,12 @@ impl<'a> Open<'a> {
return Ok(None);
}
CardApp::algorithm_information(self.card_client)
self.card_client.algorithm_information()
}
/// Firmware Version, YubiKey specific (?)
pub fn firmware_version(&mut self) -> Result<Vec<u8>> {
CardApp::firmware_version(self.card_client)
self.card_client.firmware_version()
}
// ----------
@ -341,14 +338,14 @@ impl<'a> Open<'a> {
&mut self,
key_type: KeyType,
) -> Result<PublicKeyMaterial> {
CardApp::public_key(self.card_client, key_type).map_err(|e| e.into())
self.card_client.public_key(key_type).map_err(|e| e.into())
}
// ----------
/// Delete all state on this OpenPGP card
pub fn factory_reset(&mut self) -> Result<()> {
CardApp::factory_reset(self.card_client)
self.card_client.factory_reset()
}
}
@ -411,7 +408,7 @@ impl Admin<'_, '_> {
return Err(anyhow!("Invalid char in name").into());
};
CardApp::set_name(self.oc.card_client, name)
self.oc.card_client.set_name(name)
}
pub fn set_lang(&mut self, lang: &str) -> Result<Response, Error> {
@ -419,11 +416,11 @@ impl Admin<'_, '_> {
return Err(anyhow!("lang too long").into());
}
CardApp::set_lang(self.oc.card_client, lang)
self.oc.card_client.set_lang(lang)
}
pub fn set_sex(&mut self, sex: Sex) -> Result<Response, Error> {
CardApp::set_sex(self.oc.card_client, sex)
self.oc.card_client.set_sex(sex)
}
pub fn set_url(&mut self, url: &str) -> Result<Response, Error> {
@ -441,7 +438,7 @@ impl Admin<'_, '_> {
// or if it's within the acceptable length:
// send the url update to the card.
CardApp::set_url(self.oc.card_client, url)
self.oc.card_client.set_url(url)
} else {
Err(anyhow!("URL too long").into())
}
@ -451,11 +448,13 @@ impl Admin<'_, '_> {
&mut self,
pin: &str,
) -> Result<Response, Error> {
CardApp::set_resetting_code(self.oc.card_client, pin.into())
self.oc.card_client.set_resetting_code(pin.into())
}
pub fn reset_user_pin(&mut self, new: &str) -> Result<Response, Error> {
CardApp::reset_retry_counter_pw1(self.oc.card_client, new.into(), None)
self.oc
.card_client
.reset_retry_counter_pw1(new.into(), None)
}
/// Upload a ValidErasedKeyAmalgamation to the card as a specific KeyType.
@ -468,7 +467,7 @@ impl Admin<'_, '_> {
password: Option<String>,
) -> Result<(), Error> {
let key = vka_as_uploadable_key(vka, password);
CardApp::key_import(self.oc.card_client, key, key_type)
self.oc.card_client.key_import(key, key_type)
}
pub fn generate_key_simple(
@ -477,14 +476,12 @@ impl Admin<'_, '_> {
algo: Option<AlgoSimple>,
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> {
match algo {
Some(algo) => CardApp::generate_key_simple(
self.oc.card_client,
Some(algo) => self.oc.card_client.generate_key_simple(
public_to_fingerprint,
key_type,
algo,
),
None => CardApp::generate_key(
self.oc.card_client,
None => self.oc.card_client.generate_key(
public_to_fingerprint,
key_type,
None,

View file

@ -38,7 +38,7 @@ impl<'a> CardDecryptor<'a> {
cert: &Cert,
) -> Result<CardDecryptor<'a>, Error> {
// Get the fingerprint for the decryption key from the card.
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
let fps = ard.fingerprints()?;
let fp = fps.decryption();
@ -85,7 +85,7 @@ impl<'a> crypto::Decryptor for CardDecryptor<'a> {
match (ciphertext, self.public.mpis()) {
(mpi::Ciphertext::RSA { c: ct }, mpi::PublicKey::RSA { .. }) => {
let dm = Cryptogram::RSA(ct.value());
let dec = CardApp::decipher(self.card_client, dm)?;
let dec = self.card_client.decipher(dm)?;
let sk = openpgp::crypto::SessionKey::from(&dec[..]);
Ok(sk)
@ -109,7 +109,7 @@ impl<'a> crypto::Decryptor for CardDecryptor<'a> {
};
// Decryption operation on the card
let mut dec = CardApp::decipher(self.card_client, dm)?;
let mut dec = self.card_client.decipher(dm)?;
// Specifically handle return value format like Gnuk's
// (Gnuk returns a leading '0x04' byte and

View file

@ -34,7 +34,7 @@ impl<'a> CardSigner<'a> {
cert: &openpgp::Cert,
) -> Result<CardSigner<'a>, Error> {
// Get the fingerprint for the signing key from the card.
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
let fps = ard.fingerprints()?;
let fp = fps.signature();
@ -117,7 +117,7 @@ impl<'a> crypto::Signer for CardSigner<'a> {
}
};
let sig = CardApp::signature_for_hash(self.card_client, hash)?;
let sig = self.card_client.signature_for_hash(hash)?;
let mpi = mpi::MPI::new(&sig[..]);
Ok(mpi::Signature::RSA { s: mpi })
@ -125,7 +125,7 @@ impl<'a> crypto::Signer for CardSigner<'a> {
(PublicKeyAlgorithm::EdDSA, mpi::PublicKey::EdDSA { .. }) => {
let hash = Hash::EdDSA(digest);
let sig = CardApp::signature_for_hash(self.card_client, hash)?;
let sig = self.card_client.signature_for_hash(hash)?;
let r = mpi::MPI::new(&sig[..32]);
let s = mpi::MPI::new(&sig[32..]);
@ -143,7 +143,7 @@ impl<'a> crypto::Signer for CardSigner<'a> {
_ => Hash::ECDSA(digest),
};
let sig = CardApp::signature_for_hash(self.card_client, hash)?;
let sig = self.card_client.signature_for_hash(hash)?;
let len_2 = sig.len() / 2;
let r = mpi::MPI::new(&sig[..len_2]);

View file

@ -3,25 +3,6 @@
//! CardApp exposes functionality of the "OpenPGP card" application.
use std::convert::TryFrom;
use std::convert::TryInto;
use anyhow::{anyhow, Result};
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple};
use crate::apdu::response::RawResponse;
use crate::apdu::{commands, response::Response};
use crate::card_do::{
ApplicationRelatedData, CardholderRelatedData, Fingerprint,
KeyGenerationTime, PWStatusBytes, SecuritySupportTemplate, Sex,
};
use crate::crypto_data::{
CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial,
};
use crate::tlv::{tag::Tag, value::Value, Tlv};
use crate::{apdu, keys, CardCaps, CardClient, KeyType, SmartcardError};
use crate::{Error, StatusBytes};
/// Low-level access to OpenPGP card functionality.
///
/// Not many checks are performed here (e.g. for valid data lengths).
@ -31,888 +12,4 @@ use crate::{Error, StatusBytes};
/// be done on a higher layer.
pub struct CardApp {}
impl CardApp {
/// Get a CardApp based on a CardClient.
///
/// It is expected that SELECT has already been performed on the card
/// beforehand.
///
/// This fn initializes the CardCaps by requesting
/// application_related_data from the card, and setting the
/// capabilities accordingly.
pub fn initialize(card_client: &mut dyn CardClient) -> Result<()> {
let ard = Self::application_related_data(card_client)?;
Self::init_caps(card_client, &ard)?;
Ok(())
}
/// Initialize the CardCaps settings in the underlying CardClient
/// from the data in `ard`.
///
/// This should be done at an early point, soon after opening the card.
fn init_caps(
card_client: &mut dyn CardClient,
ard: &ApplicationRelatedData,
) -> Result<()> {
// Determine chaining/extended length support from card
// metadata and cache this information in CardApp (as a
// CardCaps)
let mut ext_support = false;
let mut chaining_support = false;
if let Ok(hist) = ard.historical_bytes() {
if let Some(cc) = hist.card_capabilities() {
chaining_support = cc.command_chaining();
ext_support = cc.extended_lc_le();
}
}
let ext_cap = ard.extended_capabilities()?;
// Get max command/response byte sizes from card
let (max_cmd_bytes, max_rsp_bytes) =
if let Ok(Some(eli)) = ard.extended_length_information() {
// In card 3.x, max lengths come from ExtendedLengthInfo
(eli.max_command_bytes(), eli.max_response_bytes())
} else if let (Some(cmd), Some(rsp)) =
(ext_cap.max_cmd_len(), ext_cap.max_resp_len())
{
// In card 2.x, max lengths come from ExtendedCapabilities
(cmd, rsp)
} else {
// Fallback: use 255 if we have no information from the card
(255, 255)
};
let pw_status = ard.pw_status_bytes()?;
let pw1_max = pw_status.pw1_max_len();
let pw3_max = pw_status.pw3_max_len();
let caps = CardCaps {
ext_support,
chaining_support,
max_cmd_bytes,
max_rsp_bytes,
pw1_max_len: pw1_max,
pw3_max_len: pw3_max,
};
log::debug!("init_card_caps to: {:x?}", caps);
card_client.init_card_caps(caps);
Ok(())
}
// --- get data ---
/// Get the "application related data" from the card.
///
/// (This data should probably be cached in a higher layer. Some parts of
/// it are needed regularly, and it does not usually change during
/// normal use of a card.)
pub fn application_related_data(
card_client: &mut dyn CardClient,
) -> Result<ApplicationRelatedData> {
let ad = commands::application_related_data();
let resp = apdu::send_command(card_client, ad, true)?;
let value = Value::from(resp.data()?, true)?;
log::debug!(" App data Value: {:x?}", value);
Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value)))
}
#[allow(dead_code)]
fn ca_fingerprints() {
unimplemented!()
}
#[allow(dead_code)]
fn key_information() {
unimplemented!()
}
#[allow(dead_code)]
fn uif_pso_cds() {
unimplemented!()
}
#[allow(dead_code)]
fn uif_pso_dec() {
unimplemented!()
}
#[allow(dead_code)]
fn uif_pso_aut() {
unimplemented!()
}
#[allow(dead_code)]
fn uif_attestation() {
unimplemented!()
}
// --- login data (5e) ---
/// Get URL (5f50)
pub fn url(card_client: &mut dyn CardClient) -> Result<String> {
let resp = apdu::send_command(card_client, commands::url(), true)?;
Ok(String::from_utf8_lossy(resp.data()?).to_string())
}
/// Get cardholder related data (65)
pub fn cardholder_related_data(
card_client: &mut dyn CardClient,
) -> Result<CardholderRelatedData> {
let crd = commands::cardholder_related_data();
let resp = apdu::send_command(card_client, crd, true)?;
resp.check_ok()?;
CardholderRelatedData::try_from(resp.data()?)
}
/// Get security support template (7a)
pub fn security_support_template(
card_client: &mut dyn CardClient,
) -> Result<SecuritySupportTemplate> {
let sst = commands::security_support_template();
let resp = apdu::send_command(card_client, sst, true)?;
resp.check_ok()?;
let tlv = Tlv::try_from(resp.data()?)?;
let res = tlv.find(&[0x93].into()).ok_or_else(|| {
anyhow!("Couldn't get SecuritySupportTemplate DO")
})?;
if let Value::S(data) = res {
let mut data = data.to_vec();
assert_eq!(data.len(), 3);
data.insert(0, 0); // prepend a zero
let data: [u8; 4] = data.try_into().unwrap();
let dsc: u32 = u32::from_be_bytes(data);
Ok(SecuritySupportTemplate { dsc })
} else {
Err(anyhow!("Failed to process SecuritySupportTemplate"))
}
}
/// Get cardholder certificate (each for AUT, DEC and SIG).
///
/// Call select_data() before calling this fn, to select a particular
/// certificate (if the card supports multiple certificates).
pub fn cardholder_certificate(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let cmd = commands::cardholder_certificate();
apdu::send_command(card_client, cmd, true)?.try_into()
}
/// Get "Algorithm Information"
pub fn algorithm_information(
card_client: &mut dyn CardClient,
) -> Result<Option<AlgoInfo>> {
let resp =
apdu::send_command(card_client, commands::algo_info(), true)?;
resp.check_ok()?;
let ai = AlgoInfo::try_from(resp.data()?)?;
Ok(Some(ai))
}
/// Firmware Version (YubiKey specific (?))
pub fn firmware_version(
card_client: &mut dyn CardClient,
) -> Result<Vec<u8>> {
let resp = apdu::send_command(
card_client,
commands::firmware_version(),
true,
)?;
Ok(resp.data()?.into())
}
/// 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(
card_client: &mut dyn CardClient,
id: u8,
) -> Result<Vec<u8>> {
let resp =
apdu::send_command(card_client, commands::set_identity(id), false);
// Apparently it's normal to get "NotTransacted" from pcsclite when
// the identity switch was successful.
if let Err(Error::Smartcard(SmartcardError::NotTransacted)) = resp {
Ok(vec![])
} else {
Ok(resp?.data()?.into())
}
}
/// SELECT DATA ("select a DO in the current template",
/// e.g. for cardholder certificate)
pub fn select_data(
card_client: &mut dyn CardClient,
num: u8,
tag: &[u8],
) -> Result<Response, Error> {
let tlv = Tlv::new(
[0x60],
Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]),
);
let data = tlv.serialize();
let cmd = commands::select_data(num, data);
apdu::send_command(card_client, cmd, true)?.try_into()
}
// --- optional private DOs (0101 - 0104) ---
/// Get data from "private use" DO.
///
/// `num` must be between 1 and 4.
pub fn private_use_do(
card_client: &mut dyn CardClient,
num: u8,
) -> Result<Vec<u8>> {
assert!((1..=4).contains(&num));
let cmd = commands::private_use_do(num);
let resp = apdu::send_command(card_client, cmd, true)?;
Ok(resp.data()?.to_vec())
}
/// Set data of "private use" DO.
///
/// `num` must be between 1 and 4.
///
/// Access condition:
/// - 1/3 need PW1 (82)
/// - 2/4 need PW3
pub fn set_private_use_do(
card_client: &mut dyn CardClient,
num: u8,
data: Vec<u8>,
) -> Result<Vec<u8>> {
assert!((1..=4).contains(&num));
let cmd = commands::put_private_use_do(num, data);
let resp = apdu::send_command(card_client, cmd, true)?;
Ok(resp.data()?.to_vec())
}
// ----------
/// Reset all state on this OpenPGP card.
///
/// Note: the "factory reset" operation is not directly offered by the
/// 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(card_client: &mut dyn CardClient) -> Result<()> {
// send 4 bad requests to verify pw1
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
for _ in 0..4 {
let verify = commands::verify_pw1_81([0x40; 8].to_vec());
let resp = apdu::send_command(card_client, verify, false)?;
if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
{
return Err(anyhow!("Unexpected status for reset, at pw1."));
}
}
// send 4 bad requests to verify pw3
// [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40]
for _ in 0..4 {
let verify = commands::verify_pw3([0x40; 8].to_vec());
let resp = apdu::send_command(card_client, verify, false)?;
if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
{
return Err(anyhow!("Unexpected status for reset, at pw3."));
}
}
// terminate_df [apdu 00 e6 00 00]
let term = commands::terminate_df();
let resp = apdu::send_command(card_client, term, false)?;
resp.check_ok()?;
// activate_file [apdu 00 44 00 00]
let act = commands::activate_file();
let resp = apdu::send_command(card_client, act, false)?;
resp.check_ok()?;
Ok(())
}
// --- verify/modify ---
/// Does the cardreader support direct pinpad verify?
pub fn feature_pinpad_verify(card_client: &mut dyn CardClient) -> bool {
card_client.feature_pinpad_verify()
}
/// Does the cardreader support direct pinpad modify?
pub fn feature_pinpad_modify(card_client: &mut dyn CardClient) -> bool {
card_client.feature_pinpad_modify()
}
/// 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
/// valid for several attempts.
pub fn verify_pw1_for_signing(
card_client: &mut dyn CardClient,
pin: &str,
) -> Result<Response, Error> {
let verify = commands::verify_pw1_81(pin.as_bytes().to_vec());
apdu::send_command(card_client, verify, false)?.try_into()
}
/// 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
/// valid for several attempts.
pub fn verify_pw1_for_signing_pinpad(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let res = card_client.pinpad_verify(0x81)?;
RawResponse::try_from(res)?.try_into()
}
/// Check the current access of PW1 for signing (mode 81).
///
/// If verification is not required, an empty Ok Response is returned.
///
/// (Note: some cards don't correctly implement this feature,
/// e.g. YubiKey 5)
pub fn check_pw1_for_signing(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let verify = commands::verify_pw1_81(vec![]);
apdu::send_command(card_client, verify, false)?.try_into()
}
/// Verify PW1 (user).
/// (For operations except signing, mode 82).
pub fn verify_pw1(
card_client: &mut dyn CardClient,
pin: &str,
) -> Result<Response, Error> {
let verify = commands::verify_pw1_82(pin.as_bytes().to_vec());
apdu::send_command(card_client, verify, false)?.try_into()
}
/// 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(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let res = card_client.pinpad_verify(0x82)?;
RawResponse::try_from(res)?.try_into()
}
/// Check the current access of PW1.
/// (For operations except signing, mode 82).
///
/// If verification is not required, an empty Ok Response is returned.
///
/// (Note: some cards don't correctly implement this feature,
/// e.g. YubiKey 5)
pub fn check_pw1(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let verify = commands::verify_pw1_82(vec![]);
apdu::send_command(card_client, verify, false)?.try_into()
}
/// Verify PW3 (admin).
pub fn verify_pw3(
card_client: &mut dyn CardClient,
pin: &str,
) -> Result<Response, Error> {
let verify = commands::verify_pw3(pin.as_bytes().to_vec());
apdu::send_command(card_client, verify, false)?.try_into()
}
/// 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(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let res = card_client.pinpad_verify(0x83)?;
RawResponse::try_from(res)?.try_into()
}
/// Check the current access of PW3 (admin).
///
/// If verification is not required, an empty Ok Response is returned.
///
/// (Note: some cards don't correctly implement this feature,
/// e.g. YubiKey 5)
pub fn check_pw3(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let verify = commands::verify_pw3(vec![]);
apdu::send_command(card_client, verify, false)?.try_into()
}
/// Change the value of PW1 (user password).
///
/// The current value of PW1 must be presented in `old` for authorization.
pub fn change_pw1(
card_client: &mut dyn CardClient,
old: &str,
new: &str,
) -> Result<Response, Error> {
let mut data = vec![];
data.extend(old.as_bytes());
data.extend(new.as_bytes());
let change = commands::change_pw1(data);
apdu::send_command(card_client, change, false)?.try_into()
}
/// 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(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let res = card_client.pinpad_modify(0x81)?;
RawResponse::try_from(res)?.try_into()
}
/// Change the value of PW3 (admin password).
///
/// The current value of PW3 must be presented in `old` for authorization.
pub fn change_pw3(
card_client: &mut dyn CardClient,
old: &str,
new: &str,
) -> Result<Response, Error> {
let mut data = vec![];
data.extend(old.as_bytes());
data.extend(new.as_bytes());
let change = commands::change_pw3(data);
apdu::send_command(card_client, change, false)?.try_into()
}
/// 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(
card_client: &mut dyn CardClient,
) -> Result<Response, Error> {
let res = card_client.pinpad_modify(0x83)?;
RawResponse::try_from(res)?.try_into()
}
/// Reset the error counter for PW1 (user password) and set a new value
/// for PW1.
///
/// For authorization, either:
/// - PW3 must have been verified previously,
/// - secure messaging must be currently used,
/// - the resetting_code must be presented.
pub fn reset_retry_counter_pw1(
card_client: &mut dyn CardClient,
new_pw1: Vec<u8>,
resetting_code: Option<Vec<u8>>,
) -> Result<Response, Error> {
let reset = commands::reset_retry_counter_pw1(resetting_code, new_pw1);
apdu::send_command(card_client, reset, false)?.try_into()
}
// --- decrypt ---
/// Decrypt the ciphertext in `dm`, on the card.
///
/// (This is a wrapper around the low-level pso_decipher
/// operation, it builds the required `data` field from `dm`)
pub fn decipher(
card_client: &mut dyn CardClient,
dm: Cryptogram,
) -> Result<Vec<u8>, Error> {
match dm {
Cryptogram::RSA(message) => {
// "Padding indicator byte (00) for RSA" (pg. 69)
let mut data = vec![0x0];
data.extend_from_slice(message);
// Call the card to decrypt `data`
Self::pso_decipher(card_client, data)
}
Cryptogram::ECDH(eph) => {
// "In case of ECDH the card supports a partial decrypt
// only. The input is a cipher DO with the following data:"
// A6 xx Cipher DO
// -> 7F49 xx Public Key DO
// -> 86 xx External Public Key
// External Public Key
let epk = Tlv::new([0x86], Value::S(eph.to_vec()));
// Public Key DO
let pkdo = Tlv::new([0x7f, 0x49], Value::C(vec![epk]));
// Cipher DO
let cdo = Tlv::new([0xa6], Value::C(vec![pkdo]));
Self::pso_decipher(card_client, cdo.serialize())
}
}
}
/// Run decryption operation on the smartcard (low level operation)
/// (7.2.11 PSO: DECIPHER)
fn pso_decipher(
card_client: &mut dyn CardClient,
data: Vec<u8>,
) -> Result<Vec<u8>, Error> {
// The OpenPGP card is already connected and PW1 82 has been verified
let dec_cmd = commands::decryption(data);
let resp = apdu::send_command(card_client, dec_cmd, true)?;
resp.check_ok()?;
Ok(resp.data().map(|d| d.to_vec())?)
}
// --- sign ---
fn digestinfo(hash: Hash) -> Vec<u8> {
match hash {
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
let tlv = Tlv::new(
[0x30],
Value::C(vec![
Tlv::new(
[0x30],
Value::C(vec![
Tlv::new(
[0x06],
// unwrapping is ok, for SHA*
Value::S(hash.oid().unwrap().to_vec()),
),
Tlv::new([0x05], Value::S(vec![])),
]),
),
Tlv::new([0x04], Value::S(hash.digest().to_vec())),
]),
);
tlv.serialize()
}
Hash::EdDSA(d) => d.to_vec(),
Hash::ECDSA(d) => d.to_vec(),
}
}
/// Sign `hash`, on the card.
///
/// This is a wrapper around the low-level
/// pso_compute_digital_signature operation.
/// It builds the required `data` field from `hash`.
///
/// For RSA, this means a "DigestInfo" data structure is generated.
/// (see 7.2.10.2 DigestInfo for RSA).
///
/// With ECC the hash data is processed as is, using
/// pso_compute_digital_signature.
pub fn signature_for_hash(
card_client: &mut dyn CardClient,
hash: Hash,
) -> Result<Vec<u8>, Error> {
Self::pso_compute_digital_signature(
card_client,
Self::digestinfo(hash),
)
}
/// Run signing operation on the smartcard (low level operation)
/// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE)
pub fn pso_compute_digital_signature(
card_client: &mut dyn CardClient,
data: Vec<u8>,
) -> Result<Vec<u8>, Error> {
let cds_cmd = commands::signature(data);
let resp = apdu::send_command(card_client, cds_cmd, true)?;
Ok(resp.data().map(|d| d.to_vec())?)
}
// --- internal authenticate ---
/// Auth-sign `hash`, on the card.
///
/// This is a wrapper around the low-level
/// internal_authenticate operation.
/// It builds the required `data` field from `hash`.
///
/// For RSA, this means a "DigestInfo" data structure is generated.
/// (see 7.2.10.2 DigestInfo for RSA).
///
/// With ECC the hash data is processed as is.
pub fn authenticate_for_hash(
card_client: &mut dyn CardClient,
hash: Hash,
) -> Result<Vec<u8>, Error> {
Self::internal_authenticate(card_client, Self::digestinfo(hash))
}
/// Run signing operation on the smartcard (low level operation)
/// (7.2.13 INTERNAL AUTHENTICATE)
pub fn internal_authenticate(
card_client: &mut dyn CardClient,
data: Vec<u8>,
) -> Result<Vec<u8>, Error> {
let ia_cmd = commands::internal_authenticate(data);
let resp = apdu::send_command(card_client, ia_cmd, true)?;
Ok(resp.data().map(|d| d.to_vec())?)
}
// --- admin ---
pub fn set_name(
card_client: &mut dyn CardClient,
name: &str,
) -> Result<Response, Error> {
let put_name = commands::put_name(name.as_bytes().to_vec());
apdu::send_command(card_client, put_name, false)?.try_into()
}
pub fn set_lang(
card_client: &mut dyn CardClient,
lang: &str,
) -> Result<Response, Error> {
let put_lang = commands::put_lang(lang.as_bytes().to_vec());
apdu::send_command(card_client, put_lang, false)?.try_into()
}
pub fn set_sex(
card_client: &mut dyn CardClient,
sex: Sex,
) -> Result<Response, Error> {
let put_sex = commands::put_sex((&sex).into());
apdu::send_command(card_client, put_sex, false)?.try_into()
}
pub fn set_url(
card_client: &mut dyn CardClient,
url: &str,
) -> Result<Response, Error> {
let put_url = commands::put_url(url.as_bytes().to_vec());
apdu::send_command(card_client, put_url, false)?.try_into()
}
pub fn set_creation_time(
card_client: &mut dyn CardClient,
time: KeyGenerationTime,
key_type: KeyType,
) -> Result<Response, Error> {
// Timestamp update
let time_value: Vec<u8> = time
.get()
.to_be_bytes()
.iter()
.skip_while(|&&e| e == 0)
.copied()
.collect();
let time_cmd =
commands::put_data(&[key_type.timestamp_put_tag()], time_value);
apdu::send_command(card_client, time_cmd, false)?.try_into()
}
pub fn set_fingerprint(
card_client: &mut dyn CardClient,
fp: Fingerprint,
key_type: KeyType,
) -> Result<Response, Error> {
let fp_cmd = commands::put_data(
&[key_type.fingerprint_put_tag()],
fp.as_bytes().to_vec(),
);
apdu::send_command(card_client, fp_cmd, false)?.try_into()
}
/// Set PW Status Bytes.
///
/// If `long` is false, send 1 byte to the card, otherwise 4.
/// According to the spec, length information should not be changed.
///
/// So, effectively, with 'long == false' the setting `pw1_cds_multi`
/// can be changed.
/// With 'long == true', the settings `pw1_pin_block` and `pw3_pin_block`
/// can also be changed.
///
/// (See OpenPGP card spec, pg. 28)
pub fn set_pw_status_bytes(
card_client: &mut dyn CardClient,
pw_status: &PWStatusBytes,
long: bool,
) -> Result<Response, Error> {
let data = pw_status.serialize_for_put(long);
let cmd = commands::put_pw_status(data);
apdu::send_command(card_client, cmd, false)?.try_into()
}
/// Set cardholder certificate (for AUT, DEC or SIG).
///
/// Call select_data() before calling this fn, to select a particular
/// certificate (if the card supports multiple certificates).
pub fn set_cardholder_certificate(
card_client: &mut dyn CardClient,
data: Vec<u8>,
) -> Result<Response, Error> {
let cmd = commands::put_cardholder_certificate(data);
apdu::send_command(card_client, cmd, false)?.try_into()
}
/// Set algorithm attributes
/// (4.4.3.9 Algorithm Attributes)
pub fn set_algorithm_attributes(
card_client: &mut dyn CardClient,
key_type: KeyType,
algo: &Algo,
) -> Result<Response, Error> {
// Command to PUT the algorithm attributes
let cmd = commands::put_data(
&[key_type.algorithm_tag()],
algo.to_data_object()?,
);
apdu::send_command(card_client, cmd, false)?.try_into()
}
/// Set resetting code
/// (4.3.4 Resetting Code)
pub fn set_resetting_code(
card_client: &mut dyn CardClient,
resetting_code: Vec<u8>,
) -> Result<Response, Error> {
let cmd = commands::put_data(&[0xd3], resetting_code);
apdu::send_command(card_client, cmd, false)?.try_into()
}
/// Import an existing private key to the card.
/// (This implicitly sets the algorithm info, fingerprint and timestamp)
pub fn key_import(
card_client: &mut dyn CardClient,
key: Box<dyn CardUploadableKey>,
key_type: KeyType,
) -> Result<(), Error> {
let algo_info = Self::algorithm_information(card_client);
// An error is ok - it's fine if a card doesn't offer a list of
// supported algorithms
let algo_info = algo_info.unwrap_or(None);
keys::key_import(card_client, key, key_type, algo_info)
}
/// Generate a key on the card.
/// (7.2.14 GENERATE ASYMMETRIC KEY PAIR)
///
/// If the `algo` parameter is Some, then this algorithm will be set on
/// the card for "key_type".
///
/// Note: `algo` needs to precisely specify the RSA bitsize of e (if
/// applicable), and import format, with values that the current card
/// supports.
pub fn generate_key(
card_client: &mut dyn CardClient,
fp_from_pub: fn(
&PublicKeyMaterial,
KeyGenerationTime,
KeyType,
) -> Result<Fingerprint, Error>,
key_type: KeyType,
algo: Option<&Algo>,
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> {
keys::gen_key_with_metadata(card_client, fp_from_pub, key_type, algo)
}
/// Generate a key on the card.
/// (7.2.14 GENERATE ASYMMETRIC KEY PAIR)
///
/// This is a wrapper around generate_key() which allows
/// using the simplified `AlgoSimple` algorithm selector enum.
///
/// Note: AlgoSimple doesn't specify card specific details (such as
/// bitsize of e for RSA, and import format). This function determines
/// these values based on information from the card.
pub fn generate_key_simple(
card_client: &mut dyn CardClient,
fp_from_pub: fn(
&PublicKeyMaterial,
KeyGenerationTime,
KeyType,
) -> Result<Fingerprint, Error>,
key_type: KeyType,
simple: AlgoSimple,
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> {
let ard = Self::application_related_data(card_client)?;
let algo_info =
if let Ok(ai) = Self::algorithm_information(card_client) {
ai
} else {
None
};
let algo = simple.determine_algo(key_type, &ard, algo_info)?;
Self::generate_key(card_client, fp_from_pub, key_type, Some(&algo))
}
/// Get public key material from the card.
///
/// 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(
card_client: &mut dyn CardClient,
key_type: KeyType,
) -> Result<PublicKeyMaterial, Error> {
keys::public_key(card_client, key_type)
}
}
impl CardApp {}

View file

@ -10,7 +10,6 @@ use std::time::{SystemTime, UNIX_EPOCH};
use crate::algorithm::{Algo, AlgoInfo, Curve, EccAttrs, RsaAttrs};
use crate::apdu::command::Command;
use crate::apdu::commands;
use crate::card_app::CardApp;
use crate::card_do::{ApplicationRelatedData, Fingerprint, KeyGenerationTime};
use crate::crypto_data::{
CardUploadableKey, EccKey, EccPub, EccType, PrivateKeyMaterial,
@ -43,16 +42,12 @@ pub(crate) fn gen_key_with_metadata(
// Set algo on card if it's Some
if let Some(target_algo) = algo {
// FIXME: caching
let ard = CardApp::application_related_data(card_client)?; // no caching, here!
let ard = card_client.application_related_data()?; // no caching, here!
let ecap = ard.extended_capabilities()?;
// Only set algo if card supports setting of algo attr
if ecap.algo_attrs_changeable() {
CardApp::set_algorithm_attributes(
card_client,
key_type,
target_algo,
)?;
card_client.set_algorithm_attributes(key_type, target_algo)?;
} else {
// Check if the current algo on the card is the one we want, if
// not we return an error.
@ -69,7 +64,7 @@ pub(crate) fn gen_key_with_metadata(
}
// get current (possibly updated) state of algo
let ard = CardApp::application_related_data(card_client)?; // no caching, here!
let ard = card_client.application_related_data()?; // no caching, here!
let cur_algo = ard.algorithm_attributes(key_type)?;
// generate key
@ -91,11 +86,11 @@ pub(crate) fn gen_key_with_metadata(
let ts = ts.into();
CardApp::set_creation_time(card_client, ts, key_type)?;
card_client.set_creation_time(ts, key_type)?;
// calculate/store fingerprint
let fp = fp_from_pub(&pubkey, ts, key_type)?;
CardApp::set_fingerprint(card_client, fp, key_type)?;
card_client.set_fingerprint(fp, key_type)?;
Ok((pubkey, ts))
}
@ -158,7 +153,7 @@ pub(crate) fn public_key(
key_type: KeyType,
) -> Result<PublicKeyMaterial, Error> {
// get current algo
let ard = CardApp::application_related_data(card_client)?; // FIXME: caching
let ard = card_client.application_related_data()?; // FIXME: caching
let algo = ard.algorithm_attributes(key_type)?;
// get public key
@ -186,7 +181,7 @@ pub(crate) fn key_import(
algo_info: Option<AlgoInfo>,
) -> Result<(), Error> {
// FIXME: caching?
let ard = CardApp::application_related_data(card_client)?;
let ard = card_client.application_related_data()?;
let (algo, key_cmd) = match key.private_key()? {
PrivateKeyMaterial::R(rsa_key) => {
@ -222,12 +217,12 @@ pub(crate) fn key_import(
// Only set algo attrs if "Extended Capabilities" lists the feature
if ard.extended_capabilities()?.algo_attrs_changeable() {
CardApp::set_algorithm_attributes(card_client, key_type, &algo)?;
card_client.set_algorithm_attributes(key_type, &algo)?;
}
apdu::send_command(card_client, key_cmd, false)?.check_ok()?;
CardApp::set_fingerprint(card_client, fp, key_type)?;
CardApp::set_creation_time(card_client, key.timestamp(), key_type)?;
card_client.set_fingerprint(fp, key_type)?;
card_client.set_creation_time(key.timestamp(), key_type)?;
Ok(())
}

View file

@ -38,11 +38,21 @@ pub use crate::apdu::response::Response;
pub use crate::card_app::CardApp;
pub use crate::errors::{Error, SmartcardError, StatusBytes};
use anyhow::Result;
use anyhow::{anyhow, Result};
use std::convert::TryFrom;
use std::convert::TryInto;
use std::ops::{Deref, DerefMut};
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple};
use crate::apdu::commands;
use crate::card_do::ApplicationRelatedData;
use crate::apdu::response::RawResponse;
use crate::card_do::{
ApplicationRelatedData, CardholderRelatedData, Fingerprint,
KeyGenerationTime, PWStatusBytes, SecuritySupportTemplate, Sex,
};
use crate::crypto_data::{
CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial,
};
use crate::tlv::tag::Tag;
use crate::tlv::value::Value;
use crate::tlv::Tlv;
@ -95,8 +105,18 @@ pub trait CardClient {
fn pinpad_modify(&mut self, id: u8) -> Result<Vec<u8>>;
}
/// A boxed CardClient (which is Send+Sync).
pub type CardClientBox = Box<dyn CardClient + Send + Sync>;
impl<'a> Deref for dyn CardClient + Send + Sync + 'a {
type Target = dyn CardClient + 'a;
fn deref(&self) -> &Self::Target {
self
}
}
impl<'a> DerefMut for dyn CardClient + Send + Sync + 'a {
fn deref_mut(&mut self) -> &mut Self::Target {
self
}
}
impl<'a> dyn CardClient + 'a {
/// Select the OpenPGP card application
@ -105,7 +125,83 @@ impl<'a> dyn CardClient + 'a {
apdu::send_command(self, select_openpgp, false)?.try_into()
}
// FIXME: this is a duplicate from card_app
/// Get a CardApp based on a CardClient.
///
/// It is expected that SELECT has already been performed on the card
/// beforehand.
///
/// This fn initializes the CardCaps by requesting
/// application_related_data from the card, and setting the
/// capabilities accordingly.
pub fn initialize(&mut self) -> Result<()> {
let ard = self.application_related_data()?;
self.init_caps(&ard)?;
Ok(())
}
/// Initialize the CardCaps settings from the data in `ard`.
///
/// This should be done at an early point, soon after opening the card.
fn init_caps(&mut self, ard: &ApplicationRelatedData) -> Result<()> {
// Determine chaining/extended length support from card
// metadata and cache this information in CardApp (as a
// CardCaps)
let mut ext_support = false;
let mut chaining_support = false;
if let Ok(hist) = ard.historical_bytes() {
if let Some(cc) = hist.card_capabilities() {
chaining_support = cc.command_chaining();
ext_support = cc.extended_lc_le();
}
}
let ext_cap = ard.extended_capabilities()?;
// Get max command/response byte sizes from card
let (max_cmd_bytes, max_rsp_bytes) =
if let Ok(Some(eli)) = ard.extended_length_information() {
// In card 3.x, max lengths come from ExtendedLengthInfo
(eli.max_command_bytes(), eli.max_response_bytes())
} else if let (Some(cmd), Some(rsp)) =
(ext_cap.max_cmd_len(), ext_cap.max_resp_len())
{
// In card 2.x, max lengths come from ExtendedCapabilities
(cmd, rsp)
} else {
// Fallback: use 255 if we have no information from the card
(255, 255)
};
let pw_status = ard.pw_status_bytes()?;
let pw1_max = pw_status.pw1_max_len();
let pw3_max = pw_status.pw3_max_len();
let caps = CardCaps {
ext_support,
chaining_support,
max_cmd_bytes,
max_rsp_bytes,
pw1_max_len: pw1_max,
pw3_max_len: pw3_max,
};
log::debug!("init_card_caps to: {:x?}", caps);
self.init_card_caps(caps);
Ok(())
}
// --- get data ---
/// Get the "application related data" from the card.
///
/// (This data should probably be cached in a higher layer. Some parts of
/// it are needed regularly, and it does not usually change during
/// normal use of a card.)
pub fn application_related_data(
&mut self,
) -> Result<ApplicationRelatedData> {
@ -113,10 +209,742 @@ impl<'a> dyn CardClient + 'a {
let resp = apdu::send_command(self, ad, true)?;
let value = Value::from(resp.data()?, true)?;
log::debug!(" App data Value: {:x?}", value);
log::debug!(" ARD value: {:x?}", value);
Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value)))
}
#[allow(dead_code)]
fn ca_fingerprints() {
unimplemented!()
}
#[allow(dead_code)]
fn key_information() {
unimplemented!()
}
#[allow(dead_code)]
fn uif_pso_cds() {
unimplemented!()
}
#[allow(dead_code)]
fn uif_pso_dec() {
unimplemented!()
}
#[allow(dead_code)]
fn uif_pso_aut() {
unimplemented!()
}
#[allow(dead_code)]
fn uif_attestation() {
unimplemented!()
}
// --- login data (5e) ---
/// Get URL (5f50)
pub fn url(&mut self) -> Result<String> {
let resp = apdu::send_command(self, commands::url(), true)?;
Ok(String::from_utf8_lossy(resp.data()?).to_string())
}
/// Get cardholder related data (65)
pub fn cardholder_related_data(
&mut self,
) -> Result<CardholderRelatedData> {
let crd = commands::cardholder_related_data();
let resp = apdu::send_command(self, crd, true)?;
resp.check_ok()?;
CardholderRelatedData::try_from(resp.data()?)
}
/// Get security support template (7a)
pub fn security_support_template(
&mut self,
) -> Result<SecuritySupportTemplate> {
let sst = commands::security_support_template();
let resp = apdu::send_command(self, sst, true)?;
resp.check_ok()?;
let tlv = Tlv::try_from(resp.data()?)?;
let res = tlv.find(&[0x93].into()).ok_or_else(|| {
anyhow!("Couldn't get SecuritySupportTemplate DO")
})?;
if let Value::S(data) = res {
let mut data = data.to_vec();
assert_eq!(data.len(), 3);
data.insert(0, 0); // prepend a zero
let data: [u8; 4] = data.try_into().unwrap();
let dsc: u32 = u32::from_be_bytes(data);
Ok(SecuritySupportTemplate { dsc })
} else {
Err(anyhow!("Failed to process SecuritySupportTemplate"))
}
}
/// Get cardholder certificate (each for AUT, DEC and SIG).
///
/// Call select_data() before calling this fn, to select a particular
/// certificate (if the card supports multiple certificates).
pub fn cardholder_certificate(&mut self) -> Result<Response, Error> {
let cmd = commands::cardholder_certificate();
apdu::send_command(self, cmd, true)?.try_into()
}
/// Get "Algorithm Information"
pub fn algorithm_information(&mut self) -> Result<Option<AlgoInfo>> {
let resp = apdu::send_command(self, commands::algo_info(), true)?;
resp.check_ok()?;
let ai = AlgoInfo::try_from(resp.data()?)?;
Ok(Some(ai))
}
/// Firmware Version (YubiKey specific (?))
pub fn firmware_version(&mut self) -> Result<Vec<u8>> {
let resp =
apdu::send_command(self, commands::firmware_version(), true)?;
Ok(resp.data()?.into())
}
/// 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<Vec<u8>> {
let resp = apdu::send_command(self, commands::set_identity(id), false);
// Apparently it's normal to get "NotTransacted" from pcsclite when
// the identity switch was successful.
if let Err(Error::Smartcard(SmartcardError::NotTransacted)) = resp {
Ok(vec![])
} else {
Ok(resp?.data()?.into())
}
}
/// SELECT DATA ("select a DO in the current template",
/// e.g. for cardholder certificate)
pub fn select_data(
&mut self,
num: u8,
tag: &[u8],
) -> Result<Response, Error> {
let tlv = Tlv::new(
[0x60],
Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]),
);
let data = tlv.serialize();
let cmd = commands::select_data(num, data);
apdu::send_command(self, cmd, true)?.try_into()
}
// --- optional private DOs (0101 - 0104) ---
/// Get data from "private use" DO.
///
/// `num` must be between 1 and 4.
pub fn private_use_do(&mut self, num: u8) -> Result<Vec<u8>> {
assert!((1..=4).contains(&num));
let cmd = commands::private_use_do(num);
let resp = apdu::send_command(self, cmd, true)?;
Ok(resp.data()?.to_vec())
}
/// Set data of "private use" DO.
///
/// `num` must be between 1 and 4.
///
/// Access condition:
/// - 1/3 need PW1 (82)
/// - 2/4 need PW3
pub fn set_private_use_do(
&mut self,
num: u8,
data: Vec<u8>,
) -> Result<Vec<u8>> {
assert!((1..=4).contains(&num));
let cmd = commands::put_private_use_do(num, data);
let resp = apdu::send_command(self, cmd, true)?;
Ok(resp.data()?.to_vec())
}
// ----------
/// Reset all state on this OpenPGP card.
///
/// Note: the "factory reset" operation is not directly offered by the
/// 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]
for _ in 0..4 {
let verify = commands::verify_pw1_81([0x40; 8].to_vec());
let resp = apdu::send_command(self, verify, false)?;
if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
{
return Err(anyhow!("Unexpected status for reset, at pw1."));
}
}
// send 4 bad requests to verify pw3
// [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40]
for _ in 0..4 {
let verify = commands::verify_pw3([0x40; 8].to_vec());
let resp = apdu::send_command(self, verify, false)?;
if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
{
return Err(anyhow!("Unexpected status for reset, at pw3."));
}
}
// terminate_df [apdu 00 e6 00 00]
let term = commands::terminate_df();
let resp = apdu::send_command(self, term, false)?;
resp.check_ok()?;
// activate_file [apdu 00 44 00 00]
let act = commands::activate_file();
let resp = apdu::send_command(self, act, false)?;
resp.check_ok()?;
Ok(())
}
// --- verify/modify ---
/// Does the cardreader support direct pinpad verify?
pub fn feature_pinpad_verify(card_client: &mut dyn CardClient) -> bool {
card_client.feature_pinpad_verify()
}
/// Does the cardreader support direct pinpad modify?
pub fn feature_pinpad_modify(card_client: &mut dyn CardClient) -> bool {
card_client.feature_pinpad_modify()
}
/// 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
/// valid for several attempts.
pub fn verify_pw1_for_signing(
&mut self,
pin: &str,
) -> Result<Response, Error> {
let verify = commands::verify_pw1_81(pin.as_bytes().to_vec());
apdu::send_command(self, verify, false)?.try_into()
}
/// 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
/// valid for several attempts.
pub fn verify_pw1_for_signing_pinpad(
&mut self,
) -> Result<Response, Error> {
let res = self.pinpad_verify(0x81)?;
RawResponse::try_from(res)?.try_into()
}
/// Check the current access of PW1 for signing (mode 81).
///
/// If verification is not required, an empty Ok Response is returned.
///
/// (Note: some cards don't correctly implement this feature,
/// e.g. YubiKey 5)
pub fn check_pw1_for_signing(&mut self) -> Result<Response, Error> {
let verify = commands::verify_pw1_81(vec![]);
apdu::send_command(self, verify, false)?.try_into()
}
/// Verify PW1 (user).
/// (For operations except signing, mode 82).
pub fn verify_pw1(&mut self, pin: &str) -> Result<Response, Error> {
let verify = commands::verify_pw1_82(pin.as_bytes().to_vec());
apdu::send_command(self, verify, false)?.try_into()
}
/// 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<Response, Error> {
let res = self.pinpad_verify(0x82)?;
RawResponse::try_from(res)?.try_into()
}
/// Check the current access of PW1.
/// (For operations except signing, mode 82).
///
/// If verification is not required, an empty Ok Response is returned.
///
/// (Note: some cards don't correctly implement this feature,
/// e.g. YubiKey 5)
pub fn check_pw1(&mut self) -> Result<Response, Error> {
let verify = commands::verify_pw1_82(vec![]);
apdu::send_command(self, verify, false)?.try_into()
}
/// Verify PW3 (admin).
pub fn verify_pw3(&mut self, pin: &str) -> Result<Response, Error> {
let verify = commands::verify_pw3(pin.as_bytes().to_vec());
apdu::send_command(self, verify, false)?.try_into()
}
/// 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<Response, Error> {
let res = self.pinpad_verify(0x83)?;
RawResponse::try_from(res)?.try_into()
}
/// Check the current access of PW3 (admin).
///
/// If verification is not required, an empty Ok Response is returned.
///
/// (Note: some cards don't correctly implement this feature,
/// e.g. YubiKey 5)
pub fn check_pw3(&mut self) -> Result<Response, Error> {
let verify = commands::verify_pw3(vec![]);
apdu::send_command(self, verify, false)?.try_into()
}
/// Change the value of PW1 (user password).
///
/// The current value of PW1 must be presented in `old` for authorization.
pub fn change_pw1(
&mut self,
old: &str,
new: &str,
) -> Result<Response, Error> {
let mut data = vec![];
data.extend(old.as_bytes());
data.extend(new.as_bytes());
let change = commands::change_pw1(data);
apdu::send_command(self, change, false)?.try_into()
}
/// 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<Response, Error> {
let res = self.pinpad_modify(0x81)?;
RawResponse::try_from(res)?.try_into()
}
/// Change the value of PW3 (admin password).
///
/// The current value of PW3 must be presented in `old` for authorization.
pub fn change_pw3(
&mut self,
old: &str,
new: &str,
) -> Result<Response, Error> {
let mut data = vec![];
data.extend(old.as_bytes());
data.extend(new.as_bytes());
let change = commands::change_pw3(data);
apdu::send_command(self, change, false)?.try_into()
}
/// 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<Response, Error> {
let res = self.pinpad_modify(0x83)?;
RawResponse::try_from(res)?.try_into()
}
/// Reset the error counter for PW1 (user password) and set a new value
/// for PW1.
///
/// For authorization, either:
/// - PW3 must have been verified previously,
/// - secure messaging must be currently used,
/// - the resetting_code must be presented.
pub fn reset_retry_counter_pw1(
&mut self,
new_pw1: Vec<u8>,
resetting_code: Option<Vec<u8>>,
) -> Result<Response, Error> {
let reset = commands::reset_retry_counter_pw1(resetting_code, new_pw1);
apdu::send_command(self, reset, false)?.try_into()
}
// --- decrypt ---
/// Decrypt the ciphertext in `dm`, on the card.
///
/// (This is a wrapper around the low-level pso_decipher
/// operation, it builds the required `data` field from `dm`)
pub fn decipher(&mut self, dm: Cryptogram) -> Result<Vec<u8>, Error> {
match dm {
Cryptogram::RSA(message) => {
// "Padding indicator byte (00) for RSA" (pg. 69)
let mut data = vec![0x0];
data.extend_from_slice(message);
// Call the card to decrypt `data`
self.pso_decipher(data)
}
Cryptogram::ECDH(eph) => {
// "In case of ECDH the card supports a partial decrypt
// only. The input is a cipher DO with the following data:"
// A6 xx Cipher DO
// -> 7F49 xx Public Key DO
// -> 86 xx External Public Key
// External Public Key
let epk = Tlv::new([0x86], Value::S(eph.to_vec()));
// Public Key DO
let pkdo = Tlv::new([0x7f, 0x49], Value::C(vec![epk]));
// Cipher DO
let cdo = Tlv::new([0xa6], Value::C(vec![pkdo]));
self.pso_decipher(cdo.serialize())
}
}
}
/// Run decryption operation on the smartcard (low level operation)
/// (7.2.11 PSO: DECIPHER)
fn pso_decipher(&mut self, data: Vec<u8>) -> Result<Vec<u8>, Error> {
// The OpenPGP card is already connected and PW1 82 has been verified
let dec_cmd = commands::decryption(data);
let resp = apdu::send_command(self, dec_cmd, true)?;
resp.check_ok()?;
Ok(resp.data().map(|d| d.to_vec())?)
}
// --- sign ---
fn digestinfo(hash: Hash) -> Vec<u8> {
match hash {
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
let tlv = Tlv::new(
[0x30],
Value::C(vec![
Tlv::new(
[0x30],
Value::C(vec![
Tlv::new(
[0x06],
// unwrapping is ok, for SHA*
Value::S(hash.oid().unwrap().to_vec()),
),
Tlv::new([0x05], Value::S(vec![])),
]),
),
Tlv::new([0x04], Value::S(hash.digest().to_vec())),
]),
);
tlv.serialize()
}
Hash::EdDSA(d) => d.to_vec(),
Hash::ECDSA(d) => d.to_vec(),
}
}
/// Sign `hash`, on the card.
///
/// This is a wrapper around the low-level
/// pso_compute_digital_signature operation.
/// It builds the required `data` field from `hash`.
///
/// For RSA, this means a "DigestInfo" data structure is generated.
/// (see 7.2.10.2 DigestInfo for RSA).
///
/// With ECC the hash data is processed as is, using
/// pso_compute_digital_signature.
pub fn signature_for_hash(
&mut self,
hash: Hash,
) -> Result<Vec<u8>, Error> {
self.pso_compute_digital_signature(Self::digestinfo(hash))
}
/// Run signing operation on the smartcard (low level operation)
/// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE)
pub fn pso_compute_digital_signature(
&mut self,
data: Vec<u8>,
) -> Result<Vec<u8>, Error> {
let cds_cmd = commands::signature(data);
let resp = apdu::send_command(self, cds_cmd, true)?;
Ok(resp.data().map(|d| d.to_vec())?)
}
// --- internal authenticate ---
/// Auth-sign `hash`, on the card.
///
/// This is a wrapper around the low-level
/// internal_authenticate operation.
/// It builds the required `data` field from `hash`.
///
/// For RSA, this means a "DigestInfo" data structure is generated.
/// (see 7.2.10.2 DigestInfo for RSA).
///
/// With ECC the hash data is processed as is.
pub fn authenticate_for_hash(
&mut self,
hash: Hash,
) -> Result<Vec<u8>, Error> {
self.internal_authenticate(Self::digestinfo(hash))
}
/// Run signing operation on the smartcard (low level operation)
/// (7.2.13 INTERNAL AUTHENTICATE)
pub fn internal_authenticate(
&mut self,
data: Vec<u8>,
) -> Result<Vec<u8>, Error> {
let ia_cmd = commands::internal_authenticate(data);
let resp = apdu::send_command(self, ia_cmd, true)?;
Ok(resp.data().map(|d| d.to_vec())?)
}
// --- admin ---
pub fn set_name(&mut self, name: &str) -> Result<Response, Error> {
let put_name = commands::put_name(name.as_bytes().to_vec());
apdu::send_command(self, put_name, false)?.try_into()
}
pub fn set_lang(&mut self, lang: &str) -> Result<Response, Error> {
let put_lang = commands::put_lang(lang.as_bytes().to_vec());
apdu::send_command(self, put_lang, false)?.try_into()
}
pub fn set_sex(&mut self, sex: Sex) -> Result<Response, Error> {
let put_sex = commands::put_sex((&sex).into());
apdu::send_command(self, put_sex, false)?.try_into()
}
pub fn set_url(&mut self, url: &str) -> Result<Response, Error> {
let put_url = commands::put_url(url.as_bytes().to_vec());
apdu::send_command(self, put_url, false)?.try_into()
}
pub fn set_creation_time(
&mut self,
time: KeyGenerationTime,
key_type: KeyType,
) -> Result<Response, Error> {
// Timestamp update
let time_value: Vec<u8> = time
.get()
.to_be_bytes()
.iter()
.skip_while(|&&e| e == 0)
.copied()
.collect();
let time_cmd =
commands::put_data(&[key_type.timestamp_put_tag()], time_value);
apdu::send_command(self, time_cmd, false)?.try_into()
}
pub fn set_fingerprint(
&mut self,
fp: Fingerprint,
key_type: KeyType,
) -> Result<Response, Error> {
let fp_cmd = commands::put_data(
&[key_type.fingerprint_put_tag()],
fp.as_bytes().to_vec(),
);
apdu::send_command(self, fp_cmd, false)?.try_into()
}
/// Set PW Status Bytes.
///
/// If `long` is false, send 1 byte to the card, otherwise 4.
/// According to the spec, length information should not be changed.
///
/// So, effectively, with 'long == false' the setting `pw1_cds_multi`
/// can be changed.
/// With 'long == true', the settings `pw1_pin_block` and `pw3_pin_block`
/// can also be changed.
///
/// (See OpenPGP card spec, pg. 28)
pub fn set_pw_status_bytes(
&mut self,
pw_status: &PWStatusBytes,
long: bool,
) -> Result<Response, Error> {
let data = pw_status.serialize_for_put(long);
let cmd = commands::put_pw_status(data);
apdu::send_command(self, cmd, false)?.try_into()
}
/// Set cardholder certificate (for AUT, DEC or SIG).
///
/// Call select_data() before calling this fn, to select a particular
/// certificate (if the card supports multiple certificates).
pub fn set_cardholder_certificate(
&mut self,
data: Vec<u8>,
) -> Result<Response, Error> {
let cmd = commands::put_cardholder_certificate(data);
apdu::send_command(self, cmd, false)?.try_into()
}
/// Set algorithm attributes
/// (4.4.3.9 Algorithm Attributes)
pub fn set_algorithm_attributes(
&mut self,
key_type: KeyType,
algo: &Algo,
) -> Result<Response, Error> {
// Command to PUT the algorithm attributes
let cmd = commands::put_data(
&[key_type.algorithm_tag()],
algo.to_data_object()?,
);
apdu::send_command(self, cmd, false)?.try_into()
}
/// Set resetting code
/// (4.3.4 Resetting Code)
pub fn set_resetting_code(
&mut self,
resetting_code: Vec<u8>,
) -> Result<Response, Error> {
let cmd = commands::put_data(&[0xd3], resetting_code);
apdu::send_command(self, cmd, false)?.try_into()
}
/// Import an existing private key to the card.
/// (This implicitly sets the algorithm info, fingerprint and timestamp)
pub fn key_import(
&mut self,
key: Box<dyn CardUploadableKey>,
key_type: KeyType,
) -> Result<(), Error> {
let algo_info = self.algorithm_information();
// An error is ok - it's fine if a card doesn't offer a list of
// supported algorithms
let algo_info = algo_info.unwrap_or(None);
keys::key_import(self, key, key_type, algo_info)
}
/// Generate a key on the card.
/// (7.2.14 GENERATE ASYMMETRIC KEY PAIR)
///
/// If the `algo` parameter is Some, then this algorithm will be set on
/// the card for "key_type".
///
/// Note: `algo` needs to precisely specify the RSA bitsize of e (if
/// applicable), and import format, with values that the current card
/// supports.
pub fn generate_key(
&mut self,
fp_from_pub: fn(
&PublicKeyMaterial,
KeyGenerationTime,
KeyType,
) -> Result<Fingerprint, Error>,
key_type: KeyType,
algo: Option<&Algo>,
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> {
keys::gen_key_with_metadata(self, fp_from_pub, key_type, algo)
}
/// Generate a key on the card.
/// (7.2.14 GENERATE ASYMMETRIC KEY PAIR)
///
/// This is a wrapper around generate_key() which allows
/// using the simplified `AlgoSimple` algorithm selector enum.
///
/// Note: AlgoSimple doesn't specify card specific details (such as
/// bitsize of e for RSA, and import format). This function determines
/// these values based on information from the card.
pub fn generate_key_simple(
&mut self,
fp_from_pub: fn(
&PublicKeyMaterial,
KeyGenerationTime,
KeyType,
) -> Result<Fingerprint, Error>,
key_type: KeyType,
simple: AlgoSimple,
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> {
let ard = self.application_related_data()?;
let algo_info = if let Ok(ai) = self.algorithm_information() {
ai
} else {
None
};
let algo = simple.determine_algo(key_type, &ard, algo_info)?;
Self::generate_key(self, fp_from_pub, key_type, Some(&algo))
}
/// Get public key material from the card.
///
/// 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,
) -> Result<PublicKeyMaterial, Error> {
keys::public_key(self, key_type)
}
}
/// Configuration of the capabilities of a card.

View file

@ -12,7 +12,7 @@ use std::collections::HashMap;
use std::convert::TryInto;
use openpgp_card::card_do::ApplicationRelatedData;
use openpgp_card::{CardApp, CardCaps, CardClient, Error, SmartcardError};
use openpgp_card::{CardCaps, CardClient, Error, SmartcardError};
const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06;
const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07;
@ -393,8 +393,8 @@ impl PcscClient {
}
}
// Get initalized CardApp
CardApp::initialize(&mut self)?;
// Initalize CardClient (set CardCaps from ARD)
<dyn CardClient>::initialize(&mut self)?;
Ok(self)
}

View file

@ -13,8 +13,7 @@ use sequoia_ipc::gnupg::{Agent, Context};
use std::sync::Mutex;
use tokio::runtime::Runtime;
use openpgp_card::{CardApp, Error};
use openpgp_card::{CardCaps, CardClient};
use openpgp_card::{CardCaps, CardClient, Error};
lazy_static! {
static ref RT: Mutex<Runtime> =
@ -66,7 +65,7 @@ impl ScdClient {
let mut card = ScdClient::new(agent, true)?;
card.select_card(serial)?;
CardApp::initialize(&mut card)?;
<dyn CardClient>::initialize(&mut card)?;
Ok(card)
}
@ -80,7 +79,7 @@ impl ScdClient {
pub fn open_yolo(agent: Option<Agent>) -> Result<Self, Error> {
let mut card = ScdClient::new(agent, true)?;
CardApp::initialize(&mut card)?;
<dyn CardClient>::initialize(&mut card)?;
Ok(card)
}

View file

@ -12,7 +12,7 @@ use sequoia_openpgp::serialize::SerializeInto;
use sequoia_openpgp::Cert;
use openpgp_card::algorithm::AlgoSimple;
use openpgp_card::{card_do::Sex, CardApp, KeyType};
use openpgp_card::{card_do::Sex, CardApp, CardClient, KeyType};
use openpgp_card_sequoia::card::{Admin, Open};
use openpgp_card_sequoia::util::{make_cert, public_key_material_to_key};
@ -151,7 +151,7 @@ fn set_identity(
) -> Result<(), Box<dyn std::error::Error>> {
let mut card = util::open_card(ident)?;
CardApp::set_identity(&mut card, id)?;
<dyn CardClient>::set_identity(&mut card, id)?;
Ok(())
}