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_hello.go 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package faketls
  2. import (
  3. "crypto/hmac"
  4. "crypto/sha256"
  5. "crypto/subtle"
  6. "encoding/binary"
  7. "fmt"
  8. "time"
  9. "github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
  10. )
  11. type ClientHello struct {
  12. Time time.Time
  13. Random [RandomLen]byte
  14. SessionID []byte
  15. Host string
  16. CipherSuite uint16
  17. }
  18. func ParseClientHello(secret, handshake []byte) (ClientHello, error) {
  19. hello := ClientHello{}
  20. if len(handshake) < ClientHelloMinLen {
  21. return hello, fmt.Errorf("lengh of handshake is too small: %d", len(handshake))
  22. }
  23. if handshake[0] != HandshakeTypeClient {
  24. return hello, fmt.Errorf("unknown handshake type %#x", handshake[0])
  25. }
  26. handshakeSizeBytes := [4]byte{0, handshake[1], handshake[2], handshake[3]}
  27. handshakeLength := binary.BigEndian.Uint32(handshakeSizeBytes[:])
  28. if len(handshake)-4 != int(handshakeLength) {
  29. return hello,
  30. fmt.Errorf("incorrect handshake size. manifested=%d, real=%d",
  31. handshakeLength, len(handshake)-4) // nolint: gomnd
  32. }
  33. copy(hello.Random[:], handshake[ClientHelloRandomOffset:])
  34. copy(handshake[ClientHelloRandomOffset:], clientHelloEmptyRandom)
  35. rec := record.AcquireRecord()
  36. defer record.ReleaseRecord(rec)
  37. rec.Type = record.TypeHandshake
  38. rec.Version = record.Version10
  39. rec.Payload.Write(handshake)
  40. // mac is calculated for the whole record, not only
  41. // for the payload part
  42. mac := hmac.New(sha256.New, secret)
  43. rec.Dump(mac) // nolint: errcheck
  44. computedRandom := mac.Sum(nil)
  45. for i := 0; i < RandomLen; i++ {
  46. computedRandom[i] ^= hello.Random[i]
  47. }
  48. if subtle.ConstantTimeCompare(clientHelloEmptyRandom[:RandomLen-4], computedRandom[:RandomLen-4]) != 1 {
  49. return hello, ErrBadDigest
  50. }
  51. timestamp := int64(binary.LittleEndian.Uint32(computedRandom[RandomLen-4:]))
  52. hello.Time = time.Unix(timestamp, 0)
  53. parseSessionID(&hello, handshake)
  54. parseCipherSuite(&hello, handshake)
  55. parseSNI(&hello, handshake)
  56. return hello, nil
  57. }
  58. func parseSessionID(hello *ClientHello, handshake []byte) {
  59. hello.SessionID = make([]byte, handshake[ClientHelloSessionIDOffset])
  60. copy(hello.SessionID, handshake[ClientHelloSessionIDOffset+1:])
  61. }
  62. func parseCipherSuite(hello *ClientHello, handshake []byte) {
  63. cipherSuiteOffset := ClientHelloSessionIDOffset + len(hello.SessionID) + 3 // nolint: gomnd
  64. hello.CipherSuite = binary.BigEndian.Uint16(handshake[cipherSuiteOffset : cipherSuiteOffset+2])
  65. }
  66. func parseSNI(hello *ClientHello, handshake []byte) {
  67. cipherSuiteOffset := ClientHelloSessionIDOffset + len(hello.SessionID) + 1
  68. handshake = handshake[cipherSuiteOffset:]
  69. cipherSuiteLength := binary.BigEndian.Uint16(handshake[:2])
  70. handshake = handshake[2+cipherSuiteLength:]
  71. compressionMethodsLength := int(handshake[0])
  72. handshake = handshake[1+compressionMethodsLength:]
  73. extensionsLength := binary.BigEndian.Uint16(handshake[:2])
  74. handshake = handshake[2 : 2+extensionsLength]
  75. for len(handshake) > 0 {
  76. if binary.BigEndian.Uint16(handshake[:2]) != ExtensionSNI {
  77. extensionsLength := binary.BigEndian.Uint16(handshake[2:4])
  78. handshake = handshake[4+extensionsLength:]
  79. continue
  80. }
  81. hostnameLength := binary.BigEndian.Uint16(handshake[7:9])
  82. handshake = handshake[9:]
  83. hello.Host = string(handshake[:int(hostnameLength)])
  84. return
  85. }
  86. }