Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customable weights used in generateRandomizedSpec() #163

Merged
merged 5 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 77 additions & 33 deletions u_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ type ClientHelloID struct {
// Seed is only used for randomized fingerprints to seed PRNG.
// Must not be modified once set.
Seed *PRNGSeed

// Weights are only used for randomized fingerprints in func
// generateRandomizedSpec(). Must not be modified once set.
Weights *Weights
}

func (p *ClientHelloID) Str() string {
Expand Down Expand Up @@ -160,62 +164,102 @@ var (
// overwrite your changes to Hello(Config, Session are fine).
// You might want to call BuildHandshakeState() before applying any changes.
// UConn.Extensions will be completely ignored.
HelloGolang = ClientHelloID{helloGolang, helloAutoVers, nil}
HelloGolang = ClientHelloID{helloGolang, helloAutoVers, nil, nil}

// HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with
// TLSExtensions manually or use ApplyPreset function
HelloCustom = ClientHelloID{helloCustom, helloAutoVers, nil}
HelloCustom = ClientHelloID{helloCustom, helloAutoVers, nil, nil}

// HelloRandomized* randomly adds/reorders extensions, ciphersuites, etc.
HelloRandomized = ClientHelloID{helloRandomized, helloAutoVers, nil}
HelloRandomizedALPN = ClientHelloID{helloRandomizedALPN, helloAutoVers, nil}
HelloRandomizedNoALPN = ClientHelloID{helloRandomizedNoALPN, helloAutoVers, nil}
HelloRandomized = ClientHelloID{helloRandomized, helloAutoVers, nil, nil}
HelloRandomizedALPN = ClientHelloID{helloRandomizedALPN, helloAutoVers, nil, nil}
HelloRandomizedNoALPN = ClientHelloID{helloRandomizedNoALPN, helloAutoVers, nil, nil}

// The rest will will parrot given browser.
HelloFirefox_Auto = HelloFirefox_105
HelloFirefox_55 = ClientHelloID{helloFirefox, "55", nil}
HelloFirefox_56 = ClientHelloID{helloFirefox, "56", nil}
HelloFirefox_63 = ClientHelloID{helloFirefox, "63", nil}
HelloFirefox_65 = ClientHelloID{helloFirefox, "65", nil}
HelloFirefox_99 = ClientHelloID{helloFirefox, "99", nil}
HelloFirefox_102 = ClientHelloID{helloFirefox, "102", nil}
HelloFirefox_105 = ClientHelloID{helloFirefox, "105", nil}
HelloFirefox_55 = ClientHelloID{helloFirefox, "55", nil, nil}
HelloFirefox_56 = ClientHelloID{helloFirefox, "56", nil, nil}
HelloFirefox_63 = ClientHelloID{helloFirefox, "63", nil, nil}
HelloFirefox_65 = ClientHelloID{helloFirefox, "65", nil, nil}
HelloFirefox_99 = ClientHelloID{helloFirefox, "99", nil, nil}
HelloFirefox_102 = ClientHelloID{helloFirefox, "102", nil, nil}
HelloFirefox_105 = ClientHelloID{helloFirefox, "105", nil, nil}

HelloChrome_Auto = HelloChrome_106_Shuffle
HelloChrome_58 = ClientHelloID{helloChrome, "58", nil}
HelloChrome_62 = ClientHelloID{helloChrome, "62", nil}
HelloChrome_70 = ClientHelloID{helloChrome, "70", nil}
HelloChrome_72 = ClientHelloID{helloChrome, "72", nil}
HelloChrome_83 = ClientHelloID{helloChrome, "83", nil}
HelloChrome_87 = ClientHelloID{helloChrome, "87", nil}
HelloChrome_96 = ClientHelloID{helloChrome, "96", nil}
HelloChrome_100 = ClientHelloID{helloChrome, "100", nil}
HelloChrome_102 = ClientHelloID{helloChrome, "102", nil}
HelloChrome_106_Shuffle = ClientHelloID{helloChrome, "106", nil} // beta: shuffler enabled starting from 106
HelloChrome_58 = ClientHelloID{helloChrome, "58", nil, nil}
HelloChrome_62 = ClientHelloID{helloChrome, "62", nil, nil}
HelloChrome_70 = ClientHelloID{helloChrome, "70", nil, nil}
HelloChrome_72 = ClientHelloID{helloChrome, "72", nil, nil}
HelloChrome_83 = ClientHelloID{helloChrome, "83", nil, nil}
HelloChrome_87 = ClientHelloID{helloChrome, "87", nil, nil}
HelloChrome_96 = ClientHelloID{helloChrome, "96", nil, nil}
HelloChrome_100 = ClientHelloID{helloChrome, "100", nil, nil}
HelloChrome_102 = ClientHelloID{helloChrome, "102", nil, nil}
HelloChrome_106_Shuffle = ClientHelloID{helloChrome, "106", nil, nil} // beta: shuffler enabled starting from 106

HelloIOS_Auto = HelloIOS_14
HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil} // legacy "111" means 11.1
HelloIOS_12_1 = ClientHelloID{helloIOS, "12.1", nil}
HelloIOS_13 = ClientHelloID{helloIOS, "13", nil}
HelloIOS_14 = ClientHelloID{helloIOS, "14", nil}
HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil, nil} // legacy "111" means 11.1
HelloIOS_12_1 = ClientHelloID{helloIOS, "12.1", nil, nil}
HelloIOS_13 = ClientHelloID{helloIOS, "13", nil, nil}
HelloIOS_14 = ClientHelloID{helloIOS, "14", nil, nil}

