use crate::core::{AEAD_NONCE_SIZE, NONCE_DIR_INITIATOR, NONCE_DIR_RESPONDER};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Direction {
InitiatorToResponder,
ResponderToInitiator,
}
impl Direction {
pub fn as_byte(self) -> u8 {
match self {
Direction::InitiatorToResponder => NONCE_DIR_INITIATOR,
Direction::ResponderToInitiator => NONCE_DIR_RESPONDER,
}
}
pub fn opposite(self) -> Self {
match self {
Direction::InitiatorToResponder => Direction::ResponderToInitiator,
Direction::ResponderToInitiator => Direction::InitiatorToResponder,
}
}
}
pub fn construct_nonce(epoch: u32, direction: Direction, counter: u64) -> [u8; AEAD_NONCE_SIZE] {
let mut nonce = [0u8; AEAD_NONCE_SIZE];
nonce[0..4].copy_from_slice(&epoch.to_le_bytes());
nonce[4] = direction.as_byte();
nonce[16..24].copy_from_slice(&counter.to_le_bytes());
nonce
}
pub fn parse_nonce(nonce: &[u8; AEAD_NONCE_SIZE]) -> (u32, Direction, u64) {
let epoch = u32::from_le_bytes([nonce[0], nonce[1], nonce[2], nonce[3]]);
let direction = if nonce[4] == NONCE_DIR_INITIATOR {
Direction::InitiatorToResponder
} else {
Direction::ResponderToInitiator
};
let counter = u64::from_le_bytes([
nonce[16], nonce[17], nonce[18], nonce[19], nonce[20], nonce[21], nonce[22], nonce[23],
]);
(epoch, direction, counter)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nonce_construction() {
let nonce = construct_nonce(1, Direction::InitiatorToResponder, 42);
assert_eq!(nonce.len(), AEAD_NONCE_SIZE);
assert_eq!(&nonce[0..4], &1u32.to_le_bytes());
assert_eq!(nonce[4], 0x00);
assert_eq!(&nonce[5..16], &[0u8; 11]);
assert_eq!(&nonce[16..24], &42u64.to_le_bytes());
}
#[test]
fn test_nonce_roundtrip() {
let epoch = 0x12345678;
let direction = Direction::ResponderToInitiator;
let counter = 0xDEADBEEFCAFEBABE;
let nonce = construct_nonce(epoch, direction, counter);
let (parsed_epoch, parsed_dir, parsed_counter) = parse_nonce(&nonce);
assert_eq!(parsed_epoch, epoch);
assert_eq!(parsed_dir, direction);
assert_eq!(parsed_counter, counter);
}
#[test]
fn test_direction_opposite() {
assert_eq!(
Direction::InitiatorToResponder.opposite(),
Direction::ResponderToInitiator
);
assert_eq!(
Direction::ResponderToInitiator.opposite(),
Direction::InitiatorToResponder
);
}
#[test]
fn test_direction_bytes() {
assert_eq!(Direction::InitiatorToResponder.as_byte(), 0x00);
assert_eq!(Direction::ResponderToInitiator.as_byte(), 0x01);
}
}