github.com/database64128/shadowsocks-go@v1.7.0/ss2022/stream.go (about)

     1  package ss2022
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/cipher"
     6  	"crypto/rand"
     7  	"encoding/binary"
     8  	"errors"
     9  	"io"
    10  
    11  	"github.com/database64128/shadowsocks-go/zerocopy"
    12  )
    13  
    14  const MaxPayloadSize = 0xFFFF
    15  
    16  // ShadowStreamHeadroom is the headroom required by an encrypted Shadowsocks stream.
    17  //
    18  // Front is the size of an encrypted length chunk.
    19  // Rear is the size of an AEAD tag.
    20  var ShadowStreamHeadroom = zerocopy.Headroom{
    21  	Front: 2 + 16,
    22  	Rear:  16,
    23  }
    24  
    25  // ShadowStreamReaderInfo contains information about a [ShadowStreamReader].
    26  var ShadowStreamReaderInfo = zerocopy.ReaderInfo{
    27  	Headroom:                    ShadowStreamHeadroom,
    28  	MinPayloadBufferSizePerRead: MaxPayloadSize,
    29  }
    30  
    31  // ShadowStreamWriterInfo contains information about a [ShadowStreamWriter].
    32  var ShadowStreamWriterInfo = zerocopy.WriterInfo{
    33  	Headroom:               ShadowStreamHeadroom,
    34  	MaxPayloadSizePerWrite: MaxPayloadSize,
    35  }
    36  
    37  var (
    38  	ErrZeroLengthChunk = errors.New("length in length chunk is zero")
    39  	ErrFirstRead       = errors.New("failed to read fixed-length header in one read call")
    40  	ErrRepeatedSalt    = errors.New("detected replay: repeated salt")
    41  )
    42  
    43  var ErrUnsafeStreamPrefixMismatch = errors.New("unsafe stream prefix mismatch")
    44  
    45  // ShadowStreamServerReadWriter implements Shadowsocks stream server.
    46  type ShadowStreamServerReadWriter struct {
    47  	*ShadowStreamReader
    48  	*ShadowStreamWriter
    49  	rawRW                      zerocopy.DirectReadWriteCloser
    50  	cipherConfig               UserCipherConfig
    51  	requestSalt                []byte
    52  	unsafeResponseStreamPrefix []byte
    53  }
    54  
    55  // WriteZeroCopy implements the Writer WriteZeroCopy method.
    56  func (rw *ShadowStreamServerReadWriter) WriteZeroCopy(b []byte, payloadStart, payloadLen int) (int, error) {
    57  	if rw.ShadowStreamWriter == nil { // first write
    58  		urspLen := len(rw.unsafeResponseStreamPrefix)
    59  		saltLen := len(rw.cipherConfig.PSK)
    60  		responseHeaderStart := urspLen + saltLen
    61  		responseHeaderEnd := responseHeaderStart + TCPRequestFixedLengthHeaderLength + saltLen
    62  		payloadBufStart := responseHeaderEnd + 16
    63  		bufferLen := payloadBufStart + payloadLen + 16
    64  		hb := make([]byte, bufferLen)
    65  		ursp := hb[:urspLen]
    66  		salt := hb[urspLen:responseHeaderStart]
    67  		responseHeader := hb[responseHeaderStart:responseHeaderEnd]
    68  
    69  		// Write unsafe response stream prefix.
    70  		copy(ursp, rw.unsafeResponseStreamPrefix)
    71  
    72  		// Random salt.
    73  		_, err := rand.Read(salt)
    74  		if err != nil {
    75  			return 0, err
    76  		}
    77  
    78  		// Write response header.
    79  		WriteTCPResponseHeader(responseHeader, rw.requestSalt, uint16(payloadLen))
    80  
    81  		// Create AEAD cipher.
    82  		shadowStreamCipher, err := rw.cipherConfig.ShadowStreamCipher(salt)
    83  		if err != nil {
    84  			return 0, err
    85  		}
    86  
    87  		// Create writer.
    88  		rw.ShadowStreamWriter = &ShadowStreamWriter{
    89  			writer: rw.rawRW,
    90  			ssc:    shadowStreamCipher,
    91  		}
    92  
    93  		// Seal response header.
    94  		shadowStreamCipher.EncryptInPlace(responseHeader)
    95  
    96  		// Seal payload.
    97  		dst := hb[payloadBufStart:]
    98  		plaintext := b[payloadStart : payloadStart+payloadLen]
    99  		shadowStreamCipher.EncryptTo(dst, plaintext)
   100  
   101  		// Write out.
   102  		_, err = rw.rawRW.Write(hb)
   103  		if err != nil {
   104  			return 0, err
   105  		}
   106  
   107  		return payloadLen, nil
   108  	}
   109  
   110  	return rw.ShadowStreamWriter.WriteZeroCopy(b, payloadStart, payloadLen)
   111  }
   112  
   113  // CloseRead implements the ReadWriter CloseRead method.
   114  func (rw *ShadowStreamServerReadWriter) CloseRead() error {
   115  	return rw.rawRW.CloseRead()
   116  }
   117  
   118  // CloseWrite implements the ReadWriter CloseWrite method.
   119  func (rw *ShadowStreamServerReadWriter) CloseWrite() error {
   120  	return rw.rawRW.CloseWrite()
   121  }
   122  
   123  // Close implements the ReadWriter Close method.
   124  func (rw *ShadowStreamServerReadWriter) Close() error {
   125  	return rw.rawRW.Close()
   126  }
   127  
   128  // ShadowStreamClientReadWriter implements Shadowsocks stream client.
   129  type ShadowStreamClientReadWriter struct {
   130  	*ShadowStreamReader
   131  	*ShadowStreamWriter
   132  	rawRW                      zerocopy.DirectReadWriteCloser
   133  	cipherConfig               *ClientCipherConfig
   134  	requestSalt                []byte
   135  	unsafeResponseStreamPrefix []byte
   136  }
   137  
   138  // ReadZeroCopy implements the Reader ReadZeroCopy method.
   139  func (rw *ShadowStreamClientReadWriter) ReadZeroCopy(b []byte, payloadBufStart, payloadBufLen int) (int, error) {
   140  	if rw.ShadowStreamReader == nil { // first read
   141  		urspLen := len(rw.unsafeResponseStreamPrefix)
   142  		saltLen := len(rw.cipherConfig.PSK)
   143  		fixedLengthHeaderStart := urspLen + saltLen
   144  		bufferLen := fixedLengthHeaderStart + TCPRequestFixedLengthHeaderLength + saltLen + 16
   145  		hb := make([]byte, bufferLen)
   146  
   147  		// Read response header.
   148  		n, err := rw.rawRW.Read(hb)
   149  		if err != nil {
   150  			return 0, err
   151  		}
   152  		if n < bufferLen {
   153  			return 0, &HeaderError[int]{ErrFirstRead, bufferLen, n}
   154  		}
   155  
   156  		// Check unsafe response stream prefix.
   157  		ursp := hb[:urspLen]
   158  		if !bytes.Equal(ursp, rw.unsafeResponseStreamPrefix) {
   159  			return 0, &HeaderError[[]byte]{ErrUnsafeStreamPrefixMismatch, rw.unsafeResponseStreamPrefix, ursp}
   160  		}
   161  
   162  		// Derive key and create cipher.
   163  		salt := hb[urspLen:fixedLengthHeaderStart]
   164  		ciphertext := hb[fixedLengthHeaderStart:]
   165  		shadowStreamCipher, err := rw.cipherConfig.ShadowStreamCipher(salt)
   166  		if err != nil {
   167  			return 0, err
   168  		}
   169  
   170  		// Create reader.
   171  		rw.ShadowStreamReader = &ShadowStreamReader{
   172  			reader: rw.rawRW,
   173  			ssc:    shadowStreamCipher,
   174  		}
   175  
   176  		// AEAD open.
   177  		plaintext, err := shadowStreamCipher.DecryptInPlace(ciphertext)
   178  		if err != nil {
   179  			return 0, err
   180  		}
   181  
   182  		// Parse response header.
   183  		n, err = ParseTCPResponseHeader(plaintext, rw.requestSalt)
   184  		if err != nil {
   185  			return 0, err
   186  		}
   187  
   188  		payloadBuf := b[payloadBufStart : payloadBufStart+n+16]
   189  
   190  		// Read payload chunk.
   191  		_, err = io.ReadFull(rw.rawRW, payloadBuf)
   192  		if err != nil {
   193  			return 0, err
   194  		}
   195  
   196  		// AEAD open.
   197  		_, err = shadowStreamCipher.DecryptInPlace(payloadBuf)
   198  		if err != nil {
   199  			return 0, err
   200  		}
   201  
   202  		return n, nil
   203  	}
   204  
   205  	return rw.ShadowStreamReader.ReadZeroCopy(b, payloadBufStart, payloadBufLen)
   206  }
   207  
   208  // CloseRead implements the ReadWriter CloseRead method.
   209  func (rw *ShadowStreamClientReadWriter) CloseRead() error {
   210  	return rw.rawRW.CloseRead()
   211  }
   212  
   213  // CloseWrite implements the ReadWriter CloseWrite method.
   214  func (rw *ShadowStreamClientReadWriter) CloseWrite() error {
   215  	return rw.rawRW.CloseWrite()
   216  }
   217  
   218  // Close implements the ReadWriter Close method.
   219  func (rw *ShadowStreamClientReadWriter) Close() error {
   220  	return rw.rawRW.Close()
   221  }
   222  
   223  // ShadowStreamWriter wraps an io.WriteCloser and feeds an encrypted Shadowsocks stream to it.
   224  //
   225  // Wire format:
   226  //
   227  //	+------------------------+---------------------------+
   228  //	| encrypted length chunk |  encrypted payload chunk  |
   229  //	+------------------------+---------------------------+
   230  //	|  2B length + 16B tag   | variable length + 16B tag |
   231  //	+------------------------+---------------------------+
   232  type ShadowStreamWriter struct {
   233  	writer io.WriteCloser
   234  	ssc    *ShadowStreamCipher
   235  }
   236  
   237  // WriterInfo implements the Writer WriterInfo method.
   238  func (w *ShadowStreamWriter) WriterInfo() zerocopy.WriterInfo {
   239  	return ShadowStreamWriterInfo
   240  }
   241  
   242  // WriteZeroCopy implements the Writer WriteZeroCopy method.
   243  func (w *ShadowStreamWriter) WriteZeroCopy(b []byte, payloadStart, payloadLen int) (payloadWritten int, err error) {
   244  	overhead := w.ssc.Overhead()
   245  	lengthStart := payloadStart - overhead - 2
   246  	lengthBuf := b[lengthStart : lengthStart+2]
   247  	payloadBuf := b[payloadStart : payloadStart+payloadLen]
   248  	payloadTagEnd := payloadStart + payloadLen + overhead
   249  	chunksBuf := b[lengthStart:payloadTagEnd]
   250  
   251  	// Write length.
   252  	binary.BigEndian.PutUint16(lengthBuf, uint16(payloadLen))
   253  
   254  	// Seal length chunk.
   255  	w.ssc.EncryptInPlace(lengthBuf)
   256  
   257  	// Seal payload chunk.
   258  	w.ssc.EncryptInPlace(payloadBuf)
   259  
   260  	// Write to wrapped writer.
   261  	_, err = w.writer.Write(chunksBuf)
   262  	if err != nil {
   263  		return
   264  	}
   265  	payloadWritten = payloadLen
   266  	return
   267  }
   268  
   269  // ShadowStreamReader wraps an io.ReadCloser and reads from it as an encrypted Shadowsocks stream.
   270  type ShadowStreamReader struct {
   271  	reader io.ReadCloser
   272  	ssc    *ShadowStreamCipher
   273  }
   274  
   275  // ReaderInfo implements the Reader ReaderInfo method.
   276  func (r *ShadowStreamReader) ReaderInfo() zerocopy.ReaderInfo {
   277  	return ShadowStreamReaderInfo
   278  }
   279  
   280  // ReadZeroCopy implements the Reader ReadZeroCopy method.
   281  func (r *ShadowStreamReader) ReadZeroCopy(b []byte, payloadBufStart, payloadBufLen int) (payloadLen int, err error) {
   282  	overhead := r.ssc.Overhead()
   283  	sealedLengthChunkStart := payloadBufStart - overhead - 2
   284  	sealedLengthChunkBuf := b[sealedLengthChunkStart:payloadBufStart]
   285  
   286  	// Read sealed length chunk.
   287  	_, err = io.ReadFull(r.reader, sealedLengthChunkBuf)
   288  	if err != nil {
   289  		return
   290  	}
   291  
   292  	// Open sealed length chunk.
   293  	_, err = r.ssc.DecryptInPlace(sealedLengthChunkBuf)
   294  	if err != nil {
   295  		return
   296  	}
   297  
   298  	// Validate length.
   299  	payloadLen = int(binary.BigEndian.Uint16(sealedLengthChunkBuf))
   300  	if payloadLen == 0 {
   301  		err = ErrZeroLengthChunk
   302  		return
   303  	}
   304  
   305  	// Read sealed payload chunk.
   306  	sealedPayloadChunkBuf := b[payloadBufStart : payloadBufStart+payloadLen+overhead]
   307  	_, err = io.ReadFull(r.reader, sealedPayloadChunkBuf)
   308  	if err != nil {
   309  		payloadLen = 0
   310  		return
   311  	}
   312  
   313  	// Open sealed payload chunk.
   314  	_, err = r.ssc.DecryptInPlace(sealedPayloadChunkBuf)
   315  	if err != nil {
   316  		payloadLen = 0
   317  	}
   318  
   319  	return
   320  }
   321  
   322  // ShadowStreamCipher wraps an AEAD cipher and provides methods that transparently increments
   323  // the nonce after each AEAD operation.
   324  type ShadowStreamCipher struct {
   325  	aead  cipher.AEAD
   326  	nonce []byte
   327  }
   328  
   329  // NewShadowStreamCipher wraps the given AEAD cipher into a new ShadowStreamCipher.
   330  func NewShadowStreamCipher(aead cipher.AEAD) *ShadowStreamCipher {
   331  	return &ShadowStreamCipher{
   332  		aead:  aead,
   333  		nonce: make([]byte, aead.NonceSize()),
   334  	}
   335  }
   336  
   337  // Overhead returns the tag size of the AEAD cipher.
   338  func (c *ShadowStreamCipher) Overhead() int {
   339  	return c.aead.Overhead()
   340  }
   341  
   342  // EncryptInPlace encrypts and authenticates plaintext in-place.
   343  func (c *ShadowStreamCipher) EncryptInPlace(plaintext []byte) (ciphertext []byte) {
   344  	ciphertext = c.aead.Seal(plaintext[:0], c.nonce, plaintext, nil)
   345  	increment(c.nonce)
   346  	return
   347  }
   348  
   349  // EncryptTo encrypts and authenticates the plaintext and saves the ciphertext to dst.
   350  func (c *ShadowStreamCipher) EncryptTo(dst, plaintext []byte) (ciphertext []byte) {
   351  	ciphertext = c.aead.Seal(dst[:0], c.nonce, plaintext, nil)
   352  	increment(c.nonce)
   353  	return
   354  }
   355  
   356  // DecryptInplace decrypts and authenticates ciphertext in-place.
   357  func (c *ShadowStreamCipher) DecryptInPlace(ciphertext []byte) (plaintext []byte, err error) {
   358  	plaintext, err = c.aead.Open(ciphertext[:0], c.nonce, ciphertext, nil)
   359  	if err == nil {
   360  		increment(c.nonce)
   361  	}
   362  	return
   363  }
   364  
   365  // DecryptTo decrypts and authenticates the ciphertext and saves the plaintext to dst.
   366  func (c *ShadowStreamCipher) DecryptTo(dst, ciphertext []byte) (plaintext []byte, err error) {
   367  	plaintext, err = c.aead.Open(dst[:0], c.nonce, ciphertext, nil)
   368  	if err == nil {
   369  		increment(c.nonce)
   370  	}
   371  	return
   372  }
   373  
   374  // increment increments a little-endian unsigned integer b.
   375  func increment(b []byte) {
   376  	for i := range b {
   377  		b[i]++
   378  		if b[i] != 0 {
   379  			return
   380  		}
   381  	}
   382  }