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 Other = 0x7F,
157}
158
159impl fmt::Display for StatusCode {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 let msg = match self {
162 Self::Success => "Success",
163 Self::InvalidCommand => "Invalid command",
164 Self::InvalidParameter => "Invalid parameter",
165 Self::InvalidLength => "Invalid length",
166 Self::InvalidSeq => "Invalid sequence",
167 Self::Timeout => "Timeout",
168 Self::ChannelBusy => "Channel busy",
169 Self::LockRequired => "Lock required",
170 Self::InvalidChannel => "Invalid channel",
171 Self::CborUnexpectedType => "CBOR unexpected type",
172 Self::InvalidCbor => "Invalid CBOR",
173 Self::MissingParameter => "Missing parameter",
174 Self::LimitExceeded => "Limit exceeded",
175 Self::UnsupportedExtension => "Unsupported extension",
176 Self::CredentialExcluded => "Credential excluded",
177 Self::Processing => "Processing",
178 Self::InvalidCredential => "Invalid credential",
179 Self::UserActionPending => "User action pending",
180 Self::OperationPending => "Operation pending",
181 Self::NoOperations => "No operations",
182 Self::UnsupportedAlgorithm => "Unsupported algorithm",
183 Self::OperationDenied => "Operation denied",
184 Self::KeyStoreFull => "Key store full",
185 Self::NotBusy => "Not busy",
186 Self::NoOperationPending => "No operation pending",
187 Self::UnsupportedOption => "Unsupported option",
188 Self::InvalidOption => "Invalid option",
189 Self::KeepaliveCancel => "Keepalive cancel",
190 Self::NoCredentials => "No credentials",
191 Self::UserActionTimeout => "User action timeout",
192 Self::NotAllowed => "Not allowed",
193 Self::PinInvalid => "PIN invalid",
194 Self::PinBlocked => "PIN blocked",
195 Self::PinAuthInvalid => "PIN auth invalid",
196 Self::PinAuthBlocked => "PIN auth blocked",
197 Self::PinNotSet => "PIN not set",
198 Self::PinRequired => "PIN required",
199 Self::PinPolicyViolation => "PIN policy violation",
200 Self::PinTokenExpired => "PIN token expired",
201 Self::RequestTooLarge => "Request too large",
202 Self::ActionTimeout => "Action timeout",
203 Self::UpRequired => "UP required",
204 Self::UvBlocked => "UV blocked",
205 Self::IntegrityFailure => "Integrity failure",
206 Self::InvalidSubcommand => "Invalid subcommand",
207 Self::UvInvalid => "UV invalid",
208 Self::UnauthorizedPermission => "Unauthorized permission",
209 Self::Other => "Other error",
210 };
211 write!(f, "{}", msg)
212 }
213}
214
215#[cfg(feature = "std")]
217impl std::error::Error for StatusCode {}
218
219impl StatusCode {
220 pub fn to_u8(self) -> u8 {
222 self as u8
223 }
224
225 pub fn from_u8(value: u8) -> Self {
227 match value {
228 0x00 => Self::Success,
229 0x01 => Self::InvalidCommand,
230 0x02 => Self::InvalidParameter,
231 0x03 => Self::InvalidLength,
232 0x04 => Self::InvalidSeq,
233 0x05 => Self::Timeout,
234 0x06 => Self::ChannelBusy,
235 0x0A => Self::LockRequired,
236 0x0B => Self::InvalidChannel,
237 0x11 => Self::CborUnexpectedType,
238 0x12 => Self::InvalidCbor,
239 0x14 => Self::MissingParameter,
240 0x15 => Self::LimitExceeded,
241 0x16 => Self::UnsupportedExtension,
242 0x19 => Self::CredentialExcluded,
243 0x21 => Self::Processing,
244 0x22 => Self::InvalidCredential,
245 0x23 => Self::UserActionPending,
246 0x24 => Self::OperationPending,
247 0x25 => Self::NoOperations,
248 0x26 => Self::UnsupportedAlgorithm,
249 0x27 => Self::OperationDenied,
250 0x28 => Self::KeyStoreFull,
251 0x29 => Self::NotBusy,
252 0x2A => Self::NoOperationPending,
253 0x2B => Self::UnsupportedOption,
254 0x2C => Self::InvalidOption,
255 0x2D => Self::KeepaliveCancel,
256 0x2E => Self::NoCredentials,
257 0x2F => Self::UserActionTimeout,
258 0x30 => Self::NotAllowed,
259 0x31 => Self::PinInvalid,
260 0x32 => Self::PinBlocked,
261 0x33 => Self::PinAuthInvalid,
262 0x34 => Self::PinAuthBlocked,
263 0x35 => Self::PinNotSet,
264 0x36 => Self::PinRequired,
265 0x37 => Self::PinPolicyViolation,
266 0x38 => Self::PinTokenExpired,
267 0x39 => Self::RequestTooLarge,
268 0x3A => Self::ActionTimeout,
269 0x3B => Self::UpRequired,
270 0x3C => Self::UvBlocked,
271 0x3D => Self::IntegrityFailure,
272 0x3E => Self::InvalidSubcommand,
273 0x3F => Self::UvInvalid,
274 0x40 => Self::UnauthorizedPermission,
275 _ => Self::Other,
276 }
277 }
278
279 pub fn is_success(self) -> bool {
281 self == Self::Success
282 }
283}
284
285impl From<StatusCode> for u8 {
286 fn from(status: StatusCode) -> u8 {
287 status.to_u8()
288 }
289}
290
291impl From<u8> for StatusCode {
292 fn from(value: u8) -> Self {
293 Self::from_u8(value)
294 }
295}
296
297impl From<soft_fido2_crypto::CryptoError> for StatusCode {
298 fn from(err: soft_fido2_crypto::CryptoError) -> Self {
299 match err {
300 soft_fido2_crypto::CryptoError::InvalidPublicKey => Self::InvalidParameter,
301 soft_fido2_crypto::CryptoError::InvalidPrivateKey => Self::InvalidParameter,
302 soft_fido2_crypto::CryptoError::InvalidSignature => Self::InvalidParameter,
303 soft_fido2_crypto::CryptoError::DecryptionFailed => Self::PinAuthInvalid,
304 soft_fido2_crypto::CryptoError::EncryptionFailed => Self::Other,
305 soft_fido2_crypto::CryptoError::InvalidKeyLength { .. } => Self::InvalidParameter,
306 soft_fido2_crypto::CryptoError::KeyAgreementFailed => Self::Other,
307 soft_fido2_crypto::CryptoError::InvalidCoseKey => Self::InvalidParameter,
308 }
309 }
310}
311
312pub type Result<T> = core::result::Result<T, StatusCode>;
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_status_code_round_trip() {
321 let codes = vec![
322 StatusCode::Success,
323 StatusCode::InvalidCommand,
324 StatusCode::PinInvalid,
325 StatusCode::OperationDenied,
326 ];
327
328 for code in codes {
329 let byte = code.to_u8();
330 let recovered = StatusCode::from_u8(byte);
331 assert_eq!(code, recovered);
332 }
333 }
334
335 #[test]
336 fn test_unknown_status_code() {
337 let unknown = StatusCode::from_u8(0xFF);
338 assert_eq!(unknown, StatusCode::Other);
339 }
340
341 #[test]
342 fn test_is_success() {
343 assert!(StatusCode::Success.is_success());
344 assert!(!StatusCode::InvalidCommand.is_success());
345 }
346
347 #[test]
348 fn test_from_crypto_error() {
349 let status: StatusCode = soft_fido2_crypto::CryptoError::InvalidPublicKey.into();
350 assert_eq!(status, StatusCode::InvalidParameter);
351
352 let status: StatusCode = soft_fido2_crypto::CryptoError::DecryptionFailed.into();
353 assert_eq!(status, StatusCode::PinAuthInvalid);
354 }
355}