Revision control

Copy as Markdown

Other Tools

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
impl TryFrom<&mut Decoder<'_>> for Token {
type Error = Error;
fn try_from(d: &mut Decoder<'_>) -> Result<Self, Self::Error> {
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());
}
}