diff --git a/transport/vless/encryption/client.go b/transport/vless/encryption/client.go index 084ef42e..726e98d8 100644 --- a/transport/vless/encryption/client.go +++ b/transport/vless/encryption/client.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/cipher" "crypto/rand" - "crypto/sha256" "errors" "io" "net" @@ -13,7 +12,6 @@ import ( "time" "github.com/metacubex/utls/mlkem" - "golang.org/x/crypto/hkdf" "golang.org/x/sys/cpu" ) @@ -101,7 +99,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { clientHello[0] = ClientCipher copy(clientHello[1:], pfsEKeyBytes) copy(clientHello[1185:], encapsulatedNfsKey) - encodeHeader(clientHello[2273:], int(paddingLen)) + EncodeHeader(clientHello[2273:], int(paddingLen)) rand.Read(clientHello[2278:]) if _, err := c.Conn.Write(clientHello); err != nil { @@ -122,11 +120,9 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { } c.baseKey = append(pfsKey, nfsKey...) - authKey := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Read(authKey) nonce := [12]byte{ClientCipher} - VLESS, _ := newAead(ClientCipher, authKey).Open(nil, nonce[:], c.ticket, pfsEKeyBytes) - if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more message + VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, nonce[:], c.ticket, pfsEKeyBytes) + if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more messages return nil, errors.New("invalid server") } @@ -145,32 +141,32 @@ func (c *ClientConn) Write(b []byte) (int, error) { if len(b) == 0 { return 0, nil } + var data []byte 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) - var data []byte if c.aead == nil { c.random = make([]byte, 32) rand.Read(c.random) - key := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, c.random, c.ticket).Read(key) - c.aead = newAead(ClientCipher, key) + c.aead = NewAead(ClientCipher, c.baseKey, c.random, c.ticket) c.nonce = make([]byte, 12) - data = make([]byte, 21+32+5+len(b)+16) copy(data, c.ticket) copy(data[21:], c.random) - encodeHeader(data[53:], len(b)+16) + EncodeHeader(data[53:], len(b)+16) c.aead.Seal(data[:58], c.nonce, b, data[53:58]) } else { data = make([]byte, 5+len(b)+16) - encodeHeader(data, len(b)+16) + EncodeHeader(data, len(b)+16) c.aead.Seal(data[:5], c.nonce, b, data[:5]) + if bytes.Equal(c.nonce, MaxNonce) { + c.aead = NewAead(ClientCipher, c.baseKey, data[5:], data[:5]) + } } - increaseNonce(c.nonce) + IncreaseNonce(c.nonce) if _, err := c.Conn.Write(data); err != nil { return 0, err } @@ -189,7 +185,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { if _, err := io.ReadFull(c.Conn, peerHeader); err != nil { return 0, err } - peerPaddingLen, _ := decodeHeader(peerHeader) + peerPaddingLen, _ := DecodeHeader(peerHeader) if peerPaddingLen == 0 { break } @@ -210,9 +206,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { if c.random == nil { return 0, errors.New("empty c.random") } - peerKey := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, peerRandom, c.random).Read(peerKey) - c.peerAead = newAead(ClientCipher, peerKey) + c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandom, c.random) c.peerNonce = make([]byte, 12) } if len(c.peerCache) != 0 { @@ -223,7 +217,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { if _, err := io.ReadFull(c.Conn, peerHeader); err != nil { return 0, err } - peerLength, err := decodeHeader(peerHeader) // 17~17000 + peerLength, err := DecodeHeader(peerHeader) // 17~17000 if err != nil { if c.instance != nil { c.instance.Lock() @@ -242,8 +236,15 @@ func (c *ClientConn) Read(b []byte) (int, error) { if len(dst) <= len(b) { dst = b[:len(dst)] // avoids another copy() } + var peerAead cipher.AEAD + if bytes.Equal(c.peerNonce, MaxNonce) { + peerAead = NewAead(ClientCipher, c.baseKey, peerData, peerHeader) + } _, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader) - increaseNonce(c.peerNonce) + if peerAead != nil { + c.peerAead = peerAead + } + IncreaseNonce(c.peerNonce) if err != nil { return 0, err } diff --git a/transport/vless/encryption/common.go b/transport/vless/encryption/common.go index 7d1618ba..32222f18 100644 --- a/transport/vless/encryption/common.go +++ b/transport/vless/encryption/common.go @@ -1,17 +1,22 @@ package encryption import ( + "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" + "crypto/sha256" "errors" "math/big" "strconv" "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/hkdf" ) -func encodeHeader(b []byte, l int) { +var MaxNonce = bytes.Repeat([]byte{255}, 12) + +func EncodeHeader(b []byte, l int) { b[0] = 23 b[1] = 3 b[2] = 3 @@ -19,10 +24,10 @@ func encodeHeader(b []byte, l int) { b[4] = byte(l) } -func decodeHeader(b []byte) (int, error) { +func DecodeHeader(b []byte) (int, error) { if b[0] == 23 && b[1] == 3 && b[2] == 3 { l := int(b[3])<<8 | int(b[4]) - if l < 17 || l > 17000 { // TODO + if l < 17 || l > 17000 { // TODO: TLSv1.3 max length return 0, errors.New("invalid length in record's header: " + strconv.Itoa(l)) } return l, nil @@ -30,25 +35,24 @@ func decodeHeader(b []byte) (int, error) { return 0, errors.New("invalid record's header") } -func newAead(c byte, k []byte) (aead cipher.AEAD) { +func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) { + key := make([]byte, 32) + hkdf.New(sha256.New, secret, salt, info).Read(key) if c&1 == 1 { - block, _ := aes.NewCipher(k) + block, _ := aes.NewCipher(key) aead, _ = cipher.NewGCM(block) } else { - aead, _ = chacha20poly1305.New(k) + aead, _ = chacha20poly1305.New(key) } return } -func increaseNonce(nonce []byte) { +func IncreaseNonce(nonce []byte) { for i := 0; i < 12; i++ { nonce[11-i]++ if nonce[11-i] != 0 { break } - if i == 11 { - // TODO - } } } diff --git a/transport/vless/encryption/doc.go b/transport/vless/encryption/doc.go index 0361a935..f26826b6 100644 --- a/transport/vless/encryption/doc.go +++ b/transport/vless/encryption/doc.go @@ -3,4 +3,5 @@ // https://github.com/XTLS/Xray-core/commit/3e19bf9233bdd9bafc073a71c65b737cc1ffba5e // https://github.com/XTLS/Xray-core/commit/7ffb555fc8ec51bd1e3e60f26f1d6957984dba80 // https://github.com/XTLS/Xray-core/commit/ec1cc35188c1a5f38a2ff75e88b5d043ffdc59da +// https://github.com/XTLS/Xray-core/commit/5c611420487a92f931faefc01d4bf03869f477f6 package encryption diff --git a/transport/vless/encryption/server.go b/transport/vless/encryption/server.go index 15b17bba..35b88fb2 100644 --- a/transport/vless/encryption/server.go +++ b/transport/vless/encryption/server.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/cipher" "crypto/rand" - "crypto/sha256" "errors" "io" "net" @@ -12,7 +11,6 @@ import ( "time" "github.com/metacubex/utls/mlkem" - "golang.org/x/crypto/hkdf" ) type ServerSession struct { @@ -113,7 +111,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if _, err := io.ReadFull(c.Conn, peerHeader); err != nil { return nil, err } - if l, _ := decodeHeader(peerHeader); l != 0 { + if l, _ := DecodeHeader(peerHeader); l != 0 { noise := make([]byte, randBetween(100, 1000)) rand.Read(noise) c.Conn.Write(noise) // make client do new handshake @@ -141,17 +139,15 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate() c.baseKey = append(pfsKey, nfsKey...) - authKey := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Read(authKey) nonce := [12]byte{c.cipher} - c.ticket = newAead(c.cipher, authKey).Seal(nil, nonce[:], []byte("VLESS"), pfsEKeyBytes) + c.ticket = NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, nonce[:], []byte("VLESS"), pfsEKeyBytes) paddingLen := randBetween(100, 1000) serverHello := make([]byte, 1088+21+5+paddingLen) copy(serverHello, encapsulatedPfsKey) copy(serverHello[1088:], c.ticket) - encodeHeader(serverHello[1109:], int(paddingLen)) + EncodeHeader(serverHello[1109:], int(paddingLen)) rand.Read(serverHello[1114:]) if _, err := c.Conn.Write(serverHello); err != nil { @@ -183,7 +179,7 @@ func (c *ServerConn) Read(b []byte) (int, error) { if _, err := io.ReadFull(c.Conn, peerHeader); err != nil { return 0, err } - peerPaddingLen, _ := decodeHeader(peerHeader) + peerPaddingLen, _ := DecodeHeader(peerHeader) if peerPaddingLen == 0 { break } @@ -204,9 +200,7 @@ func (c *ServerConn) Read(b []byte) (int, error) { return 0, err } } - peerKey := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, c.peerRandom, c.ticket).Read(peerKey) - c.peerAead = newAead(c.cipher, peerKey) + c.peerAead = NewAead(c.cipher, c.baseKey, c.peerRandom, c.ticket) c.peerNonce = make([]byte, 12) } if len(c.peerCache) != 0 { @@ -217,7 +211,7 @@ func (c *ServerConn) Read(b []byte) (int, error) { if _, err := io.ReadFull(c.Conn, peerHeader); err != nil { return 0, err } - peerLength, err := decodeHeader(peerHeader) // 17~17000 + peerLength, err := DecodeHeader(peerHeader) // 17~17000 if err != nil { return 0, err } @@ -229,8 +223,15 @@ func (c *ServerConn) Read(b []byte) (int, error) { if len(dst) <= len(b) { dst = b[:len(dst)] // avoids another copy() } + var peerAead cipher.AEAD + if bytes.Equal(c.peerNonce, MaxNonce) { + peerAead = NewAead(ClientCipher, c.baseKey, peerData, peerHeader) + } _, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader) - increaseNonce(c.peerNonce) + if peerAead != nil { + c.peerAead = peerAead + } + IncreaseNonce(c.peerNonce) if err != nil { return 0, errors.New("error") } @@ -245,31 +246,32 @@ func (c *ServerConn) Write(b []byte) (int, error) { if len(b) == 0 { return 0, nil } + var data []byte for n := 0; n < len(b); { b := b[n:] if len(b) > 8192 { b = b[:8192] // for avoiding another copy() in client's Read() } n += len(b) - var data []byte if c.aead == nil { if c.peerRandom == nil { return 0, errors.New("empty c.peerRandom") } data = make([]byte, 32+5+len(b)+16) rand.Read(data[:32]) - key := make([]byte, 32) - hkdf.New(sha256.New, c.baseKey, data[:32], c.peerRandom).Read(key) - c.aead = newAead(c.cipher, key) + c.aead = NewAead(c.cipher, c.baseKey, data[:32], c.peerRandom) c.nonce = make([]byte, 12) - encodeHeader(data[32:], len(b)+16) + EncodeHeader(data[32:], len(b)+16) c.aead.Seal(data[:37], c.nonce, b, data[32:37]) } else { data = make([]byte, 5+len(b)+16) - encodeHeader(data, len(b)+16) + EncodeHeader(data, len(b)+16) c.aead.Seal(data[:5], c.nonce, b, data[:5]) + if bytes.Equal(c.nonce, MaxNonce) { + c.aead = NewAead(ClientCipher, c.baseKey, data[5:], data[:5]) + } } - increaseNonce(c.nonce) + IncreaseNonce(c.nonce) if _, err := c.Conn.Write(data); err != nil { return 0, err }