@anonympins/fingerprint 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +1 -1
  3. package/src/js/fingerprint.client.js +1271 -1191
  4. package/src/js/fingerprint.js +202 -108
  5. package/src/js/fingerprint.utils.js +213 -213
  6. package/src/js/library.js +24 -8
  7. package/src/js/mongodb-store.js +165 -79
  8. package/src/js/pow.solver.inline.js +500 -14
  9. package/src/js/pow.solver.js +323 -14
  10. package/src/js/problem-manager.js +1 -1
  11. package/src/js/redis-store.js +114 -42
  12. package/src/js/tests/dns-circuit-breaker.test.js +55 -0
  13. package/src/js/tests/fingerprint.client.init.test.js +143 -143
  14. package/src/js/tests/fingerprint.client.test.js +128 -128
  15. package/src/js/tests/fingerprint.test.js +32 -2
  16. package/src/js/tests/ip-reputation.test.js +1 -1
  17. package/src/js/tests/pow.solver.test.js +5 -2
  18. package/src/js/tests/usefulWorkSolver.test.js +162 -0
  19. package/src/php/Challenge/ChallengeUtils.php +21 -8
  20. package/src/php/FingerprintClient.php +22 -5
  21. package/src/php/FingerprintEngine.php +232 -56
  22. package/src/php/Optimization/Optimization.php +23 -16
  23. package/src/php/Optimization/OptimizationOperators.php +27 -0
  24. package/src/php/ProblemManager.php +1 -1
  25. package/src/php/Store/MongoDbStore.php +184 -104
  26. package/src/php/Store/RedisStore.php +91 -11
  27. package/src/php/Tests/ChallengeUtilsTest.php +285 -285
  28. package/src/php/Tests/DnsCircuitBreakerTest.php +106 -0
  29. package/src/php/Tests/IpReputationTest.php +1 -1
  30. package/src/php/Tests/PowTest.php +1 -1
  31. package/src/php/Tests/RequestUtilsTest.php +29 -0
  32. package/src/php/Tests/TLSClientHelloParserTest.php +177 -117
  33. package/src/php/Utils/BigInt.php +202 -202
  34. package/src/php/Utils/RequestUtils.php +74 -21
  35. package/src/php/Utils/TLSClientHelloParser.php +212 -116
