ytls_util/
nonce.rs

1use crypto_bigint::Encoding;
2
3use crypto_bigint::U128;
4
5#[cfg(feature = "zeroize")]
6use zeroize::{Zeroize, ZeroizeOnDrop};
7
8/// Running 12-byte (U96) Nonce for ChaCha20Poly1305 / AES GCM AEADs
9#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
10pub struct Nonce12 {
11    iv: U128,
12    seq_id: u64,
13}
14
15impl Nonce12 {
16    /// Start Nonce12 with TLS1.2 Key Schedule derived 12-byte "IV"
17    #[inline]
18    pub fn from_ks_iv(i: &[u8; 12]) -> Self {
19        let iv = U128::from_be_bytes([
20            0, 0, 0, 0, i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7], i[8], i[9], i[10], i[11],
21        ]);
22        let seq_id = 0u64;
23
24        Self { iv, seq_id }
25    }
26    /// Use current and return it whilst incrementing the packet sequence counter.
27    #[inline]
28    pub fn use_and_incr(&mut self) -> Option<[u8; 12]> {
29        let seq_id = U128::from_u64(self.seq_id);
30        let nonce_u128 = self.iv.wrapping_xor(&seq_id);
31        let b: [u8; 16] = nonce_u128.to_be_bytes();
32
33        if self.seq_id == u64::MAX {
34            return None;
35        }
36
37        self.seq_id += 1;
38
39        Some([
40            b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15],
41        ])
42    }
43    /// Through fast forward to X for testing purposes
44    #[inline]
45    #[cfg(test)]
46    pub fn hazmat_fast_forward_with_incr(&mut self, fast_forward: u64) -> Option<[u8; 12]> {
47        self.seq_id = fast_forward;
48        self.use_and_incr()
49    }
50}
51
52#[cfg(test)]
53mod test {
54
55    use super::*;
56    use hex_literal::hex;
57
58    #[test]
59    fn cur() {
60        let iv_bytes: [u8; 12] = hex!("6fac81d4f2c3bebe02b8b375");
61        let mut running_nonce = Nonce12::from_ks_iv(&iv_bytes);
62        let cur = running_nonce.use_and_incr();
63
64        assert_eq!(cur, Some(iv_bytes));
65    }
66
67    #[test]
68    fn packet_seq_1() {
69        let iv_bytes: [u8; 12] = hex!("6fac81d4f2c3bebe02b8b375");
70        let mut running_nonce = Nonce12::from_ks_iv(&iv_bytes);
71        let _cur = running_nonce.use_and_incr();
72        let seq_1 = running_nonce.use_and_incr();
73
74        assert_eq!(seq_1, Some(hex!("6fac81d4f2c3bebe02b8b374")));
75    }
76
77    // Wrapping u64 is not allowed, ensure it returns None
78    #[test]
79    fn packet_seq_max() {
80        let iv_bytes: [u8; 12] = hex!("6fac81d4f2c3bebe02b8b375");
81        let mut running_nonce = Nonce12::from_ks_iv(&iv_bytes);
82        let cur = running_nonce.hazmat_fast_forward_with_incr(u64::MAX);
83
84        assert_eq!(cur, None);
85    }
86}