Skip to main content

rtc_dtls/handshake/
handshake_random.rs

1use rand::RngExt;
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use std::io::{self, Read, Write};
5use std::time::{Duration, SystemTime};
6
7/// Bytes of randomness in a handshake random, excluding the timestamp.
8pub const RANDOM_BYTES_LENGTH: usize = 28;
9/// Total length of a handshake random: 4 bytes of timestamp plus 28 random.
10pub const HANDSHAKE_RANDOM_LENGTH: usize = RANDOM_BYTES_LENGTH + 4;
11
12/// ## Specifications
13///
14/// * [RFC 4346 §7.4.1.2]
15///
16/// [RFC 4346 §7.4.1.2]: https://tools.ietf.org/html/rfc4346#section-7.4.1.2
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct HandshakeRandom {
19    /// The sender's clock at the time the random was generated.
20    pub gmt_unix_time: SystemTime,
21    /// 28 bytes of randomness, which feed key derivation.
22    pub random_bytes: [u8; RANDOM_BYTES_LENGTH],
23}
24
25impl Default for HandshakeRandom {
26    fn default() -> Self {
27        HandshakeRandom {
28            gmt_unix_time: SystemTime::UNIX_EPOCH,
29            random_bytes: [0u8; RANDOM_BYTES_LENGTH],
30        }
31    }
32}
33
34impl HandshakeRandom {
35    /// The encoded size of this message in bytes.
36    pub fn size(&self) -> usize {
37        4 + RANDOM_BYTES_LENGTH
38    }
39
40    /// Encodes this message to `writer`.
41    ///
42    /// # Errors
43    ///
44    /// Fails on a write error, or if a field exceeds the length its wire format allows.
45    pub fn marshal<W: Write>(&self, writer: &mut W) -> io::Result<()> {
46        let secs = match self.gmt_unix_time.duration_since(SystemTime::UNIX_EPOCH) {
47            Ok(d) => d.as_secs() as u32,
48            Err(_) => 0,
49        };
50        writer.write_u32::<BigEndian>(secs)?;
51        writer.write_all(&self.random_bytes)?;
52
53        writer.flush()
54    }
55
56    /// Decodes one of these messages from `reader`.
57    ///
58    /// # Errors
59    ///
60    /// Fails if `reader` is truncated or its contents are not a valid encoding.
61    pub fn unmarshal<R: Read>(reader: &mut R) -> io::Result<Self> {
62        let secs = reader.read_u32::<BigEndian>()?;
63        let gmt_unix_time = if let Some(unix_time) =
64            SystemTime::UNIX_EPOCH.checked_add(Duration::new(secs as u64, 0))
65        {
66            unix_time
67        } else {
68            SystemTime::UNIX_EPOCH
69        };
70
71        let mut random_bytes = [0u8; RANDOM_BYTES_LENGTH];
72        reader.read_exact(&mut random_bytes)?;
73
74        Ok(HandshakeRandom {
75            gmt_unix_time,
76            random_bytes,
77        })
78    }
79
80    // populate fills the HandshakeRandom with random values
81    // may be called multiple times
82    /// Fills in the current time and fresh random bytes.
83    pub fn populate(&mut self) {
84        self.gmt_unix_time = SystemTime::now();
85        rand::rng().fill(&mut self.random_bytes);
86    }
87}