|
@@ -26,7 +26,7 @@ import (
|
|
|
vmessaead "v2ray.com/core/proxy/vmess/aead"
|
|
vmessaead "v2ray.com/core/proxy/vmess/aead"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-type sessionId struct {
|
|
|
|
|
|
|
+type sessionID struct {
|
|
|
user [16]byte
|
|
user [16]byte
|
|
|
key [16]byte
|
|
key [16]byte
|
|
|
nonce [16]byte
|
|
nonce [16]byte
|
|
@@ -35,14 +35,14 @@ type sessionId struct {
|
|
|
// SessionHistory keeps track of historical session ids, to prevent replay attacks.
|
|
// SessionHistory keeps track of historical session ids, to prevent replay attacks.
|
|
|
type SessionHistory struct {
|
|
type SessionHistory struct {
|
|
|
sync.RWMutex
|
|
sync.RWMutex
|
|
|
- cache map[sessionId]time.Time
|
|
|
|
|
|
|
+ cache map[sessionID]time.Time
|
|
|
task *task.Periodic
|
|
task *task.Periodic
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// NewSessionHistory creates a new SessionHistory object.
|
|
// NewSessionHistory creates a new SessionHistory object.
|
|
|
func NewSessionHistory() *SessionHistory {
|
|
func NewSessionHistory() *SessionHistory {
|
|
|
h := &SessionHistory{
|
|
h := &SessionHistory{
|
|
|
- cache: make(map[sessionId]time.Time, 128),
|
|
|
|
|
|
|
+ cache: make(map[sessionID]time.Time, 128),
|
|
|
}
|
|
}
|
|
|
h.task = &task.Periodic{
|
|
h.task = &task.Periodic{
|
|
|
Interval: time.Second * 30,
|
|
Interval: time.Second * 30,
|
|
@@ -56,7 +56,7 @@ func (h *SessionHistory) Close() error {
|
|
|
return h.task.Close()
|
|
return h.task.Close()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (h *SessionHistory) addIfNotExits(session sessionId) bool {
|
|
|
|
|
|
|
+func (h *SessionHistory) addIfNotExits(session sessionID) bool {
|
|
|
h.Lock()
|
|
h.Lock()
|
|
|
|
|
|
|
|
if expire, found := h.cache[session]; found && expire.After(time.Now()) {
|
|
if expire, found := h.cache[session]; found && expire.After(time.Now()) {
|
|
@@ -87,7 +87,7 @@ func (h *SessionHistory) removeExpiredEntries() error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if len(h.cache) == 0 {
|
|
if len(h.cache) == 0 {
|
|
|
- h.cache = make(map[sessionId]time.Time, 128)
|
|
|
|
|
|
|
+ h.cache = make(map[sessionID]time.Time, 128)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
return nil
|
|
@@ -141,7 +141,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|
|
readSizeRemain := DrainSize
|
|
readSizeRemain := DrainSize
|
|
|
|
|
|
|
|
drainConnection := func(e error) error {
|
|
drainConnection := func(e error) error {
|
|
|
- //We read a deterministic generated length of data before closing the connection to offset padding read pattern
|
|
|
|
|
|
|
+ // We read a deterministic generated length of data before closing the connection to offset padding read pattern
|
|
|
readSizeRemain -= int(buffer.Len())
|
|
readSizeRemain -= int(buffer.Len())
|
|
|
if readSizeRemain > 0 {
|
|
if readSizeRemain > 0 {
|
|
|
err := s.DrainConnN(reader, readSizeRemain)
|
|
err := s.DrainConnN(reader, readSizeRemain)
|
|
@@ -169,22 +169,24 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|
|
var fixedSizeAuthID [16]byte
|
|
var fixedSizeAuthID [16]byte
|
|
|
copy(fixedSizeAuthID[:], buffer.Bytes())
|
|
copy(fixedSizeAuthID[:], buffer.Bytes())
|
|
|
|
|
|
|
|
- if foundAEAD {
|
|
|
|
|
|
|
+ switch {
|
|
|
|
|
+ case foundAEAD:
|
|
|
vmessAccount = user.Account.(*vmess.MemoryAccount)
|
|
vmessAccount = user.Account.(*vmess.MemoryAccount)
|
|
|
var fixedSizeCmdKey [16]byte
|
|
var fixedSizeCmdKey [16]byte
|
|
|
copy(fixedSizeCmdKey[:], vmessAccount.ID.CmdKey())
|
|
copy(fixedSizeCmdKey[:], vmessAccount.ID.CmdKey())
|
|
|
- aeadData, shouldDrain, errorReason, bytesRead := vmessaead.OpenVMessAEADHeader(fixedSizeCmdKey, fixedSizeAuthID, reader)
|
|
|
|
|
|
|
+ aeadData, shouldDrain, bytesRead, errorReason := vmessaead.OpenVMessAEADHeader(fixedSizeCmdKey, fixedSizeAuthID, reader)
|
|
|
if errorReason != nil {
|
|
if errorReason != nil {
|
|
|
if shouldDrain {
|
|
if shouldDrain {
|
|
|
readSizeRemain -= bytesRead
|
|
readSizeRemain -= bytesRead
|
|
|
return nil, drainConnection(newError("AEAD read failed").Base(errorReason))
|
|
return nil, drainConnection(newError("AEAD read failed").Base(errorReason))
|
|
|
} else {
|
|
} else {
|
|
|
- return nil, drainConnection(newError("AEAD read failed, drain skiped").Base(errorReason))
|
|
|
|
|
|
|
+ return nil, drainConnection(newError("AEAD read failed, drain skipped").Base(errorReason))
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
decryptor = bytes.NewReader(aeadData)
|
|
decryptor = bytes.NewReader(aeadData)
|
|
|
s.isAEADRequest = true
|
|
s.isAEADRequest = true
|
|
|
- } else if !s.isAEADForced && errorAEAD == vmessaead.ErrNotFound {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ case !s.isAEADForced && errorAEAD == vmessaead.ErrNotFound:
|
|
|
userLegacy, timestamp, valid, userValidationError := s.userValidator.Get(buffer.Bytes())
|
|
userLegacy, timestamp, valid, userValidationError := s.userValidator.Get(buffer.Bytes())
|
|
|
if !valid || userValidationError != nil {
|
|
if !valid || userValidationError != nil {
|
|
|
return nil, drainConnection(newError("invalid user").Base(userValidationError))
|
|
return nil, drainConnection(newError("invalid user").Base(userValidationError))
|
|
@@ -193,9 +195,10 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|
|
iv := hashTimestamp(md5.New(), timestamp)
|
|
iv := hashTimestamp(md5.New(), timestamp)
|
|
|
vmessAccount = userLegacy.Account.(*vmess.MemoryAccount)
|
|
vmessAccount = userLegacy.Account.(*vmess.MemoryAccount)
|
|
|
|
|
|
|
|
- aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv[:])
|
|
|
|
|
|
|
+ aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv)
|
|
|
decryptor = crypto.NewCryptionReader(aesStream, reader)
|
|
decryptor = crypto.NewCryptionReader(aesStream, reader)
|
|
|
- } else {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
return nil, drainConnection(newError("invalid user").Base(errorAEAD))
|
|
return nil, drainConnection(newError("invalid user").Base(errorAEAD))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -212,7 +215,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|
|
|
|
|
|
|
copy(s.requestBodyIV[:], buffer.BytesRange(1, 17)) // 16 bytes
|
|
copy(s.requestBodyIV[:], buffer.BytesRange(1, 17)) // 16 bytes
|
|
|
copy(s.requestBodyKey[:], buffer.BytesRange(17, 33)) // 16 bytes
|
|
copy(s.requestBodyKey[:], buffer.BytesRange(17, 33)) // 16 bytes
|
|
|
- var sid sessionId
|
|
|
|
|
|
|
+ var sid sessionID
|
|
|
copy(sid.user[:], vmessAccount.ID.Bytes())
|
|
copy(sid.user[:], vmessAccount.ID.Bytes())
|
|
|
sid.key = s.requestBodyKey
|
|
sid.key = s.requestBodyKey
|
|
|
sid.nonce = s.requestBodyIV
|
|
sid.nonce = s.requestBodyIV
|
|
@@ -226,7 +229,6 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|
|
} else {
|
|
} else {
|
|
|
return nil, newError("duplicated session id, possibly under replay attack, but this is a AEAD request")
|
|
return nil, newError("duplicated session id, possibly under replay attack, but this is a AEAD request")
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
s.responseHeader = buffer.Byte(33) // 1 byte
|
|
s.responseHeader = buffer.Byte(33) // 1 byte
|
|
@@ -240,6 +242,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|
|
case protocol.RequestCommandMux:
|
|
case protocol.RequestCommandMux:
|
|
|
request.Address = net.DomainAddress("v1.mux.cool")
|
|
request.Address = net.DomainAddress("v1.mux.cool")
|
|
|
request.Port = 0
|
|
request.Port = 0
|
|
|
|
|
+
|
|
|
case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
|
|
case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
|
|
|
if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil {
|
|
if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil {
|
|
|
request.Address = addr
|
|
request.Address = addr
|
|
@@ -283,12 +286,11 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|
|
if burnErr != nil {
|
|
if burnErr != nil {
|
|
|
Autherr = newError("invalid auth, can't taint legacy userHash").Base(burnErr)
|
|
Autherr = newError("invalid auth, can't taint legacy userHash").Base(burnErr)
|
|
|
}
|
|
}
|
|
|
- //It is possible that we are under attack described in https://github.com/v2ray/v2ray-core/issues/2523
|
|
|
|
|
|
|
+ // It is possible that we are under attack described in https://github.com/v2ray/v2ray-core/issues/2523
|
|
|
return nil, drainConnection(Autherr)
|
|
return nil, drainConnection(Autherr)
|
|
|
} else {
|
|
} else {
|
|
|
return nil, newError("invalid auth, but this is a AEAD request")
|
|
return nil, newError("invalid auth, but this is a AEAD request")
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if request.Address == nil {
|
|
if request.Address == nil {
|
|
@@ -327,8 +329,8 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
|
|
|
}
|
|
}
|
|
|
return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding)
|
|
return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return buf.NewReader(reader)
|
|
return buf.NewReader(reader)
|
|
|
|
|
+
|
|
|
case protocol.SecurityType_LEGACY:
|
|
case protocol.SecurityType_LEGACY:
|
|
|
aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:])
|
|
aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:])
|
|
|
cryptionReader := crypto.NewCryptionReader(aesStream, reader)
|
|
cryptionReader := crypto.NewCryptionReader(aesStream, reader)
|
|
@@ -340,17 +342,17 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
|
|
|
}
|
|
}
|
|
|
return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding)
|
|
return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return buf.NewReader(cryptionReader)
|
|
return buf.NewReader(cryptionReader)
|
|
|
|
|
+
|
|
|
case protocol.SecurityType_AES128_GCM:
|
|
case protocol.SecurityType_AES128_GCM:
|
|
|
aead := crypto.NewAesGcm(s.requestBodyKey[:])
|
|
aead := crypto.NewAesGcm(s.requestBodyKey[:])
|
|
|
-
|
|
|
|
|
auth := &crypto.AEADAuthenticator{
|
|
auth := &crypto.AEADAuthenticator{
|
|
|
AEAD: aead,
|
|
AEAD: aead,
|
|
|
NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
|
|
NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
|
|
|
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
|
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
|
|
}
|
|
}
|
|
|
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
|
|
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
|
|
|
|
|
+
|
|
|
case protocol.SecurityType_CHACHA20_POLY1305:
|
|
case protocol.SecurityType_CHACHA20_POLY1305:
|
|
|
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:]))
|
|
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:]))
|
|
|
|
|
|
|
@@ -360,6 +362,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
|
|
|
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
|
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
|
|
}
|
|
}
|
|
|
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
|
|
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
|
|
|
|
|
+
|
|
|
default:
|
|
default:
|
|
|
panic("Unknown security type.")
|
|
panic("Unknown security type.")
|
|
|
}
|
|
}
|
|
@@ -395,9 +398,8 @@ func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if s.isAEADRequest {
|
|
if s.isAEADRequest {
|
|
|
-
|
|
|
|
|
- aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConst_AEADRespHeaderLenKey)
|
|
|
|
|
- aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConst_AEADRespHeaderLenIV)[:12]
|
|
|
|
|
|
|
+ aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey)
|
|
|
|
|
+ aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12]
|
|
|
|
|
|
|
|
aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block)
|
|
aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block)
|
|
|
aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD)
|
|
aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD)
|
|
@@ -411,8 +413,8 @@ func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr
|
|
|
AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil)
|
|
AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil)
|
|
|
common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength)))
|
|
common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength)))
|
|
|
|
|
|
|
|
- aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConst_AEADRespHeaderPayloadKey)
|
|
|
|
|
- aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConst_AEADRespHeaderPayloadIV)[:12]
|
|
|
|
|
|
|
+ aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey)
|
|
|
|
|
+ aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12]
|
|
|
|
|
|
|
|
aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block)
|
|
aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block)
|
|
|
aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD)
|
|
aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD)
|
|
@@ -447,8 +449,8 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
|
|
|
}
|
|
}
|
|
|
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding)
|
|
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return buf.NewWriter(writer)
|
|
return buf.NewWriter(writer)
|
|
|
|
|
+
|
|
|
case protocol.SecurityType_LEGACY:
|
|
case protocol.SecurityType_LEGACY:
|
|
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
|
|
auth := &crypto.AEADAuthenticator{
|
|
auth := &crypto.AEADAuthenticator{
|
|
@@ -458,17 +460,17 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
|
|
|
}
|
|
}
|
|
|
return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding)
|
|
return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return &buf.SequentialWriter{Writer: s.responseWriter}
|
|
return &buf.SequentialWriter{Writer: s.responseWriter}
|
|
|
|
|
+
|
|
|
case protocol.SecurityType_AES128_GCM:
|
|
case protocol.SecurityType_AES128_GCM:
|
|
|
aead := crypto.NewAesGcm(s.responseBodyKey[:])
|
|
aead := crypto.NewAesGcm(s.responseBodyKey[:])
|
|
|
-
|
|
|
|
|
auth := &crypto.AEADAuthenticator{
|
|
auth := &crypto.AEADAuthenticator{
|
|
|
AEAD: aead,
|
|
AEAD: aead,
|
|
|
NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
|
|
NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
|
|
|
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
|
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
|
|
}
|
|
}
|
|
|
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
|
|
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
|
|
|
|
|
+
|
|
|
case protocol.SecurityType_CHACHA20_POLY1305:
|
|
case protocol.SecurityType_CHACHA20_POLY1305:
|
|
|
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:]))
|
|
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:]))
|
|
|
|
|
|
|
@@ -478,6 +480,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
|
|
|
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
|
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
|
|
}
|
|
}
|
|
|
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
|
|
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
|
|
|
|
|
+
|
|
|
default:
|
|
default:
|
|
|
panic("Unknown security type.")
|
|
panic("Unknown security type.")
|
|
|
}
|
|
}
|