mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 20:54:02 +03:00
Success path for fake tls is implemented
This commit is contained in:
@@ -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
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user