1
0
mirror of https://github.com/MetaCubeX/mihomo.git synced 2025-09-20 04:25:59 +08:00
Files
mihomo/transport/vless/encryption/client.go

276 lines
7.0 KiB
Go
Raw Normal View History

2025-08-10 22:16:25 +08:00
package encryption
import (
"bytes"
"crypto/cipher"
2025-08-21 08:33:44 +08:00
"crypto/ecdh"
2025-08-10 22:16:25 +08:00
"crypto/rand"
"errors"
2025-08-13 18:51:47 +08:00
"fmt"
2025-08-10 22:16:25 +08:00
"io"
"net"
"runtime"
2025-08-14 18:31:56 +08:00
"strings"
2025-08-10 22:16:25 +08:00
"sync"
"time"
"github.com/metacubex/utls/mlkem"
2025-08-18 23:08:30 +08:00
"golang.org/x/crypto/sha3"
2025-08-10 22:16:25 +08:00
"golang.org/x/sys/cpu"
)
var (
// Keep in sync with crypto/tls/cipher_suites.go.
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
HasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
)
var ClientCipher byte
func init() {
2025-08-12 20:06:08 +08:00
if HasAESGCMHardwareSupport {
2025-08-10 22:16:25 +08:00
ClientCipher = 1
}
}
type ClientInstance struct {
sync.RWMutex
2025-08-14 18:31:56 +08:00
nfsEKey *mlkem.EncapsulationKey768
2025-08-18 08:43:17 +08:00
hash11 [11]byte // no more capacity
2025-08-21 08:33:44 +08:00
xorMode uint32
xorPKey *ecdh.PublicKey
2025-08-14 18:31:56 +08:00
minutes time.Duration
expire time.Time
baseKey []byte
ticket []byte
2025-08-10 22:16:25 +08:00
}
type ClientConn struct {
net.Conn
instance *ClientInstance
baseKey []byte
ticket []byte
2025-08-10 22:16:25 +08:00
random []byte
aead cipher.AEAD
nonce []byte
2025-08-21 19:42:56 +08:00
peerAEAD cipher.AEAD
2025-08-10 22:16:25 +08:00
peerNonce []byte
2025-08-18 09:34:20 +08:00
input bytes.Reader // peerCache
2025-08-10 22:16:25 +08:00
}
2025-08-21 08:33:44 +08:00
func (i *ClientInstance) Init(nfsEKeyBytes, xorPKeyBytes []byte, xorMode, minutes uint32) (err error) {
2025-08-14 18:31:56 +08:00
if i.nfsEKey != nil {
err = errors.New("already initialized")
return
}
2025-08-21 08:33:44 +08:00
if i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes); err != nil {
2025-08-14 18:31:56 +08:00
return
}
2025-08-19 21:37:02 +08:00
hash32 := sha3.Sum256(nfsEKeyBytes)
copy(i.hash11[:], hash32[:])
2025-08-21 08:33:44 +08:00
if xorMode > 0 {
i.xorMode = xorMode
if i.xorPKey, err = ecdh.X25519().NewPublicKey(xorPKeyBytes); err != nil {
return
}
2025-08-12 20:06:08 +08:00
}
2025-08-21 08:33:44 +08:00
i.minutes = time.Duration(minutes) * time.Minute
2025-08-10 22:16:25 +08:00
return
}
2025-08-19 21:37:02 +08:00
func (i *ClientInstance) Handshake(conn net.Conn) (*ClientConn, error) {
2025-08-12 20:06:08 +08:00
if i.nfsEKey == nil {
2025-08-10 22:16:25 +08:00
return nil, errors.New("uninitialized")
}
2025-08-21 08:33:44 +08:00
if i.xorMode > 0 {
conn, _ = NewXorConn(conn, i.xorMode, i.xorPKey, nil)
}
2025-08-10 22:16:25 +08:00
c := &ClientConn{Conn: conn}
if i.minutes > 0 {
i.RLock()
if time.Now().Before(i.expire) {
c.instance = i
c.baseKey = i.baseKey
c.ticket = i.ticket
2025-08-10 22:16:25 +08:00
i.RUnlock()
return c, nil
}
i.RUnlock()
}
2025-08-12 20:06:08 +08:00
pfsDKeySeed := make([]byte, 64)
rand.Read(pfsDKeySeed)
pfsDKey, _ := mlkem.NewDecapsulationKey768(pfsDKeySeed)
pfsEKeyBytes := pfsDKey.EncapsulationKey().Bytes()
nfsKey, encapsulatedNfsKey := i.nfsEKey.Encapsulate()
2025-08-21 19:42:56 +08:00
nfsAEAD := NewAEAD(ClientCipher, nfsKey, pfsEKeyBytes, encapsulatedNfsKey)
2025-08-10 22:16:25 +08:00
2025-08-21 19:42:56 +08:00
clientHello := make([]byte, 5+11+1+1184+1088+randBetween(100, 1000))
2025-08-18 08:43:17 +08:00
EncodeHeader(clientHello, 1, 11+1+1184+1088)
copy(clientHello[5:], i.hash11[:])
clientHello[5+11] = ClientCipher
copy(clientHello[5+11+1:], pfsEKeyBytes)
copy(clientHello[5+11+1+1184:], encapsulatedNfsKey)
2025-08-21 19:42:56 +08:00
padding := clientHello[5+11+1+1184+1088:]
rand.Read(padding) // important
EncodeHeader(padding, 23, len(padding)-5)
nfsAEAD.Seal(padding[:5], clientHello[5:5+11+1], padding[5:len(padding)-16], padding[:5])
2025-08-10 22:16:25 +08:00
2025-08-13 20:50:46 +08:00
if _, err := c.Conn.Write(clientHello); err != nil {
2025-08-10 22:16:25 +08:00
return nil, err
}
2025-08-21 19:42:56 +08:00
// client can send more NFS AEAD paddings / messages if needed
2025-08-13 18:51:47 +08:00
2025-08-14 23:52:05 +08:00
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before server hello
2025-08-13 18:51:47 +08:00
if err != nil {
return nil, err
}
2025-08-14 18:31:56 +08:00
2025-08-13 18:51:47 +08:00
if t != 1 {
2025-08-18 08:40:52 +08:00
return nil, fmt.Errorf("unexpected type %v, expect server hello", t)
2025-08-13 18:51:47 +08:00
}
2025-08-18 08:40:52 +08:00
peerServerHello := make([]byte, 1088+21)
if l != len(peerServerHello) {
return nil, fmt.Errorf("unexpected length %v for server hello", l)
2025-08-13 18:51:47 +08:00
}
2025-08-18 08:40:52 +08:00
if _, err := io.ReadFull(c.Conn, peerServerHello); err != nil {
2025-08-10 22:16:25 +08:00
return nil, err
}
2025-08-18 08:40:52 +08:00
encapsulatedPfsKey := peerServerHello[:1088]
2025-08-18 08:43:17 +08:00
c.ticket = append(i.hash11[:], peerServerHello[1088:]...)
2025-08-10 22:16:25 +08:00
2025-08-12 20:06:08 +08:00
pfsKey, err := pfsDKey.Decapsulate(encapsulatedPfsKey)
2025-08-10 22:16:25 +08:00
if err != nil {
return nil, err
}
2025-08-12 20:06:08 +08:00
c.baseKey = append(pfsKey, nfsKey...)
2025-08-10 22:16:25 +08:00
2025-08-21 08:33:44 +08:00
VLESS, _ := NewAEAD(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes)
2025-08-13 18:51:47 +08:00
if !bytes.Equal(VLESS, []byte("VLESS")) {
2025-08-10 22:16:25 +08:00
return nil, errors.New("invalid server")
}
if i.minutes > 0 {
i.Lock()
i.expire = time.Now().Add(i.minutes)
i.baseKey = c.baseKey
i.ticket = c.ticket
2025-08-10 22:16:25 +08:00
i.Unlock()
}
return c, nil
}
func (c *ClientConn) Write(b []byte) (int, error) {
if len(b) == 0 {
return 0, nil
}
2025-08-12 22:59:25 +08:00
var data []byte
2025-08-12 20:06:08 +08:00
for n := 0; n < len(b); {
b := b[n:]
if len(b) > 8192 {
b = b[:8192] // for avoiding another copy() in server's Read()
}
n += len(b)
if c.aead == nil {
2025-08-18 08:43:17 +08:00
data = make([]byte, 5+32+32+5+len(b)+16)
EncodeHeader(data, 0, 32+32)
copy(data[5:], c.ticket)
2025-08-12 20:06:08 +08:00
c.random = make([]byte, 32)
rand.Read(c.random)
2025-08-18 08:43:17 +08:00
copy(data[5+32:], c.random)
EncodeHeader(data[5+32+32:], 23, len(b)+16)
2025-08-21 08:33:44 +08:00
c.aead = NewAEAD(ClientCipher, c.baseKey, c.random, c.ticket)
2025-08-12 20:06:08 +08:00
c.nonce = make([]byte, 12)
2025-08-18 08:43:17 +08:00
c.aead.Seal(data[:5+32+32+5], c.nonce, b, data[5+32+32:5+32+32+5])
2025-08-12 20:06:08 +08:00
} else {
data = make([]byte, 5+len(b)+16)
2025-08-13 18:51:47 +08:00
EncodeHeader(data, 23, len(b)+16)
2025-08-12 20:06:08 +08:00
c.aead.Seal(data[:5], c.nonce, b, data[:5])
2025-08-12 22:59:25 +08:00
if bytes.Equal(c.nonce, MaxNonce) {
2025-08-21 08:33:44 +08:00
c.aead = NewAEAD(ClientCipher, c.baseKey, data[5:], data[:5])
2025-08-12 22:59:25 +08:00
}
2025-08-12 20:06:08 +08:00
}
2025-08-12 22:59:25 +08:00
IncreaseNonce(c.nonce)
2025-08-13 20:50:46 +08:00
if _, err := c.Conn.Write(data); err != nil {
2025-08-12 20:06:08 +08:00
return 0, err
}
2025-08-10 22:16:25 +08:00
}
return len(b), nil
}
2025-08-12 20:06:08 +08:00
func (c *ClientConn) Read(b []byte) (int, error) {
2025-08-10 22:16:25 +08:00
if len(b) == 0 {
return 0, nil
}
2025-08-21 19:42:56 +08:00
if c.peerAEAD == nil {
2025-08-14 23:52:05 +08:00
_, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello
2025-08-14 18:31:56 +08:00
if err != nil {
2025-08-14 23:52:05 +08:00
if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT
2025-08-13 18:51:47 +08:00
c.instance.Lock()
if bytes.Equal(c.ticket, c.instance.ticket) {
c.instance.expire = time.Now() // expired
}
c.instance.Unlock()
return 0, errors.New("new handshake needed")
}
2025-08-14 18:31:56 +08:00
return 0, err
2025-08-13 18:51:47 +08:00
}
if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect server random", t)
2025-08-10 22:16:25 +08:00
}
2025-08-18 08:40:52 +08:00
peerRandomHello := make([]byte, 32)
if l != len(peerRandomHello) {
2025-08-13 18:51:47 +08:00
return 0, fmt.Errorf("unexpected length %v for server random", l)
}
2025-08-18 08:40:52 +08:00
if _, err := io.ReadFull(c.Conn, peerRandomHello); err != nil {
2025-08-10 22:16:25 +08:00
return 0, err
}
if c.random == nil {
2025-08-12 20:06:08 +08:00
return 0, errors.New("empty c.random")
2025-08-10 22:16:25 +08:00
}
2025-08-21 19:42:56 +08:00
c.peerAEAD = NewAEAD(ClientCipher, c.baseKey, peerRandomHello, c.random)
2025-08-10 22:16:25 +08:00
c.peerNonce = make([]byte, 12)
}
2025-08-18 09:34:20 +08:00
if c.input.Len() > 0 {
return c.input.Read(b)
2025-08-10 22:16:25 +08:00
}
2025-08-13 18:51:47 +08:00
h, t, l, err := ReadAndDecodeHeader(c.Conn) // l: 17~17000
2025-08-10 22:16:25 +08:00
if err != nil {
return 0, err
}
2025-08-13 18:51:47 +08:00
if t != 23 {
return 0, fmt.Errorf("unexpected type %v, expect encrypted data", t)
}
peerData := make([]byte, l)
2025-08-10 22:16:25 +08:00
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
return 0, err
}
2025-08-13 18:51:47 +08:00
dst := peerData[:l-16]
2025-08-10 22:16:25 +08:00
if len(dst) <= len(b) {
2025-08-12 20:06:08 +08:00
dst = b[:len(dst)] // avoids another copy()
2025-08-10 22:16:25 +08:00
}
2025-08-21 19:42:56 +08:00
var peerAEAD cipher.AEAD
2025-08-12 22:59:25 +08:00
if bytes.Equal(c.peerNonce, MaxNonce) {
2025-08-21 19:42:56 +08:00
peerAEAD = NewAEAD(ClientCipher, c.baseKey, peerData, h)
2025-08-12 22:59:25 +08:00
}
2025-08-21 19:42:56 +08:00
_, err = c.peerAEAD.Open(dst[:0], c.peerNonce, peerData, h)
if peerAEAD != nil {
c.peerAEAD = peerAEAD
2025-08-12 22:59:25 +08:00
}
IncreaseNonce(c.peerNonce)
2025-08-10 22:16:25 +08:00
if err != nil {
return 0, err
}
if len(dst) > len(b) {
2025-08-18 09:34:20 +08:00
c.input.Reset(dst[copy(b, dst):])
2025-08-10 22:16:25 +08:00
dst = b // for len(dst)
}
return len(dst), nil
}