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
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

client_side_test.go 23KB

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