@cloudglides/veil 0.1.0

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 (53) hide show
  1. package/.envrc +1 -0
  2. package/.github/workflows/build-on-tag.yml +80 -0
  3. package/example/index.html +226 -0
  4. package/flake.nix +68 -0
  5. package/package.json +31 -0
  6. package/scripts/patch-wasm.js +12 -0
  7. package/src/cpu.ts +67 -0
  8. package/src/entropy/adblock.ts +16 -0
  9. package/src/entropy/approximate.ts +8 -0
  10. package/src/entropy/audio.ts +17 -0
  11. package/src/entropy/battery.ts +10 -0
  12. package/src/entropy/browser.ts +7 -0
  13. package/src/entropy/canvas.ts +17 -0
  14. package/src/entropy/complexity.ts +13 -0
  15. package/src/entropy/connection.ts +14 -0
  16. package/src/entropy/distribution.ts +8 -0
  17. package/src/entropy/fonts.ts +4 -0
  18. package/src/entropy/hardware.ts +10 -0
  19. package/src/entropy/language.ts +14 -0
  20. package/src/entropy/os.ts +12 -0
  21. package/src/entropy/osVersion.ts +6 -0
  22. package/src/entropy/performance.ts +14 -0
  23. package/src/entropy/permissions.ts +15 -0
  24. package/src/entropy/plugins.ts +14 -0
  25. package/src/entropy/preferences.ts +12 -0
  26. package/src/entropy/probabilistic.ts +20 -0
  27. package/src/entropy/screen.ts +12 -0
  28. package/src/entropy/screenInfo.ts +8 -0
  29. package/src/entropy/spectral.ts +8 -0
  30. package/src/entropy/statistical.ts +15 -0
  31. package/src/entropy/storage.ts +22 -0
  32. package/src/entropy/timezone.ts +10 -0
  33. package/src/entropy/userAgent.ts +16 -0
  34. package/src/entropy/webFeatures.ts +21 -0
  35. package/src/entropy/webgl.ts +11 -0
  36. package/src/gpu.ts +132 -0
  37. package/src/index.test.ts +26 -0
  38. package/src/index.ts +198 -0
  39. package/src/normalize/index.ts +31 -0
  40. package/src/probability.ts +11 -0
  41. package/src/scoring.ts +106 -0
  42. package/src/seeded-rng.ts +14 -0
  43. package/src/types/index.ts +11 -0
  44. package/src/types.ts +47 -0
  45. package/src/veil_core.d.ts +4 -0
  46. package/src/wasm-loader.ts +14 -0
  47. package/tsconfig.json +12 -0
  48. package/tsup.config.ts +35 -0
  49. package/veil-core/Cargo.lock +114 -0
  50. package/veil-core/Cargo.toml +12 -0
  51. package/veil-core/src/entropy.rs +132 -0
  52. package/veil-core/src/lib.rs +90 -0
  53. package/vitest.config.ts +15 -0
package/.envrc ADDED
@@ -0,0 +1 @@
1
+ use flake
@@ -0,0 +1,80 @@
1
+ name: Build on Tag
2
+ on:
3
+ push:
4
+ tags:
5
+ - 'v*'
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: write # For creating releases
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ with:
15
+ fetch-depth: 0
16
+
17
+ - uses: pnpm/action-setup@v2
18
+ with:
19
+ version: 8
20
+
21
+ - uses: actions/setup-node@v4
22
+ with:
23
+ node-version: '20'
24
+ cache: 'pnpm'
25
+ registry-url: 'https://registry.npmjs.org'
26
+
27
+ - uses: dtolnay/rust-toolchain@stable
28
+
29
+ - name: Install wasm-pack
30
+ run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
31
+
32
+ - name: Extract version from tag
33
+ id: version
34
+ run: |
35
+ VERSION=${GITHUB_REF#refs/tags/v}
36
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
37
+
38
+ - name: Update package.json version
39
+ run: |
40
+ VERSION="${{ steps.version.outputs.version }}"
41
+ pnpm exec jq --arg v "$VERSION" '.version = $v' package.json > package.json.tmp
42
+ mv package.json.tmp package.json
43
+
44
+ - name: Install dependencies
45
+ run: pnpm install
46
+
47
+ - name: Build WASM
48
+ run: pnpm run build:wasm
49
+
50
+ - name: Build
51
+ run: pnpm run build
52
+
53
+ - name: Publish to npm
54
+ run: npm publish
55
+ env:
56
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
57
+
58
+ - name: Get commit history
59
+ id: commits
60
+ run: |
61
+ PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || git rev-list --max-parents=0 HEAD)
62
+ COMMITS=$(git log $PREV_TAG..HEAD --oneline --no-decorate)
63
+ {
64
+ echo "commit_log<<EOF"
65
+ echo "$COMMITS"
66
+ echo "EOF"
67
+ } >> $GITHUB_OUTPUT
68
+
69
+ - name: Create GitHub Release
70
+ uses: softprops/action-gh-release@v1
71
+ with:
72
+ body: ${{ steps.commits.outputs.commit_log }}
73
+ generate_release_notes: true
74
+ files: |
75
+ dist/index.js
76
+ dist/index.d.ts
77
+ dist/veil_core.js
78
+ dist/veil_core_bg.wasm
79
+ env:
80
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,226 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Veil Fingerprinting Demo</title>
6
+ <style>
7
+ body {
8
+ font-family: monospace;
9
+ max-width: 1000px;
10
+ margin: 30px auto;
11
+ padding: 20px;
12
+ background: #fafafa;
13
+ }
14
+ h1 {
15
+ color: #333;
16
+ }
17
+ .controls {
18
+ display: flex;
19
+ gap: 10px;
20
+ margin: 20px 0;
21
+ flex-wrap: wrap;
22
+ }
23
+ button {
24
+ padding: 10px 16px;
25
+ font-size: 14px;
26
+ cursor: pointer;
27
+ background: #007bff;
28
+ color: white;
29
+ border: none;
30
+ border-radius: 4px;
31
+ transition: background 0.2s;
32
+ }
33
+ button:hover {
34
+ background: #0056b3;
35
+ }
36
+ button:disabled {
37
+ background: #ccc;
38
+ cursor: not-allowed;
39
+ }
40
+ .checkbox-group {
41
+ display: flex;
42
+ gap: 15px;
43
+ margin: 15px 0;
44
+ }
45
+ .checkbox-group label {
46
+ display: flex;
47
+ align-items: center;
48
+ gap: 6px;
49
+ cursor: pointer;
50
+ }
51
+ #result {
52
+ background: white;
53
+ padding: 20px;
54
+ border-radius: 4px;
55
+ margin-top: 20px;
56
+ border: 1px solid #ddd;
57
+ word-break: break-all;
58
+ max-height: 600px;
59
+ overflow-y: auto;
60
+ }
61
+ .loading {
62
+ color: #666;
63
+ }
64
+ .error {
65
+ color: #d32f2f;
66
+ }
67
+ .success {
68
+ color: #388e3c;
69
+ }
70
+ .info {
71
+ background: #e3f2fd;
72
+ padding: 10px;
73
+ border-radius: 4px;
74
+ margin: 10px 0;
75
+ border-left: 4px solid #2196f3;
76
+ }
77
+ pre {
78
+ background: #f5f5f5;
79
+ padding: 12px;
80
+ border-radius: 4px;
81
+ overflow-x: auto;
82
+ font-size: 12px;
83
+ line-height: 1.4;
84
+ }
85
+ </style>
86
+ </head>
87
+ <body>
88
+ <h1>Veil Browser Fingerprinting</h1>
89
+
90
+ <div class="info">
91
+ Generates a unique browser fingerprint using system, display, and performance metrics.
92
+ </div>
93
+
94
+ <div class="checkbox-group">
95
+ <label>
96
+ <input type="checkbox" id="detailedCheckbox" checked />
97
+ Detailed output (JSON)
98
+ </label>
99
+ <label>
100
+ <input type="checkbox" id="cpuCheckbox" />
101
+ CPU benchmark
102
+ </label>
103
+ <label>
104
+ <input type="checkbox" id="gpuCheckbox" />
105
+ GPU benchmark
106
+ </label>
107
+ </div>
108
+
109
+ <div class="controls">
110
+ <button onclick="generateFingerprint()">Generate</button>
111
+ <button onclick="clearResult()">Clear</button>
112
+ <button onclick="copyToClipboard()">Copy Result</button>
113
+ </div>
114
+
115
+ <div id="result">Click "Generate" to start...</div>
116
+
117
+ <script type="module">
118
+ import { getFingerprint } from "../dist/index.js";
119
+
120
+ window.generateFingerprint = async () => {
121
+ const result = document.getElementById("result");
122
+ const detailed = document.getElementById("detailedCheckbox").checked;
123
+ const cpuBenchmark = document.getElementById("cpuCheckbox").checked;
124
+ const gpuBenchmark = document.getElementById("gpuCheckbox").checked;
125
+
126
+ result.innerHTML = '<div class="loading">Generating fingerprint...</div>';
127
+ console.log("Starting fingerprint generation");
128
+ console.log("Options:", { detailed, cpuBenchmark, gpuBenchmark });
129
+
130
+ try {
131
+ const startTime = performance.now();
132
+
133
+ if (cpuBenchmark) {
134
+ console.log("Running CPU benchmark...");
135
+ }
136
+ if (gpuBenchmark) {
137
+ console.log("Running GPU benchmark...");
138
+ }
139
+
140
+ const fp = await getFingerprint({
141
+ hash: "sha256",
142
+ detailed,
143
+ cpuBenchmark,
144
+ gpuBenchmark,
145
+ });
146
+
147
+ const endTime = performance.now();
148
+ const duration = (endTime - startTime).toFixed(2);
149
+
150
+ console.log("Fingerprint generated in " + duration + "ms");
151
+
152
+ if (typeof fp === "string") {
153
+ console.log("Hash:", fp);
154
+ result.innerHTML = `
155
+ <div class="success">✓ Fingerprint generated</div>
156
+ <div style="margin-top: 10px;">
157
+ <strong>Hash:</strong><br/>
158
+ <pre>${fp}</pre>
159
+ <small>Generated in ${duration}ms</small>
160
+ </div>
161
+ `;
162
+ } else {
163
+ console.log("Detailed fingerprint:", fp);
164
+ result.innerHTML = `
165
+ <div class="success">✓ Fingerprint generated</div>
166
+ <div style="margin-top: 15px;">
167
+ <strong>Hash:</strong><br/>
168
+ <pre>${fp.hash}</pre>
169
+
170
+ <strong>Metrics:</strong><br/>
171
+ <pre>Uniqueness: ${(fp.uniqueness * 100).toFixed(2)}%
172
+ Confidence: ${(fp.confidence * 100).toFixed(2)}%</pre>
173
+
174
+ <strong>System:</strong><br/>
175
+ <pre>OS: ${fp.system.os}
176
+ Language: ${fp.system.language}
177
+ Timezone: ${fp.system.timezone}
178
+ Cores: ${fp.system.hardware.cores}
179
+ Memory: ${fp.system.hardware.memory}GB</pre>
180
+
181
+ <strong>Display:</strong><br/>
182
+ <pre>Resolution: ${fp.display.resolution}
183
+ Color Depth: ${fp.display.colorDepth}
184
+ Pixel Ratio: ${fp.display.devicePixelRatio}</pre>
185
+
186
+ <strong>Browser:</strong><br/>
187
+ <pre>Vendor: ${fp.browser.vendor}
188
+ Cookies: ${fp.browser.cookieEnabled ? "enabled" : "disabled"}</pre>
189
+
190
+ <strong>Entropy Sources (${fp.sources.length}):</strong><br/>
191
+ <pre>${fp.sources
192
+ .map(
193
+ (s) =>
194
+ `${s.source}: H=${s.entropy.toFixed(3)}, C=${(s.confidence * 100).toFixed(1)}%`,
195
+ )
196
+ .join("\n")}</pre>
197
+ </div>
198
+ `;
199
+ }
200
+ } catch (e) {
201
+ console.error("Error:", e);
202
+ result.innerHTML = `
203
+ <div class="error">Error generating fingerprint</div>
204
+ <pre>${e.message}\n\n${e.stack}</pre>
205
+ `;
206
+ }
207
+ };
208
+
209
+ window.clearResult = () => {
210
+ document.getElementById("result").textContent = "Cleared";
211
+ console.log("Result cleared");
212
+ };
213
+
214
+ window.copyToClipboard = () => {
215
+ const result = document.getElementById("result");
216
+ const text = result.innerText;
217
+ navigator.clipboard.writeText(text).then(() => {
218
+ console.log("Copied to clipboard");
219
+ alert("Copied to clipboard!");
220
+ });
221
+ };
222
+
223
+ console.log("Veil fingerprinting library loaded");
224
+ </script>
225
+ </body>
226
+ </html>
package/flake.nix ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ description = "A Nix-flake-based Rust development environment";
3
+ inputs = {
4
+ nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1"; # unstable Nixpkgs
5
+ fenix = {
6
+ url = "https://flakehub.com/f/nix-community/fenix/0.1";
7
+ inputs.nixpkgs.follows = "nixpkgs";
8
+ };
9
+ };
10
+ outputs = {self, ...} @ inputs: let
11
+ supportedSystems = [
12
+ "x86_64-linux"
13
+ "aarch64-linux"
14
+ "x86_64-darwin"
15
+ "aarch64-darwin"
16
+ ];
17
+ forEachSupportedSystem = f:
18
+ inputs.nixpkgs.lib.genAttrs supportedSystems (
19
+ system:
20
+ f {
21
+ pkgs = import inputs.nixpkgs {
22
+ inherit system;
23
+ overlays = [
24
+ inputs.self.overlays.default
25
+ ];
26
+ };
27
+ }
28
+ );
29
+ in {
30
+ overlays.default = final: prev: {
31
+ rustToolchain = with inputs.fenix.packages.${prev.stdenv.hostPlatform.system};
32
+ combine (
33
+ with stable; [
34
+ clippy
35
+ rustc
36
+ cargo
37
+ rustfmt
38
+ rust-src
39
+ # Add WASM target
40
+ (targets.wasm32-unknown-unknown.stable.rust-std)
41
+ ]
42
+ );
43
+ };
44
+ devShells = forEachSupportedSystem (
45
+ {pkgs}: {
46
+ default = pkgs.mkShellNoCC {
47
+ packages = with pkgs; [
48
+ rustToolchain
49
+ openssl
50
+ pkg-config
51
+ cargo-deny
52
+ cargo-edit
53
+ cargo-watch
54
+ rust-analyzer
55
+ wasm-pack
56
+ nodejs
57
+ pnpm
58
+ wasmtime
59
+ ];
60
+ env = {
61
+ RUST_SRC_PATH = "${pkgs.rustToolchain}/lib/rustlib/src/rust/library";
62
+ CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER = "${pkgs.wasmtime}/bin/wasmtime";
63
+ };
64
+ };
65
+ }
66
+ );
67
+ };
68
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@cloudglides/veil",
3
+ "version": "0.1.0",
4
+ "description": "Math-based deterministic browser fingerprinting library",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": "./dist/index.js",
8
+ "type": "module",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "tsup --watch",
15
+ "build:wasm": "wasm-pack build veil-core --target web",
16
+ "test": "vitest",
17
+ "test:ui": "vitest --ui"
18
+ },
19
+ "devDependencies": {
20
+ "@vitest/ui": "^1.0.0",
21
+ "eslint": "^9.39.2",
22
+ "happy-dom": "^20.0.11",
23
+ "jsdom": "^23.0.0",
24
+ "prettier": "^3.7.4",
25
+ "tsup": "^8.5.1",
26
+ "typescript": "^5.9.3",
27
+ "vite-plugin-top-level-await": "^1.6.0",
28
+ "vite-plugin-wasm": "^3.5.0",
29
+ "vitest": "^1.0.0"
30
+ }
31
+ }
@@ -0,0 +1,12 @@
1
+ import { readFileSync, writeFileSync } from "fs";
2
+
3
+ const wasmFile = "dist/veil_core.js";
4
+ let content = readFileSync(wasmFile, "utf-8");
5
+
6
+ content = content.replace(
7
+ "module_or_path = new URL('veil_core_bg.wasm', import.meta.url);",
8
+ "module_or_path = new URL('veil_core_bg.wasm', import.meta.url).href;",
9
+ );
10
+
11
+ writeFileSync(wasmFile, content);
12
+ console.log("Patched veil_core.js for browser compatibility");
package/src/cpu.ts ADDED
@@ -0,0 +1,67 @@
1
+ export async function runCPUBenchmark(): Promise<{
2
+ computeTime: number;
3
+ opsPerSecond: number;
4
+ memoryUsed: number;
5
+ }> {
6
+ const start = performance.now();
7
+ let operations = 0;
8
+
9
+ const primes: number[] = [];
10
+ for (let i = 2; i < 10000; i++) {
11
+ let isPrime = true;
12
+ for (let j = 2; j * j <= i; j++) {
13
+ if (i % j === 0) {
14
+ isPrime = false;
15
+ break;
16
+ }
17
+ }
18
+ if (isPrime) {
19
+ primes.push(i);
20
+ operations++;
21
+ }
22
+ }
23
+
24
+ let fib = 0;
25
+ let a = 0;
26
+ let b = 1;
27
+ for (let i = 0; i < 30; i++) {
28
+ fib = a + b;
29
+ a = b;
30
+ b = fib;
31
+ operations++;
32
+ }
33
+
34
+ const arr = new Array(5000).fill(0).map((_, i) => Math.sin(i) * Math.cos(i));
35
+ arr.sort((x, y) => x - y);
36
+ operations += 5000;
37
+
38
+ for (let i = 0; i < 1000; i++) {
39
+ const sum = arr.reduce((a, b) => a + b, 0);
40
+ const mean = sum / arr.length;
41
+ operations++;
42
+ }
43
+
44
+ const computeTime = performance.now() - start;
45
+ const opsPerSecond = (operations / computeTime) * 1000;
46
+ const memoryUsed = primes.length + arr.length;
47
+
48
+ return {
49
+ computeTime,
50
+ opsPerSecond,
51
+ memoryUsed,
52
+ };
53
+ }
54
+
55
+ export async function getCPUHash(): Promise<string> {
56
+ const benchmark = await runCPUBenchmark();
57
+ const data = `${benchmark.computeTime}${benchmark.opsPerSecond}${benchmark.memoryUsed}`;
58
+
59
+ let hash = 0;
60
+ for (let i = 0; i < data.length; i++) {
61
+ const char = data.charCodeAt(i);
62
+ hash = ((hash << 5) - hash) + char;
63
+ hash = hash & hash;
64
+ }
65
+
66
+ return Math.abs(hash).toString(16);
67
+ }
@@ -0,0 +1,16 @@
1
+ async function detectAdblock(): Promise<boolean> {
2
+ try {
3
+ const response = await fetch("https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js", {
4
+ method: "HEAD",
5
+ mode: "no-cors",
6
+ });
7
+ return false;
8
+ } catch {
9
+ return true;
10
+ }
11
+ }
12
+
13
+ export async function getAdblockEntropy(): Promise<string> {
14
+ const adblockPresent = await detectAdblock();
15
+ return `adblock:${adblockPresent}`;
16
+ }
@@ -0,0 +1,8 @@
1
+ import { approx_entropy } from "./veil_core.js";
2
+ import { seededRng } from "../seeded-rng";
3
+
4
+ export async function getApproximateEntropy(seed: string): Promise<string> {
5
+ const samples = seededRng(seed, 256);
6
+ const apen = approx_entropy(new Float64Array(samples), 2);
7
+ return `apen:${apen.toFixed(6)}`;
8
+ }
@@ -0,0 +1,17 @@
1
+ export async function getAudioEntropy(): Promise<string> {
2
+ try {
3
+ const ctx = new (window.AudioContext || (window as any).webkitAudioContext)();
4
+ const state = ctx.state;
5
+ const sampleRate = ctx.sampleRate;
6
+
7
+ const bitDepth = 32;
8
+ const nyquistFreq = sampleRate / 2;
9
+ const frequencyBits = Math.log2(nyquistFreq + 1);
10
+ const audioCapacity = bitDepth * sampleRate;
11
+
12
+ ctx.close();
13
+ return `state:${state}|sr:${sampleRate}|f_nyquist:${nyquistFreq}|H(audio)=${frequencyBits.toFixed(3)}`;
14
+ } catch {
15
+ return "audio:unavailable";
16
+ }
17
+ }
@@ -0,0 +1,10 @@
1
+ export async function getBatteryEntropy(): Promise<string> {
2
+ const nav = navigator as any;
3
+ if (!nav.getBattery) return "battery:unavailable";
4
+ try {
5
+ const battery = await nav.getBattery();
6
+ return `level:${battery.level}|charging:${battery.charging}`;
7
+ } catch {
8
+ return "battery:unavailable";
9
+ }
10
+ }
@@ -0,0 +1,7 @@
1
+ export async function getBrowserEntropy(): Promise<string> {
2
+ const ua = navigator.userAgent;
3
+ const vendor = navigator.vendor;
4
+ const cookieEnabled = navigator.cookieEnabled;
5
+ const doNotTrack = navigator.doNotTrack;
6
+ return `ua:${ua}|vendor:${vendor}|cookies:${cookieEnabled}|dnt:${doNotTrack}`;
7
+ }
@@ -0,0 +1,17 @@
1
+ export async function getCanvasEntropy(): Promise<string> {
2
+ try {
3
+ const canvas = document.createElement("canvas");
4
+ if (canvas.getContext === undefined) return "canvas:unavailable";
5
+
6
+ const ctx = canvas.getContext("2d");
7
+ if (!ctx) return "canvas:unavailable";
8
+
9
+ const support = ctx.fillText ? "fillText" : "none";
10
+ const textMetrics = ctx.measureText ? "measureText" : "none";
11
+ const imageDataSupport = ctx.getImageData ? "getImageData" : "none";
12
+
13
+ return `support:${support}|metrics:${textMetrics}|imageData:${imageDataSupport}`;
14
+ } catch {
15
+ return "canvas:unavailable";
16
+ }
17
+ }
@@ -0,0 +1,13 @@
1
+ import { lz_complexity } from "./veil_core.js";
2
+
3
+ export async function getComplexityEntropy(): Promise<string> {
4
+ const ua = navigator.userAgent;
5
+ const bytes = new Uint8Array(ua.length);
6
+
7
+ for (let i = 0; i < ua.length; i++) {
8
+ bytes[i] = ua.charCodeAt(i) % 256;
9
+ }
10
+
11
+ const complexity = lz_complexity(bytes);
12
+ return `lz:${complexity}`;
13
+ }
@@ -0,0 +1,14 @@
1
+ export async function getConnectionEntropy(): Promise<string> {
2
+ const nav = navigator as any;
3
+ const conn = nav.connection || nav.mozConnection || nav.webkitConnection;
4
+ if (!conn) return "connection:unknown";
5
+
6
+ const rtt = conn.rtt || 0;
7
+ const downlink = conn.downlink || 0;
8
+
9
+ const bandwidth = Math.log2(downlink * 1000 + 1);
10
+ const latencyEntropy = Math.log2(rtt + 2);
11
+ const connectionScore = (downlink / rtt) * 1000;
12
+
13
+ return `type:${conn.effectiveType}|bw:${bandwidth.toFixed(3)}|H(RTT)=${latencyEntropy.toFixed(3)}|Q=${connectionScore.toFixed(2)}`;
14
+ }
@@ -0,0 +1,8 @@
1
+ import { ks_test } from "./veil_core.js";
2
+ import { seededRng } from "../seeded-rng";
3
+
4
+ export async function getDistributionEntropy(seed: string): Promise<string> {
5
+ const samples = seededRng(seed, 1000);
6
+ const ksStatistic = ks_test(new Float64Array(samples));
7
+ return `ks:${ksStatistic.toFixed(6)}`;
8
+ }
@@ -0,0 +1,4 @@
1
+ export async function getFontsEntropy(): Promise<string> {
2
+ const fonts = ["Arial", "Courier", "Georgia", "Verdana"];
3
+ return fonts.join(",");
4
+ }
@@ -0,0 +1,10 @@
1
+ export async function getHardwareEntropy(): Promise<string> {
2
+ const cores = navigator.hardwareConcurrency || 1;
3
+ const memory = (navigator as any).deviceMemory || 4;
4
+
5
+ const computePower = Math.log2(cores) * Math.log2(memory);
6
+ const memoryBits = Math.ceil(Math.log2(memory * 1024));
7
+ const coreEntropy = cores > 0 ? Math.log2(cores) : 0;
8
+
9
+ return `cores:${cores}|mem:${memory}GB|P(compute)=${computePower.toFixed(3)}|H(cores)=${coreEntropy.toFixed(3)}`;
10
+ }
@@ -0,0 +1,14 @@
1
+ export async function getLanguageEntropy(): Promise<string> {
2
+ const language = navigator.language;
3
+ const languages = navigator.languages;
4
+
5
+ const langCount = languages.length;
6
+ const langDiversity = Math.log2(langCount + 1);
7
+ const primaryEntropy = -Array.from(language).reduce((h, c) => {
8
+ const p = 1 / language.length;
9
+ return h + p * Math.log2(p);
10
+ }, 0);
11
+
12
+ const combined = languages.join("|");
13
+ return `primary:${language}|langs:${langCount}|H(L)=${primaryEntropy.toFixed(3)}|D=${langDiversity.toFixed(3)}`;
14
+ }
@@ -0,0 +1,12 @@
1
+ export async function getOSEntropy(): Promise<string> {
2
+ const platform = navigator.platform || "unknown";
3
+ const oscpu = (navigator as any).oscpu || "unknown";
4
+ const combined = platform + oscpu;
5
+
6
+ const uniqueChars = new Set(combined).size;
7
+ const totalChars = combined.length;
8
+ const charDiversity = uniqueChars / totalChars;
9
+ const surprisal = -Math.log2(1 / uniqueChars);
10
+
11
+ return `OS:${platform}|CPU:${oscpu}|δ=${charDiversity.toFixed(4)}|I=${surprisal.toFixed(2)}`;
12
+ }
@@ -0,0 +1,6 @@
1
+ export async function getOSVersionEntropy(): Promise<string> {
2
+ const ua = navigator.userAgent;
3
+ const match = ua.match(/Windows NT|Mac OS|Linux|Android|iOS/i);
4
+ const os = match ? match[0] : "unknown";
5
+ return os;
6
+ }
@@ -0,0 +1,14 @@
1
+ export async function getPerformanceEntropy(): Promise<string> {
2
+ if (!performance.memory) return "perf:unavailable";
3
+ const mem = (performance as any).memory;
4
+
5
+ const used = mem.usedJSHeapSize;
6
+ const total = mem.totalJSHeapSize;
7
+ const limit = mem.jsHeapSizeLimit;
8
+
9
+ const utilizationRatio = used / limit;
10
+ const fragmentation = (total - used) / total;
11
+ const headroomBits = Math.log2(limit - used + 1);
12
+
13
+ return `used:${used}|limit:${limit}|ρ=${utilizationRatio.toFixed(4)}|frag=${fragmentation.toFixed(4)}|H(mem)=${headroomBits.toFixed(2)}`;
14
+ }