mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 20:04:02 +03:00
Integrate new fake package and doppel into proxy
This commit is contained in:
@@ -12,7 +12,6 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/mtglib"
|
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/tls"
|
"github.com/9seconds/mtg/v2/mtglib/internal/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,7 +36,12 @@ type ClientHello struct {
|
|||||||
CipherSuite uint16
|
CipherSuite uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadClientHello(conn net.Conn, secret mtglib.Secret, tolerateTimeSkewness time.Duration) (*ClientHello, error) {
|
func ReadClientHello(
|
||||||
|
conn net.Conn,
|
||||||
|
secret []byte,
|
||||||
|
hostname string,
|
||||||
|
tolerateTimeSkewness time.Duration,
|
||||||
|
) (*ClientHello, error) {
|
||||||
if err := conn.SetReadDeadline(time.Now().Add(ClientHelloReadTimeout)); err != nil {
|
if err := conn.SetReadDeadline(time.Now().Add(ClientHelloReadTimeout)); err != nil {
|
||||||
return nil, fmt.Errorf("cannot set read deadline: %w", err)
|
return nil, fmt.Errorf("cannot set read deadline: %w", err)
|
||||||
}
|
}
|
||||||
@@ -75,11 +79,11 @@ func ReadClientHello(conn net.Conn, secret mtglib.Secret, tolerateTimeSkewness t
|
|||||||
return nil, fmt.Errorf("cannot parse SNI: %w", err)
|
return nil, fmt.Errorf("cannot parse SNI: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slices.Contains(sniHostnames, secret.Host) {
|
if !slices.Contains(sniHostnames, hostname) {
|
||||||
return nil, fmt.Errorf("cannot find %s in %v", secret.Host, sniHostnames)
|
return nil, fmt.Errorf("cannot find %s in %v", hostname, sniHostnames)
|
||||||
}
|
}
|
||||||
|
|
||||||
digest := hmac.New(sha256.New, secret.Key[:])
|
digest := hmac.New(sha256.New, secret)
|
||||||
// we write a copy of the handshake with client random all nullified.
|
// we write a copy of the handshake with client random all nullified.
|
||||||
digest.Write(handshakeCopyBuf.Next(RandomOffset))
|
digest.Write(handshakeCopyBuf.Next(RandomOffset))
|
||||||
handshakeCopyBuf.Next(RandomLen)
|
handshakeCopyBuf.Next(RandomLen)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func FuzzReadClientHello(f *testing.F) {
|
|||||||
Twice().
|
Twice().
|
||||||
Return(nil)
|
Return(nil)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(r, secret, time.Hour)
|
_, err := fake.ReadClientHello(r, secret.Key[:], secret.Host, time.Hour)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,12 @@ func (suite *ParseClientHelloSnapshotTestSuite) TestSnapshotOk() {
|
|||||||
connMock := suite.makeConn(snapshot.GetFull())
|
connMock := suite.makeConn(snapshot.GetFull())
|
||||||
defer connMock.AssertExpectations(t)
|
defer connMock.AssertExpectations(t)
|
||||||
|
|
||||||
hello, err := fake.ReadClientHello(connMock, suite.secret, TolerateTime)
|
hello, err := fake.ReadClientHello(
|
||||||
|
connMock,
|
||||||
|
suite.secret.Key[:],
|
||||||
|
suite.secret.Host,
|
||||||
|
TolerateTime,
|
||||||
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, snapshot.GetRandom(), hello.Random[:])
|
assert.Equal(t, snapshot.GetRandom(), hello.Random[:])
|
||||||
@@ -131,7 +136,12 @@ func (suite *ParseClientHelloSnapshotTestSuite) TestSnapshotBad() {
|
|||||||
connMock := suite.makeConn(snapshot.GetFull())
|
connMock := suite.makeConn(snapshot.GetFull())
|
||||||
defer connMock.AssertExpectations(t)
|
defer connMock.AssertExpectations(t)
|
||||||
|
|
||||||
_, err = fake.ReadClientHello(connMock, suite.secret, TolerateTime)
|
_, err = fake.ReadClientHello(
|
||||||
|
connMock,
|
||||||
|
suite.secret.Key[:],
|
||||||
|
suite.secret.Host,
|
||||||
|
TolerateTime,
|
||||||
|
)
|
||||||
assert.ErrorIs(t, err, fake.ErrBadDigest)
|
assert.ErrorIs(t, err, fake.ErrBadDigest)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func (suite *ParseClientHello_TLSHeaderTestSuite) TestEmpty() {
|
|||||||
Once().
|
Once().
|
||||||
Return(errors.New("fail"))
|
Return(errors.New("fail"))
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "fail")
|
suite.ErrorContains(err, "fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ func (suite *ParseClientHello_TLSHeaderTestSuite) TestNothing() {
|
|||||||
Twice().
|
Twice().
|
||||||
Return(nil)
|
Return(nil)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorIs(err, io.EOF)
|
suite.ErrorIs(err, io.EOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ func (suite *ParseClientHello_TLSHeaderTestSuite) TestUnknownRecord() {
|
|||||||
})
|
})
|
||||||
suite.readBuf.WriteByte(10)
|
suite.readBuf.WriteByte(10)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "unexpected record type 0xa")
|
suite.ErrorContains(err, "unexpected record type 0xa")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ func (suite *ParseClientHello_TLSHeaderTestSuite) TestUnknownProtocolVersion() {
|
|||||||
0, 0,
|
0, 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "unexpected protocol version")
|
suite.ErrorContains(err, "unexpected protocol version")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ func (suite *ParseClientHello_TLSHeaderTestSuite) TestCannotReadRestOfRecord() {
|
|||||||
0, 10,
|
0, 10,
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorIs(err, io.EOF)
|
suite.ErrorIs(err, io.EOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ func (suite *ParseClientHelloHandshakeTestSuite) TestCannotReadHeader() {
|
|||||||
10,
|
10,
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read handshake header")
|
suite.ErrorContains(err, "cannot read handshake header")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ func (suite *ParseClientHelloHandshakeTestSuite) TestIncorrectHandshakeType() {
|
|||||||
10, 0, 0, 0,
|
10, 0, 0, 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "incorrect handshake type")
|
suite.ErrorContains(err, "incorrect handshake type")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ func (suite *ParseClientHelloHandshakeTestSuite) TestCannotReadHandshake() {
|
|||||||
10, 0, 0, 0,
|
10, 0, 0, 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorIs(err, io.EOF)
|
suite.ErrorIs(err, io.EOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,14 +192,14 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) writeBody(body []byte) {
|
|||||||
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadVersion() {
|
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadVersion() {
|
||||||
suite.writeBody(nil)
|
suite.writeBody(nil)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read client version")
|
suite.ErrorContains(err, "cannot read client version")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadRandom() {
|
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadRandom() {
|
||||||
suite.writeBody([]byte{3, 3})
|
suite.writeBody([]byte{3, 3})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read client random")
|
suite.ErrorContains(err, "cannot read client random")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadSessionIDLeng
|
|||||||
|
|
||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read session ID length")
|
suite.ErrorContains(err, "cannot read session ID length")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadSessionID() {
|
|||||||
|
|
||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read session id")
|
suite.ErrorContains(err, "cannot read session id")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCipherSuiteLe
|
|||||||
|
|
||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read cipher suite length")
|
suite.ErrorContains(err, "cannot read cipher suite length")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadFirstCipherSu
|
|||||||
|
|
||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read first cipher suite")
|
suite.ErrorContains(err, "cannot read first cipher suite")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipRemainingCiph
|
|||||||
|
|
||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot skip remaining cipher suites")
|
suite.ErrorContains(err, "cannot skip remaining cipher suites")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMe
|
|||||||
|
|
||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read compression methods length")
|
suite.ErrorContains(err, "cannot read compression methods length")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipCompressionMe
|
|||||||
|
|
||||||
suite.writeBody(body)
|
suite.writeBody(body)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot skip compression methods")
|
suite.ErrorContains(err, "cannot skip compression methods")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,70 +307,70 @@ func (suite *ParseClientHelloSNITestSuite) writeExtensions(extensions []byte) {
|
|||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionsLength() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionsLength() {
|
||||||
suite.writeExtensions(nil)
|
suite.writeExtensions(nil)
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read length of TLS extensions")
|
suite.ErrorContains(err, "cannot read length of TLS extensions")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensions() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensions() {
|
||||||
suite.writeExtensions([]byte{0, 10})
|
suite.writeExtensions([]byte{0, 10})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read extensions")
|
suite.ErrorContains(err, "cannot read extensions")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionType() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionType() {
|
||||||
suite.writeExtensions([]byte{0, 1, 0xAB})
|
suite.writeExtensions([]byte{0, 1, 0xAB})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read extension type")
|
suite.ErrorContains(err, "cannot read extension type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionLength() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionLength() {
|
||||||
suite.writeExtensions([]byte{0, 2, 0xFF, 0xFF})
|
suite.writeExtensions([]byte{0, 2, 0xFF, 0xFF})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "length:")
|
suite.ErrorContains(err, "length:")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionData() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionData() {
|
||||||
suite.writeExtensions([]byte{0, 4, 0xFF, 0xFF, 0, 5})
|
suite.writeExtensions([]byte{0, 4, 0xFF, 0xFF, 0, 5})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "data: len")
|
suite.ErrorContains(err, "data: len")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadSNIRecordLength() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadSNIRecordLength() {
|
||||||
suite.writeExtensions([]byte{0, 5, 0, 0, 0, 1, 0xAB})
|
suite.writeExtensions([]byte{0, 5, 0, 0, 0, 1, 0xAB})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read the length of the SNI record")
|
suite.ErrorContains(err, "cannot read the length of the SNI record")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadSNIListType() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadSNIListType() {
|
||||||
suite.writeExtensions([]byte{0, 6, 0, 0, 0, 2, 0, 1})
|
suite.writeExtensions([]byte{0, 6, 0, 0, 0, 2, 0, 1})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "cannot read SNI list type")
|
suite.ErrorContains(err, "cannot read SNI list type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestIncorrectSNIListType() {
|
func (suite *ParseClientHelloSNITestSuite) TestIncorrectSNIListType() {
|
||||||
suite.writeExtensions([]byte{0, 7, 0, 0, 0, 3, 0, 1, 5})
|
suite.writeExtensions([]byte{0, 7, 0, 0, 0, 3, 0, 1, 5})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "incorrect SNI list type")
|
suite.ErrorContains(err, "incorrect SNI list type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadHostnameLength() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadHostnameLength() {
|
||||||
suite.writeExtensions([]byte{0, 8, 0, 0, 0, 4, 0, 2, 0, 0xAB})
|
suite.writeExtensions([]byte{0, 8, 0, 0, 0, 4, 0, 2, 0, 0xAB})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "incorrect length of the hostname")
|
suite.ErrorContains(err, "incorrect length of the hostname")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParseClientHelloSNITestSuite) TestCannotReadHostname() {
|
func (suite *ParseClientHelloSNITestSuite) TestCannotReadHostname() {
|
||||||
suite.writeExtensions([]byte{0, 9, 0, 0, 0, 5, 0, 3, 0, 0, 5})
|
suite.writeExtensions([]byte{0, 9, 0, 0, 0, 5, 0, 3, 0, 0, 5})
|
||||||
|
|
||||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
|
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||||
suite.ErrorContains(err, "incorrect length of SNI hostname")
|
suite.ErrorContains(err, "incorrect length of SNI hostname")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/9seconds/mtg/v2/mtglib"
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@@ -35,7 +34,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func SendServerHello(w io.Writer, secret mtglib.Secret, clientHello *ClientHello) ([]byte, error) {
|
func SendServerHello(w io.Writer, secret []byte, clientHello *ClientHello) ([]byte, error) {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
buf.Grow(tls.MaxRecordSize)
|
buf.Grow(tls.MaxRecordSize)
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ func SendServerHello(w io.Writer, secret mtglib.Secret, clientHello *ClientHello
|
|||||||
generateNoise(noise)
|
generateNoise(noise)
|
||||||
|
|
||||||
packet := buf.Bytes()
|
packet := buf.Bytes()
|
||||||
digest := hmac.New(sha256.New, secret.Key[:])
|
digest := hmac.New(sha256.New, secret)
|
||||||
|
|
||||||
digest.Write(clientHello.Random[:])
|
digest.Write(clientHello.Random[:])
|
||||||
digest.Write(packet)
|
digest.Write(packet)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func (suite *SendServerHelloTestSuite) SetupTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *SendServerHelloTestSuite) TestRecordStructure() {
|
func (suite *SendServerHelloTestSuite) TestRecordStructure() {
|
||||||
noise, err := fake.SendServerHello(suite.buf, suite.secret, suite.hello)
|
noise, err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
var rec bytes.Buffer
|
var rec bytes.Buffer
|
||||||
@@ -65,7 +65,7 @@ func (suite *SendServerHelloTestSuite) TestRecordStructure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *SendServerHelloTestSuite) TestHMAC() {
|
func (suite *SendServerHelloTestSuite) TestHMAC() {
|
||||||
noise, err := fake.SendServerHello(suite.buf, suite.secret, suite.hello)
|
noise, 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())
|
||||||
@@ -84,7 +84,7 @@ func (suite *SendServerHelloTestSuite) TestHMAC() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *SendServerHelloTestSuite) TestHandshakePayload() {
|
func (suite *SendServerHelloTestSuite) TestHandshakePayload() {
|
||||||
_, err := fake.SendServerHello(suite.buf, suite.secret, 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()
|
||||||
@@ -106,7 +106,7 @@ func (suite *SendServerHelloTestSuite) TestHandshakePayload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *SendServerHelloTestSuite) TestChangeCipherSpec() {
|
func (suite *SendServerHelloTestSuite) TestChangeCipherSpec() {
|
||||||
_, err := fake.SendServerHello(suite.buf, suite.secret, suite.hello)
|
_, err := fake.SendServerHello(suite.buf, suite.secret.Key[:], suite.hello)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// Skip first record
|
// Skip first record
|
||||||
|
|||||||
+20
-31
@@ -11,10 +11,11 @@ import (
|
|||||||
|
|
||||||
"github.com/9seconds/mtg/v2/essentials"
|
"github.com/9seconds/mtg/v2/essentials"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/dc"
|
"github.com/9seconds/mtg/v2/mtglib/internal/dc"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/faketls"
|
"github.com/9seconds/mtg/v2/mtglib/internal/doppel"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
|
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscation"
|
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscation"
|
||||||
"github.com/9seconds/mtg/v2/mtglib/internal/relay"
|
"github.com/9seconds/mtg/v2/mtglib/internal/relay"
|
||||||
|
"github.com/9seconds/mtg/v2/mtglib/internal/tls"
|
||||||
|
"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ type Proxy struct {
|
|||||||
workerPool *ants.PoolWithFunc
|
workerPool *ants.PoolWithFunc
|
||||||
telegram *dc.Telegram
|
telegram *dc.Telegram
|
||||||
configUpdater *dc.PublicConfigUpdater
|
configUpdater *dc.PublicConfigUpdater
|
||||||
|
doppelGanger *doppel.Ganger
|
||||||
clientObfuscatror obfuscation.Obfuscator
|
clientObfuscatror obfuscation.Obfuscator
|
||||||
|
|
||||||
secret Secret
|
secret Secret
|
||||||
@@ -161,52 +163,39 @@ func (p *Proxy) Shutdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool {
|
func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) bool {
|
||||||
rec := record.AcquireRecord()
|
|
||||||
defer record.ReleaseRecord(rec)
|
|
||||||
|
|
||||||
rewind := newConnRewind(ctx.clientConn)
|
rewind := newConnRewind(ctx.clientConn)
|
||||||
|
|
||||||
if err := rec.Read(rewind); err != nil {
|
clientHello, err := fake.ReadClientHello(
|
||||||
|
rewind,
|
||||||
|
p.secret.Key[:],
|
||||||
|
p.secret.Host,
|
||||||
|
p.tolerateTimeSkewness,
|
||||||
|
)
|
||||||
|
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 false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
hello, err := faketls.ParseClientHello(p.secret.Key[:], rec.Payload.Bytes())
|
if p.antiReplayCache.SeenBefore(clientHello.SessionID) {
|
||||||
if err != nil {
|
|
||||||
p.logger.InfoError("cannot parse client hello", err)
|
|
||||||
p.doDomainFronting(ctx, rewind)
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hello.Valid(p.secret.Host, p.tolerateTimeSkewness); err != nil {
|
|
||||||
p.logger.
|
|
||||||
BindStr("hostname", hello.Host).
|
|
||||||
BindStr("hello-time", hello.Time.String()).
|
|
||||||
InfoError("invalid faketls client hello", err)
|
|
||||||
p.doDomainFronting(ctx, rewind)
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.antiReplayCache.SeenBefore(hello.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 false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := faketls.SendWelcomePacket(rewind, p.secret.Key[:], hello); err != nil {
|
_, err = fake.SendServerHello(ctx.clientConn, p.secret.Key[:], clientHello)
|
||||||
|
if err != nil {
|
||||||
p.logger.InfoError("cannot send welcome packet", err)
|
p.logger.InfoError("cannot send welcome packet", err)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.clientConn = &faketls.Conn{
|
ctx.clientConn = tls.New(ctx.clientConn, true, true)
|
||||||
Conn: ctx.clientConn,
|
|
||||||
|
ctx.clientConn, err = p.doppelGanger.NewConn(ctx.clientConn)
|
||||||
|
if err != nil {
|
||||||
|
p.logger.WarningError("cannot create connection", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
+1
-12
@@ -1,9 +1,6 @@
|
|||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import "net/http"
|
||||||
"crypto/tls"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type networkHTTPTransport struct {
|
type networkHTTPTransport struct {
|
||||||
userAgent string
|
userAgent string
|
||||||
@@ -15,11 +12,3 @@ func (n networkHTTPTransport) RoundTrip(req *http.Request) (*http.Response, erro
|
|||||||
|
|
||||||
return n.next.RoundTrip(req) //nolint: wrapcheck
|
return n.next.RoundTrip(req) //nolint: wrapcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n networkHTTPTransport) TrustTLS() {
|
|
||||||
tr := n.next.(*http.Transport)
|
|
||||||
if tr.TLSClientConfig == nil {
|
|
||||||
tr.TLSClientConfig = &tls.Config{}
|
|
||||||
}
|
|
||||||
tr.TLSClientConfig.InsecureSkipVerify = true
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user