use core::fmt;
use super::PncError;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GenChallenge([u8; Self::LEN]);
impl GenChallenge {
pub const LEN: usize = 16;
#[must_use]
pub const fn new(bytes: [u8; Self::LEN]) -> Self {
Self(bytes)
}
pub fn from_slice(bytes: &[u8]) -> Result<Self, PncError> {
match <[u8; Self::LEN]>::try_from(bytes) {
Ok(bytes) => Ok(Self(bytes)),
Err(_) => Err(PncError::BadChallengeLength { len: bytes.len() }),
}
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.0
}
#[must_use]
pub fn matches(&self, echoed: &[u8]) -> bool {
super::digests_equal(&self.0, echoed)
}
}
impl fmt::Debug for GenChallenge {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("GenChallenge(")?;
for byte in self.0 {
write!(f, "{byte:02X}")?;
}
f.write_str(")")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_challenge_is_exactly_sixteen_bytes() {
assert!(GenChallenge::from_slice(&[0; 16]).is_ok());
assert_eq!(
GenChallenge::from_slice(&[0; 15]),
Err(PncError::BadChallengeLength { len: 15 })
);
assert_eq!(
GenChallenge::from_slice(&[0; 17]),
Err(PncError::BadChallengeLength { len: 17 })
);
}
#[test]
fn matching_is_by_value_and_by_length() {
let c = GenChallenge::new([7; 16]);
assert!(c.matches(&[7; 16]));
assert!(!c.matches(&[7; 17]));
assert!(!c.matches(&[]));
let mut off_by_one = [7u8; 16];
off_by_one[15] = 8;
assert!(!c.matches(&off_by_one));
}
#[test]
fn debug_is_hex_so_two_challenges_can_be_told_apart() {
extern crate alloc;
use alloc::format;
let c = GenChallenge::new([0xAB; 16]);
assert_eq!(format!("{c:?}"), "GenChallenge(ABABABABABABABABABABABABABABABAB)");
}
}