HelloAndroid_11_OkHttp = ClientHelloID{helloAndroid, "11", nil}
HelloAndroid_11_OkHttp = ClientHelloID{helloAndroid, "11", nil, nil}

HelloEdge_Auto = HelloEdge_85 // HelloEdge_106 seems to be incompatible with this library
HelloEdge_85 = ClientHelloID{helloEdge, "85", nil}
HelloEdge_106 = ClientHelloID{helloEdge, "106", nil}
HelloEdge_85 = ClientHelloID{helloEdge, "85", nil, nil}
HelloEdge_106 = ClientHelloID{helloEdge, "106", nil, nil}

HelloSafari_Auto = HelloSafari_16_0
HelloSafari_16_0 = ClientHelloID{helloSafari, "16.0", nil}
HelloSafari_16_0 = ClientHelloID{helloSafari, "16.0", nil, nil}

Hello360_Auto = Hello360_7_5 // Hello360_11_0 seems to be incompatible with this library
Hello360_7_5 = ClientHelloID{hello360, "7.5", nil}
Hello360_11_0 = ClientHelloID{hello360, "11.0", nil}
Hello360_7_5 = ClientHelloID{hello360, "7.5", nil, nil}
Hello360_11_0 = ClientHelloID{hello360, "11.0", nil, nil}

HelloQQ_Auto = HelloQQ_11_1
HelloQQ_11_1 = ClientHelloID{helloQQ, "11.1", nil}
HelloQQ_11_1 = ClientHelloID{helloQQ, "11.1", nil, nil}
)

type Weights struct {
Extensions_Append_ALPN float64
TLSVersMax_Set_VersionTLS13 float64
CipherSuites_Remove_RandomCiphers float64
SigAndHashAlgos_Append_ECDSAWithSHA1 float64
SigAndHashAlgos_Append_ECDSAWithP521AndSHA512 float64
SigAndHashAlgos_Append_PSSWithSHA256 float64
SigAndHashAlgos_Append_PSSWithSHA384_PSSWithSHA512 float64
CurveIDs_Append_X25519 float64
CurveIDs_Append_CurveP521 float64
Extensions_Append_Padding float64
Extensions_Append_Status float64
Extensions_Append_SCT float64
Extensions_Append_Reneg float64
Extensions_Append_EMS float64
FirstKeyShare_Set_CurveP256 float64
Extensions_Append_ALPS float64
}

