Merge pull request #359 from 9seconds/fix-android

Fix android ping
This commit is contained in:
Sergei Arkhipov
2026-03-13 16:46:18 +01:00
committed by GitHub
3 changed files with 28 additions and 37 deletions
+8 -8
View File
@@ -7,14 +7,16 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/binary" "encoding/binary"
"io" "io"
"math"
rnd "math/rand/v2"
"github.com/9seconds/mtg/v2/mtglib/internal/doppel"
"github.com/9seconds/mtg/v2/mtglib/internal/tls" "github.com/9seconds/mtg/v2/mtglib/internal/tls"
"golang.org/x/crypto/curve25519" "golang.org/x/crypto/curve25519"
) )
const ( const (
TypeHandshakeServer = 0x02 TypeHandshakeServer = 0x02
ChangeCipherValue = 0x01 ChangeCipherValue = 0x01
EllipticCurveLen = 32 EllipticCurveLen = 32
@@ -32,27 +34,24 @@ var serverHelloSuffix = []byte{
0x00, 0x20, // 32 bytes of key 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 := &bytes.Buffer{}
buf.Grow(tls.MaxRecordSize) buf.Grow(tls.MaxRecordSize)
generateServerHello(buf, clientHello) generateServerHello(buf, clientHello)
generateChangeCipherValue(buf) generateChangeCipherValue(buf)
generateNoise(buf)
noise := &bytes.Buffer{}
generateNoise(noise)
packet := buf.Bytes() packet := buf.Bytes()
digest := hmac.New(sha256.New, secret) digest := hmac.New(sha256.New, secret)
digest.Write(clientHello.Random[:]) digest.Write(clientHello.Random[:])
digest.Write(packet) digest.Write(packet)
digest.Write(noise.Bytes())
copy(packet[RandomOffset:], digest.Sum(nil)) copy(packet[RandomOffset:], digest.Sum(nil))
_, err := w.Write(packet) _, err := w.Write(packet)
return noise.Bytes()[tls.SizeHeader:], err return err
} }
func generateServerHello(buf *bytes.Buffer, hello *ClientHello) { func generateServerHello(buf *bytes.Buffer, hello *ClientHello) {
@@ -128,7 +127,8 @@ func generateChangeCipherValue(buf *bytes.Buffer) {
} }
func generateNoise(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 { if _, err := rand.Read(data[:]); err != nil {
panic(err) panic(err)
+12 -14
View File
@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/9seconds/mtg/v2/mtglib" "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"
"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake" "github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@@ -38,7 +39,7 @@ func (suite *SendServerHelloTestSuite) SetupTest() {
} }
func (suite *SendServerHelloTestSuite) TestRecordStructure() { 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) suite.NoError(err)
var rec bytes.Buffer var rec bytes.Buffer
@@ -53,14 +54,18 @@ func (suite *SendServerHelloTestSuite) TestRecordStructure() {
suite.NoError(err) suite.NoError(err)
suite.Equal(byte(tls.TypeChangeCipherSpec), recordType) suite.Equal(byte(tls.TypeChangeCipherSpec), recordType)
suite.Empty(suite.buf.Bytes()) rec.Reset()
// noise is raw payload without TLS record header recordType, length, err := tls.ReadRecord(suite.buf, &rec)
suite.Len(noise, 1369) suite.NoError(err)
suite.Equal(byte(tls.TypeApplicationData), recordType)
suite.Greater(length, int64(doppel.TLSRecordSizeStart))
suite.Empty(suite.buf.Bytes())
} }
func (suite *SendServerHelloTestSuite) TestHMAC() { 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) suite.NoError(err)
packet := make([]byte, suite.buf.Len()) packet := make([]byte, suite.buf.Len())
@@ -74,18 +79,11 @@ func (suite *SendServerHelloTestSuite) TestHMAC() {
mac.Write(suite.hello.Random[:]) mac.Write(suite.hello.Random[:])
mac.Write(packet) 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)) suite.Equal(random, mac.Sum(nil))
} }
func (suite *SendServerHelloTestSuite) TestHandshakePayload() { 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) suite.NoError(err)
packet := suite.buf.Bytes() packet := suite.buf.Bytes()
@@ -107,7 +105,7 @@ func (suite *SendServerHelloTestSuite) TestHandshakePayload() {
} }
func (suite *SendServerHelloTestSuite) TestChangeCipherSpec() { 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) suite.NoError(err)
// Skip first record // Skip first record
+7 -14
View File
@@ -78,8 +78,7 @@ func (p *Proxy) ServeConn(conn essentials.Conn) {
ctx.logger.Info("Stream has been finished") ctx.logger.Info("Stream has been finished")
}() }()
noise, ok := p.doFakeTLSHandshake(ctx) if !p.doFakeTLSHandshake(ctx) {
if !ok {
return return
} }
@@ -90,11 +89,6 @@ func (p *Proxy) ServeConn(conn essentials.Conn) {
} }
defer clientConn.Stop() defer clientConn.Stop()
if _, err := clientConn.SyncWrite(noise); err != nil {
ctx.logger.InfoError("cannot send the first packet", err)
return
}
ctx.clientConn = clientConn ctx.clientConn = clientConn
if err := p.doObfuscatedHandshake(ctx); err != nil { if err := p.doObfuscatedHandshake(ctx); err != nil {
@@ -176,7 +170,7 @@ func (p *Proxy) Shutdown() {
p.blocklist.Shutdown() p.blocklist.Shutdown()
} }
func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) ([]byte, bool) { func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool {
rewind := newConnRewind(ctx.clientConn) rewind := newConnRewind(ctx.clientConn)
clientHello, err := fake.ReadClientHello( clientHello, err := fake.ReadClientHello(
@@ -188,25 +182,24 @@ func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) ([]byte, bool) {
if err != nil { if err != nil {
p.logger.InfoError("cannot read client hello", err) p.logger.InfoError("cannot read client hello", err)
p.doDomainFronting(ctx, rewind) p.doDomainFronting(ctx, rewind)
return nil, false return false
} }
if p.antiReplayCache.SeenBefore(clientHello.SessionID) { if p.antiReplayCache.SeenBefore(clientHello.SessionID) {
p.logger.Warning("replay attack has been detected!") p.logger.Warning("replay attack has been detected!")
p.eventStream.Send(p.ctx, NewEventReplayAttack(ctx.streamID)) p.eventStream.Send(p.ctx, NewEventReplayAttack(ctx.streamID))
p.doDomainFronting(ctx, rewind) p.doDomainFronting(ctx, rewind)
return nil, false return false
} }
noise, err := fake.SendServerHello(ctx.clientConn, p.secret.Key[:], clientHello) if err := fake.SendServerHello(ctx.clientConn, p.secret.Key[:], clientHello); err != nil {
if err != nil {
p.logger.InfoError("cannot send welcome packet", err) p.logger.InfoError("cannot send welcome packet", err)
return nil, false return false
} }
ctx.clientConn = tls.New(ctx.clientConn, true, false) ctx.clientConn = tls.New(ctx.clientConn, true, false)
return noise, true return true
} }
func (p *Proxy) doObfuscatedHandshake(ctx *streamContext) error { func (p *Proxy) doObfuscatedHandshake(ctx *streamContext) error {