1use core::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[repr(u8)]
13pub enum StatusCode {
14 Success = 0x00,
16
17 InvalidCommand = 0x01,
19
20 InvalidParameter = 0x02,
22
23 InvalidLength = 0x03,
25
26 InvalidSeq = 0x04,
28
29 Timeout = 0x05,
31
32 ChannelBusy = 0x06,
34
35 LockRequired = 0x0A,
37
38 InvalidChannel = 0x0B,
40
41 CborUnexpectedType = 0x11,
43
44 InvalidCbor = 0x12,
46
47 MissingParameter = 0x14,
49
50 LimitExceeded = 0x15,
52
53 UnsupportedExtension = 0x16,
55
56 CredentialExcluded = 0x19,
58
59 Processing = 0x21,
61
62 InvalidCredential = 0x22,
64
65 UserActionPending = 0x23,
67
68 OperationPending = 0x24,
70
71 NoOperations = 0x25,
73
74 UnsupportedAlgorithm = 0x26,
76
77 OperationDenied = 0x27,
79
80 KeyStoreFull = 0x28,
82
83 NotBusy = 0x29,
85
86 NoOperationPending = 0x2A,
88
89 UnsupportedOption = 0x2B,
91
92 InvalidOption = 0x2C,
94
95 KeepaliveCancel = 0x2D,
97
98 NoCredentials = 0x2E,
100
101 UserActionTimeout = 0x2F,
103
104 NotAllowed = 0x30,
106
107 PinInvalid = 0x31,
109
110 PinBlocked = 0x32,
112
113 PinAuthInvalid = 0x33,
115
116 PinAuthBlocked = 0x34,
118
119 PinNotSet = 0x35,
121
122 PinRequired = 0x36,
124
125 PinPolicyViolation = 0x37,
127
128 PinTokenExpired = 0x38,
130
131 RequestTooLarge = 0x39,
133
134 ActionTimeout = 0x3A,
136
137 UpRequired = 0x3B,
139
140 UvBlocked = 0x3C,
142
143 IntegrityFailure = 0x3D,
145
146 InvalidSubcommand = 0x3E,
148
149 UvInvalid = 0x3F,
151
152 UnauthorizedPermission = 0x40,
154
155 PuatRequired = 0x41,
157
158 Other = 0x7F,
160}
161
162impl fmt::Display for StatusCode {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 let msg = match self {
165 Self::Success => "Success",
166 Self::InvalidCommand => "Invalid command",
167 Self::InvalidParameter => "Invalid parameter",
168 Self::InvalidLength => "Invalid length",
169 Self::InvalidSeq => "Invalid sequence",
170 Self::Timeout => "Timeout",
171 Self::ChannelBusy => "Channel busy",
172 Self::LockRequired => "Lock required",
173 Self::InvalidChannel => "Invalid channel",
174 Self::CborUnexpectedType => "CBOR unexpected type",
175 Self::InvalidCbor => "Invalid CBOR",
176 Self::MissingParameter => "Missing parameter",
177 Self::LimitExceeded => "Limit exceeded",
178 Self::UnsupportedExtension => "Unsupported extension",
179 Self::CredentialExcluded => "Credential excluded",
180 Self::Processing => "Processing",
181 Self::InvalidCredential => "Invalid credential",
182 Self::UserActionPending => "User action pending",
183 Self::OperationPending => "Operation pending",
184 Self::NoOperations => "No operations",
185 Self::UnsupportedAlgorithm => "Unsupported algorithm",
186 Self::OperationDenied => "Operation denied",
187 Self::KeyStoreFull => "Key store full",
188 Self::NotBusy => "Not busy",
189 Self::NoOperationPending => "No operation pending",
190 Self::UnsupportedOption => "Unsupported option",
191 Self::InvalidOption => "Invalid option",
192 Self::KeepaliveCancel => "Keepalive cancel",
193 Self::NoCredentials => "No credentials",
194 Self::UserActionTimeout => "User action timeout",
195 Self::NotAllowed => "Not allowed",
196 Self::PinInvalid => "PIN invalid",
197 Self::PinBlocked => "PIN blocked",
198 Self::PinAuthInvalid => "PIN auth invalid",
199 Self::PinAuthBlocked => "PIN auth blocked",
200 Self::PinNotSet => "PIN not set",
201 Self::PinRequired => "PIN required",
202 Self::PinPolicyViolation => "PIN policy violation",
203 Self::PinTokenExpired => "PIN token expired",
204 Self::RequestTooLarge => "Request too large",
205 Self::ActionTimeout => "Action timeout",
206 Self::UpRequired => "UP required",
207 Self::UvBlocked => "UV blocked",
208 Self::IntegrityFailure => "Integrity failure",
209 Self::InvalidSubcommand => "Invalid subcommand",
210 Self::UvInvalid => "UV invalid",
211 Self::UnauthorizedPermission => "Unauthorized permission",
212 Self::PuatRequired => "PIN/UV auth token required",
213 Self::Other => "Other error",
214 };
215 write!(f, "{}", msg)
216 }
217}
218
219#[cfg(feature = "std")]
221impl std::error::Error for StatusCode {}
222
223impl StatusCode {
224 pub fn to_u8(self) -> u8 {
226 self as u8
227 }
228
229 pub fn from_u8(value: u8) -> Self {
231 match value {
232 0x00 => Self::Success,
233 0x01 => Self::InvalidCommand,
234 0x02 => Self::InvalidParameter,
235 0x03 => Self::InvalidLength,
236 0x04 => Self::InvalidSeq,
237 0x05 => Self::Timeout,
238 0x06 => Self::ChannelBusy,
239 0x0A => Self::LockRequired,
240 0x0B => Self::InvalidChannel,
241 0x11 => Self::CborUnexpectedType,
242 0x12 => Self::InvalidCbor,
243 0x14 => Self::MissingParameter,
244 0x15 => Self::LimitExceeded,
245 0x16 => Self::UnsupportedExtension,
246 0x19 => Self::CredentialExcluded,
247 0x21 => Self::Processing,
248 0x22 => Self::InvalidCredential,
249 0x23 => Self::UserActionPending,
250 0x24 => Self::OperationPending,
251 0x25 => Self::NoOperations,
252 0x26 => Self::UnsupportedAlgorithm,
253 0x27 => Self::OperationDenied,
254 0x28 => Self::KeyStoreFull,
255 0x29 => Self::NotBusy,
256 0x2A => Self::NoOperationPending,
257 0x2B => Self::UnsupportedOption,
258 0x2C => Self::InvalidOption,
259 0x2D => Self::KeepaliveCancel,
260 0x2E => Self::NoCredentials,
261 0x2F => Self::UserActionTimeout,
262 0x30 => Self::NotAllowed,
263 0x31 => Self::PinInvalid,
264 0x32 => Self::PinBlocked,
265 0x33 => Self::PinAuthInvalid,
266 0x34 => Self::PinAuthBlocked,
267 0x35 => Self::PinNotSet,
268 0x36 => Self::PinRequired,
269 0x37 => Self::PinPolicyViolation,
270 0x38 => Self::PinTokenExpired,
271 0x39 => Self::RequestTooLarge,
272 0x3A => Self::ActionTimeout,
273 0x3B => Self::UpRequired,
274 0x3C => Self::UvBlocked,
275 0x3D => Self::IntegrityFailure,
276 0x3E => Self::InvalidSubcommand,
277 0x3F => Self::UvInvalid,
278 0x40 => Self::UnauthorizedPermission,
279 0x41 => Self::PuatRequired,
280 _ => Self::Other,
281 }
282 }
283
284 pub fn is_success(self) -> bool {
286 self == Self::Success
287 }
288}
289
290impl From<StatusCode> for u8 {
291 fn from(status: StatusCode) -> u8 {
292 status.to_u8()
293 }
294}
295
296impl From<u8> for StatusCode {
297 fn from(value: u8) -> Self {
298 Self::from_u8(value)
299 }
300}
301
302impl From<soft_fido2_crypto::CryptoError> for StatusCode {
303 fn from(err: soft_fido2_crypto::CryptoError) -> Self {
304 match err {
305 soft_fido2_crypto::CryptoError::InvalidPublicKey => Self::InvalidParameter,
306 soft_fido2_crypto::CryptoError::InvalidPrivateKey => Self::InvalidParameter,
307 soft_fido2_crypto::CryptoError::InvalidSignature => Self::InvalidParameter,
308 soft_fido2_crypto::CryptoError::DecryptionFailed => Self::PinAuthInvalid,
309 soft_fido2_crypto::CryptoError::EncryptionFailed => Self::Other,
310 soft_fido2_crypto::CryptoError::InvalidKeyLength { .. } => Self::InvalidParameter,
311 soft_fido2_crypto::CryptoError::KeyAgreementFailed => Self::Other,
312 soft_fido2_crypto::CryptoError::InvalidCoseKey => Self::InvalidParameter,
313 }
314 }
315}
316
317pub type Result<T> = core::result::Result<T, StatusCode>;
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 #[test]
325 fn test_status_code_round_trip() {
326 let codes = vec![
327 StatusCode::Success,
328 StatusCode::InvalidCommand,
329 StatusCode::PinInvalid,
330 StatusCode::OperationDenied,
331 ];
332
333 for code in codes {
334 let byte = code.to_u8();
335 let recovered = StatusCode::from_u8(byte);
336 assert_eq!(code, recovered);
337 }
338 }
339
340 #[test]
341 fn test_unknown_status_code() {
342 let unknown = StatusCode::from_u8(0xFF);
343 assert_eq!(unknown, StatusCode::Other);
344 }
345
346 #[test]
347 fn test_is_success() {
348 assert!(StatusCode::Success.is_success());
349 assert!(!StatusCode::InvalidCommand.is_success());
350 }
351
352 #[test]
353 fn test_from_crypto_error() {
354 let status: StatusCode = soft_fido2_crypto::CryptoError::InvalidPublicKey.into();
355 assert_eq!(status, StatusCode::InvalidParameter);
356
357 let status: StatusCode = soft_fido2_crypto::CryptoError::DecryptionFailed.into();
358 assert_eq!(status, StatusCode::PinAuthInvalid);
359 }
360}