diff --git a/README.md b/README.md
@@ -129,12 +129,22 @@ preset actually emits. Re-run the probe after upgrading the utls dependency.
| `chrome-133` | `t13d1516h2_8daaf6152771_d8a2da3f94cd` |
| `firefox-120` | `t13d1715h2_5b57614c22b0_5c2c66f702b0` |
| `safari-16` | `t13d2014h2_a09f3c656075_14788d8d241b` |
+| `ios-16` | same as `safari-16`² |
+| `edge-85` | `t13d1515h2_8daaf6152771_de4a06bb82e3` |
| `edge-106` | `t13d1516h2_8daaf6152771_e5627efa2ab1` |
+| `opera-91` | same as `edge-106`³ |
+| `android-11-okhttp` | `t12d120700_d34a8e72043a_036209cd1ead` (TLS 1.2) |
> ¹ `chrome-120-pq` and `chrome-131` produce the same JA4 as `chrome-120` because JA4
> does not distinguish key share entries. Use `chrome-120-pq` when you specifically need
> the X25519MLKEM768 post-quantum key exchange; use `chrome-131` for the most current
> non-PQ Chrome preset under this hash.
+>
+> ² `ios-16` collides with `safari-16` under JA4 (both ship the same TLS stack).
+> Pick whichever matches the user-agent you intend to imitate.
+>
+> ³ `opera-91` collides with `edge-106` (both are Chromium derivatives with matching
+> ClientHello shape). Same logic as above.
---
diff --git a/fingerprint/tls.go b/fingerprint/tls.go
@@ -8,13 +8,17 @@ import (
// NameTable maps human-readable profile names to utls ClientHelloID presets.
var NameTable = map[string]utls.ClientHelloID{
- "chrome-120": utls.HelloChrome_120,
- "chrome-120-pq": utls.HelloChrome_120_PQ,
- "chrome-131": utls.HelloChrome_131,
- "chrome-133": utls.HelloChrome_133,
- "firefox-120": utls.HelloFirefox_120,
- "safari-16": utls.HelloSafari_16_0,
- "edge-106": utls.HelloEdge_106,
+ "chrome-120": utls.HelloChrome_120,
+ "chrome-120-pq": utls.HelloChrome_120_PQ,
+ "chrome-131": utls.HelloChrome_131,
+ "chrome-133": utls.HelloChrome_133,
+ "firefox-120": utls.HelloFirefox_120,
+ "safari-16": utls.HelloSafari_16_0,
+ "ios-16": utls.HelloIOS_16_0,
+ "edge-85": utls.HelloEdge_85,
+ "edge-106": utls.HelloEdge_106,
+ "opera-91": utls.HelloOpera_91,
+ "android-11-okhttp": utls.HelloAndroid_11_OkHttp,
}
type TLSFingerprint struct {
diff --git a/fingerprint/tls_test.go b/fingerprint/tls_test.go
@@ -34,7 +34,14 @@ func TestName_ReturnsProfile(t *testing.T) {
}
func TestNameTable_HasExpectedProfiles(t *testing.T) {
- expected := []string{"chrome-120", "chrome-120-pq", "firefox-120", "safari-16", "edge-106"}
+ expected := []string{
+ "chrome-120", "chrome-120-pq", "chrome-131", "chrome-133",
+ "firefox-120",
+ "safari-16", "ios-16",
+ "edge-85", "edge-106",
+ "opera-91",
+ "android-11-okhttp",
+ }
for _, name := range expected {
if _, ok := fingerprint.NameTable[name]; !ok {
t.Errorf("NameTable missing profile %q", name)
diff --git a/proxy/ja4_integration_test.go b/proxy/ja4_integration_test.go
@@ -14,14 +14,19 @@ import (
"github.com/djnnvx/mic/fingerprint"
)
-// Values measured empirically via cmd/probe against
-// tlsinfo.me). chrome-120-pq is absent: it emits the same JA4 as chrome-120
+// Values measured empirically via cmd/probe against tlsinfo.me.
+// chrome-120-pq and chrome-131 are absent: they emit the same JA4 as chrome-120
// because JA4 does not distinguish the X25519MLKEM768 key share.
var expectedJA4 = map[string]string{
- "chrome-120": "t13d1516h2_8daaf6152771_02713d6af862",
- "firefox-120": "t13d1715h2_5b57614c22b0_5c2c66f702b0",
- "safari-16": "t13d2014h2_a09f3c656075_14788d8d241b",
- "edge-106": "t13d1516h2_8daaf6152771_e5627efa2ab1",
+ "chrome-120": "t13d1516h2_8daaf6152771_02713d6af862",
+ "chrome-133": "t13d1516h2_8daaf6152771_d8a2da3f94cd",
+ "firefox-120": "t13d1715h2_5b57614c22b0_5c2c66f702b0",
+ "safari-16": "t13d2014h2_a09f3c656075_14788d8d241b",
+ "ios-16": "t13d2014h2_a09f3c656075_14788d8d241b",
+ "edge-85": "t13d1515h2_8daaf6152771_de4a06bb82e3",
+ "edge-106": "t13d1516h2_8daaf6152771_e5627efa2ab1",
+ "opera-91": "t13d1516h2_8daaf6152771_e5627efa2ab1",
+ "android-11-okhttp": "t12d120700_d34a8e72043a_036209cd1ead",
}
// TestClientFront_JA4 verifies that each fingerprint profile produces the
| | | |