Skip to content

Commit

Permalink
Update VMess protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
DarienRaymond committed Sep 14, 2015
1 parent 5af327f commit 092d73b
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 25 deletions.
23 changes: 15 additions & 8 deletions id.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"crypto/hmac"
"crypto/md5"
"encoding/hex"
"hash"
mrand "math/rand"
"time"

Expand All @@ -19,16 +18,21 @@ const (
type ID struct {
String string
Bytes []byte
hasher hash.Hash
cmdKey []byte
}

func NewID(id string) (ID, error) {
idBytes, err := UUIDToID(id)
if err != nil {
return ID{}, log.Error("Failed to parse id %s", id)
}
hasher := hmac.New(md5.New, idBytes)
return ID{id, idBytes, hasher}, nil

md5hash := md5.New()
md5hash.Write(idBytes)
md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21"))
cmdKey := md5.Sum(nil)

return ID{id, idBytes, cmdKey[:]}, nil
}

func (v ID) TimeRangeHash(rangeSec int) []byte {
Expand All @@ -54,10 +58,13 @@ func (v ID) TimeHash(timeSec int64) []byte {
}

func (v ID) Hash(data []byte) []byte {
v.hasher.Write(data)
hash := v.hasher.Sum(nil)
v.hasher.Reset()
return hash
hasher := hmac.New(md5.New, v.Bytes)
hasher.Write(data)
return hasher.Sum(nil)
}

func (v ID) CmdKey() []byte {
return v.cmdKey
}

var byteGroups = []int{8, 4, 4, 4, 12}
Expand Down
38 changes: 24 additions & 14 deletions io/vmess/vmess.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/v2ray/v2ray-core"
v2io "github.com/v2ray/v2ray-core/io"
"github.com/v2ray/v2ray-core/log"
v2net "github.com/v2ray/v2ray-core/net"
)

Expand All @@ -24,12 +25,11 @@ const (
Version = byte(0x01)

blockSize = 16

CryptoMessage = "c48619fe-8f02-49e0-b9e9-edf763e17e21"
)

var (
ErrorInvalidUser = errors.New("Invalid User")
ErrorInvalidUser = errors.New("Invalid User")
ErrorInvalidVerion = errors.New("Invalid Version")

emptyIV = make([]byte, blockSize)
)
Expand Down Expand Up @@ -62,25 +62,21 @@ func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
request := new(VMessRequest)

buffer := make([]byte, 256)
nBytes, err := reader.Read(buffer[0:1])
if err != nil {
return nil, err
}
// TODO: verify version number
request.Version = buffer[0]

nBytes, err = reader.Read(buffer[:core.IDBytesLen])
nBytes, err := reader.Read(buffer[:core.IDBytesLen])
if err != nil {
return nil, err
}

log.Debug("Read user hash: %v", buffer[:nBytes])

userId, valid := r.vUserSet.GetUser(buffer[:nBytes])
if !valid {
return nil, ErrorInvalidUser
}
request.UserId = *userId

aesCipher, err := aes.NewCipher(userId.Hash([]byte(CryptoMessage)))
aesCipher, err := aes.NewCipher(userId.CmdKey())
if err != nil {
return nil, err
}
Expand All @@ -105,6 +101,17 @@ func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
return nil, err
}

nBytes, err = decryptor.Read(buffer[0:1])
if err != nil {
return nil, err
}

request.Version = buffer[0]
if request.Version != Version {
log.Error("Unknown VMess version %d", request.Version)
return nil, ErrorInvalidVerion
}