@@ -1,117 +1,213 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Anonympins\Fingerprint\Utils;
6
-
7
- /**
8
- * Décode nativement un paquet TLS Client Hello binaire pour calculer l'empreinte JA3/JA4.
9
- */
10
- class TLSClientHelloParser
11
- {
12
- private const GREASE_VALUES = [
13
- 2570, 6682, 10794, 14906, 19018, 23130, 27242, 31354,
14
- 35466, 39578, 43690, 47802, 51914, 55926, 60038, 64150
15
- ];
16
-
17
- /**
18
- * Parse le Client Hello brut et retourne l'empreinte JA3 et une approximation JA4.
19
- *
20
- * @param string $binary Le premier paquet TCP reçu sur la socket.
21
- * @return array{ja3_string: string, ja3_hash: string, ja4_raw: string}|null
22
- */
23
- public static function parse(string $binary): ?array
24
- {
25
- $len = strlen($binary);
26
- if ($len < 43) {
27
- return null; // Paquet trop court
28
- }
29
-
30
- // 1. Vérification du Record Layer Type (0x16 = Handshake)
31
- if (ord($binary[0]) !== 0x16) {
32
- return null;
33
- }
34
-
35
- // 2. Vérification du Handshake Type (0x01 = Client Hello)
36
- if (ord($binary[5]) !== 0x01) {
37
- return null;
38
- }
39
-
40
- $offset = 43; // Sauter l'en-tête, la version et le Random Client (32 octets)
41
- if ($len < $offset + 1) return null;
42
-
43
- // 3. Lecture du Session ID
44
- $sessionLen = ord($binary[$offset]);
45
- $offset += 1 + $sessionLen;
46
- if ($len < $offset + 2) return null;
47
-
48
- // 4. Lecture des Cipher Suites
49
- $ciphersLen = unpack('n', substr($binary, $offset, 2))[1];
50
- $offset += 2;
51
- if ($len < $offset + $ciphersLen + 1) return null;
52
-
53
- $ciphers = [];
54
- for ($i = 0; $i < $ciphersLen; $i += 2) {
55
- $ciphers[] = unpack('n', substr($binary, $offset + $i, 2))[1];
56
- }
57
- $offset += $ciphersLen;
58
-
59
- // 5. Lecture des Compression Methods
60
- $compressionLen = ord($binary[$offset]);
61
- $offset += 1 + $compressionLen;
62
- if ($len < $offset + 2) return null;
63
-
64
- // 6. Lecture des Extensions
65
- $extensionsLen = unpack('n', substr($binary, $offset, 2))[1];
66
- $offset += 2;
67
-
68
- $extensions = [];
69
- $curves = [];
70
- $points = [];
71
-
72
- $extLimit = $offset + $extensionsLen;
73
- while ($offset < $extLimit && $offset + 4 <= $len) {
74
- $extType = unpack('n', substr($binary, $offset, 2))[1];
75
- $extLen = unpack('n', substr($binary, $offset + 2, 2))[1];
76
- $offset += 4;
77
-
78
- if ($offset + $extLen > $len) break;
79
-
80
- $extensions[] = $extType;
81
-
82
- if ($extType === 10) { // Extension Supported Groups (Elliptic Curves)
83
- if ($extLen >= 2) {
84
- $curvesLen = unpack('n', substr($binary, $offset, 2))[1];
85
- for ($j = 2; $j < $curvesLen + 2; $j += 2) {
86
- $curves[] = unpack('n', substr($binary, $offset + $j, 2))[1];
87
- }
88
- }
89
- } elseif ($extType === 11) { // Extension EC Point Formats
90
- if ($extLen >= 1) {
91
- $pointsLen = ord($binary[$offset]);
92
- for ($j = 1; $j < $pointsLen + 1; $j++) {
93
- $points[] = ord($binary[$offset + $j]);
94
- }
95
- }
96
- }
97
- $offset += $extLen;
98
- }
99
-
100
- // Nettoyage des valeurs GREASE (RFC 8701) pour la conformité JA3
101
- $filterGrease = fn(array $arr) => array_values(array_filter($arr, fn($v) => !in_array($v, self::GREASE_VALUES, true)));
102
-
103
- $sslVersion = unpack('n', substr($binary, 9, 2))[1];
104
- $ja3String = implode(',', [
105
- $sslVersion,
106
- implode('-', $filterGrease($ciphers)),
107
- implode('-', $filterGrease($extensions)),
108
- implode('-', $filterGrease($curves)),
109
- implode('-', $filterGrease($points))
110
- ]);
111
-
112
- return [
113
- 'ja3_string' => $ja3String,
114
- 'ja3_hash' => md5($ja3String)
115
- ];
116
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Utils;
6
+
7
+ /**
8
+ * Décode nativement un paquet TLS Client Hello binaire pour calculer l'empreinte JA3/JA4.
9
+ */
10
+ class TLSClientHelloParser
11
+ {
12
+ private const GREASE_VALUES = [
13
+ 2570, 6682, 10794, 14906, 19018, 23130, 27242, 31354,
14
+ 35466, 39578, 43690, 47802, 51914, 55926, 60038, 64150
15
+ ];
16
+
17
+ /**
18
+ * Parse le Client Hello brut et retourne l'empreinte JA3 et une approximation JA4.
19
+ *
20
+ * @param string $binary Le premier paquet TCP reçu sur la socket.
21
+ * @return array{ja3_string: string, ja3_hash: string, ja4_raw: string}|null
22
+ */
23
+ public static function parse(string $binary): ?array
24
+ {
25
+ $len = strlen($binary);
26
+ if ($len < 43) {
27
+ return null; // Paquet trop court
28
+ }
29
+
30
+ // 1. Vérification du Record Layer Type (0x16 = Handshake)
31
+ if (ord($binary[0]) !== 0x16) {
32
+ return null;
33
+ }
34
+
35
+ // 2. Vérification du Handshake Type (0x01 = Client Hello)
36
+ if (ord($binary[5]) !== 0x01) {
37
+ return null;
38
+ }
39
+
40
+ $offset = 43; // Sauter l'en-tête, la version et le Random Client (32 octets)
41
+ if ($len < $offset + 1) return null;
42
+
43
+ // 3. Lecture du Session ID
44
+ $sessionLen = ord($binary[$offset]);
45
+ $offset += 1 + $sessionLen;
46
+ if ($len < $offset + 2) return null;
47
+
48
+ // 4. Lecture des Cipher Suites
49
+ $ciphersLen = unpack('n', substr($binary, $offset, 2))[1];
50
+ $offset += 2;
51
+ if ($len < $offset + $ciphersLen + 1) return null;
52
+
53
+ $ciphers = [];
54
+ for ($i = 0; $i < $ciphersLen; $i += 2) {
55
+ $ciphers[] = unpack('n', substr($binary, $offset + $i, 2))[1];
56
+ }
57
+ $offset += $ciphersLen;
58
+
59
+ // 5. Lecture des Compression Methods
60
+ $compressionLen = ord($binary[$offset]);
61
+ $offset += 1 + $compressionLen;
62
+ if ($len < $offset + 2) return null;
63
+
64
+ // 6. Lecture des Extensions
65
+ $extensionsLen = unpack('n', substr($binary, $offset, 2))[1];
66
+ $offset += 2;
67
+
68
+ $extensions = [];
69
+ $curves = [];
70
+ $points = [];
71
+ $sigAlgs = [];
72
+ $supportedVersions = [];
73
+ $hasSni = false;
74
+ $alpnProtocol = '';
75
+
76
+ $extLimit = $offset + $extensionsLen;
77
+ while ($offset < $extLimit && $offset + 4 <= $len) {
78
+ $extType = unpack('n', substr($binary, $offset, 2))[1];
79
+ $extLen = unpack('n', substr($binary, $offset + 2, 2))[1];
80
+ $offset += 4;
81
+
82
+ if ($offset + $extLen > $len) break;
83
+
84
+ $extensions[] = $extType;
85
+
86
+ if ($extType === 0) {
87
+ $hasSni = true;
88
+ } elseif ($extType === 10) { // Extension Supported Groups (Elliptic Curves)
89
+ if ($extLen >= 2) {
90
+ $curvesLen = unpack('n', substr($binary, $offset, 2))[1];
91
+ for ($j = 2; $j < $curvesLen + 2; $j += 2) {
92
+ $curves[] = unpack('n', substr($binary, $offset + $j, 2))[1];
93
+ }
94
+ }
95
+ } elseif ($extType === 11) { // Extension EC Point Formats
96
+ if ($extLen >= 1) {
97
+ $pointsLen = ord($binary[$offset]);
98
+ for ($j = 1; $j < $pointsLen + 1; $j++) {
99
+ $points[] = ord($binary[$offset + $j]);
100
+ }
101
+ }
102
+ } elseif ($extType === 13) { // Signature Algorithms
103
+ if ($extLen >= 2 && $offset + 2 <= $len) {
104
+ $sigAlgsLen = unpack('n', substr($binary, $offset, 2))[1];
105
+ for ($j = 2; $j < $sigAlgsLen + 2; $j += 2) {
106
+ if ($offset + $j + 2 <= $len && $j + 2 <= $extLen) {
107
+ $sigAlgs[] = unpack('n', substr($binary, $offset + $j, 2))[1];
108
+ }
109
+ }
110
+ }
111
+ } elseif ($extType === 16) { // ALPN
112
+ if ($extLen >= 3 && $offset + 2 <= $len) {
113
+ $alpnListLen = unpack('n', substr($binary, $offset, 2))[1];
114
+ if ($extLen >= 2 + $alpnListLen && $offset + 2 + $alpnListLen <= $len) {
115
+ $alpnStrLen = ord($binary[$offset + 2]);
116
+ if ($alpnListLen >= 1 + $alpnStrLen) {
117
+ $alpnProtocol = substr($binary, $offset + 3, $alpnStrLen);
118
+ }
119
+ }
120
+ }
121
+ } elseif ($extType === 43) { // Supported Versions
122
+ if ($extLen >= 1 && $offset + 1 <= $len) {
123
+ $versionsLen = ord($binary[$offset]);
124
+ for ($j = 1; $j < $versionsLen + 1; $j += 2) {
125
+ if ($offset + $j + 2 <= $len && $j + 2 <= $extLen) {
126
+ $supportedVersions[] = unpack('n', substr($binary, $offset + $j, 2))[1];
127
+ }
128
+ }
129
+ }
130
+ }
131
+ $offset += $extLen;
132
+ }
133
+
134
+ // Nettoyage des valeurs GREASE (RFC 8701) pour la conformité JA3
135
+ $filterGrease = fn(array $arr) => array_values(array_filter($arr, fn($v) => !in_array($v, self::GREASE_VALUES, true)));
136
+
137
+ $cleanCiphers = $filterGrease($ciphers);
138
+ $cleanExtensions = $filterGrease($extensions);
139
+ $cleanCurves = $filterGrease($curves);
140
+ $cleanPoints = $filterGrease($points);
141
+ $cleanSigAlgs = $filterGrease($sigAlgs);
142
+ $cleanSupportedVersions = $filterGrease($supportedVersions);
143
+
144
+ $sslVersion = unpack('n', substr($binary, 9, 2))[1];
145
+ $ja3String = implode(',', [
146
+ $sslVersion,
147
+ implode('-', $cleanCiphers),
148
+ implode('-', $cleanExtensions),
149
+ implode('-', $cleanCurves),
150
+ implode('-', $cleanPoints)
151
+ ]);
152
+
153
+ // Calcul natif de JA4
154
+ $highestVersion = $sslVersion;
155
+ if (!empty($cleanSupportedVersions)) {
156
+ $highestVersion = max($cleanSupportedVersions);
157
+ }
158
+
159
+ $ja4Version = "12";
160
+ if ($highestVersion === 0x0304) {
161
+ $ja4Version = "13";
162
+ } elseif ($highestVersion === 0x0303) {
163
+ $ja4Version = "12";
164
+ } elseif ($highestVersion === 0x0302) {
165
+ $ja4Version = "11";
166
+ } elseif ($highestVersion === 0x0301) {
167
+ $ja4Version = "10";
168
+ }
169
+
170
+ $sniStatus = $hasSni ? "d" : "i";
171
+ $numCiphers = min(99, count($cleanCiphers));
172
+ $numExtensions = min(99, count($cleanExtensions));
173
+
174
+ $ja4Alpn = "00";
175
+ if ($alpnProtocol !== '') {
176
+ $lenAlpn = strlen($alpnProtocol);
177
+ if ($lenAlpn === 1) {
178
+ $ja4Alpn = $alpnProtocol . $alpnProtocol;
179
+ } else {
180
+ $ja4Alpn = $alpnProtocol[0] . $alpnProtocol[$lenAlpn - 1];
181
+ }
182
+ }
183
+
184
+ $ja4_a = "t" . $ja4Version . $sniStatus . sprintf("%02d", $numCiphers) . sprintf("%02d", $numExtensions) . $ja4Alpn;
185
+
186
+ $sortedCiphers = $cleanCiphers;
187
+ sort($sortedCiphers);
188
+ $ciphersHex = array_map(fn($c) => sprintf('%04x', $c), $sortedCiphers);
189
+ $ciphersString = implode(',', $ciphersHex);
190
+ $ja4_b = substr(hash('sha256', $ciphersString), 0, 12);
191
+
192
+ $sortedExtensions = $cleanExtensions;
193
+ sort($sortedExtensions);
194
+ $extensionsHex = array_map(fn($e) => sprintf('%04x', $e), $sortedExtensions);
195
+ $extensionsString = implode(',', $extensionsHex);
196
+
197
+ $sortedSigAlgs = $cleanSigAlgs;
198
+ sort($sortedSigAlgs);
199
+ $sigAlgsHex = array_map(fn($s) => sprintf('%04x', $s), $sortedSigAlgs);
200
+ $sigAlgsString = implode(',', $sigAlgsHex);
201
+
202
+ $ja4_c_input = $extensionsString . '_' . $sigAlgsString;
203
+ $ja4_c = substr(hash('sha256', $ja4_c_input), 0, 12);
204
+
205
+ $ja4_hash = $ja4_a . "_" . $ja4_b . "_" . $ja4_c;
206
+
207
+ return [
208
+ 'ja3_string' => $ja3String,
209
+ 'ja3_hash' => md5($ja3String),
210
+ 'ja4_raw' => $ja4_hash
211
+ ];
212
+ }
117
213
  }