@zeke-02/tinfoil 0.0.11 → 0.11.7
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/dist/ai-sdk-provider.browser.d.ts +12 -0
- package/dist/ai-sdk-provider.browser.d.ts.map +1 -0
- package/dist/{esm/ai-sdk-provider.mjs → ai-sdk-provider.browser.js} +9 -5
- package/dist/ai-sdk-provider.browser.js.map +1 -0
- package/dist/ai-sdk-provider.d.ts +6 -1
- package/dist/ai-sdk-provider.d.ts.map +1 -0
- package/dist/ai-sdk-provider.js +12 -11
- package/dist/ai-sdk-provider.js.map +1 -0
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +3 -5
- package/dist/config.js.map +1 -0
- package/dist/encrypted-body-fetch.d.ts +8 -2
- package/dist/encrypted-body-fetch.d.ts.map +1 -0
- package/dist/encrypted-body-fetch.js +27 -26
- package/dist/encrypted-body-fetch.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +2 -4
- package/dist/env.js.map +1 -0
- package/dist/index.browser.d.ts +8 -7
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.browser.js +8 -28
- package/dist/index.browser.js.map +1 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -47
- package/dist/index.js.map +1 -0
- package/dist/pinned-tls-fetch.d.ts +1 -0
- package/dist/pinned-tls-fetch.d.ts.map +1 -0
- package/dist/pinned-tls-fetch.js +13 -17
- package/dist/pinned-tls-fetch.js.map +1 -0
- package/dist/router.d.ts +1 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +6 -7
- package/dist/router.js.map +1 -0
- package/dist/{esm/secure-client.d.ts → secure-client.browser.d.ts} +2 -1
- package/dist/secure-client.browser.d.ts.map +1 -0
- package/dist/{esm/secure-client.mjs → secure-client.browser.js} +46 -56
- package/dist/secure-client.browser.js.map +1 -0
- package/dist/secure-client.d.ts +2 -1
- package/dist/secure-client.d.ts.map +1 -0
- package/dist/secure-client.js +51 -65
- package/dist/secure-client.js.map +1 -0
- package/dist/secure-fetch.browser.d.ts +1 -0
- package/dist/secure-fetch.browser.d.ts.map +1 -0
- package/dist/secure-fetch.browser.js +4 -6
- package/dist/secure-fetch.browser.js.map +1 -0
- package/dist/secure-fetch.d.ts +1 -0
- package/dist/secure-fetch.d.ts.map +1 -0
- package/dist/secure-fetch.js +16 -8
- package/dist/secure-fetch.js.map +1 -0
- package/dist/{tinfoilai.d.ts → tinfoil-ai.browser.d.ts} +5 -2
- package/dist/tinfoil-ai.browser.d.ts.map +1 -0
- package/dist/{tinfoilai.js → tinfoil-ai.browser.js} +50 -39
- package/dist/tinfoil-ai.browser.js.map +1 -0
- package/dist/{esm/tinfoilai.d.ts → tinfoil-ai.d.ts} +5 -2
- package/dist/tinfoil-ai.d.ts.map +1 -0
- package/dist/{esm/tinfoilai.mjs → tinfoil-ai.js} +29 -11
- package/dist/tinfoil-ai.js.map +1 -0
- package/dist/unverified-client.d.ts +1 -2
- package/dist/unverified-client.d.ts.map +1 -0
- package/dist/unverified-client.js +10 -13
- package/dist/unverified-client.js.map +1 -0
- package/dist/verifier.d.ts +2 -141
- package/dist/verifier.d.ts.map +1 -0
- package/dist/verifier.js +2 -570
- package/dist/verifier.js.map +1 -0
- package/package.json +48 -41
- package/LICENSE +0 -661
- package/README.md +0 -183
- package/dist/__tests__/test-utils.d.ts +0 -1
- package/dist/__tests__/test-utils.js +0 -44
- package/dist/esm/__tests__/test-utils.d.ts +0 -1
- package/dist/esm/__tests__/test-utils.mjs +0 -38
- package/dist/esm/ai-sdk-provider.d.ts +0 -7
- package/dist/esm/config.d.ts +0 -13
- package/dist/esm/config.mjs +0 -13
- package/dist/esm/encrypted-body-fetch.d.ts +0 -13
- package/dist/esm/encrypted-body-fetch.mjs +0 -105
- package/dist/esm/env.d.ts +0 -5
- package/dist/esm/env.mjs +0 -17
- package/dist/esm/fetch-adapter.d.ts +0 -21
- package/dist/esm/fetch-adapter.mjs +0 -23
- package/dist/esm/index.browser.d.ts +0 -7
- package/dist/esm/index.browser.mjs +0 -8
- package/dist/esm/index.d.ts +0 -9
- package/dist/esm/index.mjs +0 -13
- package/dist/esm/pinned-tls-fetch.d.ts +0 -1
- package/dist/esm/pinned-tls-fetch.mjs +0 -110
- package/dist/esm/router.d.ts +0 -11
- package/dist/esm/router.mjs +0 -33
- package/dist/esm/secure-fetch.browser.d.ts +0 -1
- package/dist/esm/secure-fetch.browser.mjs +0 -10
- package/dist/esm/secure-fetch.d.ts +0 -1
- package/dist/esm/secure-fetch.mjs +0 -12
- package/dist/esm/unverified-client.d.ts +0 -18
- package/dist/esm/unverified-client.mjs +0 -61
- package/dist/esm/verifier.d.ts +0 -141
- package/dist/esm/verifier.mjs +0 -532
- package/dist/esm/wasm-exec.js +0 -668
- package/dist/esm/wasm-exec.mjs +0 -668
- package/dist/fetch-adapter.d.ts +0 -21
- package/dist/fetch-adapter.js +0 -27
- package/dist/wasm-exec.js +0 -668
package/dist/verifier.js
CHANGED
|
@@ -1,570 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.Verifier = void 0;
|
|
37
|
-
exports.suppressWasmLogs = suppressWasmLogs;
|
|
38
|
-
/**
|
|
39
|
-
* VERIFIER COMPONENT OVERVIEW
|
|
40
|
-
* ==========================
|
|
41
|
-
*
|
|
42
|
-
* This implementation performs end-to-end enclave and code verification entirely on the
|
|
43
|
-
* client using a Go WebAssembly module, and exposes a small TypeScript API around it.
|
|
44
|
-
*
|
|
45
|
-
* UNIFIED VERIFICATION FLOW
|
|
46
|
-
* The primary API is `verify()`, which invokes the Go WASM `verify(enclaveHost, repo)`
|
|
47
|
-
* function that performs all verification steps atomically:
|
|
48
|
-
*
|
|
49
|
-
* 1) DIGEST FETCH
|
|
50
|
-
* - Fetches the latest release digest from GitHub
|
|
51
|
-
* - Uses Tinfoil GitHub proxy (https://api-github-proxy.tinfoil.sh) to avoid rate limits
|
|
52
|
-
*
|
|
53
|
-
* 2) CODE INTEGRITY (Release Verification)
|
|
54
|
-
* - Verifies code provenance using Sigstore/Rekor for GitHub Actions builds
|
|
55
|
-
* - Returns the expected code measurement for the release
|
|
56
|
-
*
|
|
57
|
-
* 3) REMOTE ATTESTATION (Enclave Verification)
|
|
58
|
-
* - Performs runtime attestation against the target enclave hostname
|
|
59
|
-
* - Verifies vendor certificate chains inside WASM (AMD SEV-SNP / Intel TDX)
|
|
60
|
-
* - Returns the enclave's runtime measurement and cryptographic keys (TLS fingerprint and HPKE)
|
|
61
|
-
*
|
|
62
|
-
* 4) HARDWARE VERIFICATION (for TDX platforms)
|
|
63
|
-
* - Fetches and verifies TDX platform measurements if required
|
|
64
|
-
* - Validates hardware attestation against expected measurements
|
|
65
|
-
*
|
|
66
|
-
* 5) CODE CONSISTENCY (Measurement Comparison)
|
|
67
|
-
* - Compares the runtime measurement with the expected code measurement
|
|
68
|
-
* - Uses platform-aware comparison rules for different TEE types
|
|
69
|
-
*
|
|
70
|
-
* ERROR HANDLING
|
|
71
|
-
* When verification fails, errors are prefixed with the failing step:
|
|
72
|
-
* - `fetchDigest:` - Failed to fetch GitHub release digest
|
|
73
|
-
* - `verifyCode:` - Failed to verify code provenance
|
|
74
|
-
* - `verifyEnclave:` - Failed runtime attestation
|
|
75
|
-
* - `verifyHardware:` - Failed TDX hardware verification
|
|
76
|
-
* - `validateTLS:` - TLS public key validation failed
|
|
77
|
-
* - `measurements:` - Measurement comparison failed
|
|
78
|
-
*
|
|
79
|
-
* RUNTIME AND DELIVERY
|
|
80
|
-
* - All verification executes locally via WebAssembly (Go → WASM)
|
|
81
|
-
* - WASM loader: `wasm-exec.js`
|
|
82
|
-
* - WASM module URL: https://tinfoilsh.github.io/verifier/tinfoil-verifier.wasm
|
|
83
|
-
* - Works in Node 20+ and modern browsers with lightweight polyfills for
|
|
84
|
-
* `performance`, `TextEncoder`/`TextDecoder`, and `crypto.getRandomValues`
|
|
85
|
-
* - Go stdout/stderr is suppressed by default; toggle via `suppressWasmLogs()`
|
|
86
|
-
*
|
|
87
|
-
* PROXIES AND TRUST
|
|
88
|
-
* - GitHub proxy is used only to avoid rate limits; the WASM logic independently
|
|
89
|
-
* validates release provenance via Sigstore transparency logs
|
|
90
|
-
* - AMD KDS access may be proxied within the WASM for availability; AMD roots are
|
|
91
|
-
* embedded and the full chain is verified in Go to prevent forgery
|
|
92
|
-
*
|
|
93
|
-
* SUPPORTED PLATFORMS
|
|
94
|
-
* - AMD SEV-SNP
|
|
95
|
-
* - Intel TDX (with hardware platform verification)
|
|
96
|
-
* - Predicate types: SNP/TDX multi-platform v1, TDX guest v1/v2, SEV-SNP guest v1
|
|
97
|
-
*
|
|
98
|
-
* PUBLIC API (this module)
|
|
99
|
-
* - `new Verifier({ serverURL, configRepo? })`
|
|
100
|
-
* - `verify()` → Promise<AttestationResponse> - full end-to-end verification returning cryptographic keys and measurement
|
|
101
|
-
* - `getVerificationDocument()` → VerificationDocument | undefined - detailed step-by-step verification results
|
|
102
|
-
* - `suppressWasmLogs(suppress?)` → void - control WASM log output
|
|
103
|
-
*/
|
|
104
|
-
const config_1 = require("./config");
|
|
105
|
-
const fetch_adapter_1 = require("./fetch-adapter");
|
|
106
|
-
let cachedTextEncoder = null;
|
|
107
|
-
function getTextEncoder() {
|
|
108
|
-
if (!cachedTextEncoder) {
|
|
109
|
-
if (typeof globalThis.TextEncoder !== "function") {
|
|
110
|
-
throw new Error("TextEncoder is not available in this environment");
|
|
111
|
-
}
|
|
112
|
-
cachedTextEncoder = globalThis.TextEncoder;
|
|
113
|
-
}
|
|
114
|
-
return cachedTextEncoder;
|
|
115
|
-
}
|
|
116
|
-
let cachedTextDecoder = null;
|
|
117
|
-
function getTextDecoder() {
|
|
118
|
-
if (!cachedTextDecoder) {
|
|
119
|
-
if (typeof globalThis.TextDecoder !== "function") {
|
|
120
|
-
throw new Error("TextDecoder is not available in this environment");
|
|
121
|
-
}
|
|
122
|
-
cachedTextDecoder = globalThis.TextDecoder;
|
|
123
|
-
}
|
|
124
|
-
return cachedTextDecoder;
|
|
125
|
-
}
|
|
126
|
-
const nodeRequire = createNodeRequire();
|
|
127
|
-
let wasmExecLoader = null;
|
|
128
|
-
/**
|
|
129
|
-
* Verifier performs attestation verification for Tinfoil enclaves
|
|
130
|
-
*
|
|
131
|
-
* The verifier loads a WebAssembly module (compiled from Go) that performs
|
|
132
|
-
* end-to-end attestation verification:
|
|
133
|
-
* 1. Fetches the latest code release digest from GitHub
|
|
134
|
-
* 2. Verifies code provenance using Sigstore/Rekor
|
|
135
|
-
* 3. Performs runtime attestation against the enclave
|
|
136
|
-
* 4. Verifies hardware measurements (for TDX platforms)
|
|
137
|
-
* 5. Compares code and runtime measurements using platform-specific logic
|
|
138
|
-
*
|
|
139
|
-
* Primary method: verify() - Returns AttestationResponse with cryptographic keys
|
|
140
|
-
* Verification details: getVerificationDocument() - Returns step-by-step results
|
|
141
|
-
*/
|
|
142
|
-
class Verifier {
|
|
143
|
-
constructor(options) {
|
|
144
|
-
if (!options?.serverURL) {
|
|
145
|
-
throw new Error("serverURL is required for Verifier");
|
|
146
|
-
}
|
|
147
|
-
this.serverURL = new URL(options.serverURL).hostname;
|
|
148
|
-
this.configRepo =
|
|
149
|
-
options?.configRepo ?? config_1.TINFOIL_CONFIG.INFERENCE_PROXY_REPO;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Execute a function with a fresh WASM instance that auto-cleans up
|
|
153
|
-
* This ensures Go runtime doesn't keep the process alive
|
|
154
|
-
*/
|
|
155
|
-
static async executeWithWasm(fn) {
|
|
156
|
-
await initializeWasmGlobals();
|
|
157
|
-
const goInstance = new globalThis.Go();
|
|
158
|
-
// Load WASM module
|
|
159
|
-
const fetchFn = (0, fetch_adapter_1.getFetch)();
|
|
160
|
-
const wasmResponse = await fetchFn(Verifier.defaultWasmUrl);
|
|
161
|
-
if (!wasmResponse.ok) {
|
|
162
|
-
throw new Error(`Failed to fetch WASM: ${wasmResponse.status} ${wasmResponse.statusText}`);
|
|
163
|
-
}
|
|
164
|
-
const wasmBuffer = await wasmResponse.arrayBuffer();
|
|
165
|
-
const result = await WebAssembly.instantiate(wasmBuffer, goInstance.importObject);
|
|
166
|
-
// Start the Go instance in the background
|
|
167
|
-
// We don't await this - it runs continuously
|
|
168
|
-
goInstance.run(result.instance);
|
|
169
|
-
// Wait for WASM functions to be available
|
|
170
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
171
|
-
if (typeof globalThis.verifyCode === "undefined" ||
|
|
172
|
-
typeof globalThis.verifyEnclave === "undefined") {
|
|
173
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
174
|
-
}
|
|
175
|
-
// Apply log suppression if requested
|
|
176
|
-
if (Verifier.wasmLogsSuppressed && globalThis.fs?.writeSync) {
|
|
177
|
-
const fsObj = globalThis.fs;
|
|
178
|
-
if (!Verifier.originalFsWriteSync) {
|
|
179
|
-
Verifier.originalFsWriteSync = fsObj.writeSync.bind(fsObj);
|
|
180
|
-
}
|
|
181
|
-
fsObj.writeSync = function (_fd, buf) {
|
|
182
|
-
return buf.length;
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
try {
|
|
186
|
-
// Execute the user's function
|
|
187
|
-
const result = await fn();
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
finally {
|
|
191
|
-
// Clean up the Go instance
|
|
192
|
-
if (goInstance._scheduledTimeouts instanceof Map) {
|
|
193
|
-
for (const timeoutId of goInstance._scheduledTimeouts.values()) {
|
|
194
|
-
clearTimeout(timeoutId);
|
|
195
|
-
}
|
|
196
|
-
goInstance._scheduledTimeouts.clear();
|
|
197
|
-
}
|
|
198
|
-
if (typeof goInstance.exit === "function") {
|
|
199
|
-
try {
|
|
200
|
-
goInstance.exit(0);
|
|
201
|
-
}
|
|
202
|
-
catch (e) {
|
|
203
|
-
// Ignore exit errors
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Perform end-to-end attestation verification
|
|
210
|
-
*
|
|
211
|
-
* This method performs all verification steps atomically via the Go WASM verify() function:
|
|
212
|
-
* 1. Fetches the latest code digest from GitHub releases
|
|
213
|
-
* 2. Verifies code provenance using Sigstore/Rekor
|
|
214
|
-
* 3. Performs runtime attestation against the enclave
|
|
215
|
-
* 4. Verifies hardware measurements (for TDX platforms)
|
|
216
|
-
* 5. Compares code and runtime measurements using platform-specific logic
|
|
217
|
-
*
|
|
218
|
-
* The WASM runtime is automatically initialized and cleaned up within this method.
|
|
219
|
-
* A detailed verification document is saved and can be accessed via getVerificationDocument().
|
|
220
|
-
*
|
|
221
|
-
* @returns AttestationResponse containing cryptographic keys (TLS/HPKE) and enclave measurement
|
|
222
|
-
* @throws Error if measurements don't match or verification fails at any step
|
|
223
|
-
*/
|
|
224
|
-
async verify() {
|
|
225
|
-
return Verifier.executeWithWasm(async () => {
|
|
226
|
-
return this.verifyInternal();
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Save a failed verification document
|
|
231
|
-
*/
|
|
232
|
-
saveFailedVerificationDocument(steps) {
|
|
233
|
-
this.lastVerificationDocument = {
|
|
234
|
-
configRepo: this.configRepo,
|
|
235
|
-
enclaveHost: this.serverURL,
|
|
236
|
-
releaseDigest: "",
|
|
237
|
-
codeMeasurement: { type: "", registers: [] },
|
|
238
|
-
enclaveMeasurement: { measurement: { type: "", registers: [] } },
|
|
239
|
-
tlsPublicKey: "",
|
|
240
|
-
hpkePublicKey: "",
|
|
241
|
-
hardwareMeasurement: undefined,
|
|
242
|
-
codeFingerprint: "",
|
|
243
|
-
enclaveFingerprint: "",
|
|
244
|
-
selectedRouterEndpoint: this.serverURL,
|
|
245
|
-
securityVerified: false,
|
|
246
|
-
steps,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Internal verification logic that runs within WASM context
|
|
251
|
-
*/
|
|
252
|
-
async verifyInternal() {
|
|
253
|
-
const steps = {
|
|
254
|
-
fetchDigest: { status: "pending" },
|
|
255
|
-
verifyCode: { status: "pending" },
|
|
256
|
-
verifyEnclave: { status: "pending" },
|
|
257
|
-
compareMeasurements: { status: "pending" },
|
|
258
|
-
};
|
|
259
|
-
if (typeof globalThis.verify !== "function") {
|
|
260
|
-
steps.fetchDigest = {
|
|
261
|
-
status: "failed",
|
|
262
|
-
error: "WASM verify function not available",
|
|
263
|
-
};
|
|
264
|
-
this.saveFailedVerificationDocument(steps);
|
|
265
|
-
throw new Error("WASM verify function not available");
|
|
266
|
-
}
|
|
267
|
-
let groundTruth;
|
|
268
|
-
try {
|
|
269
|
-
const groundTruthJSON = await globalThis.verify(this.serverURL, this.configRepo);
|
|
270
|
-
groundTruth = JSON.parse(groundTruthJSON);
|
|
271
|
-
// Mark all steps as successful since WASM verify() succeeded
|
|
272
|
-
steps.fetchDigest = { status: "success" };
|
|
273
|
-
steps.verifyCode = { status: "success" };
|
|
274
|
-
steps.verifyEnclave = { status: "success" };
|
|
275
|
-
steps.compareMeasurements = { status: "success" };
|
|
276
|
-
}
|
|
277
|
-
catch (error) {
|
|
278
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
279
|
-
if (errorMessage.startsWith("fetchDigest:")) {
|
|
280
|
-
steps.fetchDigest = { status: "failed", error: errorMessage };
|
|
281
|
-
}
|
|
282
|
-
else if (errorMessage.startsWith("verifyCode:")) {
|
|
283
|
-
steps.fetchDigest = { status: "success" };
|
|
284
|
-
steps.verifyCode = { status: "failed", error: errorMessage };
|
|
285
|
-
}
|
|
286
|
-
else if (errorMessage.startsWith("verifyEnclave:")) {
|
|
287
|
-
steps.fetchDigest = { status: "success" };
|
|
288
|
-
steps.verifyCode = { status: "success" };
|
|
289
|
-
steps.verifyEnclave = { status: "failed", error: errorMessage };
|
|
290
|
-
}
|
|
291
|
-
else if (errorMessage.startsWith("measurements:")) {
|
|
292
|
-
steps.fetchDigest = { status: "success" };
|
|
293
|
-
steps.verifyCode = { status: "success" };
|
|
294
|
-
steps.verifyEnclave = { status: "success" };
|
|
295
|
-
steps.compareMeasurements = { status: "failed", error: errorMessage };
|
|
296
|
-
}
|
|
297
|
-
else if (errorMessage.startsWith("verifyHardware:") ||
|
|
298
|
-
errorMessage.startsWith("validateTLS:")) {
|
|
299
|
-
steps.fetchDigest = { status: "success" };
|
|
300
|
-
steps.verifyCode = { status: "success" };
|
|
301
|
-
steps.verifyEnclave = { status: "success" };
|
|
302
|
-
steps.otherError = { status: "failed", error: errorMessage };
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
steps.otherError = { status: "failed", error: errorMessage };
|
|
306
|
-
}
|
|
307
|
-
this.saveFailedVerificationDocument(steps);
|
|
308
|
-
throw error;
|
|
309
|
-
}
|
|
310
|
-
const attestation = {
|
|
311
|
-
tlsPublicKeyFingerprint: groundTruth.tls_public_key,
|
|
312
|
-
hpkePublicKey: groundTruth.hpke_public_key,
|
|
313
|
-
measurement: groundTruth.enclave_measurement,
|
|
314
|
-
};
|
|
315
|
-
this.lastVerificationDocument = {
|
|
316
|
-
configRepo: this.configRepo,
|
|
317
|
-
enclaveHost: this.serverURL,
|
|
318
|
-
releaseDigest: groundTruth.digest,
|
|
319
|
-
codeMeasurement: groundTruth.code_measurement,
|
|
320
|
-
enclaveMeasurement: attestation,
|
|
321
|
-
tlsPublicKey: groundTruth.tls_public_key,
|
|
322
|
-
hpkePublicKey: groundTruth.hpke_public_key,
|
|
323
|
-
hardwareMeasurement: groundTruth.hardware_measurement,
|
|
324
|
-
codeFingerprint: groundTruth.code_fingerprint,
|
|
325
|
-
enclaveFingerprint: groundTruth.enclave_fingerprint,
|
|
326
|
-
selectedRouterEndpoint: this.serverURL,
|
|
327
|
-
securityVerified: true,
|
|
328
|
-
steps,
|
|
329
|
-
};
|
|
330
|
-
return attestation;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Returns the verification document from the last verify() call
|
|
334
|
-
*
|
|
335
|
-
* The document contains detailed step-by-step verification results including:
|
|
336
|
-
* - Step status (pending/success/failed) for each verification phase
|
|
337
|
-
* - Measurements, fingerprints, and cryptographic keys
|
|
338
|
-
* - Error messages for any failed steps
|
|
339
|
-
*
|
|
340
|
-
* Available even if verification failed, allowing inspection of which step failed.
|
|
341
|
-
*
|
|
342
|
-
* @returns VerificationDocument with complete verification details, or undefined if verify() hasn't been called
|
|
343
|
-
*/
|
|
344
|
-
getVerificationDocument() {
|
|
345
|
-
return this.lastVerificationDocument;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
exports.Verifier = Verifier;
|
|
349
|
-
Verifier.goInstance = null;
|
|
350
|
-
Verifier.initializationPromise = null;
|
|
351
|
-
Verifier.defaultWasmUrl = "https://tinfoilsh.github.io/verifier/tinfoil-verifier.wasm";
|
|
352
|
-
Verifier.originalFsWriteSync = null;
|
|
353
|
-
Verifier.wasmLogsSuppressed = true;
|
|
354
|
-
Verifier.globalsInitialized = false;
|
|
355
|
-
// Start initialization as soon as the module loads
|
|
356
|
-
function shouldAutoInitializeWasm() {
|
|
357
|
-
const globalAny = globalThis;
|
|
358
|
-
const env = globalAny.process?.env;
|
|
359
|
-
const autoInitFlag = env?.TINFOIL_SKIP_WASM_AUTO_INIT ?? env?.TINFOIL_DISABLE_WASM_AUTO_INIT;
|
|
360
|
-
if (typeof autoInitFlag === "string") {
|
|
361
|
-
const normalized = autoInitFlag.toLowerCase();
|
|
362
|
-
if (normalized === "1" || normalized === "true") {
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (env?.NODE_ENV === "test") {
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
const isNode = typeof globalAny.process?.versions?.node === "string";
|
|
370
|
-
if (isNode) {
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
const hasBrowserGlobals = typeof globalAny.window === "object" &&
|
|
374
|
-
typeof globalAny.document === "object";
|
|
375
|
-
return hasBrowserGlobals;
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Control WASM log output
|
|
379
|
-
*
|
|
380
|
-
* The Go WASM runtime outputs logs (stdout/stderr) through a polyfilled fs.writeSync.
|
|
381
|
-
* This function allows suppressing those logs without affecting other console output.
|
|
382
|
-
* By default, WASM logs are suppressed to reduce noise.
|
|
383
|
-
*
|
|
384
|
-
* @param suppress - Whether to suppress WASM logs (default: true)
|
|
385
|
-
* @returns void
|
|
386
|
-
*/
|
|
387
|
-
function suppressWasmLogs(suppress = true) {
|
|
388
|
-
globalThis.__tinfoilSuppressWasmLogs = suppress;
|
|
389
|
-
Verifier.wasmLogsSuppressed = suppress;
|
|
390
|
-
const fsObj = globalThis.fs;
|
|
391
|
-
if (!fsObj || typeof fsObj.writeSync !== "function")
|
|
392
|
-
return;
|
|
393
|
-
if (suppress) {
|
|
394
|
-
if (!Verifier.originalFsWriteSync) {
|
|
395
|
-
Verifier.originalFsWriteSync = fsObj.writeSync.bind(fsObj);
|
|
396
|
-
}
|
|
397
|
-
fsObj.writeSync = function (_fd, buf) {
|
|
398
|
-
return buf.length;
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
else if (Verifier.originalFsWriteSync) {
|
|
402
|
-
fsObj.writeSync = Verifier.originalFsWriteSync;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Initialize globals needed for Go WASM runtime
|
|
407
|
-
* This function sets up browser-like globals that the Go WASM runtime expects
|
|
408
|
-
*/
|
|
409
|
-
async function initializeWasmGlobals() {
|
|
410
|
-
// Only initialize once
|
|
411
|
-
if (Verifier.globalsInitialized) {
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
const root = globalThis;
|
|
415
|
-
// Performance API (Go runtime expects a few methods to exist)
|
|
416
|
-
if (!root.performance) {
|
|
417
|
-
root.performance = {
|
|
418
|
-
now: () => Date.now(),
|
|
419
|
-
markResourceTiming: () => { },
|
|
420
|
-
mark: () => { },
|
|
421
|
-
measure: () => { },
|
|
422
|
-
clearMarks: () => { },
|
|
423
|
-
clearMeasures: () => { },
|
|
424
|
-
getEntriesByName: () => [],
|
|
425
|
-
getEntriesByType: () => [],
|
|
426
|
-
getEntries: () => [],
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
else {
|
|
430
|
-
root.performance.now = root.performance.now ?? (() => Date.now());
|
|
431
|
-
root.performance.markResourceTiming =
|
|
432
|
-
root.performance.markResourceTiming ?? (() => { });
|
|
433
|
-
root.performance.mark = root.performance.mark ?? (() => { });
|
|
434
|
-
root.performance.measure = root.performance.measure ?? (() => { });
|
|
435
|
-
root.performance.clearMarks = root.performance.clearMarks ?? (() => { });
|
|
436
|
-
root.performance.clearMeasures =
|
|
437
|
-
root.performance.clearMeasures ?? (() => { });
|
|
438
|
-
root.performance.getEntriesByName =
|
|
439
|
-
root.performance.getEntriesByName ?? (() => []);
|
|
440
|
-
root.performance.getEntriesByType =
|
|
441
|
-
root.performance.getEntriesByType ?? (() => []);
|
|
442
|
-
root.performance.getEntries = root.performance.getEntries ?? (() => []);
|
|
443
|
-
}
|
|
444
|
-
// Text encoding
|
|
445
|
-
if (!root.TextEncoder) {
|
|
446
|
-
root.TextEncoder = getTextEncoder();
|
|
447
|
-
}
|
|
448
|
-
if (!root.TextDecoder) {
|
|
449
|
-
root.TextDecoder = getTextDecoder();
|
|
450
|
-
}
|
|
451
|
-
// Crypto API (needed by Go WASM)
|
|
452
|
-
ensureCrypto(root);
|
|
453
|
-
// Default: suppress WASM (Go) stdout/stderr logs unless explicitly enabled by caller
|
|
454
|
-
if (typeof root.__tinfoilSuppressWasmLogs === "undefined") {
|
|
455
|
-
root.__tinfoilSuppressWasmLogs = true;
|
|
456
|
-
}
|
|
457
|
-
// Force process to stay running (prevent Go from exiting Node process)
|
|
458
|
-
// This is a common issue with Go WASM in Node - it calls process.exit()
|
|
459
|
-
if (root.process &&
|
|
460
|
-
typeof root.process.exit === "function" &&
|
|
461
|
-
!root.__tinfoilProcessExitPatched) {
|
|
462
|
-
root.__tinfoilProcessExitPatched = true;
|
|
463
|
-
const originalExit = root.process.exit.bind(root.process);
|
|
464
|
-
root.__tinfoilOriginalProcessExit = originalExit;
|
|
465
|
-
// Replace process.exit to prevent the Go WASM runtime from terminating the Node.js process.
|
|
466
|
-
// When wasm log suppression is enabled, suppress the informational log about the ignored exit
|
|
467
|
-
// so callers can silence only the WASM-related noise while keeping application logs intact.
|
|
468
|
-
root.process.exit = ((code) => {
|
|
469
|
-
if (!root.__tinfoilSuppressWasmLogs) {
|
|
470
|
-
console.log(`Process exit called with code ${code} - ignoring to keep runtime alive`);
|
|
471
|
-
}
|
|
472
|
-
return undefined;
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
await loadWasmExec();
|
|
476
|
-
Verifier.globalsInitialized = true;
|
|
477
|
-
}
|
|
478
|
-
function ensureCrypto(root) {
|
|
479
|
-
const hasWorkingGetRandomValues = root.crypto && typeof root.crypto.getRandomValues === "function"
|
|
480
|
-
? root.crypto
|
|
481
|
-
: resolveWindowCrypto(root);
|
|
482
|
-
if (hasWorkingGetRandomValues) {
|
|
483
|
-
if (!root.crypto) {
|
|
484
|
-
root.crypto = hasWorkingGetRandomValues;
|
|
485
|
-
}
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
const nodeRandomBytes = resolveNodeRandomBytes();
|
|
489
|
-
if (!nodeRandomBytes) {
|
|
490
|
-
throw new Error("crypto.getRandomValues is not available in this environment");
|
|
491
|
-
}
|
|
492
|
-
const fallbackCrypto = {
|
|
493
|
-
getRandomValues: (buffer) => {
|
|
494
|
-
const bytes = nodeRandomBytes(buffer.length);
|
|
495
|
-
buffer.set(bytes);
|
|
496
|
-
return buffer;
|
|
497
|
-
},
|
|
498
|
-
};
|
|
499
|
-
try {
|
|
500
|
-
root.crypto = fallbackCrypto;
|
|
501
|
-
}
|
|
502
|
-
catch {
|
|
503
|
-
Object.defineProperty(root, "crypto", {
|
|
504
|
-
configurable: true,
|
|
505
|
-
enumerable: false,
|
|
506
|
-
value: fallbackCrypto,
|
|
507
|
-
writable: false,
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
function resolveWindowCrypto(root) {
|
|
512
|
-
const maybeWindow = root.window ?? (typeof window !== "undefined" ? window : undefined);
|
|
513
|
-
if (maybeWindow?.crypto &&
|
|
514
|
-
typeof maybeWindow.crypto.getRandomValues === "function") {
|
|
515
|
-
return maybeWindow.crypto;
|
|
516
|
-
}
|
|
517
|
-
return undefined;
|
|
518
|
-
}
|
|
519
|
-
function resolveNodeRandomBytes() {
|
|
520
|
-
if (!nodeRequire) {
|
|
521
|
-
return undefined;
|
|
522
|
-
}
|
|
523
|
-
try {
|
|
524
|
-
const cryptoModule = nodeRequire("crypto");
|
|
525
|
-
const randomBytes = typeof cryptoModule?.randomBytes === "function"
|
|
526
|
-
? cryptoModule.randomBytes
|
|
527
|
-
: undefined;
|
|
528
|
-
if (randomBytes) {
|
|
529
|
-
return (size) => {
|
|
530
|
-
const result = randomBytes(size);
|
|
531
|
-
return result instanceof Uint8Array ? result : new Uint8Array(result);
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
catch {
|
|
536
|
-
return undefined;
|
|
537
|
-
}
|
|
538
|
-
return undefined;
|
|
539
|
-
}
|
|
540
|
-
function loadWasmExec() {
|
|
541
|
-
if (!wasmExecLoader) {
|
|
542
|
-
wasmExecLoader = (async () => {
|
|
543
|
-
// Prefer a dynamic import so bundlers (Next/Webpack/Vite) include the file.
|
|
544
|
-
// If that fails (e.g., pure Node without bundler), fall back to require.
|
|
545
|
-
try {
|
|
546
|
-
// @ts-expect-error: Local JS helper has no TS types; ambient module declared in wasm-exec.d.ts
|
|
547
|
-
await Promise.resolve().then(() => __importStar(require("./wasm-exec.js")));
|
|
548
|
-
}
|
|
549
|
-
catch {
|
|
550
|
-
if (nodeRequire) {
|
|
551
|
-
nodeRequire("./wasm-exec.js");
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
throw new Error("Failed to load wasm-exec.js via dynamic import, and require() is unavailable");
|
|
555
|
-
}
|
|
556
|
-
})();
|
|
557
|
-
wasmExecLoader.catch(() => {
|
|
558
|
-
wasmExecLoader = null;
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
return wasmExecLoader;
|
|
562
|
-
}
|
|
563
|
-
function createNodeRequire() {
|
|
564
|
-
try {
|
|
565
|
-
return typeof require === "function" ? require : undefined;
|
|
566
|
-
}
|
|
567
|
-
catch {
|
|
568
|
-
return undefined;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
1
|
+
export * from '@tinfoilsh/verifier';
|
|
2
|
+
//# sourceMappingURL=verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.js","sourceRoot":"","sources":["../src/verifier.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
|