mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-12-20 00:50:06 +08:00
chore: sync vless encryption code
Some checks are pending
Test / test (1.20, macos-13) (push) Waiting to run
Test / test (1.20, macos-latest) (push) Waiting to run
Test / test (1.20, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.20, ubuntu-latest) (push) Waiting to run
Test / test (1.20, windows-latest) (push) Waiting to run
Test / test (1.21, macos-13) (push) Waiting to run
Test / test (1.21, macos-latest) (push) Waiting to run
Test / test (1.21, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.21, ubuntu-latest) (push) Waiting to run
Test / test (1.21, windows-latest) (push) Waiting to run
Test / test (1.22, macos-13) (push) Waiting to run
Test / test (1.22, macos-latest) (push) Waiting to run
Test / test (1.22, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.22, ubuntu-latest) (push) Waiting to run
Test / test (1.22, windows-latest) (push) Waiting to run
Test / test (1.23, macos-13) (push) Waiting to run
Test / test (1.23, macos-latest) (push) Waiting to run
Test / test (1.23, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.23, ubuntu-latest) (push) Waiting to run
Test / test (1.23, windows-latest) (push) Waiting to run
Test / test (1.24, macos-13) (push) Waiting to run
Test / test (1.24, macos-latest) (push) Waiting to run
Test / test (1.24, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.24, ubuntu-latest) (push) Waiting to run
Test / test (1.24, windows-latest) (push) Waiting to run
Test / test (1.25, macos-13) (push) Waiting to run
Test / test (1.25, macos-latest) (push) Waiting to run
Test / test (1.25, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.25, ubuntu-latest) (push) Waiting to run
Test / test (1.25, windows-latest) (push) Waiting to run
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run
Some checks are pending
Test / test (1.20, macos-13) (push) Waiting to run
Test / test (1.20, macos-latest) (push) Waiting to run
Test / test (1.20, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.20, ubuntu-latest) (push) Waiting to run
Test / test (1.20, windows-latest) (push) Waiting to run
Test / test (1.21, macos-13) (push) Waiting to run
Test / test (1.21, macos-latest) (push) Waiting to run
Test / test (1.21, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.21, ubuntu-latest) (push) Waiting to run
Test / test (1.21, windows-latest) (push) Waiting to run
Test / test (1.22, macos-13) (push) Waiting to run
Test / test (1.22, macos-latest) (push) Waiting to run
Test / test (1.22, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.22, ubuntu-latest) (push) Waiting to run
Test / test (1.22, windows-latest) (push) Waiting to run
Test / test (1.23, macos-13) (push) Waiting to run
Test / test (1.23, macos-latest) (push) Waiting to run
Test / test (1.23, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.23, ubuntu-latest) (push) Waiting to run
Test / test (1.23, windows-latest) (push) Waiting to run
Test / test (1.24, macos-13) (push) Waiting to run
Test / test (1.24, macos-latest) (push) Waiting to run
Test / test (1.24, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.24, ubuntu-latest) (push) Waiting to run
Test / test (1.24, windows-latest) (push) Waiting to run
Test / test (1.25, macos-13) (push) Waiting to run
Test / test (1.25, macos-latest) (push) Waiting to run
Test / test (1.25, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.25, ubuntu-latest) (push) Waiting to run
Test / test (1.25, windows-latest) (push) Waiting to run
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run
This commit is contained in:
parent
2605bf78f9
commit
aca0d97beb
@ -75,14 +75,13 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
iv := clientHello[:16]
|
iv := clientHello[:16]
|
||||||
rand.Read(iv)
|
rand.Read(iv)
|
||||||
relays := clientHello[16:ivAndRealysLength]
|
relays := clientHello[16:ivAndRealysLength]
|
||||||
var nfsPublicKey, nfsKey []byte
|
var nfsKey []byte
|
||||||
var lastCTR cipher.Stream
|
var lastCTR cipher.Stream
|
||||||
for j, k := range i.NfsPKeys {
|
for j, k := range i.NfsPKeys {
|
||||||
var index = 32
|
var index = 32
|
||||||
if k, ok := k.(*ecdh.PublicKey); ok {
|
if k, ok := k.(*ecdh.PublicKey); ok {
|
||||||
privateKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
|
privateKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
|
||||||
nfsPublicKey = privateKey.PublicKey().Bytes()
|
copy(relays, privateKey.PublicKey().Bytes())
|
||||||
copy(relays, nfsPublicKey)
|
|
||||||
var err error
|
var err error
|
||||||
nfsKey, err = privateKey.ECDH(k)
|
nfsKey, err = privateKey.ECDH(k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -90,11 +89,12 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if k, ok := k.(*mlkem.EncapsulationKey768); ok {
|
if k, ok := k.(*mlkem.EncapsulationKey768); ok {
|
||||||
nfsKey, nfsPublicKey = k.Encapsulate()
|
var ciphertext []byte
|
||||||
copy(relays, nfsPublicKey)
|
nfsKey, ciphertext = k.Encapsulate()
|
||||||
|
copy(relays, ciphertext)
|
||||||
index = 1088
|
index = 1088
|
||||||
}
|
}
|
||||||
if i.XorMode > 0 { // this xor can (others can't) be decrypted by client's config, revealing an X25519 public key / ML-KEM-768 ciphertext, but it is not important
|
if i.XorMode > 0 { // this xor can (others can't) be recovered by client's config, revealing an X25519 public key / ML-KEM-768 ciphertext, that's why "native" values
|
||||||
NewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // make X25519 public key / ML-KEM-768 ciphertext distinguishable from random bytes
|
NewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // make X25519 public key / ML-KEM-768 ciphertext distinguishable from random bytes
|
||||||
}
|
}
|
||||||
if lastCTR != nil {
|
if lastCTR != nil {
|
||||||
@ -107,20 +107,20 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
lastCTR.XORKeyStream(relays[index:], i.Hash32s[j+1][:])
|
lastCTR.XORKeyStream(relays[index:], i.Hash32s[j+1][:])
|
||||||
relays = relays[index+32:]
|
relays = relays[index+32:]
|
||||||
}
|
}
|
||||||
nfsGCM := NewGCM(nfsPublicKey, nfsKey)
|
nfsGCM := NewGCM(iv, nfsKey)
|
||||||
|
|
||||||
if i.Seconds > 0 {
|
if i.Seconds > 0 {
|
||||||
i.RWLock.RLock()
|
i.RWLock.RLock()
|
||||||
if time.Now().Before(i.Expire) {
|
if time.Now().Before(i.Expire) {
|
||||||
c.Client = i
|
c.Client = i
|
||||||
c.UnitedKey = append(i.PfsKey, nfsKey...)
|
c.UnitedKey = append(i.PfsKey, nfsKey...) // different unitedKey for each connection
|
||||||
nfsGCM.Seal(clientHello[:ivAndRealysLength], nil, EncodeLength(32), nil)
|
nfsGCM.Seal(clientHello[:ivAndRealysLength], nil, EncodeLength(32), nil)
|
||||||
nfsGCM.Seal(clientHello[:ivAndRealysLength+18], nil, i.Ticket, nil)
|
nfsGCM.Seal(clientHello[:ivAndRealysLength+18], nil, i.Ticket, nil)
|
||||||
i.RWLock.RUnlock()
|
i.RWLock.RUnlock()
|
||||||
c.PreWrite = clientHello[:ivAndRealysLength+18+32]
|
c.PreWrite = clientHello[:ivAndRealysLength+18+32]
|
||||||
c.GCM = NewGCM(clientHello[ivAndRealysLength+18:ivAndRealysLength+18+32], c.UnitedKey)
|
c.GCM = NewGCM(clientHello[ivAndRealysLength+18:ivAndRealysLength+18+32], c.UnitedKey)
|
||||||
if i.XorMode == 2 {
|
if i.XorMode == 2 {
|
||||||
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), nil, len(c.PreWrite), 32)
|
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), nil, len(c.PreWrite), 16)
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
@ -141,21 +141,9 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
if _, err := conn.Write(clientHello); err != nil {
|
if _, err := conn.Write(clientHello); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before VLESS flow takes control
|
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||||
|
|
||||||
encryptedLength := make([]byte, 18)
|
encryptedPfsPublicKey := make([]byte, 1088+32+16)
|
||||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := nfsGCM.Open(encryptedLength[:0], make([]byte, 12), encryptedLength, nil); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
length := DecodeLength(encryptedLength[:2])
|
|
||||||
|
|
||||||
if length < 1088+32+16 { // server may send more public keys
|
|
||||||
return nil, errors.New("too short length")
|
|
||||||
}
|
|
||||||
encryptedPfsPublicKey := make([]byte, length)
|
|
||||||
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -194,22 +182,18 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
i.RWLock.Unlock()
|
i.RWLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encryptedLength := make([]byte, 18)
|
||||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, err := c.PeerGCM.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
if _, err := c.PeerGCM.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
encryptedPadding := make([]byte, DecodeLength(encryptedLength[:2])) // TODO: move to Read()
|
length := DecodeLength(encryptedLength[:2])
|
||||||
if _, err := io.ReadFull(conn, encryptedPadding); err != nil {
|
c.PeerPadding = make([]byte, length) // important: allows server sends padding slowly, eliminating 1-RTT's traffic pattern
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := c.PeerGCM.Open(encryptedPadding[:0], nil, encryptedPadding, nil); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.XorMode == 2 {
|
if i.XorMode == 2 {
|
||||||
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), NewCTR(c.UnitedKey, encryptedTicket[:16]), 0, 0)
|
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), NewCTR(c.UnitedKey, encryptedTicket[:16]), 0, length)
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ type CommonConn struct {
|
|||||||
UnitedKey []byte
|
UnitedKey []byte
|
||||||
PreWrite []byte
|
PreWrite []byte
|
||||||
GCM *GCM
|
GCM *GCM
|
||||||
|
PeerPadding []byte
|
||||||
PeerGCM *GCM
|
PeerGCM *GCM
|
||||||
input bytes.Reader // PeerCache
|
input bytes.Reader // PeerCache
|
||||||
}
|
}
|
||||||
@ -39,12 +40,12 @@ func (c *CommonConn) Write(b []byte) (int, error) {
|
|||||||
n += len(b)
|
n += len(b)
|
||||||
data = make([]byte, 5+len(b)+16)
|
data = make([]byte, 5+len(b)+16)
|
||||||
EncodeHeader(data, len(b)+16)
|
EncodeHeader(data, len(b)+16)
|
||||||
aead := c.GCM
|
max := false
|
||||||
if bytes.Equal(c.GCM.Nonce[:], MaxNonce) {
|
if bytes.Equal(c.GCM.Nonce[:], MaxNonce) {
|
||||||
aead = nil
|
max = true
|
||||||
}
|
}
|
||||||
c.GCM.Seal(data[:5], nil, b, data[:5])
|
c.GCM.Seal(data[:5], nil, b, data[:5])
|
||||||
if aead == nil {
|
if max {
|
||||||
c.GCM = NewGCM(data[5:], c.UnitedKey)
|
c.GCM = NewGCM(data[5:], c.UnitedKey)
|
||||||
}
|
}
|
||||||
if c.PreWrite != nil {
|
if c.PreWrite != nil {
|
||||||
@ -63,15 +64,24 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if c.PeerGCM == nil { // client's 0-RTT
|
if c.PeerGCM == nil { // client's 0-RTT
|
||||||
serverRandom := make([]byte, 32)
|
serverRandom := make([]byte, 16)
|
||||||
if _, err := io.ReadFull(c.Conn, serverRandom); err != nil {
|
if _, err := io.ReadFull(c.Conn, serverRandom); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
c.PeerGCM = NewGCM(serverRandom, c.UnitedKey)
|
c.PeerGCM = NewGCM(serverRandom, c.UnitedKey)
|
||||||
if xorConn, ok := c.Conn.(*XorConn); ok {
|
if xorConn, ok := c.Conn.(*XorConn); ok {
|
||||||
xorConn.PeerCTR = NewCTR(c.UnitedKey, serverRandom[16:])
|
xorConn.PeerCTR = NewCTR(c.UnitedKey, serverRandom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if c.PeerPadding != nil { // client's 1-RTT
|
||||||
|
if _, err := io.ReadFull(c.Conn, c.PeerPadding); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if _, err := c.PeerGCM.Open(c.PeerPadding[:0], nil, c.PeerPadding, nil); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
c.PeerPadding = nil
|
||||||
|
}
|
||||||
if c.input.Len() > 0 {
|
if c.input.Len() > 0 {
|
||||||
return c.input.Read(b)
|
return c.input.Read(b)
|
||||||
}
|
}
|
||||||
@ -96,13 +106,13 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
|||||||
if len(dst) <= len(b) {
|
if len(dst) <= len(b) {
|
||||||
dst = b[:len(dst)] // avoids another copy()
|
dst = b[:len(dst)] // avoids another copy()
|
||||||
}
|
}
|
||||||
var peerAEAD *GCM
|
var newGCM *GCM
|
||||||
if bytes.Equal(c.PeerGCM.Nonce[:], MaxNonce) {
|
if bytes.Equal(c.PeerGCM.Nonce[:], MaxNonce) {
|
||||||
peerAEAD = NewGCM(peerData, c.UnitedKey)
|
newGCM = NewGCM(peerData, c.UnitedKey)
|
||||||
}
|
}
|
||||||
_, err = c.PeerGCM.Open(dst[:0], nil, peerData, h)
|
_, err = c.PeerGCM.Open(dst[:0], nil, peerData, h)
|
||||||
if peerAEAD != nil {
|
if newGCM != nil {
|
||||||
c.PeerGCM = peerAEAD
|
c.PeerGCM = newGCM
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|||||||
@ -18,4 +18,5 @@
|
|||||||
// https://github.com/XTLS/Xray-core/commit/38cc306c955c362f044e074049a5e67b6b9fb389
|
// https://github.com/XTLS/Xray-core/commit/38cc306c955c362f044e074049a5e67b6b9fb389
|
||||||
// https://github.com/XTLS/Xray-core/commit/b33555cc0a52d0af3c23d2af8fca42f8a685d9af
|
// https://github.com/XTLS/Xray-core/commit/b33555cc0a52d0af3c23d2af8fca42f8a685d9af
|
||||||
// https://github.com/XTLS/Xray-core/commit/ad7140641c44239c9dcdc3d7215ea639b1f0841c
|
// https://github.com/XTLS/Xray-core/commit/ad7140641c44239c9dcdc3d7215ea639b1f0841c
|
||||||
|
// https://github.com/XTLS/Xray-core/commit/0199dea39988a1a1b846d0bf8598631bade40902
|
||||||
package encryption
|
package encryption
|
||||||
|
|||||||
@ -109,7 +109,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
}
|
}
|
||||||
iv := ivAndRelays[:16]
|
iv := ivAndRelays[:16]
|
||||||
relays := ivAndRelays[16:]
|
relays := ivAndRelays[16:]
|
||||||
var nfsPublicKey, nfsKey []byte
|
var nfsKey []byte
|
||||||
var lastCTR cipher.Stream
|
var lastCTR cipher.Stream
|
||||||
for j, k := range i.NfsSKeys {
|
for j, k := range i.NfsSKeys {
|
||||||
if lastCTR != nil {
|
if lastCTR != nil {
|
||||||
@ -122,9 +122,8 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
if i.XorMode > 0 {
|
if i.XorMode > 0 {
|
||||||
NewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // we don't use buggy elligator, because we have PSK :)
|
NewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // we don't use buggy elligator, because we have PSK :)
|
||||||
}
|
}
|
||||||
nfsPublicKey = relays[:index]
|
|
||||||
if k, ok := k.(*ecdh.PrivateKey); ok {
|
if k, ok := k.(*ecdh.PrivateKey); ok {
|
||||||
publicKey, err := ecdh.X25519().NewPublicKey(nfsPublicKey)
|
publicKey, err := ecdh.X25519().NewPublicKey(relays[:index])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -135,7 +134,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
}
|
}
|
||||||
if k, ok := k.(*mlkem.DecapsulationKey768); ok {
|
if k, ok := k.(*mlkem.DecapsulationKey768); ok {
|
||||||
var err error
|
var err error
|
||||||
nfsKey, err = k.Decapsulate(nfsPublicKey)
|
nfsKey, err = k.Decapsulate(relays[:index])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -151,7 +150,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
}
|
}
|
||||||
relays = relays[32:]
|
relays = relays[32:]
|
||||||
}
|
}
|
||||||
nfsGCM := NewGCM(nfsPublicKey, nfsKey)
|
nfsGCM := NewGCM(iv, nfsKey)
|
||||||
|
|
||||||
encryptedLength := make([]byte, 18)
|
encryptedLength := make([]byte, 18)
|
||||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||||
@ -187,16 +186,16 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
conn.Write(noises) // make client do new handshake
|
conn.Write(noises) // make client do new handshake
|
||||||
return nil, errors.New("expired ticket")
|
return nil, errors.New("expired ticket")
|
||||||
}
|
}
|
||||||
if _, replay := s.Replays.LoadOrStore([32]byte(encryptedTicket), true); replay {
|
if _, replay := s.Replays.LoadOrStore([32]byte(nfsKey), true); replay { // prevents bad client also
|
||||||
return nil, errors.New("replay detected")
|
return nil, errors.New("replay detected")
|
||||||
}
|
}
|
||||||
c.UnitedKey = append(s.PfsKey, nfsKey...) // the same key links the upload & download
|
c.UnitedKey = append(s.PfsKey, nfsKey...) // the same nfsKey links the upload & download
|
||||||
c.PreWrite = make([]byte, 32) // always trust yourself, not the client
|
c.PreWrite = make([]byte, 16) // always trust yourself, not the client
|
||||||
rand.Read(c.PreWrite)
|
rand.Read(c.PreWrite)
|
||||||
c.GCM = NewGCM(c.PreWrite, c.UnitedKey)
|
c.GCM = NewGCM(c.PreWrite, c.UnitedKey)
|
||||||
c.PeerGCM = NewGCM(encryptedTicket, c.UnitedKey)
|
c.PeerGCM = NewGCM(encryptedTicket, c.UnitedKey) // unchangeable ctx, and different ctx length for upload / download
|
||||||
if i.XorMode == 2 {
|
if i.XorMode == 2 {
|
||||||
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, c.PreWrite[16:]), NewCTR(c.UnitedKey, iv), 32, 0)
|
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, c.PreWrite), NewCTR(c.UnitedKey, iv), 16, 0) // it doesn't matter if the attacker sends client's iv back to the client
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
@ -234,12 +233,11 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
rand.Read(ticket)
|
rand.Read(ticket)
|
||||||
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
||||||
|
|
||||||
pfsKeyExchangeLength := 18 + 1088 + 32 + 16
|
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||||
encryptedTicketLength := 32
|
encryptedTicketLength := 32
|
||||||
paddingLength := int(randBetween(100, 1000))
|
paddingLength := int(randBetween(100, 1000))
|
||||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||||
nfsGCM.Seal(serverHello[:0], make([]byte, 12), EncodeLength(pfsKeyExchangeLength-18), nil) // it is safe because our nonce starts from 1
|
nfsGCM.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||||
nfsGCM.Seal(serverHello[:18], MaxNonce, pfsPublicKey, nil)
|
|
||||||
c.GCM.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
c.GCM.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||||
c.GCM.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
c.GCM.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||||
@ -248,7 +246,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
if _, err := conn.Write(serverHello); err != nil {
|
if _, err := conn.Write(serverHello); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before VLESS flow takes control
|
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||||
|
|
||||||
if i.Seconds > 0 {
|
if i.Seconds > 0 {
|
||||||
i.RWLock.Lock()
|
i.RWLock.Lock()
|
||||||
@ -259,6 +257,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
i.RWLock.Unlock()
|
i.RWLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
|
||||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user