new: more parrots and safety update (#227)

* new: PQ and other parrots

Add new preset parrots:
- HelloChrome_114_Padding_PSK_Shuf
- HelloChrome_115_PQ
- HelloChrome_115_PQ_PSK

* new: ShuffleChromeTLSExtensions

Implement a new function `ShuffleChromeTLSExtensions(exts []TLSExtension) []TLSExtension`.

* update: include psk parameter for parrot-related functions

Update following functions' prototype to accept an optional pskExtension (of type *FakePreSharedKeyExtension):
- `UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID)` => `UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID, pskExtension ...*FakePreSharedKeyExtension)`
- `UTLSIdToSpec(id ClientHelloID)` => `UTLSIdToSpec(id ClientHelloID, pskExtension ...*FakePreSharedKeyExtension)`

* new: pre-defined error from UTLSIdToSpec

Update UTLSIdToSpec to return more comprehensive errors by pre-defining them, allowing easier error comparing/unwrapping.
This commit is contained in:
Gaukas Wang 2023-08-13 22:25:07 -06:00
parent 6663294864
commit 45e7f1de14
3 changed files with 429 additions and 67 deletions

View file

@ -582,9 +582,17 @@ 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.
// Chrome w/ PSK: Chrome start sending this ClientHello after doing TLS 1.3 handshake with the same server.
// Beta: PSK extension added. However, uTLS doesn't ship with full PSK support.
// Use at your own discretion.
HelloChrome_100_PSK = ClientHelloID{helloChrome, "100_PSK", nil, nil}
HelloChrome_112_PSK_Shuf = ClientHelloID{helloChrome, "112_PSK", nil, nil}
HelloChrome_114_Padding_PSK_Shuf = ClientHelloID{helloChrome, "114_PSK", nil, nil}
// Chrome w/ Post-Quantum Key Agreement
// Beta: PQ extension added. However, uTLS doesn't ship with full PQ support. Use at your own discretion.
HelloChrome_115_PQ = ClientHelloID{helloChrome, "115_PQ", nil, nil}
HelloChrome_115_PQ_PSK = ClientHelloID{helloChrome, "115_PQ_PSK", nil, nil}
HelloIOS_Auto = HelloIOS_14
HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil, nil} // legacy "111" means 11.1

View file

