Przeglądaj źródła

ReadClientHello function

tags/v2.2.0^2^2
9seconds 1 miesiąc temu
rodzic
commit
59557059df

+ 1
- 1
.mise.toml Wyświetl plik

@@ -48,7 +48,7 @@ depends = [
48 48
 
49 49
 [tasks."test:fuzz:client-hello"]
50 50
 description = "Run fuzzy test for ClientHello"
51
-run = "go test -v {{ vars.fuzzflags }} -fuzz=FuzzClientHello ./mtglib/internal/faketls"
51
+run = "go test -v {{ vars.fuzzflags }} -fuzz=FuzzReadClientHello ./mtglib/internal/tls/fake"
52 52
 
53 53
 [tasks."test:fuzz:client-handshake"]
54 54
 description = "Run fuzzy test for ClientHandshake"

+ 305
- 0
mtglib/internal/tls/fake/client_side.go Wyświetl plik

@@ -0,0 +1,305 @@
1
+package fake
2
+
3
+import (
4
+	"bytes"
5
+	"crypto/hmac"
6
+	"crypto/sha256"
7
+	"crypto/subtle"
8
+	"encoding/binary"
9
+	"fmt"
10
+	"io"
11
+	"net"
12
+	"slices"
13
+	"time"
14
+
15
+	"github.com/9seconds/mtg/v2/mtglib"
16
+	"github.com/9seconds/mtg/v2/mtglib/internal/tls"
17
+)
18
+
19
+const (
20
+	TypeHandshakeClient = 0x01
21
+
22
+	RandomLen = 32
23
+
24
+	// record_type(1) + version(2) + size(2) + handshake_type(1) + uint24_length(3) + client_version(2)
25
+	clientRandomOffset = 1 + 2 + 2 + 1 + 3 + 2
26
+	sniDNSNamesListType = 0
27
+)
28
+
29
+var (
30
+	emptyRandom = [RandomLen]byte{}
31
+	extTypeSNI  = [2]byte{}
32
+)
33
+
34
+type ClientHello struct {
35
+	Random      [RandomLen]byte
36
+	SessionID   []byte
37
+	CipherSuite uint16
38
+}
39
+
40
+func ReadClientHello(conn net.Conn, secret mtglib.Secret, tolerateTimeSkewness time.Duration) (*ClientHello, error) {
41
+	if err := conn.SetReadDeadline(time.Now().Add(ClientHelloReadTimeout)); err != nil {
42
+		return nil, fmt.Errorf("cannot set read deadline: %w", err)
43
+	}
44
+	defer conn.SetReadDeadline(resetDeadline) //nolint: errcheck
45
+
46
+	// This is how FakeTLS is organized:
47
+	//  1. We create sha256 HMAC with a given secret
48
+	//  2. We dump there a whole TLS frame except of the fact that random
49
+	//     is filled with all zeroes
50
+	//  3. Digest is computed. This digest should be XORed with
51
+	//     original client random
52
+	//  4. New digest should be all 0 except of last 4 bytes
53
+	//  5. Last 4 bytes are little endian uint32 of UNIX timestamp when
54
+	//     this message was created.
55
+	handshakeCopyBuf := &bytes.Buffer{}
56
+	reader := io.TeeReader(conn, handshakeCopyBuf)
57
+
58
+	reader, err := parseTLSHeader(reader)
59
+	if err != nil {
60
+		return nil, fmt.Errorf("cannot parse tls header: %w", err)
61
+	}
62
+
63
+	reader, err = parseHandshakeHeader(reader)
64
+	if err != nil {
65
+		return nil, fmt.Errorf("cannot parse handshake header: %w", err)
66
+	}
67
+
68
+	hello, err := parseHandshake(reader)
69
+	if err != nil {
70
+		return nil, fmt.Errorf("cannot parse handshake: %w", err)
71
+	}
72
+
73
+	sniHostnames, err := parseSNI(reader)
74
+	if err != nil {
75
+		return nil, fmt.Errorf("cannot parse SNI: %w", err)
76
+	}
77
+
78
+	if !slices.Contains(sniHostnames, secret.Host) {
79
+		return nil, fmt.Errorf("cannot find %s in %v", secret.Host, sniHostnames)
80
+	}
81
+
82
+	digest := hmac.New(sha256.New, secret.Key[:])
83
+	// we write a copy of the handshake with client random all nullified.
84
+	digest.Write(handshakeCopyBuf.Next(clientRandomOffset))
85
+	handshakeCopyBuf.Next(RandomLen)
86
+	digest.Write(emptyRandom[:])
87
+	digest.Write(handshakeCopyBuf.Bytes())
88
+
89
+	computed := digest.Sum(nil)
90
+
91
+	for i := range RandomLen {
92
+		computed[i] ^= hello.Random[i]
93
+	}
94
+
95
+	if subtle.ConstantTimeCompare(emptyRandom[:RandomLen-4], computed[:RandomLen-4]) != 1 {
96
+		return nil, ErrBadDigest
97
+	}
98
+
99
+	timestamp := int64(binary.LittleEndian.Uint32(computed[RandomLen-4:]))
100
+	createdAt := time.Unix(timestamp, 0)
101
+
102
+	if tdiff := time.Since(createdAt).Abs(); tdiff > tolerateTimeSkewness {
103
+		return nil, fmt.Errorf("timestamp %q is too old %s", createdAt, tdiff)
104
+	}
105
+
106
+	return hello, nil
107
+}
108
+
109
+func parseTLSHeader(r io.Reader) (io.Reader, error) {
110
+	// record_type(1) + version(2) + size(2)
111
+	//   16 - type is 0x16 (handshake record)
112
+	//   03 01 - protocol version is "3,1" (also known as TLS 1.0)
113
+	//   00 f8 - 0xF8 (248) bytes of handshake message follows
114
+	header := [1 + 2 + 2]byte{}
115
+
116
+	if _, err := io.ReadFull(r, header[:]); err != nil {
117
+		return nil, fmt.Errorf("cannot read record header: %w", err)
118
+	}
119
+
120
+	if header[0] != tls.TypeHandshake {
121
+		return nil, fmt.Errorf("unexpected record type %#x", header[0])
122
+	}
123
+
124
+	if header[1] != 3 || header[2] != 1 {
125
+		return nil, fmt.Errorf("unexpected protocol version %#x %#x", header[1], header[2])
126
+	}
127
+
128
+	length := int64(binary.BigEndian.Uint16(header[3:]))
129
+	buf := &bytes.Buffer{}
130
+
131
+	_, err := io.CopyN(buf, r, length)
132
+
133
+	return buf, err
134
+}
135
+
136
+func parseHandshakeHeader(r io.Reader) (io.Reader, error) {
137
+	// type(1) + size(3 / uint24)
138
+	// 01 - handshake message type 0x01 (client hello)
139
+	// 00 00 f4 - 0xF4 (244) bytes of client hello data follows
140
+	header := [1 + 3]byte{}
141
+
142
+	if _, err := io.ReadFull(r, header[:]); err != nil {
143
+		return nil, fmt.Errorf("cannot read handshake header: %w", err)
144
+	}
145
+
146
+	if header[0] != TypeHandshakeClient {
147
+		return nil, fmt.Errorf("incorrect handshake type: %#x", header[0])
148
+	}
149
+
150
+	// unfortunately there is not uint24 in golang, so we just reust header
151
+	header[0] = 0
152
+
153
+	length := int64(binary.BigEndian.Uint32(header[:]))
154
+	buf := &bytes.Buffer{}
155
+
156
+	_, err := io.CopyN(buf, r, length)
157
+
158
+	return buf, err
159
+}
160
+
161
+func parseHandshake(r io.Reader) (*ClientHello, error) {
162
+	//  A protocol version of "3,3" (meaning TLS 1.2) is given.
163
+	header := [2]byte{}
164
+
165
+	if _, err := io.ReadFull(r, header[:]); err != nil {
166
+		return nil, fmt.Errorf("cannot read client version: %w", err)
167
+	}
168
+
169
+	hello := &ClientHello{}
170
+
171
+	if _, err := io.ReadFull(r, hello.Random[:]); err != nil {
172
+		return nil, fmt.Errorf("cannot read client random: %w", err)
173
+	}
174
+
175
+	if _, err := io.ReadFull(r, header[:1]); err != nil {
176
+		return nil, fmt.Errorf("cannot read session ID length: %w", err)
177
+	}
178
+
179
+	hello.SessionID = make([]byte, int(header[0]))
180
+
181
+	if _, err := io.ReadFull(r, hello.SessionID); err != nil {
182
+		return nil, fmt.Errorf("cannot read session id: %w", err)
183
+	}
184
+
185
+	if _, err := io.ReadFull(r, header[:]); err != nil {
186
+		return nil, fmt.Errorf("cannot read cipher suite length: %w", err)
187
+	}
188
+
189
+	cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
190
+
191
+	// we do not care about picking up any cipher. we pick the first one,
192
+	// so it is always should be present.
193
+	if _, err := io.ReadFull(r, header[:]); err != nil {
194
+		return nil, fmt.Errorf("cannot read first cipher suite: %w", err)
195
+	}
196
+
197
+	hello.CipherSuite = binary.BigEndian.Uint16(header[:])
198
+
199
+	if _, err := io.CopyN(io.Discard, r, cipherSuiteLen-2); err != nil {
200
+		return nil, fmt.Errorf("cannot skip remaining cipher suites: %w", err)
201
+	}
202
+
203
+	if _, err := io.ReadFull(r, header[:1]); err != nil {
204
+		return nil, fmt.Errorf("cannot read compression methods length: %w", err)
205
+	}
206
+
207
+	if _, err := io.CopyN(io.Discard, r, int64(header[0])); err != nil {
208
+		return nil, fmt.Errorf("cannot skip compression methods: %w", err)
209
+	}
210
+
211
+	return hello, nil
212
+}
213
+
214
+func parseSNI(r io.Reader) ([]string, error) {
215
+	header := [2]byte{}
216
+
217
+	if _, err := io.ReadFull(r, header[:]); err != nil {
218
+		return nil, fmt.Errorf("cannot read length of TLS extensions: %w", err)
219
+	}
220
+
221
+	extensionsLength := int64(binary.BigEndian.Uint16(header[:]))
222
+	buf := &bytes.Buffer{}
223
+	buf.Grow(int(extensionsLength))
224
+
225
+	if _, err := io.CopyN(buf, r, extensionsLength); err != nil {
226
+		return nil, fmt.Errorf("cannot read extensions: %w", err)
227
+	}
228
+
229
+	for buf.Len() > 0 {
230
+		// 00 00 - assigned value for extension "server name"
231
+		// 00 18 - 0x18 (24) bytes of "server name" extension data follows
232
+		// 00 16 - 0x16 (22) bytes of first (and only) list entry follows
233
+		// 00 - list entry is type 0x00 "DNS hostname"
234
+		// 00 13 - 0x13 (19) bytes of hostname follows
235
+		// 65 78 61 ... 6e 65 74 - "example.ulfheim.net"
236
+
237
+		// 00 00 - assigned value for extension "server name"
238
+		extTypeB := buf.Next(2)
239
+		if len(extTypeB) != 2 {
240
+			return nil, fmt.Errorf("cannot read extension type: %v", extTypeB)
241
+		}
242
+
243
+		// 00 18 - 0x18 (24) bytes of "server name" extension data follows
244
+		lengthB := buf.Next(2)
245
+		if len(lengthB) != 2 {
246
+			return nil, fmt.Errorf("cannot read extension %v length: %v", extTypeB, lengthB)
247
+		}
248
+		length := int(binary.BigEndian.Uint16(lengthB))
249
+
250
+		extDataB := buf.Next(length)
251
+		if len(extDataB) != length {
252
+			return nil, fmt.Errorf("cannot read extension %v data: len %d != %d", extTypeB, length, len(extDataB))
253
+		}
254
+
255
+		if !bytes.Equal(extTypeB, extTypeSNI[:]) {
256
+			continue
257
+		}
258
+
259
+		buf.Reset()
260
+		buf.Write(extDataB)
261
+
262
+		// 00 16 - 0x16 (22) bytes of first (and only) list entry follows
263
+		lengthB = buf.Next(2)
264
+		if len(lengthB) != 2 {
265
+			return nil, fmt.Errorf("cannot read the length of the SNI record: %v", lengthB)
266
+		}
267
+
268
+		length = int(binary.BigEndian.Uint16(lengthB))
269
+		if length == 0 {
270
+			return nil, nil
271
+		}
272
+
273
+		listType, err := buf.ReadByte()
274
+		if err != nil {
275
+			return nil, fmt.Errorf("cannot read SNI list type: %w", err)
276
+		}
277
+
278
+		// 00 - list entry is type 0x00 "DNS hostname"
279
+		if listType != sniDNSNamesListType {
280
+			return nil, fmt.Errorf("incorrect SNI list type %#x", listType)
281
+		}
282
+
283
+		names := []string{}
284
+
285
+		for buf.Len() > 0 {
286
+			// 00 13 - 0x13 (19) bytes of hostname follows
287
+			lengthB = buf.Next(2)
288
+			if len(lengthB) != 2 {
289
+				return nil, fmt.Errorf("incorrect length of the hostname: %v", lengthB)
290
+			}
291
+			length = int(binary.BigEndian.Uint16(lengthB))
292
+
293
+			name := buf.Next(length)
294
+			if len(name) != length {
295
+				return nil, fmt.Errorf("incorrect length of SNI hostname: len %d != %d", length, len(name))
296
+			}
297
+
298
+			names = append(names, string(name))
299
+		}
300
+
301
+		return names, nil
302
+	}
303
+
304
+	return nil, nil
305
+}

+ 49
- 0
mtglib/internal/tls/fake/client_side_fuzz_test.go Wyświetl plik

@@ -0,0 +1,49 @@
1
+package fake_test
2
+
3
+import (
4
+	"bytes"
5
+	"testing"
6
+	"time"
7
+
8
+	"github.com/9seconds/mtg/v2/internal/testlib"
9
+	"github.com/9seconds/mtg/v2/mtglib"
10
+	"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
11
+	"github.com/stretchr/testify/assert"
12
+	"github.com/stretchr/testify/mock"
13
+	"github.com/stretchr/testify/require"
14
+)
15
+
16
+type connMock struct {
17
+	testlib.EssentialsConnMock
18
+
19
+	readBuf *bytes.Buffer
20
+}
21
+
22
+func (f *connMock) Read(p []byte) (int, error) {
23
+	return f.readBuf.Read(p)
24
+}
25
+
26
+func FuzzReadClientHello(f *testing.F) {
27
+	seed := [248]byte{}
28
+
29
+	secret, err := mtglib.ParseSecret(
30
+		"ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d",
31
+	)
32
+	require.NoError(f, err)
33
+
34
+	f.Add(seed[:])
35
+
36
+	f.Fuzz(func(t *testing.T, value []byte) {
37
+		r := &connMock{
38
+			readBuf: bytes.NewBuffer(value),
39
+		}
40
+		r.
41
+			On("SetReadDeadline", mock.AnythingOfType("time.Time")).
42
+			Twice().
43
+			Return(nil)
44
+
45
+		_, err := fake.ReadClientHello(r, secret, time.Hour)
46
+		assert.Error(t, err)
47
+	})
48
+
49
+}

+ 143
- 0
mtglib/internal/tls/fake/client_side_snapshot_test.go Wyświetl plik

@@ -0,0 +1,143 @@
1
+package fake_test
2
+
3
+import (
4
+	"bytes"
5
+	"encoding/base64"
6
+	"encoding/json"
7
+	"os"
8
+	"path/filepath"
9
+	"strings"
10
+	"testing"
11
+
12
+	"github.com/9seconds/mtg/v2/mtglib"
13
+	"github.com/9seconds/mtg/v2/mtglib/internal/tls/fake"
14
+	"github.com/stretchr/testify/assert"
15
+	"github.com/stretchr/testify/mock"
16
+	"github.com/stretchr/testify/require"
17
+	"github.com/stretchr/testify/suite"
18
+)
19
+
20
+type clientHelloSnapshot struct {
21
+	Time        int    `json:"time"`
22
+	Random      string `json:"random"`
23
+	SessionID   string `json:"sessionId"`
24
+	Host        string `json:"host"`
25
+	CipherSuite int    `json:"cipherSuite"`
26
+	Full        string `json:"full"`
27
+}
28
+
29
+func (c clientHelloSnapshot) GetRandom() []byte {
30
+	data, _ := base64.StdEncoding.DecodeString(c.Random)
31
+
32
+	return data
33
+}
34
+
35
+func (c clientHelloSnapshot) GetSessionID() []byte {
36
+	data, _ := base64.StdEncoding.DecodeString(c.SessionID)
37
+
38
+	return data
39
+}
40
+
41
+func (c clientHelloSnapshot) GetCipherSuite() uint16 {
42
+	return uint16(c.CipherSuite)
43
+}
44
+
45
+func (c clientHelloSnapshot) GetFull() []byte {
46
+	data, _ := base64.StdEncoding.DecodeString(c.Full)
47
+
48
+	return data
49
+}
50
+
51
+type ParseClientHelloSnapshotTestSuite struct {
52
+	suite.Suite
53
+
54
+	secret mtglib.Secret
55
+}
56
+
57
+func (suite *ParseClientHelloSnapshotTestSuite) SetupSuite() {
58
+	parsed, err := mtglib.ParseSecret(
59
+		"ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d",
60
+	)
61
+	require.NoError(suite.T(), err)
62
+
63
+	suite.secret = parsed
64
+}
65
+
66
+func (suite *ParseClientHelloSnapshotTestSuite) makeConn(data []byte) *parseClientHelloConnMock {
67
+	readBuf := &bytes.Buffer{}
68
+	readBuf.Write(data)
69
+
70
+	connMock := &parseClientHelloConnMock{
71
+		readBuf: readBuf,
72
+	}
73
+
74
+	connMock.
75
+		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
76
+		Twice().
77
+		Return(nil)
78
+
79
+	return connMock
80
+}
81
+
82
+func (suite *ParseClientHelloSnapshotTestSuite) TestSnapshotOk() {
83
+	files, err := os.ReadDir("testdata")
84
+	require.NoError(suite.T(), err)
85
+
86
+	for _, v := range files {
87
+		if !strings.HasPrefix(v.Name(), "client-hello-ok") {
88
+			continue
89
+		}
90
+
91
+		path := filepath.Join("testdata", v.Name())
92
+
93
+		suite.T().Run(v.Name(), func(t *testing.T) {
94
+			fileData, err := os.ReadFile(path)
95
+			assert.NoError(t, err)
96
+
97
+			snapshot := &clientHelloSnapshot{}
98
+			assert.NoError(t, json.Unmarshal(fileData, snapshot))
99
+
100
+			connMock := suite.makeConn(snapshot.GetFull())
101
+			defer connMock.AssertExpectations(t)
102
+
103
+			hello, err := fake.ReadClientHello(connMock, suite.secret, TolerateTime)
104
+			require.NoError(t, err)
105
+
106
+			assert.Equal(t, snapshot.GetRandom(), hello.Random[:])
107
+			assert.Equal(t, snapshot.GetSessionID(), hello.SessionID)
108
+			assert.Equal(t, snapshot.GetCipherSuite(), hello.CipherSuite)
109
+		})
110
+	}
111
+}
112
+
113
+func (suite *ParseClientHelloSnapshotTestSuite) TestSnapshotBad() {
114
+	files, err := os.ReadDir("testdata")
115
+	require.NoError(suite.T(), err)
116
+
117
+	for _, v := range files {
118
+		if !strings.HasPrefix(v.Name(), "client-hello-bad") {
119
+			continue
120
+		}
121
+
122
+		path := filepath.Join("testdata", v.Name())
123
+
124
+		suite.T().Run(v.Name(), func(t *testing.T) {
125
+			fileData, err := os.ReadFile(path)
126
+			assert.NoError(t, err)
127
+
128
+			snapshot := &clientHelloSnapshot{}
129
+			assert.NoError(t, json.Unmarshal(fileData, snapshot))
130
+
131
+			connMock := suite.makeConn(snapshot.GetFull())
132
+			defer connMock.AssertExpectations(t)
133
+
134
+			_, err = fake.ReadClientHello(connMock, suite.secret, TolerateTime)
135
+			assert.ErrorIs(t, err, fake.ErrBadDigest)
136
+		})
137
+	}
138
+}
139
+
140
+func TestParseClientHelloSnapshot(t *testing.T) {
141
+	t.Parallel()
142
+	suite.Run(t, &ParseClientHelloSnapshotTestSuite{})
143
+}

+ 395
- 0
mtglib/internal/tls/fake/client_side_test.go Wyświetl plik

@@ -0,0 +1,395 @@
1
+package fake_test
2
+
3
+import (
4
+	"bytes"
5
+	"encoding/binary"
6
+	"errors"
7
+	"io"
8
+	"testing"
9
+	"time"
10
+
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
+
20
+const (
21
+	TolerateTime = 365 * 30 * 24 * time.Hour
22
+)
23
+
24
+type parseClientHelloConnMock struct {
25
+	testlib.EssentialsConnMock
26
+
27
+	readBuf *bytes.Buffer
28
+}
29
+
30
+func (m *parseClientHelloConnMock) Read(p []byte) (int, error) {
31
+	return m.readBuf.Read(p)
32
+}
33
+
34
+type ParseClientHelloTestSuite struct {
35
+	suite.Suite
36
+
37
+	secret   mtglib.Secret
38
+	readBuf  *bytes.Buffer
39
+	connMock *parseClientHelloConnMock
40
+}
41
+
42
+func (suite *ParseClientHelloTestSuite) SetupSuite() {
43
+	parsed, err := mtglib.ParseSecret("ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d")
44
+	require.NoError(suite.T(), err)
45
+
46
+	suite.secret = parsed
47
+}
48
+
49
+func (suite *ParseClientHelloTestSuite) SetupTest() {
50
+	suite.readBuf = &bytes.Buffer{}
51
+	suite.connMock = &parseClientHelloConnMock{
52
+		readBuf: suite.readBuf,
53
+	}
54
+
55
+	suite.connMock.
56
+		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
57
+		Twice().
58
+		Return(nil)
59
+}
60
+
61
+func (suite *ParseClientHelloTestSuite) TearDownTest() {
62
+	suite.connMock.AssertExpectations(suite.T())
63
+}
64
+
65
+type ParseClientHello_TLSHeaderTestSuite struct {
66
+	ParseClientHelloTestSuite
67
+}
68
+
69
+func (suite *ParseClientHello_TLSHeaderTestSuite) TestEmpty() {
70
+	suite.connMock.ExpectedCalls = []*mock.Call{}
71
+	suite.connMock.
72
+		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
73
+		Once().
74
+		Return(errors.New("fail"))
75
+
76
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
77
+	suite.ErrorContains(err, "fail")
78
+}
79
+
80
+func (suite *ParseClientHello_TLSHeaderTestSuite) TestNothing() {
81
+	suite.connMock.ExpectedCalls = []*mock.Call{}
82
+	suite.connMock.
83
+		On("SetReadDeadline", mock.AnythingOfType("time.Time")).
84
+		Twice().
85
+		Return(nil)
86
+
87
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
88
+	suite.ErrorIs(err, io.EOF)
89
+}
90
+
91
+func (suite *ParseClientHello_TLSHeaderTestSuite) TestUnknownRecord() {
92
+	suite.readBuf.Write([]byte{
93
+		10,
94
+		3, 3,
95
+		0, 0,
96
+	})
97
+	suite.readBuf.WriteByte(10)
98
+
99
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
100
+	suite.ErrorContains(err, "unexpected record type 0xa")
101
+}
102
+
103
+func (suite *ParseClientHello_TLSHeaderTestSuite) TestUnknownProtocolVersion() {
104
+	suite.readBuf.Write([]byte{
105
+		tls.TypeHandshake,
106
+		3, 3,
107
+		0, 0,
108
+	})
109
+
110
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
111
+	suite.ErrorContains(err, "unexpected protocol version")
112
+}
113
+
114
+func (suite *ParseClientHello_TLSHeaderTestSuite) TestCannotReadRestOfRecord() {
115
+	suite.readBuf.Write([]byte{
116
+		tls.TypeHandshake,
117
+		3, 1,
118
+		0, 10,
119
+	})
120
+
121
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
122
+	suite.ErrorIs(err, io.EOF)
123
+}
124
+
125
+type ParseClientHelloHandshakeTestSuite struct {
126
+	ParseClientHelloTestSuite
127
+}
128
+
129
+func (suite *ParseClientHelloHandshakeTestSuite) SetupTest() {
130
+	suite.ParseClientHelloTestSuite.SetupTest()
131
+
132
+	suite.readBuf.Write([]byte{
133
+		tls.TypeHandshake,
134
+		3, 1,
135
+		0,
136
+	})
137
+}
138
+
139
+func (suite *ParseClientHelloHandshakeTestSuite) TestCannotReadHeader() {
140
+	suite.readBuf.Write([]byte{
141
+		1,
142
+		10,
143
+	})
144
+
145
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
146
+	suite.ErrorContains(err, "cannot read handshake header")
147
+}
148
+
149
+func (suite *ParseClientHelloHandshakeTestSuite) TestIncorrectHandshakeType() {
150
+	suite.readBuf.Write([]byte{
151
+		4,
152
+		10, 0, 0, 0,
153
+	})
154
+
155
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
156
+	suite.ErrorContains(err, "incorrect handshake type")
157
+}
158
+
159
+func (suite *ParseClientHelloHandshakeTestSuite) TestCannotReadHandshake() {
160
+	suite.readBuf.Write([]byte{
161
+		4 + 3,
162
+		10, 0, 0, 0,
163
+	})
164
+
165
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
166
+	suite.ErrorIs(err, io.EOF)
167
+}
168
+
169
+type ParseClientHelloHandshakeBodyTestSuite struct {
170
+	ParseClientHelloTestSuite
171
+}
172
+
173
+func (suite *ParseClientHelloHandshakeBodyTestSuite) SetupTest() {
174
+	suite.ParseClientHelloTestSuite.SetupTest()
175
+
176
+	suite.readBuf.Write([]byte{
177
+		tls.TypeHandshake,
178
+		3, 1,
179
+		0,
180
+	})
181
+}
182
+
183
+func (suite *ParseClientHelloHandshakeBodyTestSuite) writeBody(body []byte) {
184
+	suite.readBuf.WriteByte(byte(4 + len(body)))
185
+	suite.readBuf.Write([]byte{
186
+		fake.TypeHandshakeClient,
187
+		0, 0, byte(len(body)),
188
+	})
189
+	suite.readBuf.Write(body)
190
+}
191
+
192
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadVersion() {
193
+	suite.writeBody(nil)
194
+
195
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
196
+	suite.ErrorContains(err, "cannot read client version")
197
+}
198
+
199
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadRandom() {
200
+	suite.writeBody([]byte{3, 3})
201
+
202
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
203
+	suite.ErrorContains(err, "cannot read client random")
204
+}
205
+
206
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadSessionIDLength() {
207
+	body := make([]byte, 2+fake.RandomLen)
208
+
209
+	suite.writeBody(body)
210
+
211
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
212
+	suite.ErrorContains(err, "cannot read session ID length")
213
+}
214
+
215
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadSessionID() {
216
+	body := make([]byte, 2+fake.RandomLen+1)
217
+	body[2+fake.RandomLen] = 32
218
+
219
+	suite.writeBody(body)
220
+
221
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
222
+	suite.ErrorContains(err, "cannot read session id")
223
+}
224
+
225
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCipherSuiteLength() {
226
+	body := make([]byte, 2+fake.RandomLen+1)
227
+
228
+	suite.writeBody(body)
229
+
230
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
231
+	suite.ErrorContains(err, "cannot read cipher suite length")
232
+}
233
+
234
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadFirstCipherSuite() {
235
+	body := make([]byte, 2+fake.RandomLen+1+2)
236
+
237
+	suite.writeBody(body)
238
+
239
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
240
+	suite.ErrorContains(err, "cannot read first cipher suite")
241
+}
242
+
243
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipRemainingCipherSuites() {
244
+	body := make([]byte, 2+fake.RandomLen+1+2+2)
245
+	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 4)
246
+
247
+	suite.writeBody(body)
248
+
249
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
250
+	suite.ErrorContains(err, "cannot skip remaining cipher suites")
251
+}
252
+
253
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMethodsLength() {
254
+	body := make([]byte, 2+fake.RandomLen+1+2+2)
255
+	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
256
+
257
+	suite.writeBody(body)
258
+
259
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
260
+	suite.ErrorContains(err, "cannot read compression methods length")
261
+}
262
+
263
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipCompressionMethods() {
264
+	body := make([]byte, 2+fake.RandomLen+1+2+2+1)
265
+	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
266
+	body[2+fake.RandomLen+1+2+2] = 1
267
+
268
+	suite.writeBody(body)
269
+
270
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
271
+	suite.ErrorContains(err, "cannot skip compression methods")
272
+}
273
+
274
+type ParseClientHelloSNITestSuite struct {
275
+	ParseClientHelloTestSuite
276
+}
277
+
278
+func (suite *ParseClientHelloSNITestSuite) SetupTest() {
279
+	suite.ParseClientHelloTestSuite.SetupTest()
280
+
281
+	suite.readBuf.Write([]byte{
282
+		tls.TypeHandshake,
283
+		3, 1,
284
+		0,
285
+	})
286
+}
287
+
288
+func (suite *ParseClientHelloSNITestSuite) writeExtensions(extensions []byte) {
289
+	handshakeBodyLen := 41 + len(extensions)
290
+
291
+	suite.readBuf.WriteByte(byte(4 + handshakeBodyLen))
292
+	suite.readBuf.Write([]byte{
293
+		fake.TypeHandshakeClient,
294
+		0, 0, byte(handshakeBodyLen),
295
+	})
296
+
297
+	// version(2) + random(32) + sessionIDLen(1) + cipherSuiteLen(2) +
298
+	// cipherSuite(2) + compressionLen(1) + compression(1) = 41
299
+	body := make([]byte, 41)
300
+	binary.BigEndian.PutUint16(body[35:], 2)
301
+	body[39] = 1
302
+
303
+	suite.readBuf.Write(body)
304
+	suite.readBuf.Write(extensions)
305
+}
306
+
307
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionsLength() {
308
+	suite.writeExtensions(nil)
309
+
310
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
311
+	suite.ErrorContains(err, "cannot read length of TLS extensions")
312
+}
313
+
314
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensions() {
315
+	suite.writeExtensions([]byte{0, 10})
316
+
317
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
318
+	suite.ErrorContains(err, "cannot read extensions")
319
+}
320
+
321
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionType() {
322
+	suite.writeExtensions([]byte{0, 1, 0xAB})
323
+
324
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
325
+	suite.ErrorContains(err, "cannot read extension type")
326
+}
327
+
328
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionLength() {
329
+	suite.writeExtensions([]byte{0, 2, 0xFF, 0xFF})
330
+
331
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
332
+	suite.ErrorContains(err, "length:")
333
+}
334
+
335
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadExtensionData() {
336
+	suite.writeExtensions([]byte{0, 4, 0xFF, 0xFF, 0, 5})
337
+
338
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
339
+	suite.ErrorContains(err, "data: len")
340
+}
341
+
342
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadSNIRecordLength() {
343
+	suite.writeExtensions([]byte{0, 5, 0, 0, 0, 1, 0xAB})
344
+
345
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
346
+	suite.ErrorContains(err, "cannot read the length of the SNI record")
347
+}
348
+
349
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadSNIListType() {
350
+	suite.writeExtensions([]byte{0, 6, 0, 0, 0, 2, 0, 1})
351
+
352
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
353
+	suite.ErrorContains(err, "cannot read SNI list type")
354
+}
355
+
356
+func (suite *ParseClientHelloSNITestSuite) TestIncorrectSNIListType() {
357
+	suite.writeExtensions([]byte{0, 7, 0, 0, 0, 3, 0, 1, 5})
358
+
359
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
360
+	suite.ErrorContains(err, "incorrect SNI list type")
361
+}
362
+
363
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadHostnameLength() {
364
+	suite.writeExtensions([]byte{0, 8, 0, 0, 0, 4, 0, 2, 0, 0xAB})
365
+
366
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
367
+	suite.ErrorContains(err, "incorrect length of the hostname")
368
+}
369
+
370
+func (suite *ParseClientHelloSNITestSuite) TestCannotReadHostname() {
371
+	suite.writeExtensions([]byte{0, 9, 0, 0, 0, 5, 0, 3, 0, 0, 5})
372
+
373
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret, TolerateTime)
374
+	suite.ErrorContains(err, "incorrect length of SNI hostname")
375
+}
376
+
377
+func TestParseClientHelloTLSHeader(t *testing.T) {
378
+	t.Parallel()
379
+	suite.Run(t, &ParseClientHello_TLSHeaderTestSuite{})
380
+}
381
+
382
+func TestParseClientHelloHandshake(t *testing.T) {
383
+	t.Parallel()
384
+	suite.Run(t, &ParseClientHelloHandshakeTestSuite{})
385
+}
386
+
387
+func TestParseClientHelloHandshakeBody(t *testing.T) {
388
+	t.Parallel()
389
+	suite.Run(t, &ParseClientHelloHandshakeBodyTestSuite{})
390
+}
391
+
392
+func TestParseClientHelloSNI(t *testing.T) {
393
+	t.Parallel()
394
+	suite.Run(t, &ParseClientHelloSNITestSuite{})
395
+}

