use std::convert::Into;
use std::io::Write;
use byteorder::{BigEndian, ByteOrder};
use rust_sodium::crypto::{box_, secretbox};
use crate::errors::{SignalingError, SignalingResult};
use super::cookie::Cookie;
use super::csn::CombinedSequenceSnapshot;
use super::types::{Address, Identity};
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Nonce {
cookie: Cookie,
source: Address,
destination: Address,
csn: CombinedSequenceSnapshot,
}
impl Nonce {
pub(crate) fn new(cookie: Cookie, source: Address, destination: Address, csn: CombinedSequenceSnapshot) -> Self {
Nonce {
cookie,
source,
destination,
csn,
}
}
pub(crate) fn from_bytes(bytes: &[u8]) -> SignalingResult<Self> {
if bytes.len() != 24 {
return Err(SignalingError::Decode(
format!("Byte slice must be exactly 24 bytes, not {}", bytes.len())
));
}
let overflow = BigEndian::read_u16(&bytes[18..20]);
let sequence = BigEndian::read_u32(&bytes[20..24]);
let csn = CombinedSequenceSnapshot::new(overflow, sequence);
Ok(Self {
cookie: Cookie::new([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
]),
source: Address(bytes[16]),
destination: Address(bytes[17]),
csn,
})
}
pub(crate) fn into_bytes(self) -> [u8; 24] {
let mut bytes = [0u8; 24];
(&mut bytes[0..16]).write_all(self.cookie.as_bytes()).expect("Writing cookie to nonce failed");
bytes[16] = self.source.0;
bytes[17] = self.destination.0;
BigEndian::write_u16(&mut bytes[18..20], self.csn.overflow_number());
BigEndian::write_u32(&mut bytes[20..24], self.csn.sequence_number());
bytes
}
pub(crate) fn cookie(&self) -> &Cookie {
&self.cookie
}
pub(crate) fn source(&self) -> Address {
self.source
}
pub(crate) fn source_identity(&self) -> Identity {
Identity::from(self.source)
}
pub(crate) fn destination(&self) -> Address {
self.destination
}
pub(crate) fn csn(&self) -> &CombinedSequenceSnapshot {
&self.csn
}
#[cfg_attr(feature="cargo-clippy", allow(should_implement_trait))]
pub(crate) unsafe fn clone(&self) -> Nonce {
Nonce {
cookie: self.cookie.clone(),
source: self.source,
destination: self.destination,
csn: self.csn.clone(),
}
}
}
impl Into<box_::Nonce> for Nonce {
fn into(self) -> box_::Nonce {
let bytes = self.into_bytes();
box_::Nonce(bytes)
}
}
impl Into<secretbox::Nonce> for Nonce {
fn into(self) -> secretbox::Nonce {
let bytes = self.into_bytes();
secretbox::Nonce(bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_nonce() -> Nonce {
Nonce {
cookie: Cookie::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
source: Address(17),
destination: Address(18),
csn: CombinedSequenceSnapshot::new(258, 50_595_078),
}
}
fn create_test_nonce_bytes() -> [u8; 24] {
[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17,
18,
1, 2,
3, 4, 5, 6,
]
}
#[test]
fn parse_nonce() {
let bytes = create_test_nonce_bytes();
assert_eq!(Nonce::from_bytes(&bytes).unwrap(), create_test_nonce());
}
#[test]
fn nonce_methods() {
let nonce = create_test_nonce();
assert_eq!(nonce.cookie(), &Cookie::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]));
assert_eq!(nonce.source(), Address(17));
assert_eq!(nonce.destination(), Address(18));
assert_eq!(nonce.csn().overflow_number(), 258);
assert_eq!(nonce.csn().sequence_number(), 50_595_078);
}
#[test]
fn serialize_nonce() {
let nonce = create_test_nonce();
assert_eq!(nonce.into_bytes(), create_test_nonce_bytes());
}
#[test]
fn nonce_into_nonce() {
let nonce: Nonce = create_test_nonce();
let nonce_bytes: [u8; 24] = create_test_nonce_bytes();
let rust_sodium_nonce: box_::Nonce = nonce.into();
assert_eq!(rust_sodium_nonce.0, nonce_bytes);
}
}