init
Publish mtproxy_checker Docker image / test (push) Successful in 19s
Publish mtproxy_checker Docker image / build-and-push (push) Successful in 48s

This commit is contained in:
Denozordec
2026-04-11 00:38:22 +07:00
commit 4905c06b9b
26 changed files with 1887 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
package secret
import (
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"strings"
)
type Kind int
const (
KindEE Kind = iota
KindDD
)
// Parsed holds normalized MTProxy secret material for handshake and MTProxyIO.
type Parsed struct {
Kind Kind
// Key is always 16 bytes (MTProxy user secret).
Key []byte
// Domain is raw bytes after the key in ee-secrets (SNI payload); empty for dd.
Domain []byte
// RawHex is the original secret string (lowercase hex) for logging.
RawHex string
}
var (
ErrInvalidSecret = errors.New("invalid mtproxy secret")
)
// Parse decodes Telegram MTProxy secret from tg:// links (hex or legacy base64).
func Parse(secretStr string) (*Parsed, error) {
s := strings.TrimSpace(strings.ToLower(secretStr))
if s == "" {
return nil, fmt.Errorf("%w: empty", ErrInvalidSecret)
}
// Strip common tg:// noise
s = strings.TrimPrefix(s, "0x")
var raw []byte
if isHex(s) {
b, err := hex.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("%w: hex: %v", ErrInvalidSecret, err)
}
raw = b
} else {
// Telegram sometimes uses base64 secrets
pad := strings.Repeat("=", (4-len(s)%4)%4)
b, err := base64.StdEncoding.DecodeString(s + pad)
if err != nil {
b, err = base64.URLEncoding.DecodeString(s + pad)
}
if err != nil {
return nil, fmt.Errorf("%w: not hex or base64: %v", ErrInvalidSecret, err)
}
raw = b
}
if len(raw) == 0 {
return nil, fmt.Errorf("%w: no payload", ErrInvalidSecret)
}
switch raw[0] {
case 0xdd:
if len(raw) != 17 {
return nil, fmt.Errorf("%w: dd secret must be 17 bytes, got %d", ErrInvalidSecret, len(raw))
}
key := make([]byte, 16)
copy(key, raw[1:])
return &Parsed{
Kind: KindDD,
Key: key,
Domain: nil,
RawHex: s,
}, nil
case 0xee:
if len(raw) < 18 {
return nil, fmt.Errorf("%w: ee secret too short", ErrInvalidSecret)
}
key := make([]byte, 16)
copy(key, raw[1:17])
domain := make([]byte, len(raw)-17)
copy(domain, raw[17:])
return &Parsed{
Kind: KindEE,
Key: key,
Domain: domain,
RawHex: s,
}, nil
default:
return nil, fmt.Errorf("%w: unknown first byte 0x%02x (expected ee/dd)", ErrInvalidSecret, raw[0])
}
}
func isHex(s string) bool {
for _, r := range s {
switch {
case r >= '0' && r <= '9', r >= 'a' && r <= 'f':
default:
return false
}
}
return len(s)%2 == 0 && len(s) >= 2
}
+47
View File
@@ -0,0 +1,47 @@
package secret
import (
"bytes"
"encoding/hex"
"testing"
)
func TestParseEE_UserExample(t *testing.T) {
s := "ee5ba9cf5cf84698032e5300c864544dd0632e6170692e73746570612e6f6e65"
p, err := Parse(s)
if err != nil {
t.Fatal(err)
}
if p.Kind != KindEE {
t.Fatalf("kind %v", p.Kind)
}
raw, err := hex.DecodeString(s)
if err != nil {
t.Fatal(err)
}
if raw[0] != 0xee {
t.Fatal("marker")
}
if !bytes.Equal(p.Key, raw[1:17]) {
t.Fatalf("key: got %x want %x", p.Key, raw[1:17])
}
if !bytes.Equal(p.Domain, raw[17:]) {
t.Fatalf("domain: got %x want %x", p.Domain, raw[17:])
}
}
func TestParseDD(t *testing.T) {
s := "dd00000000000000000000000000000000"
p, err := Parse(s)
if err != nil {
t.Fatal(err)
}
if p.Kind != KindDD {
t.Fatalf("kind %v", p.Kind)
}
for _, b := range p.Key {
if b != 0 {
t.Fatal("expected zero key")
}
}
}