+ 16
- 0
mtglib/internal/tls/fake/init.go Wyświetl plik

@@ -0,0 +1,16 @@
1
+package fake
2
+
3
+import (
4
+	"errors"
5
+	"time"
6
+)
7
+
8
+const (
9
+	ClientHelloReadTimeout = 5 * time.Second
10
+)
11
+
12
+var (
13
+	resetDeadline time.Time
14
+
15
+	ErrBadDigest = errors.New("incorrect client random")
16
+)

+ 8
- 0
mtglib/internal/tls/fake/testdata/client-hello-bad-fa2e46cdb33e2a1b.json Wyświetl plik

@@ -0,0 +1,8 @@
1
+{
2
+  "time": 1617181365,
3
+  "random": "XvCPc3aAbHbhRLv0kUmy6BfPZOGvsused5/HNsKXEPs=",
4
+  "sessionId": "St2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCMCq4=",
5
+  "host": "storage.googleapis.com",
6
+  "cipherSuite": 4867,
7
+  "full": "FgMBAgABAAH8AwNe8I9zdoBsduFEu/SRSbLoF89k4a+y6x53n8c2wpcQ+yBK3YFna4cwWfcHa2sPWN922mOgk46DokF4uEVzIIAKrgA0EwMTARMCwCzAK8AkwCPACsAJzKnAMMAvwCjAJ8AUwBPMqACdAJwAPQA8ADUAL8AIwBIACgEAAX//AQABAAAAABsAGQAAFnN0b3JhZ2UuZ29vZ2xlYXBpcy5jb20AFwAAAA0AGAAWBAMIBAQBBQMCAwgFCAUFAQgGBgECAQAFAAUA0AAAADN0AAAAEgAAABAAMAAuAmgyBWgyLTE2BWgyLTE1BWgyLTE0CHNwZHkvMy4xBnNwZHkvMwhodHRwLzEuMQALAAIBAAAzACYAJAAdACAH/ugvH0kSUgAuwslL3UfZA3JTUfSiwrAhR6VWd2wvIgAtAAIBAQArAAkIAwQDAwMCAwEACgAKAAgAHQAXABgAGQAVAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
8
+}

