1use heapless::{String, Vec};
14
15use crate::cap::CapError;
16use crate::command::BuildError;
17use crate::limits::{MAX_KEYTYPE_SUPPORTED, OTHER_DETAIL_MAX, WARNING_DETAIL_MAX};
18use crate::tlv::TlvError;
19
20#[derive(thiserror::Error, Debug)]
22#[non_exhaustive]
23pub enum ScllError {
24 #[error("transport unavailable")]
26 TransportUnavailable,
27 #[error("card removed")]
28 CardRemoved,
29 #[error("reader gone")]
30 ReaderGone,
31 #[error("transport timeout")]
32 Timeout,
33
34 #[error("no SCP protocol the library supports")]
36 ScpProtocolUnsupported,
37 #[error("no common security level")]
38 NoCommonSecurityLevel,
39 #[error("KVN mismatch (card vs supplied keys)")]
40 KvnMismatch,
41 #[error("card cryptogram verification failed")]
42 CardCryptogramFail,
43 #[error("pseudo-random card challenge verification failed")]
44 CardChallengeFail,
45 #[error("EXTERNAL AUTHENTICATE failed (sw={sw:#06x})")]
46 ExternalAuthFail { sw: u16 },
47 #[error("security status not satisfied")]
48 SecurityStatusNotSatisfied,
49 #[error("no secure channel is open on this manager")]
50 NoOpenChannel, #[error("referenced key not found")]
54 KeyNotFound,
55 #[error("key type unsupported")]
56 KeyTypeUnsupported {
57 offered: u8,
58 supported: Vec<u8, MAX_KEYTYPE_SUPPORTED>,
59 },
60 #[error("key check value mismatch")]
61 KeyCheckValueMismatch,
62 #[error("cannot delete the active keyset")]
63 CannotDeleteActiveKeyset,
64
65 #[error("AID length {len} out of range (must be 5..=16 bytes)")]
67 InvalidAid { len: usize },
68 #[error("AID already exists")]
69 AidAlreadyExists,
70 #[error("package AID already exists")]
71 PackageAidExists,
72 #[error("package not found")]
73 PackageNotFound,
74 #[error("resident SD module not found")]
75 ResidentSdNotFound,
76 #[error("load file too large for short APDUs")]
77 LoadTooLarge,
78 #[error("SSD still has applets")]
79 SsdHasApplets,
80 #[error("ELF has other instances")]
81 ElfHasOtherInstances,
82
83 #[error("card not usable (misconfigured or TERMINATED)")]
85 CardNotUsable,
86 #[error("illegal life-cycle transition")]
87 IllegalLifecycleTransition,
88 #[error("conditions of use not satisfied")]
89 ConditionsNotSatisfied,
90 #[error("ISD AID not found")]
91 IsdAidNotFound,
92 #[error("session is not against the ISD")]
93 SessionNotIsd,
94 #[error("target no longer exists")]
95 TargetNoLongerExists,
96 #[error("TERMINATED is out of scope as a set target")]
97 TerminateOutOfScope,
98
99 #[error("parent lacks Authorized Management")]
101 ParentLacksAm,
102 #[error("unsupported privilege")]
103 UnsupportedPrivilege,
104
105 #[error("card returned status word {sw:#06x}")]
107 Card { sw: u16 },
108
109 #[error("malformed card response: {0}")]
111 MalformedResponse(#[from] TlvError),
112 #[error("APDU build error: {0}")]
113 Build(#[from] BuildError),
114 #[error("CAP parse error: {0}")]
115 Cap(#[from] CapError),
116
117 #[error(transparent)]
119 Backend(#[from] BackendError),
120}
121
122impl ScllError {
123 #[must_use]
142 pub fn from_general_sw(sw: u16) -> ScllError {
143 match sw {
144 0x6982 => ScllError::SecurityStatusNotSatisfied,
146 0x6985 => ScllError::ConditionsNotSatisfied,
147 other => ScllError::Card { sw: other },
149 }
150 }
151}
152
153#[derive(Debug, Clone)]
155pub struct Warning {
156 pub kind: WarningKind,
157 pub detail: String<WARNING_DETAIL_MAX>,
158}
159
160#[derive(Debug, Clone)]
162#[non_exhaustive]
163pub enum WarningKind {
164 CardRecognitionDataMissing, KeyInformationTemplateMissing,
166 CardCapabilityInfoMissing,
167 UnknownLifecycleByte(u8),
168 GetStatusParseFailed,
169 LifecycleNoOp, InventoryTruncated,
175}
176
177#[derive(thiserror::Error, Debug)]
181#[non_exhaustive]
182pub enum BackendError {
183 #[error("key import failed: {0}")]
184 KeyImport(String<OTHER_DETAIL_MAX>), #[error("key generation failed: {0}")]
186 KeyGen(String<OTHER_DETAIL_MAX>), #[error("crypto operation failed: {0}")]
188 Crypto(String<OTHER_DETAIL_MAX>), #[error("RNG failure: {0}")]
190 Rng(String<OTHER_DETAIL_MAX>), #[error("operation unsupported by this backend: {0}")]
192 Unsupported(String<OTHER_DETAIL_MAX>),
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
203 fn general_sw_map_is_total_with_card_as_sole_catch_all() {
204 for sw in 0x0000u16..=0xFFFF {
205 match ScllError::from_general_sw(sw) {
206 ScllError::SecurityStatusNotSatisfied => assert_eq!(sw, 0x6982),
207 ScllError::ConditionsNotSatisfied => assert_eq!(sw, 0x6985),
208 ScllError::Card { sw: got } => {
209 assert_eq!(got, sw, "Card must carry the input sw verbatim");
210 assert!(
211 sw != 0x6982 && sw != 0x6985,
212 "dedicated SWs must not fall through"
213 );
214 }
215 other => panic!("sw {sw:#06x} mapped to an unexpected variant: {other:?}"),
216 }
217 }
218 }
219
220 #[test]
221 fn dedicated_general_sws_map_to_their_variants() {
222 assert!(matches!(
223 ScllError::from_general_sw(0x6982),
224 ScllError::SecurityStatusNotSatisfied
225 ));
226 assert!(matches!(
227 ScllError::from_general_sw(0x6985),
228 ScllError::ConditionsNotSatisfied
229 ));
230 }
231
232 #[test]
233 fn success_word_is_not_special_cased() {
234 assert!(matches!(
237 ScllError::from_general_sw(0x9000),
238 ScllError::Card { sw: 0x9000 }
239 ));
240 }
241}