Improve: refactor ssr and fix #995 (#1189)

Co-authored-by: goomada <madao@DESKTOP-IOEBS0C.localdomain>
This commit is contained in:
goomadao
2021-02-15 14:32:03 +08:00
committed by GitHub
parent d48cfecf60
commit 9eb98e399d
23 changed files with 1667 additions and 1865 deletions

View File

@ -1,310 +1,18 @@
package protocol
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/binary"
"math/rand"
"strconv"
"strings"
"time"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/ssr/tools"
"github.com/Dreamacro/go-shadowsocks2/core"
)
type authAES128 struct {
*Base
*recvInfo
*authData
hasSentHeader bool
packID uint32
userKey []byte
uid [4]byte
salt string
hmac hmacMethod
hashDigest hashDigestMethod
}
import "github.com/Dreamacro/clash/component/ssr/tools"
func init() {
register("auth_aes128_md5", newAuthAES128MD5)
register("auth_aes128_md5", newAuthAES128MD5, 9)
}
func newAuthAES128MD5(b *Base) Protocol {
return &authAES128{
Base: b,
authData: &authData{},
salt: "auth_aes128_md5",
hmac: tools.HmacMD5,
hashDigest: tools.MD5Sum,
a := &authAES128{
Base: b,
authData: &authData{},
authAES128Function: &authAES128Function{salt: "auth_aes128_md5", hmac: tools.HmacMD5, hashDigest: tools.MD5Sum},
userData: &userData{},
}
}
func (a *authAES128) initForConn(iv []byte) Protocol {
return &authAES128{
Base: &Base{
IV: iv,
Key: a.Key,
TCPMss: a.TCPMss,
Overhead: a.Overhead,
Param: a.Param,
},
recvInfo: &recvInfo{recvID: 1, buffer: new(bytes.Buffer)},
authData: a.authData,
packID: 1,
salt: a.salt,
hmac: a.hmac,
hashDigest: a.hashDigest,
}
}
func (a *authAES128) GetProtocolOverhead() int {
return 9
}
func (a *authAES128) SetOverhead(overhead int) {
a.Overhead = overhead
}
func (a *authAES128) Decode(b []byte) ([]byte, int, error) {
a.buffer.Reset()
bSize := len(b)
readSize := 0
key := pool.Get(len(a.userKey) + 4)
defer pool.Put(key)
copy(key, a.userKey)
for bSize > 4 {
binary.LittleEndian.PutUint32(key[len(key)-4:], a.recvID)
h := a.hmac(key, b[:2])
if !bytes.Equal(h[:2], b[2:4]) {
return nil, 0, errAuthAES128IncorrectMAC
}
length := int(binary.LittleEndian.Uint16(b[:2]))
if length >= 8192 || length < 8 {
return nil, 0, errAuthAES128DataLengthError
}
if length > bSize {
break
}
h = a.hmac(key, b[:length-4])
if !bytes.Equal(h[:4], b[length-4:length]) {
return nil, 0, errAuthAES128IncorrectChecksum
}
a.recvID++
pos := int(b[4])
if pos < 255 {
pos += 4
} else {
pos = int(binary.LittleEndian.Uint16(b[5:7])) + 4
}
if pos > length-4 {
return nil, 0, errAuthAES128PositionTooLarge
}
a.buffer.Write(b[pos : length-4])
b = b[length:]
bSize -= length
readSize += length
}
return a.buffer.Bytes(), readSize, nil
}
func (a *authAES128) Encode(b []byte) ([]byte, error) {
a.buffer.Reset()
bSize := len(b)
offset := 0
if bSize > 0 && !a.hasSentHeader {
authSize := bSize
if authSize > 1200 {
authSize = 1200
}
a.hasSentHeader = true
a.buffer.Write(a.packAuthData(b[:authSize]))
bSize -= authSize
offset += authSize
}
const blockSize = 4096
for bSize > blockSize {
packSize, randSize := a.packedDataSize(b[offset : offset+blockSize])
pack := pool.Get(packSize)
a.packData(b[offset:offset+blockSize], pack, randSize)
a.buffer.Write(pack)
pool.Put(pack)
bSize -= blockSize
offset += blockSize
}
if bSize > 0 {
packSize, randSize := a.packedDataSize(b[offset:])
pack := pool.Get(packSize)
a.packData(b[offset:], pack, randSize)
a.buffer.Write(pack)
pool.Put(pack)
}
return a.buffer.Bytes(), nil
}
func (a *authAES128) DecodePacket(b []byte) ([]byte, int, error) {
bSize := len(b)
h := a.hmac(a.Key, b[:bSize-4])
if !bytes.Equal(h[:4], b[bSize-4:]) {
return nil, 0, errAuthAES128IncorrectMAC
}
return b[:bSize-4], bSize - 4, nil
}
func (a *authAES128) EncodePacket(b []byte) ([]byte, error) {
a.initUserKeyAndID()
var buf bytes.Buffer
buf.Write(b)
buf.Write(a.uid[:])
h := a.hmac(a.userKey, buf.Bytes())
buf.Write(h[:4])
return buf.Bytes(), nil
}
func (a *authAES128) initUserKeyAndID() {
if a.userKey == nil {
params := strings.Split(a.Param, ":")
if len(params) >= 2 {
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
binary.LittleEndian.PutUint32(a.uid[:], uint32(userID))
a.userKey = a.hashDigest([]byte(params[1]))
}
}
if a.userKey == nil {
rand.Read(a.uid[:])
a.userKey = make([]byte, len(a.Key))
copy(a.userKey, a.Key)
}
}
}
func (a *authAES128) packedDataSize(data []byte) (packSize, randSize int) {
dataSize := len(data)
randSize = 1
if dataSize <= 1200 {
if a.packID > 4 {
randSize += rand.Intn(32)
} else {
if dataSize > 900 {
randSize += rand.Intn(128)
} else {
randSize += rand.Intn(512)
}
}
}
packSize = randSize + dataSize + 8
return
}
func (a *authAES128) packData(data, ret []byte, randSize int) {
dataSize := len(data)
retSize := len(ret)
// 0~1, ret_size
binary.LittleEndian.PutUint16(ret[0:], uint16(retSize&0xFFFF))
// 2~3, hmac
key := pool.Get(len(a.userKey) + 4)
defer pool.Put(key)
copy(key, a.userKey)
binary.LittleEndian.PutUint32(key[len(key)-4:], a.packID)
h := a.hmac(key, ret[:2])
copy(ret[2:4], h[:2])
// 4~rand_size+4, rand number
rand.Read(ret[4 : 4+randSize])
// 4, rand_size
if randSize < 128 {
ret[4] = byte(randSize & 0xFF)
} else {
// 4, magic number 0xFF
ret[4] = 0xFF
// 5~6, rand_size
binary.LittleEndian.PutUint16(ret[5:], uint16(randSize&0xFFFF))
}
// rand_size+4~ret_size-4, data
if dataSize > 0 {
copy(ret[randSize+4:], data)
}
a.packID++
h = a.hmac(key, ret[:retSize-4])
copy(ret[retSize-4:], h[:4])
}
func (a *authAES128) packAuthData(data []byte) (ret []byte) {
dataSize := len(data)
var randSize int
if dataSize > 400 {
randSize = rand.Intn(512)
} else {
randSize = rand.Intn(1024)
}
dataOffset := randSize + 16 + 4 + 4 + 7
retSize := dataOffset + dataSize + 4
ret = make([]byte, retSize)
encrypt := make([]byte, 24)
key := make([]byte, len(a.IV)+len(a.Key))
copy(key, a.IV)
copy(key[len(a.IV):], a.Key)
rand.Read(ret[dataOffset-randSize:])
a.mutex.Lock()
defer a.mutex.Unlock()
a.connectionID++
if a.connectionID > 0xFF000000 {
a.clientID = nil
}
if len(a.clientID) == 0 {
a.clientID = make([]byte, 8)
rand.Read(a.clientID)
b := make([]byte, 4)
rand.Read(b)
a.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
}
copy(encrypt[4:], a.clientID)
binary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)
now := time.Now().Unix()
binary.LittleEndian.PutUint32(encrypt[:4], uint32(now))
binary.LittleEndian.PutUint16(encrypt[12:], uint16(retSize&0xFFFF))
binary.LittleEndian.PutUint16(encrypt[14:], uint16(randSize&0xFFFF))
a.initUserKeyAndID()
aesCipherKey := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+a.salt, 16)
block, err := aes.NewCipher(aesCipherKey)
if err != nil {
return nil
}
encryptData := make([]byte, 16)
iv := make([]byte, aes.BlockSize)
cbc := cipher.NewCBCEncrypter(block, iv)
cbc.CryptBlocks(encryptData, encrypt[:16])
copy(encrypt[:4], a.uid[:])
copy(encrypt[4:4+16], encryptData)
h := a.hmac(key, encrypt[:20])
copy(encrypt[20:], h[:4])
rand.Read(ret[:1])
h = a.hmac(key, ret[:1])
copy(ret[1:], h[:7-1])
copy(ret[7:], encrypt)
copy(ret[dataOffset:], data)
h = a.hmac(a.userKey, ret[:retSize-4])
copy(ret[retSize-4:], h[:4])
return
a.initUserData()
return a
}

