1#[cfg(feature = "std")]
4use std::fmt;
5
6#[cfg(not(feature = "std"))]
7use core::fmt;
8
9use alloc::string::String;
10
11#[derive(Debug, Clone, PartialEq)]
13pub enum Error {
14 Success,
16 DoesAlreadyExist,
18 DoesNotExist,
20 KeyStoreFull,
22 OutOfMemory,
24 Timeout,
26 Other,
28 InitializationFailed,
30 InvalidCallbackResult,
32 CborCommandFailed(i32),
34 InvalidClientDataHash,
36 NoCredentials,
42 PinAuthRequired,
44 UnauthorizedPermission,
49 InvalidRpIdHash,
53 PinTokenExpired,
55 InvalidSubcommand,
57 CtapError(u8),
59 IoError(String),
61 InvalidPinLength,
63}
64
65impl fmt::Display for Error {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 match self {
68 Error::Success => write!(f, "Success"),
69 Error::DoesAlreadyExist => write!(f, "Value already exists"),
70 Error::DoesNotExist => write!(f, "Value does not exist"),
71 Error::KeyStoreFull => write!(f, "Key store is full"),
72 Error::OutOfMemory => write!(f, "Out of memory"),
73 Error::Timeout => write!(f, "Operation timed out"),
74 Error::Other => write!(f, "Unspecified error"),
75 Error::InitializationFailed => write!(f, "Initialization failed"),
76 Error::InvalidCallbackResult => write!(f, "Invalid callback result"),
77 Error::CborCommandFailed(code) => {
78 write!(f, "CBOR command failed with code {}", code)
79 }
80 Error::InvalidClientDataHash => {
81 write!(f, "Invalid client data hash (must be 32 bytes)")
82 }
83 Error::NoCredentials => write!(f, "No credentials found"),
84 Error::PinAuthRequired => write!(f, "PIN/UV authentication required"),
85 Error::UnauthorizedPermission => write!(f, "Insufficient permissions"),
86 Error::InvalidRpIdHash => write!(f, "Invalid RP ID hash (must be 32 bytes)"),
87 Error::PinTokenExpired => write!(f, "PIN/UV auth token expired"),
88 Error::InvalidSubcommand => write!(f, "Invalid subcommand"),
89 Error::CtapError(code) => write!(f, "CTAP error: 0x{:02X}", code),
90 Error::IoError(msg) => write!(f, "IO error: {}", msg),
91 Error::InvalidPinLength => write!(f, "Invalid PIN length (must be 4-63 characters)"),
92 }
93 }
94}
95
96#[cfg(feature = "std")]
97impl std::error::Error for Error {}
98
99impl From<i32> for Error {
100 fn from(value: i32) -> Self {
101 match value {
102 0 => Error::Success,
103 -1 => Error::DoesAlreadyExist,
104 -2 => Error::DoesNotExist,
105 -3 => Error::KeyStoreFull,
106 -4 => Error::OutOfMemory,
107 -5 => Error::Timeout,
108 -6 => Error::Other,
109 _ => Error::CborCommandFailed(value),
110 }
111 }
112}
113
114impl From<soft_fido2_ctap::StatusCode> for Error {
115 fn from(status: soft_fido2_ctap::StatusCode) -> Self {
116 use soft_fido2_ctap::StatusCode;
117
118 match status {
119 StatusCode::Success => Error::Success,
120 StatusCode::InvalidCommand => Error::CtapError(0x01),
121 StatusCode::InvalidParameter => Error::CtapError(0x02),
122 StatusCode::InvalidLength => Error::CtapError(0x03),
123 StatusCode::InvalidSeq => Error::CtapError(0x04),
124 StatusCode::Timeout => Error::Timeout,
125 StatusCode::ChannelBusy => Error::CtapError(0x06),
126 StatusCode::LockRequired => Error::CtapError(0x0A),
127 StatusCode::InvalidChannel => Error::CtapError(0x0B),
128 StatusCode::CborUnexpectedType => Error::CtapError(0x11),
129 StatusCode::InvalidCbor => Error::CtapError(0x12),
130 StatusCode::MissingParameter => Error::CtapError(0x14),
131 StatusCode::LimitExceeded => Error::CtapError(0x15),
132 StatusCode::UnsupportedExtension => Error::CtapError(0x16),
133 StatusCode::CredentialExcluded => Error::CtapError(0x19),
134 StatusCode::Processing => Error::CtapError(0x21),
135 StatusCode::InvalidCredential => Error::CtapError(0x22),
136 StatusCode::UserActionPending => Error::CtapError(0x23),
137 StatusCode::OperationPending => Error::CtapError(0x24),
138 StatusCode::NoOperations => Error::CtapError(0x25),
139 StatusCode::UnsupportedAlgorithm => Error::CtapError(0x26),
140 StatusCode::OperationDenied => Error::CtapError(0x27),
141 StatusCode::KeyStoreFull => Error::KeyStoreFull,
142 StatusCode::NotBusy => Error::CtapError(0x29),
143 StatusCode::NoOperationPending => Error::CtapError(0x2A),
144 StatusCode::UnsupportedOption => Error::CtapError(0x2B),
145 StatusCode::InvalidOption => Error::CtapError(0x2C),
146 StatusCode::KeepaliveCancel => Error::CtapError(0x2D),
147 StatusCode::NoCredentials => Error::NoCredentials,
148 StatusCode::UserActionTimeout => Error::Timeout,
149 StatusCode::NotAllowed => Error::CtapError(0x30),
150 StatusCode::PinInvalid => Error::CtapError(0x31),
151 StatusCode::PinBlocked => Error::CtapError(0x32),
152 StatusCode::PinAuthInvalid => Error::CtapError(0x33),
153 StatusCode::PinAuthBlocked => Error::CtapError(0x34),
154 StatusCode::PinNotSet => Error::CtapError(0x35),
155 StatusCode::PinRequired => Error::CtapError(0x36),
156 StatusCode::PinPolicyViolation => Error::CtapError(0x37),
157 StatusCode::PinTokenExpired => Error::CtapError(0x38),
158 StatusCode::RequestTooLarge => Error::CtapError(0x39),
159 StatusCode::ActionTimeout => Error::Timeout,
160 StatusCode::UpRequired => Error::CtapError(0x3A),
161 StatusCode::UvBlocked => Error::CtapError(0x3C),
162 StatusCode::IntegrityFailure => Error::CtapError(0x3D),
163 StatusCode::InvalidSubcommand => Error::CtapError(0x3E),
164 StatusCode::UvInvalid => Error::CtapError(0x3F),
165 StatusCode::UnauthorizedPermission => Error::CtapError(0x40),
166 StatusCode::PuatRequired => Error::CtapError(0x41),
167 StatusCode::Other => Error::Other,
168 }
169 }
170}
171
172impl From<Error> for soft_fido2_ctap::StatusCode {
173 fn from(error: Error) -> Self {
174 use soft_fido2_ctap::StatusCode;
175
176 match error {
177 Error::Success => StatusCode::Success,
178 Error::DoesNotExist => StatusCode::NoCredentials,
179 Error::KeyStoreFull => StatusCode::KeyStoreFull,
180 Error::Timeout => StatusCode::Timeout,
181 Error::Other => StatusCode::Other,
182 Error::CtapError(code) => {
183 match code {
185 0x01 => StatusCode::InvalidCommand,
186 0x02 => StatusCode::InvalidParameter,
187 0x03 => StatusCode::InvalidLength,
188 0x04 => StatusCode::InvalidSeq,
189 0x06 => StatusCode::ChannelBusy,
190 0x0A => StatusCode::LockRequired,
191 0x0B => StatusCode::InvalidChannel,
192 0x11 => StatusCode::CborUnexpectedType,
193 0x12 => StatusCode::InvalidCbor,
194 0x14 => StatusCode::MissingParameter,
195 0x15 => StatusCode::LimitExceeded,
196 0x31 => StatusCode::PinInvalid,
197 0x33 => StatusCode::PinAuthInvalid,
198 0x35 => StatusCode::PinNotSet,
199 0x36 => StatusCode::PinRequired,
200 _ => StatusCode::Other,
201 }
202 }
203 Error::InvalidPinLength => StatusCode::PinPolicyViolation,
204 _ => StatusCode::Other,
205 }
206 }
207}
208
209#[cfg(feature = "std")]
211impl From<std::io::Error> for Error {
212 fn from(error: std::io::Error) -> Self {
213 Error::IoError(error.to_string())
214 }
215}
216
217impl Error {
218 pub fn parse_ctap_response(data: &[u8]) -> Result<&[u8]> {
226 if data.is_empty() {
227 return Err(Error::Other);
228 }
229
230 let status_byte = data[0];
231 if status_byte == 0x00 {
232 Ok(&data[1..])
234 } else {
235 Err(soft_fido2_ctap::StatusCode::from(status_byte).into())
237 }
238 }
239}
240
241#[cfg(feature = "std")]
243pub type Result<T> = std::result::Result<T, Error>;
244
245#[cfg(not(feature = "std"))]
246pub type Result<T> = core::result::Result<T, Error>;