+ 8
- 0
mtglib/internal/tls/fake/testdata/client-hello-ok-19dfe38384b9884b.json Wyświetl plik

@@ -0,0 +1,8 @@
1
+{
2
+  "time": 1617181365,
3
+  "random": "XvCPc3aAbHbhRLv0kUmy6BfPZOGvsused5/HNsKXEPs=",
4
+  "sessionId": "St2BZ2uHMFn3B2trD1jfdtpjoJOOg6JBeLhFcyCMCq4=",
5
+  "host": "storage.googleapis.com",
6
+  "cipherSuite": 4867,
7
+  "full": "FgMBAgABAAH8AwNe8I9zdoBsduFEu/SRSbLoF89k4a+y6x53n8c2wpcQ+yBK3YFna4cwWfcHa2sPWN922mOgk46DokF4uEVzIIwKrgA0EwMTARMCwCzAK8AkwCPACsAJzKnAMMAvwCjAJ8AUwBPMqACdAJwAPQA8ADUAL8AIwBIACgEAAX//AQABAAAAABsAGQAAFnN0b3JhZ2UuZ29vZ2xlYXBpcy5jb20AFwAAAA0AGAAWBAMIBAQBBQMCAwgFCAUFAQgGBgECAQAFAAUBAAAAADN0AAAAEgAAABAAMAAuAmgyBWgyLTE2BWgyLTE1BWgyLTE0CHNwZHkvMy4xBnNwZHkvMwhodHRwLzEuMQALAAIBAAAzACYAJAAdACAH/ugvH0kSUgAuwslL3UfZA3JTUfSiwrAhR6VWd2wvIgAtAAIBAQArAAkIAwQDAwMCAwEACgAKAAgAHQAXABgAGQAVAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
8
+}

