Просмотр исходного кода

Success path for fake tls is implemented

tags/1.0^2
9seconds 6 лет назад
Родитель
Сommit
038b2b200d

+ 15
- 5
antireplay/cache.go Просмотреть файл

@@ -3,14 +3,24 @@ package antireplay
3 3
 import "github.com/allegro/bigcache"
4 4
 
5 5
 type cache struct {
6
-	cache *bigcache.BigCache
6
+	obfuscated2 *bigcache.BigCache
7
+	tls         *bigcache.BigCache
7 8
 }
8 9
 
9
-func (c *cache) Add(data []byte) {
10
-	c.cache.Set(string(data), nil) // nolint: errcheck
10
+func (c *cache) AddObfuscated2(data []byte) {
11
+	c.obfuscated2.Set(string(data), nil) // nolint: errcheck
11 12
 }
12 13
 
13
-func (c *cache) Has(data []byte) bool {
14
-	_, err := c.cache.Get(string(data))
14
+func (c *cache) AddTLS(data []byte) {
15
+	c.tls.Set(string(data), nil) // nolint: errcheck
16
+}
17
+
18
+func (c *cache) HasObfuscated2(data []byte) bool {
19
+	_, err := c.obfuscated2.Get(string(data))
20
+	return err == nil
21
+}
22
+
23
+func (c *cache) HasTLS(data []byte) bool {
24
+	_, err := c.tls.Get(string(data))
15 25
 	return err == nil
16 26
 }

+ 13
- 2
antireplay/init.go Просмотреть файл

@@ -14,7 +14,17 @@ var (
14 14
 
15 15
 func Init() {
16 16
 	initOnce.Do(func() {
17
-		c, err := bigcache.NewBigCache(bigcache.Config{
17
+		c1, err := bigcache.NewBigCache(bigcache.Config{
18
+			Shards:           1024,
19
+			LifeWindow:       config.C.AntiReplayEvictionTime,
20
+			Hasher:           hasher{},
21
+			HardMaxCacheSize: config.C.AntiReplayMaxSize,
22
+		})
23
+		if err != nil {
24
+			panic(err)
25
+		}
26
+
27
+		c2, err := bigcache.NewBigCache(bigcache.Config{
18 28
 			Shards:           1024,
19 29
 			LifeWindow:       config.C.AntiReplayEvictionTime,
20 30
 			Hasher:           hasher{},
@@ -25,7 +35,8 @@ func Init() {
25 35
 		}
26 36
 
27 37
 		Cache = &cache{
28
-			cache: c,
38
+			obfuscated2: c1,
39
+			tls:         c2,
29 40
 		}
30 41
 	})
31 42
 }

+ 5
- 6
cli/proxy.go Просмотреть файл

@@ -10,6 +10,7 @@ import (
10 10
 
11 11
 	"github.com/9seconds/mtg/antireplay"
12 12
 	"github.com/9seconds/mtg/config"
13
+	"github.com/9seconds/mtg/faketls"
13 14
 	"github.com/9seconds/mtg/hub"
14 15
 	"github.com/9seconds/mtg/ntp"
15 16
 	"github.com/9seconds/mtg/obfuscated2"
@@ -75,6 +76,7 @@ func Proxy() error { // nolint: funlen
75 76
 	antireplay.Init()
76 77
 	telegram.Init()
77 78
 	hub.Init(ctx)
79
+	faketls.Init(ctx)
78 80
 
79 81
 	proxyListener, err := net.Listen("tcp", config.C.Bind.String())
80 82
 	if err != nil {
@@ -91,12 +93,9 @@ func Proxy() error { // nolint: funlen
91 93
 		Context:             ctx,
92 94
 		ClientProtocolMaker: obfuscated2.MakeClientProtocol,
93 95
 	}
94
-	// if len(config.C.AdTag) == 0 {
95
-	// 	app.TelegramProtocolMaker = obfuscated2.MakeTelegramProtocol
96
-	// }
97
-	// if config.C.SecretMode != config.SecretModeTLS {
98
-	// 	app.ClientProtocolMaker = obfuscated2.MakeClientProtocol
99
-	// }
96
+	if config.C.SecretMode == config.SecretModeTLS {
97
+		app.ClientProtocolMaker = faketls.MakeClientProtocol
98
+	}
100 99
 
101 100
 	app.Serve(proxyListener)
102 101
 

+ 3
- 1
config/urls.go Просмотреть файл

@@ -28,11 +28,13 @@ func GetURLs() (urls IPURLs) {
28 28
 		secret = hex.EncodeToString(C.Secret)
29 29
 	case SecretModeSecured:
30 30
 		secret = "dd" + hex.EncodeToString(C.Secret)
31
+	case SecretModeTLS:
32
+		secret = "ee" + hex.EncodeToString(C.Secret) + hex.EncodeToString([]byte(C.CloakHost))
31 33
 	}
32 34
 
33 35
 	urls.IPv4 = makeURLs(C.PublicIPv4, secret)
34 36
 	urls.IPv6 = makeURLs(C.PublicIPv6, secret)
35
-	urls.BotSecret = secret
37
+	urls.BotSecret = hex.EncodeToString(C.Secret)
36 38
 
37 39
 	return urls
38 40
 }

+ 91
- 0
faketls/certificate_server.go Просмотреть файл

@@ -0,0 +1,91 @@
1
+package faketls
2
+
3
+import (
4
+	"bytes"
5
+	"container/ring"
6
+	"context"
7
+	"crypto/tls"
8
+	"errors"
9
+	"fmt"
10
+	"net"
11
+	"strconv"
12
+	"time"
13
+
14
+	"go.uber.org/zap"
15
+
16
+	"github.com/9seconds/mtg/config"
17
+)
18
+
19
+type connectionServer struct {
20
+	nextWriteItem *ring.Ring
21
+	nextReadItem  *ring.Ring
22
+
23
+	ctx        context.Context
24
+	channelGet chan chan<- []byte
25
+}
26
+
27
+func (c *connectionServer) get() ([]byte, error) {
28
+	resp := make(chan []byte)
29
+	select {
30
+	case <-c.ctx.Done():
31
+		return nil, errors.New("context closed")
32
+	case c.channelGet <- resp:
33
+		return <-resp, nil
34
+	}
35
+}
36
+
37
+func (c *connectionServer) fetch() ([]byte, error) {
38
+	addr := net.JoinHostPort(config.C.CloakHost, strconv.Itoa(config.C.CloakPort))
39
+	conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true}) // nolint: gosec
40
+
41
+	if err != nil {
42
+		return nil, fmt.Errorf("cannot connect to the masked host: %w", err)
43
+	}
44
+
45
+	defer conn.Close()
46
+
47
+	if err = conn.Handshake(); err != nil {
48
+		return nil, fmt.Errorf("cannot perform tls handshake: %w", err)
49
+	}
50
+
51
+	certificates := conn.ConnectionState().PeerCertificates
52
+	if len(certificates) == 0 {
53
+		return nil, errors.New("no certificates is found")
54
+	}
55
+
56
+	var buf bytes.Buffer
57
+
58
+	for _, v := range certificates {
59
+		buf.Write(v.Raw)
60
+	}
61
+
62
+	return buf.Bytes(), nil
63
+}
64
+
65
+func (c *connectionServer) run(tickEvery time.Duration) {
66
+	logger := zap.S().Named("tls-connection-server")
67
+
68
+	ticker := time.NewTicker(tickEvery)
69
+	defer ticker.Stop()
70
+
71
+	for {
72
+		select {
73
+		case <-c.ctx.Done():
74
+			return
75
+		case resp := <-c.channelGet:
76
+			resp <- c.nextReadItem.Value.([]byte)
77
+			close(resp)
78
+
79
+			c.nextReadItem = c.nextReadItem.Next()
80
+		case <-ticker.C:
81
+			cert, err := c.fetch()
82
+			switch err {
83
+			case nil:
84
+				c.nextWriteItem.Value = cert
85
+				c.nextWriteItem = c.nextWriteItem.Next()
86
+			default:
87
+				logger.Warnw("cannot fetch certificates", "error", err)
88
+			}
89
+		}
90
+	}
91
+}

+ 71
- 3
faketls/client_protocol.go Просмотреть файл

@@ -2,9 +2,18 @@ package faketls
2 2
 
3 3
 import (
4 4
 	"bufio"
5
+	"encoding/binary"
6
+	"errors"
7
+	"fmt"
8
+	"io"
9
+	"time"
5 10
 
11
+	"github.com/9seconds/mtg/antireplay"
6 12
 	"github.com/9seconds/mtg/conntypes"
7 13
 	"github.com/9seconds/mtg/obfuscated2"
14
+	"github.com/9seconds/mtg/protocol"
15
+	"github.com/9seconds/mtg/stats"
16
+	"github.com/9seconds/mtg/tlstypes"
8 17
 	"github.com/9seconds/mtg/wrappers/stream"
9 18
 )
10 19
 
@@ -18,18 +27,77 @@ func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conn
18 27
 
19 28
 	for _, expected := range faketlsStartBytes {
20 29
 		if actual, err := bufferedReader.ReadByte(); err != nil || actual != expected {
21
-			return nil, c.simulateWebsite(rewinded)
30
+			fmt.Println("!!!!!!!!!!!! ERROR !!!!!!!!!!!!", err)
31
+			return nil, errors.New("qqq")
22 32
 		}
23 33
 	}
24 34
 
35
+	rewinded.Rewind()
36
+	rewinded = stream.NewRewind(rewinded)
37
+
25 38
 	if err := c.tlsHandshake(rewinded); err != nil {
26
-		return nil, c.simulateWebsite(rewinded)
39
+		fmt.Println("!!!!!!!!!!!! ERROR !!!!!!!!!!!!", err)
40
+		return nil, errors.New("qqq")
27 41
 	}
28 42
 
29
-	conn, err := c.ClientProtocol.Handshake(socket)
43
+	conn := stream.NewFakeTLS(socket)
44
+	conn, err := c.ClientProtocol.Handshake(conn)
45
+
30 46
 	if err != nil {
31 47
 		return nil, err
32 48
 	}
33 49
 
34 50
 	return conn, err
35 51
 }
52
+
53
+func (c *ClientProtocol) tlsHandshake(conn io.ReadWriter) error {
54
+	helloRecord, err := tlstypes.ReadRecord(conn)
55
+	if err != nil {
56
+		return fmt.Errorf("cannot read initial record: %w", err)
57
+	}
58
+
59
+	clientHello, err := tlstypes.ParseClientHello(helloRecord.Data.Bytes())
60
+	if err != nil {
61
+		return fmt.Errorf("cannot parse client hello: %w", err)
62
+	}
63
+
64
+	digest := clientHello.Digest()
65
+	for i := 0; i < len(digest)-4; i++ {
66
+		if digest[i] != 0 {
67
+			return errBadDigest
68
+		}
69
+	}
70
+
71
+	timestamp := int64(binary.LittleEndian.Uint32(digest[len(digest)-4:]))
72
+	createdAt := time.Unix(timestamp, 0)
73
+	timeDiff := time.Since(createdAt)
74
+
75
+	if (timeDiff > TimeSkew || timeDiff < -TimeSkew) && timestamp > TimeFromBoot {
76
+		return errBadTime
77
+	}
78
+
79
+	if antireplay.Cache.HasTLS(clientHello.Random[:]) {
80
+		stats.Stats.AntiReplayDetected()
81
+		return errors.New("antireplay detected")
82
+	}
83
+
84
+	antireplay.Cache.AddTLS(clientHello.Random[:])
85
+
86
+	hostCert, err := connectionServerInstance.get()
87
+	if err != nil {
88
+		return fmt.Errorf("cannot get host certificate: %w", err)
89
+	}
90
+
91
+	serverHello := tlstypes.NewServerHello(clientHello)
92
+	serverHelloPacket := serverHello.WelcomePacket(hostCert)
93
+
94
+	if _, err := conn.Write(serverHelloPacket); err != nil {
95
+		return fmt.Errorf("cannot send welcome packet: %w", err)
96
+	}
97
+
98
+	return nil
99
+}
100
+
101
+func MakeClientProtocol() protocol.ClientProtocol {
102
+	return &ClientProtocol{}
103
+}

+ 23
- 14
faketls/consts.go Просмотреть файл

@@ -1,21 +1,30 @@
1 1
 package faketls
2 2
 
3
+import (
4
+	"errors"
5
+	"time"
6
+)
7
+
3 8
 const (
4
-	TLSHandshakeLength = 1 + 2 + 2 + 512
9
+	TimeSkew     = 5 * time.Second
10
+	TimeFromBoot = 24 * 60 * 60
5 11
 )
6 12
 
7 13
 var (
8
-faketlsStartBytes = [...]byte{
9
-	0x16,
10
-	0x03,
11
-	0x01,
12
-	0x02,
13
-	0x00,
14
-	0x01,
15
-	0x00,
16
-	0x01,
17
-	0xfc,
18
-	0x03,
19
-	0x03,
20
-}
14
+	errBadDigest = errors.New("bad digest")
15
+	errBadTime   = errors.New("bad time")
16
+
17
+	faketlsStartBytes = [...]byte{
18
+		0x16,
19
+		0x03,
20
+		0x01,
21
+		0x02,
22
+		0x00,
23
+		0x01,
24
+		0x00,
25
+		0x01,
26
+		0xfc,
27
+		0x03,
28
+		0x03,
29
+	}
21 30
 )

+ 50
- 0
faketls/init.go Просмотреть файл

@@ -0,0 +1,50 @@
1
+package faketls
2
+
3
+import (
4
+	"container/ring"
5
+	"context"
6
+	"sync"
7
+	"time"
8
+
9
+	"github.com/9seconds/mtg/config"
10
+)
11
+
12
+var (
13
+	connectionServerInstance connectionServer
14
+	connectionServerInitOnce sync.Once
15
+)
16
+
17
+const (
18
+	connectionServerKeepCertificates = 5
19
+	connectionServerUpdateEvery      = 10 * time.Minute
20
+)
21
+
22
+func Init(ctx context.Context) {
23
+	connectionServerInitOnce.Do(func() {
24
+		if config.C.CloakHost == "" {
25
+			return
26
+		}
27
+
28
+		connectionServerInstance = connectionServer{
29
+			channelGet: make(chan chan<- []byte),
30
+			ctx:        ctx,
31
+		}
32
+
33
+		cert, err := connectionServerInstance.fetch()
34
+		if err != nil {
35
+			panic(err)
36
+		}
37
+
38
+		r := ring.New(connectionServerKeepCertificates)
39
+
40
+		for i := 0; i < connectionServerKeepCertificates; i++ {
41
+			r.Value = cert
42
+			r = r.Next()
43
+		}
44
+
45
+		connectionServerInstance.nextWriteItem = r
46
+		connectionServerInstance.nextReadItem = r
47
+
48
+		go connectionServerInstance.run(connectionServerUpdateEvery)
49
+	})
50
+}

+ 0
- 10
faketls/telegram_protocol.go Просмотреть файл

@@ -1,10 +0,0 @@
1
-package faketls
2
-
3
-import (
4
-	"github.com/9seconds/mtg/conntypes"
5
-	"github.com/9seconds/mtg/protocol"
6
-)
7
-
8
-func TelegramProtocol(req *protocol.TelegramRequest) (conntypes.StreamReadWriteCloser, error) {
9
-	return nil, nil
10
-}

+ 1
- 0
go.mod Просмотреть файл

@@ -17,6 +17,7 @@ require (
17 17
 	go.uber.org/atomic v1.4.0 // indirect
18 18
 	go.uber.org/multierr v1.2.0 // indirect
19 19
 	go.uber.org/zap v1.10.0
20
+	golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
20 21
 	golang.org/x/net v0.0.0-20191009170851-d66e71096ffb // indirect
21 22
 	golang.org/x/sys v0.0.0-20191010194322-b09406accb47
22 23
 	gopkg.in/alecthomas/kingpin.v2 v2.2.6

+ 1
- 0
go.sum Просмотреть файл

@@ -109,6 +109,7 @@ go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
109 109
 go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
110 110
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
111 111
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
112
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
112 113
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
113 114
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
114 115
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=

+ 4
- 2
obfuscated2/client_protocol.go Просмотреть файл

@@ -13,6 +13,7 @@ import (
13 13
 	"github.com/9seconds/mtg/config"
14 14
 	"github.com/9seconds/mtg/conntypes"
15 15
 	"github.com/9seconds/mtg/protocol"
16
+	"github.com/9seconds/mtg/stats"
16 17
 	"github.com/9seconds/mtg/utils"
17 18
 	"github.com/9seconds/mtg/wrappers/stream"
18 19
 )
@@ -81,11 +82,12 @@ func (c *ClientProtocol) Handshake(socket conntypes.StreamReadWriteCloser) (conn
81 82
 	}
82 83
 
83 84
 	antiReplayKey := decryptedFrame.Unique()
84
-	if antireplay.Cache.Has(antiReplayKey) {
85
+	if antireplay.Cache.HasObfuscated2(antiReplayKey) {
86
+		stats.Stats.AntiReplayDetected()
85 87
 		return nil, errors.New("replay attack is detected")
86 88
 	}
87 89
 
88
-	antireplay.Cache.Add(antiReplayKey)
90
+	antireplay.Cache.AddObfuscated2(antiReplayKey)
89 91
 
90 92
 	return stream.NewObfuscated2(socket, encryptor, decryptor), nil
91 93
 }

+ 80
- 0
tlstypes/certificate_server.go Просмотреть файл

@@ -0,0 +1,80 @@
1
+package tlstypes
2
+
3
+import (
4
+	"container/ring"
5
+	"context"
6
+	"crypto/tls"
7
+	"crypto/x509"
8
+	"errors"
9
+	"fmt"
10
+	"net"
11
+	"strconv"
12
+	"time"
13
+
14
+	"go.uber.org/zap"
15
+
16
+	"github.com/9seconds/mtg/config"
17
+)
18
+
19
+const (
20
+	connectionServerKeepCertificates = 5
21
+	connectionServerUpdateEvery      = 10 * time.Minute
22
+)
23
+
24
+type connectionServer struct {
25
+	nextWriteItem *ring.Ring
26
+	nextReadItem  *ring.Ring
27
+
28
+	ctx        context.Context
29
+	channelGet chan chan<- *x509.Certificate
30
+}
31
+
32
+func (c *connectionServer) fetch() (*x509.Certificate, error) {
33
+	addr := net.JoinHostPort(config.C.CloakHost, strconv.Itoa(config.C.CloakPort))
34
+	conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true}) // nolint: gosec
35
+
36
+	if err != nil {
37
+		return nil, fmt.Errorf("cannot connect to the masked host: %w", err)
38
+	}
39
+
40
+	defer conn.Close()
41
+
42
+	if err = conn.Handshake(); err != nil {
43
+		return nil, fmt.Errorf("cannot perform tls handshake: %w", err)
44
+	}
45
+
46
+	certificates := conn.ConnectionState().PeerCertificates
47
+	if len(certificates) == 0 {
48
+		return nil, errors.New("no certificates is found")
49
+	}
50
+
51
+	return certificates[0], nil
52
+}
53
+
54
+func (c *connectionServer) run() {
55
+	logger := zap.S().Named("tls-connection-server")
56
+
57
+	ticker := time.NewTicker(connectionServerUpdateEvery)
58
+	defer ticker.Stop()
59
+
60
+	for {
61
+		select {
62
+		case <-c.ctx.Done():
63
+			return
64
+		case resp := <-c.channelGet:
65
+			resp <- c.nextReadItem.Value.(*x509.Certificate)
66
+			close(resp)
67
+
68
+			c.nextReadItem = c.nextReadItem.Next()
69
+		case <-ticker.C:
70
+			cert, err := c.fetch()
71
+			switch err {
72
+			case nil:
73
+				c.nextWriteItem.Value = cert
74
+				c.nextWriteItem = c.nextWriteItem.Next()
75
+			default:
76
+				logger.Warnw("cannot fetch certificates", "error", err)
77
+			}
78
+		}
79
+	}
80
+}

+ 86
- 0
tlstypes/client_hello.go Просмотреть файл

@@ -0,0 +1,86 @@
1
+package tlstypes
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/hmac"
6
+	"crypto/sha256"
7
+	"fmt"
8
+
9
+	"github.com/9seconds/mtg/config"
10
+	"github.com/9seconds/mtg/utils"
11
+)
12
+
13
+type ClientHello struct {
14
+	Handshake
15
+}
16
+
17
+func (c ClientHello) Digest() []byte {
18
+	dirtyDigest := c.Random
19
+	c.Random = [32]byte{}
20
+
21
+	rec := Record{
22
+		Type:    RecordTypeHandshake,
23
+		Version: Version10,
24
+		Data:    &c,
25
+	}
26
+
27
+	mac := hmac.New(sha256.New, config.C.Secret)
28
+	mac.Write(rec.Bytes()) // nolint: errcheck
29
+	computedDigest := mac.Sum(nil)
30
+
31
+	for i := range computedDigest {
32
+		computedDigest[i] ^= dirtyDigest[i]
33
+	}
34
+
35
+	return computedDigest
36
+}
37
+
38
+func ParseClientHello(raw []byte) (*ClientHello, error) {
39
+	rv := &ClientHello{}
40
+
41
+	rv.Type = HandshakeType(raw[0])
42
+	if rv.Type != HandshakeTypeClient {
43
+		return nil, fmt.Errorf("incorrect handshake type %v", rv.Type)
44
+	}
45
+
46
+	raw = raw[1:]
47
+	sizeUint24 := utils.Uint24{}
48
+	copy(sizeUint24[:], utils.ReverseBytes(raw[:3]))
49
+	size := int(utils.FromUint24(sizeUint24))
50
+
51
+	raw = raw[3:]
52
+	if len(raw) != size {
53
+		return nil, fmt.Errorf("payload size mismatch (%d != %d)", len(raw), size)
54
+	}
55
+
56
+	versionRaw := raw[:2]
57
+
58
+	switch {
59
+	case bytes.Equal(versionRaw, Version13Bytes):
60
+		rv.Version = Version13
61
+	case bytes.Equal(versionRaw, Version12Bytes):
62
+		rv.Version = Version12
63
+	case bytes.Equal(versionRaw, Version11Bytes):
64
+		rv.Version = Version11
65
+	case bytes.Equal(versionRaw, Version10Bytes):
66
+		rv.Version = Version10
67
+	default:
68
+		return nil, fmt.Errorf("unknown protocol version %v", versionRaw)
69
+	}
70
+
71
+	raw = raw[2:]
72
+	copy(rv.Random[:], raw[:32])
73
+	raw = raw[32:]
74
+
75
+	sessionIDLength := int(raw[0])
76
+	raw = raw[1:]
77
+	rv.SessionID = make([]byte, sessionIDLength)
78
+	copy(rv.SessionID, raw)
79
+	raw = raw[sessionIDLength:]
80
+
81
+	tail := make([]byte, len(raw))
82
+	copy(tail, raw)
83
+	rv.Tail = RawBytes(tail)
84
+
85
+	return rv, nil
86
+}

+ 79
- 0
tlstypes/consts.go Просмотреть файл

@@ -0,0 +1,79 @@
1
+package tlstypes
2
+
3
+type RecordType uint8
4
+
5
+const (
6
+	RecordTypeHandshake        RecordType = 0x16
7
+	RecordTypeApplicationData  RecordType = 0x17
8
+	RecordTypeChangeCipherSpec RecordType = 0x14
9
+)
10
+
11
+type HandshakeType uint8
12
+
13
+const (
14
+	HandshakeTypeClient HandshakeType = 0x01
15
+	HandshakeTypeServer HandshakeType = 0x02
16
+)
17
+
18
+type CipherSuiteType uint8
19
+
20
+const (
21
+	CipherSuiteType_TLS_AES_128_GCM_SHA256       CipherSuiteType = iota // nolint: stylecheck, golint
22
+	CipherSuiteType_TLS_AES_256_GCM_SHA384                              // nolint: stylecheck, golint
23
+	CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256                        // nolint: stylecheck, golint
24
+)
25
+
26
+func (c CipherSuiteType) Bytes() []byte {
27
+	switch c {
28
+	case CipherSuiteType_TLS_AES_128_GCM_SHA256:
29
+		return CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes
30
+	case CipherSuiteType_TLS_AES_256_GCM_SHA384:
31
+		return CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes
32
+	}
33
+
34
+	return CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes
35
+}
36
+
37
+type Version uint8
38
+
39
+func (v Version) Bytes() []byte {
40
+	switch v {
41
+	case Version13:
42
+		return Version13Bytes
43
+	case Version12:
44
+		return Version12Bytes
45
+	case Version11:
46
+		return Version11Bytes
47
+	}
48
+
49
+	return Version10Bytes
50
+}
51
+
52
+const (
53
+	VersionUnknown Version = iota
54
+	Version10
55
+	Version11
56
+	Version12
57
+	Version13
58
+)
59
+
60
+var (
61
+	Version10Bytes = []byte{0x03, 0x01}
62
+	Version11Bytes = []byte{0x03, 0x02}
63
+	Version12Bytes = []byte{0x03, 0x03}
64
+	Version13Bytes = []byte{0x03, 0x04}
65
+
66
+	CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes       = []byte{0x13, 0x01} // nolint: stylecheck, golint
67
+	CipherSuiteType_TLS_AES_256_GCM_SHA384_Bytes       = []byte{0x13, 0x02} // nolint: stylecheck, golint
68
+	CipherSuiteType_TLS_CHACHA20_POLY1305_SHA256_Bytes = []byte{0x13, 0x03} // nolint; stylecheck, golint
69
+)
70
+
71
+type Byter interface {
72
+	Bytes() []byte
73
+}
74
+
75
+type RawBytes []byte
76
+
77
+func (r RawBytes) Bytes() []byte {
78
+	return []byte(r)
79
+}

+ 37
- 0
tlstypes/handshake.go Просмотреть файл

@@ -0,0 +1,37 @@
1
+package tlstypes
2
+
3
+import (
4
+	"bytes"
5
+
6
+	"github.com/9seconds/mtg/utils"
7
+)
8
+
9
+type Handshake struct {
10
+	Type      HandshakeType
11
+	Version   Version
12
+	Random    [32]byte
13
+	SessionID []byte
14
+	Tail      Byter
15
+}
16
+
17
+func (h *Handshake) Bytes() []byte {
18
+	buf := bytes.Buffer{}
19
+	packetBuf := bytes.Buffer{}
20
+
21
+	buf.WriteByte(byte(h.Type))
22
+
23
+	packetBuf.Write(h.Version.Bytes())
24
+	packetBuf.Write(h.Random[:])
25
+	packetBuf.WriteByte(byte(len(h.SessionID)))
26
+	packetBuf.Write(h.SessionID)
27
+	packetBuf.Write(h.Tail.Bytes())
28
+
29
+	sizeUint24 := utils.ToUint24(uint32(packetBuf.Len()))
30
+	sizeUint24Bytes := sizeUint24[:]
31
+	sizeUint24Bytes[0], sizeUint24Bytes[2] = sizeUint24Bytes[2], sizeUint24Bytes[0]
32
+
33
+	buf.Write(sizeUint24Bytes)
34
+	packetBuf.WriteTo(&buf) // nolint: errcheck
35
+
36
+	return buf.Bytes()
37
+}

+ 85
- 0
tlstypes/record.go Просмотреть файл

@@ -0,0 +1,85 @@
1
+package tlstypes
2
+
3
+import (
4
+	"bytes"
5
+	"encoding/binary"
6
+	"fmt"
7
+	"io"
8
+)
9
+
10
+const recordMaxChunkSize = 16384 + 24
11
+
12
+type Record struct {
13
+	Type    RecordType
14
+	Version Version
15
+	Data    Byter
16
+}
17
+
18
+func (r Record) Bytes() []byte {
19
+	buf := bytes.Buffer{}
20
+	data := r.Data.Bytes()
21
+
22
+	buf.WriteByte(byte(r.Type))
23
+	buf.Write(r.Version.Bytes())
24
+	binary.Write(&buf, binary.BigEndian, uint16(len(data))) // nolint: errcheck
25
+	buf.Write(data)
26
+
27
+	return buf.Bytes()
28
+}
29
+
30
+func ReadRecord(reader io.Reader) (Record, error) {
31
+	buf := [2]byte{}
32
+	rec := Record{}
33
+
34
+	if _, err := io.ReadFull(reader, buf[:1]); err != nil {
35
+		return rec, fmt.Errorf("cannot read record type: %w", err)
36
+	}
37
+
38
+	rec.Type = RecordType(buf[0])
39
+
40
+	if _, err := io.ReadFull(reader, buf[:]); err != nil {
41
+		return rec, fmt.Errorf("cannot read version: %w", err)
42
+	}
43
+
44
+	switch {
45
+	case bytes.Equal(buf[:], Version13Bytes):
46
+		rec.Version = Version13
47
+	case bytes.Equal(buf[:], Version12Bytes):
48
+		rec.Version = Version12
49
+	case bytes.Equal(buf[:], Version11Bytes):
50
+		rec.Version = Version11
51
+	case bytes.Equal(buf[:], Version10Bytes):
52
+		rec.Version = Version10
53
+	}
54
+
55
+	if _, err := io.ReadFull(reader, buf[:]); err != nil {
56
+		return rec, fmt.Errorf("cannot read data length: %w", err)
57
+	}
58
+
59
+	data := make([]byte, binary.BigEndian.Uint16(buf[:]))
60
+	if _, err := io.ReadFull(reader, data); err != nil {
61
+		return rec, fmt.Errorf("cannot read data: %w", err)
62
+	}
63
+
64
+	rec.Data = RawBytes(data)
65
+
66
+	return rec, nil
67
+}
68
+
69
+func MakeRecords(raw []byte) (arr []Record) {
70
+	for len(raw) > 0 {
71
+		chunkSize := recordMaxChunkSize
72
+		if chunkSize > len(raw) {
73
+			chunkSize = len(raw)
74
+		}
75
+
76
+		arr = append(arr, Record{
77
+			Type:    RecordTypeApplicationData,
78
+			Version: Version12,
79
+			Data:    RawBytes(raw[:chunkSize]),
80
+		})
81
+		raw = raw[chunkSize:]
82
+	}
83
+
84
+	return
85
+}

+ 92
- 0
tlstypes/server_hello.go Просмотреть файл

@@ -0,0 +1,92 @@
1
+package tlstypes
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/hmac"
6
+	"crypto/rand"
7
+	"crypto/sha256"
8
+	"io"
9
+
10
+	"golang.org/x/crypto/curve25519"
11
+
12
+	"github.com/9seconds/mtg/config"
13
+)
14
+
15
+type ServerHello struct {
16
+	Handshake
17
+
18
+	clientHello *ClientHello
19
+}
20
+
21
+func (s ServerHello) WelcomePacket(hostCert []byte) []byte {
22
+	s.Random = [32]byte{}
23
+	rec := Record{
24
+		Type:    RecordTypeHandshake,
25
+		Version: Version12,
26
+		Data:    &s,
27
+	}
28
+	buf := bytes.NewBuffer(rec.Bytes())
29
+
30
+	recChangeCipher := Record{
31
+		Type:    RecordTypeChangeCipherSpec,
32
+		Version: Version12,
33
+		Data:    RawBytes([]byte{0x01}),
34
+	}
35
+	buf.Write(recChangeCipher.Bytes())
36
+
37
+	recData := Record{
38
+		Type:    RecordTypeApplicationData,
39
+		Version: Version12,
40
+		Data:    RawBytes(hostCert),
41
+	}
42
+	buf.Write(recData.Bytes())
43
+	packet := buf.Bytes()
44
+
45
+	mac := hmac.New(sha256.New, config.C.Secret)
46
+	mac.Write(s.clientHello.Random[:]) // nolint: errcheck
47
+	mac.Write(packet)                  // nolint: errcheck
48
+	copy(packet[11:], mac.Sum(nil))
49
+
50
+	return packet
51
+}
52
+
53
+func NewServerHello(clientHello *ClientHello) *ServerHello {
54
+	rv := &ServerHello{
55
+		clientHello: clientHello,
56
+	}
57
+
58
+	rv.Type = HandshakeTypeServer
59
+	rv.Version = Version12
60
+	rv.SessionID = make([]byte, len(clientHello.SessionID))
61
+	copy(rv.SessionID, clientHello.SessionID)
62
+
63
+	tail := bytes.NewBuffer(CipherSuiteType_TLS_AES_128_GCM_SHA256_Bytes)
64
+	tail.WriteByte(0x00) // no compression
65
+	makeTLSExtensions(tail)
66
+	rv.Tail = RawBytes(tail.Bytes())
67
+
68
+	return rv
69
+}
70
+
71
+func makeTLSExtensions(buf io.Writer) {
72
+	buf.Write([]byte{ // nolint: errcheck
73
+		0x00, 0x2e, // 46 bytes of data
74
+		0x00, 0x33, // Extension - Key Share
75
+		0x00, 0x24, // 36 bytes
76
+		0x00, 0x1d, // x25519 curve
77
+		0x00, 0x20, // 32 bytes of key
78
+	})
79
+
80
+	var dst, in, base [32]byte
81
+
82
+	rand.Read(in[:])   // nolint: errcheck
83
+	rand.Read(base[:]) // nolint: errcheck
84
+	curve25519.ScalarMult(&dst, &in, &base)
85
+	buf.Write(dst[:]) // nolint: errcheck
86
+
87
+	buf.Write([]byte{ // nolint: errcheck
88
+		0x00, 0x2b, // Extension - Supported Versions
89
+		0x00, 0x02, // 2 bytes are following
90
+		0x03, 0x04, // TLS 1.3
91
+	})
92
+}

+ 19
- 0
utils/uint24.go Просмотреть файл

@@ -1,5 +1,10 @@
1 1
 package utils
2 2
 
3
+import (
4
+	"fmt"
5
+	"strings"
6
+)
7
+
3 8
 type Uint24 [3]byte
4 9
 
5 10
 func ToUint24(number uint32) Uint24 {
@@ -9,3 +14,17 @@ func ToUint24(number uint32) Uint24 {
9 14
 func FromUint24(number Uint24) uint32 {
10 15
 	return uint32(number[0]) + (uint32(number[1]) << 8) + (uint32(number[2]) << 16)
11 16
 }
17
+
18
+func Hexify(data []byte) string {
19
+	s := []string{}
20
+
21
+	for _, v := range data {
22
+		if v < 0x10 {
23
+			s = append(s, fmt.Sprintf("0x0%x", v))
24
+		} else {
25
+			s = append(s, fmt.Sprintf("0x%x", v))
26
+		}
27
+	}
28
+
29
+	return strings.Join(s, " ")
30
+}

+ 14
- 66
wrappers/stream/faketls.go Просмотреть файл

@@ -1,28 +1,15 @@
1 1
 package stream
2 2
 
3 3
 import (
4
-	"bytes"
5
-	"encoding/binary"
6 4
 	"errors"
7 5
 	"fmt"
8
-	"io"
9 6
 	"net"
10 7
 	"time"
11 8
 
12 9
 	"go.uber.org/zap"
13 10
 
14 11
 	"github.com/9seconds/mtg/conntypes"
15
-)
16
-
17
-var (
18
-	errFakeTLSTimeout  = errors.New("timeout")
19
-	fakeTLSWritePrefix = []byte{0x17, 0x03, 0x03}
20
-)
21
-
22
-const (
23
-	faketlsMaxChunkSize              = 16384 + 24
24
-	faketlsRecordTypeApplicationData = 0x17
25
-	faketlsRecordTypeCCS             = 0x14
12
+	"github.com/9seconds/mtg/tlstypes"
26 13
 )
27 14
 
28 15
 type wrapperFakeTLS struct {
@@ -45,38 +32,20 @@ func (w *wrapperFakeTLS) WriteTimeout(p []byte, timeout time.Duration) (int, err
45 32
 		if elapsed > timeout {
46 33
 			return w.parent.WriteTimeout(b, timeout-elapsed)
47 34
 		}
48
-		return 0, errFakeTLSTimeout
35
+		return 0, errors.New("timeout")
49 36
 	})
50 37
 }
51 38
 
52 39
 func (w *wrapperFakeTLS) write(p []byte, writeFunc func([]byte) (int, error)) (int, error) {
53 40
 	sum := 0
54
-	size := [2]byte{}
55
-
56
-	for len(p) > 0 {
57
-		chunkSize := faketlsMaxChunkSize
58
-		if chunkSize > len(p) {
59
-			chunkSize = len(p)
60
-		}
61
-
62
-		if _, err := writeFunc(fakeTLSWritePrefix); err != nil {
63
-			return sum, err
64
-		}
65
-
66
-		binary.BigEndian.PutUint16(size[:], uint16(chunkSize))
67
-
68
-		if _, err := writeFunc(size[:]); err != nil {
69
-			return sum, err
70
-		}
71
-
72
-		n, err := writeFunc(p[:chunkSize])
73
-		sum += n
74 41
 
42
+	for _, v := range tlstypes.MakeRecords(p) {
43
+		_, err := writeFunc(v.Bytes())
75 44
 		if err != nil {
76 45
 			return sum, err
77 46
 		}
78 47
 
79
-		p = p[chunkSize:]
48
+		sum += len(v.Data.Bytes())
80 49
 	}
81 50
 
82 51
 	return sum, nil
@@ -108,41 +77,20 @@ func NewFakeTLS(socket conntypes.StreamReadWriteCloser) conntypes.StreamReadWrit
108 77
 	}
109 78
 
110 79
 	faketls.readFunc = func() ([]byte, error) {
111
-		data := &bytes.Buffer{}
112
-		buf := [2]byte{}
113
-		recordType := byte(faketlsRecordTypeCCS)
114
-
115
-		for recordType == faketlsRecordTypeCCS {
116
-			if _, err := io.ReadFull(faketls.parent, buf[:1]); err != nil {
117
-				return nil, fmt.Errorf("cannot read record type: %w", err)
80
+		for {
81
+			rec, err := tlstypes.ReadRecord(faketls.parent)
82
+			if err != nil {
83
+				return nil, err
118 84
 			}
119 85
 
120
-			switch buf[0] {
121
-			case faketlsRecordTypeCCS, faketlsRecordTypeApplicationData:
122
-				recordType = buf[0]
86
+			switch rec.Type {
87
+			case tlstypes.RecordTypeChangeCipherSpec:
88
+			case tlstypes.RecordTypeApplicationData:
89
+				return rec.Data.Bytes(), nil
123 90
 			default:
124
-				return nil, fmt.Errorf("incorrect record type %v", buf[0])
125
-			}
126
-
127
-			if _, err := io.ReadFull(faketls.parent, buf[:]); err != nil {
128
-				return nil, fmt.Errorf("cannot read version: %w", err)
129
-			}
130
-
131
-			if !bytes.Equal(buf[:], []byte{0x03, 0x03}) {
132
-				return nil, fmt.Errorf("unknown tls version %v", buf)
133
-			}
134
-
135
-			if _, err := io.ReadFull(faketls.parent, buf[:]); err != nil {
136
-				return nil, fmt.Errorf("cannot read data length: %w", err)
137
-			}
138
-
139
-			dataLength := binary.BigEndian.Uint16(buf[:])
140
-			if _, err := io.CopyN(data, faketls.parent, int64(dataLength)); err != nil {
141
-				return nil, fmt.Errorf("cannot copy frame data: %w", err)
91
+				return nil, fmt.Errorf("unsupported record type %v", rec.Type)
142 92
 			}
143 93
 		}
144
-
145
-		return data.Bytes(), nil
146 94
 	}
147 95
 
148 96
 	return faketls

+ 8
- 2
wrappers/stream/rewind.go Просмотреть файл

@@ -43,7 +43,10 @@ func (w *wrapperRewind) Read(p []byte) (int, error) {
43 43
 	}
44 44
 
45 45
 	n, err := w.parent.Read(p)
46
-	w.buf.Write(p[:n])
46
+
47
+	if !w.rewinded {
48
+		w.buf.Write(p[:n])
49
+	}
47 50
 
48 51
 	return n, err
49 52
 }
@@ -59,7 +62,10 @@ func (w *wrapperRewind) ReadTimeout(p []byte, timeout time.Duration) (int, error
59 62
 	}
60 63
 
61 64
 	n, err := w.parent.ReadTimeout(p, timeout)
62
-	w.buf.Write(p[:n])
65
+
66
+	if !w.rewinded {
67
+		w.buf.Write(p[:n])
68
+	}
63 69
 
64 70
 	return n, err
65 71
 }

Загрузка…
Отмена
Сохранить