Преглед на файлове

Do not use default TLS cipher

As per RFC, if TLS server cannot pickup a suitable cipher from a client
list, it has to send handshake_failure alert. For us it means that we
have to route a request to a fronting domain, because we want to have it
exactly like a real webserver does. So, if it misbehaves, so do we.
tags/v2.2.8^2^2
9seconds преди 4 седмици
родител
ревизия
5bf218f9ab
променени са 2 файла, в които са добавени 28 реда и са изтрити 9 реда
  1. 10
    9
      mtglib/internal/tls/fake/client_side.go
  2. 18
    0
      mtglib/internal/tls/fake/client_side_test.go

+ 10
- 9
mtglib/internal/tls/fake/client_side.go Целия файл

@@ -5,8 +5,8 @@ import (
5 5
 	"crypto/hmac"
6 6
 	"crypto/sha256"
7 7
 	"crypto/subtle"
8
-	"crypto/tls"
9 8
 	"encoding/binary"
9
+	"errors"
10 10
 	"fmt"
11 11
 	"io"
12 12
 	"net"
@@ -25,7 +25,6 @@ const (
25 25
 	// https://medium.com/asecuritysite-when-bob-met-alice/in-cybersecurity-what-is-grease-9f8850558dea
26 26
 	GreaseMask      = 0x0f0f
27 27
 	GreaseValueType = 0x0a0a
28
-	DefaultCipher   = tls.TLS_AES_128_GCM_SHA256
29 28
 
30 29
 	sniDNSNamesListType = 0
31 30
 )
@@ -33,6 +32,8 @@ const (
33 32
 var (
34 33
 	emptyRandom = [RandomLen]byte{}
35 34
 	extTypeSNI  = [2]byte{}
35
+
36
+	ErrCannotFindCipher = errors.New("cannot find a cipher")
36 37
 )
37 38
 
38 39
 type ClientHello struct {
@@ -110,9 +111,7 @@ func parseHandshake(r io.Reader) (*ClientHello, error) {
110 111
 		return nil, fmt.Errorf("cannot read client version: %w", err)
111 112
 	}
112 113
 
113
-	hello := &ClientHello{
114
-		CipherSuite: DefaultCipher,
115
-	}
114
+	hello := &ClientHello{}
116 115
 
117 116
 	if _, err := io.ReadFull(r, hello.Random[:]); err != nil {
118 117
 		return nil, fmt.Errorf("cannot read client random: %w", err)
@@ -133,7 +132,6 @@ func parseHandshake(r io.Reader) (*ClientHello, error) {
133 132
 	}
134 133
 
135 134
 	cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
136
-	foundCipher := false
137 135
 
138 136
 	// Pick the first non-GREASE cipher suite from the list.
139 137
 	// Real TLS servers never select GREASE values (RFC 8701, pattern 0x?a?a),
@@ -144,17 +142,20 @@ func parseHandshake(r io.Reader) (*ClientHello, error) {
144 142
 			return nil, fmt.Errorf("cannot read cipher suite: %w", err)
145 143
 		}
146 144
 
147
-		if foundCipher {
145
+		if hello.CipherSuite != 0 {
146
+			// do not forget we have to scan until the end
148 147
 			continue
149 148
 		}
150 149
 
151 150
 		if cs := binary.BigEndian.Uint16(header[:]); cs&GreaseMask != GreaseValueType {
152 151
 			hello.CipherSuite = cs
153
-			// do not forget we have to scan until the end
154
-			foundCipher = true
155 152
 		}
156 153
 	}
157 154
 
155
+	if hello.CipherSuite == 0 {
156
+		return nil, ErrCannotFindCipher
157
+	}
158
+
158 159
 	if _, err := io.ReadFull(r, header[:1]); err != nil {
159 160
 		return nil, fmt.Errorf("cannot read compression methods length: %w", err)
160 161
 	}

+ 18
- 0
mtglib/internal/tls/fake/client_side_test.go Целия файл

@@ -2,6 +2,7 @@ package fake_test
2 2
 
3 3
 import (
4 4
 	"bytes"
5
+	cryptotls "crypto/tls"
5 6
 	"encoding/binary"
6 7
 	"encoding/json"
7 8
 	"io"
@@ -234,9 +235,24 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipRemainingCiph
234 235
 	suite.ErrorContains(err, "cannot read cipher suite")
235 236
 }
236 237
 
238
+func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotFindCipher() {
239
+	// All cipher suites are GREASE values — must return ErrCannotFindCipher.
240
+	body := make([]byte, 2+fake.RandomLen+1+2+4+1)
241
+	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 4)
242
+	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1+2:], 0x0a0a)
243
+	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1+2+2:], 0x1a1a)
244
+	body[2+fake.RandomLen+1+2+4] = 1
245
+
246
+	suite.writeBody(body)
247
+
248
+	_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
249
+	suite.ErrorIs(err, fake.ErrCannotFindCipher)
250
+}
251
+
237 252
 func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMethodsLength() {
238 253
 	body := make([]byte, 2+fake.RandomLen+1+2+2)
239 254
 	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
255
+	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1+2:], cryptotls.TLS_AES_128_GCM_SHA256)
240 256
 
241 257
 	suite.writeBody(body)
242 258
 
@@ -247,6 +263,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMe
247 263
 func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipCompressionMethods() {
248 264
 	body := make([]byte, 2+fake.RandomLen+1+2+2+1)
249 265
 	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
266
+	binary.BigEndian.PutUint16(body[2+fake.RandomLen+1+2:], cryptotls.TLS_AES_128_GCM_SHA256)
250 267
 	body[2+fake.RandomLen+1+2+2] = 1
251 268
 
252 269
 	suite.writeBody(body)
@@ -282,6 +299,7 @@ func (suite *ParseClientHelloSNITestSuite) writeExtensions(extensions []byte) {
282 299
 	// cipherSuite(2) + compressionLen(1) + compression(1) = 41
283 300
 	body := make([]byte, 41)
284 301
 	binary.BigEndian.PutUint16(body[35:], 2)
302
+	binary.BigEndian.PutUint16(body[37:], cryptotls.TLS_AES_128_GCM_SHA256)
285 303
 	body[39] = 1
286 304
 
287 305
 	suite.readBuf.Write(body)

Loading…
Отказ
Запис