github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/proxy/shadowsocksr/protocol/auth_aes128_md5.go (about)

     1  package protocol
     2  
     3  import (
     4  	"bytes"
     5  	"crypto"
     6  	"crypto/aes"
     7  	"crypto/cipher"
     8  	crand "crypto/rand"
     9  	"encoding/base64"
    10  	"encoding/binary"
    11  	"math"
    12  	"math/big"
    13  	"math/rand/v2"
    14  	"strconv"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/Asutorufa/yuhaiin/pkg/log"
    19  	"github.com/Asutorufa/yuhaiin/pkg/net/proxy/shadowsocks/core"
    20  	ssr "github.com/Asutorufa/yuhaiin/pkg/net/proxy/shadowsocksr/utils"
    21  	"github.com/Asutorufa/yuhaiin/pkg/utils/pool"
    22  	"github.com/Asutorufa/yuhaiin/pkg/utils/relay"
    23  	"github.com/Asutorufa/yuhaiin/pkg/utils/yerror"
    24  )
    25  
    26  func NewAuthAES128MD5(info Protocol) protocol { return newAuthAES128(info, crypto.MD5) }
    27  
    28  func newAuthAES128(info Protocol, hash crypto.Hash) protocol {
    29  	a := &authAES128{
    30  		salt:   strings.ToLower(info.Name),
    31  		hmac:   ssr.HMAC(hash),
    32  		packID: 1,
    33  		recvID: 1,
    34  		info:   info,
    35  	}
    36  	a.initUserKey()
    37  	return a
    38  }
    39  
    40  type authAES128 struct {
    41  	hasSentHeader, rawTrans bool
    42  	recvID, packID          uint32
    43  	uid                     [4]byte
    44  	hmac                    ssr.HMAC
    45  	userKey                 []byte
    46  	salt                    string
    47  	info                    Protocol
    48  }
    49  
    50  func (a *authAES128) packData(wbuf *bytes.Buffer, data []byte, fullDataSize int) {
    51  	dataLength := len(data)
    52  	if dataLength == 0 {
    53  		return
    54  	}
    55  	randLength := a.rndDataLen(dataLength, fullDataSize)
    56  
    57  	// 1: randLengthData Length
    58  	outLength := 1 + randLength + dataLength + 8
    59  
    60  	key := make([]byte, len(a.userKey)+4)
    61  	copy(key, a.userKey)
    62  	binary.LittleEndian.PutUint32(key[len(key)-4:], a.packID)
    63  
    64  	a.packID = (a.packID + 1) & 0xFFFFFFFF
    65  
    66  	// 0~1, out length
    67  	binary.Write(wbuf, binary.LittleEndian, uint16(outLength))
    68  
    69  	hmacBuf := pool.GetBytes(6)
    70  	defer pool.PutBytes(hmacBuf)
    71  
    72  	// 2~3, hmac
    73  	wbuf.Write(a.hmac.HMAC(key, wbuf.Bytes()[wbuf.Len()-2:], hmacBuf)[:2])
    74  
    75  	// 4, rand length
    76  	if randLength < 128 {
    77  		wbuf.WriteByte(byte(randLength + 1))
    78  	} else {
    79  		// 4, magic number 255
    80  		wbuf.WriteByte(255)
    81  		// 5~6, rand length
    82  		binary.Write(wbuf, binary.LittleEndian, uint16(randLength+1))
    83  		randLength -= 2
    84  	}
    85  
    86  	// 4~rand length+4, rand number
    87  	if _, err := relay.CopyN(wbuf, crand.Reader, int64(randLength)); err != nil {
    88  		log.Error("copy rand bytes failed", "err", err)
    89  	}
    90  
    91  	// rand length+4~out length-4, data
    92  	wbuf.Write(data)
    93  
    94  	start := wbuf.Len() - outLength + 4
    95  	// hmac
    96  	wbuf.Write(a.hmac.HMAC(key, wbuf.Bytes()[start:], hmacBuf)[:4])
    97  }
    98  
    99  func (a *authAES128) initUserKey() {
   100  	if a.userKey != nil {
   101  		return
   102  	}
   103  
   104  	params := strings.Split(a.info.Param, ":")
   105  	if len(params) >= 2 {
   106  		userID, err := strconv.ParseUint(params[0], 10, 32)
   107  		if err == nil {
   108  			binary.LittleEndian.PutUint32(a.uid[:], uint32(userID))
   109  			a.userKey = a.hmac.HASH([]byte(params[1]))
   110  		}
   111  	}
   112  
   113  	if a.userKey == nil {
   114  		crand.Read(a.uid[:])
   115  		a.userKey = make([]byte, len(a.info.Key()))
   116  		copy(a.userKey, a.info.Key())
   117  	}
   118  }
   119  
   120  // https://github.com/shadowsocksrr/shadowsocksr/blob/fd723a92c488d202b407323f0512987346944136/shadowsocks/obfsplugin/auth.py#L501
   121  func (a *authAES128) rndDataLen(bufSize, fullBufSize int) int {
   122  	trapezoidRandomFLoat := func(maxVal int, d float64) int {
   123  		var r float64
   124  		if d == 0 {
   125  			r = rand.Float64()
   126  		} else {
   127  			s := rand.Float64()
   128  			a := 1 - d
   129  			r = (math.Sqrt(a*a+4*d*s) - a) / (2 * d)
   130  		}
   131  
   132  		return int(float64(maxVal) * r)
   133  	}
   134  
   135  	if fullBufSize >= pool.DefaultSize {
   136  		return 0
   137  	}
   138  
   139  	revLen := a.info.TcpMss - bufSize - a.GetOverhead()
   140  	if revLen == 0 {
   141  		return 0
   142  	}
   143  	if revLen < 0 {
   144  		if revLen > -a.info.TcpMss {
   145  			return trapezoidRandomFLoat(revLen+a.info.TcpMss, -0.3)
   146  		}
   147  
   148  		return rand.IntN(32)
   149  	}
   150  
   151  	if bufSize > 900 {
   152  		return rand.IntN(revLen)
   153  	}
   154  
   155  	return trapezoidRandomFLoat(revLen, -0.3)
   156  }
   157  
   158  func (a *authAES128) packAuthData(wbuf *bytes.Buffer, data []byte) {
   159  	dataLength := len(data)
   160  	if dataLength == 0 {
   161  		return
   162  	}
   163  
   164  	var randLength int
   165  	if dataLength > 400 {
   166  		randLength = rand.IntN(512)
   167  	} else {
   168  		randLength = rand.IntN(1024)
   169  	}
   170  
   171  	outLength := 7 + 4 + 16 + 4 + dataLength + randLength + 4
   172  
   173  	aesCipherKey := core.KDF(base64.StdEncoding.EncodeToString(a.userKey)+a.salt, 16)
   174  	block, err := aes.NewCipher(aesCipherKey)
   175  	if err != nil {
   176  		return
   177  	}
   178  
   179  	encrypt := pool.GetBytesWriter(16)
   180  	defer encrypt.Free()
   181  
   182  	a.info.Auth.nextAuth()
   183  	encrypt.WriteLittleEndianUint32(uint32(time.Now().Unix()))
   184  	_, _ = encrypt.Write(a.info.Auth.clientID[:])
   185  	encrypt.WriteLittleEndianUint32(a.info.Auth.connectionID.Load())
   186  	encrypt.WriteLittleEndianUint16(uint16(outLength))
   187  	encrypt.WriteLittleEndianUint16(uint16(randLength))
   188  
   189  	iv := make([]byte, aes.BlockSize)
   190  	cbc := cipher.NewCBCEncrypter(block, iv)
   191  	cbc.CryptBlocks(encrypt.Bytes(), encrypt.Bytes())
   192  
   193  	key := make([]byte, a.info.IVSize()+len(a.info.Key()))
   194  	copy(key, a.info.IV)
   195  	copy(key[a.info.IVSize():], a.info.Key())
   196  
   197  	hmacBuf := pool.GetBytes(6)
   198  	defer pool.PutBytes(hmacBuf)
   199  
   200  	wbuf.WriteByte(byte(yerror.Ignore(crand.Int(crand.Reader, big.NewInt(256))).Uint64()))
   201  	wbuf.Write(a.hmac.HMAC(key, wbuf.Bytes()[wbuf.Len()-1:], hmacBuf)[:6])
   202  	wbuf.Write(a.uid[:])
   203  	wbuf.Write(encrypt.Bytes())
   204  	wbuf.Write(a.hmac.HMAC(key, wbuf.Bytes()[wbuf.Len()-20:], hmacBuf)[:4])
   205  	_, _ = relay.CopyN(wbuf, crand.Reader, int64(randLength))
   206  	wbuf.Write(data)
   207  	start := wbuf.Len() - outLength + 4
   208  	wbuf.Write(a.hmac.HMAC(a.userKey, wbuf.Bytes()[start:], hmacBuf)[:4])
   209  }
   210  
   211  func (a *authAES128) EncryptStream(wbuf *bytes.Buffer, data []byte) (err error) {
   212  	dataLen := len(data)
   213  
   214  	if dataLen <= 0 {
   215  		return nil
   216  	}
   217  
   218  	if !a.hasSentHeader {
   219  		authLen := GetHeadSize(data, 30) + rand.IntN(32)
   220  		if authLen > dataLen {
   221  			authLen = dataLen
   222  		}
   223  
   224  		a.packAuthData(wbuf, data[:authLen])
   225  		data = data[authLen:]
   226  
   227  		a.hasSentHeader = true
   228  	}
   229  
   230  	// https://github.com/shadowsocksrr/shadowsocksr/blob/fd723a92c488d202b407323f0512987346944136/shadowsocks/obfsplugin/auth.py#L459
   231  	const unitLen = 8100
   232  	for len(data) > unitLen {
   233  		a.packData(wbuf, data[:unitLen], dataLen)
   234  		data = data[unitLen:]
   235  	}
   236  	a.packData(wbuf, data, dataLen)
   237  
   238  	return nil
   239  }
   240  
   241  func (a *authAES128) DecryptStream(rbuf *bytes.Buffer, data []byte) (int, error) {
   242  	if a.rawTrans {
   243  		return rbuf.Write(data)
   244  	}
   245  
   246  	datalen, readLen := len(data), 0
   247  
   248  	keyLen := len(a.userKey) + 4
   249  
   250  	key := pool.GetBytesWriter(keyLen)
   251  	defer key.Free()
   252  
   253  	_, _ = key.Write(a.userKey)
   254  
   255  	hmacBuf := pool.GetBytes(6)
   256  	defer pool.PutBytes(hmacBuf)
   257  
   258  	for remain := datalen; remain > 4; remain = datalen - readLen {
   259  		key.Truncate(keyLen - 4)
   260  		key.WriteLittleEndianUint32(a.recvID)
   261  		if !bytes.Equal(a.hmac.HMAC(key.Bytes(), data[0:2], hmacBuf)[:2], data[2:4]) {
   262  			return 0, ssr.ErrAuthAES128IncorrectHMAC
   263  		}
   264  
   265  		clen := int(binary.LittleEndian.Uint16(data[0:2]))
   266  		cdlen := clen - 4
   267  
   268  		if clen >= 8192 || clen < 7 {
   269  			a.rawTrans = true
   270  			return 0, ssr.ErrAuthAES128DataLengthError
   271  		}
   272  
   273  		if clen > remain {
   274  			break
   275  		}
   276  
   277  		if !bytes.Equal(a.hmac.HMAC(key.Bytes(), data[:cdlen], hmacBuf)[:4], data[cdlen:clen]) {
   278  			a.rawTrans = true
   279  			return 0, ssr.ErrAuthAES128IncorrectChecksum
   280  		}
   281  
   282  		a.recvID = (a.recvID + 1) & 0xFFFFFFFF
   283  
   284  		pos := int(data[4])
   285  		if pos >= 255 {
   286  			pos = int(binary.LittleEndian.Uint16(data[5:7]))
   287  		}
   288  		pos += 4
   289  
   290  		if pos > cdlen {
   291  			return 0, ssr.ErrAuthAES128PosOutOfRange
   292  		}
   293  
   294  		rbuf.Write(data[pos:cdlen])
   295  
   296  		data, readLen = data[clen:], readLen+clen
   297  	}
   298  
   299  	return readLen, nil
   300  }
   301  
   302  // https://github.com/shadowsocksrr/shadowsocksr/blob/fd723a92c488d202b407323f0512987346944136/shadowsocks/obfsplugin/auth.py#L749
   303  func (a *authAES128) EncryptPacket(b []byte) ([]byte, error) {
   304  	hmacBuf := pool.GetBytes(6)
   305  	defer pool.PutBytes(hmacBuf)
   306  
   307  	wbuf := make([]byte, 0, len(b)+len(a.uid)+4)
   308  	wbuf = append(wbuf, b...)
   309  	wbuf = append(wbuf, a.uid[:]...)
   310  	wbuf = append(wbuf, a.hmac.HMAC(a.userKey, wbuf, hmacBuf)[:4]...)
   311  
   312  	return wbuf, nil
   313  }
   314  
   315  // https://github.com/shadowsocksrr/shadowsocksr/blob/fd723a92c488d202b407323f0512987346944136/shadowsocks/obfsplugin/auth.py#L764
   316  func (a *authAES128) DecryptPacket(b []byte) ([]byte, error) {
   317  	hmacBuf := pool.GetBytes(6)
   318  	defer pool.PutBytes(hmacBuf)
   319  	if !bytes.Equal(a.hmac.HMAC(a.info.Key(), b[:len(b)-4], hmacBuf)[:4], b[len(b)-4:]) {
   320  		return nil, ssr.ErrAuthAES128IncorrectChecksum
   321  	}
   322  
   323  	return b[:len(b)-4], nil
   324  }
   325  
   326  func (a *authAES128) GetOverhead() int { return 9 }