mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 17:54:01 +03:00
Add new obfuscation package
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package obfuscation
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
|
||||
"github.com/9seconds/mtg/v2/essentials"
|
||||
)
|
||||
|
||||
type conn struct {
|
||||
essentials.Conn
|
||||
|
||||
sendCipher cipher.Stream
|
||||
recvCipher cipher.Stream
|
||||
}
|
||||
|
||||
func (c conn) Read(p []byte) (int, error) {
|
||||
n, err := c.Conn.Read(p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
c.recvCipher.XORKeyStream(p, p[:n])
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c conn) Write(p []byte) (int, error) {
|
||||
// yes, this is a bit violent and goes against a contract in io.Writer
|
||||
// but we do it to avoid creating a new buffer just to perform this
|
||||
// encryption.
|
||||
c.sendCipher.XORKeyStream(p, p)
|
||||
|
||||
return c.Conn.Write(p)
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package obfuscation
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/9seconds/mtg/v2/essentials"
|
||||
"github.com/9seconds/mtg/v2/internal/testlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ConnTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
secret []byte
|
||||
}
|
||||
|
||||
func (s *ConnTestSuite) SetupSuite() {
|
||||
secret := [32]byte{}
|
||||
s.secret = secret[:]
|
||||
}
|
||||
|
||||
func (s *ConnTestSuite) TestRead() {
|
||||
testData := map[string]string{
|
||||
"data1": "b8f4b41993",
|
||||
"": "",
|
||||
"___": "83ca9f",
|
||||
}
|
||||
|
||||
for incoming, outgoing := range testData {
|
||||
s.T().Run(incoming, func(t *testing.T) {
|
||||
connMock := &testlib.EssentialsConnMock{}
|
||||
testConn := s.makeConn(connMock)
|
||||
data := make([]byte, len(incoming))
|
||||
|
||||
connMock.On("Read", make([]byte, len(incoming))).Return(len(incoming), nil).Run(func(args mock.Arguments) {
|
||||
arg := args.Get(0).([]byte)
|
||||
copy(arg, []byte(incoming))
|
||||
})
|
||||
|
||||
n, err := testConn.Read(data)
|
||||
|
||||
assert.Equal(t, len(data), n)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, outgoing, hex.EncodeToString(data))
|
||||
|
||||
connMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConnTestSuite) TestWrite() {
|
||||
testData := map[string]string{
|
||||
"b8f4b41993": "data1",
|
||||
"": "",
|
||||
"83ca9f": "___",
|
||||
}
|
||||
|
||||
for incoming, outgoing := range testData {
|
||||
s.T().Run(incoming, func(t *testing.T) {
|
||||
connMock := &testlib.EssentialsConnMock{}
|
||||
testConn := s.makeConn(connMock)
|
||||
toWrite, _ := hex.DecodeString(incoming)
|
||||
data := make([]byte, len(toWrite))
|
||||
|
||||
connMock.On("Write", []byte(outgoing)).Return(len(toWrite), nil)
|
||||
|
||||
n, err := testConn.Write(toWrite)
|
||||
assert.Equal(t, len(data), n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
connMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConnTestSuite) makeConn(rawConn *testlib.EssentialsConnMock) essentials.Conn {
|
||||
rblock, err := aes.NewCipher(s.secret)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
wblock, err := aes.NewCipher(s.secret)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return conn{
|
||||
Conn: rawConn,
|
||||
sendCipher: cipher.NewCTR(wblock, s.secret[:aes.BlockSize]),
|
||||
recvCipher: cipher.NewCTR(rblock, s.secret[:aes.BlockSize]),
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn(t *testing.T) {
|
||||
t.Parallel()
|
||||
suite.Run(t, &ConnTestSuite{})
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package obfuscation
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// https://core.telegram.org/mtproto/mtproto-transports#transport-obfuscation
|
||||
const (
|
||||
// default DC is nothing is selected
|
||||
defaultDC = 2
|
||||
|
||||
// the length of the handshake frame. Always 64 bytes
|
||||
hfLen = 64
|
||||
|
||||
hfLenKey = 32
|
||||
hfLenIV = 16
|
||||
hfLenConnectionType = 4
|
||||
|
||||
// A structure of obfuscated handshake frame is following:
|
||||
//
|
||||
// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd].
|
||||
//
|
||||
// - 8 bytes of noise
|
||||
// - 32 bytes of AES Key
|
||||
// - 16 bytes of AES IV
|
||||
// - 4 bytes of 'connection type' - this has some setting like a connection type
|
||||
// - 2 bytes of 'DC'. DC is little endian int16
|
||||
// - 2 bytes of noise
|
||||
hfOffsetKey = 8
|
||||
hfOffsetIV = hfOffsetKey + hfLenKey
|
||||
hfOffsetConnectionType = hfOffsetIV + hfLenIV
|
||||
hfOffsetDC = hfOffsetConnectionType + hfLenConnectionType
|
||||
)
|
||||
|
||||
// Connection-Type: Secure. We support only fake tls.
|
||||
var hfConnectionType = [hfLenConnectionType]byte{0xdd, 0xdd, 0xdd, 0xdd}
|
||||
|
||||
type handshakeFrame struct {
|
||||
data [hfLen]byte
|
||||
}
|
||||
|
||||
func (h *handshakeFrame) key() []byte {
|
||||
return h.data[hfOffsetKey : hfOffsetKey+hfLenKey]
|
||||
}
|
||||
|
||||
func (h *handshakeFrame) iv() []byte {
|
||||
return h.data[hfOffsetIV : hfOffsetIV+hfLenIV]
|
||||
}
|
||||
|
||||
func (h *handshakeFrame) connectionType() []byte {
|
||||
return h.data[hfOffsetConnectionType : hfOffsetConnectionType+hfLenConnectionType]
|
||||
}
|
||||
|
||||
func (h *handshakeFrame) dcSlice() []byte {
|
||||
return h.data[hfOffsetDC : hfOffsetDC+2]
|
||||
}
|
||||
|
||||
func (h *handshakeFrame) dc() int {
|
||||
idx := int16(binary.LittleEndian.Uint16(h.dcSlice()))
|
||||
|
||||
switch {
|
||||
case idx > 0:
|
||||
return int(idx)
|
||||
case idx < 0:
|
||||
return -int(idx)
|
||||
}
|
||||
|
||||
return defaultDC
|
||||
}
|
||||
|
||||
func (h *handshakeFrame) revert() {
|
||||
slices.Reverse(h.data[hfOffsetKey:hfOffsetConnectionType])
|
||||
}
|
||||
|
||||
func generateHandshake(dc int) handshakeFrame {
|
||||
frame := handshakeFrame{}
|
||||
|
||||
for {
|
||||
if _, err := rand.Read(frame.data[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// https://github.com/tdlib/td/blob/master/td/mtproto/TcpTransport.cpp#L157-L158.
|
||||
if frame.data[0] == 0xef { // abridged header
|
||||
// https://core.telegram.org/mtproto/mtproto-transports#abridged
|
||||
continue
|
||||
}
|
||||
|
||||
switch binary.LittleEndian.Uint32(frame.data[:4]) {
|
||||
case 0x44414548, // HEAD
|
||||
0x54534f50, // POST
|
||||
0x20544547, // GET
|
||||
0x4954504f, // OPTI
|
||||
0x02010316, // ????
|
||||
0xdddddddd, // PaddedIntermediate header
|
||||
0xeeeeeeee: // Intermediate header
|
||||
continue
|
||||
}
|
||||
|
||||
if frame.data[4]|frame.data[5]|frame.data[6]|frame.data[7] == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
copy(frame.connectionType(), hfConnectionType[:])
|
||||
binary.LittleEndian.PutUint16(frame.dcSlice(), uint16(dc))
|
||||
|
||||
return frame
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package obfuscation
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func FuzzGenerateHandshakeFrame(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, arg int16) {
|
||||
frame := generateHandshake(int(arg))
|
||||
|
||||
assert.NotEqualValues(t, 0xef, frame.data[0])
|
||||
|
||||
firstBytes := binary.LittleEndian.Uint32(frame.data[:4])
|
||||
assert.NotEqualValues(t, 0x44414548, firstBytes)
|
||||
assert.NotEqualValues(t, 0x54534f50, firstBytes)
|
||||
assert.NotEqualValues(t, 0x20544547, firstBytes)
|
||||
assert.NotEqualValues(t, 0x4954504f, firstBytes)
|
||||
assert.NotEqualValues(t, 0x02010316, firstBytes)
|
||||
assert.NotEqualValues(t, 0xeeeeeeee, firstBytes)
|
||||
assert.NotEqualValues(t, 0xdddddddd, firstBytes)
|
||||
|
||||
assert.NotEqualValues(
|
||||
t,
|
||||
0,
|
||||
frame.data[4]|frame.data[5]|frame.data[6]|frame.data[7])
|
||||
|
||||
assert.Equal(t, hfConnectionType[:], frame.connectionType())
|
||||
assert.EqualValues(t, arg, frame.dc())
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package obfuscation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type HandshakeFrameTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
frame handshakeFrame
|
||||
reverted handshakeFrame
|
||||
}
|
||||
|
||||
func (h *HandshakeFrameTestSuite) SetupSuite() {
|
||||
for i := range hfLen {
|
||||
h.frame.data[i] = byte(i + 1)
|
||||
h.reverted.data[i] = byte(hfLen - i)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HandshakeFrameTestSuite) TestKey() {
|
||||
key := h.frame.key()
|
||||
h.EqualValues(8+1, key[0])
|
||||
h.EqualValues(8+hfLenKey, key[len(key)-1])
|
||||
h.Len(key, hfLenKey)
|
||||
}
|
||||
|
||||
func (h *HandshakeFrameTestSuite) TestIV() {
|
||||
iv := h.frame.iv()
|
||||
h.EqualValues(40+1, iv[0])
|
||||
h.EqualValues(40+hfLenIV, iv[len(iv)-1])
|
||||
h.Len(iv, hfLenIV)
|
||||
}
|
||||
|
||||
func (h *HandshakeFrameTestSuite) TestConnectionType() {
|
||||
connectionType := h.frame.connectionType()
|
||||
h.EqualValues(56+1, connectionType[0])
|
||||
h.EqualValues(56+hfLenConnectionType, connectionType[len(connectionType)-1])
|
||||
h.Len(connectionType, hfLenConnectionType)
|
||||
}
|
||||
|
||||
func (h *HandshakeFrameTestSuite) TestDCSlice() {
|
||||
dcSlice := h.frame.dcSlice()
|
||||
h.EqualValues(61, dcSlice[0])
|
||||
h.EqualValues(61+1, dcSlice[1])
|
||||
h.Len(dcSlice, 2)
|
||||
}
|
||||
|
||||
func (h *HandshakeFrameTestSuite) TestDC() {
|
||||
h.Equal(15933, h.frame.dc())
|
||||
}
|
||||
|
||||
func (h *HandshakeFrameTestSuite) TestRevert() {
|
||||
fr := h.frame
|
||||
fr.revert()
|
||||
|
||||
h.Equal(h.reverted.key(), fr.key())
|
||||
h.Equal(h.reverted.iv(), fr.iv())
|
||||
}
|
||||
|
||||
func TestHandshakeFrame(t *testing.T) {
|
||||
t.Parallel()
|
||||
suite.Run(t, &HandshakeFrameTestSuite{})
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package obfuscation_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type snapshotBytes struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (s snapshotBytes) MarshalText() ([]byte, error) {
|
||||
if len(s.data) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []byte(base64.RawStdEncoding.EncodeToString(s.data)), nil
|
||||
}
|
||||
|
||||
func (s *snapshotBytes) UnmarshalText(data []byte) error {
|
||||
val, err := base64.RawStdEncoding.DecodeString(string(data))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot unmarshal %v: %w", len(val), err)
|
||||
}
|
||||
|
||||
s.data = val
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ObfuscatedSnapshot struct {
|
||||
Secret snapshotBytes `json:"secret"`
|
||||
Frame snapshotBytes `json:"frame"`
|
||||
DC int16 `json:"dc"`
|
||||
Encrypted struct {
|
||||
Text snapshotBytes `json:"text"`
|
||||
Cipher snapshotBytes `json:"cipher"`
|
||||
} `json:"encrypted"`
|
||||
Decrypted struct {
|
||||
Text snapshotBytes `json:"text"`
|
||||
Cipher snapshotBytes `json:"cipher"`
|
||||
} `json:"decrypted"`
|
||||
}
|
||||
|
||||
type SnapshotTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
snapshots map[string]*ObfuscatedSnapshot
|
||||
}
|
||||
|
||||
func (s *SnapshotTestSuite) Setup(dirname, namePrefix string) {
|
||||
s.snapshots = make(map[string]*ObfuscatedSnapshot)
|
||||
|
||||
files, err := os.ReadDir("testdata")
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
for _, v := range files {
|
||||
if !strings.HasPrefix(v.Name(), namePrefix) {
|
||||
continue
|
||||
}
|
||||
|
||||
filename := filepath.Join("testdata", v.Name())
|
||||
|
||||
contents, err := os.ReadFile(filename)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
value := &ObfuscatedSnapshot{}
|
||||
require.NoError(s.T(), json.Unmarshal(contents, value))
|
||||
|
||||
s.snapshots[v.Name()] = value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package obfuscation
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"github.com/9seconds/mtg/v2/essentials"
|
||||
)
|
||||
|
||||
type Obfuscator struct {
|
||||
Secret []byte
|
||||
}
|
||||
|
||||
func (o Obfuscator) ReadHandshake(r essentials.Conn) (int, essentials.Conn, error) {
|
||||
frame := handshakeFrame{}
|
||||
|
||||
if _, err := io.ReadFull(r, frame.data[:]); err != nil {
|
||||
return 0, nil, fmt.Errorf("cannot read frame: %w", err)
|
||||
}
|
||||
|
||||
hasher := sha256.New()
|
||||
recvCipher := o.getCipher(&frame, hasher)
|
||||
|
||||
frame.revert()
|
||||
hasher.Reset()
|
||||
sendCipher := o.getCipher(&frame, hasher)
|
||||
|
||||
recvCipher.XORKeyStream(frame.data[:], frame.data[:])
|
||||
|
||||
if val := frame.connectionType(); subtle.ConstantTimeCompare(val, hfConnectionType[:]) != 1 {
|
||||
return 0, nil, fmt.Errorf("unsupported connection type: %s", hex.EncodeToString(val))
|
||||
}
|
||||
|
||||
cn := conn{
|
||||
Conn: r,
|
||||
recvCipher: recvCipher,
|
||||
sendCipher: sendCipher,
|
||||
}
|
||||
|
||||
return frame.dc(), cn, nil
|
||||
}
|
||||
|
||||
func (o Obfuscator) SendHandshake(w essentials.Conn, dc int) (essentials.Conn, error) {
|
||||
frame := generateHandshake(dc)
|
||||
copyFrame := frame
|
||||
hasher := sha256.New()
|
||||
|
||||
sendCipher := o.getCipher(&frame, hasher)
|
||||
|
||||
frame.revert()
|
||||
hasher.Reset()
|
||||
recvCipher := o.getCipher(&frame, hasher)
|
||||
|
||||
sendCipher.XORKeyStream(frame.data[:], frame.data[:])
|
||||
copy(frame.key(), copyFrame.key())
|
||||
copy(frame.iv(), copyFrame.iv())
|
||||
|
||||
if _, err := w.Write(frame.data[:]); err != nil {
|
||||
return nil, fmt.Errorf("cannot send a handshake: %w", err)
|
||||
}
|
||||
|
||||
return conn{
|
||||
Conn: w,
|
||||
recvCipher: recvCipher,
|
||||
sendCipher: sendCipher,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o Obfuscator) getCipher(f *handshakeFrame, hasher hash.Hash) cipher.Stream {
|
||||
blockKey := f.key()
|
||||
|
||||
if o.Secret != nil {
|
||||
hasher.Write(blockKey)
|
||||
hasher.Write(o.Secret)
|
||||
blockKey = hasher.Sum(nil)
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(blockKey)
|
||||
|
||||
return cipher.NewCTR(block, f.iv())
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package obfuscation_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/9seconds/mtg/v2/internal/testlib"
|
||||
"github.com/9seconds/mtg/v2/mtglib"
|
||||
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscation"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func FuzzClientServerHandshakes(f *testing.F) {
|
||||
f.Add(int16(1), make([]byte, mtglib.SecretKeyLength))
|
||||
|
||||
f.Fuzz(func(t *testing.T, dc int16, data []byte) {
|
||||
if dc <= 0 {
|
||||
dc = 1
|
||||
}
|
||||
|
||||
client := obfuscation.Obfuscator{
|
||||
Secret: data,
|
||||
}
|
||||
server := client
|
||||
|
||||
clientToServerBuf := &bytes.Buffer{}
|
||||
|
||||
writeConnMock := &testlib.EssentialsConnMock{}
|
||||
writeConnMock.
|
||||
On("Write", mock.AnythingOfType("[]uint8")).
|
||||
Once().
|
||||
Return(64, nil).
|
||||
Run(func(args mock.Arguments) {
|
||||
arg := args.Get(0).([]byte)
|
||||
n, err := clientToServerBuf.Write(arg)
|
||||
assert.Equal(t, 64, n)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
readConnMock := &testlib.EssentialsConnMock{}
|
||||
readConnMock.
|
||||
On("Read", mock.AnythingOfType("[]uint8")).
|
||||
Once().
|
||||
Return(64, nil).
|
||||
Run(func(args mock.Arguments) {
|
||||
arg := args.Get(0).([]byte)
|
||||
n, err := clientToServerBuf.Read(arg)
|
||||
assert.Equal(t, 64, n)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
_, err := client.SendHandshake(writeConnMock, int(dc))
|
||||
assert.NoError(t, err)
|
||||
|
||||
readDc, _, err := server.ReadHandshake(readConnMock)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, dc, readDc)
|
||||
|
||||
writeConnMock.AssertExpectations(t)
|
||||
readConnMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package obfuscation_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/9seconds/mtg/v2/internal/testlib"
|
||||
"github.com/9seconds/mtg/v2/mtglib"
|
||||
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscation"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ObfuscatorTestSuite struct {
|
||||
SnapshotTestSuite
|
||||
|
||||
secret *mtglib.Secret
|
||||
}
|
||||
|
||||
func (s *ObfuscatorTestSuite) SetupSuite() {
|
||||
s.SnapshotTestSuite.Setup("", "client-handshake")
|
||||
|
||||
secret := mtglib.GenerateSecret("hostname.com")
|
||||
s.secret = &secret
|
||||
}
|
||||
|
||||
func (s *ObfuscatorTestSuite) TestSnapshot() {
|
||||
for name, snapshot := range s.snapshots {
|
||||
s.T().Run(name, func(t *testing.T) {
|
||||
obfs := obfuscation.Obfuscator{
|
||||
Secret: snapshot.Secret.data,
|
||||
}
|
||||
|
||||
connMock := &testlib.EssentialsConnMock{}
|
||||
|
||||
connMockReadBuffer := &bytes.Buffer{}
|
||||
connMockReadBuffer.Write(snapshot.Frame.data)
|
||||
connMockReadBuffer.Write(snapshot.Decrypted.Cipher.data)
|
||||
|
||||
connMockWriteBuffer := &bytes.Buffer{}
|
||||
|
||||
connMock.
|
||||
On("Read", mock.AnythingOfType("[]uint8")).
|
||||
Return(64, nil).
|
||||
Run(func(args mock.Arguments) {
|
||||
arr := args.Get(0).([]byte)
|
||||
_, err := connMockReadBuffer.Read(arr)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
dc, cn, err := obfs.ReadHandshake(connMock)
|
||||
assert.EqualValues(t, 2, dc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
connMock.Calls = []mock.Call{}
|
||||
connMock.ExpectedCalls = []*mock.Call{}
|
||||
|
||||
connMock.
|
||||
On("Read", mock.AnythingOfType("[]uint8")).
|
||||
Return(len(snapshot.Decrypted.Cipher.data), nil).
|
||||
Run(func(args mock.Arguments) {
|
||||
arr := args.Get(0).([]byte)
|
||||
_, err := connMockReadBuffer.Read(arr)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
connMock.
|
||||
On("Write", mock.AnythingOfType("[]uint8")).
|
||||
Return(len(snapshot.Encrypted.Cipher.data), nil).
|
||||
Run(func(args mock.Arguments) {
|
||||
arr := args.Get(0).([]byte)
|
||||
_, err := connMockWriteBuffer.Write(arr)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
readBuf := make([]byte, len(snapshot.Decrypted.Text.data))
|
||||
_, err = cn.Read(readBuf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, readBuf, snapshot.Decrypted.Text.data)
|
||||
|
||||
_, err = cn.Write(snapshot.Encrypted.Text.data)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, connMockWriteBuffer.Bytes(), snapshot.Encrypted.Cipher.data)
|
||||
|
||||
connMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestObfuscator(t *testing.T) {
|
||||
t.Parallel()
|
||||
suite.Run(t, &ObfuscatorTestSuite{})
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"secret": "NnoYmu4Y+jHBkAVO/UqOlQ",
|
||||
"frame": "gDcXwaMY4RwlR+nJw+ILDr123UJHHjjE/U5pF4m/Y04AmH7lEpEL6UYRnIYDbDlOHSDxc1ToziPvNlJJh8RMow",
|
||||
"dc": 2,
|
||||
"encrypted": {
|
||||
"text": "AQIDBAUGBwgJCg",
|
||||
"cipher": "wZV3TR39l9nRoQ"
|
||||
},
|
||||
"decrypted": {
|
||||
"text": "4wZj6mUUew",
|
||||
"cipher": "YWJjZGVmZw"
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"secret": "NnoYmu4Y+jHBkAVO/UqOlQ",
|
||||
"frame": "M2WyxeiwIQB+ZOFxNzSNHtu9OdESkfxv3JkKFimCxUoYA3BD/Ql9nXB/OIonCKLUKCcS0VzZ2P6/+5oQ9GI8YA",
|
||||
"dc": 2,
|
||||
"encrypted": {
|
||||
"text": "AQIDBAUGBwgJCg",
|
||||
"cipher": "tzAwrCz00odERg"
|
||||
},
|
||||
"decrypted": {
|
||||
"text": "QkIvwGQDgA",
|
||||
"cipher": "YWJjZGVmZw"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user