package faketlsimport (utls)// clientRandomOffset is the offset of the 32-byte ClientRandom field inside a// TLS ClientHello record://// 5 bytes record header (type + version + length)// 1 byte handshake type// 3 bytes handshake length// 2 bytes client versionconst (clientRandomOffset = 11clientRandomLength = 32)// generateClientHello builds a browser-like TLS ClientHello record using uTLS.//// Instead of hand-crafting the bytes (as older clients did), we mimic the// fingerprint of a recent Chrome release so that the handshake is// indistinguishable from a real browser connecting to the cloak domain. uTLS// tracks browser fingerprints upstream, keeping us in line with the approach// taken by the official Telegram clients.//// See https://github.com/refraction-networking/utls and// https://github.com/tdlib/td/blob/master/td/mtproto/TlsInit.cpp.func ( io.Reader, string) ([]byte, error) { := &utls.Config{ServerName: ,// uTLS uses Rand for the ClientHello random, session ID and key shares.Rand: , }// The connection is never used: BuildHandshakeState only assembles the // ClientHello in memory, it does not perform any I/O. := utls.UClient(nil, , utls.HelloChrome_Auto)if := .BuildHandshakeState(); != nil {returnnil, errors.Wrap(, "build handshake state") } := .HandshakeState.Hello.Raw// hello is the handshake message; wrap it into a TLS handshake record. := make([]byte, 0, 5+len()) = append(, byte(RecordTypeHandshake), Version10Bytes[0], Version10Bytes[1]) = binary.BigEndian.AppendUint16(, uint16(len())) = append(, ...)return , nil}// writeClientHello writes faketls ClientHello.//// The ClientHello carries the MTProxy digest in its ClientRandom field: the// HMAC-SHA256 of the whole record (computed with the ClientRandom zeroed) using// the proxy secret as key, with the lower 4 bytes XORed with the current// timestamp.//// See https://github.com/tdlib/td/blob/27d3fdd09d90f6b77ecbcce50b1e86dc4b3dd366/td/mtproto/TlsInit.cpp#L380-L384// and https://tools.ietf.org/html/rfc5246#section-7.4.1.1.func (io.Writer,io.Reader,clock.Clock,string, []byte,) ( [32]byte, error) { , := generateClientHello(, )if != nil {return [32]byte{}, errors.Wrap(, "generate ClientHello") }iflen() < clientRandomOffset+clientRandomLength {return [32]byte{}, errors.Errorf("ClientHello is too short: %d bytes", len()) } := [clientRandomOffset : clientRandomOffset+clientRandomLength]// Zero the ClientRandom before computing the digest, exactly as the proxy // does when it validates the handshake.for := range { [] = 0 } := hmac.New(sha256.New, )if , := .Write(); != nil {return [32]byte{}, errors.Wrap(, "hmac write") }copy(, .Sum(nil))// Overwrite last 4 bytes using final := original ^ timestamp. := binary.LittleEndian.Uint32([clientRandomLength-4:]) ^= uint32(.Now().Unix())binary.LittleEndian.PutUint32([clientRandomLength-4:], )// Copy ClientRandom for later use.copy([:], ) _, = .Write()return , }
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.