tls_parser/
tls_record.rs

1use alloc::vec::Vec;
2use nom::bytes::streaming::take;
3use nom::combinator::{complete, map_parser};
4use nom::error::{make_error, ErrorKind};
5use nom::multi::many1;
6use nom::{Err, IResult};
7use nom_derive::{NomBE, Parse};
8use rusticata_macros::newtype_enum;
9
10use crate::tls_handshake::*;
11use crate::tls_message::*;
12use crate::TlsVersion;
13
14/// Max record size for TLSCipherText (RFC8446 5.2)
15pub const MAX_RECORD_LEN: u16 = (1 << 14) + 256;
16
17/// Content type, as defined in IANA TLS ContentType registry
18#[derive(Clone, Copy, PartialEq, Eq, NomBE)]
19pub struct TlsRecordType(pub u8);
20
21newtype_enum! {
22impl debug TlsRecordType {
23    ChangeCipherSpec = 0x14,
24    Alert            = 0x15,
25    Handshake        = 0x16,
26    ApplicationData  = 0x17,
27    Heartbeat        = 0x18,
28}
29}
30
31impl From<TlsRecordType> for u8 {
32    fn from(v: TlsRecordType) -> u8 {
33        v.0
34    }
35}
36
37/// TLS record header
38#[derive(Clone, Copy, PartialEq, NomBE)]
39pub struct TlsRecordHeader {
40    pub record_type: TlsRecordType,
41    pub version: TlsVersion,
42    pub len: u16,
43}
44
45/// TLS plaintext record
46///
47/// A TLS record can contain multiple messages (sharing the same record type).
48/// Plaintext records can only be found during the handshake.
49#[derive(Clone, Debug, PartialEq)]
50pub struct TlsPlaintext<'a> {
51    pub hdr: TlsRecordHeader,
52    pub msg: Vec<TlsMessage<'a>>,
53}
54
55/// TLS encrypted data
56///
57/// This struct only contains an opaque pointer (data are encrypted).
58#[derive(Clone, Debug, PartialEq)]
59pub struct TlsEncryptedContent<'a> {
60    pub blob: &'a [u8],
61}
62
63/// Encrypted TLS record (containing opaque data)
64#[derive(Clone, Debug, PartialEq)]
65pub struct TlsEncrypted<'a> {
66    pub hdr: TlsRecordHeader,
67    pub msg: TlsEncryptedContent<'a>,
68}
69
70/// Tls Record with raw (unparsed) data
71///
72/// Use [`parse_tls_raw_record`] to parse content
73#[derive(Clone, Debug, PartialEq)]
74pub struct TlsRawRecord<'a> {
75    pub hdr: TlsRecordHeader,
76    pub data: &'a [u8],
77}
78
79/// Read TLS record header
80///
81/// This function is used to get the record header.
82/// After calling this function, caller can read the expected number of bytes and use
83/// [`parse_tls_record_with_header`] to parse content.
84#[inline]
85pub fn parse_tls_record_header(i: &[u8]) -> IResult<&[u8], TlsRecordHeader> {
86    TlsRecordHeader::parse(i)
87}
88
89/// Given data and a TLS record header, parse content.
90///
91/// A record can contain multiple messages (with the same type).
92///
93/// Note that message length is checked (not required for parser safety, but for
94/// strict protocol conformance).
95#[rustfmt::skip]
96#[allow(clippy::trivially_copy_pass_by_ref)] // TlsRecordHeader is only 6 bytes, but we prefer not breaking current API
97pub fn parse_tls_record_with_header<'i>(i:&'i [u8], hdr:&TlsRecordHeader ) -> IResult<&'i [u8], Vec<TlsMessage<'i>>> {
98    match hdr.record_type {
99        TlsRecordType::ChangeCipherSpec => many1(complete(parse_tls_message_changecipherspec))(i),
100        TlsRecordType::Alert            => many1(complete(parse_tls_message_alert))(i),
101        TlsRecordType::Handshake        => many1(complete(parse_tls_message_handshake))(i),
102        TlsRecordType::ApplicationData  => many1(complete(parse_tls_message_applicationdata))(i),
103        TlsRecordType::Heartbeat        => parse_tls_message_heartbeat(i, hdr.len),
104        _                               => Err(Err::Error(make_error(i, ErrorKind::Switch)))
105    }
106}
107
108/// Parse one packet only, as plaintext
109/// A single record can contain multiple messages, they must share the same record type
110pub fn parse_tls_plaintext(i: &[u8]) -> IResult<&[u8], TlsPlaintext> {
111    let (i, hdr) = parse_tls_record_header(i)?;
112    if hdr.len > MAX_RECORD_LEN {
113        return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
114    }
115    let (i, msg) = map_parser(take(hdr.len as usize), |i| {
116        parse_tls_record_with_header(i, &hdr)
117    })(i)?;
118    Ok((i, TlsPlaintext { hdr, msg }))
119}
120
121/// Parse one packet only, as encrypted content
122pub fn parse_tls_encrypted(i: &[u8]) -> IResult<&[u8], TlsEncrypted> {
123    let (i, hdr) = parse_tls_record_header(i)?;
124    if hdr.len > MAX_RECORD_LEN {
125        return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
126    }
127    let (i, blob) = take(hdr.len as usize)(i)?;
128    let msg = TlsEncryptedContent { blob };
129    Ok((i, TlsEncrypted { hdr, msg }))
130}
131
132/// Read TLS record envelope, but do not decode data
133///
134/// This function is used to get the record type, and to make sure the record is
135/// complete (not fragmented).
136/// After calling this function, use [`parse_tls_record_with_header`] to parse content.
137pub fn parse_tls_raw_record(i: &[u8]) -> IResult<&[u8], TlsRawRecord> {
138    let (i, hdr) = parse_tls_record_header(i)?;
139    if hdr.len > MAX_RECORD_LEN {
140        return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
141    }
142    let (i, data) = take(hdr.len as usize)(i)?;
143    Ok((i, TlsRawRecord { hdr, data }))
144}
145
146/// Parse one packet only, as plaintext
147///
148/// This function is deprecated. Use [`parse_tls_plaintext`] instead.
149///
150/// This function will be removed from API, as the name is not correct: it is
151/// not possible to parse TLS packets without knowing the TLS state.
152#[deprecated(since = "0.5.0", note = "Use parse_tls_plaintext")]
153#[inline]
154pub fn tls_parser(i: &[u8]) -> IResult<&[u8], TlsPlaintext> {
155    parse_tls_plaintext(i)
156}
157
158/// Parse one chunk of data, possibly containing multiple TLS plaintext records
159///
160/// This function is deprecated. Use [`parse_tls_plaintext`] instead, checking if
161/// there are remaining bytes, and calling [`parse_tls_plaintext`] recursively.
162///
163/// This function will be removed from API, as it should be replaced by a more
164/// useful one to handle fragmentation.
165pub fn tls_parser_many(i: &[u8]) -> IResult<&[u8], Vec<TlsPlaintext>> {
166    many1(complete(parse_tls_plaintext))(i)
167}