rama_net/tls/client/
mod.rs

1//! TLS implementation agnostic client types
2//!
3//! [`ClientHello`] is used in Rama as the implementation agnostic type
4//! to convey what client hello was set by the incoming TLS Connection,
5//! if the server middleware is configured to store it.
6//!
7//! By being implementation agnostic we have the advantage to be able to bridge
8//! easily between different implementations. Making it possible to run for example
9//! a Rustls proxy service but establish connections using BoringSSL.
10
11mod hello;
12#[doc(inline)]
13pub use hello::{ClientHello, ClientHelloExtension, ECHClientHello};
14
15mod parser;
16pub use parser::parse_client_hello;
17
18mod config;
19#[doc(inline)]
20pub use config::{
21    ClientAuth, ClientAuthData, ClientConfig, ClientConfigChain, ProxyClientConfig,
22    ServerVerifyMode, append_all_client_configs_to_ctx, append_client_config_to_ctx,
23    extract_client_config_from_ctx,
24};
25
26use super::{ApplicationProtocol, DataEncoding, ProtocolVersion};
27
28#[derive(Debug, Clone)]
29/// Indicate (some) of the negotiated tls parameters that
30/// can be added to the service context by Tls implementations.
31pub struct NegotiatedTlsParameters {
32    /// The used [`ProtocolVersion`].
33    ///
34    /// e.g. [`ProtocolVersion::TLSv1_3`]
35    pub protocol_version: ProtocolVersion,
36    /// Indicates the agreed upon [`ApplicationProtocol`]
37    /// in case the tls implementation can surfice this
38    /// AND there is such a protocol negotiated and agreed upon.
39    ///
40    /// e.g. [`ApplicationProtocol::HTTP_2`]
41    pub application_layer_protocol: Option<ApplicationProtocol>,
42    /// Certificate chain provided the peer (only stored if config requested this)
43    pub peer_certificate_chain: Option<DataEncoding>,
44}
45
46/// Merge extension lists A and B, with
47/// B overwriting any conflict with A, and otherwise push it to the back.
48pub fn merge_client_hello_lists(
49    a: impl AsRef<[ClientHelloExtension]>,
50    b: impl AsRef<[ClientHelloExtension]>,
51) -> Vec<ClientHelloExtension> {
52    let a = a.as_ref();
53    let b = b.as_ref();
54
55    let mut output = Vec::with_capacity(a.len() + b.len());
56
57    output.extend(a.iter().cloned());
58
59    for ext in b.iter().cloned() {
60        match output.iter_mut().find(|e| e.id() == ext.id()) {
61            Some(old) => {
62                *old = ext;
63            }
64            None => output.push(ext),
65        }
66    }
67
68    output
69}
70
71#[cfg(test)]
72mod tests {
73    use crate::address::{Domain, Host};
74
75    use super::*;
76
77    #[test]
78    fn test_merge_client_hello_lists_empty() {
79        assert!(merge_client_hello_lists(vec![], vec![]).is_empty());
80    }
81
82    #[test]
83    fn test_merge_client_hello_lists_zero_one() {
84        let output = merge_client_hello_lists(&[], [ClientHelloExtension::ServerName(None)]);
85        assert_eq!(1, output.len());
86        assert!(matches!(output[0], ClientHelloExtension::ServerName(_)))
87    }
88
89    #[test]
90    fn test_merge_client_hello_lists_one_zero() {
91        let output = merge_client_hello_lists(vec![ClientHelloExtension::ServerName(None)], &[]);
92        assert_eq!(1, output.len());
93        assert!(matches!(output[0], ClientHelloExtension::ServerName(_)))
94    }
95
96    #[test]
97    fn test_merge_client_hello_lists_one_one() {
98        let output = merge_client_hello_lists(
99            vec![ClientHelloExtension::ServerName(None)],
100            &[ClientHelloExtension::SupportedVersions(vec![])],
101        );
102        assert_eq!(2, output.len());
103        assert!(matches!(output[0], ClientHelloExtension::ServerName(_)));
104        assert!(matches!(
105            output[1],
106            ClientHelloExtension::SupportedVersions(_)
107        ));
108    }
109
110    #[test]
111    fn test_merge_client_hello_lists_two_two_with_one_conflict() {
112        let output = merge_client_hello_lists(
113            vec![
114                ClientHelloExtension::ServerName(None),
115                ClientHelloExtension::SupportedVersions(vec![]),
116            ],
117            &[
118                ClientHelloExtension::ServerName(Some(Host::Name(Domain::from_static(
119                    "example.com",
120                )))),
121                ClientHelloExtension::ApplicationLayerProtocolNegotiation(vec![]),
122            ],
123        );
124        assert_eq!(3, output.len());
125        assert!(matches!(output[0], ClientHelloExtension::ServerName(_)));
126        assert!(matches!(
127            output[1],
128            ClientHelloExtension::SupportedVersions(_)
129        ));
130        assert!(matches!(
131            output[2],
132            ClientHelloExtension::ApplicationLayerProtocolNegotiation(_)
133        ));
134    }
135}