Highly-opinionated (ex-bullshit-free) MTPROTO proxy for Telegram. If you use v1.0 or upgrade broke you proxy, please read the chapter Version 2
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

client_side.go 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. package fake
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "crypto/sha256"
  6. "crypto/subtle"
  7. "encoding/binary"
  8. "fmt"
  9. "io"
  10. "net"
  11. "slices"
  12. "time"
  13. "github.com/dolonet/mtg-multi/mtglib/internal/tls"
  14. )
  15. const (
  16. TypeHandshakeClient = 0x01
  17. RandomLen = 32
  18. // record_type(1) + version(2) + size(2) + handshake_type(1) + uint24_length(3) + client_version(2)
  19. RandomOffset = 1 + 2 + 2 + 1 + 3 + 2
  20. sniDNSNamesListType = 0
  21. )
  22. var (
  23. emptyRandom = [RandomLen]byte{}
  24. extTypeSNI = [2]byte{}
  25. )
  26. type ClientHello struct {
  27. Random [RandomLen]byte
  28. SessionID []byte
  29. CipherSuite uint16
  30. }
  31. // ReadClientHelloResult contains the parsed ClientHello and the index of the
  32. // secret that matched the HMAC validation.
  33. type ReadClientHelloResult struct {
  34. Hello *ClientHello
  35. MatchedIndex int
  36. }
  37. func ReadClientHello(
  38. conn net.Conn,
  39. secret []byte,
  40. hostname string,
  41. tolerateTimeSkewness time.Duration,
  42. ) (*ClientHello, error) {
  43. result, err := ReadClientHelloMulti(conn, [][]byte{secret}, hostname, tolerateTimeSkewness)
  44. if err != nil {
  45. return nil, err
  46. }
  47. return result.Hello, nil
  48. }
  49. // ReadClientHelloMulti is like ReadClientHello but accepts multiple secrets.
  50. // It tries each secret until one validates the HMAC. On success it returns
  51. // the ClientHello and the index of the matched secret.
  52. func ReadClientHelloMulti(
  53. conn net.Conn,
  54. secrets [][]byte,
  55. hostname string,
  56. tolerateTimeSkewness time.Duration,
  57. ) (*ReadClientHelloResult, error) {
  58. if err := conn.SetReadDeadline(time.Now().Add(ClientHelloReadTimeout)); err != nil {
  59. return nil, fmt.Errorf("cannot set read deadline: %w", err)
  60. }
  61. defer conn.SetReadDeadline(resetDeadline) //nolint: errcheck
  62. handshakeCopyBuf := &bytes.Buffer{}
  63. reader := io.TeeReader(conn, handshakeCopyBuf)
  64. reader, err := parseTLSHeader(reader)
  65. if err != nil {
  66. return nil, fmt.Errorf("cannot parse tls header: %w", err)
  67. }
  68. reader, err = parseHandshakeHeader(reader)
  69. if err != nil {
  70. return nil, fmt.Errorf("cannot parse handshake header: %w", err)
  71. }
  72. hello, err := parseHandshake(reader)
  73. if err != nil {
  74. return nil, fmt.Errorf("cannot parse handshake: %w", err)
  75. }
  76. sniHostnames, err := parseSNI(reader)
  77. if err != nil {
  78. return nil, fmt.Errorf("cannot parse SNI: %w", err)
  79. }
  80. if !slices.Contains(sniHostnames, hostname) {
  81. return nil, fmt.Errorf("cannot find %s in %v", hostname, sniHostnames)
  82. }
  83. // Save the handshake bytes so we can reuse them for each secret attempt.
  84. handshakeBytes := handshakeCopyBuf.Bytes()
  85. for idx, secret := range secrets {
  86. digest := hmac.New(sha256.New, secret)
  87. // Write the handshake with client random all nullified.
  88. digest.Write(handshakeBytes[:RandomOffset])
  89. digest.Write(emptyRandom[:])
  90. digest.Write(handshakeBytes[RandomOffset+RandomLen:])
  91. computed := digest.Sum(nil)
  92. for i := range RandomLen {
  93. computed[i] ^= hello.Random[i]
  94. }
  95. if subtle.ConstantTimeCompare(emptyRandom[:RandomLen-4], computed[:RandomLen-4]) != 1 {
  96. continue
  97. }
  98. timestamp := int64(binary.LittleEndian.Uint32(computed[RandomLen-4:]))
  99. createdAt := time.Unix(timestamp, 0)
  100. if tdiff := time.Since(createdAt).Abs(); tdiff > tolerateTimeSkewness {
  101. continue
  102. }
  103. return &ReadClientHelloResult{
  104. Hello: hello,
  105. MatchedIndex: idx,
  106. }, nil
  107. }
  108. return nil, ErrBadDigest
  109. }
  110. func parseTLSHeader(r io.Reader) (io.Reader, error) {
  111. // record_type(1) + version(2) + size(2)
  112. // 16 - type is 0x16 (handshake record)
  113. // 03 01 - protocol version is "3,1" (also known as TLS 1.0)
  114. // 00 f8 - 0xF8 (248) bytes of handshake message follows
  115. header := [1 + 2 + 2]byte{}
  116. if _, err := io.ReadFull(r, header[:]); err != nil {
  117. return nil, fmt.Errorf("cannot read record header: %w", err)
  118. }
  119. if header[0] != tls.TypeHandshake {
  120. return nil, fmt.Errorf("unexpected record type %#x", header[0])
  121. }
  122. if header[1] != 3 || header[2] != 1 {
  123. return nil, fmt.Errorf("unexpected protocol version %#x %#x", header[1], header[2])
  124. }
  125. length := int64(binary.BigEndian.Uint16(header[3:]))
  126. buf := &bytes.Buffer{}
  127. _, err := io.CopyN(buf, r, length)
  128. return buf, err
  129. }
  130. func parseHandshakeHeader(r io.Reader) (io.Reader, error) {
  131. // type(1) + size(3 / uint24)
  132. // 01 - handshake message type 0x01 (client hello)
  133. // 00 00 f4 - 0xF4 (244) bytes of client hello data follows
  134. header := [1 + 3]byte{}
  135. if _, err := io.ReadFull(r, header[:]); err != nil {
  136. return nil, fmt.Errorf("cannot read handshake header: %w", err)
  137. }
  138. if header[0] != TypeHandshakeClient {
  139. return nil, fmt.Errorf("incorrect handshake type: %#x", header[0])
  140. }
  141. // unfortunately there is not uint24 in golang, so we just reust header
  142. header[0] = 0
  143. length := int64(binary.BigEndian.Uint32(header[:]))
  144. buf := &bytes.Buffer{}
  145. _, err := io.CopyN(buf, r, length)
  146. return buf, err
  147. }
  148. func parseHandshake(r io.Reader) (*ClientHello, error) {
  149. // A protocol version of "3,3" (meaning TLS 1.2) is given.
  150. header := [2]byte{}
  151. if _, err := io.ReadFull(r, header[:]); err != nil {
  152. return nil, fmt.Errorf("cannot read client version: %w", err)
  153. }
  154. hello := &ClientHello{}
  155. if _, err := io.ReadFull(r, hello.Random[:]); err != nil {
  156. return nil, fmt.Errorf("cannot read client random: %w", err)
  157. }
  158. if _, err := io.ReadFull(r, header[:1]); err != nil {
  159. return nil, fmt.Errorf("cannot read session ID length: %w", err)
  160. }
  161. hello.SessionID = make([]byte, int(header[0]))
  162. if _, err := io.ReadFull(r, hello.SessionID); err != nil {
  163. return nil, fmt.Errorf("cannot read session id: %w", err)
  164. }
  165. if _, err := io.ReadFull(r, header[:]); err != nil {
  166. return nil, fmt.Errorf("cannot read cipher suite length: %w", err)
  167. }
  168. cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
  169. // we do not care about picking up any cipher. we pick the first one,
  170. // so it is always should be present.
  171. if _, err := io.ReadFull(r, header[:]); err != nil {
  172. return nil, fmt.Errorf("cannot read first cipher suite: %w", err)
  173. }
  174. hello.CipherSuite = binary.BigEndian.Uint16(header[:])
  175. if _, err := io.CopyN(io.Discard, r, cipherSuiteLen-2); err != nil {
  176. return nil, fmt.Errorf("cannot skip remaining cipher suites: %w", err)
  177. }
  178. if _, err := io.ReadFull(r, header[:1]); err != nil {
  179. return nil, fmt.Errorf("cannot read compression methods length: %w", err)
  180. }
  181. if _, err := io.CopyN(io.Discard, r, int64(header[0])); err != nil {
  182. return nil, fmt.Errorf("cannot skip compression methods: %w", err)
  183. }
  184. return hello, nil
  185. }
  186. func parseSNI(r io.Reader) ([]string, error) {
  187. header := [2]byte{}
  188. if _, err := io.ReadFull(r, header[:]); err != nil {
  189. return nil, fmt.Errorf("cannot read length of TLS extensions: %w", err)
  190. }
  191. extensionsLength := int64(binary.BigEndian.Uint16(header[:]))
  192. buf := &bytes.Buffer{}
  193. buf.Grow(int(extensionsLength))
  194. if _, err := io.CopyN(buf, r, extensionsLength); err != nil {
  195. return nil, fmt.Errorf("cannot read extensions: %w", err)
  196. }
  197. for buf.Len() > 0 {
  198. // 00 00 - assigned value for extension "server name"
  199. // 00 18 - 0x18 (24) bytes of "server name" extension data follows
  200. // 00 16 - 0x16 (22) bytes of first (and only) list entry follows
  201. // 00 - list entry is type 0x00 "DNS hostname"
  202. // 00 13 - 0x13 (19) bytes of hostname follows
  203. // 65 78 61 ... 6e 65 74 - "example.ulfheim.net"
  204. // 00 00 - assigned value for extension "server name"
  205. extTypeB := buf.Next(2)
  206. if len(extTypeB) != 2 {
  207. return nil, fmt.Errorf("cannot read extension type: %v", extTypeB)
  208. }
  209. // 00 18 - 0x18 (24) bytes of "server name" extension data follows
  210. lengthB := buf.Next(2)
  211. if len(lengthB) != 2 {
  212. return nil, fmt.Errorf("cannot read extension %v length: %v", extTypeB, lengthB)
  213. }
  214. length := int(binary.BigEndian.Uint16(lengthB))
  215. extDataB := buf.Next(length)
  216. if len(extDataB) != length {
  217. return nil, fmt.Errorf("cannot read extension %v data: len %d != %d", extTypeB, length, len(extDataB))
  218. }
  219. if !bytes.Equal(extTypeB, extTypeSNI[:]) {
  220. continue
  221. }
  222. buf.Reset()
  223. buf.Write(extDataB)
  224. // 00 16 - 0x16 (22) bytes of first (and only) list entry follows
  225. lengthB = buf.Next(2)
  226. if len(lengthB) != 2 {
  227. return nil, fmt.Errorf("cannot read the length of the SNI record: %v", lengthB)
  228. }
  229. length = int(binary.BigEndian.Uint16(lengthB))
  230. if length == 0 {
  231. return nil, nil
  232. }
  233. listType, err := buf.ReadByte()
  234. if err != nil {
  235. return nil, fmt.Errorf("cannot read SNI list type: %w", err)
  236. }
  237. // 00 - list entry is type 0x00 "DNS hostname"
  238. if listType != sniDNSNamesListType {
  239. return nil, fmt.Errorf("incorrect SNI list type %#x", listType)
  240. }
  241. names := []string{}
  242. for buf.Len() > 0 {
  243. // 00 13 - 0x13 (19) bytes of hostname follows
  244. lengthB = buf.Next(2)
  245. if len(lengthB) != 2 {
  246. return nil, fmt.Errorf("incorrect length of the hostname: %v", lengthB)
  247. }
  248. length = int(binary.BigEndian.Uint16(lengthB))
  249. name := buf.Next(length)
  250. if len(name) != length {
  251. return nil, fmt.Errorf("incorrect length of SNI hostname: len %d != %d", length, len(name))
  252. }
  253. names = append(names, string(name))
  254. }
  255. return names, nil
  256. }
  257. return nil, nil
  258. }