use crate::StatusWord;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
InvalidArgument,
InvalidPin,
InvalidResponse,
ProtocolViolation,
LimitExceeded,
AuthenticationFailed,
DeviceAuthenticationFailed,
PinBlocked,
SecurityStatusNotSatisfied,
ConditionsNotSatisfied,
NotFound,
UnsupportedDevice,
UnsupportedFeature,
UnsupportedAlgorithm,
CapabilityUnknown,
UnsupportedProtocolVersion,
UnexpectedStatusWord,
OperationStateError,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Phase {
Construction,
Select,
Command,
Authentication,
Parsing,
Conversation,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SecretReference {
Pin,
Puk,
ManagementKey,
AdminPin,
OathAccess,
Pw1Sign,
Pw1Other,
Pw3,
ResetCode,
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("{kind:?} during {phase:?}")]
pub struct Error {
pub kind: ErrorKind,
pub phase: Phase,
pub status_word: Option<StatusWord>,
pub application_status: Option<u8>,
pub reference: Option<SecretReference>,
pub retries_remaining: Option<u8>,
}
impl Error {
pub fn new(kind: ErrorKind) -> Self {
Self {
kind,
phase: Phase::Construction,
status_word: None,
application_status: None,
reference: None,
retries_remaining: None,
}
}
pub fn at(mut self, phase: Phase) -> Self {
self.phase = phase;
self
}
pub fn status(sw: StatusWord, phase: Phase, reference: Option<SecretReference>) -> Self {
let pin_reference = matches!(
reference,
Some(
SecretReference::Pin
| SecretReference::Puk
| SecretReference::AdminPin
| SecretReference::Pw1Sign
| SecretReference::Pw1Other
| SecretReference::Pw3
| SecretReference::ResetCode
)
);
let kind = match sw.raw() {
0x6983 if pin_reference => ErrorKind::PinBlocked,
0x6982 => ErrorKind::SecurityStatusNotSatisfied,
0x6985 => ErrorKind::ConditionsNotSatisfied,
0x6a82 | 0x6a88 if phase == Phase::Select => ErrorKind::UnsupportedDevice,
0x6a82 | 0x6a88 => ErrorKind::NotFound,
0x6d00 | 0x6e00 => ErrorKind::UnsupportedFeature,
n if n & 0xfff0 == 0x63c0 && reference.is_some() => ErrorKind::AuthenticationFailed,
_ => ErrorKind::UnexpectedStatusWord,
};
Self {
kind,
phase,
status_word: Some(sw),
application_status: None,
reference,
retries_remaining: if pin_reference && sw.raw() & 0xfff0 == 0x63c0 {
Some((sw.raw() & 15) as u8)
} else {
None
},
}
}
}