package faketls import ( "crypto/hmac" "crypto/sha256" "encoding/binary" "io" "github.com/go-faster/errors" "go.mau.fi/mautrix-telegram/pkg/gotd/bin" "go.mau.fi/mautrix-telegram/pkg/gotd/clock" ) const clientHelloLength = 517 func createClientHello(b *bin.Buffer, sessionID [32]byte, domain string, key [32]byte) (randomOffset int) { S := func(s string) { b.Buf = append(b.Buf, s...) } Z := func(n int) { randomOffset = len(b.Buf) b.Expand(n) } G := func(_ int) { b.Expand(0) } R := func() { b.Buf = append(b.Buf, sessionID[:]...) } D := func() { b.Buf = append(b.Buf, domain...) } K := func() { b.Buf = append(b.Buf, key[:]...) } var stack []int Open := func() { stack = append(stack, b.Len()) b.Expand(2) } Close := func() { lastIdx := len(stack) - 1 s := stack[lastIdx] stack = stack[:lastIdx] length := b.Len() - (s + 2) binary.BigEndian.PutUint16(b.Buf[s:], uint16(length)) } S("\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03") Z(32) S("\x20") R() S("\x00\x20") G(0) S("\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9" + "\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x01\x00" + "\x01\x93") G(2) S("\x00\x00\x00\x00") Open() Open() S("\x00") Open() D() Close() Close() Close() S("\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08") G(4) S("\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00" + "\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31" + "\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x12\x00" + "\x10\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06" + "\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29") G(4) S("\x00\x01\x00\x00\x1d\x00\x20") K() S("\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a") G(6) S("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02") G(3) S("\x00\x01\x00\x00\x15") if pad := clientHelloLength - b.Len(); pad > 0 { b.Expand(pad) } return randomOffset } // writeClientHello writes faketls ClientHello. // // See https://tools.ietf.org/html/rfc5246#section-7.4.1.1. func writeClientHello( w io.Writer, now clock.Clock, sessionID [32]byte, domain string, secret []byte, ) (r [32]byte, err error) { b := &bin.Buffer{ Buf: make([]byte, 0, 576), } randomOffset := createClientHello(b, sessionID, domain, [32]byte{}) // https://github.com/tdlib/td/blob/27d3fdd09d90f6b77ecbcce50b1e86dc4b3dd366/td/mtproto/TlsInit.cpp#L380-L384 mac := hmac.New(sha256.New, secret) if _, err := mac.Write(b.Buf); err != nil { return [32]byte{}, errors.Wrap(err, "hmac write") } s := mac.Sum(nil) copy(b.Buf[randomOffset:randomOffset+32], s) // Overwrite last 4 bytes using final := original ^ timestamp. old := binary.LittleEndian.Uint32(b.Buf[randomOffset+28 : randomOffset+32]) old ^= uint32(now.Now().Unix()) binary.LittleEndian.PutUint32(b.Buf[randomOffset+28:randomOffset+32], old) // Copy ClientRandom for later use. copy(r[:], b.Buf[randomOffset:randomOffset+32]) _, err = w.Write(b.Buf) return r, err }