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 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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. )
  14. const (
  15. TypeHandshakeClient = 0x01
  16. RandomLen = 32
  17. // record_type(1) + version(2) + size(2) + handshake_type(1) + uint24_length(3) + client_version(2)
  18. RandomOffset = 1 + 2 + 2 + 1 + 3 + 2
  19. sniDNSNamesListType = 0
  20. )
  21. var (
  22. emptyRandom = [RandomLen]byte{}
  23. extTypeSNI = [2]byte{}
  24. )
  25. type ClientHello struct {
  26. Random [RandomLen]byte
  27. SessionID []byte
  28. CipherSuite uint16
  29. }
  30. func ReadClientHello(
  31. conn net.Conn,
  32. secret []byte,
  33. hostname string,
  34. tolerateTimeSkewness time.Duration,
  35. ) (*ClientHello, error) {
  36. if err := conn.SetReadDeadline(time.Now().Add(ClientHelloReadTimeout)); err != nil {
  37. return nil, fmt.Errorf("cannot set read deadline: %w", err)
  38. }
  39. defer conn.SetReadDeadline(resetDeadline) //nolint: errcheck
  40. // This is how FakeTLS is organized:
  41. // 1. We create sha256 HMAC with a given secret
  42. // 2. We dump there a whole TLS frame except of the fact that random
  43. // is filled with all zeroes
  44. // 3. Digest is computed. This digest should be XORed with
  45. // original client random
  46. // 4. New digest should be all 0 except of last 4 bytes
  47. // 5. Last 4 bytes are little endian uint32 of UNIX timestamp when
  48. // this message was created.
  49. clientHelloCopy, handshakeReader, err := parseClientHello(conn)
  50. if err != nil {
  51. return nil, fmt.Errorf("cannot read client hello: %w", err)
  52. }
  53. hello, err := parseHandshake(handshakeReader)
  54. if err != nil {
  55. return nil, fmt.Errorf("cannot parse handshake: %w", err)
  56. }
  57. sniHostnames, err := parseSNI(handshakeReader)
  58. if err != nil {
  59. return nil, fmt.Errorf("cannot parse SNI: %w", err)
  60. }
  61. if !slices.Contains(sniHostnames, hostname) {
  62. return nil, fmt.Errorf("cannot find %s in %v", hostname, sniHostnames)
  63. }
  64. digest := hmac.New(sha256.New, secret)
  65. // we write a copy of the handshake with client random all nullified.
  66. digest.Write(clientHelloCopy.Next(RandomOffset))
  67. clientHelloCopy.Next(RandomLen)
  68. digest.Write(emptyRandom[:])
  69. digest.Write(clientHelloCopy.Bytes())
  70. computed := digest.Sum(nil)
  71. for i := range RandomLen {
  72. computed[i] ^= hello.Random[i]
  73. }
  74. if subtle.ConstantTimeCompare(emptyRandom[:RandomLen-4], computed[:RandomLen-4]) != 1 {
  75. return nil, ErrBadDigest
  76. }
  77. timestamp := int64(binary.LittleEndian.Uint32(computed[RandomLen-4:]))
  78. createdAt := time.Unix(timestamp, 0)
  79. if tdiff := time.Since(createdAt).Abs(); tdiff > tolerateTimeSkewness {
  80. return nil, fmt.Errorf("timestamp %q is too old %s", createdAt, tdiff)
  81. }
  82. return hello, nil
  83. }
  84. func parseHandshake(r io.Reader) (*ClientHello, error) {
  85. // A protocol version of "3,3" (meaning TLS 1.2) is given.
  86. header := [2]byte{}
  87. if _, err := io.ReadFull(r, header[:]); err != nil {
  88. return nil, fmt.Errorf("cannot read client version: %w", err)
  89. }
  90. hello := &ClientHello{}
  91. if _, err := io.ReadFull(r, hello.Random[:]); err != nil {
  92. return nil, fmt.Errorf("cannot read client random: %w", err)
  93. }
  94. if _, err := io.ReadFull(r, header[:1]); err != nil {
  95. return nil, fmt.Errorf("cannot read session ID length: %w", err)
  96. }
  97. hello.SessionID = make([]byte, int(header[0]))
  98. if _, err := io.ReadFull(r, hello.SessionID); err != nil {
  99. return nil, fmt.Errorf("cannot read session id: %w", err)
  100. }
  101. if _, err := io.ReadFull(r, header[:]); err != nil {
  102. return nil, fmt.Errorf("cannot read cipher suite length: %w", err)
  103. }
  104. cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
  105. // we do not care about picking up any cipher. we pick the first one,
  106. // so it is always should be present.
  107. if _, err := io.ReadFull(r, header[:]); err != nil {
  108. return nil, fmt.Errorf("cannot read first cipher suite: %w", err)
  109. }
  110. hello.CipherSuite = binary.BigEndian.Uint16(header[:])
  111. if _, err := io.CopyN(io.Discard, r, cipherSuiteLen-2); err != nil {
  112. return nil, fmt.Errorf("cannot skip remaining cipher suites: %w", err)
  113. }
  114. if _, err := io.ReadFull(r, header[:1]); err != nil {
  115. return nil, fmt.Errorf("cannot read compression methods length: %w", err)
  116. }
  117. if _, err := io.CopyN(io.Discard, r, int64(header[0])); err != nil {
  118. return nil, fmt.Errorf("cannot skip compression methods: %w", err)
  119. }
  120. return hello, nil
  121. }
  122. func parseSNI(r io.Reader) ([]string, error) {
  123. header := [2]byte{}
  124. if _, err := io.ReadFull(r, header[:]); err != nil {
  125. return nil, fmt.Errorf("cannot read length of TLS extensions: %w", err)
  126. }
  127. extensionsLength := int64(binary.BigEndian.Uint16(header[:]))
  128. buf := &bytes.Buffer{}
  129. buf.Grow(int(extensionsLength))
  130. if _, err := io.CopyN(buf, r, extensionsLength); err != nil {
  131. return nil, fmt.Errorf("cannot read extensions: %w", err)
  132. }
  133. for buf.Len() > 0 {
  134. // 00 00 - assigned value for extension "server name"
  135. // 00 18 - 0x18 (24) bytes of "server name" extension data follows
  136. // 00 16 - 0x16 (22) bytes of first (and only) list entry follows
  137. // 00 - list entry is type 0x00 "DNS hostname"
  138. // 00 13 - 0x13 (19) bytes of hostname follows
  139. // 65 78 61 ... 6e 65 74 - "example.ulfheim.net"
  140. // 00 00 - assigned value for extension "server name"
  141. extTypeB := buf.Next(2)
  142. if len(extTypeB) != 2 {
  143. return nil, fmt.Errorf("cannot read extension type: %v", extTypeB)
  144. }
  145. // 00 18 - 0x18 (24) bytes of "server name" extension data follows
  146. lengthB := buf.Next(2)
  147. if len(lengthB) != 2 {
  148. return nil, fmt.Errorf("cannot read extension %v length: %v", extTypeB, lengthB)
  149. }
  150. length := int(binary.BigEndian.Uint16(lengthB))
  151. extDataB := buf.Next(length)
  152. if len(extDataB) != length {
  153. return nil, fmt.Errorf("cannot read extension %v data: len %d != %d", extTypeB, length, len(extDataB))
  154. }
  155. if !bytes.Equal(extTypeB, extTypeSNI[:]) {
  156. continue
  157. }
  158. buf.Reset()
  159. buf.Write(extDataB)
  160. // 00 16 - 0x16 (22) bytes of first (and only) list entry follows
  161. lengthB = buf.Next(2)
  162. if len(lengthB) != 2 {
  163. return nil, fmt.Errorf("cannot read the length of the SNI record: %v", lengthB)
  164. }
  165. length = int(binary.BigEndian.Uint16(lengthB))
  166. if length == 0 {
  167. return nil, nil
  168. }
  169. listType, err := buf.ReadByte()
  170. if err != nil {
  171. return nil, fmt.Errorf("cannot read SNI list type: %w", err)
  172. }
  173. // 00 - list entry is type 0x00 "DNS hostname"
  174. if listType != sniDNSNamesListType {
  175. return nil, fmt.Errorf("incorrect SNI list type %#x", listType)
  176. }
  177. names := []string{}
  178. for buf.Len() > 0 {
  179. // 00 13 - 0x13 (19) bytes of hostname follows
  180. lengthB = buf.Next(2)
  181. if len(lengthB) != 2 {
  182. return nil, fmt.Errorf("incorrect length of the hostname: %v", lengthB)
  183. }
  184. length = int(binary.BigEndian.Uint16(lengthB))
  185. name := buf.Next(length)
  186. if len(name) != length {
  187. return nil, fmt.Errorf("incorrect length of SNI hostname: len %d != %d", length, len(name))
  188. }
  189. names = append(names, string(name))
  190. }
  191. return names, nil
  192. }
  193. return nil, nil
  194. }