// Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Stateless Reset Token implementation. use neqo_common::Decoder; use neqo_crypto::random; use crate::Error; /// A stateless reset token is a 16-byte value that is used to identify /// a stateless reset packet. #[derive(Clone, Debug, Default, Eq)] pub struct Token([u8; Self::LEN]); impl Token { pub const LEN: usize = 16; /// Create a new stateless reset token from a byte array. #[must_use] pub const fn new(token: [u8; Self::LEN]) -> Self { Self(token) } /// Generate a random stateless reset token. #[must_use] pub fn random() -> Self { Self(random::<{ Self::LEN }>()) } /// Get the token as a byte array. #[must_use] pub const fn as_bytes(&self) -> &[u8; Self::LEN] { &self.0 } } /// Compare two tokens in constant time to prevent timing attacks. impl PartialEq for Token { fn eq(&self, other: &Self) -> bool { // rustc might decide to optimize this and make this non-constant-time. // It doesn't appear to currently. let mut c = 0; for (&a, &b) in self.0.iter().zip(&other.0) { c |= a ^ b; } c == 0 } } impl TryFrom<&[u8]> for Token { type Error = Error; fn try_from(value: &[u8]) -> Result { Ok(Self(value.try_into()?)) } } impl TryFrom<&mut Decoder<'_>> for Token { type Error = Error; fn try_from(d: &mut Decoder<'_>) -> Result { Ok(Self( d.decode(Self::LEN).ok_or(Error::NoMoreData)?.try_into()?, )) } } impl AsRef<[u8]> for Token { fn as_ref(&self) -> &[u8] { &self.0 } } #[cfg(test)] mod tests { use super::*; #[test] fn new_token() { let bytes = [1u8; Token::LEN]; let token = Token::new(bytes); assert_eq!(token.as_bytes(), &bytes); } #[test] fn random_token() { neqo_crypto::init().unwrap(); let token1 = Token::random(); let token2 = Token::random(); // With very high probability, two random tokens should be different assert_ne!(token1, token2); } #[test] fn eq_same() { let bytes = [42u8; Token::LEN]; let token1 = Token::new(bytes); let token2 = Token::new(bytes); assert_eq!(token1, token2); } #[test] fn eq_different() { let token1 = Token::new([1u8; Token::LEN]); let token2 = Token::new([2u8; Token::LEN]); assert_ne!(token1, token2); } #[test] fn from_slice_valid() { let bytes = [3u8; Token::LEN]; let token = Token::try_from(&bytes[..]).unwrap(); assert_eq!(token.as_bytes(), &bytes); } #[test] fn from_slice_invalid_length() { let bytes = [3u8; 15]; let result = Token::try_from(&bytes[..]); assert!(result.is_err()); } }