Skip to content

Commit

Permalink
fix: don't shuf psk (#180)
Browse files Browse the repository at this point in the history
- Shuffle Function will no longer shuffle PSK.
- Shuffle Function optimized.
- Fixed a bug in `FakePresharedKeyExtension` causing program to panic.
- Added `HelloChrome_100_PSK` and `HelloChrome_112_PSK_Shuf`. Both are beta fingerprints, use at your own risk.
  • Loading branch information
gaukas committed Apr 7, 2023
1 parent 8dc35be commit c785bd3
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 42 deletions.
4 changes: 4 additions & 0 deletions u_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,10 @@ var (
HelloChrome_102 = ClientHelloID{helloChrome, "102", nil, nil}
HelloChrome_106_Shuffle = ClientHelloID{helloChrome, "106", nil, nil} // beta: shuffler enabled starting from 106

// Chrome with PSK: Chrome start sending this ClientHello after doing TLS 1.3 handshake with the same server.
HelloChrome_100_PSK = ClientHelloID{helloChrome, "100_PSK", nil, nil} // beta: PSK extension added. uTLS doesn't fully support PSK. Use at your own risk.
HelloChrome_112_PSK_Shuf = ClientHelloID{helloChrome, "112_PSK", nil, nil} // beta: PSK extension added. uTLS doesn't fully support PSK. Use at your own risk.

HelloIOS_Auto = HelloIOS_14
HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil, nil} // legacy "111" means 11.1
HelloIOS_12_1 = ClientHelloID{helloIOS, "12.1", nil, nil}
Expand Down
153 changes: 112 additions & 41 deletions u_parrots.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,14 +508,95 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
},
}, nil
case HelloChrome_100_PSK:
return ClientHelloSpec{
CipherSuites: []uint16{
GREASE_PLACEHOLDER,
TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384,
TLS_CHACHA20_POLY1305_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_RSA_WITH_AES_256_GCM_SHA384,
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA,
},
CompressionMethods: []byte{
0x00, // compressionNone
},
Extensions: []TLSExtension{
&UtlsGREASEExtension{},
&SNIExtension{},
&UtlsExtendedMasterSecretExtension{},
&RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient},
&SupportedCurvesExtension{[]CurveID{
GREASE_PLACEHOLDER,
X25519,
CurveP256,
CurveP384,
}},
&SupportedPointsExtension{SupportedPoints: []byte{
0x00, // pointFormatUncompressed
}},
&SessionTicketExtension{},
&ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}},
&StatusRequestExtension{},
&SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{
ECDSAWithP256AndSHA256,
PSSWithSHA256,
PKCS1WithSHA256,
ECDSAWithP384AndSHA384,
PSSWithSHA384,
PKCS1WithSHA384,
PSSWithSHA512,
PKCS1WithSHA512,
}},
&SCTExtension{},
&KeyShareExtension{[]KeyShare{
{Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}},
{Group: X25519},
}},
&PSKKeyExchangeModesExtension{[]uint8{
PskModeDHE,
}},
&SupportedVersionsExtension{[]uint16{
GREASE_PLACEHOLDER,
VersionTLS13,
VersionTLS12,
}},
&UtlsCompressCertExtension{[]CertCompressionAlgo{
CertCompressionBrotli,
}},
&ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}},
&UtlsGREASEExtension{},
&FakePreSharedKeyExtension{},
},
}, nil
case HelloChrome_106_Shuffle:
chs, err := utlsIdToSpec(HelloChrome_102)
if err != nil {
return chs, err
}

// Chrome 107 started shuffling the order of extensions
return shuffleExtensions(chs)
shuffleExtensions(&chs)
return chs, err
case HelloChrome_112_PSK_Shuf:
chs, err := utlsIdToSpec(HelloChrome_100_PSK)
if err != nil {
return chs, err
}

// Chrome 112 started shuffling the order of extensions
shuffleExtensions(&chs)
return chs, err
case HelloFirefox_55, HelloFirefox_56:
return ClientHelloSpec{
TLSVersMax: VersionTLS12,
Expand Down Expand Up @@ -1853,59 +1934,49 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
}
}

