mic.git


mic

~ proxy for JA4-TLS fingerprinting evasion
git clone https://github.com/djnnvx/mic.git
log | files | refs | archive | README | LICENSE

commit 4380d634e424cbd175dbcf8eea6a54df99a5e683
parent 70d7f80b6cb24baf8aba2318f8c42a093ab7089d
Author: djnn <[email protected]>
Date: Sun, 17 May 2026 18:57:51 +0900

feat(utls): update lib+expose new profiles

Diffstat:
MREADME.md | 10++++++++++
Mfingerprint/tls.go | 18+++++++++++-------
Mfingerprint/tls_test.go | 9++++++++-
Mproxy/ja4_integration_test.go | 17+++++++++++------

4 files changed, 40 insertions(+), 14 deletions(-)

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