1use std::{future::Future, io, net::SocketAddr, os::unix::io::AsRawFd};
4
5use rustls::SupportedCipherSuite;
6use smallvec::SmallVec;
7use tokio::net::{TcpListener, TcpStream};
8
9use crate::{
10 cipher::{cipher_suite, KtlsCipherSuite, KtlsCipherType},
11 version::KtlsVersion,
12 CryptoInfo, Error,
13};
14
15#[derive(Debug, Default, Clone, Copy)]
16pub struct CompatibleCiphers {
18 pub tls12: CompatibleCiphersForVersion,
20
21 pub tls13: CompatibleCiphersForVersion,
23}
24
25impl CompatibleCiphers {
26 pub async fn new() -> io::Result<Self> {
29 let mut ciphers = CompatibleCiphers::default();
30
31 let ln = TcpListener::bind("0.0.0.0:0").await?;
32 let local_addr = ln.local_addr()?;
33
34 let mut accepted_conns: SmallVec<[TcpStream; 12]> = SmallVec::new();
36
37 let accept_conns_fut = async {
38 loop {
39 if let Ok((conn, _addr)) = ln.accept().await {
40 accepted_conns.push(conn);
41 }
42 }
43 };
44
45 ciphers.test_ciphers(local_addr, accept_conns_fut).await?;
46
47 Ok(ciphers)
48 }
49
50 async fn test_ciphers(
51 &mut self,
52 local_addr: SocketAddr,
53 accept_conns_fut: impl Future<Output = ()>,
54 ) -> io::Result<()> {
55 let ciphers: Vec<(SupportedCipherSuite, &mut bool)> = vec![
56 (
57 cipher_suite::TLS13_AES_128_GCM_SHA256,
58 &mut self.tls13.aes_gcm_128,
59 ),
60 (
61 cipher_suite::TLS13_AES_256_GCM_SHA384,
62 &mut self.tls13.aes_gcm_256,
63 ),
64 (
65 cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
66 &mut self.tls13.chacha20_poly1305,
67 ),
68 (
69 cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
70 &mut self.tls12.aes_gcm_128,
71 ),
72 (
73 cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
74 &mut self.tls12.aes_gcm_256,
75 ),
76 (
77 cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
78 &mut self.tls12.chacha20_poly1305,
79 ),
80 ];
81
82 let create_connections_fut = futures_util::future::try_join_all(
83 (0..ciphers.len()).map(|_| TcpStream::connect(local_addr)),
84 );
85
86 let socks = tokio::select! {
87 biased;
101
102 res = create_connections_fut => res?,
103 _ = accept_conns_fut => unreachable!(),
104 };
105
106 assert_eq!(ciphers.len(), socks.len());
107
108 ciphers
109 .into_iter()
110 .zip(socks)
111 .for_each(|((cipher_suite, field), sock)| {
112 *field = sample_cipher_setup(&sock, cipher_suite).is_ok();
113 });
114
115 Ok(())
116 }
117
118 pub fn is_compatible(&self, suite: SupportedCipherSuite) -> bool {
121 let kcs = match KtlsCipherSuite::try_from(suite) {
122 Ok(kcs) => kcs,
123 Err(_) => return false,
124 };
125
126 let fields = match kcs.version {
127 KtlsVersion::TLS12 => &self.tls12,
128 KtlsVersion::TLS13 => &self.tls13,
129 };
130
131 match kcs.typ {
132 KtlsCipherType::AesGcm128 => fields.aes_gcm_128,
133 KtlsCipherType::AesGcm256 => fields.aes_gcm_256,
134 KtlsCipherType::Chacha20Poly1305 => fields.chacha20_poly1305,
135 }
136 }
137}
138
139#[derive(Debug, Default, Clone, Copy)]
140pub struct CompatibleCiphersForVersion {
141 pub aes_gcm_128: bool,
142 pub aes_gcm_256: bool,
143 pub chacha20_poly1305: bool,
144}
145
146fn sample_cipher_setup(sock: &TcpStream, cipher_suite: SupportedCipherSuite) -> Result<(), Error> {
147 let kcs = match KtlsCipherSuite::try_from(cipher_suite) {
148 Ok(kcs) => kcs,
149 Err(_) => panic!("unsupported cipher suite"),
150 };
151
152 let ffi_version = match kcs.version {
153 KtlsVersion::TLS12 => crate::ffi::TLS_1_2_VERSION_NUMBER,
154 KtlsVersion::TLS13 => crate::ffi::TLS_1_3_VERSION_NUMBER,
155 };
156
157 let crypto_info = match kcs.typ {
158 KtlsCipherType::AesGcm128 => {
159 CryptoInfo::AesGcm128(crate::ffi::bindings::tls12_crypto_info_aes_gcm_128 {
160 info: crate::ffi::bindings::tls_crypto_info {
161 version: ffi_version,
162 cipher_type: crate::ffi::bindings::TLS_CIPHER_AES_GCM_128 as _,
163 },
164 iv: Default::default(),
165 key: Default::default(),
166 salt: Default::default(),
167 rec_seq: Default::default(),
168 })
169 }
170 KtlsCipherType::AesGcm256 => {
171 CryptoInfo::AesGcm256(crate::ffi::bindings::tls12_crypto_info_aes_gcm_256 {
172 info: crate::ffi::bindings::tls_crypto_info {
173 version: ffi_version,
174 cipher_type: crate::ffi::bindings::TLS_CIPHER_AES_GCM_256 as _,
175 },
176 iv: Default::default(),
177 key: Default::default(),
178 salt: Default::default(),
179 rec_seq: Default::default(),
180 })
181 }
182 KtlsCipherType::Chacha20Poly1305 => CryptoInfo::Chacha20Poly1305(
183 crate::ffi::bindings::tls12_crypto_info_chacha20_poly1305 {
184 info: crate::ffi::bindings::tls_crypto_info {
185 version: ffi_version,
186 cipher_type: crate::ffi::bindings::TLS_CIPHER_CHACHA20_POLY1305 as _,
187 },
188 iv: Default::default(),
189 key: Default::default(),
190 salt: Default::default(),
191 rec_seq: Default::default(),
192 },
193 ),
194 };
195 let fd = sock.as_raw_fd();
196
197 crate::ffi::setup_ulp(fd).map_err(Error::UlpError)?;
198
199 crate::ffi::setup_tls_info(fd, crate::ffi::Direction::Tx, crypto_info)?;
200
201 Ok(())
202}