1#[cfg(not(feature = "std"))]
29use alloc::format;
30
31pub mod framing;
32
33use crate::errors::CoreError;
34use async_lock::Mutex;
35use bytes::Bytes;
36use embedded_io_async::{Error, Read, Write};
37
38pub struct EmbeddedLeg<R, W, const N: usize> {
42 rx: Mutex<(R, [u8; N])>,
43 tx: Mutex<W>,
44}
45
46impl<R, W, const N: usize> EmbeddedLeg<R, W, N> {
47 pub fn new(reader: R, writer: W) -> Self {
51 Self {
52 rx: Mutex::new((reader, [0u8; N])),
53 tx: Mutex::new(writer),
54 }
55 }
56
57 pub fn into_inner(self) -> (R, W) {
59 let (r, _buf) = self.rx.into_inner();
60 let w = self.tx.into_inner();
61 (r, w)
62 }
63}
64
65impl<R, W, const N: usize> EmbeddedLeg<R, W, N>
66where
67 R: Read,
68 W: Write,
69{
70 pub async fn send_frame(&self, data: &[u8]) -> Result<(), CoreError> {
74 let header = framing::encode_header(data.len(), N)
75 .map_err(|e| CoreError::NetworkError(format!("framing: {:?}", e)))?;
76 let mut w = self.tx.lock().await;
77 w.write_all(&header)
78 .await
79 .map_err(|e| CoreError::NetworkError(format!("write header: {:?}", e.kind())))?;
80 w.write_all(data)
81 .await
82 .map_err(|e| CoreError::NetworkError(format!("write payload: {:?}", e.kind())))?;
83 w.flush()
84 .await
85 .map_err(|e| CoreError::NetworkError(format!("flush: {:?}", e.kind())))?;
86 Ok(())
87 }
88
89 pub async fn recv_frame(&self) -> Result<Bytes, CoreError> {
93 let mut header = [0u8; framing::HEADER_LEN];
94 let mut guard = self.rx.lock().await;
95 let (r, buf) = &mut *guard;
96 r.read_exact(&mut header)
97 .await
98 .map_err(|_| CoreError::NetworkError("read header".into()))?;
99 let len = framing::decode_header(&header, N)
100 .map_err(|e| CoreError::NetworkError(format!("framing: {:?}", e)))?;
101 r.read_exact(&mut buf[..len])
102 .await
103 .map_err(|_| CoreError::NetworkError("read payload".into()))?;
104 Ok(Bytes::copy_from_slice(&buf[..len]))
105 }
106}
107
108#[macro_export]
129macro_rules! impl_embedded_session_transport {
130 ($reader:ty, $writer:ty, $n:expr) => {
131 impl $crate::transport::session_transport::SessionTransport
132 for $crate::transport::legs::embedded::EmbeddedLeg<$reader, $writer, $n>
133 {
134 fn send_bytes(
135 &self,
136 data: &[u8],
137 ) -> impl core::future::Future<Output = Result<(), $crate::errors::CoreError>> + Send
138 {
139 self.send_frame(data)
140 }
141 fn recv_bytes(
142 &self,
143 ) -> impl core::future::Future<
144 Output = Result<bytes::Bytes, $crate::errors::CoreError>,
145 > + Send {
146 self.recv_frame()
147 }
148 }
149 };
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use crate::transport::session_transport::SessionTransport;
156 use core::convert::Infallible;
157 use std::collections::VecDeque;
158 use std::sync::Arc;
159 use std::time::Duration;
160 use tokio::sync::{Mutex as TokioMutex, Notify};
161
162 crate::impl_embedded_session_transport!(MockReader, MockWriter, 1024);
167
168 struct Pipe {
175 buf: VecDeque<u8>,
176 closed: bool,
177 }
178
179 struct MockReader {
180 read_from: Arc<TokioMutex<Pipe>>,
181 read_notify: Arc<Notify>,
182 max_read: usize,
183 }
184
185 struct MockWriter {
186 write_to: Arc<TokioMutex<Pipe>>,
187 write_notify: Arc<Notify>,
188 }
189
190 fn duplex_pair() -> ((MockReader, MockWriter), (MockReader, MockWriter)) {
191 duplex_pair_with_chunk(usize::MAX)
192 }
193
194 fn duplex_pair_with_chunk(
195 max_read: usize,
196 ) -> ((MockReader, MockWriter), (MockReader, MockWriter)) {
197 let ab = Arc::new(TokioMutex::new(Pipe {
198 buf: VecDeque::new(),
199 closed: false,
200 }));
201 let ba = Arc::new(TokioMutex::new(Pipe {
202 buf: VecDeque::new(),
203 closed: false,
204 }));
205 let n_ab = Arc::new(Notify::new());
206 let n_ba = Arc::new(Notify::new());
207 let a = (
208 MockReader {
209 read_from: ba.clone(),
210 read_notify: n_ba.clone(),
211 max_read,
212 },
213 MockWriter {
214 write_to: ab.clone(),
215 write_notify: n_ab.clone(),
216 },
217 );
218 let b = (
219 MockReader {
220 read_from: ab,
221 read_notify: n_ab,
222 max_read,
223 },
224 MockWriter {
225 write_to: ba,
226 write_notify: n_ba,
227 },
228 );
229 (a, b)
230 }
231
232 impl embedded_io_async::ErrorType for MockReader {
233 type Error = Infallible;
234 }
235 impl embedded_io_async::ErrorType for MockWriter {
236 type Error = Infallible;
237 }
238
239 impl Read for MockReader {
240 async fn read(&mut self, out: &mut [u8]) -> Result<usize, Infallible> {
241 if out.is_empty() {
242 return Ok(0);
243 }
244 loop {
245 let notified = self.read_notify.notified();
248 tokio::pin!(notified);
249 notified.as_mut().enable();
250 {
251 let mut p = self.read_from.lock().await;
252 if !p.buf.is_empty() {
253 let n = out.len().min(p.buf.len()).min(self.max_read);
254 for slot in out.iter_mut().take(n) {
255 *slot = p.buf.pop_front().expect("checked non-empty");
256 }
257 return Ok(n);
258 }
259 if p.closed {
260 return Ok(0);
261 }
262 }
263 notified.await;
264 }
265 }
266 }
267
268 impl Write for MockWriter {
269 async fn write(&mut self, data: &[u8]) -> Result<usize, Infallible> {
270 let mut p = self.write_to.lock().await;
271 p.buf.extend(data.iter().copied());
272 drop(p);
273 self.write_notify.notify_waiters();
274 Ok(data.len())
275 }
276 }
278
279 #[tokio::test]
284 async fn send_frame_writes_length_prefixed_payload() {
285 let ((a_r, a_w), (mut b_r, _b_w)) = duplex_pair();
286 let leg: EmbeddedLeg<MockReader, MockWriter, 1024> = EmbeddedLeg::new(a_r, a_w);
287
288 leg.send_frame(b"hello").await.expect("send_frame");
289
290 let mut buf = vec![0u8; 4 + 5];
291 tokio::time::timeout(Duration::from_secs(1), b_r.read_exact(&mut buf))
292 .await
293 .expect("peer read should not hang")
294 .expect("peer read_exact");
295
296 assert_eq!(&buf[..4], &[0x00, 0x00, 0x00, 0x05], "length prefix");
297 assert_eq!(&buf[4..], b"hello", "payload");
298 }
299
300 #[tokio::test]
303 async fn recv_frame_reads_length_prefixed_payload() {
304 let ((a_r, a_w), (_b_r, mut b_w)) = duplex_pair();
305 let leg: EmbeddedLeg<MockReader, MockWriter, 1024> = EmbeddedLeg::new(a_r, a_w);
306
307 b_w.write_all(&[0x00, 0x00, 0x00, 0x05]).await.unwrap();
308 b_w.write_all(b"world").await.unwrap();
309
310 let frame = tokio::time::timeout(Duration::from_secs(1), leg.recv_frame())
311 .await
312 .expect("recv should not hang")
313 .expect("recv_frame");
314
315 assert_eq!(&frame[..], b"world");
316 }
317
318 #[tokio::test]
323 async fn recv_frame_reassembles_under_adversarial_chunking() {
324 let ((a_r, a_w), (_b_r, mut b_w)) = duplex_pair_with_chunk(1);
325 let leg: EmbeddedLeg<MockReader, MockWriter, 1024> = EmbeddedLeg::new(a_r, a_w);
326
327 let writer = tokio::spawn(async move {
331 for &b in &[0x00, 0x00, 0x00, 0x05] {
332 b_w.write_all(&[b]).await.expect("write header byte");
333 tokio::task::yield_now().await;
334 }
335 for &b in b"abcde" {
336 b_w.write_all(&[b]).await.expect("write payload byte");
337 tokio::task::yield_now().await;
338 }
339 });
340
341 let frame = tokio::time::timeout(Duration::from_secs(1), leg.recv_frame())
342 .await
343 .expect("recv should not hang under 1-byte chunking")
344 .expect("recv_frame");
345
346 writer.await.expect("writer task");
347 assert_eq!(&frame[..], b"abcde");
348 }
349
350 #[tokio::test]
355 async fn recv_frame_rejects_oversized_header() {
356 let ((a_r, a_w), (_b_r, mut b_w)) = duplex_pair();
360 let leg: EmbeddedLeg<MockReader, MockWriter, 8> = EmbeddedLeg::new(a_r, a_w);
361
362 b_w.write_all(&[0x00, 0x00, 0x00, 0x10])
363 .await
364 .expect("write bogus header");
365 let err = tokio::time::timeout(Duration::from_secs(1), leg.recv_frame())
368 .await
369 .expect("recv should error fast, not hang on payload");
370 match err {
371 Err(CoreError::NetworkError(msg)) => {
372 assert!(
373 msg.contains("framing"),
374 "expected framing error, got: {msg}"
375 );
376 }
377 other => panic!("expected NetworkError(framing), got {other:?}"),
378 }
379 }
380
381 #[tokio::test]
386 async fn recv_frame_returns_error_on_eof_mid_header() {
387 let ((a_r, a_w), (_b_r, b_w)) = duplex_pair();
388 let leg: EmbeddedLeg<MockReader, MockWriter, 1024> = EmbeddedLeg::new(a_r, a_w);
389
390 let target = b_w.write_to.clone();
393 let notify = b_w.write_notify.clone();
394 {
395 let mut p = target.lock().await;
396 p.buf.extend([0x00u8, 0x00]);
397 p.closed = true;
398 }
399 notify.notify_waiters();
400
401 let err = tokio::time::timeout(Duration::from_secs(1), leg.recv_frame())
402 .await
403 .expect("recv should error fast on EOF");
404 match err {
405 Err(CoreError::NetworkError(msg)) => {
406 assert!(
407 msg.contains("read header"),
408 "expected `read header` in error msg, got: {msg}"
409 );
410 }
411 other => panic!("expected NetworkError(read header), got {other:?}"),
412 }
413 }
414
415 #[tokio::test]
420 async fn send_recv_run_concurrently_without_blocking() {
421 let ((a_r, a_w), (b_r, b_w)) = duplex_pair();
422 let leg_a: Arc<EmbeddedLeg<MockReader, MockWriter, 1024>> =
423 Arc::new(EmbeddedLeg::new(a_r, a_w));
424 let leg_b: Arc<EmbeddedLeg<MockReader, MockWriter, 1024>> =
425 Arc::new(EmbeddedLeg::new(b_r, b_w));
426
427 let leg_a_send = Arc::clone(&leg_a);
428 let send = tokio::spawn(async move { leg_a_send.send_frame(b"ping").await });
429 let leg_b_recv = Arc::clone(&leg_b);
430 let recv = tokio::spawn(async move { leg_b_recv.recv_frame().await });
431
432 tokio::time::timeout(Duration::from_secs(1), async {
433 send.await.expect("send task").expect("send_frame result");
434 let frame = recv.await.expect("recv task").expect("recv_frame result");
435 assert_eq!(&frame[..], b"ping");
436 })
437 .await
438 .expect("concurrent send+recv should complete within 1s");
439 }
440
441 #[tokio::test]
447 async fn session_transport_round_trip() {
448 let ((a_r, a_w), (b_r, b_w)) = duplex_pair();
449 let leg_a: EmbeddedLeg<MockReader, MockWriter, 1024> = EmbeddedLeg::new(a_r, a_w);
450 let leg_b: EmbeddedLeg<MockReader, MockWriter, 1024> = EmbeddedLeg::new(b_r, b_w);
451
452 tokio::time::timeout(Duration::from_secs(1), async {
453 <EmbeddedLeg<MockReader, MockWriter, 1024> as SessionTransport>::send_bytes(
454 &leg_a,
455 b"hello-trait",
456 )
457 .await
458 .expect("send_bytes");
459 let frame =
460 <EmbeddedLeg<MockReader, MockWriter, 1024> as SessionTransport>::recv_bytes(&leg_b)
461 .await
462 .expect("recv_bytes");
463 assert_eq!(&frame[..], b"hello-trait");
464 })
465 .await
466 .expect("trait round-trip should complete within 1s");
467 }
468
469 use crate::api::session::{ConnectionState, PhantomSession};
479 use crate::transport::handshake::{
480 ClientHello, HandshakeResponse, HandshakeServer, ServerReply,
481 };
482 use crate::transport::types::{
483 PacketFlags, PacketHeader, PhantomPacket, SessionId, StreamId as TransportStreamId,
484 };
485
486 fn decrypt_incoming_local(
490 server_session: &crate::transport::session::Session,
491 bytes: &[u8],
492 ) -> Vec<u8> {
493 let pkt = server_session
496 .parse_protected(bytes)
497 .expect("parse header-protected PhantomPacket");
498 assert!(
499 pkt.header.flags.contains(PacketFlags::ENCRYPTED),
500 "expected ENCRYPTED flag on application data"
501 );
502 let plain = server_session
503 .decrypt_packet(&pkt.header, &pkt.payload, &[])
504 .expect("decrypt application data");
505 if pkt.header.flags.contains(PacketFlags::RELIABLE) && plain.len() >= 4 {
508 plain[4..].to_vec()
509 } else {
510 plain
511 }
512 }
513
514 fn encrypt_outgoing_local(
520 server_session: &crate::transport::session::Session,
521 session_id: SessionId,
522 stream_id: TransportStreamId,
523 sequence: u32,
524 payload: &[u8],
525 ) -> Vec<u8> {
526 let flag_bits = PacketFlags::RELIABLE | PacketFlags::ENCRYPTED;
527 let header = PacketHeader::new(
528 session_id,
529 stream_id,
530 sequence as u64,
531 PacketFlags::new(flag_bits),
532 )
533 .with_epoch(server_session.current_epoch());
534 let mut pt = Vec::with_capacity(4 + payload.len());
535 pt.extend_from_slice(&sequence.to_be_bytes());
536 pt.extend_from_slice(payload);
537 let ct = server_session
538 .encrypt_packet(&header, &pt, &[])
539 .expect("encrypt reply");
540 let packet = PhantomPacket::new(header, ct);
541 server_session
543 .protect_packet(&packet)
544 .expect("header protection")
545 }
546
547 struct TeeWriter {
552 inner: MockWriter,
553 recorder: Arc<TokioMutex<Vec<u8>>>,
554 }
555
556 impl embedded_io_async::ErrorType for TeeWriter {
557 type Error = Infallible;
558 }
559
560 impl Write for TeeWriter {
561 async fn write(&mut self, data: &[u8]) -> Result<usize, Infallible> {
562 self.recorder.lock().await.extend_from_slice(data);
563 self.inner.write(data).await
564 }
565 }
566
567 crate::impl_embedded_session_transport!(MockReader, TeeWriter, 16384);
574 crate::impl_embedded_session_transport!(MockReader, MockWriter, 16384);
575
576 #[tokio::test]
582 async fn test_phantom_session_handshake_via_embedded_leg() {
583 let ((client_r, client_w_inner), (server_r, server_w)) = duplex_pair();
586 let client_wire_recorder: Arc<TokioMutex<Vec<u8>>> = Arc::new(TokioMutex::new(Vec::new()));
587 let client_w = TeeWriter {
588 inner: client_w_inner,
589 recorder: Arc::clone(&client_wire_recorder),
590 };
591
592 let client_leg: EmbeddedLeg<MockReader, TeeWriter, 16384> =
593 EmbeddedLeg::new(client_r, client_w);
594 let server_leg: EmbeddedLeg<MockReader, MockWriter, 16384> =
595 EmbeddedLeg::new(server_r, server_w);
596
597 let server_hs = HandshakeServer::new().expect("HandshakeServer::new");
598 let server_pinned_key = server_hs.verifying_key().clone();
599
600 let session = PhantomSession::connect_with_transport(
602 "test-server:9000",
603 client_leg,
604 server_pinned_key,
605 );
606
607 session
609 .send(b"early-data".to_vec())
610 .await
611 .expect("queue early-data");
612
613 let server_handle = tokio::spawn(async move {
617 let client_ip = "127.0.0.1".parse().expect("parse loopback IP");
618
619 let client_hello_bytes =
621 tokio::time::timeout(Duration::from_secs(5), server_leg.recv_frame())
622 .await
623 .expect("recv ClientHello within 5s")
624 .expect("recv ClientHello frame");
625 let client_hello = borsh::from_slice::<ClientHello>(&client_hello_bytes)
626 .expect("deserialize ClientHello");
627
628 let server_session = loop {
630 let response = server_hs.process_client_hello(&client_hello, 0, client_ip);
631 match response {
632 HandshakeResponse::Retry(retry) => {
633 let retry_bytes = ServerReply::Retry(retry)
636 .to_wire()
637 .expect("serialize retry");
638 tokio::time::timeout(
639 Duration::from_secs(5),
640 server_leg.send_frame(&retry_bytes),
641 )
642 .await
643 .expect("send retry within 5s")
644 .expect("send retry frame");
645
646 let next_bytes =
647 tokio::time::timeout(Duration::from_secs(5), server_leg.recv_frame())
648 .await
649 .expect("recv retried ClientHello within 5s")
650 .expect("recv retried ClientHello frame");
651 let next_hello = borsh::from_slice::<ClientHello>(&next_bytes)
652 .expect("deserialize retried ClientHello");
653 let resp2 = server_hs.process_client_hello(&next_hello, 0, client_ip);
654 match resp2 {
655 HandshakeResponse::Success(server_hello, session, _) => {
656 let server_hello_bytes = ServerReply::Hello(server_hello)
657 .to_wire()
658 .expect("serialize ServerHello");
659 tokio::time::timeout(
660 Duration::from_secs(5),
661 server_leg.send_frame(&server_hello_bytes),
662 )
663 .await
664 .expect("send ServerHello within 5s")
665 .expect("send ServerHello frame");
666 break session;
667 }
668 other => panic!("expected success after retry, got {other:?}"),
669 }
670 }
671 HandshakeResponse::Success(server_hello, session, _) => {
672 let server_hello_bytes = ServerReply::Hello(server_hello)
673 .to_wire()
674 .expect("serialize ServerHello");
675 tokio::time::timeout(
676 Duration::from_secs(5),
677 server_leg.send_frame(&server_hello_bytes),
678 )
679 .await
680 .expect("send ServerHello within 5s")
681 .expect("send ServerHello frame");
682 break session;
683 }
684 HandshakeResponse::Reject(r) => panic!("unexpected reject: {r:?}"),
685 HandshakeResponse::Fail(e) => panic!("handshake failed: {e:?}"),
686 }
687 };
688
689 let session_id = *server_session.id();
690
691 let early_frame = tokio::time::timeout(Duration::from_secs(5), server_leg.recv_frame())
693 .await
694 .expect("recv early-data within 5s")
695 .expect("recv early-data frame");
696 let early_plain = decrypt_incoming_local(&server_session, &early_frame);
697 assert_eq!(early_plain, b"early-data");
698
699 let post_frame = tokio::time::timeout(Duration::from_secs(5), server_leg.recv_frame())
701 .await
702 .expect("recv after-handshake within 5s")
703 .expect("recv after-handshake frame");
704 let post_plain = decrypt_incoming_local(&server_session, &post_frame);
705 assert_eq!(post_plain, b"after-handshake");
706
707 let reply = encrypt_outgoing_local(&server_session, session_id, 1, 0, b"server-reply");
711 tokio::time::timeout(Duration::from_secs(5), server_leg.send_frame(&reply))
712 .await
713 .expect("send reply within 5s")
714 .expect("send reply frame");
715 });
716
717 tokio::time::sleep(Duration::from_millis(500)).await;
719 assert_eq!(session.connection_state(), ConnectionState::Connected);
720
721 session
723 .send(b"after-handshake".to_vec())
724 .await
725 .expect("send after-handshake");
726
727 let reply = tokio::time::timeout(Duration::from_secs(5), session.recv())
729 .await
730 .expect("recv reply within 5s")
731 .expect("recv server-reply");
732 assert_eq!(reply, b"server-reply");
733
734 tokio::time::timeout(Duration::from_secs(5), server_handle)
735 .await
736 .expect("server task within 5s")
737 .expect("server task joined");
738 session.disconnect().await.expect("close session");
739
740 let wire = client_wire_recorder.lock().await;
744 assert!(
745 !wire
746 .windows(b"early-data".len())
747 .any(|w| w == b"early-data"),
748 "plaintext early-data leaked onto the embedded wire"
749 );
750 assert!(
751 !wire
752 .windows(b"after-handshake".len())
753 .any(|w| w == b"after-handshake"),
754 "plaintext after-handshake leaked onto the embedded wire"
755 );
756 }
757}