+ 8
- 0
mtglib/internal/tls/fake/testdata/client-hello-ok-48f8a72a56f3174a.json Wyświetl plik

@@ -0,0 +1,8 @@
1
+{
2
+  "time": 1617181352,
3
+  "random": "oYEu33jl+zQbUKMtQbV1OHB0gXIM2y2aq9iY0QX12os=",
4
+  "sessionId": "FGqA3ZFYrSlj//xl7lammNn64K9/MK2mQ3HJUGvP+8g=",
5
+  "host": "storage.googleapis.com",
6
+  "cipherSuite": 4867,
7
+  "full": "FgMBAgABAAH8AwOhgS7feOX7NBtQoy1BtXU4cHSBcgzbLZqr2JjRBfXaiyAUaoDdkVitKWP//GXuVqaY2frgr38wraZDcclQa8/7yAA0EwMTARMCwCzAK8AkwCPACsAJzKnAMMAvwCjAJ8AUwBPMqACdAJwAPQA8ADUAL8AIwBIACgEAAX//AQABAAAAABsAGQAAFnN0b3JhZ2UuZ29vZ2xlYXBpcy5jb20AFwAAAA0AGAAWBAMIBAQBBQMCAwgFCAUFAQgGBgECAQAFAAUBAAAAADN0AAAAEgAAABAAMAAuAmgyBWgyLTE2BWgyLTE1BWgyLTE0CHNwZHkvMy4xBnNwZHkvMwhodHRwLzEuMQALAAIBAAAzACYAJAAdACBroKhykU/xB3hgIVH2mRoKv3umjYAuPQ/mcj02dvdRYwAtAAIBAQArAAkIAwQDAwMCAwEACgAKAAgAHQAXABgAGQAVAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
8
+}