func shuffleExtensions(chs ClientHelloSpec) (ClientHelloSpec, error) {
func shuffleExtensions(chs *ClientHelloSpec) error {
// Shuffle extensions to avoid fingerprinting -- introduced in Chrome 106
// GREASE, padding will remain in place (if present)

// Find indexes of GREASE and padding extensions
var greaseIdx []int
var paddingIdx []int
var otherExtensions []TLSExtension
var err error = nil

for i, ext := range chs.Extensions {
switch ext.(type) {
// unshufCheck checks:
// - if the exts[idx] is a GREASE extension, then it should not be shuffled
// - if the exts[idx] is a padding/pre_shared_key extension, then it should be the
// last extension in the list and should not be shuffled
var unshufCheck = func(idx int, exts []TLSExtension) (donotshuf bool, userErr error) {
switch exts[idx].(type) {
case *UtlsGREASEExtension:
greaseIdx = append(greaseIdx, i)
case *UtlsPaddingExtension:
paddingIdx = append(paddingIdx, i)
donotshuf = true
case *UtlsPaddingExtension, *FakePreSharedKeyExtension:
donotshuf = true
if idx != len(chs.Extensions)-1 {
userErr = errors.New("UtlsPaddingExtension or FakePreSharedKeyExtension must be the last extension")
}
default:
otherExtensions = append(otherExtensions, ext)
donotshuf = false
}
return
}

// Shuffle other extensions
rand.Shuffle(len(otherExtensions), func(i, j int) {
otherExtensions[i], otherExtensions[j] = otherExtensions[j], otherExtensions[i]
})

// Rebuild extensions slice
otherExtIdx := 0
SHUF_EXTENSIONS:
for i := 0; i < len(chs.Extensions); i++ {
// if current index is in greaseIdx or paddingIdx, add GREASE or padding extension
for _, idx := range greaseIdx {
if i == idx {
chs.Extensions[i] = &UtlsGREASEExtension{}
continue SHUF_EXTENSIONS
rand.Shuffle(len(chs.Extensions), func(i, j int) {
if unshuf, shuferr := unshufCheck(i, chs.Extensions); unshuf {
if shuferr != nil {
err = shuferr
}
return
}
for _, idx := range paddingIdx {
if i == idx {
chs.Extensions[i] = &UtlsPaddingExtension{
GetPaddingLen: BoringPaddingStyle,
}
break SHUF_EXTENSIONS

if unshuf, shuferr := unshufCheck(j, chs.Extensions); unshuf {
if shuferr != nil {
err = shuferr
}
return
}

// otherwise add other extension
chs.Extensions[i] = otherExtensions[otherExtIdx]
otherExtIdx++
}
if otherExtIdx != len(otherExtensions) {
return ClientHelloSpec{}, errors.New("shuffleExtensions: otherExtIdx != len(otherExtensions)")
}
return chs, nil
chs.Extensions[i], chs.Extensions[j] = chs.Extensions[j], chs.Extensions[i]
})

return err
}

func (uconn *UConn) applyPresetByID(id ClientHelloID) (err error) {
Expand Down
5 changes: 4 additions & 1 deletion u_tls_extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1859,8 +1859,11 @@ type FakePreSharedKeyExtension struct {
}

func (e *FakePreSharedKeyExtension) writeToUConn(uc *UConn) error {
if uc.config.ClientSessionCache == nil {
return nil // don't write the extension if there is no session cache
}
if session, ok := uc.config.ClientSessionCache.Get(clientSessionCacheKey(uc.conn.RemoteAddr(), uc.config)); !ok || session == nil {
return nil // don't write the extension if there is no session
return nil // don't write the extension if there is no session cache available for this session
}
uc.HandshakeState.Hello.PskIdentities = e.PskIdentities
uc.HandshakeState.Hello.PskBinders = e.PskBinders
Expand Down

0 comments on commit c785bd3

Please sign in to comment.