Use StatusBytes in RawResponse (instead of a pair of u8).

Replace status bytes constants in the code with StatusBytes enum variants.
This commit is contained in:
Heiko Schaefer 2021-09-07 13:28:01 +02:00
parent c5d03bd677
commit 2e7ee82a58
4 changed files with 51 additions and 38 deletions

View file

@ -39,28 +39,30 @@ pub(crate) fn send_command(
expect_reply, expect_reply,
)?)?; )?)?;
while resp.status().0 == 0x61 { while let StatusBytes::OkBytesAvailable(_) = resp.status() {
// More data is available for this command from the card // More data is available for this command from the card
log::debug!(" chained response, getting more data");
log::debug!(" response was truncated, getting more data"); // Get next chunk of data
// Get additional data
let next = RawResponse::try_from(send_command_low_level( let next = RawResponse::try_from(send_command_low_level(
card_client, card_client,
commands::get_response(), commands::get_response(),
expect_reply, expect_reply,
)?)?; )?)?;
// Only continue if status is 0x61xx or 0x9000. match next.status() {
if next.status().0 != 0x61 && next.status() != (0x90, 0x0) { StatusBytes::OkBytesAvailable(_) | StatusBytes::Ok => {
return Err(Error::CardStatus(StatusBytes::from(next.status()))); log::debug!(
" appending {} bytes to response",
next.raw_data().len()
);
// Append new data to resp.data and overwrite status.
resp.raw_mut_data().extend_from_slice(next.raw_data());
resp.set_status(next.status());
}
error => return Err(error.into()),
} }
log::debug!(" appending {} bytes to response", next.raw_data().len());
// Append new data to resp.data and overwrite status.
resp.raw_mut_data().extend_from_slice(next.raw_data());
resp.set_status(next.status());
} }
log::debug!(" final response len: {}", resp.raw_data().len()); log::debug!(" final response len: {}", resp.raw_data().len());
@ -156,11 +158,11 @@ fn send_command_low_level(
let serialized = let serialized =
partial.serialize(ext).map_err(Error::InternalError)?; partial.serialize(ext).map_err(Error::InternalError)?;
log::debug!(" -> chunked APDU command: {:x?}", &serialized); log::debug!(" -> chained APDU command: {:x?}", &serialized);
let resp = card_client.transmit(&serialized, buf_size)?; let resp = card_client.transmit(&serialized, buf_size)?;
log::debug!(" <- APDU chunk response: {:x?}", &resp); log::debug!(" <- APDU response: {:x?}", &resp);
if resp.len() < 2 { if resp.len() < 2 {
return Err(Error::ResponseLength(resp.len())); return Err(Error::ResponseLength(resp.len()));
@ -171,13 +173,15 @@ fn send_command_low_level(
let sw1 = resp[resp.len() - 2]; let sw1 = resp[resp.len() - 2];
let sw2 = resp[resp.len() - 1]; let sw2 = resp[resp.len() - 1];
let status = StatusBytes::from((sw1, sw2));
// ISO: "If SW1-SW2 is set to '6883', then the last // ISO: "If SW1-SW2 is set to '6883', then the last
// command of the chain is expected." // command of the chain is expected."
if !((sw1 == 0x90 && sw2 == 0x00) if !(status == StatusBytes::Ok
|| (sw1 == 0x68 && sw2 == 0x83)) || status == StatusBytes::LastCommandOfChainExpected)
{ {
// Unexpected status for a non-final chunked response // Unexpected status for a non-final chunked response
return Err(StatusBytes::from((sw1, sw2)).into()); return Err(status.into());
} }
// ISO: "If SW1-SW2 is set to '6884', then command // ISO: "If SW1-SW2 is set to '6884', then command

View file

@ -30,8 +30,7 @@ impl Response {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct RawResponse { pub(crate) struct RawResponse {
data: Vec<u8>, data: Vec<u8>,
sw1: u8, status: StatusBytes,
sw2: u8,
} }
impl TryFrom<RawResponse> for Response { impl TryFrom<RawResponse> for Response {
@ -41,7 +40,7 @@ impl TryFrom<RawResponse> for Response {
if value.is_ok() { if value.is_ok() {
Ok(Response { data: value.data }) Ok(Response { data: value.data })
} else { } else {
Err(Error::CardStatus(StatusBytes::from(value.status()))) Err(value.status().into())
} }
} }
} }
@ -49,7 +48,7 @@ impl TryFrom<RawResponse> for Response {
impl RawResponse { impl RawResponse {
pub fn check_ok(&self) -> Result<(), StatusBytes> { pub fn check_ok(&self) -> Result<(), StatusBytes> {
if !self.is_ok() { if !self.is_ok() {
Err(StatusBytes::from((self.sw1, self.sw2))) Err(self.status())
} else { } else {
Ok(()) Ok(())
} }
@ -69,18 +68,17 @@ impl RawResponse {
&mut self.data &mut self.data
} }
pub(crate) fn set_status(&mut self, new_status: (u8, u8)) { pub(crate) fn set_status(&mut self, new_status: StatusBytes) {
self.sw1 = new_status.0; self.status = new_status;
self.sw2 = new_status.1;
} }
pub fn status(&self) -> (u8, u8) { pub fn status(&self) -> StatusBytes {
(self.sw1, self.sw2) self.status
} }
/// Is the response status "ok"? (0x90, 0x00) /// Is the response status "ok"? (0x90, 0x00)
pub fn is_ok(&self) -> bool { pub fn is_ok(&self) -> bool {
self.status() == (0x90, 0x00) self.status() == StatusBytes::Ok
} }
} }
@ -95,7 +93,9 @@ impl TryFrom<Vec<u8>> for RawResponse {
.pop() .pop()
.ok_or_else(|| Error::ResponseLength(data.len()))?; .ok_or_else(|| Error::ResponseLength(data.len()))?;
Ok(RawResponse { data, sw1, sw2 }) let status = (sw1, sw2).into();
Ok(RawResponse { data, status })
} }
} }

View file

@ -19,8 +19,8 @@ use crate::crypto_data::{
CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial, CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial,
}; };
use crate::tlv::{tag::Tag, value::Value, Tlv}; use crate::tlv::{tag::Tag, value::Value, Tlv};
use crate::Error;
use crate::{apdu, keys, CardCaps, CardClientBox, KeyType}; use crate::{apdu, keys, CardCaps, CardClientBox, KeyType};
use crate::{Error, StatusBytes};
/// Low-level access to OpenPGP card functionality. /// Low-level access to OpenPGP card functionality.
/// ///
@ -264,8 +264,8 @@ impl CardApp {
let verify = commands::verify_pw1_81([0x40; 8].to_vec()); let verify = commands::verify_pw1_81([0x40; 8].to_vec());
let resp = let resp =
apdu::send_command(&mut self.card_client, verify, false)?; apdu::send_command(&mut self.card_client, verify, false)?;
if !(resp.status() == (0x69, 0x82) if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied
|| resp.status() == (0x69, 0x83)) || resp.status() == StatusBytes::AuthenticationMethodBlocked)
{ {
return Err(anyhow!("Unexpected status for reset, at pw1.")); return Err(anyhow!("Unexpected status for reset, at pw1."));
} }
@ -278,8 +278,8 @@ impl CardApp {
let resp = let resp =
apdu::send_command(&mut self.card_client, verify, false)?; apdu::send_command(&mut self.card_client, verify, false)?;
if !(resp.status() == (0x69, 0x82) if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied
|| resp.status() == (0x69, 0x83)) || resp.status() == StatusBytes::AuthenticationMethodBlocked)
{ {
return Err(anyhow!("Unexpected status for reset, at pw3.")); return Err(anyhow!("Unexpected status for reset, at pw3."));
} }

View file

@ -42,10 +42,16 @@ impl From<anyhow::Error> for Error {
} }
} }
/// OpenPGP card "Status Bytes" errors /// OpenPGP card "Status Bytes" (ok statuses and errors)
#[derive(thiserror::Error, Debug, PartialEq)] #[derive(thiserror::Error, Debug, PartialEq, Copy, Clone)]
#[non_exhaustive] #[non_exhaustive]
pub enum StatusBytes { pub enum StatusBytes {
#[error("Command correct")]
Ok,
#[error("Command correct, [{0}] bytes available in response")]
OkBytesAvailable(u8),
#[error("Selected file or DO in termination state")] #[error("Selected file or DO in termination state")]
TerminationState, TerminationState,
@ -74,7 +80,7 @@ pub enum StatusBytes {
LastCommandOfChainExpected, LastCommandOfChainExpected,
#[error("Command chaining not supported")] #[error("Command chaining not supported")]
CommandChainingUnsupported, CommandChainingNotSupported,
#[error("Security status not satisfied")] #[error("Security status not satisfied")]
SecurityStatusNotSatisfied, SecurityStatusNotSatisfied,
@ -119,6 +125,9 @@ pub enum StatusBytes {
impl From<(u8, u8)> for StatusBytes { impl From<(u8, u8)> for StatusBytes {
fn from(status: (u8, u8)) -> Self { fn from(status: (u8, u8)) -> Self {
match (status.0, status.1) { match (status.0, status.1) {
(0x90, 0x00) => StatusBytes::Ok,
(0x61, bytes) => StatusBytes::OkBytesAvailable(bytes),
(0x62, 0x85) => StatusBytes::TerminationState, (0x62, 0x85) => StatusBytes::TerminationState,
(0x63, 0xC0..=0xCF) => { (0x63, 0xC0..=0xCF) => {
StatusBytes::PasswordNotChecked(status.1 & 0xf) StatusBytes::PasswordNotChecked(status.1 & 0xf)
@ -130,7 +139,7 @@ impl From<(u8, u8)> for StatusBytes {
(0x68, 0x81) => StatusBytes::LogicalChannelNotSupported, (0x68, 0x81) => StatusBytes::LogicalChannelNotSupported,
(0x68, 0x82) => StatusBytes::SecureMessagingNotSupported, (0x68, 0x82) => StatusBytes::SecureMessagingNotSupported,
(0x68, 0x83) => StatusBytes::LastCommandOfChainExpected, (0x68, 0x83) => StatusBytes::LastCommandOfChainExpected,
(0x68, 0x84) => StatusBytes::CommandChainingUnsupported, (0x68, 0x84) => StatusBytes::CommandChainingNotSupported,
(0x69, 0x82) => StatusBytes::SecurityStatusNotSatisfied, (0x69, 0x82) => StatusBytes::SecurityStatusNotSatisfied,
(0x69, 0x83) => StatusBytes::AuthenticationMethodBlocked, (0x69, 0x83) => StatusBytes::AuthenticationMethodBlocked,
(0x69, 0x85) => StatusBytes::ConditionOfUseNotSatisfied, (0x69, 0x85) => StatusBytes::ConditionOfUseNotSatisfied,