Skip to main content

rtc_dtls/record_layer/
record_layer_header.rs

1//! The DTLS record header.
2//!
3//! Thirteen bytes: content type, protocol version, a 16-bit epoch, a 48-bit sequence number, and
4//! the body length. The epoch is what DTLS adds over TLS here — it increments on every
5//! ChangeCipherSpec, so records protected with the old and new keys can be told apart while a
6//! rekey is in flight.
7//!
8//! The sequence number is 48 bits on the wire but held as a `u64`; [`MAX_SEQUENCE_NUMBER`](crate::record_layer::record_layer_header::MAX_SEQUENCE_NUMBER) is the
9//! largest value that fits.
10use crate::content::*;
11
12use shared::error::*;
13
14use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
15use std::io::{Read, Write};
16
17/// Length of the DTLS record header in bytes.
18pub const RECORD_LAYER_HEADER_SIZE: usize = 13;
19/// The largest sequence number the 48-bit field can hold.
20pub const MAX_SEQUENCE_NUMBER: u64 = 0x0000FFFFFFFFFFFF;
21
22/// Major version byte for DTLS 1.2.
23pub const DTLS1_2MAJOR: u8 = 0xfe;
24/// Minor version byte for DTLS 1.2.
25pub const DTLS1_2MINOR: u8 = 0xfd;
26
27/// Major version byte for DTLS 1.0.
28pub const DTLS1_0MAJOR: u8 = 0xfe;
29/// Minor version byte for DTLS 1.0.
30pub const DTLS1_0MINOR: u8 = 0xff;
31
32// VERSION_DTLS12 is the DTLS version in the same style as
33// VersionTLSXX from crypto/tls
34/// DTLS 1.2 as a single 16-bit value.
35pub const VERSION_DTLS12: u16 = 0xfefd;
36
37/// DTLS 1.0 as a [`ProtocolVersion`].
38pub const PROTOCOL_VERSION1_0: ProtocolVersion = ProtocolVersion {
39    major: DTLS1_0MAJOR,
40    minor: DTLS1_0MINOR,
41};
42/// DTLS 1.2 as a [`ProtocolVersion`].
43pub const PROTOCOL_VERSION1_2: ProtocolVersion = ProtocolVersion {
44    major: DTLS1_2MAJOR,
45    minor: DTLS1_2MINOR,
46};
47
48/// ## Specifications
49///
50/// * [RFC 4346 §6.2.1]
51///
52/// [RFC 4346 §6.2.1]: https://tools.ietf.org/html/rfc4346#section-6.2.1
53#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
54pub struct ProtocolVersion {
55    /// The major version byte.
56    pub major: u8,
57    /// The minor version byte.
58    pub minor: u8,
59}
60
61#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
62/// The header on every DTLS record.
63pub struct RecordLayerHeader {
64    /// What the record body holds.
65    pub content_type: ContentType,
66    /// The record's protocol version.
67    pub protocol_version: ProtocolVersion,
68    /// The key epoch, incremented on each ChangeCipherSpec so old and new keys can coexist.
69    pub epoch: u16,
70    /// The record sequence number — a 48-bit field on the wire.
71    pub sequence_number: u64, // uint48 in spec
72    /// The body length in bytes.
73    pub content_len: u16,
74}
75
76impl RecordLayerHeader {
77    /// Encodes this message to `writer`.
78    ///
79    /// # Errors
80    ///
81    /// Fails on a write error, or if a field exceeds the length its wire format allows.
82    pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
83        if self.sequence_number > MAX_SEQUENCE_NUMBER {
84            return Err(Error::ErrSequenceNumberOverflow);
85        }
86
87        writer.write_u8(self.content_type as u8)?;
88        writer.write_u8(self.protocol_version.major)?;
89        writer.write_u8(self.protocol_version.minor)?;
90        writer.write_u16::<BigEndian>(self.epoch)?;
91
92        let be: [u8; 8] = self.sequence_number.to_be_bytes();
93        writer.write_all(&be[2..])?; // uint48 in spec
94
95        writer.write_u16::<BigEndian>(self.content_len)?;
96
97        Ok(writer.flush()?)
98    }
99
100    /// Decodes one of these messages from `reader`.
101    ///
102    /// # Errors
103    ///
104    /// Fails if `reader` is truncated or its contents are not a valid encoding.
105    pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
106        let content_type = reader.read_u8()?.into();
107        let major = reader.read_u8()?;
108        let minor = reader.read_u8()?;
109        let epoch = reader.read_u16::<BigEndian>()?;
110
111        // SequenceNumber is stored as uint48, make into uint64
112        let mut be: [u8; 8] = [0u8; 8];
113        reader.read_exact(&mut be[2..])?;
114        let sequence_number = u64::from_be_bytes(be);
115
116        let protocol_version = ProtocolVersion { major, minor };
117        if protocol_version != PROTOCOL_VERSION1_0 && protocol_version != PROTOCOL_VERSION1_2 {
118            return Err(Error::ErrUnsupportedProtocolVersion);
119        }
120        let content_len = reader.read_u16::<BigEndian>()?;
121
122        Ok(RecordLayerHeader {
123            content_type,
124            protocol_version,
125            epoch,
126            sequence_number,
127            content_len,
128        })
129    }
130}