Files

108 lines
2.6 KiB
Go

package mtproxy
import (
"bytes"
"crypto/aes"
"crypto/cipher"
crand "crypto/rand"
"crypto/sha256"
"encoding/binary"
"fmt"
"io"
)
// ObfuscateTagRandomizedIntermediate matches Telethon RandomizedIntermediatePacketCodec.
var ObfuscateTagRandomizedIntermediate = []byte{0xdd, 0xdd, 0xdd, 0xdd}
// randSource is injectable for tests.
var randSource io.Reader = crand.Reader
// InitHeader returns the 64-byte first payload (MTProxyIO.init_header) and CTR streams.
func InitHeader(secret16 []byte, dcID int16) (header64 []byte, enc cipher.Stream, dec cipher.Stream, err error) {
if len(secret16) != 16 {
return nil, nil, nil, fmt.Errorf("mtproxy secret must be 16 bytes")
}
sec := secret16
keywords := [][]byte{
[]byte("PVrG"),
[]byte("GET "),
[]byte("POST"),
{0xee, 0xee, 0xee, 0xee},
}
var rnd [64]byte
for {
if _, err := io.ReadFull(randSource, rnd[:]); err != nil {
return nil, nil, nil, err
}
if rnd[0] == 0xef {
continue
}
bad := false
for _, k := range keywords {
if len(k) <= 4 && bytes.Equal(rnd[:len(k)], k) {
bad = true
break
}
}
if bad {
continue
}
if rnd[4] == 0 && rnd[5] == 0 && rnd[6] == 0 && rnd[7] == 0 {
continue
}
break
}
random := rnd[:]
rev := make([]byte, 48)
for i := 0; i < 48; i++ {
rev[i] = random[55-i]
}
encKey := sha256Sum(append(append([]byte{}, random[8:40]...), sec...))
encIV := random[40:56]
decKey := sha256Sum(append(append([]byte{}, rev[:32]...), sec...))
decIV := rev[32:48]
enc, err = newCTR(encKey, encIV)
if err != nil {
return nil, nil, nil, err
}
dec, err = newCTR(decKey, decIV)
if err != nil {
return nil, nil, nil, err
}
buf := make([]byte, 64)
copy(buf, random)
copy(buf[56:60], ObfuscateTagRandomizedIntermediate)
dcBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(dcBytes, uint16(dcID))
copy(buf[60:62], dcBytes)
// Как в Telethon MTProxyIO.init_header: encrypt(bytes(random)) на всём 64-байтном буфере,
// на провод уходит только random[0:56] как есть и random[56:64] = out[56:64].
// CTR enc должен продвинуться на 64 байта — иначе req_pq и чтение DC расходятся с прокси.
work := make([]byte, 64)
copy(work, buf)
out := make([]byte, 64)
enc.XORKeyStream(out, work)
copy(buf[56:64], out[56:64])
return buf, enc, dec, nil
}
func sha256Sum(b []byte) []byte {
h := sha256.Sum256(b)
return h[:]
}
func newCTR(key, iv []byte) (cipher.Stream, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewCTR(block, iv), nil
}