|
|
@@ -130,18 +130,25 @@ func parseHandshake(r io.Reader) (*ClientHello, error) {
|
|
130
|
130
|
|
|
131
|
131
|
cipherSuiteLen := int64(binary.BigEndian.Uint16(header[:]))
|
|
132
|
132
|
|
|
133
|
|
- // we do not care about picking up any cipher. we pick the first one,
|
|
134
|
|
- // so it is always should be present.
|
|
135
|
|
- if _, err := io.ReadFull(r, header[:]); err != nil {
|
|
136
|
|
- return nil, fmt.Errorf("cannot read first cipher suite: %w", err)
|
|
137
|
|
- }
|
|
|
133
|
+ // Pick the first non-GREASE cipher suite from the list.
|
|
|
134
|
+ // Real TLS servers never select GREASE values (RFC 8701, pattern 0x?a?a),
|
|
|
135
|
+ // so echoing them back is a trivial DPI fingerprint.
|
|
|
136
|
+ for remaining := cipherSuiteLen; remaining >= 2; remaining -= 2 {
|
|
|
137
|
+ if _, err := io.ReadFull(r, header[:]); err != nil {
|
|
|
138
|
+ return nil, fmt.Errorf("cannot read cipher suite: %w", err)
|
|
|
139
|
+ }
|
|
138
|
140
|
|
|
139
|
|
- hello.CipherSuite = binary.BigEndian.Uint16(header[:])
|
|
|
141
|
+ cs := binary.BigEndian.Uint16(header[:])
|
|
|
142
|
+ if hello.CipherSuite == 0 && cs&0x0f0f != 0x0a0a {
|
|
|
143
|
+ hello.CipherSuite = cs
|
|
|
144
|
+ }
|
|
|
145
|
+ }
|
|
140
|
146
|
|
|
141
|
|
- if _, err := io.CopyN(io.Discard, r, cipherSuiteLen-2); err != nil {
|
|
142
|
|
- return nil, fmt.Errorf("cannot skip remaining cipher suites: %w", err)
|
|
|
147
|
+ if hello.CipherSuite == 0 {
|
|
|
148
|
+ hello.CipherSuite = 0x1301 // fallback: TLS_AES_128_GCM_SHA256
|
|
143
|
149
|
}
|
|
144
|
150
|
|
|
|
151
|
+
|
|
145
|
152
|
if _, err := io.ReadFull(r, header[:1]); err != nil {
|
|
146
|
153
|
return nil, fmt.Errorf("cannot read compression methods length: %w", err)
|
|
147
|
154
|
}
|