// Do not modify them directly as they may being used. If you
// want to use your custom weights, please make a copy first.
var DefaultWeights = Weights{
Extensions_Append_ALPN: 0.7,
TLSVersMax_Set_VersionTLS13: 0.4,
CipherSuites_Remove_RandomCiphers: 0.4,
SigAndHashAlgos_Append_ECDSAWithSHA1: 0.63,
SigAndHashAlgos_Append_ECDSAWithP521AndSHA512: 0.59,
SigAndHashAlgos_Append_PSSWithSHA256: 0.51,
SigAndHashAlgos_Append_PSSWithSHA384_PSSWithSHA512: 0.9,
CurveIDs_Append_X25519: 0.71,
CurveIDs_Append_CurveP521: 0.46,
Extensions_Append_Padding: 0.62,
Extensions_Append_Status: 0.74,
Extensions_Append_SCT: 0.46,
Extensions_Append_Reneg: 0.75,
Extensions_Append_EMS: 0.77,
FirstKeyShare_Set_CurveP256: 0.25,
Extensions_Append_ALPS: 0.33,
}

// based on spec's GreaseStyle, GREASE_PLACEHOLDER may be replaced by another GREASE value
// https://tools.ietf.org/html/draft-ietf-tls-grease-01
const GREASE_PLACEHOLDER = 0x0a0a
Expand Down
42 changes: 23 additions & 19 deletions u_parrots.go
Original file line number Diff line number Diff line change
Expand Up @@ -1847,7 +1847,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
default:
if id.Client == helloRandomized || id.Client == helloRandomizedALPN || id.Client == helloRandomizedNoALPN {
// Use empty values as they can be filled later by UConn.ApplyPreset or manually.
return generateRandomizedSpec(id, "", nil, nil)
return generateRandomizedSpec(&id, "", nil, nil)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the original ClientHelloID won't be changed, and it's expected too.

}
return ClientHelloSpec{}, errors.New("ClientHello ID " + id.Str() + " is unknown")
}
Expand Down Expand Up @@ -2076,11 +2076,11 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
}

