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
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

client_side.go 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package fake
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "crypto/sha256"
  6. "crypto/subtle"
  7. "encoding/binary"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "net"
  12. "slices"
  13. "time"
  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. // https://datatracker.ietf.org/doc/html/rfc8701#name-grease-values
  21. // https://medium.com/asecuritysite-when-bob-met-alice/in-cybersecurity-what-is-grease-9f8850558dea
  22. GreaseMask = 0x0f0f
  23. GreaseValueType = 0x0a0a
  24. sniDNSNamesListType = 0
  25. )
  26. var (
  27. emptyRandom = [RandomLen]byte{}
  28. extTypeSNI = [2]byte{}
  29. ErrCannotFindCipher = errors.New("cannot find a cipher")
  30. )
  31. type ClientHello struct {
  32. Random [RandomLen]byte
  33. SessionID []byte
  34. CipherSuite uint16
  35. }
  36. func ReadClientHello(
  37. conn net.Conn,
  38. secret []byte,
  39. hostname string,
  40. tolerateTimeSkewness time.Duration,
  41. ) (*ClientHello, error) {
  42. // This is how FakeTLS is organized:
  43. // 1. We create sha256 HMAC with a given secret
  44. // 2. We dump there a whole TLS frame except of the fact that random
  45. // is filled with all zeroes
  46. // 3. Digest is computed. This digest should be XORed with
  47. // original client random
  48. // 4. New digest should be all 0 except of last 4 bytes
  49. // 5. Last 4 bytes are little endian uint32 of UNIX timestamp when
  50. // this message was created.
  51. clientHelloCopy, handshakeReader, err := parseClientHello(conn)
  52. if err != nil {
  53. return nil, fmt.Errorf("cannot read client hello: %w", err)
  54. }
  55. hello, err := parseHandshake(handshakeReader)
  56. if err != nil {
  57. return nil, fmt.Errorf("cannot parse handshake: %w", err)
  58. }
  59. sniHostnames, err := parseSNI(handshakeReader)
  60. if err != nil {
  61. return nil, fmt.Errorf("cannot parse SNI: %w", err)
  62. }
  63. if !slices.Contains(sniHostnames, hostname) {
  64. return nil, fmt.Errorf("cannot find %s in %v", hostname, sniHostnames)
  65. }
  66. digest := hmac.New(sha256.New, secret)
  67. // we write a copy of the handshake with client random all nullified.
  68. digest.Write(clientHelloCopy.Next(RandomOffset))
  69. clientHelloCopy.Next(RandomLen)
  70. digest.Write(emptyRandom[:])
  71. digest.Write(clientHelloCopy.Bytes())
  72. computed := digest.Sum(nil)
  73. for i := range RandomLen {
  74. computed[i] ^= hello.Random[i]
  75. }
  76. if subtle.ConstantTimeCompare(emptyRandom[:RandomLen-4], computed[:RandomLen-4]) != 1 {
  77. return nil, ErrBadDigest
  78. }
  79. timestamp := int64(binary.LittleEndian.Uint32(computed[RandomLen-4:]))
  80. createdAt := time.Unix(timestamp, 0)
  81. if tdiff := time.Since(createdAt).Abs(); tdiff > tolerateTimeSkewness {
  82. return nil, fmt.Errorf("timestamp %q is too old %s", createdAt, tdiff)
  83. }
  84. return hello, nil
  85. }
  86. func parseHandshake(r io.Reader) (*ClientHello, error) {
  87. // A protocol version of "3,3" (meaning TLS 1.2) is given.
  88. header := [2]byte{}
  89. if _, err := io.ReadFull(r, header[:]); err != nil {
  90. return nil, fmt.Errorf("cannot read client version: %w", err)
  91. }
  92. hello := &ClientHello{}
  93. if _, err := io.ReadFull(r, hello.Random[:]); err != nil {
  94. return nil, fmt.Errorf("cannot read client random: %w", err)
  95. }
  96. if _, err := io.ReadFull(r, header[:1]); err != nil {
  97. return nil, fmt.Errorf("cannot read session ID length: %w", err)
  98. }
  99. hello.SessionID = make([]byte, int(header[0]))
  100. if _, err := io.ReadFull(r, hello.SessionID); err != nil {
  101. return nil, fmt.Errorf("cannot read session id: %w", err)
  102. }
  103. if _, err := io.ReadFull(r, header[:]); err != nil {
  104. return nil, fmt.Errorf("cannot read cipher suite length: %w", err)
  105. }
  106. cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
  107. // Pick the first non-GREASE cipher suite from the list.
  108. // Real TLS servers never select GREASE values (RFC 8701, pattern 0x?a?a),
  109. // so echoing them back is a trivial DPI fingerprint.
  110. // cipherSuiteLen is in bytes; each cipher suite is 2 bytes.
  111. for range cipherSuiteLen / 2 {
  112. if _, err := io.ReadFull(r, header[:]); err != nil {
  113. return nil, fmt.Errorf("cannot read cipher suite: %w", err)
  114. }
  115. if hello.CipherSuite != 0 {
  116. // do not forget we have to scan until the end
  117. continue
  118. }
  119. if cs := binary.BigEndian.Uint16(header[:]); cs&GreaseMask != GreaseValueType {
  120. hello.CipherSuite = cs
  121. }
  122. }
  123. if hello.CipherSuite == 0 {
  124. return nil, ErrCannotFindCipher
  125. }
  126. if _, err := io.ReadFull(r, header[:1]); err != nil {
  127. return nil, fmt.Errorf("cannot read compression methods length: %w", err)
  128. }
  129. if _, err := io.CopyN(io.Discard, r, int64(header[0])); err != nil {
  130. return nil, fmt.Errorf("cannot skip compression methods: %w", err)
  131. }
  132. return hello, nil
  133. }
  134. func parseSNI(r io.Reader) ([]string, error) {
  135. header := [2]byte{}
  136. if _, err := io.ReadFull(r, header[:]); err != nil {
  137. return nil, fmt.Errorf("cannot read length of TLS extensions: %w", err)
  138. }
  139. extensionsLength := int64(binary.BigEndian.Uint16(header[:]))
  140. buf := &bytes.Buffer{}
  141. buf.Grow(int(extensionsLength))
  142. if _, err := io.CopyN(buf, r, extensionsLength); err != nil {
  143. return nil, fmt.Errorf("cannot read extensions: %w", err)
  144. }
  145. for buf.Len() > 0 {
  146. // 00 00 - assigned value for extension "server name"
  147. // 00 18 - 0x18 (24) bytes of "server name" extension data follows
  148. // 00 16 - 0x16 (22) bytes of first (and only) list entry follows
  149. // 00 - list entry is type 0x00 "DNS hostname"
  150. // 00 13 - 0x13 (19) bytes of hostname follows
  151. // 65 78 61 ... 6e 65 74 - "example.ulfheim.net"
  152. // 00 00 - assigned value for extension "server name"
  153. extTypeB := buf.Next(2)
  154. if len(extTypeB) != 2 {
  155. return nil, fmt.Errorf("cannot read extension type: %v", extTypeB)
  156. }
  157. // 00 18 - 0x18 (24) bytes of "server name" extension data follows
  158. lengthB := buf.Next(2)
  159. if len(lengthB) != 2 {
  160. return nil, fmt.Errorf("cannot read extension %v length: %v", extTypeB, lengthB)
  161. }
  162. length := int(binary.BigEndian.Uint16(lengthB))
  163. extDataB := buf.Next(length)
  164. if len(extDataB) != length {
  165. return nil, fmt.Errorf("cannot read extension %v data: len %d != %d", extTypeB, length, len(extDataB))
  166. }
  167. if !bytes.Equal(extTypeB, extTypeSNI[:]) {
  168. continue
  169. }
  170. buf.Reset()
  171. buf.Write(extDataB)
  172. // 00 16 - 0x16 (22) bytes of first (and only) list entry follows
  173. lengthB = buf.Next(2)
  174. if len(lengthB) != 2 {
  175. return nil, fmt.Errorf("cannot read the length of the SNI record: %v", lengthB)
  176. }
  177. length = int(binary.BigEndian.Uint16(lengthB))
  178. if length == 0 {
  179. return nil, nil
  180. }
  181. listType, err := buf.ReadByte()
  182. if err != nil {
  183. return nil, fmt.Errorf("cannot read SNI list type: %w", err)
  184. }
  185. // 00 - list entry is type 0x00 "DNS hostname"
  186. if listType != sniDNSNamesListType {
  187. return nil, fmt.Errorf("incorrect SNI list type %#x", listType)
  188. }
  189. names := []string{}
  190. for buf.Len() > 0 {
  191. // 00 13 - 0x13 (19) bytes of hostname follows
  192. lengthB = buf.Next(2)
  193. if len(lengthB) != 2 {
  194. return nil, fmt.Errorf("incorrect length of the hostname: %v", lengthB)
  195. }
  196. length = int(binary.BigEndian.Uint16(lengthB))
  197. name := buf.Next(length)
  198. if len(name) != length {
  199. return nil, fmt.Errorf("incorrect length of SNI hostname: len %d != %d", length, len(name))
  200. }
  201. names = append(names, string(name))
  202. }
  203. return names, nil
  204. }
  205. return nil, nil
  206. }