View File

@ -2,21 +2,274 @@ package protocol
import (
"bytes"
"encoding/binary"
"math"
"math/rand"
"net"
"strconv"
"strings"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/ssr/tools"
"github.com/Dreamacro/clash/log"
)
type hmacMethod func(key, data []byte) []byte
type hashDigestMethod func([]byte) []byte
func init() {
register("auth_aes128_sha1", newAuthAES128SHA1)
register("auth_aes128_sha1", newAuthAES128SHA1, 9)
}
type authAES128Function struct {
salt string
hmac hmacMethod
hashDigest hashDigestMethod
}
type authAES128 struct {
*Base
*authData
*authAES128Function
*userData
iv []byte
hasSentHeader bool
rawTrans bool
packID uint32
recvID uint32
}
func newAuthAES128SHA1(b *Base) Protocol {
return &authAES128{
Base: b,
recvInfo: &recvInfo{buffer: new(bytes.Buffer)},
authData: &authData{},
salt: "auth_aes128_sha1",
hmac: tools.HmacSHA1,
hashDigest: tools.SHA1Sum,
a := &authAES128{
Base: b,
authData: &authData{},
authAES128Function: &authAES128Function{salt: "auth_aes128_sha1", hmac: tools.HmacSHA1, hashDigest: tools.SHA1Sum},
userData: &userData{},
}
a.initUserData()
return a
}
func (a *authAES128) initUserData() {
params := strings.Split(a.Param, ":")
if len(params) > 1 {
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
binary.LittleEndian.PutUint32(a.userID[:], uint32(userID))
a.userKey = a.hashDigest([]byte(params[1]))
} else {
log.Warnln("Wrong protocol-param for %s, only digits are expected before ':'", a.salt)
}
}
if len(a.userKey) == 0 {
a.userKey = a.Key
rand.Read(a.userID[:])
}
}
func (a *authAES128) StreamConn(c net.Conn, iv []byte) net.Conn {
p := &authAES128{
Base: a.Base,
authData: a.next(),
authAES128Function: a.authAES128Function,
userData: a.userData,
packID: 1,
recvID: 1,
}
p.iv = iv
return &Conn{Conn: c, Protocol: p}
}
func (a *authAES128) PacketConn(c net.PacketConn) net.PacketConn {
p := &authAES128{
Base: a.Base,
authAES128Function: a.authAES128Function,
userData: a.userData,
}
return &PacketConn{PacketConn: c, Protocol: p}
}
func (a *authAES128) Decode(dst, src *bytes.Buffer) error {
if a.rawTrans {
dst.ReadFrom(src)
return nil
}
for src.Len() > 4 {
macKey := pool.Get(len(a.userKey) + 4)
defer pool.Put(macKey)
copy(macKey, a.userKey)
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.recvID)
if !bytes.Equal(a.hmac(macKey, src.Bytes()[:2])[:2], src.Bytes()[2:4]) {
src.Reset()
return errAuthAES128MACError
}
length := int(binary.LittleEndian.Uint16(src.Bytes()[:2]))
if length >= 8192 || length < 7 {
a.rawTrans = true
src.Reset()
return errAuthAES128LengthError
}
if length > src.Len() {
break
}
if !bytes.Equal(a.hmac(macKey, src.Bytes()[:length-4])[:4], src.Bytes()[length-4:length]) {
a.rawTrans = true
src.Reset()
return errAuthAES128ChksumError
}
a.recvID++
pos := int(src.Bytes()[4])
if pos < 255 {
pos += 4
} else {
pos = int(binary.LittleEndian.Uint16(src.Bytes()[5:7])) + 4
}
dst.Write(src.Bytes()[pos : length-4])
src.Next(length)
}
return nil
}
func (a *authAES128) Encode(buf *bytes.Buffer, b []byte) error {
fullDataLength := len(b)
if !a.hasSentHeader {
dataLength := getDataLength(b)
a.packAuthData(buf, b[:dataLength])
b = b[dataLength:]
a.hasSentHeader = true
}
for len(b) > 8100 {
a.packData(buf, b[:8100], fullDataLength)
b = b[8100:]
}
if len(b) > 0 {
a.packData(buf, b, fullDataLength)
}
return nil
}
func (a *authAES128) DecodePacket(b []byte) ([]byte, error) {
if !bytes.Equal(a.hmac(a.Key, b[:len(b)-4])[:4], b[len(b)-4:]) {
return nil, errAuthAES128ChksumError
}
return b[:len(b)-4], nil
}
func (a *authAES128) EncodePacket(buf *bytes.Buffer, b []byte) error {
buf.Write(b)
buf.Write(a.userID[:])
buf.Write(a.hmac(a.userKey, buf.Bytes())[:4])
return nil
}
func (a *authAES128) packData(poolBuf *bytes.Buffer, data []byte, fullDataLength int) {
dataLength := len(data)
randDataLength := a.getRandDataLengthForPackData(dataLength, fullDataLength)
/*
2: uint16 LittleEndian packedDataLength
2: hmac of packedDataLength
3: maxRandDataLengthPrefix (min:1)
4: hmac of packedData except the last 4 bytes
*/
packedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4
if randDataLength < 128 {
packedDataLength -= 2
}
macKey := pool.Get(len(a.userKey) + 4)
defer pool.Put(macKey)
copy(macKey, a.userKey)
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.packID)
a.packID++
binary.Write(poolBuf, binary.LittleEndian, uint16(packedDataLength))
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[poolBuf.Len()-2:])[:2])
a.packRandData(poolBuf, randDataLength)
poolBuf.Write(data)
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:])[:4])
}
func trapezoidRandom(max int, d float64) int {
base := rand.Float64()
if d-0 > 1e-6 {
a := 1 - d
base = (math.Sqrt(a*a+4*d*base) - a) / (2 * d)
}
return int(base * float64(max))
}
func (a *authAES128) getRandDataLengthForPackData(dataLength, fullDataLength int) int {
if fullDataLength >= 32*1024-a.Overhead {
return 0
}
// 1460: tcp_mss
revLength := 1460 - dataLength - 9
if revLength == 0 {
return 0
}
if revLength < 0 {
if revLength > -1460 {
return trapezoidRandom(revLength+1460, -0.3)
}
return rand.Intn(32)
}
if dataLength > 900 {
return rand.Intn(revLength)
}
return trapezoidRandom(revLength, -0.3)
}
func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) {
if len(data) == 0 {
return
}
dataLength := len(data)
randDataLength := a.getRandDataLengthForPackAuthData(dataLength)
/*
7: checkHead(1) and hmac of checkHead(6)
4: userID
16: encrypted data of authdata(12), uint16 BigEndian packedDataLength(2) and uint16 BigEndian randDataLength(2)
4: hmac of userID and encrypted data
4: hmac of packedAuthData except the last 4 bytes
*/
packedAuthDataLength := 7 + 4 + 16 + 4 + randDataLength + dataLength + 4
macKey := pool.Get(len(a.iv) + len(a.Key))
defer pool.Put(macKey)
copy(macKey, a.iv)
copy(macKey[len(a.iv):], a.Key)
poolBuf.WriteByte(byte(rand.Intn(256)))
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes())[:6])
poolBuf.Write(a.userID[:])
err := a.authData.putEncryptedData(poolBuf, a.userKey, [2]int{packedAuthDataLength, randDataLength}, a.salt)
if err != nil {
poolBuf.Reset()
return
}
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[7:])[:4])
tools.AppendRandBytes(poolBuf, randDataLength)
poolBuf.Write(data)
poolBuf.Write(a.hmac(a.userKey, poolBuf.Bytes())[:4])
}
func (a *authAES128) getRandDataLengthForPackAuthData(size int) int {
if size > 400 {
return rand.Intn(512)
}
return rand.Intn(1024)
}
func (a *authAES128) packRandData(poolBuf *bytes.Buffer, size int) {
if size < 128 {
poolBuf.WriteByte(byte(size + 1))
tools.AppendRandBytes(poolBuf, size)
return
}
poolBuf.WriteByte(255)
binary.Write(poolBuf, binary.LittleEndian, uint16(size+3))
tools.AppendRandBytes(poolBuf, size)
}

