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.1KB

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