Skip to main content

oasis_core_runtime/common/crypto/mrae/
nonce.rs

1//! Nonce utility used to ensure nonces are safely incremented.
2use std::ops::Deref;
3
4use anyhow::{anyhow, Result};
5use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
6use rand::{rand_core::TryRng, rngs::SysRng};
7
8/// Size of the nonce in bytes.
9pub use super::deoxysii::NONCE_SIZE;
10/// Size of tag portion of the nonce in bytes. These bytes will never update.
11pub const TAG_SIZE: usize = 11;
12
13/// 120 bit nonce with a 88 bit tag and 32 bit counter. If the counter exceeds
14/// 32 bits, then the nonce is no longer valid and must be refreshed with a new
15/// random nonce. It is expected that all 128 bits are given randomly. However,
16/// the last 32 counting bits may wrap around to ensure 2^32 counts may be used
17/// per nonce.
18#[derive(Debug, Clone)]
19pub struct Nonce {
20    /// The current value of the nonce, from which we may increment.
21    current_value: [u8; NONCE_SIZE],
22    /// The initial value of the nonce, used to ensure we never allow the nonce
23    /// to be the same again (after incrementing 2^32 times).
24    start_value: [u8; NONCE_SIZE],
25}
26
27impl Nonce {
28    /// Create a new nonce.
29    pub fn new(start_value: [u8; NONCE_SIZE]) -> Self {
30        Nonce {
31            current_value: start_value,
32            start_value,
33        }
34    }
35
36    /// Generate a random nonce.
37    pub fn generate() -> Self {
38        let mut start_value = [0u8; NONCE_SIZE];
39        SysRng.try_fill_bytes(&mut start_value).unwrap();
40
41        Self::new(start_value)
42    }
43
44    /// Adds one to the nonce, affecting only the last 32 counting bits.
45    /// Returns an error iff we've exceeded our nonce's counter capacity, i.e.,
46    /// we've incremented 2^32 times. In this case, the Nonce remains unchanged,
47    /// and all subsequent calls to this method will return an Error.
48    pub fn increment(&mut self) -> Result<()> {
49        // Extract the current counter out of the nonce.
50        let mut counter_array = &self.current_value[TAG_SIZE..];
51        // Increment the count and wrap to 0 if necessary.
52        let new_counter: u32 = {
53            let mut counter = counter_array.read_u32::<BigEndian>().unwrap();
54            // If about to overflow wrap around to 0.
55            #[allow(clippy::nonminimal_bool)]
56            if counter == !0u32 {
57                counter = 0;
58            } else {
59                counter += 1;
60            }
61            counter
62        };
63        // Merge this new counter back into the nonce.
64        let new_value: [u8; NONCE_SIZE] = {
65            let mut new_value_vec = self.current_value[..TAG_SIZE].to_vec();
66            new_value_vec.write_u32::<BigEndian>(new_counter).unwrap();
67
68            assert!(new_value_vec.len() == NONCE_SIZE);
69
70            let mut new_value = [0; NONCE_SIZE];
71            new_value.copy_from_slice(&new_value_vec);
72            new_value
73        };
74        // If we've exhausted all 2^32 counters, then error.
75        if new_value == self.start_value {
76            return Err(anyhow!(
77                "This nonce has been exhausted, and a new one must be created",
78            ));
79        }
80        // Update is valid, so mutate.
81        self.current_value = new_value;
82        // Success.
83        Ok(())
84    }
85}
86
87impl Deref for Nonce {
88    type Target = [u8; NONCE_SIZE];
89
90    fn deref(&self) -> &Self::Target {
91        &self.current_value
92    }
93}
94
95#[cfg(test)]
96mod tests {
97
98    use super::*;
99
100    #[test]
101    fn test_increment_zero() {
102        let inner = [0; 15];
103        let mut nonce = Nonce::new(inner);
104        nonce.increment().unwrap();
105        let mut expected = [0; 15];
106        expected[14] = 1;
107        assert_eq!(nonce.to_vec(), expected.to_vec());
108    }
109
110    #[test]
111    fn test_increment_one() {
112        let mut start_value = [0; 15];
113        start_value[14] = 1;
114        let mut nonce = Nonce::new(start_value);
115        nonce.increment().unwrap();
116        let mut expected = [0; 15];
117        expected[14] = 2;
118
119        assert_eq!(nonce.to_vec(), expected.to_vec());
120    }
121
122    #[test]
123    fn test_increment_carry() {
124        let start_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255];
125        let mut nonce = Nonce::new(start_value);
126        nonce.increment().unwrap();
127        let expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0];
128        assert_eq!(nonce.to_vec(), expected.to_vec());
129    }
130
131    #[test]
132    fn test_increment_overflow() {
133        let start_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255];
134        let mut nonce = Nonce::new(start_value);
135        nonce.increment().unwrap();
136        let expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
137        assert_eq!(nonce.to_vec(), expected.to_vec());
138    }
139
140    #[test]
141    fn test_increment_exhaustion() {
142        let start_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255];
143        let current_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 254];
144        let mut nonce = Nonce {
145            start_value,
146            current_value,
147        };
148        assert_eq!(nonce.increment().is_err(), true);
149        // Try again.
150        assert_eq!(nonce.increment().is_err(), true);
151    }
152
153    #[test]
154    fn test_double_increment_exhaustion() {
155        let start_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255];
156        let current_value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 253];
157        let mut nonce = Nonce {
158            start_value,
159            current_value,
160        };
161        let first_expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 254];
162        nonce.increment().unwrap();
163        assert_eq!(nonce.to_vec(), first_expected.to_vec());
164        assert_eq!(nonce.increment().is_err(), true);
165    }
166}