scram_rs/
scram_error.rs

1/*-
2 * Scram-rs - a SCRAM authentification authorization library
3 * 
4 * Copyright (C) 2021  Aleksandr Morozov
5 * Copyright (C) 2025 Aleksandr Morozov
6 * 
7 * The syslog-rs crate can be redistributed and/or modified
8 * under the terms of either of the following licenses:
9 *
10 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
11 *
12 *   2. The MIT License (MIT)
13 *                     
14 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
15 */
16
17
18
19#[cfg(feature = "std")]
20use std::fmt;
21#[cfg(not(feature = "std"))]
22use core::fmt;
23
24#[cfg(not(feature = "std"))]
25use alloc::string::String;
26
27#[derive(Clone, Eq, PartialEq)]
28pub struct ScramRuntimeError 
29{
30    /// Error code for internal usage
31    pub err_code: ScramErrorCode,
32    /// Error code for exteranl usage i.e RFC5802 errors for error 
33    ///  handling on client side if no other signalling is used.
34    pub err_serv: ScramServerError,
35    /// Human readable message with description.
36    pub message: String,
37}
38
39impl ScramRuntimeError
40{
41    pub 
42    fn new(err_code: ScramErrorCode, err_serv: ScramServerError, msg: String) -> Self
43    {
44        return ScramRuntimeError{err_code: err_code, err_serv: err_serv, message: msg};
45    }
46
47    /// Converts the external error code to SCRAM protocol format.
48    pub 
49    fn serv_err_value(&self) -> &str 
50    {
51       return  
52            match self.err_serv
53            {
54                ScramServerError::None => "e=other-error",
55                ScramServerError::InvalidEncoding => "e=invalid-encoding",
56                ScramServerError::ExtensionsNotSupported => "e=extensions-not-supported",
57                ScramServerError::InvalidProof => "e=invalid-proof",
58                ScramServerError::ChannelBindingsDontMatch => "e=channel-bindings-dont-match",
59                ScramServerError::ServerDoesSupportChannelBinding => "e=server-does-support-channel-binding",
60                ScramServerError::ChannelBindingNotSupported => "e=channel-binding-not-supported",
61                ScramServerError::UnsupportedChannelBindingType => "e=unsupported-channel-binding-type",
62                ScramServerError::UnknownUser => "e=unknown-user",
63                ScramServerError::InvalidUsernameEncoding => "e=invalid-username-encoding",
64                ScramServerError::NoResources => "e=no-resources",
65                ScramServerError::OtherError => "e=other-error"
66            };
67    }
68}
69
70impl fmt::Display for ScramRuntimeError 
71{
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 
73    {
74        write!(f, "scram: {}, {}", self.err_code, self.message)
75    }
76}
77impl fmt::Debug for ScramRuntimeError 
78{
79    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 
80    {
81        write!(f, "scram: {}, {}", self.err_code, self.message)
82    }
83}
84
85#[repr(u32)]
86#[derive(Copy, Clone, PartialEq, Eq)]
87pub enum ScramServerError
88{
89    /// Same as OtherError
90    None,
91    InvalidEncoding,
92
93    /// unrecognized 'm' value
94    ExtensionsNotSupported,
95    InvalidProof,
96    ChannelBindingsDontMatch,
97    ServerDoesSupportChannelBinding,
98    ChannelBindingNotSupported,
99    UnsupportedChannelBindingType,
100    /// Unused, if user was not found a mock auth is performed
101    ///  and `InvalidProof` is sent.
102    UnknownUser,
103    InvalidUsernameEncoding,
104    NoResources,
105
106    /// For any errors that may disclose sensitive information
107    OtherError
108}
109
110
111
112impl fmt::Display for ScramServerError 
113{
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 
115    {
116        let d = 
117            match *self
118            {
119                Self::None => 
120                    "None",
121                Self::InvalidEncoding => 
122                    "Invalid Encoding",
123                Self::ExtensionsNotSupported => 
124                    "Extension Not Supported, unrecognized 'm' value",
125                Self::InvalidProof => 
126                    "Invalid Proof",
127                Self::ChannelBindingsDontMatch => 
128                    "Channel Bindings Don't Match",
129                Self::ServerDoesSupportChannelBinding => 
130                    "Server Does Support Channel Binding",
131                Self::ChannelBindingNotSupported => 
132                    "Channel Binding Not Supported",
133                Self::UnsupportedChannelBindingType => 
134                    "Unsupported Channel Binding Type",
135                Self::UnknownUser => 
136                    "Unknown User",
137                Self::InvalidUsernameEncoding => 
138                    "Invalid Username Enconding",
139                Self::NoResources => 
140                    "No Resources",
141                Self::OtherError => 
142                    "Other Error"
143            };
144
145        return write!(f, "{}", d);
146    }
147}
148impl fmt::Debug for ScramServerError 
149{
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 
151    {
152        let d = 
153            match *self
154            {
155                Self::None => 
156                    "None",
157                Self::InvalidEncoding => 
158                    "Invalid Encoding",
159                Self::ExtensionsNotSupported => 
160                    "Extension Not Supported, unrecognized 'm' value",
161                Self::InvalidProof => 
162                    "Invalid Proof",
163                Self::ChannelBindingsDontMatch => 
164                    "Channel Bindings Don't Match",
165                Self::ServerDoesSupportChannelBinding => 
166                    "Server Does Support Channel Binding",
167                Self::ChannelBindingNotSupported => 
168                    "Channel Binding Not Supported",
169                Self::UnsupportedChannelBindingType => 
170                    "Unsupported Channel Binding Type",
171                Self::UnknownUser => 
172                    "Unknown User",
173                Self::InvalidUsernameEncoding => 
174                    "Invalid Username Enconding",
175                Self::NoResources => 
176                    "No Resources",
177                Self::OtherError => 
178                    "Other Error"
179            };
180
181        return write!(f, "{}", d);
182    }
183}
184
185impl From<&str> for ScramServerError
186{
187    fn from(value: &str) -> Self
188    {
189        match value
190        {
191            "invalid-encoding" => 
192                Self::InvalidEncoding,
193            "extensions-not-supported" => 
194                Self::ExtensionsNotSupported,
195            "invalid-proof" => 
196                Self::InvalidProof,
197            "channel-bindings-dont-match" => 
198                Self::ChannelBindingsDontMatch,
199            "server-does-support-channel-binding" => 
200                Self::ServerDoesSupportChannelBinding,
201            "channel-binding-not-supported" => 
202                Self::ChannelBindingNotSupported,
203            "unsupported-channel-binding-type" => 
204                Self::UnsupportedChannelBindingType,
205            "unknown-user" => 
206                Self::UnknownUser,
207            "invalid-username-encoding" => 
208                Self::InvalidUsernameEncoding,
209            "no-resources" => 
210                Self::NoResources,
211            "other-error" => Self::OtherError,
212            _ => Self::OtherError,
213        }
214    }
215}
216
217/// Error code
218#[repr(u32)]
219#[derive(Debug, Copy, Clone, Eq, PartialEq)]
220pub enum ScramErrorCode
221{
222    /// Error happened inside lib
223    InternalError,
224
225    /// Error during verification of proof or other value
226    VerificationError,
227
228    /// Error which occurs outside of the lib
229    ExternalError,
230
231    /// Error due malformed SCRAM message
232    MalformedScramMsg,
233
234    /// Error which occure when unsupported options are included in received msg
235    FeatureNotSupported,
236
237    /// Error due to protocol violation
238    ProtocolViolation,
239
240    /// Wrong call on extract result
241    AuthSeqCompleted,
242
243    /// Client side received error
244    ClientSide,
245
246    /// SCRAM channel binding 'type' not supported or not implemented
247    ChanBindNotImplemented
248}
249
250impl fmt::Display for ScramErrorCode 
251{
252    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 
253    {
254        match *self 
255        {
256            Self::InternalError     => write!(f, "Internal Server Error"),
257            Self::VerificationError => write!(f, "Data Verification Error"),
258            Self::ExternalError     => write!(f, "External Server Error"),
259            Self::MalformedScramMsg => write!(f, "Malformed Scram Message"),
260            Self::FeatureNotSupported => write!(f, "Feature is not supported"),
261            Self::ProtocolViolation => write!(f, "Protocol Violation"),
262            Self::AuthSeqCompleted  => write!(f, "Completed!"),
263            Self::ClientSide        => write!(f, "Server reported error"),
264            Self::ChanBindNotImplemented => write!(f, "Channel bind not implemented"),
265        }
266    }
267}
268
269/// An envilope for the scram library result.
270pub type ScramResult<T> = Result<T, ScramRuntimeError>;
271
272#[cfg(feature = "std")]
273#[macro_export]
274macro_rules! scram_error 
275{
276    ($src:expr, $serv_err:expr, $($arg:tt)*) => (
277        return std::result::Result::Err($crate::ScramRuntimeError::new($src, $serv_err, format!($($arg)*)))
278    )
279}
280
281#[cfg(not(feature = "std"))]
282#[macro_export]
283macro_rules! scram_error 
284{
285    ($src:expr, $serv_err:expr, $($arg:tt)*) => (
286        return core::result::Result::Err($crate::ScramRuntimeError::new($src, $serv_err, alloc::format!($($arg)*)))
287    )
288}
289
290#[cfg(feature = "std")]
291#[macro_export]
292macro_rules! scram_ierror 
293{
294    ($src:expr, $($arg:tt)*) => (
295        return std::result::Result::Err($crate::ScramRuntimeError::new($src, $crate::ScramServerError::None, format!($($arg)*)))
296    )
297}
298
299#[cfg(not(feature = "std"))]
300#[macro_export]
301macro_rules! scram_ierror 
302{
303    ($src:expr, $($arg:tt)*) => (
304        return core::result::Result::Err($crate::ScramRuntimeError::new($src, $crate::ScramServerError::None, alloc::format!($($arg)*)))
305    )
306}
307
308#[cfg(feature = "std")]
309#[macro_export]
310macro_rules! scram_ierror_map
311{
312    ($src:expr, $($arg:tt)*) => (
313        $crate::ScramRuntimeError::new($src, $crate::ScramServerError::None, format!($($arg)*))
314    )
315}
316
317#[cfg(not(feature = "std"))]
318#[macro_export]
319macro_rules! scram_ierror_map
320{
321    ($src:expr, $($arg:tt)*) => (
322        $crate::ScramRuntimeError::new($src, $crate::ScramServerError::None, alloc::format!($($arg)*))
323    )
324}
325
326#[cfg(feature = "std")]
327#[macro_export]
328macro_rules! scram_error_map
329{
330    ($src:expr, $serv_err:expr, $($arg:tt)*) => (
331        $crate::ScramRuntimeError::new($src, $serv_err, format!($($arg)*))
332    )
333}
334
335
336#[cfg(not(feature = "std"))]
337#[macro_export]
338macro_rules! scram_error_map
339{
340    ($src:expr, $serv_err:expr, $($arg:tt)*) => (
341        $crate::ScramRuntimeError::new($src, $serv_err, alloc::format!($($arg)*))
342    )
343}
344