1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-06-29 02:35:23 +00:00
v2fly/proxy/shadowsocks2022/encoding.go
2024-03-15 20:19:18 +00:00

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
}