soft_fido2/
error.rs

1//! Error types for CTAP operations
2
3use std::fmt;
4
5/// Error type for CTAP operations
6#[derive(Debug, Clone, PartialEq)]
7pub enum Error {
8    /// The given operation was successful
9    Success,
10    /// The given value already exists
11    DoesAlreadyExist,
12    /// The requested value doesn't exist
13    DoesNotExist,
14    /// Credentials can't be inserted into the key-store
15    KeyStoreFull,
16    /// The client ran out of memory
17    OutOfMemory,
18    /// The operation timed out
19    Timeout,
20    /// Unspecified operation
21    Other,
22    /// Initialization failed
23    InitializationFailed,
24    /// Invalid callback result
25    InvalidCallbackResult,
26    /// CBOR command failed
27    CborCommandFailed(i32),
28    /// Invalid client data hash (must be 32 bytes)
29    InvalidClientDataHash,
30    /// CTAP error with status code
31    CtapError(u8),
32    /// IO error (from transport operations)
33    IoError(String),
34}
35
36impl fmt::Display for Error {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Error::Success => write!(f, "Success"),
40            Error::DoesAlreadyExist => write!(f, "Value already exists"),
41            Error::DoesNotExist => write!(f, "Value does not exist"),
42            Error::KeyStoreFull => write!(f, "Key store is full"),
43            Error::OutOfMemory => write!(f, "Out of memory"),
44            Error::Timeout => write!(f, "Operation timed out"),
45            Error::Other => write!(f, "Unspecified error"),
46            Error::InitializationFailed => write!(f, "Initialization failed"),
47            Error::InvalidCallbackResult => write!(f, "Invalid callback result"),
48            Error::CborCommandFailed(code) => {
49                write!(f, "CBOR command failed with code {}", code)
50            }
51            Error::InvalidClientDataHash => {
52                write!(f, "Invalid client data hash (must be 32 bytes)")
53            }
54            Error::CtapError(code) => write!(f, "CTAP error: 0x{:02X}", code),
55            Error::IoError(msg) => write!(f, "IO error: {}", msg),
56        }
57    }
58}
59
60impl std::error::Error for Error {}
61
62impl From<i32> for Error {
63    fn from(value: i32) -> Self {
64        match value {
65            0 => Error::Success,
66            -1 => Error::DoesAlreadyExist,
67            -2 => Error::DoesNotExist,
68            -3 => Error::KeyStoreFull,
69            -4 => Error::OutOfMemory,
70            -5 => Error::Timeout,
71            -6 => Error::Other,
72            _ => Error::CborCommandFailed(value),
73        }
74    }
75}
76
77impl From<soft_fido2_ctap::StatusCode> for Error {
78    fn from(status: soft_fido2_ctap::StatusCode) -> Self {
79        use soft_fido2_ctap::StatusCode;
80
81        match status {
82            StatusCode::Success => Error::Success,
83            StatusCode::InvalidCommand => Error::CtapError(0x01),
84            StatusCode::InvalidParameter => Error::CtapError(0x02),
85            StatusCode::InvalidLength => Error::CtapError(0x03),
86            StatusCode::InvalidSeq => Error::CtapError(0x04),
87            StatusCode::Timeout => Error::Timeout,
88            StatusCode::ChannelBusy => Error::CtapError(0x06),
89            StatusCode::LockRequired => Error::CtapError(0x0A),
90            StatusCode::InvalidChannel => Error::CtapError(0x0B),
91            StatusCode::CborUnexpectedType => Error::CtapError(0x11),
92            StatusCode::InvalidCbor => Error::CtapError(0x12),
93            StatusCode::MissingParameter => Error::CtapError(0x14),
94            StatusCode::LimitExceeded => Error::CtapError(0x15),
95            StatusCode::UnsupportedExtension => Error::CtapError(0x16),
96            StatusCode::CredentialExcluded => Error::CtapError(0x19),
97            StatusCode::Processing => Error::CtapError(0x21),
98            StatusCode::InvalidCredential => Error::CtapError(0x22),
99            StatusCode::UserActionPending => Error::CtapError(0x23),
100            StatusCode::OperationPending => Error::CtapError(0x24),
101            StatusCode::NoOperations => Error::CtapError(0x25),
102            StatusCode::UnsupportedAlgorithm => Error::CtapError(0x26),
103            StatusCode::OperationDenied => Error::CtapError(0x27),
104            StatusCode::KeyStoreFull => Error::KeyStoreFull,
105            StatusCode::NotBusy => Error::CtapError(0x29),
106            StatusCode::NoOperationPending => Error::CtapError(0x2A),
107            StatusCode::UnsupportedOption => Error::CtapError(0x2B),
108            StatusCode::InvalidOption => Error::CtapError(0x2C),
109            StatusCode::KeepaliveCancel => Error::CtapError(0x2D),
110            StatusCode::NoCredentials => Error::DoesNotExist,
111            StatusCode::UserActionTimeout => Error::Timeout,
112            StatusCode::NotAllowed => Error::CtapError(0x30),
113            StatusCode::PinInvalid => Error::CtapError(0x31),
114            StatusCode::PinBlocked => Error::CtapError(0x32),
115            StatusCode::PinAuthInvalid => Error::CtapError(0x33),
116            StatusCode::PinAuthBlocked => Error::CtapError(0x34),
117            StatusCode::PinNotSet => Error::CtapError(0x35),
118            StatusCode::PinRequired => Error::CtapError(0x36),
119            StatusCode::PinPolicyViolation => Error::CtapError(0x37),
120            StatusCode::PinTokenExpired => Error::CtapError(0x38),
121            StatusCode::RequestTooLarge => Error::CtapError(0x39),
122            StatusCode::ActionTimeout => Error::Timeout,
123            StatusCode::UpRequired => Error::CtapError(0x3A),
124            StatusCode::UvBlocked => Error::CtapError(0x3C),
125            StatusCode::IntegrityFailure => Error::CtapError(0x3D),
126            StatusCode::InvalidSubcommand => Error::CtapError(0x3E),
127            StatusCode::UvInvalid => Error::CtapError(0x3F),
128            StatusCode::UnauthorizedPermission => Error::CtapError(0x40),
129            StatusCode::Other => Error::Other,
130        }
131    }
132}
133
134impl From<Error> for soft_fido2_ctap::StatusCode {
135    fn from(error: Error) -> Self {
136        use soft_fido2_ctap::StatusCode;
137
138        match error {
139            Error::Success => StatusCode::Success,
140            Error::DoesNotExist => StatusCode::NoCredentials,
141            Error::KeyStoreFull => StatusCode::KeyStoreFull,
142            Error::Timeout => StatusCode::Timeout,
143            Error::Other => StatusCode::Other,
144            Error::CtapError(code) => {
145                // Map back to StatusCode
146                match code {
147                    0x01 => StatusCode::InvalidCommand,
148                    0x02 => StatusCode::InvalidParameter,
149                    0x03 => StatusCode::InvalidLength,
150                    0x04 => StatusCode::InvalidSeq,
151                    0x06 => StatusCode::ChannelBusy,
152                    0x0A => StatusCode::LockRequired,
153                    0x0B => StatusCode::InvalidChannel,
154                    0x11 => StatusCode::CborUnexpectedType,
155                    0x12 => StatusCode::InvalidCbor,
156                    0x14 => StatusCode::MissingParameter,
157                    0x15 => StatusCode::LimitExceeded,
158                    0x31 => StatusCode::PinInvalid,
159                    0x33 => StatusCode::PinAuthInvalid,
160                    0x35 => StatusCode::PinNotSet,
161                    0x36 => StatusCode::PinRequired,
162                    _ => StatusCode::Other,
163                }
164            }
165            _ => StatusCode::Other,
166        }
167    }
168}
169
170// Conversion from IO errors
171impl From<std::io::Error> for Error {
172    fn from(error: std::io::Error) -> Self {
173        Error::IoError(error.to_string())
174    }
175}
176
177/// Result type alias for common operations
178pub type Result<T> = std::result::Result<T, Error>;