mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-09-01 00:24:02 +03:00
Success path for fake tls is implemented
This commit is contained in:
+15
-5
@@ -3,14 +3,24 @@ package antireplay
|
||||
import "github.com/allegro/bigcache"
|
||||
|
||||
type cache struct {
|
||||
cache *bigcache.BigCache
|
||||
obfuscated2 *bigcache.BigCache
|
||||
tls *bigcache.BigCache
|
||||
}
|
||||
|
||||
func (c *cache) Add(data []byte) {
|
||||
c.cache.Set(string(data), nil) // nolint: errcheck
|
||||
func (c *cache) AddObfuscated2(data []byte) {
|
||||
c.obfuscated2.Set(string(data), nil) // nolint: errcheck
|
||||
}
|
||||
|
||||
func (c *cache) Has(data []byte) bool {
|
||||
_, err := c.cache.Get(string(data))
|
||||
func (c *cache) AddTLS(data []byte) {
|
||||
c.tls.Set(string(data), nil) // nolint: errcheck
|
||||
}
|
||||
|
||||
func (c *cache) HasObfuscated2(data []byte) bool {
|
||||
_, err := c.obfuscated2.Get(string(data))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (c *cache) HasTLS(data []byte) bool {
|
||||
_, err := c.tls.Get(string(data))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
+13
-2
@@ -14,7 +14,17 @@ var (
|
||||
|
||||
func Init() {
|
||||
initOnce.Do(func() {
|
||||
c, err := bigcache.NewBigCache(bigcache.Config{
|
||||
c1, err := bigcache.NewBigCache(bigcache.Config{
|
||||
Shards: 1024,
|
||||
LifeWindow: config.C.AntiReplayEvictionTime,
|
||||
Hasher: hasher{},
|
||||
HardMaxCacheSize: config.C.AntiReplayMaxSize,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c2, err := bigcache.NewBigCache(bigcache.Config{
|
||||
Shards: 1024,
|
||||
LifeWindow: config.C.AntiReplayEvictionTime,
|
||||
Hasher: hasher{},
|
||||
@@ -25,7 +35,8 @@ func Init() {
|
||||
}
|
||||
|
||||
Cache = &cache{
|
||||
cache: c,
|
||||
obfuscated2: c1,
|
||||
tls: c2,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+5
-6
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/9seconds/mtg/antireplay"
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/faketls"
|
||||
"github.com/9seconds/mtg/hub"
|
||||
"github.com/9seconds/mtg/ntp"
|
||||
"github.com/9seconds/mtg/obfuscated2"
|
||||
@@ -75,6 +76,7 @@ func Proxy() error { // nolint: funlen
|
||||
antireplay.Init()
|
||||
telegram.Init()
|
||||
hub.Init(ctx)
|
||||
faketls.Init(ctx)
|
||||
|
||||
proxyListener, err := net.Listen("tcp", config.C.Bind.String())
|
||||
if err != nil {
|
||||
@@ -91,12 +93,9 @@ func Proxy() error { // nolint: funlen
|
||||
Context: ctx,
|
||||
ClientProtocolMaker: obfuscated2.MakeClientProtocol,
|
||||
}
|
||||
// if len(config.C.AdTag) == 0 {
|
||||
// app.TelegramProtocolMaker = obfuscated2.MakeTelegramProtocol
|
||||
// }
|
||||
// if config.C.SecretMode != config.SecretModeTLS {
|
||||
// app.ClientProtocolMaker = obfuscated2.MakeClientProtocol
|
||||
// }
|
||||
if config.C.SecretMode == config.SecretModeTLS {
|
||||
app.ClientProtocolMaker = faketls.MakeClientProtocol
|
||||
}
|
||||
|
||||
app.Serve(proxyListener)
|
||||
|
||||
|
||||
+3
-1
@@ -28,11 +28,13 @@ func GetURLs() (urls IPURLs) {
|
||||
secret = hex.EncodeToString(C.Secret)
|
||||
case SecretModeSecured:
|
||||
secret = "dd" + hex.EncodeToString(C.Secret)
|
||||
case SecretModeTLS:
|
||||
secret = "ee" + hex.EncodeToString(C.Secret) + hex.EncodeToString([]byte(C.CloakHost))
|
||||
}
|
||||
|
||||
urls.IPv4 = makeURLs(C.PublicIPv4, secret)
|
||||
urls.IPv6 = makeURLs(C.PublicIPv6, secret)
|
||||
urls.BotSecret = secret
|
||||
urls.BotSecret = hex.EncodeToString(C.Secret)
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package faketls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/ring"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
)
|
||||
|
||||
type connectionServer struct {
|
||||
nextWriteItem *ring.Ring
|
||||
nextReadItem *ring.Ring
|
||||
|
||||
ctx context.Context
|
||||
channelGet chan chan<- []byte
|
||||
}
|
||||
|
||||
func (c *connectionServer) get() ([]byte, error) {
|
||||
resp := make(chan []byte)
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return nil, errors.New("context closed")
|
||||
case c.channelGet <- resp:
|
||||
return <-resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connectionServer) fetch() ([]byte, error) {
|
||||
addr := net.JoinHostPort(config.C.CloakHost, strconv.Itoa(config.C.CloakPort))
|
||||
conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true}) // nolint: gosec
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot connect to the masked host: %w", err)
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
if err = conn.Handshake(); err != nil {
|
||||
return nil, fmt.Errorf("cannot perform tls handshake: %w", err)
|
||||
}
|
||||
|
||||
certificates := conn.ConnectionState().PeerCertificates
|
||||
if len(certificates) == 0 {
|
||||
return nil, errors.New("no certificates is found")
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, v := range certificates {
|
||||
buf.Write(v.Raw)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *connectionServer) run(tickEvery time.Duration) {
|
||||
logger := zap.S().Named("tls-connection-server")
|
||||
|
||||
ticker := time.NewTicker(tickEvery)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case resp := <-c.channelGet:
|
||||
resp <- c.nextReadItem.Value.([]byte)
|
||||
close(resp)
|
||||
|
||||
c.nextReadItem = c.nextReadItem.Next()
|
||||
case <-ticker.C:
|
||||
cert, err := c.fetch()
|
||||
switch err {
|
||||
case nil:
|
||||
c.nextWriteItem.Value = cert
|
||||
c.nextWriteItem = c.nextWriteItem.Next()
|
||||
default:
|
||||
logger.Warnw("cannot fetch certificates", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,18 @@ package faketls
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/antireplay"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/obfuscated2"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"github.com/9seconds/mtg/stats"
|
||||
"github.com/9seconds/mtg/tlstypes"
|
||||
"github.com/9seconds/mtg/wrappers/stream"
|
||||
)
|
||||
|
||||
@@ -18,18 +27,77 @@ func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conn
|
||||
|
||||
for _, expected := range faketlsStartBytes {
|
||||
if actual, err := bufferedReader.ReadByte(); err != nil || actual != expected {
|
||||
return nil, c.simulateWebsite(rewinded)
|
||||
fmt.Println("!!!!!!!!!!!! ERROR !!!!!!!!!!!!", err)
|
||||
return nil, errors.New("qqq")
|
||||
}
|
||||
}
|
||||
|
||||
rewinded.Rewind()
|
||||
rewinded = stream.NewRewind(rewinded)
|
||||
|
||||
if err := c.tlsHandshake(rewinded); err != nil {
|
||||
return nil, c.simulateWebsite(rewinded)
|
||||
fmt.Println("!!!!!!!!!!!! ERROR !!!!!!!!!!!!", err)
|
||||
return nil, errors.New("qqq")
|
||||
}
|
||||
|
||||
conn, err := c.ClientProtocol.Handshake(socket)
|
||||
conn := stream.NewFakeTLS(socket)
|
||||
conn, err := c.ClientProtocol.Handshake(conn)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
func (c *ClientProtocol) tlsHandshake(conn io.ReadWriter) error {
|
||||
helloRecord, err := tlstypes.ReadRecord(conn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read initial record: %w", err)
|
||||
}
|
||||
|
||||
clientHello, err := tlstypes.ParseClientHello(helloRecord.Data.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse client hello: %w", err)
|
||||
}
|
||||
|
||||
digest := clientHello.Digest()
|
||||
for i := 0; i < len(digest)-4; i++ {
|
||||
if digest[i] != 0 {
|
||||
return errBadDigest
|
||||
}
|
||||
}
|
||||
|
||||
timestamp := int64(binary.LittleEndian.Uint32(digest[len(digest)-4:]))
|
||||
createdAt := time.Unix(timestamp, 0)
|
||||
timeDiff := time.Since(createdAt)
|
||||
|
||||
if (timeDiff > TimeSkew || timeDiff < -TimeSkew) && timestamp > TimeFromBoot {
|
||||
return errBadTime
|
||||
}
|
||||
|
||||
if antireplay.Cache.HasTLS(clientHello.Random[:]) {
|
||||
stats.Stats.AntiReplayDetected()
|
||||
return errors.New("antireplay detected")
|
||||
}
|
||||
|
||||
antireplay.Cache.AddTLS(clientHello.Random[:])
|
||||
|
||||
hostCert, err := connectionServerInstance.get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get host certificate: %w", err)
|
||||
}
|
||||
|
||||
serverHello := tlstypes.NewServerHello(clientHello)
|
||||
serverHelloPacket := serverHello.WelcomePacket(hostCert)
|
||||
|
||||
if _, err := conn.Write(serverHelloPacket); err != nil {
|
||||
return fmt.Errorf("cannot send welcome packet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeClientProtocol() protocol.ClientProtocol {
|
||||
return &ClientProtocol{}
|
||||
}
|
||||
|
||||
+23
-14
@@ -1,21 +1,30 @@
|
||||
package faketls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TLSHandshakeLength = 1 + 2 + 2 + 512
|
||||
TimeSkew = 5 * time.Second
|
||||
TimeFromBoot = 24 * 60 * 60
|
||||
)
|
||||
|
||||
var (
|
||||
faketlsStartBytes = [...]byte{
|
||||
0x16,
|
||||
0x03,
|
||||
0x01,
|
||||
0x02,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x01,
|
||||
0xfc,
|
||||
0x03,
|
||||
0x03,
|
||||
}
|
||||
errBadDigest = errors.New("bad digest")
|
||||
errBadTime = errors.New("bad time")
|
||||
|
||||
faketlsStartBytes = [...]byte{
|
||||
0x16,
|
||||
0x03,
|
||||
0x01,
|
||||
0x02,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x01,
|
||||
0xfc,
|
||||
0x03,
|
||||
0x03,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package faketls
|
||||
|
||||
import (
|
||||
"container/ring"
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
)
|
||||
|
||||
var (
|
||||
connectionServerInstance connectionServer
|
||||
connectionServerInitOnce sync.Once
|
||||
)
|
||||
|
||||
const (
|
||||
connectionServerKeepCertificates = 5
|
||||
connectionServerUpdateEvery = 10 * time.Minute
|
||||
)
|
||||
|
||||
func Init(ctx context.Context) {
|
||||
connectionServerInitOnce.Do(func() {
|
||||
if config.C.CloakHost == "" {
|
||||
return
|
||||
}
|
||||
|
||||
connectionServerInstance = connectionServer{
|
||||
channelGet: make(chan chan<- []byte),
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
cert, err := connectionServerInstance.fetch()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
r := ring.New(connectionServerKeepCertificates)
|
||||
|
||||
for i := 0; i < connectionServerKeepCertificates; i++ {
|
||||
r.Value = cert
|
||||
r = r.Next()
|
||||
}
|
||||
|
||||
connectionServerInstance.nextWriteItem = r
|
||||
connectionServerInstance.nextReadItem = r
|
||||
|
||||
go connectionServerInstance.run(connectionServerUpdateEvery)
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package faketls
|
||||
|
||||
import (
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
)
|
||||
|
||||
func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.StreamReadWriteCloser, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -17,6 +17,7 @@ require (
|
||||
go.uber.org/atomic v1.4.0 // indirect
|
||||
go.uber.org/multierr v1.2.0 // indirect
|
||||
go.uber.org/zap v1.10.0
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb // indirect
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
|
||||
@@ -109,6 +109,7 @@ go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
"github.com/9seconds/mtg/protocol"
|
||||
"github.com/9seconds/mtg/stats"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
"github.com/9seconds/mtg/wrappers/stream"
|
||||
)
|
||||
@@ -81,11 +82,12 @@ func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conn
|
||||
}
|
||||
|
||||
antiReplayKey := decryptedFrame.Unique()
|
||||
if antireplay.Cache.Has(antiReplayKey) {
|
||||
if antireplay.Cache.HasObfuscated2(antiReplayKey) {
|
||||
stats.Stats.AntiReplayDetected()
|
||||
return nil, errors.New("replay attack is detected")
|
||||
}
|
||||
|
||||
antireplay.Cache.Add(antiReplayKey)
|
||||
antireplay.Cache.AddObfuscated2(antiReplayKey)
|
||||
|
||||
return stream.NewObfuscated2(socket, encryptor, decryptor), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"container/ring"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
)
|
||||
|
||||
const (
|
||||
connectionServerKeepCertificates = 5
|
||||
connectionServerUpdateEvery = 10 * time.Minute
|
||||
)
|
||||
|
||||
type connectionServer struct {
|
||||
nextWriteItem *ring.Ring
|
||||
nextReadItem *ring.Ring
|
||||
|
||||
ctx context.Context
|
||||
channelGet chan chan<- *x509.Certificate
|
||||
}
|
||||
|
||||
func (c *connectionServer) fetch() (*x509.Certificate, error) {
|
||||
addr := net.JoinHostPort(config.C.CloakHost, strconv.Itoa(config.C.CloakPort))
|
||||
conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true}) // nolint: gosec
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot connect to the masked host: %w", err)
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
if err = conn.Handshake(); err != nil {
|
||||
return nil, fmt.Errorf("cannot perform tls handshake: %w", err)
|
||||
}
|
||||
|
||||
certificates := conn.ConnectionState().PeerCertificates
|
||||
if len(certificates) == 0 {
|
||||
return nil, errors.New("no certificates is found")
|
||||
}
|
||||
|
||||
return certificates[0], nil
|
||||
}
|
||||
|
||||
func (c *connectionServer) run() {
|
||||
logger := zap.S().Named("tls-connection-server")
|
||||
|
||||
ticker := time.NewTicker(connectionServerUpdateEvery)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case resp := <-c.channelGet:
|
||||
resp <- c.nextReadItem.Value.(*x509.Certificate)
|
||||
close(resp)
|
||||
|
||||
c.nextReadItem = c.nextReadItem.Next()
|
||||
case <-ticker.C:
|
||||
cert, err := c.fetch()
|
||||
switch err {
|
||||
case nil:
|
||||
c.nextWriteItem.Value = cert
|
||||
c.nextWriteItem = c.nextWriteItem.Next()
|
||||
default:
|
||||
logger.Warnw("cannot fetch certificates", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
"github.com/9seconds/mtg/utils"
|
||||
)
|
||||
|
||||
type ClientHello struct {
|
||||
Handshake
|
||||
}
|
||||
|
||||
func (c ClientHello) Digest() []byte {
|
||||
dirtyDigest := c.Random
|
||||
c.Random = [32]byte{}
|
||||
|
||||
rec := Record{
|
||||
Type: RecordTypeHandshake,
|
||||
Version: Version10,
|
||||
Data: &c,
|
||||
}
|
||||
|
||||
mac := hmac.New(sha256.New, config.C.Secret)
|
||||
mac.Write(rec.Bytes()) // nolint: errcheck
|
||||
computedDigest := mac.Sum(nil)
|
||||
|
||||
for i := range computedDigest {
|
||||
computedDigest[i] ^= dirtyDigest[i]
|
||||
}
|
||||
|
||||
return computedDigest
|
||||
}
|
||||
|
||||
func ParseClientHello(raw []byte) (*ClientHello, error) {
|
||||
rv := &ClientHello{}
|
||||
|
||||
rv.Type = HandshakeType(raw[0])
|
||||
if rv.Type != HandshakeTypeClient {
|
||||
return nil, fmt.Errorf("incorrect handshake type %v", rv.Type)
|
||||
}
|
||||
|
||||
raw = raw[1:]
|
||||
sizeUint24 := utils.Uint24{}
|
||||
copy(sizeUint24[:], utils.ReverseBytes(raw[:3]))
|
||||
size := int(utils.FromUint24(sizeUint24))
|
||||
|
||||
raw = raw[3:]
|
||||
if len(raw) != size {
|
||||
return nil, fmt.Errorf("payload size mismatch (%d != %d)", len(raw), size)
|
||||
}
|
||||
|
||||
versionRaw := raw[:2]
|
||||
|
||||
switch {
|
||||
case bytes.Equal(versionRaw, Version13Bytes):
|
||||
rv.Version = Version13
|
||||
case bytes.Equal(versionRaw, Version12Bytes):
|
||||
rv.Version = Version12
|
||||
case bytes.Equal(versionRaw, Version11Bytes):
|
||||
rv.Version = Version11
|
||||
case bytes.Equal(versionRaw, Version10Bytes):
|
||||
rv.Version = Version10
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown protocol version %v", versionRaw)
|
||||
}
|
||||
|
||||
raw = raw[2:]
|
||||
copy(rv.Random[:], raw[:32])
|
||||
raw = raw[32:]
|
||||
|
||||
sessionIDLength := int(raw[0])
|
||||
raw = raw[1:]
|
||||
rv.SessionID = make([]byte, sessionIDLength)
|
||||
copy(rv.SessionID, raw)
|
||||
raw = raw[sessionIDLength:]
|
||||
|
||||
tail := make([]byte, len(raw))
|
||||
copy(tail, raw)
|
||||
rv.Tail = RawBytes(tail)
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package tlstypes
|
||||
|
||||
type RecordType uint8
|
||||
|
||||
const (
|
||||
RecordTypeHandshake RecordType = 0x16
|
||||
RecordTypeApplicationData RecordType = 0x17
|
||||
RecordTypeChangeCipherSpec RecordType = 0x14
|
||||
)
|
||||
|
||||
type HandshakeType uint8
|
||||
|
||||
const (
|
||||
HandshakeTypeClient HandshakeType = 0x01
|
||||
HandshakeTypeServer HandshakeType = 0x02
|
||||
)
|
||||
|
||||
type CipherSuiteType uint8
|
||||
|
||||
const (
|
||||
CipherSuiteType_TLS_AES_128_GCM_SHA256 CipherSuiteType = iota // nolint: stylecheck, golint
|
||||
CipherSuiteType_TLS_AES_256_GCM_SHA384 // nolint: stylecheck, golint
|
||||
CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256 // nolint: stylecheck, golint
|
||||
)
|
||||
|
||||
func (c CipherSuiteType) Bytes() []byte {
|
||||
switch c {
|
||||
case CipherSuiteType_TLS_AES_128_GCM_SHA256:
|
||||
return CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes
|
||||
case CipherSuiteType_TLS_AES_256_GCM_SHA384:
|
||||
return CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes
|
||||
}
|
||||
|
||||
return CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes
|
||||
}
|
||||
|
||||
type Version uint8
|
||||
|
||||
func (v Version) Bytes() []byte {
|
||||
switch v {
|
||||
case Version13:
|
||||
return Version13Bytes
|
||||
case Version12:
|
||||
return Version12Bytes
|
||||
case Version11:
|
||||
return Version11Bytes
|
||||
}
|
||||
|
||||
return Version10Bytes
|
||||
}
|
||||
|
||||
const (
|
||||
VersionUnknown Version = iota
|
||||
Version10
|
||||
Version11
|
||||
Version12
|
||||
Version13
|
||||
)
|
||||
|
||||
var (
|
||||
Version10Bytes = []byte{0x03, 0x01}
|
||||
Version11Bytes = []byte{0x03, 0x02}
|
||||
Version12Bytes = []byte{0x03, 0x03}
|
||||
Version13Bytes = []byte{0x03, 0x04}
|
||||
|
||||
CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes = []byte{0x13, 0x01} // nolint: stylecheck, golint
|
||||
CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes = []byte{0x13, 0x02} // nolint: stylecheck, golint
|
||||
CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes = []byte{0x13, 0x03} // nolint; stylecheck, golint
|
||||
)
|
||||
|
||||
type Byter interface {
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
type RawBytes []byte
|
||||
|
||||
func (r RawBytes) Bytes() []byte {
|
||||
return []byte(r)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/9seconds/mtg/utils"
|
||||
)
|
||||
|
||||
type Handshake struct {
|
||||
Type HandshakeType
|
||||
Version Version
|
||||
Random [32]byte
|
||||
SessionID []byte
|
||||
Tail Byter
|
||||
}
|
||||
|
||||
func (h *Handshake) Bytes() []byte {
|
||||
buf := bytes.Buffer{}
|
||||
packetBuf := bytes.Buffer{}
|
||||
|
||||
buf.WriteByte(byte(h.Type))
|
||||
|
||||
packetBuf.Write(h.Version.Bytes())
|
||||
packetBuf.Write(h.Random[:])
|
||||
packetBuf.WriteByte(byte(len(h.SessionID)))
|
||||
packetBuf.Write(h.SessionID)
|
||||
packetBuf.Write(h.Tail.Bytes())
|
||||
|
||||
sizeUint24 := utils.ToUint24(uint32(packetBuf.Len()))
|
||||
sizeUint24Bytes := sizeUint24[:]
|
||||
sizeUint24Bytes[0], sizeUint24Bytes[2] = sizeUint24Bytes[2], sizeUint24Bytes[0]
|
||||
|
||||
buf.Write(sizeUint24Bytes)
|
||||
packetBuf.WriteTo(&buf) // nolint: errcheck
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const recordMaxChunkSize = 16384 + 24
|
||||
|
||||
type Record struct {
|
||||
Type RecordType
|
||||
Version Version
|
||||
Data Byter
|
||||
}
|
||||
|
||||
func (r Record) Bytes() []byte {
|
||||
buf := bytes.Buffer{}
|
||||
data := r.Data.Bytes()
|
||||
|
||||
buf.WriteByte(byte(r.Type))
|
||||
buf.Write(r.Version.Bytes())
|
||||
binary.Write(&buf, binary.BigEndian, uint16(len(data))) // nolint: errcheck
|
||||
buf.Write(data)
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func ReadRecord(reader io.Reader) (Record, error) {
|
||||
buf := [2]byte{}
|
||||
rec := Record{}
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:1]); err != nil {
|
||||
return rec, fmt.Errorf("cannot read record type: %w", err)
|
||||
}
|
||||
|
||||
rec.Type = RecordType(buf[0])
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:]); err != nil {
|
||||
return rec, fmt.Errorf("cannot read version: %w", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case bytes.Equal(buf[:], Version13Bytes):
|
||||
rec.Version = Version13
|
||||
case bytes.Equal(buf[:], Version12Bytes):
|
||||
rec.Version = Version12
|
||||
case bytes.Equal(buf[:], Version11Bytes):
|
||||
rec.Version = Version11
|
||||
case bytes.Equal(buf[:], Version10Bytes):
|
||||
rec.Version = Version10
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(reader, buf[:]); err != nil {
|
||||
return rec, fmt.Errorf("cannot read data length: %w", err)
|
||||
}
|
||||
|
||||
data := make([]byte, binary.BigEndian.Uint16(buf[:]))
|
||||
if _, err := io.ReadFull(reader, data); err != nil {
|
||||
return rec, fmt.Errorf("cannot read data: %w", err)
|
||||
}
|
||||
|
||||
rec.Data = RawBytes(data)
|
||||
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
func MakeRecords(raw []byte) (arr []Record) {
|
||||
for len(raw) > 0 {
|
||||
chunkSize := recordMaxChunkSize
|
||||
if chunkSize > len(raw) {
|
||||
chunkSize = len(raw)
|
||||
}
|
||||
|
||||
arr = append(arr, Record{
|
||||
Type: RecordTypeApplicationData,
|
||||
Version: Version12,
|
||||
Data: RawBytes(raw[:chunkSize]),
|
||||
})
|
||||
raw = raw[chunkSize:]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package tlstypes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
|
||||
"github.com/9seconds/mtg/config"
|
||||
)
|
||||
|
||||
type ServerHello struct {
|
||||
Handshake
|
||||
|
||||
clientHello *ClientHello
|
||||
}
|
||||
|
||||
func (s ServerHello) WelcomePacket(hostCert []byte) []byte {
|
||||
s.Random = [32]byte{}
|
||||
rec := Record{
|
||||
Type: RecordTypeHandshake,
|
||||
Version: Version12,
|
||||
Data: &s,
|
||||
}
|
||||
buf := bytes.NewBuffer(rec.Bytes())
|
||||
|
||||
recChangeCipher := Record{
|
||||
Type: RecordTypeChangeCipherSpec,
|
||||
Version: Version12,
|
||||
Data: RawBytes([]byte{0x01}),
|
||||
}
|
||||
buf.Write(recChangeCipher.Bytes())
|
||||
|
||||
recData := Record{
|
||||
Type: RecordTypeApplicationData,
|
||||
Version: Version12,
|
||||
Data: RawBytes(hostCert),
|
||||
}
|
||||
buf.Write(recData.Bytes())
|
||||
packet := buf.Bytes()
|
||||
|
||||
mac := hmac.New(sha256.New, config.C.Secret)
|
||||
mac.Write(s.clientHello.Random[:]) // nolint: errcheck
|
||||
mac.Write(packet) // nolint: errcheck
|
||||
copy(packet[11:], mac.Sum(nil))
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
func NewServerHello(clientHello *ClientHello) *ServerHello {
|
||||
rv := &ServerHello{
|
||||
clientHello: clientHello,
|
||||
}
|
||||
|
||||
rv.Type = HandshakeTypeServer
|
||||
rv.Version = Version12
|
||||
rv.SessionID = make([]byte, len(clientHello.SessionID))
|
||||
copy(rv.SessionID, clientHello.SessionID)
|
||||
|
||||
tail := bytes.NewBuffer(CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes)
|
||||
tail.WriteByte(0x00) // no compression
|
||||
makeTLSExtensions(tail)
|
||||
rv.Tail = RawBytes(tail.Bytes())
|
||||
|
||||
return rv
|
||||
}
|
||||
|
||||
func makeTLSExtensions(buf io.Writer) {
|
||||
buf.Write([]byte{ // nolint: errcheck
|
||||
0x00, 0x2e, // 46 bytes of data
|
||||
0x00, 0x33, // Extension - Key Share
|
||||
0x00, 0x24, // 36 bytes
|
||||
0x00, 0x1d, // x25519 curve
|
||||
0x00, 0x20, // 32 bytes of key
|
||||
})
|
||||
|
||||
var dst, in, base [32]byte
|
||||
|
||||
rand.Read(in[:]) // nolint: errcheck
|
||||
rand.Read(base[:]) // nolint: errcheck
|
||||
curve25519.ScalarMult(&dst, &in, &base)
|
||||
buf.Write(dst[:]) // nolint: errcheck
|
||||
|
||||
buf.Write([]byte{ // nolint: errcheck
|
||||
0x00, 0x2b, // Extension - Supported Versions
|
||||
0x00, 0x02, // 2 bytes are following
|
||||
0x03, 0x04, // TLS 1.3
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Uint24 [3]byte
|
||||
|
||||
func ToUint24(number uint32) Uint24 {
|
||||
@@ -9,3 +14,17 @@ func ToUint24(number uint32) Uint24 {
|
||||
func FromUint24(number Uint24) uint32 {
|
||||
return uint32(number[0]) + (uint32(number[1]) << 8) + (uint32(number[2]) << 16)
|
||||
}
|
||||
|
||||
func Hexify(data []byte) string {
|
||||
s := []string{}
|
||||
|
||||
for _, v := range data {
|
||||
if v < 0x10 {
|
||||
s = append(s, fmt.Sprintf("0x0%x", v))
|
||||
} else {
|
||||
s = append(s, fmt.Sprintf("0x%x", v))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(s, " ")
|
||||
}
|
||||
|
||||
+14
-66
@@ -1,28 +1,15 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/9seconds/mtg/conntypes"
|
||||
)
|
||||
|
||||
var (
|
||||
errFakeTLSTimeout = errors.New("timeout")
|
||||
fakeTLSWritePrefix = []byte{0x17, 0x03, 0x03}
|
||||
)
|
||||
|
||||
const (
|
||||
faketlsMaxChunkSize = 16384 + 24
|
||||
faketlsRecordTypeApplicationData = 0x17
|
||||
faketlsRecordTypeCCS = 0x14
|
||||
"github.com/9seconds/mtg/tlstypes"
|
||||
)
|
||||
|
||||
type wrapperFakeTLS struct {
|
||||
@@ -45,38 +32,20 @@ func (w *wrapperFakeTLS) WriteTimeout(p []byte, timeout time.Duration) (int, err
|
||||
if elapsed > timeout {
|
||||
return w.parent.WriteTimeout(b, timeout-elapsed)
|
||||
}
|
||||
return 0, errFakeTLSTimeout
|
||||
return 0, errors.New("timeout")
|
||||
})
|
||||
}
|
||||
|
||||
func (w *wrapperFakeTLS) write(p []byte, writeFunc func([]byte) (int, error)) (int, error) {
|
||||
sum := 0
|
||||
size := [2]byte{}
|
||||
|
||||
for len(p) > 0 {
|
||||
chunkSize := faketlsMaxChunkSize
|
||||
if chunkSize > len(p) {
|
||||
chunkSize = len(p)
|
||||
}
|
||||
|
||||
if _, err := writeFunc(fakeTLSWritePrefix); err != nil {
|
||||
return sum, err
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(size[:], uint16(chunkSize))
|
||||
|
||||
if _, err := writeFunc(size[:]); err != nil {
|
||||
return sum, err
|
||||
}
|
||||
|
||||
n, err := writeFunc(p[:chunkSize])
|
||||
sum += n
|
||||
|
||||
for _, v := range tlstypes.MakeRecords(p) {
|
||||
_, err := writeFunc(v.Bytes())
|
||||
if err != nil {
|
||||
return sum, err
|
||||
}
|
||||
|
||||
p = p[chunkSize:]
|
||||
sum += len(v.Data.Bytes())
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
@@ -108,41 +77,20 @@ func NewFakeTLS(socket conntypes.StreamReadWriteCloser) conntypes.StreamReadWrit
|
||||
}
|
||||
|
||||
faketls.readFunc = func() ([]byte, error) {
|
||||
data := &bytes.Buffer{}
|
||||
buf := [2]byte{}
|
||||
recordType := byte(faketlsRecordTypeCCS)
|
||||
|
||||
for recordType == faketlsRecordTypeCCS {
|
||||
if _, err := io.ReadFull(faketls.parent, buf[:1]); err != nil {
|
||||
return nil, fmt.Errorf("cannot read record type: %w", err)
|
||||
for {
|
||||
rec, err := tlstypes.ReadRecord(faketls.parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch buf[0] {
|
||||
case faketlsRecordTypeCCS, faketlsRecordTypeApplicationData:
|
||||
recordType = buf[0]
|
||||
switch rec.Type {
|
||||
case tlstypes.RecordTypeChangeCipherSpec:
|
||||
case tlstypes.RecordTypeApplicationData:
|
||||
return rec.Data.Bytes(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("incorrect record type %v", buf[0])
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(faketls.parent, buf[:]); err != nil {
|
||||
return nil, fmt.Errorf("cannot read version: %w", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf[:], []byte{0x03, 0x03}) {
|
||||
return nil, fmt.Errorf("unknown tls version %v", buf)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(faketls.parent, buf[:]); err != nil {
|
||||
return nil, fmt.Errorf("cannot read data length: %w", err)
|
||||
}
|
||||
|
||||
dataLength := binary.BigEndian.Uint16(buf[:])
|
||||
if _, err := io.CopyN(data, faketls.parent, int64(dataLength)); err != nil {
|
||||
return nil, fmt.Errorf("cannot copy frame data: %w", err)
|
||||
return nil, fmt.Errorf("unsupported record type %v", rec.Type)
|
||||
}
|
||||
}
|
||||
|
||||
return data.Bytes(), nil
|
||||
}
|
||||
|
||||
return faketls
|
||||
|
||||
@@ -43,7 +43,10 @@ func (w *wrapperRewind) Read(p []byte) (int, error) {
|
||||
}
|
||||
|
||||
n, err := w.parent.Read(p)
|
||||
w.buf.Write(p[:n])
|
||||
|
||||
if !w.rewinded {
|
||||
w.buf.Write(p[:n])
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
@@ -59,7 +62,10 @@ func (w *wrapperRewind) ReadTimeout(p []byte, timeout time.Duration) (int, error
|
||||
}
|
||||
|
||||
n, err := w.parent.ReadTimeout(p, timeout)
|
||||
w.buf.Write(p[:n])
|
||||
|
||||
if !w.rewinded {
|
||||
w.buf.Write(p[:n])
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user