ytls_record/
record.rs

1mod handshake;
2#[doc(inline)]
3pub use handshake::*;
4
5mod alert;
6#[doc(inline)]
7pub use alert::*;
8
9use crate::error::RecordError;
10
11use zerocopy::byteorder::network_endian::U16 as N16;
12use zerocopy::{Immutable, IntoBytes, KnownLayout, TryFromBytes, Unaligned};
13
14use ytls_traits::ClientHelloProcessor;
15use ytls_traits::ServerRecordProcessor;
16
17/// TLS Record Conten Type
18#[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
19#[repr(u8)]
20#[derive(Copy, Clone, Debug)]
21pub enum ContentType {
22    /// Change Cipher Spec
23    ChangeCipherSpec = 20,
24    /// Alert
25    Alert = 21,
26    /// Handshake
27    Handshake = 22,
28    /// Application Data
29    ApplicationData = 23,
30}
31
32/// TLS Record Layer header
33#[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
34#[repr(C)]
35#[derive(Debug)]
36pub struct RecordHeader {
37    content_type: ContentType,
38    legacy_version: [u8; 2],
39    record_length: N16,
40}
41
42/// TLS Record Layer captured information
43#[derive(Debug)]
44pub struct Record<'r> {
45    header: &'r RecordHeader,
46    raw_bytes: &'r [u8],
47    content: Content<'r>,
48}
49
50/// Content of the underlying Record
51#[derive(Debug)]
52pub enum Content<'r> {
53    ChangeCipherSpec,
54    /// Record is ApplicationData
55    ApplicationData,
56    /// Record is a Handshake
57    Handshake(HandshakeMsg<'r>),
58    /// Record is an Alert
59    Alert(AlertMsg<'r>),
60}
61
62impl<'r> Record<'r> {
63    /// Provide the header associated data
64    #[inline]
65    pub fn header_as_bytes(&self) -> &[u8] {
66        self.header.as_bytes()
67    }
68    /// Provide the raw record in bytes without header
69    #[inline]
70    pub fn as_bytes(&self) -> &[u8] {
71        self.raw_bytes
72    }
73    /// Provide the Conten Type of the Record
74    #[inline]
75    pub fn content_type(&self) -> ContentType {
76        self.header.content_type
77    }
78    /// Provide the Content of the Record
79    #[inline]
80    pub fn content(&'r self) -> &'r Content<'r> {
81        &self.content
82    }
83    /// Only parse appdata in non-handshaking context
84    #[inline]
85    pub fn parse_client_appdata(bytes: &'r [u8]) -> Result<(Record<'r>, &'r [u8]), RecordError> {
86        let (hdr, rest) =
87            RecordHeader::try_ref_from_prefix(bytes).map_err(|e| RecordError::from_zero_copy(e))?;
88
89        if hdr.record_length > 16384 {
90            return Err(RecordError::OverflowLength);
91        }
92        let raw_bytes = &rest[0..usize::from(hdr.record_length)];
93
94        let (content, rest_next) = match hdr.content_type {
95            ContentType::Alert => {
96                let (c, r_next) = AlertMsg::client_parse(rest)?;
97                (Content::Alert(c), r_next)
98            }
99            ContentType::ChangeCipherSpec => {
100                let r_next = &rest[usize::from(hdr.record_length)..];
101                (Content::ChangeCipherSpec, r_next)
102            }
103            ContentType::ApplicationData => {
104                let r_next = &rest[usize::from(hdr.record_length)..];
105                (Content::ApplicationData, r_next)
106            }
107            _ => return Err(RecordError::NotAllowed),
108        };
109
110        Ok((
111            Self {
112                header: hdr,
113                raw_bytes,
114                content,
115            },
116            rest_next,
117        ))
118    }
119    /// Parse incoming byte slics into TLS Recor types with the given RecordProcessor
120    pub fn parse_server<P: ServerRecordProcessor>(
121        prc: &mut P,
122        bytes: &'r [u8],
123    ) -> Result<(Record<'r>, &'r [u8]), RecordError> {
124        let (hdr, rest) =
125            RecordHeader::try_ref_from_prefix(bytes).map_err(|e| RecordError::from_zero_copy(e))?;
126
127        if hdr.record_length > 8192 {
128            return Err(RecordError::OverflowLength);
129        }
130
131        let raw_bytes = &rest[0..usize::from(hdr.record_length)];
132
133        let (content, rest_next) = match hdr.content_type {
134            ContentType::Handshake => {
135                let (c, r_next) = HandshakeMsg::server_parse(prc, rest)?;
136                (Content::Handshake(c), r_next)
137            }
138            ContentType::Alert => {
139                let (c, r_next) = AlertMsg::client_parse(rest)?;
140                (Content::Alert(c), r_next)
141            }
142            ContentType::ApplicationData => {
143                let r_next = &rest[usize::from(hdr.record_length)..];
144                (Content::ApplicationData, r_next)
145            }
146            ContentType::ChangeCipherSpec => {
147                let r_next = &rest[usize::from(hdr.record_length)..];
148                (Content::ChangeCipherSpec, r_next)
149            }
150        };
151
152        Ok((
153            Self {
154                header: hdr,
155                raw_bytes,
156                content,
157            },
158            rest_next,
159        ))
160    }
161    /// Parse incoming byte slices into TLS Record types with the given HelloProcessor.
162    #[inline]
163    pub fn parse_client<P: ClientHelloProcessor>(
164        prc: &mut P,
165        bytes: &'r [u8],
166    ) -> Result<(Record<'r>, &'r [u8]), RecordError> {
167        let (hdr, rest) =
168            RecordHeader::try_ref_from_prefix(bytes).map_err(|e| RecordError::from_zero_copy(e))?;
169
170        if hdr.record_length > 8196 {
171            return Err(RecordError::OverflowLength);
172        }
173
174        if rest.len() < usize::from(hdr.record_length) {
175            return Err(RecordError::OverflowLength);
176        }
177
178        let raw_bytes = &rest[0..usize::from(hdr.record_length)];
179
180        let (content, rest_next) = match hdr.content_type {
181            ContentType::Handshake => {
182                let (c, r_next) = HandshakeMsg::client_parse(prc, rest)?;
183                (Content::Handshake(c), r_next)
184            }
185            ContentType::Alert => {
186                let (c, r_next) = AlertMsg::client_parse(rest)?;
187                (Content::Alert(c), r_next)
188            }
189            ContentType::ApplicationData => {
190                let r_next = &rest[usize::from(hdr.record_length)..];
191                (Content::ApplicationData, r_next)
192            }
193            ContentType::ChangeCipherSpec => {
194                let r_next = &rest[usize::from(hdr.record_length)..];
195                (Content::ChangeCipherSpec, r_next)
196            }
197        };
198
199        Ok((
200            Self {
201                header: hdr,
202                raw_bytes,
203                content,
204            },
205            rest_next,
206        ))
207    }
208}
209
210#[cfg(test)]
211mod test {
212
213    use super::*;
214    use hex_literal::hex;
215
216    #[derive(Debug, PartialEq)]
217    struct Tester {
218        suites_encountered: Vec<[u8; 2]>,
219        extensions_encountered: Vec<u16>,
220    }
221    impl ClientHelloProcessor for Tester {
222        fn handle_extension(&mut self, ext_id: u16, _ext_data: &[u8]) -> () {
223            self.extensions_encountered.push(ext_id);
224        }
225        fn handle_cipher_suite(&mut self, cipher_suite: &[u8; 2]) -> () {
226            self.suites_encountered
227                .push([cipher_suite[0], cipher_suite[1]]);
228        }
229        fn handle_client_random(&mut self, _cr: &[u8; 32]) {
230            //self.client_random = Some(cr);
231        }
232        fn handle_session_id(&mut self, _sid: &[u8]) {
233            //self.session_id = Some(sid);
234        }
235    }
236
237    #[test]
238    fn test_firefox_handshake_client_hello() {
239        let mut tester = Tester {
240            suites_encountered: vec![],
241            extensions_encountered: vec![],
242        };
243        let data = hex!("16030102970100029303030b77e4fa04ceb4dc026c74213fe2a55c14883219b9e6f7b0b503ee2b4a331d842065dcc0babe8c401c1e8afe1f5e40e54155dd0f28e1c7be6e2326143f89bcd95d0022130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f003501000228000000150013000010746573742e72757374637279702e746f00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000022000a00080403050306030203001200000033006b0069001d0020978142d12fa56febea3f967a43cf5accea191ce4cd5dcfe9d1fd7a5817bbc72700170041043d89d5b8f29cb5c29230bcc6eae0c2890f489724426bd26e2a72581231956ae99117c739f4d24d564143a732a73e92421b49ff51a9c44f729460f6ee251e537b002b00050403040303000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001fe0d01190000010003af0020e80e102405db3ad2cfb267f6e11556f1f8b6364f5a02b07897b8eaee4e0d5a1900efe3b3a5d24df62d045ab566ba61536b5443cec82be022b712882204f783afe7c7eb59b93e6b9d30d623fe9a85d0895936be8f85d54818e9a06889e6ed53e3d5aa94e0812c872d5eb40277f6d2b9c1afdbab70bc7da5d6281d2632895855675bc5e7ddadd6aefec02342135082950c430deb6c4ce3d9294929271722aaddb06a7770594ec2bd395378e061b292dfdaa537e2535ca7ee5c698991f8dd8b5c227295e2ceccb7a9b84db5cadcb055f1ef019d6699f76959260a0a49574d18456be3936e74f76d3e5e5b418ddc45b2b219cee91c9ddf0c58dd3c0fb87d954cb59a43d897ed11f7ea0a51fb7b093ad547d2b0");
244        let (r, rest) =
245            Record::parse_client(&mut tester, &data).expect("Test: Record parsing failure.");
246
247        insta::assert_debug_snapshot!(r);
248        assert_eq!(rest.len(), 0);
249    }
250}