#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(i32)]
#[non_exhaustive]
pub enum Error {
OutputPtrNull = -1,
OutputTooShort = -2,
OutputTooLong = -3,
PwdTooShort = -4,
PwdTooLong = -5,
SaltTooShort = -6,
SaltTooLong = -7,
AdTooShort = -8,
AdTooLong = -9,
SecretTooShort = -10,
SecretTooLong = -11,
TimeTooSmall = -12,
TimeTooLarge = -13,
MemoryTooLittle = -14,
MemoryTooMuch = -15,
LanesTooFew = -16,
LanesTooMany = -17,
PwdPtrMismatch = -18,
SaltPtrMismatch = -19,
SecretPtrMismatch = -20,
AdPtrMismatch = -21,
MemoryAllocationError = -22,
FreeMemoryCbkNull = -23,
AllocateMemoryCbkNull = -24,
IncorrectParameter = -25,
IncorrectType = -26,
OutPtrMismatch = -27,
ThreadsTooFew = -28,
ThreadsTooMany = -29,
MissingArgs = -30,
EncodingFail = -31,
DecodingFail = -32,
ThreadFail = -33,
DecodingLengthFail = -34,
VerifyMismatch = -35,
OsRandom = -100,
}
impl Error {
pub const MIN_C_CODE: i32 = -35;
pub const MAX_C_CODE: i32 = -1;
#[inline]
#[must_use]
pub const fn as_c_code(&self) -> i32 {
*self as i32
}
#[must_use]
pub const fn from_c_code(code: i32) -> Option<Error> {
Some(match code {
-1 => Error::OutputPtrNull,
-2 => Error::OutputTooShort,
-3 => Error::OutputTooLong,
-4 => Error::PwdTooShort,
-5 => Error::PwdTooLong,
-6 => Error::SaltTooShort,
-7 => Error::SaltTooLong,
-8 => Error::AdTooShort,
-9 => Error::AdTooLong,
-10 => Error::SecretTooShort,
-11 => Error::SecretTooLong,
-12 => Error::TimeTooSmall,
-13 => Error::TimeTooLarge,
-14 => Error::MemoryTooLittle,
-15 => Error::MemoryTooMuch,
-16 => Error::LanesTooFew,
-17 => Error::LanesTooMany,
-18 => Error::PwdPtrMismatch,
-19 => Error::SaltPtrMismatch,
-20 => Error::SecretPtrMismatch,
-21 => Error::AdPtrMismatch,
-22 => Error::MemoryAllocationError,
-23 => Error::FreeMemoryCbkNull,
-24 => Error::AllocateMemoryCbkNull,
-25 => Error::IncorrectParameter,
-26 => Error::IncorrectType,
-27 => Error::OutPtrMismatch,
-28 => Error::ThreadsTooFew,
-29 => Error::ThreadsTooMany,
-30 => Error::MissingArgs,
-31 => Error::EncodingFail,
-32 => Error::DecodingFail,
-33 => Error::ThreadFail,
-34 => Error::DecodingLengthFail,
-35 => Error::VerifyMismatch,
-100 => Error::OsRandom,
_ => return None,
})
}
#[must_use]
pub const fn message(&self) -> &'static str {
match self {
Error::OutputPtrNull => "Output pointer is NULL",
Error::OutputTooShort => "Output is too short",
Error::OutputTooLong => "Output is too long",
Error::PwdTooShort => "Password is too short",
Error::PwdTooLong => "Password is too long",
Error::SaltTooShort => "Salt is too short",
Error::SaltTooLong => "Salt is too long",
Error::AdTooShort => "Associated data is too short",
Error::AdTooLong => "Associated data is too long",
Error::SecretTooShort => "Secret is too short",
Error::SecretTooLong => "Secret is too long",
Error::TimeTooSmall => "Time cost is too small",
Error::TimeTooLarge => "Time cost is too large",
Error::MemoryTooLittle => "Memory cost is too small",
Error::MemoryTooMuch => "Memory cost is too large",
Error::LanesTooFew => "Too few lanes",
Error::LanesTooMany => "Too many lanes",
Error::PwdPtrMismatch => "Password pointer is NULL, but password length is not 0",
Error::SaltPtrMismatch => "Salt pointer is NULL, but salt length is not 0",
Error::SecretPtrMismatch => "Secret pointer is NULL, but secret length is not 0",
Error::AdPtrMismatch => "Associated data pointer is NULL, but ad length is not 0",
Error::MemoryAllocationError => "Memory allocation error",
Error::FreeMemoryCbkNull => "The free memory callback is NULL",
Error::AllocateMemoryCbkNull => "The allocate memory callback is NULL",
Error::IncorrectParameter => "Argon2_Context context is NULL",
Error::IncorrectType => "There is no such version of Argon2",
Error::OutPtrMismatch => "Output pointer mismatch",
Error::ThreadsTooFew => "Not enough threads",
Error::ThreadsTooMany => "Too many threads",
Error::MissingArgs => "Missing arguments",
Error::EncodingFail => "Encoding failed",
Error::DecodingFail => "Decoding failed",
Error::ThreadFail => "Threading failure",
Error::DecodingLengthFail => "Some of encoded parameters are too long or too short",
Error::VerifyMismatch => "The password does not match the supplied hash",
Error::OsRandom => "OS entropy source failed",
}
}
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.message())
}
}
impl core::error::Error for Error {}
#[cfg(test)]
mod tests {
use super::Error;
#[test]
fn discriminants_match_c() {
assert_eq!(Error::OutputPtrNull.as_c_code(), -1);
assert_eq!(Error::VerifyMismatch.as_c_code(), -35);
assert_eq!(Error::MemoryTooLittle.as_c_code(), -14);
}
#[test]
fn round_trip_every_code() {
for code in Error::MIN_C_CODE..=Error::MAX_C_CODE {
let e = Error::from_c_code(code).expect("every code in range maps");
assert_eq!(e.as_c_code(), code);
}
assert!(Error::from_c_code(0).is_none());
assert!(Error::from_c_code(-36).is_none());
assert!(Error::from_c_code(1).is_none());
}
const CRATE_SPECIFIC: &[Error] = &[Error::OsRandom];
#[test]
fn crate_specific_codes_round_trip_and_avoid_the_c_range() {
for &e in CRATE_SPECIFIC {
let code = e.as_c_code();
assert_eq!(
Error::from_c_code(code),
Some(e),
"{e:?} does not round-trip"
);
assert!(
code < Error::MIN_C_CODE,
"{e:?} ({code}) must sit below the C range so it cannot collide \
with a present or future argon2.h code"
);
assert!(!e.message().is_empty());
}
}
}