Skip to content

Commit

Permalink
fixes #131: correctly instantiate GREASE Extension when using more th…
Browse files Browse the repository at this point in the history
…an one GREASE Extension in ja3;
  • Loading branch information
bogdanfinn committed Nov 18, 2024
1 parent 38ad3c6 commit e125f19
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 66 deletions.
98 changes: 98 additions & 0 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func main() {
postAsTlsClient()
requestWithFollowRedirectSwitch()
requestWithCustomClient()
requestWithJa3CustomClientWithTwoGreaseExtensions()
rotateProxiesOnClient()
downloadImageWithTlsClient()
testPskExtension()
Expand Down Expand Up @@ -689,6 +690,103 @@ func requestWithCustomClient() {
log.Printf("requesting topps as customClient1 => status code: %d\n", resp.StatusCode)
}

func requestWithJa3CustomClientWithTwoGreaseExtensions() {
settings := map[http2.SettingID]uint32{
http2.SettingHeaderTableSize: 65536,
http2.SettingEnablePush: 0,
http2.SettingInitialWindowSize: 6291456,
http2.SettingMaxHeaderListSize: 262144,
}
settingsOrder := []http2.SettingID{
http2.SettingHeaderTableSize,
http2.SettingEnablePush,
http2.SettingInitialWindowSize,
http2.SettingMaxHeaderListSize,
}

pseudoHeaderOrder := []string{
":method",
":authority",
":scheme",
":path",
}

connectionFlow := uint32(15663105)

ja3String := "771,2570-4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,2570-18-5-27-11-0-10-35-16-65037-51-13-23-43-17513-65281-45-2570,2570-25497-29-23-24,0"

supportedSignatureAlgorithms := []string{
"ECDSAWithP256AndSHA256",
"PSSWithSHA256",
"PKCS1WithSHA256",
"ECDSAWithP384AndSHA384",
"PSSWithSHA384",
"PKCS1WithSHA384",
"PSSWithSHA512",
"PKCS1WithSHA512",
}
var supportedDelegatedCredentialsAlgorithms []string
supportedVersions := []string{"GREASE", "1.3", "1.2"}
keyShareCurves := []string{"GREASE", "X25519Kyber768", "X25519"}
supportedProtocolsALPN := []string{"h2", "http/1.1"}
supportedProtocolsALPS := []string{"h2"}
echCandidateCipherSuites := []tls_client.CandidateCipherSuites{
{
KdfId: "HKDF_SHA256",
AeadId: "AEAD_AES_128_GCM",
},
{
KdfId: "HKDF_SHA256",
AeadId: "AEAD_CHACHA20_POLY1305",
},
}
candidatePayloads := []uint16{128, 160, 192, 224}
certCompressionAlgo := "brotli"

specFactory, err := tls_client.GetSpecFactoryFromJa3String(ja3String, supportedSignatureAlgorithms, supportedDelegatedCredentialsAlgorithms, supportedVersions, keyShareCurves, supportedProtocolsALPN, supportedProtocolsALPS, echCandidateCipherSuites, candidatePayloads, certCompressionAlgo)

customClientProfile := profiles.NewClientProfile(tls.ClientHelloID{
Client: "MyCustomProfile",
Version: "1",
Seed: nil,
SpecFactory: specFactory,
}, settings, settingsOrder, pseudoHeaderOrder, connectionFlow, nil, nil)

options := []tls_client.HttpClientOption{
tls_client.WithTimeoutSeconds(60),
tls_client.WithClientProfile(customClientProfile), // use custom profile here
}

client, err := tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
if err != nil {
log.Println(err)
return
}

req, err := http.NewRequest(http.MethodGet, "https://tls.browserleaks.com/tls", nil)
if err != nil {
log.Println(err)
return
}

resp, err := client.Do(req)
if err != nil {
log.Println(err)
return
}

defer resp.Body.Close()

readBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Println(err)
return
}

log.Printf(string(readBytes))
log.Printf("status code: %d\n", resp.StatusCode)
}

func testPskExtension() {
settings := map[http2.SettingID]uint32{
http2.SettingHeaderTableSize: 65536,
Expand Down
12 changes: 10 additions & 2 deletions ja3.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ func stringToSpec(ja3 string, signatureAlgorithms []tls.SignatureScheme, delegat
return tls.ClientHelloSpec{}, err
}

if uint16(eId) == tls.GREASE_PLACEHOLDER {
// if we use multiple grease extensions with need to generate always a new value. therefore we are creating a new instance here
exts = append(exts, &tls.UtlsGREASEExtension{})
continue
}

te, ok := extMap[uint16(eId)]
if !ok {
return tls.ClientHelloSpec{}, fmt.Errorf("unknown extension with id %s provided", e)
Expand All @@ -211,15 +217,17 @@ func stringToSpec(ja3 string, signatureAlgorithms []tls.SignatureScheme, delegat

return tls.ClientHelloSpec{
CipherSuites: suites,
CompressionMethods: []byte{0},
CompressionMethods: []byte{tls.CompressionNone},
Extensions: exts,
GetSessionID: sha256.Sum256,
}, nil
}

func getExtensionBaseMap() map[uint16]tls.TLSExtension {
return map[uint16]tls.TLSExtension{
tls.GREASE_PLACEHOLDER: &tls.UtlsGREASEExtension{},
// This extension needs to be instantiated every time and not be reused if it occurs multiple times in the same ja3
//tls.GREASE_PLACEHOLDER: &tls.UtlsGREASEExtension{},

tls.ExtensionServerName: &tls.SNIExtension{},
tls.ExtensionStatusRequest: &tls.StatusRequestExtension{},

Expand Down
Loading

0 comments on commit e125f19

Please sign in to comment.