+ 8
- 0
mtglib/internal/tls/fake/testdata/client-hello-ok-651054256093c6cd.json Wyświetl plik

@@ -0,0 +1,8 @@
1
+{
2
+  "time": 1617181352,
3
+  "random": "5V5sSprk/tFIgy+x1BeKNGhLlFkqfggLpgN7GYOA1ro=",
4
+  "sessionId": "jxr4d6PXPDk+Lwx3WUp9wvj8TGlOxEdrRJ0ydyJ9+H8=",
5
+  "host": "storage.googleapis.com",
6
+  "cipherSuite": 4867,
7
+  "full": "FgMBAgABAAH8AwPlXmxKmuT+0UiDL7HUF4o0aEuUWSp+CAumA3sZg4DWuiCPGvh3o9c8OT4vDHdZSn3C+PxMaU7ER2tEnTJ3In34fwA0EwMTARMCwCzAK8AkwCPACsAJzKnAMMAvwCjAJ8AUwBPMqACdAJwAPQA8ADUAL8AIwBIACgEAAX//AQABAAAAABsAGQAAFnN0b3JhZ2UuZ29vZ2xlYXBpcy5jb20AFwAAAA0AGAAWBAMIBAQBBQMCAwgFCAUFAQgGBgECAQAFAAUBAAAAADN0AAAAEgAAABAAMAAuAmgyBWgyLTE2BWgyLTE1BWgyLTE0CHNwZHkvMy4xBnNwZHkvMwhodHRwLzEuMQALAAIBAAAzACYAJAAdACCu6UBqpR0p5VgzQX6m7qif+HosGk7LM4objEUgpygWTgAtAAIBAQArAAkIAwQDAwMCAwEACgAKAAgAHQAXABgAGQAVAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
8
+}

