Skip to main content

eggress_protocol_shadowsocks/
tcp_stream.rs

1use std::cmp;
2use std::io;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use bytes::{Buf as _, BytesMut};
7use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
8use zeroize::Zeroizing;
9
10use crate::aead::AeadCipher;
11use crate::method::CipherMethod;
12use crate::nonce::NonceCounter;
13
14/// Maximum plaintext payload per pproxy/SIP003 AEAD chunk.
15pub const MAX_CHUNK_PAYLOAD: usize = 16 * 1024 - 1;
16
17/// Internal read state machine for standard SIP003 framing.
18#[derive(Clone, Copy, Debug)]
19enum ReadState {
20    /// Waiting to read the peer's salt (first packet from peer).
21    PeerSalt,
22    /// Waiting for the 18-byte encrypted length block.
23    LengthBlock,
24    /// Reading `len` bytes of encrypted payload.
25    Payload { len: usize },
26    /// A valid zero-length payload has permanently ended the stream.
27    Closed,
28}
29
30/// Bidirectional AEAD stream adapter for Shadowsocks TCP.
31///
32/// Wraps an `AsyncRead + AsyncWrite` stream and encrypts/decrypts all data
33/// using standard Shadowsocks AEAD chunk framing (SIP003).
34///
35/// In SIP003, each direction uses its own salt and subkey:
36/// - The SENDER prepends a random salt to the first packet, derives a subkey
37///   from it, and encrypts all subsequent packets with that subkey.
38/// - The RECEIVER reads the salt from the first packet, derives the same
39///   subkey, and uses it for decryption.
40///
41/// This means:
42/// - For the client: `subkey` is the write subkey (for encrypting to server),
43///   and the read subkey is derived from the server's first response salt.
44/// - For the server: `subkey` is the read subkey (for decrypting from client),
45///   and the write subkey is sent as the first bytes of the response.
46pub struct ShadowsocksAeadStream<S> {
47    inner: S,
48    method: CipherMethod,
49    /// Subkey for writing (encrypting outbound data).
50    write_subkey: Zeroizing<Vec<u8>>,
51    write_cipher: Option<AeadCipher>,
52    /// Subkey for reading (decrypting inbound data). `None` until the peer's
53    /// salt is received on the first read.
54    read_subkey: Option<Zeroizing<Vec<u8>>>,
55    read_cipher: Option<AeadCipher>,
56    /// Cached password key material for deriving subkeys from peer salts.
57    password_ikm: Option<Zeroizing<Vec<u8>>>,
58    /// Whether the write side needs to send its salt before the first data chunk.
59    send_write_salt: bool,
60    write_nonce: NonceCounter,
61    read_nonce: NonceCounter,
62    read_plain: BytesMut,
63    read_buf: BytesMut,
64    read_state: ReadState,
65    write_buf: BytesMut,
66}
67
68impl<S: AsyncRead + AsyncWrite + Unpin> ShadowsocksAeadStream<S> {
69    /// Create a new AEAD stream (same subkey for both directions — for internal use).
70    pub fn new(
71        inner: S,
72        method: CipherMethod,
73        subkey: Vec<u8>,
74    ) -> Result<Self, crate::error::ShadowsocksError> {
75        let nonce_size = method.nonce_size();
76        let write_cipher = AeadCipher::new(method, &subkey)?;
77        let read_cipher = AeadCipher::new(method, &subkey)?;
78        let subkey = Zeroizing::new(subkey);
79        Ok(Self {
80            inner,
81            method,
82            write_subkey: subkey.clone(),
83            write_cipher: Some(write_cipher),
84            read_subkey: Some(subkey),
85            read_cipher: Some(read_cipher),
86            password_ikm: None,
87            send_write_salt: false,
88            write_nonce: NonceCounter::starting_at(nonce_size, 0),
89            read_nonce: NonceCounter::starting_at(nonce_size, 0),
90            read_plain: BytesMut::new(),
91            read_buf: BytesMut::new(),
92            read_state: ReadState::LengthBlock,
93            write_buf: BytesMut::new(),
94        })
95    }
96
97    /// Create a client-side AEAD stream.
98    ///
99    /// - `write_subkey`: derived from the client's salt (for encrypting to server).
100    /// - On first read, the peer's salt is read and `read_subkey` is derived.
101    pub fn new_client(
102        inner: S,
103        method: CipherMethod,
104        write_subkey: Vec<u8>,
105        password: &str,
106    ) -> Result<Self, crate::error::ShadowsocksError> {
107        let nonce_size = method.nonce_size();
108        let write_cipher = AeadCipher::new(method, &write_subkey)?;
109        Ok(Self {
110            inner,
111            method,
112            write_subkey: Zeroizing::new(write_subkey),
113            write_cipher: Some(write_cipher),
114            read_subkey: None,
115            read_cipher: None,
116            password_ikm: Some(Zeroizing::new(CipherMethod::password_key_material(
117                password.as_bytes(),
118            ))),
119            send_write_salt: false,
120            // Write nonces start at 2 (address header used 0,1).
121            write_nonce: NonceCounter::starting_at(nonce_size, 2),
122            // Read nonces start at 0 (peer's first response).
123            read_nonce: NonceCounter::starting_at(nonce_size, 0),
124            read_plain: BytesMut::new(),
125            read_buf: BytesMut::new(),
126            read_state: ReadState::PeerSalt,
127            write_buf: BytesMut::new(),
128        })
129    }
130
131    /// Create a server-side AEAD stream.
132    ///
133    /// - `read_subkey`: derived from the client's salt (for decrypting from client).
134    /// - `send_write_salt`: must be true so the server can send a fresh salt
135    ///   before its first data chunk (as required by standard implementations).
136    pub fn new_server(
137        inner: S,
138        method: CipherMethod,
139        read_subkey: Vec<u8>,
140        send_write_salt: bool,
141        password: &str,
142    ) -> Result<Self, crate::error::ShadowsocksError> {
143        if !send_write_salt {
144            return Err(crate::error::ShadowsocksError::Other(
145                "server streams must send a write salt".to_string(),
146            ));
147        }
148        let nonce_size = method.nonce_size();
149        let read_cipher = AeadCipher::new(method, &read_subkey)?;
150        Ok(Self {
151            inner,
152            method,
153            write_subkey: Zeroizing::new(Vec::new()), // will be derived when salt is sent
154            write_cipher: None,
155            read_subkey: Some(Zeroizing::new(read_subkey)),
156            read_cipher: Some(read_cipher),
157            password_ikm: Some(Zeroizing::new(CipherMethod::password_key_material(
158                password.as_bytes(),
159            ))),
160            send_write_salt,
161            // Read nonces start at 2 (address header used 0,1).
162            read_nonce: NonceCounter::starting_at(nonce_size, 2),
163            // Write nonces start at 0 (first response).
164            write_nonce: NonceCounter::starting_at(nonce_size, 0),
165            read_plain: BytesMut::new(),
166            read_buf: BytesMut::new(),
167            read_state: ReadState::LengthBlock,
168            write_buf: BytesMut::new(),
169        })
170    }
171
172    /// Create a new AEAD stream with explicit starting nonces (legacy/internal).
173    pub fn new_with_nonces(
174        inner: S,
175        method: CipherMethod,
176        subkey: Vec<u8>,
177        write_start: u64,
178        read_start: u64,
179    ) -> Result<Self, crate::error::ShadowsocksError> {
180        let nonce_size = method.nonce_size();
181        let write_cipher = AeadCipher::new(method, &subkey)?;
182        let read_cipher = AeadCipher::new(method, &subkey)?;
183        let subkey = Zeroizing::new(subkey);
184        Ok(Self {
185            inner,
186            method,
187            write_subkey: subkey.clone(),
188            write_cipher: Some(write_cipher),
189            read_subkey: Some(subkey),
190            read_cipher: Some(read_cipher),
191            password_ikm: None,
192            send_write_salt: false,
193            write_nonce: NonceCounter::starting_at(nonce_size, write_start),
194            read_nonce: NonceCounter::starting_at(nonce_size, read_start),
195            read_plain: BytesMut::new(),
196            read_buf: BytesMut::new(),
197            read_state: ReadState::LengthBlock,
198            write_buf: BytesMut::new(),
199        })
200    }
201
202    pub fn into_inner(self) -> S {
203        self.inner
204    }
205
206    /// Preserve plaintext that was carried in the initial request frame after
207    /// the destination address. Standard Shadowsocks clients may coalesce the
208    /// address and their first application payload into one AEAD chunk.
209    pub(crate) fn prepend_read_plaintext(&mut self, plaintext: &[u8]) {
210        if !plaintext.is_empty() {
211            self.read_plain.extend_from_slice(plaintext);
212        }
213    }
214}
215
216/// Read bytes from `inner` into `buf` until `buf.len() >= target`.
217///
218/// Handles partial reads across `poll_read` calls. On `Pending`, already-read
219/// bytes are appended to `buf` so they survive across poll invocations.
220///
221/// Returns `Poll::Ready(Ok(true))` if the target was reached,
222/// `Poll::Ready(Ok(false))` on clean EOF (zero bytes read from inner),
223/// or `Poll::Ready(Err(..))` on error / premature EOF.
224fn read_until<S: AsyncRead + Unpin>(
225    inner: &mut S,
226    cx: &mut Context<'_>,
227    buf: &mut BytesMut,
228    target: usize,
229) -> Poll<io::Result<bool>> {
230    while buf.len() < target {
231        let start = buf.len();
232        buf.resize(target, 0);
233        let mut rbuf = ReadBuf::new(&mut buf[start..target]);
234        match Pin::new(&mut *inner).poll_read(cx, &mut rbuf) {
235            Poll::Ready(Ok(())) => {
236                let n = rbuf.filled().len();
237                if n == 0 {
238                    // Clean EOF — no more data from the inner stream.
239                    buf.truncate(start);
240                    return Poll::Ready(Ok(false));
241                }
242                buf.truncate(start + n);
243            }
244            Poll::Ready(Err(e)) => {
245                buf.truncate(start);
246                return Poll::Ready(Err(e));
247            }
248            Poll::Pending => {
249                buf.truncate(start);
250                return Poll::Pending;
251            }
252        }
253    }
254    Poll::Ready(Ok(true))
255}
256
257impl<S: AsyncRead + AsyncWrite + Unpin> AsyncRead for ShadowsocksAeadStream<S> {
258    fn poll_read(
259        self: Pin<&mut Self>,
260        cx: &mut Context<'_>,
261        buf: &mut ReadBuf<'_>,
262    ) -> Poll<io::Result<()>> {
263        let this = self.get_mut();
264
265        // Drain any previously-buffered plaintext first.
266        if !this.read_plain.is_empty() {
267            let n = cmp::min(this.read_plain.len(), buf.remaining());
268            buf.put_slice(&this.read_plain.split_to(n));
269            return Poll::Ready(Ok(()));
270        }
271
272        // Drive the read state machine until we produce plaintext or stall.
273        loop {
274            let state = this.read_state;
275            match state {
276                ReadState::Closed => return Poll::Ready(Ok(())),
277                ReadState::PeerSalt => {
278                    // Read the peer's salt to derive the read subkey.
279                    let salt_size = this.method.salt_size();
280                    match read_until(&mut this.inner, cx, &mut this.read_buf, salt_size) {
281                        Poll::Ready(Ok(true)) => {}
282                        Poll::Ready(Ok(false)) => {
283                            this.read_buf.clear();
284                            return Poll::Ready(Ok(()));
285                        }
286                        Poll::Ready(Err(e)) => {
287                            this.read_buf.clear();
288                            return Poll::Ready(Err(e));
289                        }
290                        Poll::Pending => return Poll::Pending,
291                    }
292
293                    let salt = this.read_buf.split_to(salt_size);
294                    let password_ikm = this
295                        .password_ikm
296                        .as_deref()
297                        .ok_or_else(|| io::Error::other("no password for subkey derivation"))?;
298                    let read_subkey = this
299                        .method
300                        .derive_key_from_ikm(password_ikm, &salt)
301                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
302                    let read_cipher = AeadCipher::new(this.method, &read_subkey)
303                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
304                    this.read_subkey = Some(Zeroizing::new(read_subkey));
305                    this.read_cipher = Some(read_cipher);
306                    this.read_buf.clear();
307                    this.read_state = ReadState::LengthBlock;
308                }
309                ReadState::LengthBlock => {
310                    // Read the encrypted length block (2 plaintext bytes + tag).
311                    let len_block_size = 2 + this.method.tag_size();
312                    match read_until(&mut this.inner, cx, &mut this.read_buf, len_block_size) {
313                        Poll::Ready(Ok(true)) => {}
314                        Poll::Ready(Ok(false)) => {
315                            // Clean EOF — no more data from the inner stream.
316                            this.read_buf.clear();
317                            return Poll::Ready(Ok(()));
318                        }
319                        Poll::Ready(Err(e)) => {
320                            this.read_buf.clear();
321                            return Poll::Ready(Err(e));
322                        }
323                        Poll::Pending => return Poll::Pending,
324                    }
325
326                    let cipher = this
327                        .read_cipher
328                        .as_ref()
329                        .ok_or_else(|| io::Error::other("read cipher not yet derived"))?;
330
331                    // Decrypt length block with current nonce.
332                    let mut nonce = [0u8; 12];
333                    this.read_nonce
334                        .current(&mut nonce)
335                        .map_err(io::Error::other)?;
336                    let len_plaintext = cipher
337                        .decrypt(&nonce, &this.read_buf[..len_block_size])
338                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
339
340                    // Advance past length nonce.
341                    this.read_nonce.advance().map_err(io::Error::other)?;
342
343                    if len_plaintext.len() != 2 {
344                        this.read_buf.clear();
345                        return Poll::Ready(Err(io::Error::new(
346                            io::ErrorKind::InvalidData,
347                            "invalid length block plaintext",
348                        )));
349                    }
350
351                    let payload_len =
352                        u16::from_be_bytes([len_plaintext[0], len_plaintext[1]]) as usize;
353                    this.read_buf.clear();
354
355                    if payload_len > MAX_CHUNK_PAYLOAD {
356                        return Poll::Ready(Err(io::Error::new(
357                            io::ErrorKind::InvalidData,
358                            format!(
359                                "payload length {} exceeds maximum {}",
360                                payload_len, MAX_CHUNK_PAYLOAD
361                            ),
362                        )));
363                    }
364
365                    // A zero-length payload signals end-of-stream.
366                    if payload_len == 0 {
367                        this.read_state = ReadState::Closed;
368                        return Poll::Ready(Ok(()));
369                    }
370
371                    this.read_state = ReadState::Payload { len: payload_len };
372                }
373                ReadState::Payload { len } => {
374                    // Read `len` bytes of encrypted payload (+ 16-byte tag).
375                    let wire_len = len + this.method.tag_size();
376                    match read_until(&mut this.inner, cx, &mut this.read_buf, wire_len) {
377                        Poll::Ready(Ok(true)) => {}
378                        Poll::Ready(Ok(false)) | Poll::Ready(Err(_)) => {
379                            this.read_buf.clear();
380                            this.read_state = ReadState::LengthBlock;
381                            return Poll::Ready(Err(io::Error::new(
382                                io::ErrorKind::UnexpectedEof,
383                                "unexpected EOF in payload",
384                            )));
385                        }
386                        Poll::Pending => return Poll::Pending,
387                    }
388
389                    let cipher = this
390                        .read_cipher
391                        .as_ref()
392                        .ok_or_else(|| io::Error::other("read cipher not yet derived"))?;
393
394                    // Decrypt payload with current nonce (now at payload nonce).
395                    let mut nonce = [0u8; 12];
396                    this.read_nonce
397                        .current(&mut nonce)
398                        .map_err(io::Error::other)?;
399                    let plaintext = cipher
400                        .decrypt(&nonce, &this.read_buf)
401                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
402
403                    // Advance past payload nonce.
404                    this.read_nonce.advance().map_err(io::Error::other)?;
405
406                    this.read_buf.clear();
407                    this.read_state = ReadState::LengthBlock;
408                    // Avoid copying through the plaintext queue when the
409                    // caller can accept this whole decrypted chunk.
410                    if plaintext.len() <= buf.remaining() {
411                        buf.put_slice(&plaintext);
412                    } else {
413                        this.read_plain.extend_from_slice(&plaintext);
414                        let n = cmp::min(this.read_plain.len(), buf.remaining());
415                        buf.put_slice(&this.read_plain.split_to(n));
416                    }
417                    return Poll::Ready(Ok(()));
418                }
419            }
420        }
421    }
422}
423
424impl<S: AsyncRead + AsyncWrite + Unpin> AsyncWrite for ShadowsocksAeadStream<S> {
425    fn poll_write(
426        self: Pin<&mut Self>,
427        cx: &mut Context<'_>,
428        buf: &[u8],
429    ) -> Poll<io::Result<usize>> {
430        let this = self.get_mut();
431
432        // If the server needs to send its salt before the first data chunk:
433        if this.send_write_salt {
434            // Generate a random salt and derive the write subkey.
435            use rand::RngCore;
436            let mut salt_buf = [0u8; 32];
437            rand::thread_rng().fill_bytes(&mut salt_buf[..this.method.salt_size()]);
438            let salt = &salt_buf[..this.method.salt_size()];
439            let password_ikm = this
440                .password_ikm
441                .as_deref()
442                .ok_or_else(|| io::Error::other("no password for subkey derivation"))?;
443            let write_subkey = this
444                .method
445                .derive_key_from_ikm(password_ikm, salt)
446                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
447            let write_cipher = AeadCipher::new(this.method, &write_subkey)
448                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
449            this.write_subkey = Zeroizing::new(write_subkey);
450            this.write_cipher = Some(write_cipher);
451            this.write_buf.extend_from_slice(salt);
452            this.send_write_salt = false;
453        }
454
455        // Flush any leftover ciphertext buffered from a previous call.
456        while !this.write_buf.is_empty() {
457            match Pin::new(&mut this.inner).poll_write(cx, &this.write_buf) {
458                Poll::Ready(Ok(0)) => {
459                    return Poll::Ready(Err(io::Error::new(
460                        io::ErrorKind::WriteZero,
461                        "zero-byte write",
462                    )));
463                }
464                Poll::Ready(Ok(n)) => this.write_buf.advance(n),
465                Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
466                Poll::Pending => return Poll::Pending,
467            }
468        }
469
470        // Encrypt at most MAX_CHUNK_PAYLOAD bytes of plaintext.
471        let chunk_size = cmp::min(buf.len(), MAX_CHUNK_PAYLOAD);
472        if chunk_size == 0 {
473            return Poll::Ready(Ok(0));
474        }
475
476        // Encrypt length block: AEAD(len_u16_be, nonce)
477        let len_bytes = (chunk_size as u16).to_be_bytes();
478        let cipher = this
479            .write_cipher
480            .as_ref()
481            .ok_or_else(|| io::Error::other("write cipher not yet derived"))?;
482        let mut len_nonce = [0u8; 12];
483        this.write_nonce
484            .current(&mut len_nonce)
485            .map_err(io::Error::other)?;
486        let len_ct = cipher
487            .encrypt(&len_nonce, &len_bytes)
488            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
489        this.write_nonce.advance().map_err(io::Error::other)?;
490
491        // Encrypt payload block: AEAD(payload, nonce+1)
492        let mut payload_nonce = [0u8; 12];
493        this.write_nonce
494            .current(&mut payload_nonce)
495            .map_err(io::Error::other)?;
496        let payload_ct = cipher
497            .encrypt(&payload_nonce, &buf[..chunk_size])
498            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
499        this.write_nonce.advance().map_err(io::Error::other)?;
500
501        // Wire frame: [18-byte length block] [payload_len + 16-byte payload block]
502        this.write_buf.extend_from_slice(&len_ct);
503        this.write_buf.extend_from_slice(&payload_ct);
504
505        // Best-effort flush of the newly-buffered ciphertext.
506        while !this.write_buf.is_empty() {
507            match Pin::new(&mut this.inner).poll_write(cx, &this.write_buf) {
508                Poll::Ready(Ok(0)) => {
509                    return Poll::Ready(Err(io::Error::new(
510                        io::ErrorKind::WriteZero,
511                        "zero-byte write",
512                    )));
513                }
514                Poll::Ready(Ok(n)) => this.write_buf.advance(n),
515                Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
516                Poll::Pending => break,
517            }
518        }
519
520        Poll::Ready(Ok(chunk_size))
521    }
522
523    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
524        let this = self.get_mut();
525
526        while !this.write_buf.is_empty() {
527            match Pin::new(&mut this.inner).poll_write(cx, &this.write_buf) {
528                Poll::Ready(Ok(0)) => {
529                    return Poll::Ready(Err(io::Error::new(
530                        io::ErrorKind::WriteZero,
531                        "zero-byte write",
532                    )));
533                }
534                Poll::Ready(Ok(n)) => this.write_buf.advance(n),
535                Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
536                Poll::Pending => return Poll::Pending,
537            }
538        }
539
540        Pin::new(&mut this.inner).poll_flush(cx)
541    }
542
543    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
544        let this = self.get_mut();
545
546        // Flush any remaining buffered ciphertext before shutting down.
547        while !this.write_buf.is_empty() {
548            match Pin::new(&mut this.inner).poll_write(cx, &this.write_buf) {
549                Poll::Ready(Ok(0)) => {
550                    return Poll::Ready(Err(io::Error::new(
551                        io::ErrorKind::WriteZero,
552                        "zero-byte write",
553                    )));
554                }
555                Poll::Ready(Ok(n)) => this.write_buf.advance(n),
556                Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
557                Poll::Pending => return Poll::Pending,
558            }
559        }
560
561        Pin::new(&mut this.inner).poll_shutdown(cx)
562    }
563}
564
565#[cfg(test)]
566mod tests {
567    use super::*;
568    use tokio::io::{AsyncReadExt, AsyncWriteExt};
569
570    use crate::aead::encrypt_chunk_standard;
571
572    #[test]
573    fn invalid_subkey_returns_error_instead_of_panicking() {
574        let (stream, _) = tokio::io::duplex(64);
575        let result = ShadowsocksAeadStream::new(stream, CipherMethod::Aes256Gcm, vec![0; 16]);
576        assert!(result.is_err());
577    }
578
579    #[tokio::test]
580    async fn roundtrip_small_data() {
581        let (client, server) = tokio::io::duplex(4096);
582        let method = CipherMethod::Aes256Gcm;
583        let subkey = vec![0x42u8; 32];
584
585        let mut client_stream = ShadowsocksAeadStream::new(client, method, subkey.clone()).unwrap();
586        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey).unwrap();
587
588        // Write from client
589        client_stream.write_all(b"hello").await.unwrap();
590        client_stream.flush().await.unwrap();
591
592        // Read from server
593        let mut buf = vec![0u8; 64];
594        let n = server_stream.read(&mut buf).await.unwrap();
595        assert_eq!(&buf[..n], b"hello");
596    }
597
598    #[tokio::test]
599    async fn roundtrip_large_data() {
600        let (client, server) = tokio::io::duplex(1 << 16);
601        let method = CipherMethod::ChaCha20IetfPoly1305;
602        let subkey = vec![0xABu8; 32];
603
604        let payload = vec![0xCDu8; 100_000];
605        let expected = payload.clone();
606        let write_subkey = subkey.clone();
607
608        let write_handle = tokio::spawn(async move {
609            let mut client_stream =
610                ShadowsocksAeadStream::new(client, method, write_subkey).unwrap();
611            client_stream.write_all(&payload).await.unwrap();
612            client_stream.flush().await.unwrap();
613        });
614
615        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey).unwrap();
616
617        let mut received = Vec::new();
618        server_stream.read_to_end(&mut received).await.unwrap();
619        write_handle.await.unwrap();
620        assert_eq!(received, expected);
621    }
622
623    #[tokio::test]
624    async fn bidirectional_communication() {
625        let (c1, s1) = tokio::io::duplex(4096);
626        let (c2, s2) = tokio::io::duplex(4096);
627        let method = CipherMethod::Aes128Gcm;
628        let subkey = vec![0x11u8; 16];
629
630        let mut client_a = ShadowsocksAeadStream::new(c1, method, subkey.clone()).unwrap();
631        let mut server_a = ShadowsocksAeadStream::new(s1, method, subkey.clone()).unwrap();
632        let mut client_b = ShadowsocksAeadStream::new(c2, method, subkey.clone()).unwrap();
633        let mut server_b = ShadowsocksAeadStream::new(s2, method, subkey).unwrap();
634
635        // Client A -> Server A -> Client B -> Server B
636        client_a.write_all(b"ping").await.unwrap();
637        client_a.flush().await.unwrap();
638
639        let mut buf = vec![0u8; 64];
640        let n = server_a.read(&mut buf).await.unwrap();
641        assert_eq!(&buf[..n], b"ping");
642
643        client_b.write_all(b"pong").await.unwrap();
644        client_b.flush().await.unwrap();
645
646        let n = server_b.read(&mut buf).await.unwrap();
647        assert_eq!(&buf[..n], b"pong");
648    }
649
650    #[tokio::test]
651    async fn empty_read_on_eof() {
652        let (client, server) = tokio::io::duplex(256);
653        let method = CipherMethod::Aes256Gcm;
654        let subkey = vec![0x55u8; 32];
655
656        let client_stream = ShadowsocksAeadStream::new(client, method, subkey.clone()).unwrap();
657        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey).unwrap();
658
659        // Drop client to signal EOF
660        drop(client_stream);
661
662        let mut buf = vec![0u8; 64];
663        let result = server_stream.read(&mut buf).await;
664        assert!(result.is_ok());
665        assert_eq!(result.unwrap(), 0); // EOF
666    }
667
668    #[tokio::test]
669    async fn write_buffer_flushed_on_flush() {
670        let (client, server) = tokio::io::duplex(4096);
671        let method = CipherMethod::Aes256Gcm;
672        let subkey = vec![0x99u8; 32];
673
674        let mut client_stream = ShadowsocksAeadStream::new(client, method, subkey.clone()).unwrap();
675        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey).unwrap();
676
677        client_stream.write_all(b"data").await.unwrap();
678        client_stream.flush().await.unwrap();
679
680        let mut buf = vec![0u8; 64];
681        let n = server_stream.read(&mut buf).await.unwrap();
682        assert_eq!(&buf[..n], b"data");
683    }
684
685    #[tokio::test]
686    async fn multiple_chunks() {
687        let (client, server) = tokio::io::duplex(8192);
688        let method = CipherMethod::Aes256Gcm;
689        let subkey = vec![0x77u8; 32];
690
691        let mut client_stream = ShadowsocksAeadStream::new(client, method, subkey.clone()).unwrap();
692        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey).unwrap();
693
694        // Send multiple small writes; each becomes its own AEAD chunk.
695        for i in 0..10 {
696            let msg = format!("msg-{i}");
697            client_stream.write_all(msg.as_bytes()).await.unwrap();
698        }
699        client_stream.flush().await.unwrap();
700        drop(client_stream);
701
702        let mut received = Vec::new();
703        server_stream.read_to_end(&mut received).await.unwrap();
704
705        let mut expected = Vec::new();
706        for i in 0..10 {
707            expected.extend_from_slice(format!("msg-{i}").as_bytes());
708        }
709        assert_eq!(received, expected);
710    }
711
712    #[tokio::test]
713    async fn into_inner_returns_original_stream() {
714        let (client, _server) = tokio::io::duplex(256);
715        let method = CipherMethod::Aes256Gcm;
716        let subkey = vec![0x01u8; 32];
717
718        let stream = ShadowsocksAeadStream::new(client, method, subkey).unwrap();
719        let _ = stream.into_inner();
720    }
721
722    #[tokio::test]
723    async fn zero_length_payload_signals_eof() {
724        let (client, server) = tokio::io::duplex(256);
725        let method = CipherMethod::Aes256Gcm;
726        let subkey = vec![0x33u8; 32];
727
728        // Use new_with_nonces to start at nonce 0 (for testing raw wire data).
729        let mut server_stream =
730            ShadowsocksAeadStream::new_with_nonces(server, method, subkey.clone(), 0, 0).unwrap();
731
732        // Manually send a zero-length payload chunk (raw, not through the adapter).
733        // Wire format: AEAD(len_u16=0, nonce) + AEAD(empty, nonce+1)
734        let mut nonce = vec![0u8; method.nonce_size()];
735        nonce[0] = 0;
736        let wire = encrypt_chunk_standard(method, &subkey, &nonce, b"").unwrap();
737        let mut raw_stream = client;
738        raw_stream.write_all(&wire).await.unwrap();
739
740        // The server should see EOF after the zero-length payload chunk.
741        let mut buf = vec![0u8; 64];
742        let result = server_stream.read(&mut buf).await;
743        assert!(result.is_ok());
744        assert_eq!(result.unwrap(), 0); // EOF
745
746        // EOF is terminal even if the peer violates the protocol and sends
747        // more bytes afterward.
748        raw_stream.write_all(b"ignored").await.unwrap();
749        let result = server_stream.read(&mut buf).await;
750        assert!(result.is_ok());
751        assert_eq!(result.unwrap(), 0);
752    }
753
754    #[tokio::test]
755    async fn tampered_length_block_fails() {
756        let (client, server) = tokio::io::duplex(256);
757        let method = CipherMethod::Aes256Gcm;
758        let subkey = vec![0x42u8; 32];
759
760        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey.clone()).unwrap();
761
762        // Manually create wire: encrypt with nonce 2 (first data nonce), then tamper length block
763        let mut raw_stream = client;
764        let nonce1 = {
765            let mut n = vec![0u8; method.nonce_size()];
766            n[0] = 2; // first data chunk nonce
767            n
768        };
769        let len_bytes = (5u16).to_be_bytes();
770        let len_ct = crate::aead::aead_encrypt_raw(method, &subkey, &nonce1, &len_bytes).unwrap();
771        let mut tampered_len_ct = len_ct;
772        tampered_len_ct[0] ^= 0xFF;
773
774        // Write tampered length block + valid payload block
775        raw_stream.write_all(&tampered_len_ct).await.unwrap();
776        // Write a valid payload block (won't matter since length decryption fails)
777        let nonce2 = {
778            let mut n = vec![0u8; method.nonce_size()];
779            n[0] = 3; // payload nonce = length nonce + 1
780            n
781        };
782        let payload_ct = crate::aead::aead_encrypt_raw(method, &subkey, &nonce2, b"hello").unwrap();
783        raw_stream.write_all(&payload_ct).await.unwrap();
784        drop(raw_stream);
785
786        let mut buf = vec![0u8; 64];
787        let result = tokio::time::timeout(
788            std::time::Duration::from_secs(2),
789            server_stream.read(&mut buf),
790        )
791        .await;
792
793        let result = result.expect("tampered length block must fail promptly");
794        let error = result.expect_err("tampered length block must not produce plaintext or EOF");
795        assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
796    }
797
798    #[test]
799    fn server_without_write_salt_is_rejected() {
800        let (_, server) = tokio::io::duplex(64);
801        let result = ShadowsocksAeadStream::new_server(
802            server,
803            CipherMethod::Aes256Gcm,
804            vec![0x42; 32],
805            false,
806            "password",
807        );
808        assert!(result.is_err());
809    }
810
811    #[tokio::test]
812    async fn tampered_payload_block_fails() {
813        let (client, server) = tokio::io::duplex(1024);
814        let method = CipherMethod::Aes256Gcm;
815        let subkey = vec![0x42u8; 32];
816
817        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey.clone()).unwrap();
818
819        // Manually create wire: valid length block + tampered payload block
820        let mut raw_stream = client;
821        let nonce1 = {
822            let mut n = vec![0u8; method.nonce_size()];
823            n[0] = 2; // first data chunk nonce
824            n
825        };
826        let len_bytes = (5u16).to_be_bytes();
827        let len_ct = crate::aead::aead_encrypt_raw(method, &subkey, &nonce1, &len_bytes).unwrap();
828        raw_stream.write_all(&len_ct).await.unwrap();
829
830        // Write tampered payload block
831        let nonce2 = {
832            let mut n = vec![0u8; method.nonce_size()];
833            n[0] = 3; // payload nonce = length nonce + 1
834            n
835        };
836        let payload_ct = crate::aead::aead_encrypt_raw(method, &subkey, &nonce2, b"hello").unwrap();
837        let mut tampered_payload_ct = payload_ct;
838        tampered_payload_ct[0] ^= 0xFF;
839        raw_stream.write_all(&tampered_payload_ct).await.unwrap();
840        drop(raw_stream);
841
842        let mut buf = vec![0u8; 64];
843        let result = tokio::time::timeout(
844            std::time::Duration::from_secs(2),
845            server_stream.read(&mut buf),
846        )
847        .await;
848
849        let result = result.expect("tampered payload block must fail promptly");
850        let error = result.expect_err("tampered payload block must not produce plaintext or EOF");
851        assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
852    }
853
854    #[tokio::test]
855    async fn tampered_payload_fails() {
856        use crate::aead::aead_encrypt_raw;
857
858        let method = CipherMethod::Aes256Gcm;
859        let subkey = vec![0x42u8; 32];
860        let plaintext = b"hello world";
861
862        let nonce = vec![0u8; method.nonce_size()];
863        let ciphertext = aead_encrypt_raw(method, &subkey, &nonce, plaintext).unwrap();
864
865        let mut tampered = ciphertext.clone();
866        tampered[0] ^= 0x01;
867
868        let result = crate::aead::aead_decrypt_raw(method, &subkey, &nonce, &tampered);
869        assert!(
870            result.is_err(),
871            "decryption of tampered ciphertext should fail"
872        );
873    }
874
875    #[tokio::test]
876    async fn wrong_key_fails() {
877        use crate::aead::aead_encrypt_raw;
878
879        let method = CipherMethod::Aes256Gcm;
880        let correct_key = vec![0x42u8; 32];
881        let wrong_key = vec![0x99u8; 32];
882        let plaintext = b"secret data";
883
884        let nonce = vec![0u8; method.nonce_size()];
885        let ciphertext = aead_encrypt_raw(method, &correct_key, &nonce, plaintext).unwrap();
886
887        let result = crate::aead::aead_decrypt_raw(method, &wrong_key, &nonce, &ciphertext);
888        assert!(result.is_err(), "decryption with wrong key should fail");
889
890        let result = crate::aead::aead_decrypt_raw(method, &correct_key, &nonce, &ciphertext);
891        assert!(result.is_ok(), "decryption with correct key should succeed");
892        assert_eq!(result.unwrap(), plaintext);
893    }
894
895    #[tokio::test]
896    async fn standard_chunk_format_roundtrip() {
897        let (client, server) = tokio::io::duplex(4096);
898        let method = CipherMethod::Aes256Gcm;
899        let subkey = vec![0x55u8; 32];
900
901        let mut client_stream = ShadowsocksAeadStream::new(client, method, subkey.clone()).unwrap();
902        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey).unwrap();
903
904        let data = b"standard SIP003 framing test";
905        client_stream.write_all(data).await.unwrap();
906        client_stream.flush().await.unwrap();
907
908        let mut buf = vec![0u8; 64];
909        let n = server_stream.read(&mut buf).await.unwrap();
910        assert_eq!(&buf[..n], data.as_slice());
911    }
912
913    #[tokio::test]
914    async fn empty_plaintext_roundtrip() {
915        let (client, server) = tokio::io::duplex(256);
916        let method = CipherMethod::Aes128Gcm;
917        let subkey = vec![0x88u8; 16];
918
919        let mut client_stream = ShadowsocksAeadStream::new(client, method, subkey.clone()).unwrap();
920        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey).unwrap();
921
922        // Write empty data — should produce a zero-length payload chunk
923        client_stream.write_all(b"").await.unwrap();
924        client_stream.flush().await.unwrap();
925        drop(client_stream);
926
927        let mut buf = vec![0u8; 64];
928        let result = server_stream.read(&mut buf).await;
929        assert!(result.is_ok());
930        assert_eq!(result.unwrap(), 0); // EOF signal
931    }
932
933    #[tokio::test]
934    async fn large_chunk_split_across_reads() {
935        let (client, server) = tokio::io::duplex(1 << 18);
936        let method = CipherMethod::Aes256Gcm;
937        let subkey = vec![0xBBu8; 32];
938
939        let payload = vec![0x44u8; 50_000];
940        let expected = payload.clone();
941        let write_subkey = subkey.clone();
942
943        let write_handle = tokio::spawn(async move {
944            let mut client_stream =
945                ShadowsocksAeadStream::new(client, method, write_subkey).unwrap();
946            client_stream.write_all(&payload).await.unwrap();
947            client_stream.flush().await.unwrap();
948            drop(client_stream);
949        });
950
951        let mut server_stream = ShadowsocksAeadStream::new(server, method, subkey).unwrap();
952
953        // Read in small chunks to exercise partial-read logic
954        let mut received = Vec::new();
955        let mut tmp = [0u8; 1024];
956        loop {
957            let n = server_stream.read(&mut tmp).await.unwrap();
958            if n == 0 {
959                break;
960            }
961            received.extend_from_slice(&tmp[..n]);
962        }
963        write_handle.await.unwrap();
964        assert_eq!(received, expected);
965    }
966}