tls_parser/lib.rs
1//! [](./LICENSE-MIT)
2//! [](./LICENSE-APACHE)
3//! [](https://crates.io/crates/tls-parser)
4//! [](https://github.com/cpu/tls-parser/actions/workflows/rust.yml)
5//! [](#rust-version-requirements)
6//!
7//! # TLS Parser
8//!
9//! A TLS parser, implemented with the [nom](https://github.com/Geal/nom)
10//! parser combinator framework.
11//!
12//! The goal of this parser is to implement TLS messages analysis, for example
13//! to use rules from a network IDS, for ex during the TLS handshake.
14//!
15//! It implements structures and parsing functions for records and messages, but
16//! need additional code to handle fragmentation, or to fully inspect messages.
17//! Parsing some TLS messages requires to know the previously selected parameters.
18//! See [the rusticata TLS parser](https://github.com/rusticata/rusticata/blob/master/src/tls.rs)
19//! for a full example.
20//!
21//! It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken
22//! to ensure security and safety of this crate, including design (recursion limit, defensive
23//! programming), tests, and fuzzing. It also aims to be panic-free.
24//!
25//! The code is available on [Github](https://github.com/rusticata/tls-parser)
26//! and is part of the [Rusticata](https://github.com/rusticata) project.
27//!
28//! ## Parsing records
29//!
30//! The main parsing functions are located in the [tls.rs](src/tls.rs) file. The entry functions are:
31//! - `parse_tls_plaintext`: parses a record as plaintext
32//! - `parse_tls_encrypted`: read an encrypted record. The parser has no crypto or decryption features, so the content
33//! will be left as opaque data.
34//!
35//! # Examples
36//!
37//! ```rust
38//! use tls_parser::parse_tls_plaintext;
39//! use tls_parser::nom::{Err, IResult};
40//!
41//! let bytes : &[u8]= include_bytes!("../assets/client_hello_dhe.bin");
42//! // [ 0x16, 0x03, 0x01 ... ];
43//! let res = parse_tls_plaintext(&bytes);
44//! match res {
45//! Ok((rem,record)) => {
46//! // rem is the remaining data (not parsed)
47//! // record is an object of type TlsRecord
48//! },
49//! Err(Err::Incomplete(needed)) => {
50//! eprintln!("Defragmentation required (TLS record)");
51//! },
52//! Err(e) => { eprintln!("parse_tls_record_with_header failed: {:?}",e); }
53//! }
54//! ```
55//!
56//! Note that knowing if a record is plaintext or not is the responsibility of the caller.
57//!
58//! As reading TLS records may imply defragmenting records, some functions are
59//! provided to only read the record as opaque data (which ensures the record is
60//! complete and gives the record header) and then reading messages from data.
61//!
62//! Here is an example of two-steps parsing:
63//!
64//! ```rust
65//! # use tls_parser::{parse_tls_raw_record, parse_tls_record_with_header};
66//! # use tls_parser::nom::{Err, IResult};
67//!
68//! # let bytes : &[u8]= include_bytes!("../assets/client_hello_dhe.bin");
69//! // [ 0x16, 0x03, 0x01 ... ];
70//! match parse_tls_raw_record(bytes) {
71//! Ok((rem, ref r)) => {
72//! match parse_tls_record_with_header(r.data, &r.hdr) {
73//! Ok((rem2,ref msg_list)) => {
74//! for msg in msg_list {
75//! // msg has type TlsMessage
76//! }
77//! }
78//! Err(Err::Incomplete(needed)) => { eprintln!("incomplete record") }
79//! Err(_) => { eprintln!("error while parsing record") }
80//! }
81//! }
82//! Err(Err::Incomplete(needed)) => { eprintln!("incomplete record header") }
83//! Err(_) => { eprintln!("error while parsing record header") }
84//! }
85//! ```
86//!
87//! Some additional work is required if reading packets from the network, to support
88//! reassembly of TCP segments and reassembly of TLS records.
89//!
90//! For a complete example of a TLS parser supporting defragmentation and states, see the
91//! [rusticata/src/tls.rs](https://github.com/rusticata/rusticata/blob/master/src/tls.rs) file of
92//! the [rusticata](https://github.com/rusticata/rusticata) crate.
93//!
94//! ## State machine
95//!
96//! A TLS state machine is provided in [tls_states.rs](src/tls_states.rs). The state machine is separated from the
97//! parsing functions, and is almost independent.
98//! It is implemented as a table of transitions, mainly for the handshake phase.
99//!
100//! After reading a TLS message using the previous functions, the TLS state can be
101//! updated using the `tls_state_transition` function. If the transition succeeds,
102//! it returns `Ok(new_state)`, otherwise it returns `Err(error_state)`.
103//!
104//! ```rust
105//! # use tls_parser::{tls_state_transition, TlsMessage, TlsState};
106//! # use tls_parser::nom::{Err, IResult};
107//!
108//! struct ParseContext {
109//! state: TlsState,
110//! }
111//!
112//! # fn update_state_machine(msg: &TlsMessage, ctx: &mut ParseContext, to_server:bool) -> Result<(),&'static str> {
113//! match tls_state_transition(ctx.state, msg, to_server) {
114//! Ok(s) => { ctx.state = s; Ok(()) }
115//! Err(_) => {
116//! ctx.state = TlsState::Invalid;
117//! Err("Invalid state")
118//! }
119//! }
120//! # }
121//! ```
122//!
123//! # Implementation notes
124//!
125//! When parsing messages, if a field is an integer corresponding to an enum of known values,
126//! it is not parsed as an enum type, but as an integer. While this complicates accesses,
127//! it allows to read invalid values and continue parsing (for an IDS, it's better to read
128//! values than to get a generic parse error).
129
130#![deny(/*missing_docs,*/
131 unstable_features,
132 /*unused_import_braces,*/ unused_qualifications)]
133#![forbid(unsafe_code)]
134#![allow(clippy::upper_case_acronyms)]
135#![no_std]
136
137#[cfg(any(test, feature = "std"))]
138#[macro_use]
139extern crate std;
140
141extern crate alloc;
142
143mod certificate_transparency;
144mod dtls;
145mod tls_alert;
146mod tls_ciphers;
147mod tls_debug;
148mod tls_dh;
149mod tls_ec;
150mod tls_extensions;
151mod tls_handshake;
152mod tls_message;
153mod tls_record;
154mod tls_sign_hash;
155mod tls_states;
156
157pub use certificate_transparency::*;
158pub use dtls::*;
159pub use tls_alert::*;
160pub use tls_ciphers::*;
161pub use tls_dh::*;
162pub use tls_ec::*;
163pub use tls_extensions::*;
164pub use tls_handshake::*;
165pub use tls_message::*;
166pub use tls_record::*;
167pub use tls_sign_hash::*;
168pub use tls_states::*;
169
170#[cfg(all(feature = "serialize", not(feature = "std")))]
171compile_error!("features `serialize` cannot be enabled when using `no_std`");
172
173#[cfg(all(feature = "serialize", feature = "std"))]
174mod tls_serialize;
175#[cfg(feature = "serialize")]
176pub use tls_serialize::*;
177
178pub use nom;
179pub use nom::{Err, IResult};
180pub use rusticata_macros;