@ -24,6 +24,7 @@ type UConn struct {
Extensions []TLSExtension
ClientHelloID ClientHelloID
pskExtension []*FakePreSharedKeyExtension
ClientHelloBuilt bool
HandshakeState PubClientHandshakeState
@ -43,13 +44,13 @@ type UConn struct {
// UClient returns a new uTLS client, with behavior depending on clientHelloID.
// Config CAN be nil, but make sure to eventually specify ServerName.
func UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID) *UConn {
func UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID, pskExtension ...*FakePreSharedKeyExtension) *UConn {
if config == nil {
config = &Config{}
}
tlsConn := Conn{conn: conn, config: config, isClient: true}
handshakeState := PubClientHandshakeState{C: &tlsConn, Hello: &PubClientHelloMsg{}}
uconn := UConn{Conn: &tlsConn, ClientHelloID: clientHelloID, HandshakeState: handshakeState}
uconn := UConn{Conn: &tlsConn, ClientHelloID: clientHelloID, pskExtension: pskExtension, HandshakeState: handshakeState}
uconn.HandshakeState.uconn = &uconn
uconn.handshakeFn = uconn.clientHandshake
return &uconn

View file

@ -16,11 +16,24 @@ import (
"strconv"
)
var ErrUnknownClientHelloID = errors.New("tls: unknown ClientHelloID")
var ErrNotPSKClientHelloID = errors.New("tls: ClientHello does not contain pre_shared_key extension")
var ErrPSKExtensionExpected = errors.New("tls: pre_shared_key extension expected when fetching preset ClientHelloSpec")
// UTLSIdToSpec converts a ClientHelloID to a corresponding ClientHelloSpec.
//
// Exported internal function utlsIdToSpec per request.
func UTLSIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
return utlsIdToSpec(id)
func UTLSIdToSpec(id ClientHelloID, pskExtension ...*FakePreSharedKeyExtension) (ClientHelloSpec, error) {
if len(pskExtension) > 1 {
return ClientHelloSpec{}, errors.New("tls: at most one FakePreSharedKeyExtensions is allowed")
}
chs, err := utlsIdToSpec(id)
if err != nil && errors.Is(err, ErrUnknownClientHelloID) {
chs, err = utlsIdToSpecWithPSK(id, pskExtension...)
}
return chs, err
}
func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
@ -509,7 +522,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
},
}, nil
case HelloChrome_100_PSK:
case HelloChrome_106_Shuffle:
return ClientHelloSpec{
CipherSuites: []uint16{
GREASE_PLACEHOLDER,
@ -532,7 +545,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
CompressionMethods: []byte{
0x00, // compressionNone
},
Extensions: []TLSExtension{
Extensions: ShuffleChromeTLSExtensions([]TLSExtension{
&UtlsGREASEExtension{},
&SNIExtension{},
&ExtendedMasterSecretExtension{},
@ -577,27 +590,83 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
}},
&ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}},
&UtlsGREASEExtension{},
&FakePreSharedKeyExtension{},
},
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
}),
}, nil
// Chrome w/ Post-Quantum Key Agreement
case HelloChrome_115_PQ:
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: ShuffleChromeTLSExtensions([]TLSExtension{
&UtlsGREASEExtension{},
&SNIExtension{},
&ExtendedMasterSecretExtension{},
&RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient},
&SupportedCurvesExtension{[]CurveID{
GREASE_PLACEHOLDER,
X25519Kyber768Draft00,
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: X25519Kyber768Draft00},
{Group: X25519},
}},
&PSKKeyExchangeModesExtension{[]uint8{
PskModeDHE,
}},
&SupportedVersionsExtension{[]uint16{
GREASE_PLACEHOLDER,
VersionTLS13,
VersionTLS12,
}},
&UtlsCompressCertExtension{[]CertCompressionAlgo{
CertCompressionBrotli,
}},
&ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}},
&UtlsGREASEExtension{},
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
}),
}, nil
case HelloChrome_106_Shuffle:
chs, err := utlsIdToSpec(HelloChrome_102)
if err != nil {
return chs, err
}
// Chrome 107 started shuffling the order of extensions
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,
@ -1931,53 +2000,337 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
// Use empty values as they can be filled later by UConn.ApplyPreset or manually.
return generateRandomizedSpec(&id, "", nil, nil)
}
return ClientHelloSpec{}, errors.New("ClientHello ID " + id.Str() + " is unknown")
return ClientHelloSpec{}, fmt.Errorf("%w: %s", ErrUnknownClientHelloID, id.Str())
}
}
func shuffleExtensions(chs *ClientHelloSpec) error {
// Shuffle extensions to avoid fingerprinting -- introduced in Chrome 106
var err error = nil
// 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:
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:
donotshuf = false
func utlsIdToSpecWithPSK(id ClientHelloID, pskExtension ...*FakePreSharedKeyExtension) (ClientHelloSpec, error) {
switch id {
case HelloChrome_100_PSK, HelloChrome_112_PSK_Shuf, HelloChrome_114_Padding_PSK_Shuf, HelloChrome_115_PQ_PSK:
if len(pskExtension) == 0 || pskExtension[0] == nil {
return ClientHelloSpec{}, fmt.Errorf("%w: %s", ErrPSKExtensionExpected, id.Str())
}
}
switch id {
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{},
&ExtendedMasterSecretExtension{},
&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{},
pskExtension[0],
},
}, nil
case HelloChrome_112_PSK_Shuf:
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: ShuffleChromeTLSExtensions([]TLSExtension{
&UtlsGREASEExtension{},
&SNIExtension{},
&ExtendedMasterSecretExtension{},
&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{},
pskExtension[0],
}),
}, nil
case HelloChrome_114_Padding_PSK_Shuf:
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: ShuffleChromeTLSExtensions([]TLSExtension{
&UtlsGREASEExtension{},
&SNIExtension{},
&ExtendedMasterSecretExtension{},
&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{},
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
pskExtension[0],
}),
}, nil
// Chrome w/ Post-Quantum Key Agreement
case HelloChrome_115_PQ_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: ShuffleChromeTLSExtensions([]TLSExtension{
&UtlsGREASEExtension{},
&SNIExtension{},
&ExtendedMasterSecretExtension{},
&RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient},
&SupportedCurvesExtension{[]CurveID{
GREASE_PLACEHOLDER,
X25519Kyber768Draft00,
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: X25519Kyber768Draft00},
{Group: X25519},
}},
&PSKKeyExchangeModesExtension{[]uint8{
PskModeDHE,
}},
&SupportedVersionsExtension{[]uint16{
GREASE_PLACEHOLDER,
VersionTLS13,
VersionTLS12,
}},
&UtlsCompressCertExtension{[]CertCompressionAlgo{
CertCompressionBrotli,
}},
&ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}},
&UtlsGREASEExtension{},
pskExtension[0],
}),
}, nil
}
return ClientHelloSpec{}, fmt.Errorf("%w: %s", ErrUnknownClientHelloID, id.Str())
}
// ShuffleChromeTLSExtensions shuffles the extensions in the ClientHelloSpec to avoid ossification.
// It shuffles every extension except GREASE, padding and pre_shared_key extensions.
//
// This feature was first introduced by Chrome 106.
func ShuffleChromeTLSExtensions(exts []TLSExtension) []TLSExtension {
// unshufCheck checks if the exts[idx] is a GREASE/padding/pre_shared_key extension,
// and returns true on success. For these extensions are considered positionally invariant.
var skipShuf = func(idx int, exts []TLSExtension) bool {
switch exts[idx].(type) {
case *UtlsGREASEExtension, *UtlsPaddingExtension, *FakePreSharedKeyExtension:
return true
default:
return false
}
return
}
// Shuffle other extensions
rand.Shuffle(len(chs.Extensions), func(i, j int) {
if unshuf, shuferr := unshufCheck(i, chs.Extensions); unshuf {
if shuferr != nil {
err = shuferr
}
return
rand.Shuffle(len(exts), func(i, j int) {
if skipShuf(i, exts) || skipShuf(j, exts) {
return // do not shuffle some of the extensions
}
if unshuf, shuferr := unshufCheck(j, chs.Extensions); unshuf {
if shuferr != nil {
err = shuferr
}
return
}
chs.Extensions[i], chs.Extensions[j] = chs.Extensions[j], chs.Extensions[i]
exts[i], exts[j] = exts[j], exts[i]
})
return err
return exts
}
func (uconn *UConn) applyPresetByID(id ClientHelloID) (err error) {
@ -1994,7 +2347,7 @@ func (uconn *UConn) applyPresetByID(id ClientHelloID) (err error) {
return nil
default:
spec, err = utlsIdToSpec(id)
spec, err = UTLSIdToSpec(id, uconn.pskExtension...)
if err != nil {
return err
}