mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-08-31 08:54:02 +03:00
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.
This commit is contained in:
@@ -5,8 +5,8 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@@ -25,7 +25,6 @@ const (
|
||||
// https://medium.com/asecuritysite-when-bob-met-alice/in-cybersecurity-what-is-grease-9f8850558dea
|
||||
GreaseMask = 0x0f0f
|
||||
GreaseValueType = 0x0a0a
|
||||
DefaultCipher = tls.TLS_AES_128_GCM_SHA256
|
||||
|
||||
sniDNSNamesListType = 0
|
||||
)
|
||||
@@ -33,6 +32,8 @@ const (
|
||||
var (
|
||||
emptyRandom = [RandomLen]byte{}
|
||||
extTypeSNI = [2]byte{}
|
||||
|
||||
ErrCannotFindCipher = errors.New("cannot find a cipher")
|
||||
)
|
||||
|
||||
type ClientHello struct {
|
||||
@@ -110,9 +111,7 @@ func parseHandshake(r io.Reader) (*ClientHello, error) {
|
||||
return nil, fmt.Errorf("cannot read client version: %w", err)
|
||||
}
|
||||
|
||||
hello := &ClientHello{
|
||||
CipherSuite: DefaultCipher,
|
||||
}
|
||||
hello := &ClientHello{}
|
||||
|
||||
if _, err := io.ReadFull(r, hello.Random[:]); err != nil {
|
||||
return nil, fmt.Errorf("cannot read client random: %w", err)
|
||||
@@ -133,7 +132,6 @@ func parseHandshake(r io.Reader) (*ClientHello, error) {
|
||||
}
|
||||
|
||||
cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
|
||||
foundCipher := false
|
||||
|
||||
// Pick the first non-GREASE cipher suite from the list.
|
||||
// Real TLS servers never select GREASE values (RFC 8701, pattern 0x?a?a),
|
||||
@@ -144,17 +142,20 @@ func parseHandshake(r io.Reader) (*ClientHello, error) {
|
||||
return nil, fmt.Errorf("cannot read cipher suite: %w", err)
|
||||
}
|
||||
|
||||
if foundCipher {
|
||||
if hello.CipherSuite != 0 {
|
||||
// do not forget we have to scan until the end
|
||||
continue
|
||||
}
|
||||
|
||||
if cs := binary.BigEndian.Uint16(header[:]); cs&GreaseMask != GreaseValueType {
|
||||
hello.CipherSuite = cs
|
||||
// do not forget we have to scan until the end
|
||||
foundCipher = true
|
||||
}
|
||||
}
|
||||
|
||||
if hello.CipherSuite == 0 {
|
||||
return nil, ErrCannotFindCipher
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, header[:1]); err != nil {
|
||||
return nil, fmt.Errorf("cannot read compression methods length: %w", err)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package fake_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
cryptotls "crypto/tls"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io"
|
||||
@@ -234,9 +235,24 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipRemainingCiph
|
||||
suite.ErrorContains(err, "cannot read cipher suite")
|
||||
}
|
||||
|
||||
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotFindCipher() {
|
||||
// All cipher suites are GREASE values — must return ErrCannotFindCipher.
|
||||
body := make([]byte, 2+fake.RandomLen+1+2+4+1)
|
||||
binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 4)
|
||||
binary.BigEndian.PutUint16(body[2+fake.RandomLen+1+2:], 0x0a0a)
|
||||
binary.BigEndian.PutUint16(body[2+fake.RandomLen+1+2+2:], 0x1a1a)
|
||||
body[2+fake.RandomLen+1+2+4] = 1
|
||||
|
||||
suite.writeBody(body)
|
||||
|
||||
_, err := fake.ReadClientHello(suite.connMock, suite.secret.Key[:], suite.secret.Host, TolerateTime)
|
||||
suite.ErrorIs(err, fake.ErrCannotFindCipher)
|
||||
}
|
||||
|
||||
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMethodsLength() {
|
||||
body := make([]byte, 2+fake.RandomLen+1+2+2)
|
||||
binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
|
||||
binary.BigEndian.PutUint16(body[2+fake.RandomLen+1+2:], cryptotls.TLS_AES_128_GCM_SHA256)
|
||||
|
||||
suite.writeBody(body)
|
||||
|
||||
@@ -247,6 +263,7 @@ func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotReadCompressionMe
|
||||
func (suite *ParseClientHelloHandshakeBodyTestSuite) TestCannotSkipCompressionMethods() {
|
||||
body := make([]byte, 2+fake.RandomLen+1+2+2+1)
|
||||
binary.BigEndian.PutUint16(body[2+fake.RandomLen+1:], 2)
|
||||
binary.BigEndian.PutUint16(body[2+fake.RandomLen+1+2:], cryptotls.TLS_AES_128_GCM_SHA256)
|
||||
body[2+fake.RandomLen+1+2+2] = 1
|
||||
|
||||
suite.writeBody(body)
|
||||
@@ -282,6 +299,7 @@ func (suite *ParseClientHelloSNITestSuite) writeExtensions(extensions []byte) {
|
||||
// cipherSuite(2) + compressionLen(1) + compression(1) = 41
|
||||
body := make([]byte, 41)
|
||||
binary.BigEndian.PutUint16(body[35:], 2)
|
||||
binary.BigEndian.PutUint16(body[37:], cryptotls.TLS_AES_128_GCM_SHA256)
|
||||
body[39] = 1
|
||||
|
||||
suite.readBuf.Write(body)
|
||||
|
||||
Reference in New Issue
Block a user