diff --git a/mtglib/internal/tls/fake/client_side.go b/mtglib/internal/tls/fake/client_side.go index 2388c9b..e13c02b 100644 --- a/mtglib/internal/tls/fake/client_side.go +++ b/mtglib/internal/tls/fake/client_side.go @@ -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) } diff --git a/mtglib/internal/tls/fake/client_side_test.go b/mtglib/internal/tls/fake/client_side_test.go index d248d2a..5c717e0 100644 --- a/mtglib/internal/tls/fake/client_side_test.go +++ b/mtglib/internal/tls/fake/client_side_test.go @@ -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)