glider/proxy/ssr/internal/protocol/auth_aes128_md5.go
2022-01-26 23:40:49 +08:00

281 lines
6.4 KiB
Go

package protocol
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/binary"
"math/rand"
"strconv"
"strings"
"time"
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
"github.com/nadoo/glider/proxy/ssr/internal/tools"
)
func init() {
register("auth_aes128_md5", NewAuthAES128MD5)
}
func NewAuthAES128MD5() IProtocol {
a := &authAES128{
salt: "auth_aes128_md5",
hmac: tools.HmacMD5,
hashDigest: tools.MD5Sum,
packID: 1,
recvInfo: recvInfo{
recvID: 1,
buffer: bytes.NewBuffer(nil),
},
}
return a
}
type recvInfo struct {
recvID uint32
buffer *bytes.Buffer
}
type authAES128 struct {
ssr.ServerInfo
recvInfo
data *AuthData
hasSentHeader bool
packID uint32
userKey []byte
salt string
hmac hmacMethod
hashDigest hashDigestMethod
}
func (a *authAES128) SetServerInfo(s *ssr.ServerInfo) {
a.ServerInfo = *s
}
func (a *authAES128) GetServerInfo() (s *ssr.ServerInfo) {
return &a.ServerInfo
}
func (a *authAES128) SetData(data any) {
if auth, ok := data.(*AuthData); ok {
a.data = auth
}
}
func (a *authAES128) GetData() any {
if a.data == nil {
a.data = &AuthData{}
}
return a.data
}
func (a *authAES128) packData(data []byte) (outData []byte) {
dataLength := len(data)
randLength := 1
rand.Seed(time.Now().UnixNano())
if dataLength <= 1200 {
if a.packID > 4 {
randLength += rand.Intn(32)
} else {
if dataLength > 900 {
randLength += rand.Intn(128)
} else {
randLength += rand.Intn(512)
}
}
}
outLength := randLength + dataLength + 8
outData = make([]byte, outLength)
// 0~1, out length
binary.LittleEndian.PutUint16(outData[0:], uint16(outLength&0xFFFF))
// 2~3, hmac
key := make([]byte, len(a.userKey)+4)
copy(key, a.userKey)
binary.LittleEndian.PutUint32(key[len(key)-4:], a.packID)
h := a.hmac(key, outData[0:2])
copy(outData[2:4], h[:2])
// 4~rand length+4, rand number
rand.Read(outData[4 : 4+randLength])
// 4, rand length
if randLength < 128 {
outData[4] = byte(randLength & 0xFF)
} else {
// 4, magic number 0xFF
outData[4] = 0xFF
// 5~6, rand length
binary.LittleEndian.PutUint16(outData[5:], uint16(randLength&0xFFFF))
}
// rand length+4~out length-4, data
if dataLength > 0 {
copy(outData[randLength+4:], data)
}
a.packID++
h = a.hmac(key, outData[:outLength-4])
copy(outData[outLength-4:], h[:4])
return
}
func (a *authAES128) packAuthData(data []byte) (outData []byte) {
dataLength := len(data)
var randLength int
rand.Seed(time.Now().UnixNano())
if dataLength > 400 {
randLength = rand.Intn(512)
} else {
randLength = rand.Intn(1024)
}
dataOffset := randLength + 16 + 4 + 4 + 7
outLength := dataOffset + dataLength + 4
outData = make([]byte, outLength)
encrypt := make([]byte, 24)
key := make([]byte, a.IVLen+a.KeyLen)
copy(key, a.IV)
copy(key[a.IVLen:], a.Key)
rand.Read(outData[dataOffset-randLength:])
a.data.mutex.Lock()
a.data.connectionID++
if a.data.connectionID > 0xFF000000 {
a.data.clientID = nil
}
if len(a.data.clientID) == 0 {
a.data.clientID = make([]byte, 8)
rand.Read(a.data.clientID)
b := make([]byte, 4)
rand.Read(b)
a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
}
copy(encrypt[4:], a.data.clientID)
binary.LittleEndian.PutUint32(encrypt[8:], a.data.connectionID)
a.data.mutex.Unlock()
now := time.Now().Unix()
binary.LittleEndian.PutUint32(encrypt[0:4], uint32(now))
binary.LittleEndian.PutUint16(encrypt[12:], uint16(outLength&0xFFFF))
binary.LittleEndian.PutUint16(encrypt[14:], uint16(randLength&0xFFFF))
params := strings.Split(a.Param, ":")
uid := make([]byte, 4)
if len(params) >= 2 {
if userID, err := strconv.ParseUint(params[0], 10, 32); err != nil {
rand.Read(uid)
} else {
binary.LittleEndian.PutUint32(uid, uint32(userID))
a.userKey = a.hashDigest([]byte(params[1]))
}
} else {
rand.Read(uid)
}
if a.userKey == nil {
a.userKey = make([]byte, a.KeyLen)
copy(a.userKey, a.Key)
}
encryptKey := make([]byte, len(a.userKey))
copy(encryptKey, a.userKey)
aesCipherKey := tools.EVPBytesToKey(base64.StdEncoding.EncodeToString(encryptKey)+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[0:16])
copy(encrypt[4:4+16], encryptData)
copy(encrypt[0:4], uid)
h := a.hmac(key, encrypt[0:20])
copy(encrypt[20:], h[:4])
rand.Read(outData[0:1])
h = a.hmac(key, outData[0:1])
copy(outData[1:], h[0:7-1])
copy(outData[7:], encrypt)
copy(outData[dataOffset:], data)
h = a.hmac(a.userKey, outData[0:outLength-4])
copy(outData[outLength-4:], h[:4])
//log.Println("clientID:", a.data.clientID, "connectionID:", a.data.connectionID)
return
}
func (a *authAES128) PreEncrypt(plainData []byte) (outData []byte, err error) {
dataLength := len(plainData)
offset := 0
if dataLength > 0 && !a.hasSentHeader {
authLength := dataLength
if authLength > 1200 {
authLength = 1200
}
packData := a.packAuthData(plainData[:authLength])
a.hasSentHeader = true
outData = append(outData, packData...)
dataLength -= authLength
offset += authLength
}
const blockSize = 4096
for dataLength > blockSize {
packData := a.packData(plainData[offset : offset+blockSize])
outData = append(outData, packData...)
dataLength -= blockSize
offset += blockSize
}
if dataLength > 0 {
packData := a.packData(plainData[offset:])
outData = append(outData, packData...)
}
return
}
func (a *authAES128) PostDecrypt(plainData []byte) ([]byte, int, error) {
a.buffer.Reset()
plainLength := len(plainData)
readlenth := 0
key := make([]byte, len(a.userKey)+4)
copy(key, a.userKey)
for plainLength > 4 {
binary.LittleEndian.PutUint32(key[len(key)-4:], a.recvID)
h := a.hmac(key, plainData[0:2])
if h[0] != plainData[2] || h[1] != plainData[3] {
return nil, 0, ssr.ErrAuthAES128IncorrectHMAC
}
length := int(binary.LittleEndian.Uint16(plainData[0:2]))
if length >= 8192 || length < 8 {
return nil, 0, ssr.ErrAuthAES128DataLengthError
}
if length > plainLength {
break
}
a.recvID++
pos := int(plainData[4])
if pos < 255 {
pos += 4
} else {
pos = int(binary.LittleEndian.Uint16(plainData[5:7])) + 4
}
a.buffer.Write(plainData[pos : length-4])
plainData = plainData[length:]
plainLength -= length
readlenth += length
}
return a.buffer.Bytes(), readlenth, nil
}
func (a *authAES128) GetOverhead() int {
return 9
}