From 8aae0a357e3bee427d3dac575d7704bbb3e1605c Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Fri, 20 Aug 2021 13:17:08 +0200 Subject: [PATCH] Split the `Response` type into an internal `RawResponse` type which also contains the status bytes, and an external `Response` type that can only be generated from a RawResponse with status "ok". This removes the need for external users of openpgp-card to check the status or operations. That is, openpgp-card now always returns an `Err` if the status of a command is not ok. --- card-functionality/src/tests.rs | 77 +++++++++++++++++-------------- openpgp-card-sequoia/src/lib.rs | 45 ++++++------------ openpgp-card-sequoia/src/main.rs | 4 -- openpgp-card/src/apdu/mod.rs | 10 ++-- openpgp-card/src/apdu/response.rs | 55 ++++++++++++++-------- openpgp-card/src/card_app.rs | 38 ++++++++------- openpgp-card/src/errors.rs | 2 +- openpgp-card/src/keys.rs | 16 +++---- pcsc/src/lib.rs | 4 +- 9 files changed, 128 insertions(+), 123 deletions(-) diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index 4988824..ae3694a 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -24,7 +24,8 @@ use crate::util; #[derive(Debug)] pub enum TestResult { - Status([u8; 2]), + Status(OcErrorStatus), + StatusOk, Text(String), } @@ -62,8 +63,7 @@ pub fn test_decrypt( let cert = Cert::from_str(param[0])?; let msg = param[1].to_string(); - let res = ca.verify_pw1("123456")?; - res.check_ok()?; + ca.verify_pw1("123456")?; let res = openpgp_card_sequoia::decrypt(&mut ca, &cert, msg.into_bytes())?; let plain = String::from_utf8_lossy(&res); @@ -80,8 +80,7 @@ pub fn test_sign( ) -> Result { assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'"); - let res = ca.verify_pw1_for_signing("123456")?; - res.check_ok()?; + ca.verify_pw1_for_signing("123456")?; let cert = Cert::from_str(param[0])?; @@ -190,8 +189,7 @@ pub fn test_upload_keys( "test_upload_keys needs a filename for 'cert'" ); - let verify = ca.verify_pw3("12345678")?; - verify.check_ok()?; + ca.verify_pw3("12345678")?; let cert = Cert::from_file(param[0])?; @@ -211,8 +209,7 @@ pub fn test_keygen( ca: &mut CardApp, param: &[&str], ) -> Result { - let verify = ca.verify_pw3("12345678")?; - verify.check_ok()?; + ca.verify_pw3("12345678")?; // Generate all three subkeys on card let algo = param[0]; @@ -328,24 +325,19 @@ pub fn test_set_user_data( ca: &mut CardApp, _param: &[&str], ) -> Result { - let res = ca.verify_pw3("12345678")?; - res.check_ok()?; + ca.verify_pw3("12345678")?; // name - let res = ca.set_name("Bar< { + // e.g. yubikey5 returns an error status! + out.push(TestResult::Status(s)); + } + Err(_) => { + panic!("unexpected error"); + } + Ok(_) => out.push(TestResult::StatusOk), + } + + ca.set_name("Admin< { + // e.g. yubikey5 returns an error status! + out.push(TestResult::Status(s)); + } + Err(_) => { + panic!("unexpected error"); + } + Ok(_) => out.push(TestResult::StatusOk), + } - let res = ca.set_name("There< Result { assert!(pin.len() >= 6); // FIXME: Err - let res = self.card_app.verify_pw1_for_signing(pin); - - if let Ok(resp) = res { - if resp.is_ok() { - return Ok(CardSign { oc: self }); - } + if self.card_app.verify_pw1_for_signing(pin).is_ok() { + Ok(CardSign { oc: self }) + } else { + Err(self) } - - Err(self) } pub fn check_pw1(&mut self) -> Result { @@ -732,15 +725,11 @@ impl CardBase { pub fn verify_pw1(mut self, pin: &str) -> Result { assert!(pin.len() >= 6); // FIXME: Err - let res = self.card_app.verify_pw1(pin); - - if let Ok(resp) = res { - if resp.is_ok() { - return Ok(CardUser { oc: self }); - } + if self.card_app.verify_pw1(pin).is_ok() { + Ok(CardUser { oc: self }) + } else { + Err(self) } - - Err(self) } pub fn check_pw3(&mut self) -> Result { @@ -750,15 +739,11 @@ impl CardBase { pub fn verify_pw3(mut self, pin: &str) -> Result { assert!(pin.len() >= 8); // FIXME: Err - let res = self.card_app.verify_pw3(pin); - - if let Ok(resp) = res { - if resp.is_ok() { - return Ok(CardAdmin { oc: self }); - } + if self.card_app.verify_pw3(pin).is_ok() { + Ok(CardAdmin { oc: self }) + } else { + Err(self) } - - Err(self) } } diff --git a/openpgp-card-sequoia/src/main.rs b/openpgp-card-sequoia/src/main.rs index d76cd33..75e851c 100644 --- a/openpgp-card-sequoia/src/main.rs +++ b/openpgp-card-sequoia/src/main.rs @@ -108,20 +108,16 @@ fn main() -> Result<(), Box> { let res = oc_admin.set_name("Bar< Result { - let mut resp = Response::try_from(send_command_low_level( +) -> Result { + let mut resp = RawResponse::try_from(send_command_low_level( card_client, cmd, expect_reply, )?)?; - while resp.status()[0] == 0x61 { + while resp.status().0 == 0x61 { // More data is available for this command from the card log::debug!(" response was truncated, getting more data"); // Get additional data - let next = Response::try_from(send_command_low_level( + let next = RawResponse::try_from(send_command_low_level( card_client, commands::get_response(), expect_reply, diff --git a/openpgp-card/src/apdu/response.rs b/openpgp-card/src/apdu/response.rs index 86ddac8..5f32718 100644 --- a/openpgp-card/src/apdu/response.rs +++ b/openpgp-card/src/apdu/response.rs @@ -1,19 +1,38 @@ // SPDX-FileCopyrightText: 2021 Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::errors::OcErrorStatus; +use crate::errors::{OcErrorStatus, OpenpgpCardError}; use std::convert::TryFrom; /// APDU Response #[allow(unused)] #[derive(Clone, Debug)] -pub struct Response { +pub(crate) struct RawResponse { data: Vec, sw1: u8, sw2: u8, } -impl Response { +#[derive(Debug)] +pub struct Response { + data: Vec, +} + +impl TryFrom for Response { + type Error = OpenpgpCardError; + + fn try_from(value: RawResponse) -> Result { + if value.is_ok() { + Ok(Response { data: value.data }) + } else { + Err(OpenpgpCardError::OcStatus(OcErrorStatus::from( + value.status(), + ))) + } + } +} + +impl RawResponse { pub fn check_ok(&self) -> Result<(), OcErrorStatus> { if !self.is_ok() { Err(OcErrorStatus::from((self.sw1, self.sw2))) @@ -36,22 +55,22 @@ impl Response { &mut self.data } - pub(crate) fn set_status(&mut self, new_status: [u8; 2]) { - self.sw1 = new_status[0]; - self.sw2 = new_status[1]; + pub(crate) fn set_status(&mut self, new_status: (u8, u8)) { + self.sw1 = new_status.0; + self.sw2 = new_status.1; } - pub fn status(&self) -> [u8; 2] { - [self.sw1, self.sw2] + pub fn status(&self) -> (u8, u8) { + (self.sw1, self.sw2) } - /// Is the response status "ok"? [0x90, 0x00] + /// Is the response status "ok"? (0x90, 0x00) pub fn is_ok(&self) -> bool { - self.status() == [0x90, 0x00] + self.status() == (0x90, 0x00) } } -impl<'a> TryFrom<&[u8]> for Response { +impl<'a> TryFrom<&[u8]> for RawResponse { type Error = OcErrorStatus; fn try_from(buf: &[u8]) -> Result { @@ -59,7 +78,7 @@ impl<'a> TryFrom<&[u8]> for Response { if n < 2 { return Err(OcErrorStatus::ResponseLength(buf.len())); } - Ok(Response { + Ok(RawResponse { data: buf[..n - 2].into(), sw1: buf[n - 2], sw2: buf[n - 1], @@ -67,7 +86,7 @@ impl<'a> TryFrom<&[u8]> for Response { } } -impl TryFrom> for Response { +impl TryFrom> for RawResponse { type Error = OcErrorStatus; fn try_from(mut data: Vec) -> Result { @@ -78,32 +97,32 @@ impl TryFrom> for Response { .pop() .ok_or_else(|| OcErrorStatus::ResponseLength(data.len()))?; - Ok(Response { data, sw1, sw2 }) + Ok(RawResponse { data, sw1, sw2 }) } } #[cfg(test)] mod tests { - use crate::apdu::response::Response; + use crate::apdu::response::RawResponse; use std::convert::TryFrom; #[test] fn test_two_bytes_data_response() { - let res = Response::try_from(vec![0x01, 0x02, 0x90, 0x00]).unwrap(); + let res = RawResponse::try_from(vec![0x01, 0x02, 0x90, 0x00]).unwrap(); assert_eq!(res.is_ok(), true); assert_eq!(res.data, vec![0x01, 0x02]); } #[test] fn test_no_data_response() { - let res = Response::try_from(vec![0x90, 0x00]).unwrap(); + let res = RawResponse::try_from(vec![0x90, 0x00]).unwrap(); assert_eq!(res.is_ok(), true); assert_eq!(res.data, vec![]); } #[test] fn test_more_data_response() { - let res = Response::try_from(vec![0xAB, 0x61, 0x02]).unwrap(); + let res = RawResponse::try_from(vec![0xAB, 0x61, 0x02]).unwrap(); assert_eq!(res.is_ok(), false); assert_eq!(res.data, vec![0xAB]); } diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index 8f6a86b..9a05c17 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -11,6 +11,7 @@ use std::borrow::BorrowMut; use std::convert::TryFrom; +use std::convert::TryInto; use std::time::SystemTime; use anyhow::{anyhow, Result}; @@ -90,7 +91,8 @@ impl CardApp { /// "Select" the OpenPGP card application pub fn select(&mut self) -> Result { let select_openpgp = commands::select_openpgp(); - apdu::send_command(&mut self.card_client, select_openpgp, false) + apdu::send_command(&mut self.card_client, select_openpgp, false)? + .try_into() } // --- application data --- @@ -323,8 +325,8 @@ impl CardApp { let verify = commands::verify_pw1_81([0x40; 8].to_vec()); let resp = apdu::send_command(&mut self.card_client, verify, false)?; - if !(resp.status() == [0x69, 0x82] - || resp.status() == [0x69, 0x83]) + if !(resp.status() == (0x69, 0x82) + || resp.status() == (0x69, 0x83)) { return Err(anyhow!("Unexpected status for reset, at pw1.")); } @@ -337,8 +339,8 @@ impl CardApp { let resp = apdu::send_command(&mut self.card_client, verify, false)?; - if !(resp.status() == [0x69, 0x82] - || resp.status() == [0x69, 0x83]) + if !(resp.status() == (0x69, 0x82) + || resp.status() == (0x69, 0x83)) { return Err(anyhow!("Unexpected status for reset, at pw3.")); } @@ -367,12 +369,12 @@ impl CardApp { assert!(pin.len() >= 6); // FIXME: Err let verify = commands::verify_pw1_81(pin.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, verify, false) + apdu::send_command(&mut self.card_client, verify, false)?.try_into() } pub fn check_pw1(&mut self) -> Result { let verify = commands::verify_pw1_82(vec![]); - apdu::send_command(&mut self.card_client, verify, false) + apdu::send_command(&mut self.card_client, verify, false)?.try_into() } pub fn verify_pw1( @@ -382,12 +384,12 @@ impl CardApp { assert!(pin.len() >= 6); // FIXME: Err let verify = commands::verify_pw1_82(pin.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, verify, false) + apdu::send_command(&mut self.card_client, verify, false)?.try_into() } pub fn check_pw3(&mut self) -> Result { let verify = commands::verify_pw3(vec![]); - apdu::send_command(&mut self.card_client, verify, false) + apdu::send_command(&mut self.card_client, verify, false)?.try_into() } pub fn verify_pw3( @@ -397,7 +399,7 @@ impl CardApp { assert!(pin.len() >= 8); // FIXME: Err let verify = commands::verify_pw3(pin.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, verify, false) + apdu::send_command(&mut self.card_client, verify, false)?.try_into() } // --- decrypt --- @@ -503,7 +505,7 @@ impl CardApp { name: &str, ) -> Result { let put_name = commands::put_name(name.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, put_name, false) + apdu::send_command(&mut self.card_client, put_name, false)?.try_into() } pub fn set_lang( @@ -511,12 +513,14 @@ impl CardApp { lang: &str, ) -> Result { let put_lang = commands::put_lang(lang.as_bytes().to_vec()); - apdu::send_command(self.card_client.borrow_mut(), put_lang, false) + apdu::send_command(self.card_client.borrow_mut(), put_lang, false)? + .try_into() } pub fn set_sex(&mut self, sex: Sex) -> Result { let put_sex = commands::put_sex((&sex).into()); - apdu::send_command(self.card_client.borrow_mut(), put_sex, false) + apdu::send_command(self.card_client.borrow_mut(), put_sex, false)? + .try_into() } pub fn set_url( @@ -524,7 +528,7 @@ impl CardApp { url: &str, ) -> Result { let put_url = commands::put_url(url.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, put_url, false) + apdu::send_command(&mut self.card_client, put_url, false)?.try_into() } pub fn set_creation_time( @@ -545,7 +549,7 @@ impl CardApp { time_value, ); - apdu::send_command(&mut self.card_client, time_cmd, false) + apdu::send_command(&mut self.card_client, time_cmd, false)?.try_into() } pub fn set_fingerprint( @@ -558,7 +562,7 @@ impl CardApp { fp.to_vec(), ); - apdu::send_command(self.card(), fp_cmd, false) + apdu::send_command(self.card(), fp_cmd, false)?.try_into() } /// Set algorithm attributes [4.4.3.9 Algorithm Attributes] @@ -582,7 +586,7 @@ impl CardApp { // Command to PUT the algorithm attributes let cmd = commands::put_data(&[key_type.get_algorithm_tag()], data); - apdu::send_command(&mut self.card_client, cmd, false) + apdu::send_command(&mut self.card_client, cmd, false)?.try_into() } fn rsa_algo_attrs(algo_attrs: &RsaAttrs) -> Result> { diff --git a/openpgp-card/src/errors.rs b/openpgp-card/src/errors.rs index 62d885e..e4313f4 100644 --- a/openpgp-card/src/errors.rs +++ b/openpgp-card/src/errors.rs @@ -41,7 +41,7 @@ impl From for OpenpgpCardError { } /// OpenPGP card "Status Byte" errors -#[derive(Error, Debug)] +#[derive(Error, Debug, PartialEq)] pub enum OcErrorStatus { #[error("Selected file or DO in termination state")] TerminationState, diff --git a/openpgp-card/src/keys.rs b/openpgp-card/src/keys.rs index 954696f..b7a7c40 100644 --- a/openpgp-card/src/keys.rs +++ b/openpgp-card/src/keys.rs @@ -32,9 +32,7 @@ pub(crate) fn gen_key_with_metadata( ) -> Result<(PublicKeyMaterial, u32), OpenpgpCardError> { // set algo on card if it's Some if let Some(algo) = algo { - card_app - .set_algorithm_attributes(key_type, algo)? - .check_ok()?; + card_app.set_algorithm_attributes(key_type, algo)?; } // algo @@ -58,11 +56,11 @@ pub(crate) fn gen_key_with_metadata( .map_err(|e| OpenpgpCardError::InternalError(anyhow!(e)))? .as_secs() as u32; - card_app.set_creation_time(ts, key_type)?.check_ok()?; + card_app.set_creation_time(ts, key_type)?; // calculate/store fingerprint let fp = fp_from_pub(&pubkey, time, key_type)?; - card_app.set_fingerprint(fp, key_type)?.check_ok()?; + card_app.set_fingerprint(fp, key_type)?; Ok((pubkey, ts)) } @@ -438,15 +436,13 @@ fn copy_key_to_card( // FIXME: Only write algo attributes to the card if "extended // capabilities" show that they are changeable! - card_app - .set_algorithm_attributes(key_type, algo)? - .check_ok()?; + card_app.set_algorithm_attributes(key_type, algo)?; apdu::send_command(card_app.card(), key_cmd, false)?.check_ok()?; - card_app.set_fingerprint(fp, key_type)?.check_ok()?; + card_app.set_fingerprint(fp, key_type)?; - card_app.set_creation_time(ts, key_type)?.check_ok()?; + card_app.set_creation_time(ts, key_type)?; Ok(()) } diff --git a/pcsc/src/lib.rs b/pcsc/src/lib.rs index 080a47b..fc35415 100644 --- a/pcsc/src/lib.rs +++ b/pcsc/src/lib.rs @@ -99,9 +99,7 @@ impl PcscClient { let ccb = Box::new(card_client) as CardClientBox; let mut ca = CardApp::new(ccb); - let resp = ca.select()?; - - if resp.is_ok() { + if ca.select().is_ok() { Ok(ca) } else { Err(OpenpgpCardError::Smartcard(