password_hash/
errors.rs

1//! Error types.
2
3pub use base64ct::Error as B64Error;
4
5use core::cmp::Ordering;
6use core::fmt;
7
8/// Result type.
9pub type Result<T> = core::result::Result<T, Error>;
10
11/// Password hashing errors.
12#[derive(Copy, Clone, Debug, Eq, PartialEq)]
13#[non_exhaustive]
14pub enum Error {
15    /// Unsupported algorithm.
16    Algorithm,
17
18    /// "B64" encoding error.
19    B64Encoding(B64Error),
20
21    /// Cryptographic error.
22    Crypto,
23
24    /// Output size unexpected.
25    OutputSize {
26        /// Indicates why the output size is unexpected.
27        ///
28        /// - [`Ordering::Less`]: Size is too small.
29        /// - [`Ordering::Equal`]: Size is not exactly as `expected`.
30        /// - [`Ordering::Greater`]: Size is too long.
31        provided: Ordering,
32        /// Expected output size in relation to `provided`.
33        ///
34        /// - [`Ordering::Less`]: Minimum size.
35        /// - [`Ordering::Equal`]: Expected size.
36        /// - [`Ordering::Greater`]: Maximum size.
37        expected: usize,
38    },
39
40    /// Duplicate parameter name encountered.
41    ParamNameDuplicated,
42
43    /// Invalid parameter name.
44    ParamNameInvalid,
45
46    /// Invalid parameter value.
47    ParamValueInvalid(InvalidValue),
48
49    /// Maximum number of parameters exceeded.
50    ParamsMaxExceeded,
51
52    /// Invalid password.
53    Password,
54
55    /// Password hash string invalid.
56    PhcStringField,
57
58    /// Password hash string contains trailing data.
59    PhcStringTrailingData,
60
61    /// Salt invalid.
62    SaltInvalid(InvalidValue),
63
64    /// Invalid algorithm version.
65    Version,
66
67    /// Out of memory (heap allocation failure).
68    OutOfMemory,
69}
70
71impl fmt::Display for Error {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> {
73        match self {
74            Self::Algorithm => write!(f, "unsupported algorithm"),
75            Self::B64Encoding(err) => write!(f, "{err}"),
76            Self::Crypto => write!(f, "cryptographic error"),
77            Self::OutputSize { provided, expected } => match provided {
78                Ordering::Less => write!(
79                    f,
80                    "output size too short, expected at least {expected} bytes",
81                ),
82                Ordering::Equal => write!(f, "output size unexpected, expected {expected} bytes"),
83                Ordering::Greater => {
84                    write!(f, "output size too long, expected at most {expected} bytes")
85                }
86            },
87            Self::ParamNameDuplicated => f.write_str("duplicate parameter"),
88            Self::ParamNameInvalid => f.write_str("invalid parameter name"),
89            Self::ParamValueInvalid(val_err) => write!(f, "invalid parameter value: {val_err}"),
90            Self::ParamsMaxExceeded => f.write_str("maximum number of parameters reached"),
91            Self::Password => write!(f, "invalid password"),
92            Self::PhcStringField => write!(f, "password hash string missing field"),
93            Self::PhcStringTrailingData => {
94                write!(f, "password hash string contains trailing characters")
95            }
96            Self::SaltInvalid(val_err) => write!(f, "salt invalid: {val_err}"),
97            Self::Version => write!(f, "invalid algorithm version"),
98            Self::OutOfMemory => write!(f, "out of memory"),
99        }
100    }
101}
102
103impl core::error::Error for Error {
104    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
105        match self {
106            Self::B64Encoding(err) => Some(err),
107            Self::ParamValueInvalid(err) => Some(err),
108            Self::SaltInvalid(err) => Some(err),
109            _ => None,
110        }
111    }
112}
113
114impl From<B64Error> for Error {
115    fn from(err: B64Error) -> Error {
116        Error::B64Encoding(err)
117    }
118}
119
120impl From<base64ct::InvalidLengthError> for Error {
121    fn from(_: base64ct::InvalidLengthError) -> Error {
122        Error::B64Encoding(B64Error::InvalidLength)
123    }
124}
125
126/// Parse errors relating to invalid parameter values or salts.
127#[derive(Copy, Clone, Debug, Eq, PartialEq)]
128#[non_exhaustive]
129pub enum InvalidValue {
130    /// Character is not in the allowed set.
131    InvalidChar(char),
132
133    /// Format is invalid.
134    InvalidFormat,
135
136    /// Value is malformed.
137    Malformed,
138
139    /// Value exceeds the maximum allowed length.
140    TooLong,
141
142    /// Value does not satisfy the minimum length.
143    TooShort,
144}
145
146impl InvalidValue {
147    /// Create an [`Error::ParamValueInvalid`] which warps this error.
148    pub fn param_error(self) -> Error {
149        Error::ParamValueInvalid(self)
150    }
151
152    /// Create an [`Error::SaltInvalid`] which wraps this error.
153    pub fn salt_error(self) -> Error {
154        Error::SaltInvalid(self)
155    }
156}
157
158impl fmt::Display for InvalidValue {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> {
160        match self {
161            Self::InvalidChar(c) => write!(f, "contains invalid character: '{c}'"),
162            Self::InvalidFormat => f.write_str("value format is invalid"),
163            Self::Malformed => f.write_str("value malformed"),
164            Self::TooLong => f.write_str("value to long"),
165            Self::TooShort => f.write_str("value to short"),
166        }
167    }
168}
169
170impl core::error::Error for InvalidValue {}