use std::ops::Deref;
use anyhow::{anyhow, Result};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use rand::{rngs::OsRng, Rng};
pub use super::deoxysii::NONCE_SIZE;
pub const TAG_SIZE: usize = 11;
#[derive(Debug, Clone)]
pub struct Nonce {
current_value: [u8; NONCE_SIZE],
start_value: [u8; NONCE_SIZE],
}
impl Nonce {
pub fn new(start_value: [u8; NONCE_SIZE]) -> Self {
Nonce {
current_value: start_value,
start_value,
}
}
pub fn generate() -> Self {
let mut rng = OsRng {};
let mut start_value = [0u8; NONCE_SIZE];
rng.fill(&mut start_value);
Self::new(start_value)
}
pub fn increment(&mut self) -> Result<()> {
let mut counter_array = &self.current_value[TAG_SIZE..];
let new_counter: u32 = {
let mut counter = counter_array.read_u32::<BigEndian>().unwrap();
#[allow(clippy::nonminimal_bool)]
if counter == !0u32 {
counter = 0;
} else {
counter += 1;
}
counter
};
let new_value: [u8; NONCE_SIZE] = {
let mut new_value_vec = self.current_value[..TAG_SIZE].to_vec();
new_value_vec.write_u32::<BigEndian>(new_counter).unwrap();
assert!(new_value_vec.len() == NONCE_SIZE);
let mut new_value = [0; NONCE_SIZE];
new_value.copy_from_slice(&new_value_vec);
new_value
};
if new_value == self.start_value {
return Err(anyhow!(
"This nonce has been exhausted, and a new one must be created",
));
}
self.current_value = new_value;
Ok(())
}
}
impl Deref for Nonce {
type Target = [u8; NONCE_SIZE];
fn deref(&self) -> &Self::Target {
&self.current_value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_increment_zero() {
let inner = [0; 15];
let mut nonce = Nonce::new(inner);
nonce.increment().unwrap();
let mut expected = [0; 15];
expected[14] = 1;
assert_eq!(nonce.to_vec(), expected.to_vec());
}
#[test]
fn test_increment_one() {
let mut start_value = [0; 15];
start_value[14] = 1;
let mut nonce = Nonce::new(start_value);
nonce.increment().unwrap();
let mut expected = [0; 15];
expected[14] = 2;
assert_eq!(nonce.to_vec(), expected.to_vec());
}
#[test]
fn test_increment_carry() {
let start_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255];
let mut nonce = Nonce::new(start_value);
nonce.increment().unwrap();
let expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0];
assert_eq!(nonce.to_vec(), expected.to_vec());
}
#[test]
fn test_increment_overflow() {
let start_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255];
let mut nonce = Nonce::new(start_value);
nonce.increment().unwrap();
let expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
assert_eq!(nonce.to_vec(), expected.to_vec());
}
#[test]
fn test_increment_exhaustion() {
let start_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255];
let current_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 254];
let mut nonce = Nonce {
start_value,
current_value,
};
assert_eq!(nonce.increment().is_err(), true);
assert_eq!(nonce.increment().is_err(), true);
}
#[test]
fn test_double_increment_exhaustion() {
let start_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255];
let current_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 253];
let mut nonce = Nonce {
start_value,
current_value,
};
let first_expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 254];
nonce.increment().unwrap();
assert_eq!(nonce.to_vec(), first_expected.to_vec());
assert_eq!(nonce.increment().is_err(), true);
}
}