twzrd-receipt-verifier 1.1.0 → 1.2.1
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/README.md +10 -1
- package/package.json +1 -1
- package/verify_twzrd_receipt.js +54 -9
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ verified_tx, behavior_proof, minted_at}` (exact key order). The `wallet` is the
|
|
|
80
80
|
first signed field but is **not** stored in the anchor - it is the `<wallet>.json`
|
|
81
81
|
filename / the cNFT leaf owner - so pass `--wallet` or keep the filename. The
|
|
82
82
|
signing key (`2ELSDx...`) is **built in** to the verifier (pinned in the audited
|
|
83
|
-
package); override with `--pubkey`.
|
|
83
|
+
package); override with `--pubkey`, or fetch the published copy with `--fetch-key`.
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
86
|
# fetch a receipt and verify it (wallet inferred from the filename, key built-in)
|
|
@@ -90,8 +90,17 @@ npx twzrd-receipt-verifier $W.json --self-test
|
|
|
90
90
|
|
|
91
91
|
# or pass the wallet explicitly (e.g. when piping from stdin)
|
|
92
92
|
npx twzrd-receipt-verifier anchor.json --wallet $W
|
|
93
|
+
|
|
94
|
+
# fetch the key from the published descriptor instead of the built-in copy
|
|
95
|
+
# (cross-check, or pin to whatever the live domain publishes)
|
|
96
|
+
npx twzrd-receipt-verifier $W.json --fetch-key
|
|
93
97
|
```
|
|
94
98
|
|
|
99
|
+
The key is published, machine-readable, at `https://api.twzrd.xyz/v1/receipts/pubkey`
|
|
100
|
+
(and `https://twzrd.xyz/.well-known/twzrd-receipt-pubkey`) with the full signing spec
|
|
101
|
+
(`public_key`, `signed_fields`, `scheme`, `tree`). It must equal the built-in key **and**
|
|
102
|
+
the on-chain verified creator of every cNFT in the tree - three independent sources.
|
|
103
|
+
|
|
95
104
|
```
|
|
96
105
|
mode : cNFT (Bubblegum anchor)
|
|
97
106
|
trusted pubkey : 2ELSDxLkb7dYrN6EUG69tNtULAq4Fo7WPvXyrZPmuFif [source: built-in genesis authority]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twzrd-receipt-verifier",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Standalone offline verifier for TWZRD receipts: AO-Receipt V5/V6 (keccak256 leaf) and genesis cNFT receipts (Ed25519 over compact JSON). No trust in TWZRD servers or code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twzrd",
|
package/verify_twzrd_receipt.js
CHANGED
|
@@ -44,9 +44,12 @@ const DEFAULT_BASE_URL = 'https://intel.twzrd.xyz';
|
|
|
44
44
|
const KECCAK_EMPTY = 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
|
|
45
45
|
// Genesis cNFT receipt authority (airship). Baked in as the most paranoid form of
|
|
46
46
|
// out-of-band pinning: the key ships in this audited package, never fetched live.
|
|
47
|
-
// Override with --pubkey
|
|
48
|
-
//
|
|
47
|
+
// Override with --pubkey, or fetch the published copy with --fetch-key (cross-check).
|
|
48
|
+
// Matches `verify_pubkey` in every genesis anchor and the verified creator on every
|
|
49
|
+
// cNFT in tree 8QFdTqBkSeyuvp47dXdpwfWzXTuYSbAC64oT4soPGnXS.
|
|
49
50
|
const DEFAULT_CNFT_PUBKEY = '2ELSDxLkb7dYrN6EUG69tNtULAq4Fo7WPvXyrZPmuFif';
|
|
51
|
+
// Where --fetch-key looks for the published cNFT key descriptor.
|
|
52
|
+
const DEFAULT_CNFT_BASE_URL = 'https://api.twzrd.xyz';
|
|
50
53
|
|
|
51
54
|
function b58decode(s) { return Buffer.from(bs58.decode(s)); }
|
|
52
55
|
|
|
@@ -146,6 +149,32 @@ function fetchPublishedPubkey(baseUrl) {
|
|
|
146
149
|
return fetchPath(0);
|
|
147
150
|
}
|
|
148
151
|
|
|
152
|
+
// Fetch the published cNFT signing key descriptor (for --fetch-key). Returns the
|
|
153
|
+
// base58 pubkey. This trades package-trust for domain/TLS-trust; the built-in key is
|
|
154
|
+
// the default precisely because it needs no network. Use this to CROSS-CHECK the
|
|
155
|
+
// built-in, or to pin to whatever the live domain currently publishes.
|
|
156
|
+
function fetchCnftPubkey(baseUrl) {
|
|
157
|
+
const base = baseUrl.replace(/\/+$/, '');
|
|
158
|
+
const paths = ['/v1/receipts/pubkey', '/.well-known/twzrd-receipt-pubkey'];
|
|
159
|
+
const headers = { 'User-Agent': 'twzrd-receipt-verifier/cnft' };
|
|
160
|
+
function fetchPath(i) {
|
|
161
|
+
if (i >= paths.length) return Promise.reject(new Error('no cNFT pubkey endpoint responded'));
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
https.get(base + paths[i], { headers }, (res) => {
|
|
164
|
+
let body = '';
|
|
165
|
+
res.on('data', (c) => (body += c));
|
|
166
|
+
res.on('end', () => {
|
|
167
|
+
try {
|
|
168
|
+
const pk = JSON.parse(body).public_key;
|
|
169
|
+
if (pk) resolve(pk); else throw new Error('no public_key field');
|
|
170
|
+
} catch (e) { fetchPath(i + 1).then(resolve, reject); }
|
|
171
|
+
});
|
|
172
|
+
}).on('error', () => fetchPath(i + 1).then(resolve, reject));
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return fetchPath(0);
|
|
176
|
+
}
|
|
177
|
+
|
|
149
178
|
function verify(receipt, trustedPubkey) {
|
|
150
179
|
const out = { leaf_valid: false, signature_valid: false, errors: [] };
|
|
151
180
|
const pre = receipt.preimage || {};
|
|
@@ -275,14 +304,18 @@ TWZRD's published Ed25519 key and was not tampered with. Auto-detects two famili
|
|
|
275
304
|
- cNFT Receipt (genesis anchor): compact-JSON payload, signed by ${DEFAULT_CNFT_PUBKEY.slice(0, 7)}... (built-in)
|
|
276
305
|
|
|
277
306
|
usage:
|
|
278
|
-
twzrd-receipt-verifier <receipt.json|-> [--pubkey KEY] [--wallet ADDR] [--base-url URL] [--max-age SECS] [--self-test]
|
|
307
|
+
twzrd-receipt-verifier <receipt.json|-> [--pubkey KEY] [--fetch-key] [--wallet ADDR] [--base-url URL] [--max-age SECS] [--self-test]
|
|
279
308
|
|
|
280
309
|
arguments:
|
|
281
310
|
<receipt.json> path to the receipt JSON, or "-" to read from stdin
|
|
282
311
|
--pubkey KEY trust this base58 Ed25519 pubkey (out-of-band) instead of fetching/built-in
|
|
312
|
+
--fetch-key (cNFT only) fetch the signing key from the published well-known descriptor
|
|
313
|
+
(--base-url or ${DEFAULT_CNFT_BASE_URL}) instead of the built-in copy. Trades
|
|
314
|
+
package-trust for domain/TLS-trust; default stays built-in (no network).
|
|
283
315
|
--wallet ADDR (cNFT only) the leaf-owner wallet, which is part of the signed payload but
|
|
284
316
|
not stored in the anchor block. Inferred from a <wallet>.json filename if omitted.
|
|
285
|
-
--base-url URL
|
|
317
|
+
--base-url URL where to fetch the key: trust-API default ${DEFAULT_BASE_URL}; cNFT (--fetch-key)
|
|
318
|
+
default ${DEFAULT_CNFT_BASE_URL}
|
|
286
319
|
--max-age SECS replay-resistance policy: reject if the receipt's timestamp (preimage.timestamp_unix
|
|
287
320
|
for trust-API, anchor.minted_at for cNFT) is older than SECS, OR is missing.
|
|
288
321
|
Crypto is time-independent; this is opt-in relying-party policy.
|
|
@@ -290,10 +323,11 @@ arguments:
|
|
|
290
323
|
-h, --help show this help
|
|
291
324
|
|
|
292
325
|
exit code: 0 = VALID, 1 = INVALID / error
|
|
293
|
-
trust-API key source: ${DEFAULT_BASE_URL}/.well-known/x402
|
|
326
|
+
trust-API key source: ${DEFAULT_BASE_URL}/.well-known/x402
|
|
327
|
+
cNFT key source: ${DEFAULT_CNFT_BASE_URL}/v1/receipts/pubkey`;
|
|
294
328
|
if (args.includes('-h') || args.includes('--help')) { console.log(HELP); process.exit(0); }
|
|
295
329
|
if (args.length === 0) {
|
|
296
|
-
console.error('usage: twzrd-receipt-verifier <receipt.json|-> [--pubkey KEY] [--wallet ADDR] [--base-url URL] [--max-age SECS] [--self-test]');
|
|
330
|
+
console.error('usage: twzrd-receipt-verifier <receipt.json|-> [--pubkey KEY] [--fetch-key] [--wallet ADDR] [--base-url URL] [--max-age SECS] [--self-test]');
|
|
297
331
|
console.error(' twzrd-receipt-verifier --help');
|
|
298
332
|
process.exit(1);
|
|
299
333
|
}
|
|
@@ -310,8 +344,16 @@ trust-API key source: ${DEFAULT_BASE_URL}/.well-known/x402`;
|
|
|
310
344
|
// ── cNFT (Bubblegum anchor) receipt: Ed25519 over compact JSON, no keccak leaf ──
|
|
311
345
|
if (isCnftReceipt(receipt)) {
|
|
312
346
|
let trusted = getOpt('--pubkey'), keySrc;
|
|
313
|
-
if (trusted) {
|
|
314
|
-
|
|
347
|
+
if (trusted) {
|
|
348
|
+
keySrc = '--pubkey (out-of-band)';
|
|
349
|
+
} else if (args.includes('--fetch-key')) {
|
|
350
|
+
const fetchBase = getOpt('--base-url') || DEFAULT_CNFT_BASE_URL;
|
|
351
|
+
trusted = await fetchCnftPubkey(fetchBase);
|
|
352
|
+
keySrc = `fetched from ${fetchBase}`;
|
|
353
|
+
} else {
|
|
354
|
+
trusted = DEFAULT_CNFT_PUBKEY;
|
|
355
|
+
keySrc = 'built-in genesis authority';
|
|
356
|
+
}
|
|
315
357
|
const { wallet, src: walletSrc } = resolveWallet({
|
|
316
358
|
explicitWallet: getOpt('--wallet'), receipt, receiptPath: receiptArg,
|
|
317
359
|
});
|
|
@@ -339,6 +381,7 @@ trust-API key source: ${DEFAULT_BASE_URL}/.well-known/x402`;
|
|
|
339
381
|
res.errors.forEach((e) => console.log(' - ' + e));
|
|
340
382
|
let ok = !!res.valid;
|
|
341
383
|
console.log(`RESULT : ${ok ? 'VALID (TWZRD-authored, untampered)' : 'INVALID'}`);
|
|
384
|
+
if (ok) console.log(' verified with the same library TWZRD uses internally (npm: twzrd-receipt-verifier)');
|
|
342
385
|
|
|
343
386
|
if (selfTest) {
|
|
344
387
|
const tampered = JSON.parse(raw);
|
|
@@ -387,6 +430,7 @@ trust-API key source: ${DEFAULT_BASE_URL}/.well-known/x402`;
|
|
|
387
430
|
res.errors.forEach((e) => console.log(' - ' + e));
|
|
388
431
|
let ok = !!res.valid;
|
|
389
432
|
console.log(`RESULT : ${ok ? 'VALID (TWZRD-authored, untampered)' : 'INVALID'}`);
|
|
433
|
+
if (ok) console.log(' verified with the same library TWZRD uses internally (npm: twzrd-receipt-verifier)');
|
|
390
434
|
|
|
391
435
|
if (selfTest) {
|
|
392
436
|
const tampered = JSON.parse(raw);
|
|
@@ -406,7 +450,8 @@ trust-API key source: ${DEFAULT_BASE_URL}/.well-known/x402`;
|
|
|
406
450
|
module.exports = {
|
|
407
451
|
verify, recomputeLeaf,
|
|
408
452
|
verifyCnft, cnftSignedPayload, isCnftReceipt, resolveWallet,
|
|
409
|
-
|
|
453
|
+
fetchCnftPubkey,
|
|
454
|
+
DEFAULT_CNFT_PUBKEY, DEFAULT_CNFT_BASE_URL, CNFT_SIGNED_FIELDS,
|
|
410
455
|
};
|
|
411
456
|
|
|
412
457
|
if (require.main === module) {
|