Skip to content

TwilgateLabs/utproto

Repository files navigation

UTProto

UTProto is a generalized fork of Telegram's MTProto FakeTLS transport, re-engineered to carry arbitrary payloads (VPN traffic, application data, HTTP — anything that fits into a net.Conn) instead of MTProto frames.

Pure Go, zero third-party dependencies. Drop-in for any sing-box / Xray / v2ray / Mihomo / Clash-style proxy core with a thin adapter.

What it is

UTProto is a two-layer obfuscated transport:

[raw TCP socket]
  ↓
[FakeTLS framing]      TLS record header \x17\x03\x03 + length
  ↓
[obfuscated2 layer]    AES-256-CTR keyed from 64-byte init header
  ↓
[your payload]         arbitrary bytes

The ClientHello — emitted before the encrypted stream begins — is a byte-for-byte clone of a current Chrome-on-Linux ClientHello, with ECH (extension 0xfe0d) and a randomized extension permutation. To a DPI observer looking at the TLS handshake bytes, the connection is indistinguishable from a real browser talking to a real TLS server.

What it is not

  • Not MTProto. UTProto borrows MTProto's wire mimicry but uses it as a framing layer for arbitrary bytes. It does not implement Telegram's MTProto protocol semantics, message types, or auth.
  • Not a proxy core. UTProto provides the transport. You bring the proxy logic (VLESS, Shadowsocks, your own — your choice) on top.
  • Not a censorship-circumvention silver bullet. Like Reality, naive, Hysteria2, and other modern transports, UTProto is one tool in a toolbox. Its strength is wire-mimicry of a popular browser's TLS handshake.

Install

go get github.com/TwilgateLabs/utproto

Go 1.22 or newer.

Quick start

Client

import (
    "context"
    "fmt"
    "net"

    "github.com/TwilgateLabs/utproto"
)

func dial() (net.Conn, error) {
    tcp, err := net.Dial("tcp", "your-server.example.com:443")
    if err != nil {
        return nil, err
    }
    secret, _ := utproto.DecodeSecret("ee" + "00112233445566778899aabbccddeeff")
    cfg := &utproto.Config{
        Secret:    secret,
        TLSDomain: "your-fake-sni.example.com",
    }
    return utproto.Dial(context.Background(), tcp, cfg)
}

The returned net.Conn carries your payload. Read/write it like any other net.Conn — UTProto handles framing transparently.

Server

ln, _ := net.Listen("tcp", ":443")
users := []utproto.ServerUser{
    {Name: "alice", Secret: parseHexSecret("00112233445566778899aabbccddeeff")},
    {Name: "bob",   Secret: parseHexSecret("ffeeddccbbaa99887766554433221100")},
}
for {
    raw, err := ln.Accept()
    if err != nil { continue }
    go func() {
        defer raw.Close()
        conn, user, err := utproto.Accept(context.Background(), raw, users)
        if err != nil {
            // ServerHello identification failed. err.(*utproto.HandshakeError)
            // exposes the original ClientHello bytes — you can fall back to
            // proxying them to a real TLS server to avoid leaking that you
            // run UTProto.
            return
        }
        log.Printf("user %s connected", user.Name)
        // Read/write conn — your application bytes.
    }()
}

Spec

The wire format is documented in SPEC.md.

Examples

  • examples/singbox/ — a sing-box adapter that wires UTProto into a sing-box protocol.Outbound / protocol.Inbound. Use this as a reference if you want to port UTProto into Xray-core, Mihomo, v2ray, or any other sing-box-derived stack.

Public API

// Types
type Config struct {
    Secret    [16]byte
    TLSDomain string
}
type ServerUser struct {
    Name   string
    Secret [16]byte
}
type HandshakeError struct {
    Kind   string         // sentinel: ErrSkew, ErrUser, ErrUnknown
    Err    error
    Buffer []byte         // original ClientHello bytes, for DPI fallback
}

// Constructors
func Dial(ctx context.Context, raw net.Conn, cfg *Config) (net.Conn, error)
func Accept(ctx context.Context, raw net.Conn, users []ServerUser) (net.Conn, *ServerUser, error)

// Helpers
func GenCurve25519PublicKey() ([32]byte, error)
func GenFakeMLKem768Key(buf []byte) error

Origin

UTProto's ClientHello renderer (tls_init.go) and the obfuscated2 layer (obfuscated.go) are ported from tdlib's td/mtproto/TlsInit.cpp and td/mtproto/TcpTransport.cpp, both under the Boost Software License 1.0. See NOTICE for attribution.

Maintained by Twilgate Labs as part of the InHive VPN client stack.

License

Apache License 2.0

About

UTProto — FakeTLS transport with MTProto-style wire mimicry. Pure Go, zero proxy-core dependencies. Drop-in for sing-box / Xray / v2ray / Mihomo.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages