@anonympins/fingerprint 0.7.4 → 0.7.5
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.
- package/CHANGELOG.md +27 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/public/fingerprint-anti-bot.zip +0 -0
- package/src/js/dynamic-wasm.js +218 -218
- package/src/js/fingerprint.client.js +77 -43
- package/src/js/fingerprint.client.obfuscated.js +1 -1
- package/src/js/fingerprint.js +291 -71
- package/src/js/tests/dynamic-wasm.test.js +77 -77
- package/src/js/tests/fingerprint.client.init.test.js +78 -0
- package/src/js/tests/fingerprint.test.js +18 -0
- package/src/js/tests/threatIntel.test.js +40 -1
- package/src/php/Challenge/ChallengeUtils.php +10 -0
- package/src/php/Config/SecurityProfiles.php +5 -0
- package/src/php/FingerprintEngine.php +186 -37
- package/src/php/Tests/ThreatIntelTest.php +34 -0
- package/src/php/Utils/RequestUtils.php +48 -0
- package/src/php/WordPress/fingerprint-anti-bot.php +1 -1
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +1 -1
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +1 -1
- package/src/php/WordPress/readme.txt +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
## Version 0.7.5
|
|
2
|
+
|
|
3
|
+
- 🛡️ **Differential Privacy on Federated Threat Intelligence**:
|
|
4
|
+
- **Laplacian Noise on Federation Timestamps ($\text{Laplace}(0, b)$)**: Added mathematical perturbation to exact event timestamps in federated ban exchanges (`share_threat_intel`) parameterized by sensitivity $\Delta t = 5000\text{ms}$ and privacy budget $\epsilon$ (`differentialPrivacy.epsilon`). Timestamps are bounded within a safe $\pm 60\text{s}$ window to satisfy anti-replay checks while completely preventing malicious or compromised federation nodes from reconstructing inter-domain browsing trajectories via millisecond timing correlation.
|
|
5
|
+
- **Randomized Response Decoy Injection (Chaffing)**: Implemented randomized response logic where nodes inject and broadcast synthetic ZKP public keys generated uniformly in $\mathbb{Z}_p$ with probability $p = 1 / (1 + e^{\epsilon})$ (or customizable `dummyRate`). Due to the negligible collision probability of 256-bit keys ($\approx 2^{-256}$), decoys never reach the collaborative consensus threshold ($K \ge 3$), guaranteeing mathematical plausible deniability without causing false positives.
|
|
6
|
+
- **Multi-Runtime Parity**: Fully implemented across JavaScript (`fingerprint.js`), PHP (`FingerprintEngine.php`), Python (`engine.py`), and Java (`FingerprintEngine.java`, `FingerprintProperties.java`).
|
|
7
|
+
|
|
8
|
+
- 🌐 **Network Layer Anomaly Detection**:
|
|
9
|
+
- **MTU Anomaly Scoring (`mtuAnomalyScore`)**: Introduced `mtuAnomalyScore` to the suspicion vector and configuration weights across all runtimes. Evaluates passive TCP SYN options (specifically MSS — Maximum Segment Size) to detect anomalous MTU values typical of VPN/GRE tunneling, WireGuard encapsulation, or residential proxy backconnect chains attempting to masquerade as standard domestic browser connections.
|
|
10
|
+
|
|
11
|
+
- ♿ **Accessibility (a11y) & Invisible Honeypot Hardening**:
|
|
12
|
+
- **Screen-Reader Compliant Honeypot Fields**: Hardened dynamic DOM honeypot generation within `FingerprintClient` with `aria-hidden="true"`, `tabindex="-1"`, and `autocomplete="off"` attributes combined with defensive offscreen CSS positioning (`position: absolute; left: -9999px; transform: scale(0); opacity: 0; pointer-events: none;`). Ensures assistive technologies (NVDA, JAWS, VoiceOver) and browser autofill routines ignore honeypot inputs for visually impaired users while maintaining a 100% trap catch rate against automated scrapers.
|
|
13
|
+
|
|
14
|
+
- 🤖 **Crawler Whitelist Updates & Multi-Language Parity**:
|
|
15
|
+
- **Meta / Facebook Whitelist Addition**: Added dedicated IP subnets and CIDR definitions for Meta and Facebook crawlers (`facebookexternalhit`, `31.13.64.0/18`, `66.220.144.0/20`, `69.63.176.0/20`, `157.240.0.0/16`) to guarantee smooth link preview generation and indexing without challenge interruptions.
|
|
16
|
+
- **Cross-Platform Whitelist Synchronization**: Standardized crawler allowlists and DNS verification workflows (Googlebot, Bingbot, Yandex, Facebook) across all supported backend engines (Node.js, PHP, Python, and Java).
|
|
17
|
+
|
|
18
|
+
- 🔧 **Core Engine Refinements**:
|
|
19
|
+
- **`fingerprint.js` Updates**: Synchronized client-side metric collectors, refined behavioral input tracking, and plugged federation broadcast hooks directly into the core security pipeline.
|
|
20
|
+
- **Java Configuration Enhancements**: Added missing getters/setters and mapped configuration fields (`quicAnomalyScore`, `virtualizationScore`, `differentialPrivacy`, `federatedPeers`) in `FingerprintProperties.java`.
|
|
21
|
+
- **PHP Store Lifecycle**: Resolved initialization order edge cases for store instances during request processing in `FingerprintEngine.php`.
|
|
22
|
+
|
|
23
|
+
- 📖 **Documentation**:
|
|
24
|
+
- **Configuration Options Reference (`full_options.md`)**: Documented all new settings, including `mtuAnomalyScore`, `differentialPrivacy` block (`enabled`, `epsilon`, `dummyRate`), and federated threat sharing parameters.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
1
28
|
## Version 0.7.4
|
|
2
29
|
|
|
3
30
|
- 🛡️ **Security & Rate Limiting**:
|
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anonympins/fingerprint",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Advanced anti-bot library for Node.js using multi-layer fingerprinting (JA3, client-side, headers), behavioral analysis, and adaptive Proof-of-Work (PoW) challenges to mitigate scraping, scalping, and automated threats.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
Binary file
|
package/src/js/dynamic-wasm.js
CHANGED
|
@@ -1,219 +1,219 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
|
|
3
|
-
function encodeULEB128(val) {
|
|
4
|
-
const bytes = [];
|
|
5
|
-
let num = val >>> 0;
|
|
6
|
-
do {
|
|
7
|
-
let byte = num & 0x7f;
|
|
8
|
-
num >>>= 7;
|
|
9
|
-
if (num !== 0) byte |= 0x80;
|
|
10
|
-
bytes.push(byte);
|
|
11
|
-
} while (num !== 0);
|
|
12
|
-
return bytes;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function encodeSLEB128(val) {
|
|
16
|
-
const bytes = [];
|
|
17
|
-
let num = val | 0;
|
|
18
|
-
while (true) {
|
|
19
|
-
let byte = num & 0x7f;
|
|
20
|
-
num >>= 7;
|
|
21
|
-
if ((num === 0 && (byte & 0x40) === 0) || (num === -1 && (byte & 0x40) !== 0)) {
|
|
22
|
-
bytes.push(byte);
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
bytes.push(byte | 0x80);
|
|
26
|
-
}
|
|
27
|
-
return bytes;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getRandomNonTrivialOpcodes(tempLocal) {
|
|
31
|
-
const insts = [];
|
|
32
|
-
const ops = [
|
|
33
|
-
// local.get tempLocal, i32.const rand, rotl, local.set tempLocal
|
|
34
|
-
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(1, 32)), 0x77, 0x21, tempLocal],
|
|
35
|
-
// local.get tempLocal, i32.const rand, rotr, local.set tempLocal
|
|
36
|
-
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(1, 32)), 0x78, 0x21, tempLocal],
|
|
37
|
-
// local.get tempLocal, i32.const rand, xor, local.set tempLocal
|
|
38
|
-
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(0, 10000)), 0x73, 0x21, tempLocal],
|
|
39
|
-
// local.get tempLocal, popcnt, i32.const rand, mul, local.set tempLocal
|
|
40
|
-
() => [0x20, tempLocal, 0x69, 0x41, ...encodeSLEB128(crypto.randomInt(1, 1001)), 0x6c, 0x21, tempLocal],
|
|
41
|
-
// local.get tempLocal, clz, local.get tempLocal, ctz, add, local.set tempLocal
|
|
42
|
-
() => [0x20, tempLocal, 0x67, 0x20, tempLocal, 0x68, 0x6a, 0x21, tempLocal]
|
|
43
|
-
];
|
|
44
|
-
const count = crypto.randomInt(2, 5);
|
|
45
|
-
for (let i = 0; i < count; i++) {
|
|
46
|
-
const op = ops[crypto.randomInt(0, ops.length)];
|
|
47
|
-
insts.push(...op());
|
|
48
|
-
}
|
|
49
|
-
return insts;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function generatePolymorphicInstructions(stateLocal = 4, tempLocal = 5) {
|
|
53
|
-
const insts = [];
|
|
54
|
-
|
|
55
|
-
// Initialize tempLocal with a random value
|
|
56
|
-
const initVal = crypto.randomInt(-500, 500);
|
|
57
|
-
insts.push(0x41, ...encodeSLEB128(initVal), 0x21, tempLocal);
|
|
58
|
-
|
|
59
|
-
// Initialize stateLocal to 0
|
|
60
|
-
insts.push(0x41, ...encodeSLEB128(0), 0x21, stateLocal);
|
|
61
|
-
|
|
62
|
-
// Loop & Block for state machine
|
|
63
|
-
insts.push(0x03, 0x40); // loop
|
|
64
|
-
insts.push(0x02, 0x40); // block
|
|
65
|
-
|
|
66
|
-
for (let state = 0; state < 3; state++) {
|
|
67
|
-
insts.push(0x20, stateLocal, 0x41, ...encodeSLEB128(state), 0x46); // state == expected
|
|
68
|
-
insts.push(0x04, 0x40); // if
|
|
69
|
-
insts.push(...getRandomNonTrivialOpcodes(tempLocal));
|
|
70
|
-
insts.push(0x41, ...encodeSLEB128(state + 1), 0x21, stateLocal); // transit to state + 1
|
|
71
|
-
insts.push(0x0c, ...encodeULEB128(2)); // br 2 (targets loop start)
|
|
72
|
-
insts.push(0x0b); // end if
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Fallthrough / Default break (targets block depth 0, which exits the loop)
|
|
76
|
-
insts.push(0x0c, ...encodeULEB128(0));
|
|
77
|
-
|
|
78
|
-
// end block
|
|
79
|
-
insts.push(0x0b);
|
|
80
|
-
// end loop
|
|
81
|
-
insts.push(0x0b);
|
|
82
|
-
|
|
83
|
-
return insts;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export class DynamicWasmGenerator {
|
|
87
|
-
/**
|
|
88
|
-
* Generates a unique polymorphic WebAssembly module containing a custom hash function
|
|
89
|
-
* with randomized constants and control flow variables.
|
|
90
|
-
* @param {object} constants - Custom seed, multiplier and adder.
|
|
91
|
-
* @returns {Buffer} Valid WebAssembly binary buffer.
|
|
92
|
-
*/
|
|
93
|
-
static generate(constants) {
|
|
94
|
-
const { seed, multiplier, adder } = constants;
|
|
95
|
-
|
|
96
|
-
const preLoopPoly = generatePolymorphicInstructions(4, 5);
|
|
97
|
-
const midLoopPoly = generatePolymorphicInstructions(4, 5);
|
|
98
|
-
|
|
99
|
-
const inst = [
|
|
100
|
-
0x01, 0x06, 0x7f, // Locals: 1 entry of 6 locals of type i32
|
|
101
|
-
...preLoopPoly,
|
|
102
|
-
// h = seed
|
|
103
|
-
0x41, ...encodeSLEB128(seed),
|
|
104
|
-
0x21, 0x03,
|
|
105
|
-
// i = 0
|
|
106
|
-
0x41, 0x00,
|
|
107
|
-
0x21, 0x02,
|
|
108
|
-
|
|
109
|
-
// block
|
|
110
|
-
0x02, 0x40,
|
|
111
|
-
// loop
|
|
112
|
-
0x03, 0x40,
|
|
113
|
-
|
|
114
|
-
// if i >= len break
|
|
115
|
-
0x20, 0x02,
|
|
116
|
-
0x20, 0x01,
|
|
117
|
-
0x4f,
|
|
118
|
-
0x0d, 0x01,
|
|
119
|
-
|
|
120
|
-
// byte = load8_u(ptr + i)
|
|
121
|
-
0x20, 0x00,
|
|
122
|
-
0x20, 0x02,
|
|
123
|
-
0x6a,
|
|
124
|
-
0x2d, 0x00, 0x00,
|
|
125
|
-
|
|
126
|
-
// h = h ^ byte
|
|
127
|
-
0x20, 0x03,
|
|
128
|
-
0x73,
|
|
129
|
-
|
|
130
|
-
// h = h * multiplier
|
|
131
|
-
0x41, ...encodeSLEB128(multiplier),
|
|
132
|
-
0x6c,
|
|
133
|
-
|
|
134
|
-
// h = h + adder
|
|
135
|
-
0x41, ...encodeSLEB128(adder),
|
|
136
|
-
0x6a,
|
|
137
|
-
|
|
138
|
-
// local.set 3
|
|
139
|
-
0x21, 0x03,
|
|
140
|
-
...midLoopPoly,
|
|
141
|
-
|
|
142
|
-
// i = i + 1
|
|
143
|
-
0x20, 0x02,
|
|
144
|
-
0x41, 0x01,
|
|
145
|
-
0x6a,
|
|
146
|
-
0x21, 0x02,
|
|
147
|
-
|
|
148
|
-
// br 0
|
|
149
|
-
0x0c, 0x00,
|
|
150
|
-
|
|
151
|
-
0x0b, // end loop
|
|
152
|
-
0x0b, // end block
|
|
153
|
-
|
|
154
|
-
// return h
|
|
155
|
-
0x20, 0x03,
|
|
156
|
-
0x0b // end function
|
|
157
|
-
];
|
|
158
|
-
|
|
159
|
-
const funcBody = [
|
|
160
|
-
...encodeULEB128(inst.length),
|
|
161
|
-
...inst
|
|
162
|
-
];
|
|
163
|
-
|
|
164
|
-
const codeSectionPayload = [
|
|
165
|
-
...encodeULEB128(1),
|
|
166
|
-
...funcBody
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
const codeSection = [
|
|
170
|
-
0x0a,
|
|
171
|
-
...encodeULEB128(codeSectionPayload.length),
|
|
172
|
-
...codeSectionPayload
|
|
173
|
-
];
|
|
174
|
-
|
|
175
|
-
const typeSection = [
|
|
176
|
-
0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f
|
|
177
|
-
];
|
|
178
|
-
|
|
179
|
-
const funcSection = [
|
|
180
|
-
0x03, 0x02, 0x01, 0x00
|
|
181
|
-
];
|
|
182
|
-
|
|
183
|
-
const memSection = [
|
|
184
|
-
0x05, 0x03, 0x01, 0x00, 0x01
|
|
185
|
-
];
|
|
186
|
-
|
|
187
|
-
const exportSection = [
|
|
188
|
-
0x07, 0x11, 0x02, 0x04, 0x68, 0x61, 0x73, 0x68, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00
|
|
189
|
-
];
|
|
190
|
-
|
|
191
|
-
const wasm = [
|
|
192
|
-
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
193
|
-
...typeSection,
|
|
194
|
-
...funcSection,
|
|
195
|
-
...memSection,
|
|
196
|
-
...exportSection,
|
|
197
|
-
...codeSection
|
|
198
|
-
];
|
|
199
|
-
|
|
200
|
-
return Buffer.from(wasm);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Pure JavaScript fallback equivalent of the custom polymorphic hash.
|
|
205
|
-
* @param {string} str Input string.
|
|
206
|
-
* @param {object} constants Parameters.
|
|
207
|
-
* @returns {number} 32-bit unsigned integer hash.
|
|
208
|
-
*/
|
|
209
|
-
static hashJs(str, constants) {
|
|
210
|
-
const { seed, multiplier, adder } = constants;
|
|
211
|
-
let h = seed | 0;
|
|
212
|
-
for (let i = 0; i < str.length; i++) {
|
|
213
|
-
const byte = str.charCodeAt(i) & 0xff;
|
|
214
|
-
h = h ^ byte;
|
|
215
|
-
h = Math.imul(h, multiplier) + adder;
|
|
216
|
-
}
|
|
217
|
-
return h >>> 0;
|
|
218
|
-
}
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
function encodeULEB128(val) {
|
|
4
|
+
const bytes = [];
|
|
5
|
+
let num = val >>> 0;
|
|
6
|
+
do {
|
|
7
|
+
let byte = num & 0x7f;
|
|
8
|
+
num >>>= 7;
|
|
9
|
+
if (num !== 0) byte |= 0x80;
|
|
10
|
+
bytes.push(byte);
|
|
11
|
+
} while (num !== 0);
|
|
12
|
+
return bytes;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function encodeSLEB128(val) {
|
|
16
|
+
const bytes = [];
|
|
17
|
+
let num = val | 0;
|
|
18
|
+
while (true) {
|
|
19
|
+
let byte = num & 0x7f;
|
|
20
|
+
num >>= 7;
|
|
21
|
+
if ((num === 0 && (byte & 0x40) === 0) || (num === -1 && (byte & 0x40) !== 0)) {
|
|
22
|
+
bytes.push(byte);
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
bytes.push(byte | 0x80);
|
|
26
|
+
}
|
|
27
|
+
return bytes;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getRandomNonTrivialOpcodes(tempLocal) {
|
|
31
|
+
const insts = [];
|
|
32
|
+
const ops = [
|
|
33
|
+
// local.get tempLocal, i32.const rand, rotl, local.set tempLocal
|
|
34
|
+
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(1, 32)), 0x77, 0x21, tempLocal],
|
|
35
|
+
// local.get tempLocal, i32.const rand, rotr, local.set tempLocal
|
|
36
|
+
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(1, 32)), 0x78, 0x21, tempLocal],
|
|
37
|
+
// local.get tempLocal, i32.const rand, xor, local.set tempLocal
|
|
38
|
+
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(0, 10000)), 0x73, 0x21, tempLocal],
|
|
39
|
+
// local.get tempLocal, popcnt, i32.const rand, mul, local.set tempLocal
|
|
40
|
+
() => [0x20, tempLocal, 0x69, 0x41, ...encodeSLEB128(crypto.randomInt(1, 1001)), 0x6c, 0x21, tempLocal],
|
|
41
|
+
// local.get tempLocal, clz, local.get tempLocal, ctz, add, local.set tempLocal
|
|
42
|
+
() => [0x20, tempLocal, 0x67, 0x20, tempLocal, 0x68, 0x6a, 0x21, tempLocal]
|
|
43
|
+
];
|
|
44
|
+
const count = crypto.randomInt(2, 5);
|
|
45
|
+
for (let i = 0; i < count; i++) {
|
|
46
|
+
const op = ops[crypto.randomInt(0, ops.length)];
|
|
47
|
+
insts.push(...op());
|
|
48
|
+
}
|
|
49
|
+
return insts;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function generatePolymorphicInstructions(stateLocal = 4, tempLocal = 5) {
|
|
53
|
+
const insts = [];
|
|
54
|
+
|
|
55
|
+
// Initialize tempLocal with a random value
|
|
56
|
+
const initVal = crypto.randomInt(-500, 500);
|
|
57
|
+
insts.push(0x41, ...encodeSLEB128(initVal), 0x21, tempLocal);
|
|
58
|
+
|
|
59
|
+
// Initialize stateLocal to 0
|
|
60
|
+
insts.push(0x41, ...encodeSLEB128(0), 0x21, stateLocal);
|
|
61
|
+
|
|
62
|
+
// Loop & Block for state machine
|
|
63
|
+
insts.push(0x03, 0x40); // loop
|
|
64
|
+
insts.push(0x02, 0x40); // block
|
|
65
|
+
|
|
66
|
+
for (let state = 0; state < 3; state++) {
|
|
67
|
+
insts.push(0x20, stateLocal, 0x41, ...encodeSLEB128(state), 0x46); // state == expected
|
|
68
|
+
insts.push(0x04, 0x40); // if
|
|
69
|
+
insts.push(...getRandomNonTrivialOpcodes(tempLocal));
|
|
70
|
+
insts.push(0x41, ...encodeSLEB128(state + 1), 0x21, stateLocal); // transit to state + 1
|
|
71
|
+
insts.push(0x0c, ...encodeULEB128(2)); // br 2 (targets loop start)
|
|
72
|
+
insts.push(0x0b); // end if
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fallthrough / Default break (targets block depth 0, which exits the loop)
|
|
76
|
+
insts.push(0x0c, ...encodeULEB128(0));
|
|
77
|
+
|
|
78
|
+
// end block
|
|
79
|
+
insts.push(0x0b);
|
|
80
|
+
// end loop
|
|
81
|
+
insts.push(0x0b);
|
|
82
|
+
|
|
83
|
+
return insts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export class DynamicWasmGenerator {
|
|
87
|
+
/**
|
|
88
|
+
* Generates a unique polymorphic WebAssembly module containing a custom hash function
|
|
89
|
+
* with randomized constants and control flow variables.
|
|
90
|
+
* @param {object} constants - Custom seed, multiplier and adder.
|
|
91
|
+
* @returns {Buffer} Valid WebAssembly binary buffer.
|
|
92
|
+
*/
|
|
93
|
+
static generate(constants) {
|
|
94
|
+
const { seed, multiplier, adder } = constants;
|
|
95
|
+
|
|
96
|
+
const preLoopPoly = generatePolymorphicInstructions(4, 5);
|
|
97
|
+
const midLoopPoly = generatePolymorphicInstructions(4, 5);
|
|
98
|
+
|
|
99
|
+
const inst = [
|
|
100
|
+
0x01, 0x06, 0x7f, // Locals: 1 entry of 6 locals of type i32
|
|
101
|
+
...preLoopPoly,
|
|
102
|
+
// h = seed
|
|
103
|
+
0x41, ...encodeSLEB128(seed),
|
|
104
|
+
0x21, 0x03,
|
|
105
|
+
// i = 0
|
|
106
|
+
0x41, 0x00,
|
|
107
|
+
0x21, 0x02,
|
|
108
|
+
|
|
109
|
+
// block
|
|
110
|
+
0x02, 0x40,
|
|
111
|
+
// loop
|
|
112
|
+
0x03, 0x40,
|
|
113
|
+
|
|
114
|
+
// if i >= len break
|
|
115
|
+
0x20, 0x02,
|
|
116
|
+
0x20, 0x01,
|
|
117
|
+
0x4f,
|
|
118
|
+
0x0d, 0x01,
|
|
119
|
+
|
|
120
|
+
// byte = load8_u(ptr + i)
|
|
121
|
+
0x20, 0x00,
|
|
122
|
+
0x20, 0x02,
|
|
123
|
+
0x6a,
|
|
124
|
+
0x2d, 0x00, 0x00,
|
|
125
|
+
|
|
126
|
+
// h = h ^ byte
|
|
127
|
+
0x20, 0x03,
|
|
128
|
+
0x73,
|
|
129
|
+
|
|
130
|
+
// h = h * multiplier
|
|
131
|
+
0x41, ...encodeSLEB128(multiplier),
|
|
132
|
+
0x6c,
|
|
133
|
+
|
|
134
|
+
// h = h + adder
|
|
135
|
+
0x41, ...encodeSLEB128(adder),
|
|
136
|
+
0x6a,
|
|
137
|
+
|
|
138
|
+
// local.set 3
|
|
139
|
+
0x21, 0x03,
|
|
140
|
+
...midLoopPoly,
|
|
141
|
+
|
|
142
|
+
// i = i + 1
|
|
143
|
+
0x20, 0x02,
|
|
144
|
+
0x41, 0x01,
|
|
145
|
+
0x6a,
|
|
146
|
+
0x21, 0x02,
|
|
147
|
+
|
|
148
|
+
// br 0
|
|
149
|
+
0x0c, 0x00,
|
|
150
|
+
|
|
151
|
+
0x0b, // end loop
|
|
152
|
+
0x0b, // end block
|
|
153
|
+
|
|
154
|
+
// return h
|
|
155
|
+
0x20, 0x03,
|
|
156
|
+
0x0b // end function
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const funcBody = [
|
|
160
|
+
...encodeULEB128(inst.length),
|
|
161
|
+
...inst
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const codeSectionPayload = [
|
|
165
|
+
...encodeULEB128(1),
|
|
166
|
+
...funcBody
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const codeSection = [
|
|
170
|
+
0x0a,
|
|
171
|
+
...encodeULEB128(codeSectionPayload.length),
|
|
172
|
+
...codeSectionPayload
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
const typeSection = [
|
|
176
|
+
0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
const funcSection = [
|
|
180
|
+
0x03, 0x02, 0x01, 0x00
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
const memSection = [
|
|
184
|
+
0x05, 0x03, 0x01, 0x00, 0x01
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
const exportSection = [
|
|
188
|
+
0x07, 0x11, 0x02, 0x04, 0x68, 0x61, 0x73, 0x68, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
const wasm = [
|
|
192
|
+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
193
|
+
...typeSection,
|
|
194
|
+
...funcSection,
|
|
195
|
+
...memSection,
|
|
196
|
+
...exportSection,
|
|
197
|
+
...codeSection
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
return Buffer.from(wasm);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Pure JavaScript fallback equivalent of the custom polymorphic hash.
|
|
205
|
+
* @param {string} str Input string.
|
|
206
|
+
* @param {object} constants Parameters.
|
|
207
|
+
* @returns {number} 32-bit unsigned integer hash.
|
|
208
|
+
*/
|
|
209
|
+
static hashJs(str, constants) {
|
|
210
|
+
const { seed, multiplier, adder } = constants;
|
|
211
|
+
let h = seed | 0;
|
|
212
|
+
for (let i = 0; i < str.length; i++) {
|
|
213
|
+
const byte = str.charCodeAt(i) & 0xff;
|
|
214
|
+
h = h ^ byte;
|
|
215
|
+
h = Math.imul(h, multiplier) + adder;
|
|
216
|
+
}
|
|
217
|
+
return h >>> 0;
|
|
218
|
+
}
|
|
219
219
|
}
|