// TODO: check number of bytes returned
_, err = decryptor.Read(request.RequestIV[:])
if err != nil {
Expand Down Expand Up @@ -182,8 +189,10 @@ func NewVMessRequestWriter() *VMessRequestWriter {

func (w *VMessRequestWriter) Write(writer io.Writer, request *VMessRequest) error {
buffer := make([]byte, 0, 300)
buffer = append(buffer, request.Version)
buffer = append(buffer, request.UserId.TimeRangeHash(30)...)
userHash := request.UserId.TimeRangeHash(30)

log.Debug("Writing userhash: %v", userHash)
buffer = append(buffer, userHash...)

encryptionBegin := len(buffer)

Expand All @@ -196,6 +205,7 @@ func (w *VMessRequestWriter) Write(writer io.Writer, request *VMessRequest) erro
buffer = append(buffer, byte(randomLength))
buffer = append(buffer, randomContent...)

buffer = append(buffer, request.Version)
buffer = append(buffer, request.RequestIV[:]...)
buffer = append(buffer, request.RequestKey[:]...)
buffer = append(buffer, request.ResponseHeader[:]...)
Expand Down Expand Up @@ -231,7 +241,7 @@ func (w *VMessRequestWriter) Write(writer io.Writer, request *VMessRequest) erro
buffer = append(buffer, paddingBuffer...)
encryptionEnd := len(buffer)

aesCipher, err := aes.NewCipher(request.UserId.Hash([]byte(CryptoMessage)))
aesCipher, err := aes.NewCipher(request.UserId.CmdKey())
if err != nil {
return err
}
Expand Down
25 changes: 24 additions & 1 deletion io/vmess/vmess_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package vmess
import (
"bytes"
"crypto/rand"
"io/ioutil"
"testing"

"github.com/v2ray/v2ray-core"
Expand Down Expand Up @@ -51,7 +52,7 @@ func TestVMessSerialization(t *testing.T) {
t.Fatal(err)
}

userSet.UserHashes[string(buffer.Bytes()[1:17])] = 0
userSet.UserHashes[string(buffer.Bytes()[:16])] = 0

requestReader := NewVMessRequestReader(&userSet)
actualRequest, err := requestReader.Read(buffer)
Expand All @@ -67,3 +68,25 @@ func TestVMessSerialization(t *testing.T) {
assert.Byte(actualRequest.Command).Named("Command").Equals(request.Command)
assert.String(actualRequest.Address.String()).Named("Address").Equals(request.Address.String())
}

func BenchmarkVMessRequestWriting(b *testing.B) {
userId, _ := core.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
userSet := mocks.MockUserSet{[]core.ID{}, make(map[string]int)}
userSet.AddUser(core.User{userId})

request := new(VMessRequest)
request.Version = byte(0x01)
request.UserId = userId

rand.Read(request.RequestIV[:])
rand.Read(request.RequestKey[:])
rand.Read(request.ResponseHeader[:])

request.Command = byte(0x01)
request.Address = v2net.DomainAddress("v2ray.com", 80)

requestWriter := NewVMessRequestWriter()
for i := 0; i < b.N; i++ {
requestWriter.Write(ioutil.Discard, request)
}
}
1 change: 1 addition & 0 deletions net/vmess/vmessin.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error

request, err := reader.Read(connection)
if err != nil {
log.Debug("Failed to parse VMess request: %v", err)
return err
}
log.Debug("Received request for %s", request.Address.String())
Expand Down
9 changes: 7 additions & 2 deletions net/vmess/vmessout.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@ func (handler *VMessOutboundHandler) startCommunicate(request *vmessio.VMessRequ
return err
}
defer conn.Close()

input := ray.OutboundInput()
output := ray.OutboundOutput()

requestWriter := vmessio.NewVMessRequestWriter()
err = requestWriter.Write(conn, request)
if err != nil {
log.Error("Failed to write VMess request: %v", err)
close(output)
return err
}

Expand All @@ -90,6 +94,7 @@ func (handler *VMessOutboundHandler) startCommunicate(request *vmessio.VMessRequ
response := vmessio.VMessResponse{}
nBytes, err := conn.Read(response[:])
if err != nil {
close(output)
log.Error("Failed to read VMess response (%d bytes): %v", nBytes, err)
return err
}
Expand All @@ -98,17 +103,17 @@ func (handler *VMessOutboundHandler) startCommunicate(request *vmessio.VMessRequ

encryptRequestWriter, err := v2io.NewAesEncryptWriter(requestKey, requestIV, conn)
if err != nil {
close(output)
log.Error("Failed to create encrypt writer: %v", err)
return err
}
decryptResponseReader, err := v2io.NewAesDecryptReader(responseKey[:], responseIV[:], conn)
if err != nil {
close(output)
log.Error("Failed to create decrypt reader: %v", err)
return err
}

input := ray.OutboundInput()
output := ray.OutboundOutput()
readFinish := make(chan bool)
writeFinish := make(chan bool)

Expand Down

0 comments on commit 092d73b

Please sign in to comment.