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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package mtglib
  2. import (
  3. "crypto/rand"
  4. "encoding/base64"
  5. "encoding/hex"
  6. "fmt"
  7. )
  8. const secretFakeTLSFirstByte byte = 0xee
  9. var secretEmptyKey [SecretKeyLength]byte
  10. // Secret is a data structure that presents a secret.
  11. //
  12. // Telegram secret is not a simple string like
  13. // "ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d".
  14. // Actually, this is a serialized datastructure of 2 parts: key and host.
  15. //
  16. // ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d
  17. // |-|-------------------------------|-------------------------------------------
  18. // p key hostname
  19. //
  20. // Serialized secret starts with 'ee'. Actually, in the past we also had
  21. // 'dd' secrets and prefixless ones. But this is history. Currently,
  22. // we do have only 'ee' secrets which mean faketls + protection from
  23. // statistical attacks on a length. 'ee' is a byte 238 (0xee).
  24. //
  25. // After that, we have 16 bytes of the key. This is a random generated
  26. // secret data of the proxy and this data is used to derive
  27. // authentication schemas. These secrets are mixed into hmacs and sha256
  28. // checksums which are used to build AEAD ciphers for obfuscated2
  29. // protocol and ensure faketls handshake.
  30. //
  31. // Host is a domain fronting hostname in latin1 (ASCII) encoding. This
  32. // hostname should be used for SNI in faketls and MTG verifies it. Also,
  33. // this is when mtg gets about a domain fronting hostname.
  34. //
  35. // Secrets can be serialized into 2 forms: hex and base64. If
  36. // you decode both forms into bytes, you'll get the same byte array.
  37. // Telegram clients nowadays accept all forms.
  38. type Secret struct {
  39. // Key is a set of bytes used for traffic authentication.
  40. Key [SecretKeyLength]byte
  41. // Host is a domain fronting hostname.
  42. Host string
  43. }
  44. // MarshalText is to support text.Marshaller interface.
  45. func (s Secret) MarshalText() ([]byte, error) {
  46. if s.Valid() {
  47. return []byte(s.String()), nil
  48. }
  49. return nil, nil
  50. }
  51. // UnmarshalText is to support text.Unmarshaller interface.
  52. func (s *Secret) UnmarshalText(data []byte) error {
  53. return s.Set(string(data))
  54. }
  55. func (s *Secret) Set(text string) error {
  56. if text == "" {
  57. return ErrSecretEmpty
  58. }
  59. decoded, err := hex.DecodeString(text)
  60. if err != nil {
  61. decoded, err = base64.RawURLEncoding.DecodeString(text)
  62. }
  63. if err != nil {
  64. return fmt.Errorf("incorrect secret format: %w", err)
  65. }
  66. if len(decoded) < 2 { // nolint: gomnd // we need at least 1 byte here
  67. return fmt.Errorf("secret is truncated, length=%d", len(decoded))
  68. }
  69. if decoded[0] != secretFakeTLSFirstByte {
  70. return fmt.Errorf("incorrect first byte of secret: %#x", decoded[0])
  71. }
  72. decoded = decoded[1:]
  73. if len(decoded) < SecretKeyLength {
  74. return fmt.Errorf("secret has incorrect length %d", len(decoded))
  75. }
  76. copy(s.Key[:], decoded[:SecretKeyLength])
  77. s.Host = string(decoded[SecretKeyLength:])
  78. if s.Host == "" {
  79. return fmt.Errorf("hostname cannot be empty: %s", text)
  80. }
  81. return nil
  82. }
  83. // Valid checks if this secret is valid and can be used in proxy.
  84. func (s Secret) Valid() bool {
  85. return s.Key != secretEmptyKey && s.Host != ""
  86. }
  87. // String is to support fmt.Stringer interface.
  88. func (s Secret) String() string {
  89. return s.Base64()
  90. }
  91. // Base64 returns a base64-encoded form of this secret.
  92. func (s Secret) Base64() string {
  93. return base64.RawURLEncoding.EncodeToString(s.makeBytes())
  94. }
  95. // Hex returns a hex-encoded form of this secret (ee-secret).
  96. func (s Secret) Hex() string {
  97. return hex.EncodeToString(s.makeBytes())
  98. }
  99. func (s *Secret) makeBytes() []byte {
  100. data := append([]byte{secretFakeTLSFirstByte}, s.Key[:]...)
  101. data = append(data, s.Host...)
  102. return data
  103. }
  104. // GenerateSecret makes a new secret with a given hostname.
  105. func GenerateSecret(hostname string) Secret {
  106. s := Secret{
  107. Host: hostname,
  108. }
  109. if _, err := rand.Read(s.Key[:]); err != nil {
  110. panic(err)
  111. }
  112. return s
  113. }
  114. // ParseSecret parses a secret (both hex and base64 forms).
  115. func ParseSecret(secret string) (Secret, error) {
  116. s := Secret{}
  117. return s, s.UnmarshalText([]byte(secret))
  118. }