package shadowsocks2022 import ( "bytes" "crypto/cipher" cryptoRand "crypto/rand" "encoding/binary" "io" "time" "github.com/lunixbochs/struc" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/crypto" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) type TCPRequest struct { keyDerivation KeyDerivation method Method c2sSalt RequestSalt c2sNonce crypto.BytesGenerator c2sAEAD cipher.AEAD s2cSalt RequestSalt s2cNonce crypto.BytesGenerator s2cAEAD cipher.AEAD s2cSaltAssert RequestSalt s2cInitialPayloadSize int } func (t *TCPRequest) EncodeTCPRequestHeader(effectivePsk []byte, eih [][]byte, address DestinationAddress, destPort int, initialPayload []byte, out *buf.Buffer, ) error { requestSalt := newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength()) { err := requestSalt.FillAllFrom(cryptoRand.Reader) if err != nil { return newError("failed to fill salt").Base(err) } } t.c2sSalt = requestSalt sessionKey := make([]byte, t.method.GetSessionSubKeyAndSaltLength()) { err := t.keyDerivation.GetSessionSubKey(effectivePsk, requestSalt.Bytes(), sessionKey) if err != nil { return newError("failed to get session sub key").Base(err) } } aead, err := t.method.GetStreamAEAD(sessionKey) if err != nil { return newError("failed to get stream AEAD").Base(err) } t.c2sAEAD = aead paddingLength := TCPMinPaddingLength if initialPayload == nil { initialPayload = []byte{} paddingLength += 1 + dice.RollWith(TCPMaxPaddingLength, cryptoRand.Reader) } variableLengthHeader := &TCPRequestHeader3VariableLength{ DestinationAddress: address, Contents: struct { PaddingLength uint16 `struc:"sizeof=Padding"` Padding []byte }(struct { PaddingLength uint16 Padding []byte }{ PaddingLength: uint16(paddingLength), Padding: make([]byte, paddingLength), }), } variableLengthHeaderBuffer := buf.New() defer variableLengthHeaderBuffer.Release() { err := addrParser.WriteAddressPort(variableLengthHeaderBuffer, address, net.Port(destPort)) if err != nil { return newError("failed to write address port").Base(err) } } { err := struc.Pack(variableLengthHeaderBuffer, &variableLengthHeader.Contents) if err != nil { return newError("failed to pack variable length header").Base(err) } } { _, err := variableLengthHeaderBuffer.Write(initialPayload) if err != nil { return newError("failed to write initial payload").Base(err) } } fixedLengthHeader := &TCPRequestHeader2FixedLength{ Type: TCPHeaderTypeClientToServerStream, Timestamp: uint64(time.Now().Unix()), HeaderLength: uint16(variableLengthHeaderBuffer.Len()), } fixedLengthHeaderBuffer := buf.New() defer fixedLengthHeaderBuffer.Release() { err := struc.Pack(fixedLengthHeaderBuffer, fixedLengthHeader) if err != nil { return newError("failed to pack fixed length header").Base(err) } } eihHeader := ExtensibleIdentityHeaders(newAESEIH(0)) if len(eih) != 0 { eihGenerator := newAESEIHGeneratorContainer(len(eih), effectivePsk, eih) eihHeaderGenerated, err := eihGenerator.GenerateEIH(t.keyDerivation, t.method, requestSalt.Bytes()) if err != nil { return newError("failed to construct EIH").Base(err) } eihHeader = eihHeaderGenerated } preSessionKeyHeader := &TCPRequestHeader1PreSessionKey{ Salt: requestSalt, EIH: eihHeader, } preSessionKeyHeaderBuffer := buf.New() defer preSessionKeyHeaderBuffer.Release() { err := struc.Pack(preSessionKeyHeaderBuffer, preSessionKeyHeader) if err != nil { return newError("failed to pack pre session key header").Base(err) } } requestNonce := crypto.GenerateInitialAEADNonce() t.c2sNonce = requestNonce { n, err := out.Write(preSessionKeyHeaderBuffer.BytesFrom(0)) if err != nil { return newError("failed to write pre session key header").Base(err) } if int32(n) != preSessionKeyHeaderBuffer.Len() { return newError("failed to write pre session key header") } } { fixedLengthEncrypted := out.Extend(fixedLengthHeaderBuffer.Len() + int32(aead.Overhead())) aead.Seal(fixedLengthEncrypted[:0], requestNonce(), fixedLengthHeaderBuffer.Bytes(), nil) } { variableLengthEncrypted := out.Extend(variableLengthHeaderBuffer.Len() + int32(aead.Overhead())) aead.Seal(variableLengthEncrypted[:0], requestNonce(), variableLengthHeaderBuffer.Bytes(), nil) } return nil } func (t *TCPRequest) DecodeTCPResponseHeader(effectivePsk []byte, in io.Reader) error { var preSessionKeyHeader TCPResponseHeader1PreSessionKey preSessionKeyHeader.Salt = newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength()) { err := struc.Unpack(in, &preSessionKeyHeader) if err != nil { return newError("failed to unpack pre session key header").Base(err) } } s2cSalt := preSessionKeyHeader.Salt.Bytes() t.s2cSalt = preSessionKeyHeader.Salt sessionKey := make([]byte, t.method.GetSessionSubKeyAndSaltLength()) { err := t.keyDerivation.GetSessionSubKey(effectivePsk, s2cSalt, sessionKey) if err != nil { return newError("failed to get session sub key").Base(err) } } aead, err := t.method.GetStreamAEAD(sessionKey) if err != nil { return newError("failed to get stream AEAD").Base(err) } t.s2cAEAD = aead fixedLengthHeaderEncryptedBuffer := buf.New() defer fixedLengthHeaderEncryptedBuffer.Release() { _, err := fixedLengthHeaderEncryptedBuffer.ReadFullFrom(in, 11+int32(t.method.GetSessionSubKeyAndSaltLength())+int32(aead.Overhead())) if err != nil { return newError("failed to read fixed length header encrypted").Base(err) } } s2cNonce := crypto.GenerateInitialAEADNonce() t.s2cNonce = s2cNonce fixedLengthHeaderDecryptedBuffer := buf.New() defer fixedLengthHeaderDecryptedBuffer.Release() { decryptionBuffer := fixedLengthHeaderDecryptedBuffer.Extend(11 + int32(t.method.GetSessionSubKeyAndSaltLength())) _, err = aead.Open(decryptionBuffer[:0], s2cNonce(), fixedLengthHeaderEncryptedBuffer.Bytes(), nil) if err != nil { return newError("failed to decrypt fixed length header").Base(err) } } var fixedLengthHeader TCPResponseHeader2FixedLength fixedLengthHeader.RequestSalt = newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength()) { err := struc.Unpack(bytes.NewReader(fixedLengthHeaderDecryptedBuffer.Bytes()), &fixedLengthHeader) if err != nil { return newError("failed to unpack fixed length header").Base(err) } } if fixedLengthHeader.Type != TCPHeaderTypeServerToClientStream { return newError("unexpected TCP header type") } timeDifference := int64(fixedLengthHeader.Timestamp) - time.Now().Unix() if timeDifference < -30 || timeDifference > 30 { return newError("timestamp is too far away, timeDifference = ", timeDifference) } t.s2cSaltAssert = fixedLengthHeader.RequestSalt t.s2cInitialPayloadSize = int(fixedLengthHeader.InitialPayloadLength) return nil } func (t *TCPRequest) CheckC2SConnectionConstraint() error { if !bytes.Equal(t.c2sSalt.Bytes(), t.s2cSaltAssert.Bytes()) { return newError("c2s salt not equal to s2c salt assert") } return nil } func (t *TCPRequest) CreateClientS2CReader(in io.Reader, initialPayload *buf.Buffer) (buf.Reader, error) { AEADAuthenticator := &crypto.AEADAuthenticator{ AEAD: t.s2cAEAD, NonceGenerator: t.s2cNonce, AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } initialPayloadEncrypted := buf.NewWithSize(65535) defer initialPayloadEncrypted.Release() initialPayloadEncryptedBytes := initialPayloadEncrypted.Extend(int32(t.s2cAEAD.Overhead()) + int32(t.s2cInitialPayloadSize)) _, err := io.ReadFull(in, initialPayloadEncryptedBytes) if err != nil { return nil, newError("failed to read initial payload").Base(err) } initialPayloadBytes := initialPayload.Extend(int32(t.s2cInitialPayloadSize)) _, err = t.s2cAEAD.Open(initialPayloadBytes[:0], t.s2cNonce(), initialPayloadEncryptedBytes, nil) if err != nil { return nil, newError("failed to decrypt initial payload").Base(err) } return crypto.NewAuthenticationReader(AEADAuthenticator, &AEADChunkSizeParser{ Auth: AEADAuthenticator, }, in, protocol.TransferTypeStream, nil), nil } func (t *TCPRequest) CreateClientC2SWriter(writer io.Writer) buf.Writer { AEADAuthenticator := &crypto.AEADAuthenticator{ AEAD: t.c2sAEAD, NonceGenerator: t.c2sNonce, AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser := &crypto.AEADChunkSizeParser{ Auth: AEADAuthenticator, } return crypto.NewAuthenticationWriter(AEADAuthenticator, sizeParser, writer, protocol.TransferTypeStream, nil) } type AEADChunkSizeParser struct { Auth *crypto.AEADAuthenticator } func (p *AEADChunkSizeParser) HasConstantOffset() uint16 { return uint16(p.Auth.Overhead()) } func (p *AEADChunkSizeParser) SizeBytes() int32 { return 2 + int32(p.Auth.Overhead()) } func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte { binary.BigEndian.PutUint16(b, size-uint16(p.Auth.Overhead())) b, err := p.Auth.Seal(b[:0], b[:2]) common.Must(err) return b } func (p *AEADChunkSizeParser) Decode(b []byte) (uint16, error) { b, err := p.Auth.Open(b[:0], b) if err != nil { return 0, err } return binary.BigEndian.Uint16(b), nil }