@cloudglides/veil 1.0.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.
- package/.envrc +1 -0
- package/.github/workflows/build-on-tag.yml +75 -0
- package/example/index.html +226 -0
- package/flake.nix +68 -0
- package/package.json +31 -0
- package/scripts/patch-wasm.js +12 -0
- package/src/cpu.ts +67 -0
- package/src/entropy/adblock.ts +16 -0
- package/src/entropy/approximate.ts +8 -0
- package/src/entropy/audio.ts +17 -0
- package/src/entropy/battery.ts +10 -0
- package/src/entropy/browser.ts +7 -0
- package/src/entropy/canvas.ts +17 -0
- package/src/entropy/complexity.ts +13 -0
- package/src/entropy/connection.ts +14 -0
- package/src/entropy/distribution.ts +8 -0
- package/src/entropy/fonts.ts +4 -0
- package/src/entropy/hardware.ts +10 -0
- package/src/entropy/language.ts +14 -0
- package/src/entropy/os.ts +12 -0
- package/src/entropy/osVersion.ts +6 -0
- package/src/entropy/performance.ts +14 -0
- package/src/entropy/permissions.ts +15 -0
- package/src/entropy/plugins.ts +14 -0
- package/src/entropy/preferences.ts +12 -0
- package/src/entropy/probabilistic.ts +20 -0
- package/src/entropy/screen.ts +12 -0
- package/src/entropy/screenInfo.ts +8 -0
- package/src/entropy/spectral.ts +8 -0
- package/src/entropy/statistical.ts +15 -0
- package/src/entropy/storage.ts +22 -0
- package/src/entropy/timezone.ts +10 -0
- package/src/entropy/userAgent.ts +16 -0
- package/src/entropy/webFeatures.ts +21 -0
- package/src/entropy/webgl.ts +11 -0
- package/src/gpu.ts +132 -0
- package/src/index.test.ts +26 -0
- package/src/index.ts +198 -0
- package/src/normalize/index.ts +31 -0
- package/src/probability.ts +11 -0
- package/src/scoring.ts +106 -0
- package/src/seeded-rng.ts +14 -0
- package/src/types/index.ts +11 -0
- package/src/types.ts +47 -0
- package/src/veil_core.d.ts +4 -0
- package/src/wasm-loader.ts +14 -0
- package/tsconfig.json +12 -0
- package/tsup.config.ts +35 -0
- package/veil-core/Cargo.lock +114 -0
- package/veil-core/Cargo.toml +12 -0
- package/veil-core/src/entropy.rs +132 -0
- package/veil-core/src/lib.rs +90 -0
- package/vitest.config.ts +15 -0
package/.envrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
use flake
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
env:
|
|
75
|
+
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": "1.0.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,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,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
|
+
}
|