+ 8
- 0
mtglib/internal/tls/fake/testdata/client-hello-ok-79d01ef18a9d2621.json Wyświetl plik

@@ -0,0 +1,8 @@
1
+{
2
+  "time": 1617181365,
3
+  "random": "8xljlOhkDlkafEF5vu3e1r3fWvh8AX548wC3hLZ3szQ=",
4
+  "sessionId": "00uvDYKnFyZFKyf3HlLwWGCOyeHsPFiU5UZ+Fs5pDAU=",
5
+  "host": "storage.googleapis.com",
6
+  "cipherSuite": 4867,
7
+  "full": "FgMBAgABAAH8AwPzGWOU6GQOWRp8QXm+7d7Wvd9a+HwBfnjzALeEtnezNCDTS68NgqcXJkUrJ/ceUvBYYI7J4ew8WJTlRn4WzmkMBQA0EwMTARMCwCzAK8AkwCPACsAJzKnAMMAvwCjAJ8AUwBPMqACdAJwAPQA8ADUAL8AIwBIACgEAAX//AQABAAAAABsAGQAAFnN0b3JhZ2UuZ29vZ2xlYXBpcy5jb20AFwAAAA0AGAAWBAMIBAQBBQMCAwgFCAUFAQgGBgECAQAFAAUBAAAAADN0AAAAEgAAABAAMAAuAmgyBWgyLTE2BWgyLTE1BWgyLTE0CHNwZHkvMy4xBnNwZHkvMwhodHRwLzEuMQALAAIBAAAzACYAJAAdACD/0/vXjQ20rOPIPAF/32Y7LX4WNE8A8dM1D1bEc4qlXgAtAAIBAQArAAkIAwQDAwMCAwEACgAKAAgAHQAXABgAGQAVAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
8
+}