func (uconn *UConn) generateRandomizedSpec() (ClientHelloSpec, error) {
return generateRandomizedSpec(uconn.ClientHelloID, uconn.serverName, uconn.HandshakeState.Session, uconn.config.NextProtos)
return generateRandomizedSpec(&uconn.ClientHelloID, uconn.serverName, uconn.HandshakeState.Session, uconn.config.NextProtos)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

func generateRandomizedSpec(
id ClientHelloID,
id *ClientHelloID,
gaukas marked this conversation as resolved.
Show resolved Hide resolved
serverName string,
session *ClientSessionState,
nextProtos []string,
Expand All @@ -2100,14 +2100,18 @@ func generateRandomizedSpec(
return p, err
}

if id.Weights == nil {
id.Weights = &DefaultWeights
}

var WithALPN bool
switch id.Client {
case helloRandomizedALPN:
WithALPN = true
case helloRandomizedNoALPN:
WithALPN = false
case helloRandomized:
if r.FlipWeightedCoin(0.7) {
if r.FlipWeightedCoin(id.Weights.Extensions_Append_ALPN) {
WithALPN = true
} else {
WithALPN = false
Expand All @@ -2123,7 +2127,7 @@ func generateRandomizedSpec(
return p, err
}

if r.FlipWeightedCoin(0.4) {
if r.FlipWeightedCoin(id.Weights.TLSVersMax_Set_VersionTLS13) {
p.TLSVersMin = VersionTLS10
p.TLSVersMax = VersionTLS13
tls13ciphers := make([]uint16, len(defaultCipherSuitesTLS13))
Expand All @@ -2141,7 +2145,7 @@ func generateRandomizedSpec(
p.TLSVersMax = VersionTLS12
}

p.CipherSuites = removeRandomCiphers(r, shuffledSuites, 0.4)
p.CipherSuites = removeRandomCiphers(r, shuffledSuites, id.Weights.CipherSuites_Remove_RandomCiphers)

sni := SNIExtension{serverName}
sessionTicket := SessionTicketExtension{Session: session}
Expand All @@ -2155,16 +2159,16 @@ func generateRandomizedSpec(
PKCS1WithSHA512,
}

if r.FlipWeightedCoin(0.63) {
if r.FlipWeightedCoin(id.Weights.SigAndHashAlgos_Append_ECDSAWithSHA1) {
sigAndHashAlgos = append(sigAndHashAlgos, ECDSAWithSHA1)
}
if r.FlipWeightedCoin(0.59) {
if r.FlipWeightedCoin(id.Weights.SigAndHashAlgos_Append_ECDSAWithP521AndSHA512) {
sigAndHashAlgos = append(sigAndHashAlgos, ECDSAWithP521AndSHA512)
}
if r.FlipWeightedCoin(0.51) || p.TLSVersMax == VersionTLS13 {
if r.FlipWeightedCoin(id.Weights.SigAndHashAlgos_Append_PSSWithSHA256) || p.TLSVersMax == VersionTLS13 {
// https://tools.ietf.org/html/rfc8446 says "...RSASSA-PSS (which is mandatory in TLS 1.3)..."
sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA256)
if r.FlipWeightedCoin(0.9) {
if r.FlipWeightedCoin(id.Weights.SigAndHashAlgos_Append_PSSWithSHA384_PSSWithSHA512) {
// these usually go together
sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA384)
sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA512)
Expand All @@ -2182,11 +2186,11 @@ func generateRandomizedSpec(
points := SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}}

curveIDs := []CurveID{}
if r.FlipWeightedCoin(0.71) || p.TLSVersMax == VersionTLS13 {
if r.FlipWeightedCoin(id.Weights.CurveIDs_Append_X25519) || p.TLSVersMax == VersionTLS13 {
curveIDs = append(curveIDs, X25519)
}
curveIDs = append(curveIDs, CurveP256, CurveP384)
if r.FlipWeightedCoin(0.46) {
if r.FlipWeightedCoin(id.Weights.CurveIDs_Append_CurveP521) {
curveIDs = append(curveIDs, CurveP521)
}

Expand All @@ -2212,28 +2216,28 @@ func generateRandomizedSpec(
p.Extensions = append(p.Extensions, &alpn)
}

if r.FlipWeightedCoin(0.62) || p.TLSVersMax == VersionTLS13 {
if r.FlipWeightedCoin(id.Weights.Extensions_Append_Padding) || p.TLSVersMax == VersionTLS13 {
// always include for TLS 1.3, since TLS 1.3 ClientHellos are often over 256 bytes
// and that's when padding is required to work around buggy middleboxes
p.Extensions = append(p.Extensions, &padding)
}
if r.FlipWeightedCoin(0.74) {
if r.FlipWeightedCoin(id.Weights.Extensions_Append_Status) {
p.Extensions = append(p.Extensions, &status)
}
if r.FlipWeightedCoin(0.46) {
if r.FlipWeightedCoin(id.Weights.Extensions_Append_SCT) {
p.Extensions = append(p.Extensions, &sct)
}
if r.FlipWeightedCoin(0.75) {
if r.FlipWeightedCoin(id.Weights.Extensions_Append_Reneg) {
p.Extensions = append(p.Extensions, &reneg)
}
if r.FlipWeightedCoin(0.77) {
if r.FlipWeightedCoin(id.Weights.Extensions_Append_EMS) {
p.Extensions = append(p.Extensions, &ems)
}
if p.TLSVersMax == VersionTLS13 {
ks := KeyShareExtension{[]KeyShare{
{Group: X25519}, // the key for the group will be generated later
}}
if r.FlipWeightedCoin(0.25) {
if r.FlipWeightedCoin(id.Weights.FirstKeyShare_Set_CurveP256) {
// do not ADD second keyShare because crypto/tls does not support multiple ecdheParams
// TODO: add it back when they implement multiple keyShares, or implement it oursevles
// ks.KeyShares = append(ks.KeyShares, KeyShare{Group: CurveP256})
Expand All @@ -2260,7 +2264,7 @@ func generateRandomizedSpec(
if err != nil {
return p, err
}
if r.FlipWeightedCoin(0.33) {
if r.FlipWeightedCoin(id.Weights.Extensions_Append_ALPS) {
// As with the ALPN case above, default to something popular
// (unlike ALPN, ALPS can't yet be specified in uconn.config).
alps := &ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}}
Expand Down