View File

@ -2,430 +2,308 @@ package protocol
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rc4"
"encoding/base64"
"encoding/binary"
"math/rand"
"net"
"strconv"
"strings"
"time"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/ssr/tools"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/go-shadowsocks2/core"
)
type authChain struct {
*Base
*recvInfo
*authData
randomClient shift128PlusContext
randomServer shift128PlusContext
enc cipher.Stream
dec cipher.Stream
headerSent bool
lastClientHash []byte
lastServerHash []byte
userKey []byte
uid [4]byte
salt string
hmac hmacMethod
hashDigest hashDigestMethod
rnd rndMethod
dataSizeList []int
dataSizeList2 []int
chunkID uint32
func init() {
register("auth_chain_a", newAuthChainA, 4)
}
func init() {
register("auth_chain_a", newAuthChainA)
type randDataLengthMethod func(int, []byte, *tools.XorShift128Plus) int
type authChainA struct {
*Base
*authData
*userData
iv []byte
salt string
hasSentHeader bool
rawTrans bool
lastClientHash []byte
lastServerHash []byte
encrypter cipher.Stream
decrypter cipher.Stream
randomClient tools.XorShift128Plus
randomServer tools.XorShift128Plus
randDataLength randDataLengthMethod
packID uint32
recvID uint32
}
func newAuthChainA(b *Base) Protocol {
return &authChain{
Base: b,
authData: &authData{},
salt: "auth_chain_a",
hmac: tools.HmacMD5,
hashDigest: tools.SHA1Sum,
rnd: authChainAGetRandLen,
a := &authChainA{
Base: b,
authData: &authData{},
userData: &userData{},
salt: "auth_chain_a",
}
a.initUserData()
return a
}
func (a *authChain) initForConn(iv []byte) Protocol {
r := &authChain{
Base: &Base{
IV: iv,
Key: a.Key,
TCPMss: a.TCPMss,
Overhead: a.Overhead,
Param: a.Param,
},
recvInfo: &recvInfo{recvID: 1, buffer: new(bytes.Buffer)},
authData: a.authData,
salt: a.salt,
hmac: a.hmac,
hashDigest: a.hashDigest,
rnd: a.rnd,
}
if r.salt == "auth_chain_b" {
initDataSize(r)
}
return r
}
func (a *authChain) GetProtocolOverhead() int {
return 4
}
func (a *authChain) SetOverhead(overhead int) {
a.Overhead = overhead
}
func (a *authChain) Decode(b []byte) ([]byte, int, error) {
a.buffer.Reset()
key := pool.Get(len(a.userKey) + 4)
defer pool.Put(key)
readSize := 0
copy(key, a.userKey)
for len(b) > 4 {
binary.LittleEndian.PutUint32(key[len(a.userKey):], a.recvID)
dataLen := (int)((uint(b[1]^a.lastServerHash[15]) << 8) + uint(b[0]^a.lastServerHash[14]))
randLen := a.getServerRandLen(dataLen, a.Overhead)
length := randLen + dataLen
if length >= 4096 {
return nil, 0, errAuthChainDataLengthError
func (a *authChainA) initUserData() {
params := strings.Split(a.Param, ":")
if len(params) > 1 {
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
binary.LittleEndian.PutUint32(a.userID[:], uint32(userID))
a.userKey = []byte(params[1])
} else {
log.Warnln("Wrong protocol-param for %s, only digits are expected before ':'", a.salt)
}
length += 4
if length > len(b) {
}
if len(a.userKey) == 0 {
a.userKey = a.Key
rand.Read(a.userID[:])
}
}
func (a *authChainA) StreamConn(c net.Conn, iv []byte) net.Conn {
p := &authChainA{
Base: a.Base,
authData: a.next(),
userData: a.userData,
salt: a.salt,
packID: 1,
recvID: 1,
}
p.iv = iv
p.randDataLength = p.getRandLength
return &Conn{Conn: c, Protocol: p}
}
func (a *authChainA) PacketConn(c net.PacketConn) net.PacketConn {
p := &authChainA{
Base: a.Base,
salt: a.salt,
userData: a.userData,
}
return &PacketConn{PacketConn: c, Protocol: p}
}
func (a *authChainA) Decode(dst, src *bytes.Buffer) error {
if a.rawTrans {
dst.ReadFrom(src)
return nil
}
for src.Len() > 4 {
macKey := pool.Get(len(a.userKey) + 4)
defer pool.Put(macKey)
copy(macKey, a.userKey)
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.recvID)
dataLength := int(binary.LittleEndian.Uint16(src.Bytes()[:2]) ^ binary.LittleEndian.Uint16(a.lastServerHash[14:16]))
randDataLength := a.randDataLength(dataLength, a.lastServerHash, &a.randomServer)
length := dataLength + randDataLength
if length >= 4096 {
a.rawTrans = true
src.Reset()
return errAuthChainLengthError
}
if 4+length > src.Len() {
break
}
hash := a.hmac(key, b[:length-2])
if !bytes.Equal(hash[:2], b[length-2:length]) {
return nil, 0, errAuthChainHMACError
serverHash := tools.HmacMD5(macKey, src.Bytes()[:length+2])
if !bytes.Equal(serverHash[:2], src.Bytes()[length+2:length+4]) {
a.rawTrans = true
src.Reset()
return errAuthChainChksumError
}
var dataPos int
if dataLen > 0 && randLen > 0 {
dataPos = 2 + getRandStartPos(&a.randomServer, randLen)
} else {
dataPos = 2
a.lastServerHash = serverHash
pos := 2
if dataLength > 0 && randDataLength > 0 {
pos += getRandStartPos(randDataLength, &a.randomServer)
}
d := pool.Get(dataLen)
a.dec.XORKeyStream(d, b[dataPos:dataPos+dataLen])
a.buffer.Write(d)
pool.Put(d)
wantedData := src.Bytes()[pos : pos+dataLength]
a.decrypter.XORKeyStream(wantedData, wantedData)
if a.recvID == 1 {
a.TCPMss = int(binary.LittleEndian.Uint16(a.buffer.Next(2)))
dst.Write(wantedData[2:])
} else {
dst.Write(wantedData)
}
a.lastServerHash = hash
a.recvID++
b = b[length:]
readSize += length
src.Next(length + 4)
}
return a.buffer.Bytes(), readSize, nil
return nil
}
func (a *authChain) Encode(b []byte) ([]byte, error) {
a.buffer.Reset()
bSize := len(b)
offset := 0
if bSize > 0 && !a.headerSent {
headSize := 1200
if headSize > bSize {
headSize = bSize
}
a.buffer.Write(a.packAuthData(b[:headSize]))
offset += headSize
bSize -= headSize
a.headerSent = true
func (a *authChainA) Encode(buf *bytes.Buffer, b []byte) error {
if !a.hasSentHeader {
dataLength := getDataLength(b)
a.packAuthData(buf, b[:dataLength])
b = b[dataLength:]
a.hasSentHeader = true
}
var unitSize = a.TCPMss - a.Overhead
for bSize > unitSize {
dataLen, randLength := a.packedDataLen(b[offset : offset+unitSize])
d := pool.Get(dataLen)
a.packData(d, b[offset:offset+unitSize], randLength)
a.buffer.Write(d)
pool.Put(d)
bSize -= unitSize
offset += unitSize
for len(b) > 2800 {
a.packData(buf, b[:2800])
b = b[2800:]
}
if bSize > 0 {
dataLen, randLength := a.packedDataLen(b[offset:])
d := pool.Get(dataLen)
a.packData(d, b[offset:], randLength)
a.buffer.Write(d)
pool.Put(d)
if len(b) > 0 {
a.packData(buf, b)
}
return a.buffer.Bytes(), nil
return nil
}
func (a *authChain) DecodePacket(b []byte) ([]byte, int, error) {
bSize := len(b)
if bSize < 9 {
return nil, 0, errAuthChainDataLengthError
func (a *authChainA) DecodePacket(b []byte) ([]byte, error) {
if len(b) < 9 {
return nil, errAuthChainLengthError
}
h := a.hmac(a.userKey, b[:bSize-1])
if h[0] != b[bSize-1] {
return nil, 0, errAuthChainHMACError
if !bytes.Equal(tools.HmacMD5(a.userKey, b[:len(b)-1])[:1], b[len(b)-1:]) {
return nil, errAuthChainChksumError
}
hash := a.hmac(a.Key, b[bSize-8:bSize-1])
cipherKey := a.getRC4CipherKey(hash)
dec, _ := rc4.NewCipher(cipherKey)
randLength := udpGetRandLen(&a.randomServer, hash)
bSize -= 8 + randLength
dec.XORKeyStream(b, b[:bSize])
return b, bSize, nil
md5Data := tools.HmacMD5(a.Key, b[len(b)-8:len(b)-1])
randDataLength := udpGetRandLength(md5Data, &a.randomServer)
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(md5Data), 16)
rc4Cipher, err := rc4.NewCipher(key)
if err != nil {
return nil, err
}
wantedData := b[:len(b)-8-randDataLength]
rc4Cipher.XORKeyStream(wantedData, wantedData)
return wantedData, nil
}
func (a *authChain) EncodePacket(b []byte) ([]byte, error) {
a.initUserKeyAndID()
func (a *authChainA) EncodePacket(buf *bytes.Buffer, b []byte) error {
authData := pool.Get(3)
defer pool.Put(authData)
rand.Read(authData)
hash := a.hmac(a.Key, authData)
uid := pool.Get(4)
defer pool.Put(uid)
for i := 0; i < 4; i++ {
uid[i] = a.uid[i] ^ hash[i]
}
cipherKey := a.getRC4CipherKey(hash)
enc, _ := rc4.NewCipher(cipherKey)
var buf bytes.Buffer
enc.XORKeyStream(b, b)
md5Data := tools.HmacMD5(a.Key, authData)
randDataLength := udpGetRandLength(md5Data, &a.randomClient)
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(md5Data), 16)
rc4Cipher, err := rc4.NewCipher(key)
if err != nil {
return err
}
rc4Cipher.XORKeyStream(b, b)
buf.Write(b)
randLength := udpGetRandLen(&a.randomClient, hash)
randBytes := pool.Get(randLength)
defer pool.Put(randBytes)
buf.Write(randBytes)
tools.AppendRandBytes(buf, randDataLength)
buf.Write(authData)
buf.Write(uid)
h := a.hmac(a.userKey, buf.Bytes())
buf.Write(h[:1])
return buf.Bytes(), nil
binary.Write(buf, binary.LittleEndian, binary.LittleEndian.Uint32(a.userID[:])^binary.LittleEndian.Uint32(md5Data[:4]))
buf.Write(tools.HmacMD5(a.userKey, buf.Bytes())[:1])
return nil
}
func (a *authChain) getRC4CipherKey(hash []byte) []byte {
base64UserKey := base64.StdEncoding.EncodeToString(a.userKey)
return a.calcRC4CipherKey(hash, base64UserKey)
}
func (a *authChainA) packAuthData(poolBuf *bytes.Buffer, data []byte) {
/*
dataLength := len(data)
12: checkHead(4) and hmac of checkHead(8)
4: uint32 LittleEndian uid (uid = userID ^ last client hash)
16: encrypted data of authdata(12), uint16 LittleEndian overhead(2) and uint16 LittleEndian number zero(2)
4: last server hash(4)
packedAuthDataLength := 12 + 4 + 16 + 4 + dataLength
*/
func (a *authChain) calcRC4CipherKey(hash []byte, base64UserKey string) []byte {
password := pool.Get(len(base64UserKey) + base64.StdEncoding.EncodedLen(16))
defer pool.Put(password)
copy(password, base64UserKey)
base64.StdEncoding.Encode(password[len(base64UserKey):], hash[:16])
return core.Kdf(string(password), 16)
}
macKey := pool.Get(len(a.iv) + len(a.Key))
defer pool.Put(macKey)
copy(macKey, a.iv)
copy(macKey[len(a.iv):], a.Key)
func (a *authChain) initUserKeyAndID() {
if a.userKey == nil {
params := strings.Split(a.Param, ":")
if len(params) >= 2 {
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
binary.LittleEndian.PutUint32(a.uid[:], uint32(userID))
a.userKey = []byte(params[1])
}
}
if a.userKey == nil {
rand.Read(a.uid[:])
a.userKey = make([]byte, len(a.Key))
copy(a.userKey, a.Key)
}
// check head
tools.AppendRandBytes(poolBuf, 4)
a.lastClientHash = tools.HmacMD5(macKey, poolBuf.Bytes())
a.initRC4Cipher()
poolBuf.Write(a.lastClientHash[:8])
// uid
binary.Write(poolBuf, binary.LittleEndian, binary.LittleEndian.Uint32(a.userID[:])^binary.LittleEndian.Uint32(a.lastClientHash[8:12]))
// encrypted data
err := a.putEncryptedData(poolBuf, a.userKey, [2]int{a.Overhead, 0}, a.salt)
if err != nil {
poolBuf.Reset()
return
}
// last server hash
a.lastServerHash = tools.HmacMD5(a.userKey, poolBuf.Bytes()[12:])
poolBuf.Write(a.lastServerHash[:4])
// packed data
a.packData(poolBuf, data)
}
func (a *authChain) getClientRandLen(dataLength int, overhead int) int {
return a.rnd(dataLength, &a.randomClient, a.lastClientHash, a.dataSizeList, a.dataSizeList2, overhead)
func (a *authChainA) packData(poolBuf *bytes.Buffer, data []byte) {
a.encrypter.XORKeyStream(data, data)
macKey := pool.Get(len(a.userKey) + 4)
defer pool.Put(macKey)
copy(macKey, a.userKey)
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.packID)
a.packID++
length := uint16(len(data)) ^ binary.LittleEndian.Uint16(a.lastClientHash[14:16])
originalLength := poolBuf.Len()
binary.Write(poolBuf, binary.LittleEndian, length)
a.putMixedRandDataAndData(poolBuf, data)
a.lastClientHash = tools.HmacMD5(macKey, poolBuf.Bytes()[originalLength:])
poolBuf.Write(a.lastClientHash[:2])
}
func (a *authChain) getServerRandLen(dataLength int, overhead int) int {
return a.rnd(dataLength, &a.randomServer, a.lastServerHash, a.dataSizeList, a.dataSizeList2, overhead)
func (a *authChainA) putMixedRandDataAndData(poolBuf *bytes.Buffer, data []byte) {
randDataLength := a.randDataLength(len(data), a.lastClientHash, &a.randomClient)
if len(data) == 0 {
tools.AppendRandBytes(poolBuf, randDataLength)
return
}
if randDataLength > 0 {
startPos := getRandStartPos(randDataLength, &a.randomClient)
tools.AppendRandBytes(poolBuf, startPos)
poolBuf.Write(data)
tools.AppendRandBytes(poolBuf, randDataLength-startPos)
return
}
poolBuf.Write(data)
}
func (a *authChain) packedDataLen(data []byte) (chunkLength, randLength int) {
dataLength := len(data)
randLength = a.getClientRandLen(dataLength, a.Overhead)
chunkLength = randLength + dataLength + 2 + 2
return
}
func (a *authChain) packData(outData []byte, data []byte, randLength int) {
dataLength := len(data)
outLength := randLength + dataLength + 2
outData[0] = byte(dataLength) ^ a.lastClientHash[14]
outData[1] = byte(dataLength>>8) ^ a.lastClientHash[15]
{
if dataLength > 0 {
randPart1Length := getRandStartPos(&a.randomClient, randLength)
rand.Read(outData[2 : 2+randPart1Length])
a.enc.XORKeyStream(outData[2+randPart1Length:], data)
rand.Read(outData[2+randPart1Length+dataLength : outLength])
} else {
rand.Read(outData[2 : 2+randLength])
}
}
userKeyLen := uint8(len(a.userKey))
key := pool.Get(int(userKeyLen + 4))
defer pool.Put(key)
copy(key, a.userKey)
a.chunkID++
binary.LittleEndian.PutUint32(key[userKeyLen:], a.chunkID)
a.lastClientHash = a.hmac(key, outData[:outLength])
copy(outData[outLength:], a.lastClientHash[:2])
}
const authHeadLength = 4 + 8 + 4 + 16 + 4
func (a *authChain) packAuthData(data []byte) (outData []byte) {
outData = make([]byte, authHeadLength, authHeadLength+1500)
a.mutex.Lock()
defer a.mutex.Unlock()
a.connectionID++
if a.connectionID > 0xFF000000 {
a.clientID = nil
}
if len(a.clientID) == 0 {
a.clientID = make([]byte, 4)
rand.Read(a.clientID)
b := make([]byte, 4)
rand.Read(b)
a.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
}
var key = make([]byte, len(a.IV)+len(a.Key))
copy(key, a.IV)
copy(key[len(a.IV):], a.Key)
encrypt := make([]byte, 20)
t := time.Now().Unix()
binary.LittleEndian.PutUint32(encrypt[:4], uint32(t))
copy(encrypt[4:8], a.clientID)
binary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)
binary.LittleEndian.PutUint16(encrypt[12:], uint16(a.Overhead))
binary.LittleEndian.PutUint16(encrypt[14:], 0)
// first 12 bytes
{
rand.Read(outData[:4])
a.lastClientHash = a.hmac(key, outData[:4])
copy(outData[4:], a.lastClientHash[:8])
}
var base64UserKey string
// uid & 16 bytes auth data
{
a.initUserKeyAndID()
uid := make([]byte, 4)
for i := 0; i < 4; i++ {
uid[i] = a.uid[i] ^ a.lastClientHash[8+i]
}
base64UserKey = base64.StdEncoding.EncodeToString(a.userKey)
aesCipherKey := core.Kdf(base64UserKey+a.salt, 16)
block, err := aes.NewCipher(aesCipherKey)
if err != nil {
return
}
encryptData := make([]byte, 16)
iv := make([]byte, aes.BlockSize)
cbc := cipher.NewCBCEncrypter(block, iv)
cbc.CryptBlocks(encryptData, encrypt[:16])
copy(encrypt[:4], uid[:])
copy(encrypt[4:4+16], encryptData)
}
// final HMAC
{
a.lastServerHash = a.hmac(a.userKey, encrypt[:20])
copy(outData[12:], encrypt)
copy(outData[12+20:], a.lastServerHash[:4])
}
// init cipher
cipherKey := a.calcRC4CipherKey(a.lastClientHash, base64UserKey)
a.enc, _ = rc4.NewCipher(cipherKey)
a.dec, _ = rc4.NewCipher(cipherKey)
// data
chunkLength, randLength := a.packedDataLen(data)
if chunkLength+authHeadLength <= cap(outData) {
outData = outData[:authHeadLength+chunkLength]
} else {
newOutData := make([]byte, authHeadLength+chunkLength)
copy(newOutData, outData[:authHeadLength])
outData = newOutData
}
a.packData(outData[authHeadLength:], data, randLength)
return
}
func getRandStartPos(random *shift128PlusContext, randLength int) int {
if randLength > 0 {
return int(random.Next() % 8589934609 % uint64(randLength))
}
return 0
}
func authChainAGetRandLen(dataLength int, random *shift128PlusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int {
if dataLength > 1440 {
func getRandStartPos(length int, random *tools.XorShift128Plus) int {
if length == 0 {
return 0
}
random.InitFromBinDatalen(lastHash[:16], dataLength)
if dataLength > 1300 {
return int(random.Next()%8589934609) % length
}
func (a *authChainA) getRandLength(length int, lastHash []byte, random *tools.XorShift128Plus) int {
if length > 1440 {
return 0
}
random.InitFromBinAndLength(lastHash, length)
if length > 1300 {
return int(random.Next() % 31)
}
if dataLength > 900 {
if length > 900 {
return int(random.Next() % 127)
}
if dataLength > 400 {
if length > 400 {
return int(random.Next() % 521)
}
return int(random.Next() % 1021)
}
func udpGetRandLen(random *shift128PlusContext, lastHash []byte) int {
random.InitFromBin(lastHash[:16])
func (a *authChainA) initRC4Cipher() {
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(a.lastClientHash), 16)
a.encrypter, _ = rc4.NewCipher(key)
a.decrypter, _ = rc4.NewCipher(key)
}
func udpGetRandLength(lastHash []byte, random *tools.XorShift128Plus) int {
random.InitFromBin(lastHash)
return int(random.Next() % 127)
}
type shift128PlusContext struct {
v [2]uint64
}
func (ctx *shift128PlusContext) InitFromBin(bin []byte) {
var fillBin [16]byte
copy(fillBin[:], bin)
ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8])
ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:])
}
func (ctx *shift128PlusContext) InitFromBinDatalen(bin []byte, datalen int) {
var fillBin [16]byte
copy(fillBin[:], bin)
binary.LittleEndian.PutUint16(fillBin[:2], uint16(datalen))
ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8])
ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:])
for i := 0; i < 4; i++ {
ctx.Next()
}
}
func (ctx *shift128PlusContext) Next() uint64 {
x := ctx.v[0]
y := ctx.v[1]
ctx.v[0] = y
x ^= x << 23
x ^= y ^ (x >> 17) ^ (y >> 26)
ctx.v[1] = x
return x + y
}

View File

@ -1,71 +1,96 @@
package protocol
import (
"net"
"sort"
"github.com/Dreamacro/clash/component/ssr/tools"
)
func init() {
register("auth_chain_b", newAuthChainB)
register("auth_chain_b", newAuthChainB, 4)
}
type authChainB struct {
*authChainA
dataSizeList []int
dataSizeList2 []int
}
func newAuthChainB(b *Base) Protocol {
return &authChain{
Base: b,
authData: &authData{},
salt: "auth_chain_b",
hmac: tools.HmacMD5,
hashDigest: tools.SHA1Sum,
rnd: authChainBGetRandLen,
a := &authChainB{
authChainA: &authChainA{
Base: b,
authData: &authData{},
userData: &userData{},
salt: "auth_chain_b",
},
}
a.initUserData()
return a
}
func initDataSize(r *authChain) {
random := &r.randomServer
random.InitFromBin(r.Key)
len := random.Next()%8 + 4
r.dataSizeList = make([]int, len)
for i := 0; i < int(len); i++ {
r.dataSizeList[i] = int(random.Next() % 2340 % 2040 % 1440)
func (a *authChainB) StreamConn(c net.Conn, iv []byte) net.Conn {
p := &authChainB{
authChainA: &authChainA{
Base: a.Base,
authData: a.next(),
userData: a.userData,
salt: a.salt,
packID: 1,
recvID: 1,
},
}
sort.Ints(r.dataSizeList)
len = random.Next()%16 + 8
r.dataSizeList2 = make([]int, len)
for i := 0; i < int(len); i++ {
r.dataSizeList2[i] = int(random.Next() % 2340 % 2040 % 1440)
}
sort.Ints(r.dataSizeList2)
p.iv = iv
p.randDataLength = p.getRandLength
p.initDataSize()
return &Conn{Conn: c, Protocol: p}
}
func authChainBGetRandLen(dataLength int, random *shift128PlusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int {
if dataLength > 1440 {
func (a *authChainB) initDataSize() {
a.dataSizeList = a.dataSizeList[:0]
a.dataSizeList2 = a.dataSizeList2[:0]
a.randomServer.InitFromBin(a.Key)
length := a.randomServer.Next()%8 + 4
for ; length > 0; length-- {
a.dataSizeList = append(a.dataSizeList, int(a.randomServer.Next()%2340%2040%1440))
}
sort.Ints(a.dataSizeList)
length = a.randomServer.Next()%16 + 8
for ; length > 0; length-- {
a.dataSizeList2 = append(a.dataSizeList2, int(a.randomServer.Next()%2340%2040%1440))
}
sort.Ints(a.dataSizeList2)
}
func (a *authChainB) getRandLength(length int, lashHash []byte, random *tools.XorShift128Plus) int {
if length >= 1440 {
return 0
}
random.InitFromBinDatalen(lastHash[:16], dataLength)
pos := sort.Search(len(dataSizeList), func(i int) bool { return dataSizeList[i] > dataLength+overhead })
finalPos := uint64(pos) + random.Next()%uint64(len(dataSizeList))
if finalPos < uint64(len(dataSizeList)) {
return dataSizeList[finalPos] - dataLength - overhead
random.InitFromBinAndLength(lashHash, length)
pos := sort.Search(len(a.dataSizeList), func(i int) bool { return a.dataSizeList[i] >= length+a.Overhead })
finalPos := pos + int(random.Next()%uint64(len(a.dataSizeList)))
if finalPos < len(a.dataSizeList) {
return a.dataSizeList[finalPos] - length - a.Overhead
}
pos = sort.Search(len(dataSizeList2), func(i int) bool { return dataSizeList2[i] > dataLength+overhead })
finalPos = uint64(pos) + random.Next()%uint64(len(dataSizeList2))
if finalPos < uint64(len(dataSizeList2)) {
return dataSizeList2[finalPos] - dataLength - overhead
pos = sort.Search(len(a.dataSizeList2), func(i int) bool { return a.dataSizeList2[i] >= length+a.Overhead })
finalPos = pos + int(random.Next()%uint64(len(a.dataSizeList2)))
if finalPos < len(a.dataSizeList2) {
return a.dataSizeList2[finalPos] - length - a.Overhead
}
if finalPos < uint64(pos+len(dataSizeList2)-1) {
if finalPos < pos+len(a.dataSizeList2)-1 {
return 0
}
if dataLength > 1300 {
if length > 1300 {
return int(random.Next() % 31)
}
if dataLength > 900 {
if length > 900 {
return int(random.Next() % 127)
}
if dataLength > 400 {
if length > 400 {
return int(random.Next() % 521)
}
return int(random.Next() % 1021)

View File

@ -6,248 +6,177 @@ import (
"hash/adler32"
"hash/crc32"
"math/rand"
"time"
"net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/ssr/tools"
)
func init() {
register("auth_sha1_v4", newAuthSHA1V4, 7)
}
type authSHA1V4 struct {
*Base
*authData
headerSent bool
buffer bytes.Buffer
}
func init() {
register("auth_sha1_v4", newAuthSHA1V4)
iv []byte
hasSentHeader bool
rawTrans bool
}
func newAuthSHA1V4(b *Base) Protocol {
return &authSHA1V4{Base: b, authData: &authData{}}
}
func (a *authSHA1V4) initForConn(iv []byte) Protocol {
return &authSHA1V4{
Base: &Base{
IV: iv,
Key: a.Key,
TCPMss: a.TCPMss,
Overhead: a.Overhead,
Param: a.Param,
},
authData: a.authData,
func (a *authSHA1V4) StreamConn(c net.Conn, iv []byte) net.Conn {
p := &authSHA1V4{Base: a.Base, authData: a.next()}
p.iv = iv
return &Conn{Conn: c, Protocol: p}
}
func (a *authSHA1V4) PacketConn(c net.PacketConn) net.PacketConn {
return c
}
func (a *authSHA1V4) Decode(dst, src *bytes.Buffer) error {
if a.rawTrans {
dst.ReadFrom(src)
return nil
}
}
func (a *authSHA1V4) GetProtocolOverhead() int {
return 7
}
func (a *authSHA1V4) SetOverhead(overhead int) {
a.Overhead = overhead
}
func (a *authSHA1V4) Decode(b []byte) ([]byte, int, error) {
a.buffer.Reset()
bSize := len(b)
originalSize := bSize
for bSize > 4 {
crc := crc32.ChecksumIEEE(b[:2]) & 0xFFFF
if binary.LittleEndian.Uint16(b[2:4]) != uint16(crc) {
return nil, 0, errAuthSHA1v4CRC32Error
for src.Len() > 4 {
if uint16(crc32.ChecksumIEEE(src.Bytes()[:2])&0xffff) != binary.LittleEndian.Uint16(src.Bytes()[2:4]) {
src.Reset()
return errAuthSHA1V4CRC32Error
}
length := int(binary.BigEndian.Uint16(b[:2]))
if length >= 8192 || length < 8 {
return nil, 0, errAuthSHA1v4DataLengthError
length := int(binary.BigEndian.Uint16(src.Bytes()[:2]))
if length >= 8192 || length < 7 {
a.rawTrans = true
src.Reset()
return errAuthSHA1V4LengthError
}
if length > bSize {
if length > src.Len() {
break
}
if adler32.Checksum(b[:length-4]) == binary.LittleEndian.Uint32(b[length-4:]) {
pos := int(b[4])
if pos != 0xFF {
pos += 4
} else {
pos = int(binary.BigEndian.Uint16(b[5:5+2])) + 4
}
retSize := length - pos - 4
a.buffer.Write(b[pos : pos+retSize])
bSize -= length
b = b[length:]
if adler32.Checksum(src.Bytes()[:length-4]) != binary.LittleEndian.Uint32(src.Bytes()[length-4:length]) {
a.rawTrans = true
src.Reset()
return errAuthSHA1V4Adler32Error
}
pos := int(src.Bytes()[4])
if pos < 255 {
pos += 4
} else {
return nil, 0, errAuthSHA1v4IncorrectChecksum
pos = int(binary.BigEndian.Uint16(src.Bytes()[5:7])) + 4
}
dst.Write(src.Bytes()[pos : length-4])
src.Next(length)
}
return a.buffer.Bytes(), originalSize - bSize, nil
return nil
}
func (a *authSHA1V4) Encode(b []byte) ([]byte, error) {
a.buffer.Reset()
bSize := len(b)
offset := 0
if !a.headerSent && bSize > 0 {
headSize := getHeadSize(b, 30)
if headSize > bSize {
headSize = bSize
}
a.buffer.Write(a.packAuthData(b[:headSize]))
offset += headSize
bSize -= headSize
a.headerSent = true
func (a *authSHA1V4) Encode(buf *bytes.Buffer, b []byte) error {
if !a.hasSentHeader {
dataLength := getDataLength(b)
a.packAuthData(buf, b[:dataLength])
b = b[dataLength:]
a.hasSentHeader = true
}
const blockSize = 4096
for bSize > blockSize {
packSize, randSize := a.packedDataSize(b[offset : offset+blockSize])
pack := pool.Get(packSize)
a.packData(b[offset:offset+blockSize], pack, randSize)
a.buffer.Write(pack)
pool.Put(pack)
offset += blockSize
bSize -= blockSize
for len(b) > 8100 {
a.packData(buf, b[:8100])
b = b[8100:]
}
if bSize > 0 {
packSize, randSize := a.packedDataSize(b[offset:])
pack := pool.Get(packSize)
a.packData(b[offset:], pack, randSize)
a.buffer.Write(pack)
pool.Put(pack)
if len(b) > 0 {
a.packData(buf, b)
}
return a.buffer.Bytes(), nil
return nil
}
func (a *authSHA1V4) DecodePacket(b []byte) ([]byte, int, error) {
return b, len(b), nil
func (a *authSHA1V4) DecodePacket(b []byte) ([]byte, error) { return b, nil }
func (a *authSHA1V4) EncodePacket(buf *bytes.Buffer, b []byte) error {
buf.Write(b)
return nil
}
func (a *authSHA1V4) EncodePacket(b []byte) ([]byte, error) {
return b, nil
func (a *authSHA1V4) packData(poolBuf *bytes.Buffer, data []byte) {
dataLength := len(data)
randDataLength := a.getRandDataLength(dataLength)
/*
2: uint16 BigEndian packedDataLength
2: uint16 LittleEndian crc32Data & 0xffff
3: maxRandDataLengthPrefix (min:1)
4: adler32Data
*/
packedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4
if randDataLength < 128 {
packedDataLength -= 2
}
binary.Write(poolBuf, binary.BigEndian, uint16(packedDataLength))
binary.Write(poolBuf, binary.LittleEndian, uint16(crc32.ChecksumIEEE(poolBuf.Bytes()[poolBuf.Len()-2:])&0xffff))
a.packRandData(poolBuf, randDataLength)
poolBuf.Write(data)
binary.Write(poolBuf, binary.LittleEndian, adler32.Checksum(poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:]))
}
func (a *authSHA1V4) packedDataSize(data []byte) (packSize, randSize int) {
dataSize := len(data)
randSize = 1
if dataSize <= 1300 {
if dataSize > 400 {
randSize += rand.Intn(128)
} else {
randSize += rand.Intn(1024)
}
func (a *authSHA1V4) packAuthData(poolBuf *bytes.Buffer, data []byte) {
dataLength := len(data)
randDataLength := a.getRandDataLength(12 + dataLength)
/*
2: uint16 BigEndian packedAuthDataLength
4: uint32 LittleEndian crc32Data
3: maxRandDataLengthPrefix (min: 1)
12: authDataLength
10: hmacSHA1DataLength
*/
packedAuthDataLength := 2 + 4 + 3 + randDataLength + 12 + dataLength + 10
if randDataLength < 128 {
packedAuthDataLength -= 2
}
packSize = randSize + dataSize + 8
return
}
func (a *authSHA1V4) packData(data, ret []byte, randSize int) {
dataSize := len(data)
retSize := len(ret)
// 0~1, ret size
binary.BigEndian.PutUint16(ret[:2], uint16(retSize&0xFFFF))
// 2~3, crc of ret size
crc := crc32.ChecksumIEEE(ret[:2]) & 0xFFFF
binary.LittleEndian.PutUint16(ret[2:4], uint16(crc))
// 4, rand size
if randSize < 128 {
ret[4] = uint8(randSize & 0xFF)
} else {
ret[4] = uint8(0xFF)
binary.BigEndian.PutUint16(ret[5:7], uint16(randSize&0xFFFF))
}
// (rand size+4)~(ret size-4), data
if dataSize > 0 {
copy(ret[randSize+4:], data)
}
// (ret size-4)~end, adler32 of full data
adler := adler32.Checksum(ret[:retSize-4])
binary.LittleEndian.PutUint32(ret[retSize-4:], adler)
}
func (a *authSHA1V4) packAuthData(data []byte) (ret []byte) {
dataSize := len(data)
randSize := 1
if dataSize <= 1300 {
if dataSize > 400 {
randSize += rand.Intn(128)
} else {
randSize += rand.Intn(1024)
}
}
dataOffset := randSize + 4 + 2
retSize := dataOffset + dataSize + 12 + tools.HmacSHA1Len
ret = make([]byte, retSize)
a.mutex.Lock()
defer a.mutex.Unlock()
a.connectionID++
if a.connectionID > 0xFF000000 {
a.clientID = nil
}
if len(a.clientID) == 0 {
a.clientID = make([]byte, 8)
rand.Read(a.clientID)
b := make([]byte, 4)
rand.Read(b)
a.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
}
// 0~1, ret size
binary.BigEndian.PutUint16(ret[:2], uint16(retSize&0xFFFF))
// 2~6, crc of (ret size+salt+key)
salt := []byte("auth_sha1_v4")
crcData := make([]byte, len(salt)+len(a.Key)+2)
copy(crcData[:2], ret[:2])
crcData := pool.Get(len(salt) + len(a.Key) + 2)
defer pool.Put(crcData)
binary.BigEndian.PutUint16(crcData, uint16(packedAuthDataLength))
copy(crcData[2:], salt)
copy(crcData[2+len(salt):], a.Key)
crc := crc32.ChecksumIEEE(crcData) & 0xFFFFFFFF
// 2~6, crc of (ret size+salt+key)
binary.LittleEndian.PutUint32(ret[2:], crc)
// 6~(rand size+6), rand numbers
rand.Read(ret[dataOffset-randSize : dataOffset])
// 6, rand size
if randSize < 128 {
ret[6] = byte(randSize & 0xFF)
} else {
// 6, magic number 0xFF
ret[6] = 0xFF
// 7~8, rand size
binary.BigEndian.PutUint16(ret[7:9], uint16(randSize&0xFFFF))
}
// rand size+6~(rand size+10), time stamp
now := time.Now().Unix()
binary.LittleEndian.PutUint32(ret[dataOffset:dataOffset+4], uint32(now))
// rand size+10~(rand size+14), client ID
copy(ret[dataOffset+4:dataOffset+4+4], a.clientID[:4])
// rand size+14~(rand size+18), connection ID
binary.LittleEndian.PutUint32(ret[dataOffset+8:dataOffset+8+4], a.connectionID)
// rand size+18~(rand size+18)+data length, data
copy(ret[dataOffset+12:], data)
key := make([]byte, len(a.IV)+len(a.Key))
copy(key, a.IV)
copy(key[len(a.IV):], a.Key)
key := pool.Get(len(a.iv) + len(a.Key))
defer pool.Put(key)
copy(key, a.iv)
copy(key[len(a.iv):], a.Key)
h := tools.HmacSHA1(key, ret[:retSize-tools.HmacSHA1Len])
// (ret size-10)~(ret size)/(rand size)+18+data length~end, hmac
copy(ret[retSize-tools.HmacSHA1Len:], h[:tools.HmacSHA1Len])
return ret
poolBuf.Write(crcData[:2])
binary.Write(poolBuf, binary.LittleEndian, crc32.ChecksumIEEE(crcData))
a.packRandData(poolBuf, randDataLength)
a.putAuthData(poolBuf)
poolBuf.Write(data)
poolBuf.Write(tools.HmacSHA1(key, poolBuf.Bytes()[poolBuf.Len()-packedAuthDataLength+10:])[:10])
}
func getHeadSize(data []byte, defaultValue int) int {
if data == nil || len(data) < 2 {
return defaultValue
func (a *authSHA1V4) packRandData(poolBuf *bytes.Buffer, size int) {
if size < 128 {
poolBuf.WriteByte(byte(size + 1))
tools.AppendRandBytes(poolBuf, size)
return
}
headType := data[0] & 0x07
switch headType {
case 1:
// IPv4 1+4+2
return 7
case 4:
// IPv6 1+16+2
return 19
case 3:
// domain name, variant length
return 4 + int(data[1])
}
return defaultValue
poolBuf.WriteByte(255)
binary.Write(poolBuf, binary.BigEndian, uint16(size+3))
tools.AppendRandBytes(poolBuf, size)
}
func (a *authSHA1V4) getRandDataLength(size int) int {
if size > 1200 {
return 0
}
if size > 400 {
return rand.Intn(256)
}
return rand.Intn(512)
}

View File

@ -1,10 +1,77 @@
package protocol
// Base information for protocol
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/binary"
"math/rand"
"sync"
"time"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/go-shadowsocks2/core"
)
type Base struct {
IV []byte
Key []byte
TCPMss int
Overhead int
Param string
}
type userData struct {
userKey []byte
userID [4]byte
}
type authData struct {
clientID [4]byte
connectionID uint32
mutex sync.Mutex
}
func (a *authData) next() *authData {
r := &authData{}
a.mutex.Lock()
defer a.mutex.Unlock()
if a.connectionID > 0xff000000 || a.connectionID == 0 {
rand.Read(a.clientID[:])
a.connectionID = rand.Uint32() & 0xffffff
}
a.connectionID++
copy(r.clientID[:], a.clientID[:])
r.connectionID = a.connectionID
return r
}
func (a *authData) putAuthData(buf *bytes.Buffer) {
binary.Write(buf, binary.LittleEndian, uint32(time.Now().Unix()))
buf.Write(a.clientID[:])
binary.Write(buf, binary.LittleEndian, a.connectionID)
}
func (a *authData) putEncryptedData(b *bytes.Buffer, userKey []byte, paddings [2]int, salt string) error {
encrypt := pool.Get(16)
defer pool.Put(encrypt)
binary.LittleEndian.PutUint32(encrypt, uint32(time.Now().Unix()))
copy(encrypt[4:], a.clientID[:])
binary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)
binary.LittleEndian.PutUint16(encrypt[12:], uint16(paddings[0]))
binary.LittleEndian.PutUint16(encrypt[14:], uint16(paddings[1]))
cipherKey := core.Kdf(base64.StdEncoding.EncodeToString(userKey)+salt, 16)
block, err := aes.NewCipher(cipherKey)
if err != nil {
log.Warnln("New cipher error: %s", err.Error())
return err
}
iv := bytes.Repeat([]byte{0}, 16)
cbcCipher := cipher.NewCBCEncrypter(block, iv)
cbcCipher.CryptBlocks(encrypt, encrypt)
b.Write(encrypt)
return nil
}

View File

@ -1,36 +1,33 @@
package protocol
type origin struct{ *Base }
import (
"bytes"
"net"
)
func init() {
register("origin", newOrigin)
type origin struct{}
func init() { register("origin", newOrigin, 0) }
func newOrigin(b *Base) Protocol { return &origin{} }
func (o *origin) StreamConn(c net.Conn, iv []byte) net.Conn { return c }
func (o *origin) PacketConn(c net.PacketConn) net.PacketConn { return c }
func (o *origin) Decode(dst, src *bytes.Buffer) error {
dst.ReadFrom(src)
return nil
}
func newOrigin(b *Base) Protocol {
return &origin{}
func (o *origin) Encode(buf *bytes.Buffer, b []byte) error {
buf.Write(b)
return nil
}
func (o *origin) initForConn(iv []byte) Protocol { return &origin{} }
func (o *origin) DecodePacket(b []byte) ([]byte, error) { return b, nil }
func (o *origin) GetProtocolOverhead() int {
return 0
}
func (o *origin) SetOverhead(overhead int) {
}
func (o *origin) Decode(b []byte) ([]byte, int, error) {
return b, len(b), nil
}
func (o *origin) Encode(b []byte) ([]byte, error) {
return b, nil
}
func (o *origin) DecodePacket(b []byte) ([]byte, int, error) {
return b, len(b), nil
}
func (o *origin) EncodePacket(b []byte) ([]byte, error) {
return b, nil
func (o *origin) EncodePacket(buf *bytes.Buffer, b []byte) error {
buf.Write(b)
return nil
}

View File

@ -1,30 +1,26 @@
package protocol
import (
"bytes"
"net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/ssr/tools"
)
// NewPacketConn returns a net.NewPacketConn with protocol decoding/encoding
func NewPacketConn(pc net.PacketConn, p Protocol) net.PacketConn {
return &PacketConn{PacketConn: pc, Protocol: p.initForConn(nil)}
}
// PacketConn represents a protocol packet connection
type PacketConn struct {
net.PacketConn
Protocol
}
func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
buf := pool.Get(pool.RelayBufferSize)
defer pool.Put(buf)
buf, err := c.EncodePacket(b)
buf := tools.BufPool.Get().(*bytes.Buffer)
defer tools.BufPool.Put(buf)
defer buf.Reset()
err := c.EncodePacket(buf, b)
if err != nil {
return 0, err
}
_, err = c.PacketConn.WriteTo(buf, addr)
_, err = c.PacketConn.WriteTo(buf.Bytes(), addr)
return len(b), err
}
@ -33,10 +29,10 @@ func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
if err != nil {
return n, addr, err
}
bb, length, err := c.DecodePacket(b[:n])
decoded, err := c.DecodePacket(b[:n])
if err != nil {
return n, addr, err
}
copy(b, bb)
return length, addr, err
copy(b, decoded)
return len(decoded), addr, nil
}

View File

@ -4,60 +4,73 @@ import (
"bytes"
"errors"
"fmt"
"strings"
"sync"
"math/rand"
"net"
)
var (
errAuthAES128IncorrectMAC = errors.New("auth_aes128_* post decrypt incorrect mac")
errAuthAES128DataLengthError = errors.New("auth_aes128_* post decrypt length mismatch")
errAuthAES128IncorrectChecksum = errors.New("auth_aes128_* post decrypt incorrect checksum")
errAuthAES128PositionTooLarge = errors.New("auth_aes128_* post decrypt position is too large")
errAuthSHA1v4CRC32Error = errors.New("auth_sha1_v4 post decrypt data crc32 error")
errAuthSHA1v4DataLengthError = errors.New("auth_sha1_v4 post decrypt data length error")
errAuthSHA1v4IncorrectChecksum = errors.New("auth_sha1_v4 post decrypt incorrect checksum")
errAuthChainDataLengthError = errors.New("auth_chain_* post decrypt length mismatch")
errAuthChainHMACError = errors.New("auth_chain_* post decrypt hmac error")
errAuthSHA1V4CRC32Error = errors.New("auth_sha1_v4 decode data wrong crc32")
errAuthSHA1V4LengthError = errors.New("auth_sha1_v4 decode data wrong length")
errAuthSHA1V4Adler32Error = errors.New("auth_sha1_v4 decode data wrong adler32")
errAuthAES128MACError = errors.New("auth_aes128 decode data wrong mac")
errAuthAES128LengthError = errors.New("auth_aes128 decode data wrong length")
errAuthAES128ChksumError = errors.New("auth_aes128 decode data wrong checksum")
errAuthChainLengthError = errors.New("auth_chain decode data wrong length")
errAuthChainChksumError = errors.New("auth_chain decode data wrong checksum")
)
type authData struct {
clientID []byte
connectionID uint32
mutex sync.Mutex
}
type recvInfo struct {
recvID uint32
buffer *bytes.Buffer
}
type hmacMethod func(key []byte, data []byte) []byte
type hashDigestMethod func(data []byte) []byte
type rndMethod func(dataSize int, random *shift128PlusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int
// Protocol provides methods for decoding, encoding and iv setting
type Protocol interface {
initForConn(iv []byte) Protocol
GetProtocolOverhead() int
SetOverhead(int)
Decode([]byte) ([]byte, int, error)
Encode([]byte) ([]byte, error)
DecodePacket([]byte) ([]byte, int, error)
EncodePacket([]byte) ([]byte, error)
StreamConn(net.Conn, []byte) net.Conn
PacketConn(net.PacketConn) net.PacketConn
Decode(dst, src *bytes.Buffer) error
Encode(buf *bytes.Buffer, b []byte) error
DecodePacket([]byte) ([]byte, error)
EncodePacket(buf *bytes.Buffer, b []byte) error
}
type protocolCreator func(b *Base) Protocol
var protocolList = make(map[string]protocolCreator)
var protocolList = make(map[string]struct {
overhead int
new protocolCreator
})
func register(name string, c protocolCreator) {
protocolList[name] = c
func register(name string, c protocolCreator, o int) {
protocolList[name] = struct {
overhead int
new protocolCreator
}{overhead: o, new: c}
}
// PickProtocol returns a protocol of the given name
func PickProtocol(name string, b *Base) (Protocol, error) {
if protocolCreator, ok := protocolList[strings.ToLower(name)]; ok {
return protocolCreator(b), nil
if choice, ok := protocolList[name]; ok {
b.Overhead += choice.overhead
return choice.new(b), nil
}
return nil, fmt.Errorf("Protocol %s not supported", name)
return nil, fmt.Errorf("protocol %s not supported", name)
}
func getHeadSize(b []byte, defaultValue int) int {
if len(b) < 2 {
return defaultValue
}
headType := b[0] & 7
switch headType {
case 1:
return 7
case 4:
return 19
case 3:
return 4 + int(b[1])
}
return defaultValue
}
func getDataLength(b []byte) int {
bLength := len(b)
dataLength := getHeadSize(b, 30) + rand.Intn(32)
if bLength < dataLength {
return bLength
}
return dataLength
}

View File

@ -5,31 +5,21 @@ import (
"net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/ssr/tools"
)
// NewConn wraps a stream-oriented net.Conn with protocol decoding/encoding
func NewConn(c net.Conn, p Protocol, iv []byte) net.Conn {
return &Conn{Conn: c, Protocol: p.initForConn(iv)}
}
// Conn represents a protocol connection
type Conn struct {
net.Conn
Protocol
buf []byte
offset int
decoded bytes.Buffer
underDecoded bytes.Buffer
}
func (c *Conn) Read(b []byte) (int, error) {
if c.buf != nil {
n := copy(b, c.buf[c.offset:])
c.offset += n
if c.offset == len(c.buf) {
c.buf = nil
}
return n, nil
if c.decoded.Len() > 0 {
return c.decoded.Read(b)
}
buf := pool.Get(pool.RelayBufferSize)
defer pool.Put(buf)
n, err := c.Conn.Read(buf)
@ -37,32 +27,26 @@ func (c *Conn) Read(b []byte) (int, error) {
return 0, err
}
c.underDecoded.Write(buf[:n])
underDecoded := c.underDecoded.Bytes()
decoded, length, err := c.Decode(underDecoded)
err = c.Decode(&c.decoded, &c.underDecoded)
if err != nil {
c.underDecoded.Reset()
return 0, nil
}
if length == 0 {
return 0, nil
}
c.underDecoded.Next(length)
n = copy(b, decoded)
if len(decoded) > len(b) {
c.buf = decoded
c.offset = n
return 0, err
}
n, _ = c.decoded.Read(b)
return n, nil
}
func (c *Conn) Write(b []byte) (int, error) {
encoded, err := c.Encode(b)
bLength := len(b)
buf := tools.BufPool.Get().(*bytes.Buffer)
defer tools.BufPool.Put(buf)
defer buf.Reset()
err := c.Encode(buf, b)
if err != nil {
return 0, err
}
_, err = c.Conn.Write(encoded)
_, err = c.Conn.Write(buf.Bytes())
if err != nil {
return 0, err
}
return len(b), nil
return bLength, nil
}