diff --git a/infra/conf/vless.go b/infra/conf/vless.go index a13b735b..34288d16 100644 --- a/infra/conf/vless.go +++ b/infra/conf/vless.go @@ -56,15 +56,15 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { account.Id = u.String() switch account.Flow { - case "": - case vless.XRV: - if c.Decryption != "none" { - return nil, errors.New(`VLESS clients: "decryption" doesn't support "flow" yet`) - } + case "", vless.XRV: default: return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`) } + if strings.Contains(c.Decryption, "xored") && account.Flow == vless.XRV { + return nil, errors.New(`VLESS clients: "xored" doesn't support "flow" yet`) + } + if account.Encryption != "" { return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`) } @@ -215,8 +215,8 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) { switch account.Flow { case "": case vless.XRV, vless.XRV + "-udp443": - if account.Encryption != "none" { - return nil, errors.New(`VLESS users: "encryption" doesn't support "flow" yet`) + if strings.Contains(account.Encryption, "xored") { + return nil, errors.New(`VLESS users: "xored" doesn't support "flow" yet`) } default: return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`) diff --git a/proxy/vless/encoding/encoding.go b/proxy/vless/encoding/encoding.go index 38043e68..8246aef2 100644 --- a/proxy/vless/encoding/encoding.go +++ b/proxy/vless/encoding/encoding.go @@ -172,7 +172,7 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A } // XtlsRead filter and read xtls protocol -func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error { +func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, peerCache *[]byte, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error { err := func() error { for { if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy { @@ -194,15 +194,21 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, if !buffer.IsEmpty() { timer.Update() if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy { - // XTLS Vision processes struct TLS Conn's input and rawInput - if inputBuffer, err := buf.ReadFrom(input); err == nil { - if !inputBuffer.IsEmpty() { - buffer, _ = buf.MergeMulti(buffer, inputBuffer) + // XTLS Vision processes struct Encryption Conn's peerCache or TLS Conn's input and rawInput + if peerCache != nil { + if len(*peerCache) != 0 { + buffer = buf.MergeBytes(buffer, *peerCache) } - } - if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil { - if !rawInputBuffer.IsEmpty() { - buffer, _ = buf.MergeMulti(buffer, rawInputBuffer) + } else { + if inputBuffer, err := buf.ReadFrom(input); err == nil { + if !inputBuffer.IsEmpty() { + buffer, _ = buf.MergeMulti(buffer, inputBuffer) + } + } + if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil { + if !rawInputBuffer.IsEmpty() { + buffer, _ = buf.MergeMulti(buffer, rawInputBuffer) + } } } } diff --git a/proxy/vless/encryption/client.go b/proxy/vless/encryption/client.go index 9a17135e..d0d4b3d5 100644 --- a/proxy/vless/encryption/client.go +++ b/proxy/vless/encryption/client.go @@ -28,6 +28,7 @@ func init() { type ClientInstance struct { sync.RWMutex nfsEKey *mlkem.EncapsulationKey768 + hash11 [11]byte // no more capacity xorKey []byte minutes time.Duration expire time.Time @@ -45,7 +46,7 @@ type ClientConn struct { nonce []byte peerAead cipher.AEAD peerNonce []byte - peerCache []byte + PeerCache []byte } func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) { @@ -57,6 +58,8 @@ func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Dura if err != nil { return } + hash256 := sha256.Sum256(nfsEKeyBytes) + copy(i.hash11[:], hash256[:]) if xor > 0 { xorKey := sha256.Sum256(nfsEKeyBytes) i.xorKey = xorKey[:] @@ -93,13 +96,14 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { nfsKey, encapsulatedNfsKey := i.nfsEKey.Encapsulate() paddingLen := crypto.RandBetween(100, 1000) - clientHello := make([]byte, 5+1+1184+1088+5+paddingLen) - EncodeHeader(clientHello, 1, 1+1184+1088) - clientHello[5] = ClientCipher - copy(clientHello[5+1:], pfsEKeyBytes) - copy(clientHello[5+1+1184:], encapsulatedNfsKey) - EncodeHeader(clientHello[5+1+1184+1088:], 23, int(paddingLen)) - rand.Read(clientHello[5+1+1184+1088+5:]) + clientHello := make([]byte, 5+11+1+1184+1088+5+paddingLen) + 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) + EncodeHeader(clientHello[5+11+1+1184+1088:], 23, int(paddingLen)) + rand.Read(clientHello[5+11+1+1184+1088+5:]) if _, err := c.Conn.Write(clientHello); err != nil { return nil, err @@ -122,7 +126,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { return nil, err } encapsulatedPfsKey := peerServerHello[:1088] - c.ticket = peerServerHello[1088:] + c.ticket = append(i.hash11[:], peerServerHello[1088:]...) pfsKey, err := pfsDKey.Decapsulate(encapsulatedPfsKey) if err != nil { @@ -130,8 +134,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) { } c.baseKey = append(pfsKey, nfsKey...) - nonce := [12]byte{ClientCipher} - VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, nonce[:], c.ticket, pfsEKeyBytes) + VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes) if !bytes.Equal(VLESS, []byte("VLESS")) { return nil, errors.New("invalid server").AtError() } @@ -159,16 +162,16 @@ func (c *ClientConn) Write(b []byte) (int, error) { } n += len(b) if c.aead == nil { + data = make([]byte, 5+32+32+5+len(b)+16) + EncodeHeader(data, 0, 32+32) + copy(data[5:], c.ticket) c.random = make([]byte, 32) rand.Read(c.random) + copy(data[5+32:], c.random) + EncodeHeader(data[5+32+32:], 23, len(b)+16) c.aead = NewAead(ClientCipher, c.baseKey, c.random, c.ticket) c.nonce = make([]byte, 12) - data = make([]byte, 5+21+32+5+len(b)+16) - EncodeHeader(data, 0, 21+32) - copy(data[5:], c.ticket) - copy(data[5+21:], c.random) - EncodeHeader(data[5+21+32:], 23, len(b)+16) - c.aead.Seal(data[:5+21+32+5], c.nonce, b, data[5+21+32:5+21+32+5]) + c.aead.Seal(data[:5+32+32+5], c.nonce, b, data[5+32+32:5+32+32+5]) } else { data = make([]byte, 5+len(b)+16) EncodeHeader(data, 23, len(b)+16) @@ -218,9 +221,9 @@ func (c *ClientConn) Read(b []byte) (int, error) { c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandomHello, c.random) c.peerNonce = make([]byte, 12) } - if len(c.peerCache) != 0 { - n := copy(b, c.peerCache) - c.peerCache = c.peerCache[n:] + if len(c.PeerCache) != 0 { + n := copy(b, c.PeerCache) + c.PeerCache = c.PeerCache[n:] return n, nil } h, t, l, err := ReadAndDecodeHeader(c.Conn) // l: 17~17000 @@ -251,7 +254,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { return 0, err } if len(dst) > len(b) { - c.peerCache = dst[copy(b, dst):] + c.PeerCache = dst[copy(b, dst):] dst = b // for len(dst) } return len(dst), nil diff --git a/proxy/vless/encryption/server.go b/proxy/vless/encryption/server.go index c5b0163e..5ea1f0d6 100644 --- a/proxy/vless/encryption/server.go +++ b/proxy/vless/encryption/server.go @@ -6,6 +6,7 @@ import ( "crypto/mlkem" "crypto/rand" "crypto/sha256" + "fmt" "io" "net" "sync" @@ -25,9 +26,10 @@ type ServerSession struct { type ServerInstance struct { sync.RWMutex nfsDKey *mlkem.DecapsulationKey768 + hash11 [11]byte // no more capacity xorKey []byte minutes time.Duration - sessions map[[21]byte]*ServerSession + sessions map[[32]byte]*ServerSession closed bool } @@ -39,7 +41,7 @@ type ServerConn struct { peerRandom []byte peerAead cipher.AEAD peerNonce []byte - peerCache []byte + PeerCache []byte aead cipher.AEAD nonce []byte } @@ -53,13 +55,15 @@ func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Durat if err != nil { return } + hash256 := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) + copy(i.hash11[:], hash256[:]) if xor > 0 { xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) i.xorKey = xorKey[:] } if minutes > 0 { i.minutes = minutes - i.sessions = make(map[[21]byte]*ServerSession) + i.sessions = make(map[[32]byte]*ServerSession) go func() { for { time.Sleep(time.Minute) @@ -106,15 +110,18 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.minutes == 0 { return nil, errors.New("0-RTT is not allowed") } - peerTicketHello := make([]byte, 21+32) + peerTicketHello := make([]byte, 32+32) if l != len(peerTicketHello) { return nil, errors.New("unexpected length ", l, " for ticket hello") } if _, err := io.ReadFull(c.Conn, peerTicketHello); err != nil { return nil, err } + if !bytes.Equal(peerTicketHello[:11], i.hash11[:]) { + return nil, errors.New("unexpected hash11: ", fmt.Sprintf("%v", peerTicketHello[:11])) + } i.RLock() - s := i.sessions[[21]byte(peerTicketHello)] + s := i.sessions[[32]byte(peerTicketHello)] i.RUnlock() if s == nil { noises := make([]byte, crypto.RandBetween(100, 1000)) @@ -126,26 +133,29 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { c.Conn.Write(noises) // make client do new handshake return nil, errors.New("expired ticket") } - if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay { + if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[32:]), true); replay { return nil, errors.New("replay detected") } c.cipher = s.cipher c.baseKey = s.baseKey - c.ticket = peerTicketHello[:21] - c.peerRandom = peerTicketHello[21:] + c.ticket = peerTicketHello[:32] + c.peerRandom = peerTicketHello[32:] return c, nil } - peerClientHello := make([]byte, 1+1184+1088) + peerClientHello := make([]byte, 11+1+1184+1088) if l != len(peerClientHello) { return nil, errors.New("unexpected length ", l, " for client hello") } if _, err := io.ReadFull(c.Conn, peerClientHello); err != nil { return nil, err } - c.cipher = peerClientHello[0] - pfsEKeyBytes := peerClientHello[1:1185] - encapsulatedNfsKey := peerClientHello[1185:2273] + if !bytes.Equal(peerClientHello[:11], i.hash11[:]) { + return nil, errors.New("unexpected hash11: ", fmt.Sprintf("%v", peerClientHello[:11])) + } + c.cipher = peerClientHello[11] + pfsEKeyBytes := peerClientHello[11+1 : 11+1+1184] + encapsulatedNfsKey := peerClientHello[11+1+1184:] pfsEKey, err := mlkem.NewEncapsulationKey768(pfsEKeyBytes) if err != nil { @@ -158,15 +168,14 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate() c.baseKey = append(pfsKey, nfsKey...) - nonce := [12]byte{c.cipher} - c.ticket = NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, nonce[:], []byte("VLESS"), pfsEKeyBytes) + c.ticket = append(i.hash11[:], NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...) paddingLen := crypto.RandBetween(100, 1000) serverHello := make([]byte, 5+1088+21+5+paddingLen) EncodeHeader(serverHello, 1, 1088+21) copy(serverHello[5:], encapsulatedPfsKey) - copy(serverHello[5+1088:], c.ticket) + copy(serverHello[5+1088:], c.ticket[11:]) EncodeHeader(serverHello[5+1088+21:], 23, int(paddingLen)) rand.Read(serverHello[5+1088+21+5:]) @@ -177,7 +186,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) { if i.minutes > 0 { i.Lock() - i.sessions[[21]byte(c.ticket)] = &ServerSession{ + i.sessions[[32]byte(c.ticket)] = &ServerSession{ expire: time.Now().Add(i.minutes), cipher: c.cipher, baseKey: c.baseKey, @@ -201,24 +210,24 @@ func (c *ServerConn) Read(b []byte) (int, error) { if t != 0 { return 0, errors.New("unexpected type ", t, ", expect ticket hello") } - peerTicketHello := make([]byte, 21+32) + peerTicketHello := make([]byte, 32+32) if l != len(peerTicketHello) { return 0, errors.New("unexpected length ", l, " for ticket hello") } if _, err := io.ReadFull(c.Conn, peerTicketHello); err != nil { return 0, err } - if !bytes.Equal(peerTicketHello[:21], c.ticket) { + if !bytes.Equal(peerTicketHello[:32], c.ticket) { return 0, errors.New("naughty boy") } - c.peerRandom = peerTicketHello[21:] + c.peerRandom = peerTicketHello[32:] } c.peerAead = NewAead(c.cipher, c.baseKey, c.peerRandom, c.ticket) c.peerNonce = make([]byte, 12) } - if len(c.peerCache) != 0 { - n := copy(b, c.peerCache) - c.peerCache = c.peerCache[n:] + if len(c.PeerCache) != 0 { + n := copy(b, c.PeerCache) + c.PeerCache = c.PeerCache[n:] return n, nil } h, t, l, err := ReadAndDecodeHeader(c.Conn) // l: 17~17000 @@ -249,7 +258,7 @@ func (c *ServerConn) Read(b []byte) (int, error) { return 0, err } if len(dst) > len(b) { - c.peerCache = dst[copy(b, dst):] + c.PeerCache = dst[copy(b, dst):] dst = b // for len(dst) } return len(dst), nil diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index 06c9e518..deaf1cf1 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -484,6 +484,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s // Flow: requestAddons.Flow, } + var peerCache *[]byte var input *bytes.Reader var rawInput *bytes.Buffer switch requestAddons.Flow { @@ -496,6 +497,10 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s case protocol.RequestCommandMux: fallthrough // we will break Mux connections that contain TCP requests case protocol.RequestCommandTCP: + if serverConn, ok := connection.(*encryption.ServerConn); ok { + peerCache = &serverConn.PeerCache + break + } var t reflect.Type var p uintptr if tlsConn, ok := iConn.(*tls.Conn); ok { @@ -564,7 +569,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if requestAddons.Flow == vless.XRV { ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx1) - err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, true, ctx1) + err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, peerCache, input, rawInput, trafficState, nil, true, ctx1) } else { // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)) diff --git a/proxy/vless/outbound/outbound.go b/proxy/vless/outbound/outbound.go index 1bbd879b..9be88039 100644 --- a/proxy/vless/outbound/outbound.go +++ b/proxy/vless/outbound/outbound.go @@ -140,6 +140,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte Flow: account.Flow, } + var peerCache *[]byte var input *bytes.Reader var rawInput *bytes.Buffer allowUDP443 := false @@ -158,6 +159,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte case protocol.RequestCommandMux: fallthrough // let server break Mux connections that contain TCP requests case protocol.RequestCommandTCP: + if clientConn, ok := conn.(*encryption.ClientConn); ok { + peerCache = &clientConn.PeerCache + break + } var t reflect.Type var p uintptr if tlsConn, ok := iConn.(*tls.Conn); ok { @@ -292,7 +297,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte } if requestAddons.Flow == vless.XRV { - err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, false, ctx) + err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, peerCache, input, rawInput, trafficState, ob, false, ctx) } else { // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))