tls_detect/
lib.rs

1/*
2 * Copyright 2014 The Netty Project
3 * Copyright 2024 Alexander Liesenfeld
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 * use this file except in compliance with the License. You may obtain a copy of
7 * the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17
18//! This library contains utilities to simplify operating multiple protocols through a
19//! single network port.
20//!
21use async_trait::async_trait;
22use std::fmt::{Display, Formatter};
23
24#[async_trait]
25pub trait Read<'a> {
26    /// Peeks or reads byte.
27    async fn read_byte(&mut self, from_offset: usize) -> std::io::Result<u8>;
28
29    /// Peeks or reads u16 from big endian.
30    async fn read_bytes(
31        &mut self,
32        from_offset: usize,
33        to_offset: usize,
34    ) -> std::io::Result<Vec<u8>>; // TODO: Potentially make this more efficient by returning a slice (be aware of lifetime hurdles!)
35
36    /// Peeks or reads u16 from big endian.
37    async fn read_u16_from_be(&mut self, offset: usize) -> std::io::Result<u16>;
38
39    /// Peeks or reads until limit.
40    async fn buffer_to(&mut self, limit: usize) -> std::io::Result<()>;
41}
42
43// ************************************************************************************************
44// Errors
45#[derive(Debug)]
46pub enum Error {
47    NotEnoughDataError,
48    NotEncryptedError,
49    IoError(std::io::Error),
50}
51
52impl Display for Error {
53    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
54        match *self {
55            Error::NotEncryptedError => write!(f, "Byte buffer does not seem to be encrypted"),
56            Error::NotEnoughDataError => write!(f, "Byte buffer length too short"),
57            Error::IoError(ref err) => write!(f, "IO error: {}", err),
58        }
59    }
60}
61
62impl std::error::Error for Error {}
63
64impl From<std::io::Error> for Error {
65    fn from(error: std::io::Error) -> Self {
66        match error.kind() {
67            std::io::ErrorKind::UnexpectedEof => Error::NotEnoughDataError,
68            _ => Error::IoError(error),
69        }
70    }
71}
72
73// ************************************************************************************************
74// Constants
75
76/// Change Cipher Spec (20): Indicates that subsequent records will
77/// be protected under the newly negotiated CipherSpec and keys.
78const SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC: u8 = 20;
79
80/// Alert (21): Used to convey alerts to the peer. Alerts can be of
81/// severity warning or fatal and include a description of the alert.
82const SSL_CONTENT_TYPE_ALERT: u8 = 21;
83
84/// Handshake (22): Manages the negotiation of security parameters
85/// for the SSL/TLS session. It encompasses a series of messages
86/// for capabilities exchange, key distribution, and session setup.
87const SSL_CONTENT_TYPE_HANDSHAKE: u8 = 22;
88
89/// Application Data (23): Used for transmitting encrypted data
90/// (payload) between the client and server once a secure connection
91/// is established.
92const SSL_CONTENT_TYPE_APPLICATION_DATA: u8 = 23;
93
94/// Extension Heartbeat (24): Supports a keep-alive functionality
95/// within the TLS protocol, allowing for heartbeat messages to
96/// maintain the connection without full renegotiation. Its usage
97/// has become less common due to security vulnerabilities like Heartbleed.
98const SSL_CONTENT_TYPE_EXTENSION_HEARTBEAT: u8 = 24;
99
100/// Represents a specific version of the GMSSL protocol that the application supports.
101///
102/// GMSSL is an extension of SSL/TLS protocols with additional features and security mechanisms.
103/// This constant is used to identify the protocol version during the SSL/TLS handshake process
104/// and ensure compatibility between client and server.
105///
106/// The use of GMSSL is particularly important for applications and services that need to
107/// adhere to Chinese cryptographic standards for reasons of regulatory compliance,
108/// security policy, or interoperability with Chinese technologies and networks.
109///
110/// The value `0x101` corresponds to a specific version of GMSSL, indicating support for
111/// particular cryptographic algorithms and security features.
112const GMSSL_PROTOCOL_VERSION: u16 = 0x101;
113
114/// Represents the length of the SSL (Secure Sockets Layer) record header in bytes.
115///
116/// In SSL/TLS protocols, each record transmitted or received is prefixed with a header.
117/// This header contains essential information for processing the record, such as its type
118/// (e.g., handshake, data, alert), version, and the length of the payload.
119///
120/// The constant value of `5` bytes is derived from the following components of the SSL record header:
121/// - 1 byte for the "Content Type" indicating the type of record (handshake, application data, etc.).
122/// - 2 bytes for the "Version" specifying the SSL or TLS version used.
123/// - 2 bytes for the "Length" denoting the size of the record payload in bytes (excluding the header).
124///
125/// This constant is critical for correctly parsing and constructing SSL/TLS records, ensuring proper
126/// protocol operation and security.
127///
128/// Note: While this constant is specific to SSL, it is also applicable to TLS records,
129/// as the header format has remained consistent across versions of the protocol.
130const SSL_RECORD_HEADER_LENGTH: u16 = 5;
131
132// The following constants are representing the version numbers for different versions of the
133// DTLS (Datagram Transport Layer Security) protocol. DTLS is designed to provide secure
134// communication between clients and servers over datagram protocols such as UDP. It is based on
135// the TLS (Transport Layer Security) protocol and provides similar security guarantees.
136
137/// DTLS version 1.0 identifier.
138///
139/// This version is defined in RFC 4347 and is identified by the specific protocol version number `0xFEFF`.
140/// It introduced the basic security features and mechanisms for securing datagram communication.
141const DTLS_1_0: u16 = 0xFEFF;
142
143/// DTLS version 1.2 identifier.
144///
145/// Defined in RFC 6347, this version enhances the security features introduced in DTLS 1.0 and aligns
146/// more closely with TLS version 1.2, introducing stronger cryptographic algorithms and security practices.
147const DTLS_1_2: u16 = 0xFEFD;
148
149/// DTLS version 1.3 identifier.
150///
151/// Although DTLS 1.3 is conceptually aligned with TLS 1.3, it is still in the process of being
152/// standardized. The version number `0xFEFC` is used here as a placeholder and should be verified
153/// against the latest standards and RFCs related to DTLS 1.3.
154///
155/// DTLS 1.3 aims to further improve the security and efficiency of DTLS by adopting modern cryptographic
156/// algorithms, reducing handshake latency, and improving resistance against various attack vectors.
157const DTLS_1_3: u16 = 0xFEFC;
158
159/// The length of the DTLS record header in bytes.
160///
161/// In the DTLS protocol, each record transmitted or received is prefixed with a header. This header
162/// contains critical information for processing the record, including the protocol version, epoch, sequence number,
163/// length, and more.
164///
165/// The constant value of `13` bytes is composed of the following parts of the DTLS record header:
166/// - 1 byte for the "Content Type"
167/// - 2 bytes for the "Version"
168/// - 2 bytes for the "Epoch"
169/// - 6 bytes for the "Sequence Number"
170/// - 2 bytes for the "Length"
171///
172/// This header length is essential for the correct parsing and handling of DTLS records.
173const DTLS_RECORD_HEADER_LENGTH: u16 = 13;
174
175/// Determines whether a byte slice contains encrypted data at a specified offset.
176///
177/// This function leverages `get_encrypted_packet_length` to analyze the provided byte slice, starting
178/// from the given offset, to assess if it contains an encrypted packet according to recognized SSL/TLS,
179/// GMSSL, and DTLS protocols. It effectively serves as a boolean helper to quickly identify encrypted data
180/// without needing to parse or understand the specifics of the encryption protocol.
181///
182/// # Arguments
183///
184/// * `buffer` - A slice of bytes representing the buffer to be analyzed for encrypted data.
185/// * `offset` - The offset within `buffer` from which to start the analysis.
186///
187/// # Returns
188///
189/// `true` if the data at the specified offset is recognized as an encrypted packet, otherwise `false`.
190/// The determination is based on whether `get_encrypted_packet_length` returns a packet length greater than 0
191/// or if it encounters an error (e.g., `NotEnoughDataError` or `NotEncryptedError`), in which case it will
192/// return `false`.
193pub async fn is_encrypted<'a, R>(reader: &mut R, offset: usize) -> bool
194where
195    R: Read<'a>,
196{
197    match get_encrypted_packet_length(reader, offset).await {
198        Ok(length) => length > 0,
199        Err(_) => false,
200    }
201}
202
203/// Calculates the length of an encrypted data packet within a byte slice, without consuming the bytes.
204///
205/// This function analyzes the provided byte slice, starting from a specified offset, to determine
206/// the length of the encrypted packet based on the encryption protocol's header format. It supports
207/// various SSL/TLS versions, including SSLv3, TLS (all versions up to TLS 1.3), GMSSL, and DTLS protocols.
208/// It can distinguish between SSLv2 and other protocol versions, handling each according to its
209/// specific header and length encoding scheme.
210///
211/// The function returns an `Ok(u16)` with the total length of the encrypted packet (including its header)
212/// if successful. If the byte slice does not contain enough data to determine the packet length or
213/// if the data does not appear to be encrypted according to the recognized protocols, it returns
214/// an `Err(Error)`, with `Error` being a custom enum indicating the type of error encountered
215/// (`NotEnoughDataError` or `NotEncryptedError`).
216///
217/// # Arguments
218///
219/// * `buffer` - A slice of bytes representing the buffer from which to read the encrypted packet.
220/// * `offset` - The offset within `buffer` from which to start analyzing the encrypted data.
221///
222/// # Returns
223///
224/// A `Result<u16, Error>` indicating the outcome of the function. On success, it contains the length
225/// of the encrypted packet. On failure, it contains an `Error` enum indicating the reason for the failure.
226///
227/// # Errors
228///
229/// * `NotEnoughDataError` - If the buffer does not contain enough data to determine the packet length.
230/// * `NotEncryptedError` - If the data does not appear to be encrypted according to recognized protocols.
231///
232pub async fn get_encrypted_packet_length<'a, R>(reader: &mut R, offset: usize) -> Result<u16, Error>
233where
234    R: Read<'a>,
235{
236    let mut packet_length: u16 = 0;
237
238    // SSLv3 or TLS - Check ContentType
239    let mut tls = match reader.read_byte(offset).await? {
240        SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC
241        | SSL_CONTENT_TYPE_ALERT
242        | SSL_CONTENT_TYPE_HANDSHAKE
243        | SSL_CONTENT_TYPE_APPLICATION_DATA
244        | SSL_CONTENT_TYPE_EXTENSION_HEARTBEAT => true,
245        _ => false,
246    };
247
248    if tls {
249        // SSLv3 or TLS or GMSSLv1.0 or GMSSLv1.1 - Check ProtocolVersion
250
251        // TLS 1.0 (RFC 2246) is represented by the major version 3 and minor version 1, which corresponds to the byte sequence {3, 1}.
252        // TLS 1.1 (RFC 4346) uses the version represented by the bytes {3, 2}.
253        // TLS 1.2 (RFC 5246) has the version indicated by {3, 3}.
254        // TLS 1.3 (RFC 8446) is represented by {3, 4}
255        // SSL 3.0 is represented as {3, 4} (but this is SSL, not TLS).
256        let major_version = reader.read_byte(offset + 1).await?;
257        let version = reader.read_u16_from_be(offset + 1).await?;
258
259        if major_version == 3 || version == GMSSL_PROTOCOL_VERSION {
260            // SSLv3 or TLS or GMSSLv1.0 or GMSSLv1.1
261            packet_length = reader.read_u16_from_be(offset + 3).await? + SSL_RECORD_HEADER_LENGTH;
262            if packet_length <= SSL_RECORD_HEADER_LENGTH {
263                // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
264                tls = false;
265            }
266        } else if version == DTLS_1_0 || version == DTLS_1_2 || version == DTLS_1_3 {
267            let packet_length_offset = offset + DTLS_RECORD_HEADER_LENGTH as usize;
268            packet_length = reader.read_u16_from_be(packet_length_offset - 2).await?
269                + DTLS_RECORD_HEADER_LENGTH;
270        } else {
271            // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
272            tls = false;
273        }
274    }
275
276    if !tls {
277        // SSLv2 or bad data - Check the version
278        let header_length = if (reader.read_byte(offset).await? & 0x80) != 0 {
279            2
280        } else {
281            3
282        };
283
284        let major_version = reader.read_byte(offset + header_length + 1).await?;
285        if major_version == 2 || major_version == 3 {
286            // SSLv2
287            packet_length = if header_length == 2 {
288                (reader.read_u16_from_be(offset).await? & 0x7FFF) + 2
289            } else {
290                (reader.read_u16_from_be(offset).await? & 0x3FFF) + 3
291            };
292            if packet_length as usize <= header_length {
293                return Err(Error::NotEnoughDataError);
294            }
295        } else {
296            return Err(Error::NotEncryptedError);
297        }
298    }
299
300    Ok(packet_length)
301}