Skip to main content

klukai_agent/
quinn_plaintext.rs

1use std::{
2    hash::{Hash, Hasher},
3    sync::Arc,
4};
5
6use bytes::{Buf, BytesMut};
7
8use quinn_proto::{
9    ConnectionId, Side, TransportError,
10    crypto::{self, CryptoError, HeaderKey},
11    transport_parameters,
12};
13use seahash::SeaHasher;
14use tracing::{error, trace};
15
16/// Sets up a basic [`quinn::ServerConfig`] for use with plaintext cryptography.
17///
18/// # Examples
19///
20/// ```
21/// let server = quinn::Endpoint::server(quinn_plaintext::server_config(), "[::]:0".parse()?)?;
22/// ```
23pub fn server_config() -> quinn_proto::ServerConfig {
24    quinn_proto::ServerConfig::with_crypto(Arc::new(PlaintextServerConfig::new()))
25}
26
27/// Sets up a basic [`quinn::ClientConfig`] for use with plaintext cryptography.
28///
29/// # Examples
30///
31/// ```
32/// let mut client = quinn::Endpoint::client("[::]:0".parse()?)?;
33/// client.set_default_client_config(quinn_plaintext::client_config());
34/// ```
35pub fn client_config() -> quinn_proto::ClientConfig {
36    quinn_proto::ClientConfig::new(Arc::new(PlaintextClientConfig::new()))
37}
38
39pub struct PlaintextHeaderKey {
40    side: Side,
41}
42
43impl PlaintextHeaderKey {
44    pub fn new(side: Side) -> Self {
45        Self { side }
46    }
47}
48
49impl HeaderKey for PlaintextHeaderKey {
50    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]) {
51        trace!(side = ?self.side, "HeaderKey::decrypt pn_offset: {pn_offset}");
52        trace!(side = ?self.side, "packet: {packet:?}");
53        // do nothing
54    }
55
56    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]) {
57        trace!(side = ?self.side, "HeaderKey::encrypt pn_offset: {pn_offset}");
58        trace!(side = ?self.side, "packet: {packet:?}");
59        // do nothing
60    }
61
62    fn sample_size(&self) -> usize {
63        trace!(side = ?self.side, "HeaderKey::sample_size");
64        0
65    }
66}
67
68pub struct PlaintextPacketKey {
69    side: Side,
70}
71
72impl PlaintextPacketKey {
73    fn new(side: Side) -> Self {
74        Self { side }
75    }
76}
77
78#[derive(Default)]
79pub struct PlaintextClientConfig;
80
81impl PlaintextClientConfig {
82    pub fn new() -> Self {
83        Self
84    }
85}
86
87#[derive(Default)]
88pub struct PlaintextServerConfig;
89
90impl PlaintextServerConfig {
91    pub fn new() -> Self {
92        Self
93    }
94}
95
96fn crypto_keys(side: Side) -> crypto::Keys {
97    crypto::Keys {
98        header: crypto_header_keypair(side),
99        packet: crypto_packet_keypair(side),
100    }
101}
102
103fn crypto_header_keypair(side: Side) -> crypto::KeyPair<Box<dyn crypto::HeaderKey>> {
104    crypto::KeyPair {
105        local: Box::new(PlaintextHeaderKey::new(side)),
106        remote: Box::new(PlaintextHeaderKey::new(side)),
107    }
108}
109
110fn crypto_packet_keypair(side: Side) -> crypto::KeyPair<Box<dyn crypto::PacketKey>> {
111    crypto::KeyPair {
112        local: Box::new(PlaintextPacketKey::new(side)),
113        remote: Box::new(PlaintextPacketKey::new(side)),
114    }
115}
116
117/// A plaintext session which does not perform packet encryption/decryption
118pub struct PlaintextSession {
119    side: Side,
120    params: transport_parameters::TransportParameters,
121    peer_params: Option<transport_parameters::TransportParameters>,
122    wrote_transporter_params: bool,
123    initial_keys: Option<crypto::Keys>,
124    handshake_keys: Option<crypto::Keys>,
125}
126
127impl PlaintextSession {
128    fn new(side: Side, params: transport_parameters::TransportParameters) -> Self {
129        Self {
130            side,
131            params,
132            peer_params: None,
133            wrote_transporter_params: false,
134            initial_keys: Some(crypto_keys(side)),
135            handshake_keys: Some(crypto_keys(side)),
136        }
137    }
138}
139
140// forward all calls to inner except those related to packet encryption/decryption
141impl crypto::Session for PlaintextSession {
142    fn initial_keys(&self, dst_cid: &ConnectionId, _side: Side) -> crypto::Keys {
143        trace!(side = ?self.side, "initial_keys dst_cid: {dst_cid}");
144        crypto_keys(self.side)
145    }
146
147    fn handshake_data(&self) -> Option<Box<dyn std::any::Any>> {
148        trace!(side = ?self.side, "handshake_data");
149        self.peer_params
150            .map(|tp| Box::new(tp) as Box<dyn std::any::Any>)
151    }
152
153    fn peer_identity(&self) -> Option<Box<dyn std::any::Any>> {
154        trace!(side = ?self.side, "peer_identity");
155        None
156    }
157
158    fn early_crypto(&self) -> Option<(Box<dyn crypto::HeaderKey>, Box<dyn crypto::PacketKey>)> {
159        trace!(side = ?self.side, "early_crypto");
160        None
161    }
162
163    fn early_data_accepted(&self) -> Option<bool> {
164        trace!(side = ?self.side, "early_data_accepted");
165        Some(false)
166    }
167
168    fn is_handshaking(&self) -> bool {
169        trace!(side = ?self.side, "is_handshaking");
170        self.peer_params.is_none()
171            || !self.wrote_transporter_params
172                && (self.initial_keys.is_some() || self.handshake_keys.is_some())
173    }
174
175    fn read_handshake(&mut self, mut buf: &[u8]) -> Result<bool, TransportError> {
176        trace!(side = ?self.side, "read_handshake {buf:?}");
177
178        if self.peer_params.is_none() {
179            self.peer_params = Some(
180                transport_parameters::TransportParameters::read(self.side, &mut buf)
181                    .expect("could not read shit"),
182            );
183        }
184        Ok(true)
185    }
186
187    fn transport_parameters(
188        &self,
189    ) -> Result<Option<transport_parameters::TransportParameters>, TransportError> {
190        trace!(side = ?self.side, "transport_parameters");
191        Ok(self.peer_params)
192    }
193
194    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<crypto::Keys> {
195        if self.side.is_client() && !self.wrote_transporter_params {
196            self.params.write(buf);
197            self.wrote_transporter_params = true;
198            trace!("wrote data: {buf:?}");
199        }
200
201        trace!(side = ?self.side, "write_handshake");
202
203        match self.initial_keys.take().or_else(|| {
204            self.handshake_keys.take().inspect(|_k| {
205                if self.side.is_server() && !self.wrote_transporter_params {
206                    self.params.write(buf);
207                    self.wrote_transporter_params = true;
208                    trace!("wrote data: {buf:?}");
209                }
210                trace!("taking handshake keys");
211            })
212        }) {
213            Some(k) => Some(k),
214            None => {
215                trace!("returning None");
216                None
217            }
218        }
219    }
220
221    fn next_1rtt_keys(&mut self) -> Option<crypto::KeyPair<Box<dyn crypto::PacketKey>>> {
222        trace!(side = ?self.side, "next_1rtt_keys");
223        Some(crypto_packet_keypair(self.side))
224    }
225
226    fn is_valid_retry(&self, orig_dst_cid: &ConnectionId, _header: &[u8], _payload: &[u8]) -> bool {
227        trace!(side = ?self.side, "is_valid_retry orig_dst_cid: {orig_dst_cid}");
228        true
229    }
230
231    fn export_keying_material(
232        &self,
233        _output: &mut [u8],
234        _label: &[u8],
235        _context: &[u8],
236    ) -> Result<(), crypto::ExportKeyingMaterialError> {
237        trace!(side = ?self.side, "export_keying_material");
238        Ok(())
239    }
240}
241
242impl crypto::ClientConfig for PlaintextClientConfig {
243    fn start_session(
244        self: std::sync::Arc<Self>,
245        version: u32,
246        server_name: &str,
247        params: &transport_parameters::TransportParameters,
248    ) -> Result<Box<dyn crypto::Session>, quinn_proto::ConnectError> {
249        trace!(
250            "ClientConfig::start_session version: {version}, server_name: {server_name}, params: {params:?}"
251        );
252        Ok(Box::new(PlaintextSession::new(Side::Client, *params)))
253    }
254}
255
256impl crypto::ServerConfig for PlaintextServerConfig {
257    fn initial_keys(
258        &self,
259        version: u32,
260        dst_cid: &ConnectionId,
261    ) -> Result<crypto::Keys, crypto::UnsupportedVersion> {
262        trace!("ServerConfig::initial_keys version: {version}, dst_cid: {dst_cid:?}");
263        Ok(crypto_keys(Side::Server))
264    }
265
266    fn retry_tag(&self, version: u32, orig_dst_cid: &ConnectionId, packet: &[u8]) -> [u8; 16] {
267        trace!(
268            "ServerConfig::retry_tag version: {version}, orig_dst_cid: {orig_dst_cid:?}, packet: {packet:?}"
269        );
270
271        [0u8; 16]
272    }
273
274    fn start_session(
275        self: Arc<Self>,
276        version: u32,
277        params: &transport_parameters::TransportParameters,
278    ) -> Box<dyn crypto::Session> {
279        trace!("ServerConfig::start_session version: {version}, params: {params:?}");
280        Box::new(PlaintextSession::new(Side::Server, *params))
281    }
282}
283
284// forward all calls to inner except those related to packet encryption/decryption
285impl crypto::PacketKey for PlaintextPacketKey {
286    fn encrypt(&self, packet: u64, buf: &mut [u8], header_len: usize) {
287        trace!(side = ?self.side, "PacketKey::encrypt packet: {packet}, header_len: {header_len}");
288        let (header, payload_tag) = buf.split_at_mut(header_len);
289        trace!(side = ?self.side, "header: {header:?}");
290        trace!(side = ?self.side, "payload_tag: {payload_tag:?}");
291        let (payload, tag_storage) = payload_tag.split_at_mut(payload_tag.len() - self.tag_len());
292        trace!("tag_storage: {tag_storage:?}");
293        let mut hasher = SeaHasher::default();
294        header.hash(&mut hasher);
295        payload.hash(&mut hasher);
296        let checksum = hasher.finish();
297        trace!("checksum: {checksum:?}");
298        tag_storage.copy_from_slice(&checksum.to_be_bytes());
299        trace!("tag_storage (after put): {tag_storage:?}");
300        // do nothing
301    }
302
303    fn decrypt(
304        &self,
305        packet: u64,
306        header: &[u8],
307        payload: &mut BytesMut,
308    ) -> Result<(), CryptoError> {
309        trace!(side = ?self.side, "PacketKey::decrypt packet: {packet}, header: {header:?}");
310
311        let mut tag_storage = payload.split_off(payload.len() - self.tag_len());
312
313        trace!(side = ?self.side, "payload: {:?}", payload.as_ref());
314        trace!(side = ?self.side, "tag_storage: {:?}", tag_storage.as_ref());
315
316        let mut hasher = SeaHasher::default();
317        header.hash(&mut hasher);
318        payload.hash(&mut hasher);
319        let checksum = hasher.finish();
320
321        let expected = tag_storage.get_u64();
322        if checksum != expected {
323            error!(side = ?self.side, "checksum mismatch, expected {expected}, got: {checksum}");
324            return Err(CryptoError);
325        }
326
327        // do nothing
328        Ok(())
329    }
330
331    fn tag_len(&self) -> usize {
332        trace!(side = ?self.side, "PacketKey::tag_len");
333        8
334    }
335
336    fn confidentiality_limit(&self) -> u64 {
337        trace!(side = ?self.side, "PacketKey::confidentiality_limit");
338        u64::MAX
339    }
340
341    fn integrity_limit(&self) -> u64 {
342        trace!(side = ?self.side, "PacketKey::integrity_limit");
343        1 << 36
344    }
345}
346
347#[cfg(test)]
348mod tests {
349    use super::*;
350
351    #[tokio::test]
352    async fn basic_test() {
353        _ = tracing_subscriber::fmt::try_init();
354
355        let server_config =
356            quinn::ServerConfig::with_crypto(Arc::new(PlaintextServerConfig::new()));
357        let server = quinn::Endpoint::server(server_config, "127.0.0.1:0".parse().unwrap())
358            .expect("could not create endpoint server");
359
360        let addr = server
361            .local_addr()
362            .expect("could not get server local addr");
363
364        let test_data = b"hello world";
365
366        let server_fut = async move {
367            println!("server waiting to accept...");
368            let incoming = server.accept().await.expect("did not accept a conn!");
369            println!("accepted: {incoming:?}");
370            let connecting = incoming
371                .accept()
372                .expect("could not accept incoming connection");
373            let conn = connecting
374                .await
375                .expect("could not complete connection accept");
376
377            println!("completed accept: {conn:?}");
378
379            let mut recv = conn
380                .accept_uni()
381                .await
382                .expect("could not accept uni stream");
383
384            println!("ACCEPTED UNI STREAM");
385
386            let mut b = vec![0u8; test_data.len()];
387            recv.read_exact(&mut b)
388                .await
389                .expect("could not test string");
390
391            b
392        };
393
394        let client_fut = async move {
395            let client_config = quinn::ClientConfig::new(Arc::new(PlaintextClientConfig::new()));
396            let client = quinn::Endpoint::client("127.0.0.1:0".parse().unwrap())
397                .expect("could not create endpoint client");
398
399            let conn = client
400                .connect_with(client_config, addr, "plaintext.test")
401                .expect("could not create connection future")
402                .await
403                .expect("could not connect to server");
404
405            println!("OPENING UNI STREAM");
406
407            let mut send = conn.open_uni().await.unwrap();
408
409            println!("opened a unidirectional stream");
410
411            send.write_all(b"hello world").await.unwrap();
412
413            // Give the server a moment to accept the stream before closing
414            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
415
416            send.finish().unwrap();
417        };
418
419        let (buf, _) = tokio::join!(server_fut, client_fut);
420
421        assert_eq!(buf, test_data);
422    }
423}