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_test.go 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. package fake_test
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "encoding/json"
  6. "errors"
  7. "io"
  8. "os"
  9. "testing"
  10. "time"
  11. "github.com/9seconds/mtg/v2/internal/testlib"
  12. "github.com/9seconds/mtg/v2/mtglib"
  13. "github.com/9seconds/mtg/v2/mtglib/internal/tls"
  14. "github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
  15. "github.com/stretchr/testify/mock"
  16. "github.com/stretchr/testify/require"
  17. "github.com/stretchr/testify/suite"
  18. )
  19. const (
  20. TolerateTime = 365 * 30 * 24 * time.Hour
  21. )
  22. type parseClientHelloConnMock struct {
  23. testlib.EssentialsConnMock
  24. readBuf *bytes.Buffer
  25. }
  26. func (m *parseClientHelloConnMock) Read(p []byte) (int, error) {
  27. return m.readBuf.Read(p)
  28. }
  29. type ParseClientHelloTestSuite struct {
  30. suite.Suite
  31. secret mtglib.Secret
  32. readBuf *bytes.Buffer
  33. connMock *parseClientHelloConnMock
  34. }
  35. func (suite *ParseClientHelloTestSuite) SetupSuite() {
  36. parsed, err := mtglib.ParseSecret("ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d")
  37. require.NoError(suite.T(), err)
  38. suite.secret = parsed
  39. }
  40. func (suite *ParseClientHelloTestSuite) SetupTest() {
  41. suite.readBuf = &bytes.Buffer{}
  42. suite.connMock = &parseClientHelloConnMock{
  43. readBuf: suite.readBuf,
  44. }
  45. suite.connMock.
  46. On("SetReadDeadline", mock.AnythingOfType("time.Time")).
  47. Twice().
  48. Return(nil)
  49. }
  50. func (suite *ParseClientHelloTestSuite) TearDownTest() {
  51. suite.connMock.AssertExpectations(suite.T())
  52. }
  53. type ParseClientHello_TLSHeaderTestSuite struct {
  54. ParseClientHelloTestSuite
  55. }
  56. func (suite *ParseClientHello_TLSHeaderTestSuite) TestEmpty() {
  57. suite.connMock.ExpectedCalls = []*mock.Call{}
  58. suite.connMock.
  59. On("SetReadDeadline", mock.AnythingOfType("time.Time")).
  60. Once().
  61. Return(errors.New("fail"))
  62. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  63. suite.ErrorContains(err, "fail")
  64. }
  65. func (suite *ParseClientHello_TLSHeaderTestSuite) TestNothing() {
  66. suite.connMock.ExpectedCalls = []*mock.Call{}
  67. suite.connMock.
  68. On("SetReadDeadline", mock.AnythingOfType("time.Time")).
  69. Twice().
  70. Return(nil)
  71. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  72. suite.ErrorIs(err, io.EOF)
  73. }
  74. func (suite *ParseClientHello_TLSHeaderTestSuite) TestUnknownRecord() {
  75. suite.readBuf.Write([]byte{
  76. 10,
  77. 3, 3,
  78. 0, 0,
  79. })
  80. suite.readBuf.WriteByte(10)
  81. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  82. suite.ErrorContains(err, "unexpected record type 0xa")
  83. }
  84. func (suite *ParseClientHello_TLSHeaderTestSuite) TestUnknownProtocolVersion() {
  85. suite.readBuf.Write([]byte{
  86. tls.TypeHandshake,
  87. 3, 3,
  88. 0, 0,
  89. })
  90. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  91. suite.ErrorContains(err, "unexpected protocol version")
  92. }
  93. func (suite *ParseClientHello_TLSHeaderTestSuite) TestCannotReadRestOfRecord() {
  94. suite.readBuf.Write([]byte{
  95. tls.TypeHandshake,
  96. 3, 1,
  97. 0, 10,
  98. })
  99. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  100. suite.ErrorIs(err, io.EOF)
  101. }
  102. type ParseClientHelloHandshakeTestSuite struct {
  103. ParseClientHelloTestSuite
  104. }
  105. func (suite *ParseClientHelloHandshakeTestSuite) SetupTest() {
  106. suite.ParseClientHelloTestSuite.SetupTest()
  107. suite.readBuf.Write([]byte{
  108. tls.TypeHandshake,
  109. 3, 1,
  110. 0,
  111. })
  112. }
  113. func (suite *ParseClientHelloHandshakeTestSuite) TestCannotReadHeader() {
  114. suite.readBuf.Write([]byte{
  115. 1,
  116. 10,
  117. })
  118. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  119. suite.ErrorContains(err, "cannot read handshake header")
  120. }
  121. func (suite *ParseClientHelloHandshakeTestSuite) TestIncorrectHandshakeType() {
  122. suite.readBuf.Write([]byte{
  123. 4,
  124. 10, 0, 0, 0,
  125. })
  126. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  127. suite.ErrorContains(err, "incorrect handshake type")
  128. }
  129. func (suite *ParseClientHelloHandshakeTestSuite) TestCannotReadHandshake() {
  130. suite.readBuf.Write([]byte{
  131. 4 + 3,
  132. 10, 0, 0, 0,
  133. })
  134. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  135. suite.ErrorIs(err, io.EOF)
  136. }
  137. type ParseClientHelloHandshakeBodyTestSuite struct {
  138. ParseClientHelloTestSuite
  139. }
  140. func (suite *ParseClientHelloHandshakeBodyTestSuite) SetupTest() {
  141. suite.ParseClientHelloTestSuite.SetupTest()
  142. suite.readBuf.Write([]byte{
  143. tls.TypeHandshake,
  144. 3, 1,
  145. 0,
  146. })
  147. }
  148. func (suite *ParseClientHelloHandshakeBodyTestSuite) writeBody(body []byte) {
  149. suite.readBuf.WriteByte(byte(4 + len(body)))
  150. suite.readBuf.Write([]byte{
  151. fake.TypeHandshakeClient,
  152. 0, 0, byte(len(body)),
  153. })
  154. suite.readBuf.Write(body)
  155. }
  156. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadVersion() {
  157. suite.writeBody(nil)
  158. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  159. suite.ErrorContains(err, "cannot read client version")
  160. }
  161. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadRandom() {
  162. suite.writeBody([]byte{3, 3})
  163. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  164. suite.ErrorContains(err, "cannot read client random")
  165. }
  166. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadSessionIDLength() {
  167. body := make([]byte, 2+fake.RandomLen)
  168. suite.writeBody(body)
  169. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  170. suite.ErrorContains(err, "cannot read session ID length")
  171. }
  172. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadSessionID() {
  173. body := make([]byte, 2+fake.RandomLen+1)
  174. body[2+fake.RandomLen] = 32
  175. suite.writeBody(body)
  176. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  177. suite.ErrorContains(err, "cannot read session id")
  178. }
  179. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCipherSuiteLength() {
  180. body := make([]byte, 2+fake.RandomLen+1)
  181. suite.writeBody(body)
  182. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  183. suite.ErrorContains(err, "cannot read cipher suite length")
  184. }
  185. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadFirstCipherSuite() {
  186. body := make([]byte, 2+fake.RandomLen+1+2)
  187. suite.writeBody(body)
  188. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  189. suite.ErrorContains(err, "cannot read first cipher suite")
  190. }
  191. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipRemainingCipherSuites() {
  192. body := make([]byte, 2+fake.RandomLen+1+2+2)
  193. binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 4)
  194. suite.writeBody(body)
  195. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  196. suite.ErrorContains(err, "cannot skip remaining cipher suites")
  197. }
  198. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMethodsLength() {
  199. body := make([]byte, 2+fake.RandomLen+1+2+2)
  200. binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
  201. suite.writeBody(body)
  202. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  203. suite.ErrorContains(err, "cannot read compression methods length")
  204. }
  205. func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipCompressionMethods() {
  206. body := make([]byte, 2+fake.RandomLen+1+2+2+1)
  207. binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
  208. body[2+fake.RandomLen+1+2+2] = 1
  209. suite.writeBody(body)
  210. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  211. suite.ErrorContains(err, "cannot skip compression methods")
  212. }
  213. type ParseClientHelloSNITestSuite struct {
  214. ParseClientHelloTestSuite
  215. }
  216. func (suite *ParseClientHelloSNITestSuite) SetupTest() {
  217. suite.ParseClientHelloTestSuite.SetupTest()
  218. suite.readBuf.Write([]byte{
  219. tls.TypeHandshake,
  220. 3, 1,
  221. 0,
  222. })
  223. }
  224. func (suite *ParseClientHelloSNITestSuite) writeExtensions(extensions []byte) {
  225. handshakeBodyLen := 41 + len(extensions)
  226. suite.readBuf.WriteByte(byte(4 + handshakeBodyLen))
  227. suite.readBuf.Write([]byte{
  228. fake.TypeHandshakeClient,
  229. 0, 0, byte(handshakeBodyLen),
  230. })
  231. // version(2) + random(32) + sessionIDLen(1) + cipherSuiteLen(2) +
  232. // cipherSuite(2) + compressionLen(1) + compression(1) = 41
  233. body := make([]byte, 41)
  234. binary.BigEndian.PutUint16(body[35:], 2)
  235. body[39] = 1
  236. suite.readBuf.Write(body)
  237. suite.readBuf.Write(extensions)
  238. }
  239. func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionsLength() {
  240. suite.writeExtensions(nil)
  241. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  242. suite.ErrorContains(err, "cannot read length of TLS extensions")
  243. }
  244. func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensions() {
  245. suite.writeExtensions([]byte{0, 10})
  246. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  247. suite.ErrorContains(err, "cannot read extensions")
  248. }
  249. func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionType() {
  250. suite.writeExtensions([]byte{0, 1, 0xAB})
  251. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  252. suite.ErrorContains(err, "cannot read extension type")
  253. }
  254. func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionLength() {
  255. suite.writeExtensions([]byte{0, 2, 0xFF, 0xFF})
  256. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  257. suite.ErrorContains(err, "length:")
  258. }
  259. func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionData() {
  260. suite.writeExtensions([]byte{0, 4, 0xFF, 0xFF, 0, 5})
  261. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  262. suite.ErrorContains(err, "data: len")
  263. }
  264. func (suite *ParseClientHelloSNITestSuite) TestCannotReadSNIRecordLength() {
  265. suite.writeExtensions([]byte{0, 5, 0, 0, 0, 1, 0xAB})
  266. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  267. suite.ErrorContains(err, "cannot read the length of the SNI record")
  268. }
  269. func (suite *ParseClientHelloSNITestSuite) TestCannotReadSNIListType() {
  270. suite.writeExtensions([]byte{0, 6, 0, 0, 0, 2, 0, 1})
  271. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  272. suite.ErrorContains(err, "cannot read SNI list type")
  273. }
  274. func (suite *ParseClientHelloSNITestSuite) TestIncorrectSNIListType() {
  275. suite.writeExtensions([]byte{0, 7, 0, 0, 0, 3, 0, 1, 5})
  276. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  277. suite.ErrorContains(err, "incorrect SNI list type")
  278. }
  279. func (suite *ParseClientHelloSNITestSuite) TestCannotReadHostnameLength() {
  280. suite.writeExtensions([]byte{0, 8, 0, 0, 0, 4, 0, 2, 0, 0xAB})
  281. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  282. suite.ErrorContains(err, "incorrect length of the hostname")
  283. }
  284. func (suite *ParseClientHelloSNITestSuite) TestCannotReadHostname() {
  285. suite.writeExtensions([]byte{0, 9, 0, 0, 0, 5, 0, 3, 0, 0, 5})
  286. _, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
  287. suite.ErrorContains(err, "incorrect length of SNI hostname")
  288. }
  289. func TestParseClientHelloTLSHeader(t *testing.T) {
  290. t.Parallel()
  291. suite.Run(t, &ParseClientHello_TLSHeaderTestSuite{})
  292. }
  293. func TestParseClientHelloHandshake(t *testing.T) {
  294. t.Parallel()
  295. suite.Run(t, &ParseClientHelloHandshakeTestSuite{})
  296. }
  297. func TestParseClientHelloHandshakeBody(t *testing.T) {
  298. t.Parallel()
  299. suite.Run(t, &ParseClientHelloHandshakeBodyTestSuite{})
  300. }
  301. func TestParseClientHelloSNI(t *testing.T) {
  302. t.Parallel()
  303. suite.Run(t, &ParseClientHelloSNITestSuite{})
  304. }
  305. // fragmentTLSRecord splits a single TLS record into n TLS records by
  306. // dividing the payload into roughly equal parts. Each part gets its own
  307. // TLS record header with the same record type and version.
  308. func fragmentTLSRecord(t testing.TB, full []byte, n int) []byte {
  309. t.Helper()
  310. recordType := full[0]
  311. version := full[1:3]
  312. payload := full[tls.SizeHeader:]
  313. chunkSize := len(payload) / n
  314. result := &bytes.Buffer{}
  315. for i := 0; i < n; i++ {
  316. start := i * chunkSize
  317. end := start + chunkSize
  318. if i == n-1 {
  319. end = len(payload)
  320. }
  321. chunk := payload[start:end]
  322. result.WriteByte(recordType)
  323. result.Write(version)
  324. require.NoError(t, binary.Write(result, binary.BigEndian, uint16(len(chunk))))
  325. result.Write(chunk)
  326. }
  327. return result.Bytes()
  328. }
  329. // splitPayloadAt creates two TLS records from a single record by splitting
  330. // the payload at the given byte position.
  331. func splitPayloadAt(t testing.TB, full []byte, pos int) []byte {
  332. t.Helper()
  333. payload := full[tls.SizeHeader:]
  334. buf := &bytes.Buffer{}
  335. buf.WriteByte(tls.TypeHandshake)
  336. buf.Write(full[1:3])
  337. require.NoError(t, binary.Write(buf, binary.BigEndian, uint16(pos)))
  338. buf.Write(payload[:pos])
  339. buf.WriteByte(tls.TypeHandshake)
  340. buf.Write(full[1:3])
  341. require.NoError(t, binary.Write(buf, binary.BigEndian, uint16(len(payload)-pos)))
  342. buf.Write(payload[pos:])
  343. return buf.Bytes()
  344. }
  345. type ParseClientHelloFragmentedTestSuite struct {
  346. suite.Suite
  347. secret mtglib.Secret
  348. snapshot *clientHelloSnapshot
  349. }
  350. func (s *ParseClientHelloFragmentedTestSuite) SetupSuite() {
  351. parsed, err := mtglib.ParseSecret(
  352. "ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d",
  353. )
  354. require.NoError(s.T(), err)
  355. s.secret = parsed
  356. fileData, err := os.ReadFile("testdata/client-hello-ok-19dfe38384b9884b.json")
  357. require.NoError(s.T(), err)
  358. s.snapshot = &clientHelloSnapshot{}
  359. require.NoError(s.T(), json.Unmarshal(fileData, s.snapshot))
  360. }
  361. func (s *ParseClientHelloFragmentedTestSuite) makeConn(data []byte) *parseClientHelloConnMock {
  362. readBuf := &bytes.Buffer{}
  363. readBuf.Write(data)
  364. connMock := &parseClientHelloConnMock{
  365. readBuf: readBuf,
  366. }
  367. connMock.
  368. On("SetReadDeadline", mock.AnythingOfType("time.Time")).
  369. Twice().
  370. Return(nil)
  371. return connMock
  372. }
  373. func (s *ParseClientHelloFragmentedTestSuite) TestReassemblySuccess() {
  374. full := s.snapshot.GetFull()
  375. tests := []struct {
  376. name string
  377. data []byte
  378. }{
  379. {"two equal fragments", fragmentTLSRecord(s.T(), full, 2)},
  380. {"three equal fragments", fragmentTLSRecord(s.T(), full, 3)},
  381. {"single byte first fragment", splitPayloadAt(s.T(), full, 1)},
  382. {"three byte first fragment", splitPayloadAt(s.T(), full, 3)},
  383. }
  384. for _, tt := range tests {
  385. s.Run(tt.name, func() {
  386. connMock := s.makeConn(tt.data)
  387. defer connMock.AssertExpectations(s.T())
  388. hello, err := fake.ReadClientHello(
  389. connMock,
  390. s.secret.Key[:],
  391. s.secret.Host,
  392. TolerateTime,
  393. )
  394. s.Require().NoError(err)
  395. s.Equal(s.snapshot.GetRandom(), hello.Random[:])
  396. s.Equal(s.snapshot.GetSessionID(), hello.SessionID)
  397. s.Equal(uint16(s.snapshot.CipherSuite), hello.CipherSuite)
  398. })
  399. }
  400. }
  401. func (s *ParseClientHelloFragmentedTestSuite) TestReassemblyErrors() {
  402. full := s.snapshot.GetFull()
  403. payload := full[tls.SizeHeader:]
  404. tests := []struct {
  405. name string
  406. buildData func() []byte
  407. errMsg string
  408. }{
  409. {
  410. name: "wrong continuation record type",
  411. buildData: func() []byte {
  412. buf := &bytes.Buffer{}
  413. buf.WriteByte(tls.TypeHandshake)
  414. buf.Write(full[1:3])
  415. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(10)))
  416. buf.Write(payload[:10])
  417. // Wrong type: application data instead of handshake
  418. buf.WriteByte(tls.TypeApplicationData)
  419. buf.Write(full[1:3])
  420. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(len(payload)-10)))
  421. buf.Write(payload[10:])
  422. return buf.Bytes()
  423. },
  424. errMsg: "unexpected continuation record type",
  425. },
  426. {
  427. name: "too many continuation records",
  428. buildData: func() []byte {
  429. // Handshake header claiming 256 bytes, but we only send 1 byte per continuation
  430. handshakePayload := []byte{0x01, 0x00, 0x01, 0x00}
  431. buf := &bytes.Buffer{}
  432. buf.WriteByte(tls.TypeHandshake)
  433. buf.Write([]byte{3, 1})
  434. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(len(handshakePayload))))
  435. buf.Write(handshakePayload)
  436. for range 11 {
  437. buf.WriteByte(tls.TypeHandshake)
  438. buf.Write([]byte{3, 1})
  439. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(1)))
  440. buf.WriteByte(0xAB)
  441. }
  442. return buf.Bytes()
  443. },
  444. errMsg: "too many continuation records",
  445. },
  446. {
  447. name: "zero-length continuation record",
  448. buildData: func() []byte {
  449. buf := &bytes.Buffer{}
  450. buf.WriteByte(tls.TypeHandshake)
  451. buf.Write(full[1:3])
  452. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(10)))
  453. buf.Write(payload[:10])
  454. // Valid header but zero-length payload
  455. buf.WriteByte(tls.TypeHandshake)
  456. buf.Write(full[1:3])
  457. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(0)))
  458. return buf.Bytes()
  459. },
  460. errMsg: "zero-length continuation record",
  461. },
  462. {
  463. name: "wrong continuation record version",
  464. buildData: func() []byte {
  465. buf := &bytes.Buffer{}
  466. buf.WriteByte(tls.TypeHandshake)
  467. buf.Write(full[1:3])
  468. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(10)))
  469. buf.Write(payload[:10])
  470. // Wrong version: 3.3 instead of 3.1
  471. buf.WriteByte(tls.TypeHandshake)
  472. buf.Write([]byte{3, 3})
  473. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(len(payload)-10)))
  474. buf.Write(payload[10:])
  475. return buf.Bytes()
  476. },
  477. errMsg: "unexpected continuation record version",
  478. },
  479. {
  480. name: "handshake message too large",
  481. buildData: func() []byte {
  482. // Handshake header claiming 0x010000 (65536) bytes — exceeds 0xFFFF limit
  483. handshakePayload := []byte{0x01, 0x01, 0x00, 0x00}
  484. buf := &bytes.Buffer{}
  485. buf.WriteByte(tls.TypeHandshake)
  486. buf.Write([]byte{3, 1})
  487. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(len(handshakePayload))))
  488. buf.Write(handshakePayload)
  489. return buf.Bytes()
  490. },
  491. errMsg: "handshake message too large",
  492. },
  493. {
  494. name: "truncated continuation record header",
  495. buildData: func() []byte {
  496. buf := &bytes.Buffer{}
  497. buf.WriteByte(tls.TypeHandshake)
  498. buf.Write(full[1:3])
  499. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(10)))
  500. buf.Write(payload[:10])
  501. // Connection ends mid-header (only 2 bytes)
  502. buf.WriteByte(tls.TypeHandshake)
  503. buf.WriteByte(3)
  504. return buf.Bytes()
  505. },
  506. errMsg: "cannot read continuation record header",
  507. },
  508. {
  509. name: "truncated continuation record payload",
  510. buildData: func() []byte {
  511. buf := &bytes.Buffer{}
  512. buf.WriteByte(tls.TypeHandshake)
  513. buf.Write(full[1:3])
  514. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(10)))
  515. buf.Write(payload[:10])
  516. // Claims 100 bytes but no payload follows
  517. buf.WriteByte(tls.TypeHandshake)
  518. buf.Write(full[1:3])
  519. require.NoError(s.T(), binary.Write(buf, binary.BigEndian, uint16(100)))
  520. return buf.Bytes()
  521. },
  522. errMsg: "cannot read continuation record payload",
  523. },
  524. }
  525. for _, tt := range tests {
  526. s.Run(tt.name, func() {
  527. connMock := s.makeConn(tt.buildData())
  528. defer connMock.AssertExpectations(s.T())
  529. _, err := fake.ReadClientHello(
  530. connMock,
  531. s.secret.Key[:],
  532. s.secret.Host,
  533. TolerateTime,
  534. )
  535. s.ErrorContains(err, tt.errMsg)
  536. })
  537. }
  538. }
  539. func TestParseClientHelloFragmented(t *testing.T) {
  540. t.Parallel()
  541. suite.Run(t, &ParseClientHelloFragmentedTestSuite{})
  542. }