mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-11-16 17:38:45 -05:00
291 lines
9.2 KiB
Go
291 lines
9.2 KiB
Go
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
|
|
}
|