use heapless::{String, Vec};
use crate::cap::CapError;
use crate::command::BuildError;
use crate::limits::{MAX_KEYTYPE_SUPPORTED, OTHER_DETAIL_MAX, WARNING_DETAIL_MAX};
use crate::tlv::TlvError;
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ScllError {
#[error("transport unavailable")]
TransportUnavailable,
#[error("card removed")]
CardRemoved,
#[error("reader gone")]
ReaderGone,
#[error("transport timeout")]
Timeout,
#[error("no SCP protocol the library supports")]
ScpProtocolUnsupported,
#[error("no common security level")]
NoCommonSecurityLevel,
#[error("KVN mismatch (card vs supplied keys)")]
KvnMismatch,
#[error("card cryptogram verification failed")]
CardCryptogramFail,
#[error("pseudo-random card challenge verification failed")]
CardChallengeFail,
#[error("EXTERNAL AUTHENTICATE failed (sw={sw:#06x})")]
ExternalAuthFail { sw: u16 },
#[error("security status not satisfied")]
SecurityStatusNotSatisfied,
#[error("no secure channel is open on this manager")]
NoOpenChannel,
#[error("referenced key not found")]
KeyNotFound,
#[error("key type unsupported")]
KeyTypeUnsupported {
offered: u8,
supported: Vec<u8, MAX_KEYTYPE_SUPPORTED>,
},
#[error("key check value mismatch")]
KeyCheckValueMismatch,
#[error("cannot delete the active keyset")]
CannotDeleteActiveKeyset,
#[error("AID length {len} out of range (must be 5..=16 bytes)")]
InvalidAid { len: usize },
#[error("AID already exists")]
AidAlreadyExists,
#[error("package AID already exists")]
PackageAidExists,
#[error("package not found")]
PackageNotFound,
#[error("resident SD module not found")]
ResidentSdNotFound,
#[error("load file too large for short APDUs")]
LoadTooLarge,
#[error("SSD still has applets")]
SsdHasApplets,
#[error("ELF has other instances")]
ElfHasOtherInstances,
#[error("card not usable (misconfigured or TERMINATED)")]
CardNotUsable,
#[error("illegal life-cycle transition")]
IllegalLifecycleTransition,
#[error("conditions of use not satisfied")]
ConditionsNotSatisfied,
#[error("ISD AID not found")]
IsdAidNotFound,
#[error("session is not against the ISD")]
SessionNotIsd,
#[error("target no longer exists")]
TargetNoLongerExists,
#[error("TERMINATED is out of scope as a set target")]
TerminateOutOfScope,
#[error("parent lacks Authorized Management")]
ParentLacksAm,
#[error("unsupported privilege")]
UnsupportedPrivilege,
#[error("card returned status word {sw:#06x}")]
Card { sw: u16 },
#[error("malformed card response: {0}")]
MalformedResponse(#[from] TlvError),
#[error("APDU build error: {0}")]
Build(#[from] BuildError),
#[error("CAP parse error: {0}")]
Cap(#[from] CapError),
#[error(transparent)]
Backend(#[from] BackendError),
}
impl ScllError {
#[must_use]
pub fn from_general_sw(sw: u16) -> ScllError {
match sw {
0x6982 => ScllError::SecurityStatusNotSatisfied,
0x6985 => ScllError::ConditionsNotSatisfied,
other => ScllError::Card { sw: other },
}
}
}
#[derive(Debug, Clone)]
pub struct Warning {
pub kind: WarningKind,
pub detail: String<WARNING_DETAIL_MAX>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum WarningKind {
CardRecognitionDataMissing, KeyInformationTemplateMissing,
CardCapabilityInfoMissing,
UnknownLifecycleByte(u8),
GetStatusParseFailed,
LifecycleNoOp, InventoryTruncated,
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum BackendError {
#[error("key import failed: {0}")]
KeyImport(String<OTHER_DETAIL_MAX>), #[error("key generation failed: {0}")]
KeyGen(String<OTHER_DETAIL_MAX>), #[error("crypto operation failed: {0}")]
Crypto(String<OTHER_DETAIL_MAX>), #[error("RNG failure: {0}")]
Rng(String<OTHER_DETAIL_MAX>), #[error("operation unsupported by this backend: {0}")]
Unsupported(String<OTHER_DETAIL_MAX>),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn general_sw_map_is_total_with_card_as_sole_catch_all() {
for sw in 0x0000u16..=0xFFFF {
match ScllError::from_general_sw(sw) {
ScllError::SecurityStatusNotSatisfied => assert_eq!(sw, 0x6982),
ScllError::ConditionsNotSatisfied => assert_eq!(sw, 0x6985),
ScllError::Card { sw: got } => {
assert_eq!(got, sw, "Card must carry the input sw verbatim");
assert!(
sw != 0x6982 && sw != 0x6985,
"dedicated SWs must not fall through"
);
}
other => panic!("sw {sw:#06x} mapped to an unexpected variant: {other:?}"),
}
}
}
#[test]
fn dedicated_general_sws_map_to_their_variants() {
assert!(matches!(
ScllError::from_general_sw(0x6982),
ScllError::SecurityStatusNotSatisfied
));
assert!(matches!(
ScllError::from_general_sw(0x6985),
ScllError::ConditionsNotSatisfied
));
}
#[test]
fn success_word_is_not_special_cased() {
assert!(matches!(
ScllError::from_general_sw(0x9000),
ScllError::Card { sw: 0x9000 }
));
}
}