+ 8
- 0
mtglib/internal/tls/fake/testdata/client-hello-ok-7a5569f05b118145.json Wyświetl plik

@@ -0,0 +1,8 @@
1
+{
2
+  "time": 1617181352,
3
+  "random": "zja3MLZ8WGSfsQRtPV75+tY6gbK3zKPi1Sy7SBBafg4=",
4
+  "sessionId": "qPut2yMqXa9zGLII/872SQ3d4Tfqo0uoDb7tpkRfBnA=",
5
+  "host": "storage.googleapis.com",
6
+  "cipherSuite": 4867,
7
+  "full": "FgMBAgABAAH8AwPONrcwtnxYZJ+xBG09Xvn61jqBsrfMo+LVLLtIEFp+DiCo+63bIypdr3MYsgj/zvZJDd3hN+qjS6gNvu2mRF8GcAA0EwMTARMCwCzAK8AkwCPACsAJzKnAMMAvwCjAJ8AUwBPMqACdAJwAPQA8ADUAL8AIwBIACgEAAX//AQABAAAAABsAGQAAFnN0b3JhZ2UuZ29vZ2xlYXBpcy5jb20AFwAAAA0AGAAWBAMIBAQBBQMCAwgFCAUFAQgGBgECAQAFAAUBAAAAADN0AAAAEgAAABAAMAAuAmgyBWgyLTE2BWgyLTE1BWgyLTE0CHNwZHkvMy4xBnNwZHkvMwhodHRwLzEuMQALAAIBAAAzACYAJAAdACBe+ItECoBgnzE4t2VyxSGV0jheXSD+z37LZCt3yto8SAAtAAIBAQArAAkIAwQDAwMCAwEACgAKAAgAHQAXABgAGQAVAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
8
+}

Ładowanie…
Anuluj
Zapisz