From 45b0964afd560ba96a8dbf6473af04887cf27795 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Fri, 13 Mar 2026 16:20:18 +0100 Subject: [PATCH 1/2] Fix android ping --- mtglib/internal/tls/fake/server_side.go | 9 +++---- mtglib/internal/tls/fake/server_side_test.go | 25 +++++++++----------- mtglib/proxy.go | 21 ++++++---------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/mtglib/internal/tls/fake/server_side.go b/mtglib/internal/tls/fake/server_side.go index 3a0d43a..4c2fe3a 100644 --- a/mtglib/internal/tls/fake/server_side.go +++ b/mtglib/internal/tls/fake/server_side.go @@ -32,27 +32,24 @@ var serverHelloSuffix = []byte{ 0x00, 0x20, // 32 bytes of key } -func SendServerHello(w io.Writer, secret []byte, clientHello *ClientHello) ([]byte, error) { +func SendServerHello(w io.Writer, secret []byte, clientHello *ClientHello) error { buf := &bytes.Buffer{} buf.Grow(tls.MaxRecordSize) generateServerHello(buf, clientHello) generateChangeCipherValue(buf) - - noise := &bytes.Buffer{} - generateNoise(noise) + generateNoise(buf) packet := buf.Bytes() digest := hmac.New(sha256.New, secret) digest.Write(clientHello.Random[:]) digest.Write(packet) - digest.Write(noise.Bytes()) copy(packet[RandomOffset:], digest.Sum(nil)) _, err := w.Write(packet) - return noise.Bytes()[tls.SizeHeader:], err + return err } func generateServerHello(buf *bytes.Buffer, hello *ClientHello) { diff --git a/mtglib/internal/tls/fake/server_side_test.go b/mtglib/internal/tls/fake/server_side_test.go index 0b4ee67..abde893 100644 --- a/mtglib/internal/tls/fake/server_side_test.go +++ b/mtglib/internal/tls/fake/server_side_test.go @@ -38,7 +38,7 @@ func (suite *SendServerHelloTestSuite) SetupTest() { } func (suite *SendServerHelloTestSuite) TestRecordStructure() { - noise, err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello) + err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello) suite.NoError(err) var rec bytes.Buffer @@ -53,14 +53,18 @@ func (suite *SendServerHelloTestSuite) TestRecordStructure() { suite.NoError(err) suite.Equal(byte(tls.TypeChangeCipherSpec), recordType) - suite.Empty(suite.buf.Bytes()) + rec.Reset() - // noise is raw payload without TLS record header - suite.Len(noise, 1369) + recordType, length, err := tls.ReadRecord(suite.buf, &rec) + suite.NoError(err) + suite.Equal(byte(tls.TypeApplicationData), recordType) + suite.Equal(int64(1369), length) + + suite.Empty(suite.buf.Bytes()) } func (suite *SendServerHelloTestSuite) TestHMAC() { - noise, err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello) + err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello) suite.NoError(err) packet := make([]byte, suite.buf.Len()) @@ -74,18 +78,11 @@ func (suite *SendServerHelloTestSuite) TestHMAC() { mac.Write(suite.hello.Random[:]) mac.Write(packet) - // HMAC is computed over the full noise TLS record (with header), - // but SendServerHello returns noise without the header, - // so we reconstruct the full record. - var fullNoise bytes.Buffer - tls.WriteRecord(&fullNoise, noise) //nolint: errcheck - mac.Write(fullNoise.Bytes()) - suite.Equal(random, mac.Sum(nil)) } func (suite *SendServerHelloTestSuite) TestHandshakePayload() { - _, err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello) + err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello) suite.NoError(err) packet := suite.buf.Bytes() @@ -107,7 +104,7 @@ func (suite *SendServerHelloTestSuite) TestHandshakePayload() { } func (suite *SendServerHelloTestSuite) TestChangeCipherSpec() { - _, err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello) + err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello) suite.NoError(err) // Skip first record diff --git a/mtglib/proxy.go b/mtglib/proxy.go index 4afcdd8..7eb31a6 100644 --- a/mtglib/proxy.go +++ b/mtglib/proxy.go @@ -78,8 +78,7 @@ func (p *Proxy) ServeConn(conn essentials.Conn) { ctx.logger.Info("Stream has been finished") }() - noise, ok := p.doFakeTLSHandshake(ctx) - if !ok { + if !p.doFakeTLSHandshake(ctx) { return } @@ -90,11 +89,6 @@ func (p *Proxy) ServeConn(conn essentials.Conn) { } defer clientConn.Stop() - if _, err := clientConn.SyncWrite(noise); err != nil { - ctx.logger.InfoError("cannot send the first packet", err) - return - } - ctx.clientConn = clientConn if err := p.doObfuscatedHandshake(ctx); err != nil { @@ -176,7 +170,7 @@ func (p *Proxy) Shutdown() { p.blocklist.Shutdown() } -func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) ([]byte, bool) { +func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool { rewind := newConnRewind(ctx.clientConn) clientHello, err := fake.ReadClientHello( @@ -188,25 +182,24 @@ func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) ([]byte, bool) { if err != nil { p.logger.InfoError("cannot read client hello", err) p.doDomainFronting(ctx, rewind) - return nil, false + return false } if p.antiReplayCache.SeenBefore(clientHello.SessionID) { p.logger.Warning("replay attack has been detected!") p.eventStream.Send(p.ctx, NewEventReplayAttack(ctx.streamID)) p.doDomainFronting(ctx, rewind) - return nil, false + return false } - noise, err := fake.SendServerHello(ctx.clientConn, p.secret.Key[:], clientHello) - if err != nil { + if err := fake.SendServerHello(ctx.clientConn, p.secret.Key[:], clientHello); err != nil { p.logger.InfoError("cannot send welcome packet", err) - return nil, false + return false } ctx.clientConn = tls.New(ctx.clientConn, true, false) - return noise, true + return true } func (p *Proxy) doObfuscatedHandshake(ctx *streamContext) error { From b35c212d65e21d7528cc34cc762938e3d6e3c421 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Fri, 13 Mar 2026 16:39:16 +0100 Subject: [PATCH 2/2] Have a noise of variable size --- mtglib/internal/tls/fake/server_side.go | 9 ++++++--- mtglib/internal/tls/fake/server_side_test.go | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mtglib/internal/tls/fake/server_side.go b/mtglib/internal/tls/fake/server_side.go index 4c2fe3a..90b736d 100644 --- a/mtglib/internal/tls/fake/server_side.go +++ b/mtglib/internal/tls/fake/server_side.go @@ -7,15 +7,17 @@ import ( "crypto/sha256" "encoding/binary" "io" + "math" + rnd "math/rand/v2" + "github.com/9seconds/mtg/v2/mtglib/internal/doppel" "github.com/9seconds/mtg/v2/mtglib/internal/tls" "golang.org/x/crypto/curve25519" ) const ( TypeHandshakeServer = 0x02 - - ChangeCipherValue = 0x01 + ChangeCipherValue = 0x01 EllipticCurveLen = 32 ) @@ -125,7 +127,8 @@ func generateChangeCipherValue(buf *bytes.Buffer) { } func generateNoise(buf *bytes.Buffer) { - data := [1369]byte{} + minSize := int(math.Round(0.75 * float64(doppel.TLSRecordSizeMax))) + data := make([]byte, minSize+rnd.IntN(doppel.TLSRecordSizeMax-minSize)) if _, err := rand.Read(data[:]); err != nil { panic(err) diff --git a/mtglib/internal/tls/fake/server_side_test.go b/mtglib/internal/tls/fake/server_side_test.go index abde893..c4b54a6 100644 --- a/mtglib/internal/tls/fake/server_side_test.go +++ b/mtglib/internal/tls/fake/server_side_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/9seconds/mtg/v2/mtglib" + "github.com/9seconds/mtg/v2/mtglib/internal/doppel" "github.com/9seconds/mtg/v2/mtglib/internal/tls" "github.com/9seconds/mtg/v2/mtglib/internal/tls/fake" "github.com/stretchr/testify/suite" @@ -58,7 +59,7 @@ func (suite *SendServerHelloTestSuite) TestRecordStructure() { recordType, length, err := tls.ReadRecord(suite.buf, &rec) suite.NoError(err) suite.Equal(byte(tls.TypeApplicationData), recordType) - suite.Equal(int64(1369), length) + suite.Greater(length, int64(doppel.TLSRecordSizeStart)) suite.Empty(suite.buf.Bytes()) }