mirror of https://github.com/v2ray/v2ray-core
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
3.3 KiB
150 lines
3.3 KiB
package mtproto |
|
|
|
import ( |
|
"context" |
|
"crypto/rand" |
|
"crypto/sha256" |
|
"io" |
|
"sync" |
|
|
|
"v2ray.com/core/common" |
|
) |
|
|
|
const ( |
|
HeaderSize = 64 |
|
) |
|
|
|
type SessionContext struct { |
|
ConnectionType [4]byte |
|
DataCenterID uint16 |
|
} |
|
|
|
func DefaultSessionContext() SessionContext { |
|
return SessionContext{ |
|
ConnectionType: [4]byte{0xef, 0xef, 0xef, 0xef}, |
|
DataCenterID: 0, |
|
} |
|
} |
|
|
|
type contextKey int32 |
|
|
|
const ( |
|
sessionContextKey contextKey = iota |
|
) |
|
|
|
func ContextWithSessionContext(ctx context.Context, c SessionContext) context.Context { |
|
return context.WithValue(ctx, sessionContextKey, c) |
|
} |
|
|
|
func SessionContextFromContext(ctx context.Context) SessionContext { |
|
if c := ctx.Value(sessionContextKey); c != nil { |
|
return c.(SessionContext) |
|
} |
|
return DefaultSessionContext() |
|
} |
|
|
|
type Authentication struct { |
|
Header [HeaderSize]byte |
|
DecodingKey [32]byte |
|
EncodingKey [32]byte |
|
DecodingNonce [16]byte |
|
EncodingNonce [16]byte |
|
} |
|
|
|
func (a *Authentication) DataCenterID() uint16 { |
|
x := ((int16(a.Header[61]) << 8) | int16(a.Header[60])) |
|
if x < 0 { |
|
x = -x |
|
} |
|
return uint16(x) - 1 |
|
} |
|
|
|
func (a *Authentication) ConnectionType() [4]byte { |
|
var x [4]byte |
|
copy(x[:], a.Header[56:60]) |
|
return x |
|
} |
|
|
|
func (a *Authentication) ApplySecret(b []byte) { |
|
a.DecodingKey = sha256.Sum256(append(a.DecodingKey[:], b...)) |
|
a.EncodingKey = sha256.Sum256(append(a.EncodingKey[:], b...)) |
|
} |
|
|
|
func generateRandomBytes(random []byte, connType [4]byte) { |
|
for { |
|
common.Must2(rand.Read(random)) |
|
|
|
if random[0] == 0xef { |
|
continue |
|
} |
|
|
|
val := (uint32(random[3]) << 24) | (uint32(random[2]) << 16) | (uint32(random[1]) << 8) | uint32(random[0]) |
|
if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee { |
|
continue |
|
} |
|
|
|
if (uint32(random[7])<<24)|(uint32(random[6])<<16)|(uint32(random[5])<<8)|uint32(random[4]) == 0x00000000 { |
|
continue |
|
} |
|
|
|
copy(random[56:60], connType[:]) |
|
|
|
return |
|
} |
|
} |
|
|
|
func NewAuthentication(sc SessionContext) *Authentication { |
|
auth := getAuthenticationObject() |
|
random := auth.Header[:] |
|
generateRandomBytes(random, sc.ConnectionType) |
|
copy(auth.EncodingKey[:], random[8:]) |
|
copy(auth.EncodingNonce[:], random[8+32:]) |
|
keyivInverse := Inverse(random[8 : 8+32+16]) |
|
copy(auth.DecodingKey[:], keyivInverse) |
|
copy(auth.DecodingNonce[:], keyivInverse[32:]) |
|
return auth |
|
} |
|
|
|
func ReadAuthentication(reader io.Reader) (*Authentication, error) { |
|
auth := getAuthenticationObject() |
|
|
|
if _, err := io.ReadFull(reader, auth.Header[:]); err != nil { |
|
putAuthenticationObject(auth) |
|
return nil, err |
|
} |
|
|
|
copy(auth.DecodingKey[:], auth.Header[8:]) |
|
copy(auth.DecodingNonce[:], auth.Header[8+32:]) |
|
keyivInverse := Inverse(auth.Header[8 : 8+32+16]) |
|
copy(auth.EncodingKey[:], keyivInverse) |
|
copy(auth.EncodingNonce[:], keyivInverse[32:]) |
|
|
|
return auth, nil |
|
} |
|
|
|
// Inverse returns a new byte array. It is a sequence of bytes when the input is read from end to beginning.Inverse |
|
// Visible for testing only. |
|
func Inverse(b []byte) []byte { |
|
lenb := len(b) |
|
b2 := make([]byte, lenb) |
|
for i, v := range b { |
|
b2[lenb-i-1] = v |
|
} |
|
return b2 |
|
} |
|
|
|
var ( |
|
authPool = sync.Pool{ |
|
New: func() interface{} { |
|
return new(Authentication) |
|
}, |
|
} |
|
) |
|
|
|
func getAuthenticationObject() *Authentication { |
|
return authPool.Get().(*Authentication) |
|
} |
|
|
|
func putAuthenticationObject(auth *Authentication) { |
|
authPool.Put(auth) |
|
}
|
|
|