Merge pull request #22 from 9seconds/secure-padding

Correct support of secure padding
This commit is contained in:
Sergey Arkhipov
2018-07-13 08:20:57 +03:00
committed by GitHub
7 changed files with 128 additions and 20 deletions
+9 -2
View File
@@ -17,9 +17,16 @@ func MiddleInit(socket net.Conn, connID string, conf *config.Config) (wrappers.W
} }
connStream := conn.(wrappers.StreamReadWriteCloser) connStream := conn.(wrappers.StreamReadWriteCloser)
newConn := wrappers.NewMTProtoAbridged(connStream, opts) var newConn wrappers.PacketReadWriteCloser
if opts.ConnectionType != mtproto.ConnectionTypeAbridged { switch opts.ConnectionType {
case mtproto.ConnectionTypeAbridged:
newConn = wrappers.NewMTProtoAbridged(connStream, opts)
case mtproto.ConnectionTypeIntermediate:
newConn = wrappers.NewMTProtoIntermediate(connStream, opts) newConn = wrappers.NewMTProtoIntermediate(connStream, opts)
case mtproto.ConnectionTypeSecure:
newConn = wrappers.NewMTProtoIntermediateSecure(connStream, opts)
default:
panic("Unknown connection type")
} }
opts.ConnectionProto = mtproto.ConnectionProtocolIPv4 opts.ConnectionProto = mtproto.ConnectionProtocolIPv4
+31 -8
View File
@@ -18,8 +18,9 @@ const (
// Config represents common configuration of mtg. // Config represents common configuration of mtg.
type Config struct { type Config struct {
Debug bool Debug bool
Verbose bool Verbose bool
SecureMode bool
BindPort uint16 BindPort uint16
PublicIPv4Port uint16 PublicIPv4Port uint16
@@ -45,8 +46,9 @@ type URLs struct {
// IPURLs contains links to both ipv4 and ipv6 of the proxy. // IPURLs contains links to both ipv4 and ipv6 of the proxy.
type IPURLs struct { type IPURLs struct {
IPv4 URLs `json:"ipv4"` IPv4 URLs `json:"ipv4"`
IPv6 URLs `json:"ipv6"` IPv6 URLs `json:"ipv6"`
BotSecret string `json:"secret_for_mtproxybot"`
} }
// BindAddr returns connection for this server to bind to. // BindAddr returns connection for this server to bind to.
@@ -65,15 +67,32 @@ func (c *Config) UseMiddleProxy() bool {
return len(c.AdTag) > 0 return len(c.AdTag) > 0
} }
// BotSecretString returns secret string which should work with MTProxybot.
func (c *Config) BotSecretString() string {
return hex.EncodeToString(c.Secret)
}
// SecretString returns a secret in a form entered on the start of the
// application.
func (c *Config) SecretString() string {
secret := c.BotSecretString()
if c.SecureMode {
return "dd" + secret
}
return secret
}
// GetURLs returns configured IPURLs instance with links to this server. // GetURLs returns configured IPURLs instance with links to this server.
func (c *Config) GetURLs() IPURLs { func (c *Config) GetURLs() IPURLs {
urls := IPURLs{} urls := IPURLs{}
secret := c.SecretString()
if c.PublicIPv4 != nil { if c.PublicIPv4 != nil {
urls.IPv4 = getURLs(c.PublicIPv4, c.PublicIPv4Port, c.Secret) urls.IPv4 = getURLs(c.PublicIPv4, c.PublicIPv4Port, secret)
} }
if c.PublicIPv6 != nil { if c.PublicIPv6 != nil {
urls.IPv6 = getURLs(c.PublicIPv6, c.PublicIPv6Port, c.Secret) urls.IPv6 = getURLs(c.PublicIPv6, c.PublicIPv6Port, secret)
} }
urls.BotSecret = c.BotSecretString()
return urls return urls
} }
@@ -91,8 +110,11 @@ func NewConfig(debug, verbose bool, // nolint: gocyclo
publicIPv6 net.IP, publicIPv6Port uint16, publicIPv6 net.IP, publicIPv6Port uint16,
statsIP net.IP, statsPort uint16, statsIP net.IP, statsPort uint16,
secret, adtag string) (*Config, error) { secret, adtag string) (*Config, error) {
secret = strings.TrimPrefix(secret, "dd") secureMode := false
if len(secret) != 32 { if strings.HasPrefix(secret, "dd") && len(secret) == 34 {
secureMode = true
secret = strings.TrimPrefix(secret, "dd")
} else if len(secret) != 32 {
return nil, errors.New("Telegram demands secret of length 32") return nil, errors.New("Telegram demands secret of length 32")
} }
secretBytes, err := hex.DecodeString(secret) secretBytes, err := hex.DecodeString(secret)
@@ -149,6 +171,7 @@ func NewConfig(debug, verbose bool, // nolint: gocyclo
StatsPort: statsPort, StatsPort: statsPort,
Secret: secretBytes, Secret: secretBytes,
AdTag: adTagBytes, AdTag: adTagBytes,
SecureMode: secureMode,
} }
return conf, nil return conf, nil
+2 -3
View File
@@ -1,17 +1,16 @@
package config package config
import ( import (
"encoding/hex"
"net" "net"
"net/url" "net/url"
"strconv" "strconv"
) )
func getURLs(addr net.IP, port uint16, secret []byte) (urls URLs) { func getURLs(addr net.IP, port uint16, secret string) (urls URLs) {
values := url.Values{} values := url.Values{}
values.Set("server", addr.String()) values.Set("server", addr.String())
values.Set("port", strconv.Itoa(int(port))) values.Set("port", strconv.Itoa(int(port)))
values.Set("secret", hex.EncodeToString(secret)) values.Set("secret", secret)
urls.TG = makeTGURL(values) urls.TG = makeTGURL(values)
urls.TMe = makeTMeURL(values) urls.TMe = makeTMeURL(values)
+4
View File
@@ -15,6 +15,7 @@ const (
proxyRequestFlagsIntermediate = 0x20000000 proxyRequestFlagsIntermediate = 0x20000000
proxyRequestFlagsAbdridged = 0x40000000 proxyRequestFlagsAbdridged = 0x40000000
proxyRequestFlagsQuickAck = 0x80000000 proxyRequestFlagsQuickAck = 0x80000000
proxyRequestFlagsPad = 0x8000000
) )
var proxyRequestFlagsEncryptedPrefix [8]byte var proxyRequestFlagsEncryptedPrefix [8]byte
@@ -50,6 +51,9 @@ func (r proxyRequestFlags) String() string {
if r&proxyRequestFlagsQuickAck != 0 { if r&proxyRequestFlagsQuickAck != 0 {
flags = append(flags, "QUICK_ACK") flags = append(flags, "QUICK_ACK")
} }
if r&proxyRequestFlagsPad != 0 {
flags = append(flags, "PAD")
}
return strings.Join(flags, " | ") return strings.Join(flags, " | ")
} }
+7 -2
View File
@@ -67,10 +67,15 @@ func (r *ProxyRequest) MakeHeader(message []byte) (*bytes.Buffer, fmt.Stringer)
func NewProxyRequest(clientAddr, ownAddr *net.TCPAddr, opts *mtproto.ConnectionOpts, adTag []byte) (*ProxyRequest, error) { func NewProxyRequest(clientAddr, ownAddr *net.TCPAddr, opts *mtproto.ConnectionOpts, adTag []byte) (*ProxyRequest, error) {
flags := proxyRequestFlagsHasAdTag | proxyRequestFlagsMagic | proxyRequestFlagsExtMode2 flags := proxyRequestFlagsHasAdTag | proxyRequestFlagsMagic | proxyRequestFlagsExtMode2
if opts.ConnectionType == mtproto.ConnectionTypeAbridged { switch opts.ConnectionType {
case mtproto.ConnectionTypeAbridged:
flags |= proxyRequestFlagsAbdridged flags |= proxyRequestFlagsAbdridged
} else { case mtproto.ConnectionTypeIntermediate:
flags |= proxyRequestFlagsIntermediate flags |= proxyRequestFlagsIntermediate
case mtproto.ConnectionTypeSecure:
flags |= proxyRequestFlagsIntermediate | proxyRequestFlagsPad
default:
panic("Unknown connection type")
} }
request := &ProxyRequest{ request := &ProxyRequest{
+1 -5
View File
@@ -62,10 +62,6 @@ func (m *MTProtoIntermediate) Read() ([]byte, error) {
return nil, errors.Annotate(err, "Cannot read the message") return nil, errors.Annotate(err, "Cannot read the message")
} }
if length%4 != 0 {
length -= length % 4
}
return buf.Bytes()[:length], nil return buf.Bytes()[:length], nil
} }
@@ -80,7 +76,7 @@ func (m *MTProtoIntermediate) Write(p []byte) (int, error) {
"counter", m.writeCounter, "counter", m.writeCounter,
) )
if m.opts.ReadHacks.SimpleAck { if m.opts.WriteHacks.SimpleAck {
return m.conn.Write(p) return m.conn.Write(p)
} }
+74
View File
@@ -0,0 +1,74 @@
package wrappers
import (
"bytes"
"encoding/binary"
"math/rand"
"github.com/9seconds/mtg/mtproto"
)
// MTProtoIntermediateSecure is an extension of MTProtoIntermediate
// mode which supports random paddings (socalled 'secure mode' or
// 'dd-secrets').
type MTProtoIntermediateSecure struct {
MTProtoIntermediate
}
func (m *MTProtoIntermediateSecure) Read() ([]byte, error) {
data, err := m.MTProtoIntermediate.Read()
if err != nil {
return nil, err
}
length := len(data) - (len(data) % 4)
return data[:length], nil
}
func (m *MTProtoIntermediateSecure) Write(p []byte) (int, error) {
defer func() {
m.writeCounter++
}()
m.logger.Debugw("Write packet",
"simple_ack", m.opts.WriteHacks.SimpleAck,
"quick_ack", m.opts.WriteHacks.QuickAck,
"counter", m.writeCounter,
)
if m.opts.WriteHacks.SimpleAck {
return m.conn.Write(p)
}
buf := &bytes.Buffer{}
paddingLength := rand.Intn(4)
buf.Grow(4 + len(p) + paddingLength)
binary.Write(buf, binary.LittleEndian, uint32(len(p)+paddingLength)) // nolint: errcheck
buf.Write(p)
buf.Write(make([]byte, paddingLength))
m.logger.Debugw("Write packet with padding",
"simple_ack", m.opts.WriteHacks.SimpleAck,
"quick_ack", m.opts.WriteHacks.QuickAck,
"counter", m.writeCounter,
"padding_length", paddingLength,
"length", len(p),
)
_, err := m.conn.Write(buf.Bytes())
return len(p), err
}
// NewMTProtoIntermediateSecure create new instance of
// MTProtoIntermediateSecure instance.
func NewMTProtoIntermediateSecure(conn StreamReadWriteCloser, opts *mtproto.ConnectionOpts) PacketReadWriteCloser {
return &MTProtoIntermediateSecure{
MTProtoIntermediate: MTProtoIntermediate{
conn: conn,
logger: conn.Logger().Named("mtproto-intermediate-secure"),
opts: opts,
},
}
}