From a39f25d8a32bd6c747bb79c0333c9d83d4c4d455 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Fri, 17 Sep 2021 13:36:20 +0200 Subject: [PATCH] Handle SW_EXACT_LENGTH (0x6c??) in send_command() --- openpgp-card/src/apdu.rs | 21 +++++++++++++++++---- openpgp-card/src/apdu/command.rs | 24 +++++++++++++++++++----- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/openpgp-card/src/apdu.rs b/openpgp-card/src/apdu.rs index 12d4f82..2d0496c 100644 --- a/openpgp-card/src/apdu.rs +++ b/openpgp-card/src/apdu.rs @@ -11,6 +11,7 @@ pub mod response; use anyhow::Result; use std::convert::TryFrom; +use crate::apdu::command::Expect; use crate::apdu::{command::Command, response::RawResponse}; use crate::{CardClientBox, Error, StatusBytes}; @@ -28,10 +29,22 @@ pub(crate) fn send_command( ) -> Result { let mut resp = RawResponse::try_from(send_command_low_level( card_client, - cmd, - expect_reply, + cmd.clone(), + if expect_reply { + Expect::Some + } else { + Expect::Empty + }, )?)?; + if let StatusBytes::UnknownStatus(0x6c, size) = resp.status() { + resp = RawResponse::try_from(send_command_low_level( + card_client, + cmd, + Expect::Short(size), + )?)?; + } + while let StatusBytes::OkBytesAvailable(_) = resp.status() { // More data is available for this command from the card log::debug!(" chained response, getting more data"); @@ -40,7 +53,7 @@ pub(crate) fn send_command( let next = RawResponse::try_from(send_command_low_level( card_client, commands::get_response(), - expect_reply, + Expect::Some, )?)?; match next.status() { @@ -71,7 +84,7 @@ pub(crate) fn send_command( fn send_command_low_level( card_client: &mut CardClientBox, cmd: Command, - expect_response: bool, + expect_response: Expect, ) -> Result, Error> { let (ext_support, chaining_support, mut max_cmd_bytes, max_rsp_bytes) = if let Some(caps) = card_client.get_caps() { diff --git a/openpgp-card/src/apdu/command.rs b/openpgp-card/src/apdu/command.rs index d0bdb2e..3c77898 100644 --- a/openpgp-card/src/apdu/command.rs +++ b/openpgp-card/src/apdu/command.rs @@ -6,6 +6,13 @@ use anyhow::Result; +#[derive(Clone, Copy)] +pub enum Expect { + Empty, + Some, + Short(u8), +} + #[derive(Clone, Debug)] pub(crate) struct Command { // Class byte (CLA) @@ -63,7 +70,7 @@ impl Command { pub(crate) fn serialize( &self, ext_len: bool, - expect_response: bool, + expect_response: Expect, ) -> Result> { // FIXME? (from scd/apdu.c): // T=0 does not allow the use of Lc together with Le; @@ -101,18 +108,22 @@ impl Command { /// Encode value for Le field /// ("maximum number of bytes expected in the response data field"). - fn make_le(nc: u16, ext_len: bool, expect_response: bool) -> Vec { + fn make_le(nc: u16, ext_len: bool, expect_response: Expect) -> Vec { match (ext_len, expect_response) { - (_, false) => { + (_, Expect::Empty) => { // No response data expected. // "If the Le field is absent, then Ne is zero" vec![] } - (false, true) => { + (false, Expect::Some) => { // A short Le field consists of one byte with any value vec![0] } - (true, true) => { + (false, Expect::Short(size)) => { + // A short Le field consists of one byte with any value + vec![size] + } + (true, Expect::Some) => { if nc == 0 { // "three bytes (one byte set to '00' followed by two // bytes with any value) if the Lc field is absent" @@ -123,6 +134,9 @@ impl Command { vec![0, 0] } } + _ => { + unreachable!("This should not happen") + } } } }