mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-10 11:03:13 +00:00
278 lines
6.7 KiB
Go
278 lines
6.7 KiB
Go
|
package protocol
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"math"
|
||
|
"math/rand"
|
||
|
"net"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/Dreamacro/clash/common/pool"
|
||
|
"github.com/Dreamacro/clash/transport/ssr/tools"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
hmacMethod func(key, data []byte) []byte
|
||
|
hashDigestMethod func([]byte) []byte
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
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 {
|
||
|
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]))
|
||
|
}
|
||
|
}
|
||
|
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 len(b) < 4 {
|
||
|
return nil, errAuthAES128LengthError
|
||
|
}
|
||
|
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)
|
||
|
}
|