agent-passport-system 3.0.0 → 3.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.
- package/README.md +1 -1
- package/dist/src/v2/read_fidelity_receipt/__tests__/read_fidelity_receipt.test.d.ts +2 -0
- package/dist/src/v2/read_fidelity_receipt/__tests__/read_fidelity_receipt.test.d.ts.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/__tests__/read_fidelity_receipt.test.js +392 -0
- package/dist/src/v2/read_fidelity_receipt/__tests__/read_fidelity_receipt.test.js.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/index.d.ts +4 -0
- package/dist/src/v2/read_fidelity_receipt/index.d.ts.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/index.js +16 -0
- package/dist/src/v2/read_fidelity_receipt/index.js.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/receipt.d.ts +45 -0
- package/dist/src/v2/read_fidelity_receipt/receipt.d.ts.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/receipt.js +283 -0
- package/dist/src/v2/read_fidelity_receipt/receipt.js.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/sampler.d.ts +35 -0
- package/dist/src/v2/read_fidelity_receipt/sampler.d.ts.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/sampler.js +110 -0
- package/dist/src/v2/read_fidelity_receipt/sampler.js.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/types.d.ts +139 -0
- package/dist/src/v2/read_fidelity_receipt/types.d.ts.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/types.js +17 -0
- package/dist/src/v2/read_fidelity_receipt/types.js.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/vectors/generate.d.ts +2 -0
- package/dist/src/v2/read_fidelity_receipt/vectors/generate.d.ts.map +1 -0
- package/dist/src/v2/read_fidelity_receipt/vectors/generate.js +261 -0
- package/dist/src/v2/read_fidelity_receipt/vectors/generate.js.map +1 -0
- package/dist/src/v2/word_handles/__tests__/word_handles.test.d.ts +2 -0
- package/dist/src/v2/word_handles/__tests__/word_handles.test.d.ts.map +1 -0
- package/dist/src/v2/word_handles/__tests__/word_handles.test.js +312 -0
- package/dist/src/v2/word_handles/__tests__/word_handles.test.js.map +1 -0
- package/dist/src/v2/word_handles/codec.d.ts +54 -0
- package/dist/src/v2/word_handles/codec.d.ts.map +1 -0
- package/dist/src/v2/word_handles/codec.js +334 -0
- package/dist/src/v2/word_handles/codec.js.map +1 -0
- package/dist/src/v2/word_handles/index.d.ts +4 -0
- package/dist/src/v2/word_handles/index.d.ts.map +1 -0
- package/dist/src/v2/word_handles/index.js +13 -0
- package/dist/src/v2/word_handles/index.js.map +1 -0
- package/dist/src/v2/word_handles/lexicon.d.ts +7 -0
- package/dist/src/v2/word_handles/lexicon.d.ts.map +1 -0
- package/dist/src/v2/word_handles/lexicon.js +276 -0
- package/dist/src/v2/word_handles/lexicon.js.map +1 -0
- package/dist/src/v2/word_handles/types.d.ts +58 -0
- package/dist/src/v2/word_handles/types.d.ts.map +1 -0
- package/dist/src/v2/word_handles/types.js +13 -0
- package/dist/src/v2/word_handles/types.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
|
|
2
|
+
// ══════════════════════════════════════════════════════════════════
|
|
3
|
+
// read_fidelity_receipt (v2): shared parity vector generator
|
|
4
|
+
// ══════════════════════════════════════════════════════════════════
|
|
5
|
+
// Writes read-fidelity-receipt-v0.1-vectors.json next to this file.
|
|
6
|
+
// Fully deterministic: every input is a fixed string or derived from
|
|
7
|
+
// sha256 over a fixed label. No wall clock, no randomness. The Python
|
|
8
|
+
// SDK copies the emitted file BYTE-IDENTICALLY and asserts its
|
|
9
|
+
// implementation reproduces every value; Ed25519 signing is
|
|
10
|
+
// deterministic, so re-signing the record body with the fixture key
|
|
11
|
+
// must yield the identical sig.
|
|
12
|
+
//
|
|
13
|
+
// Run: npx tsx src/v2/read_fidelity_receipt/vectors/generate.ts
|
|
14
|
+
// ══════════════════════════════════════════════════════════════════
|
|
15
|
+
import assert from 'node:assert/strict';
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
17
|
+
import { writeFileSync } from 'node:fs';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { canonicalizeJCS } from '../../../core/canonical-jcs.js';
|
|
20
|
+
import { verify as edVerify, publicKeyFromPrivate, sign } from '../../../crypto/keys.js';
|
|
21
|
+
import { LEXICON_ID, LEXICON_PROFILE, PROFILES, WORDS, decodeProfile, encodeProfile, } from '../../word_handles/index.js';
|
|
22
|
+
import { canonicalNoSig, commitSpans, createReadFidelityReceipt, deriveSeed, sampleSpans, verifyAgainstSource, verifyReadFidelityReceipt, } from '../index.js';
|
|
23
|
+
function sha256hex(input) {
|
|
24
|
+
return createHash('sha256').update(input, 'utf8').digest('hex');
|
|
25
|
+
}
|
|
26
|
+
/** First 4 bytes of sha256(label) as a big-endian uint32. */
|
|
27
|
+
function seedUint32(label) {
|
|
28
|
+
return createHash('sha256').update(label, 'utf8').digest().readUInt32BE(0);
|
|
29
|
+
}
|
|
30
|
+
const WORD_TO_INDEX = (() => {
|
|
31
|
+
const m = new Map();
|
|
32
|
+
for (let i = 0; i < WORDS.length; i++)
|
|
33
|
+
m.set(WORDS[i], i);
|
|
34
|
+
return m;
|
|
35
|
+
})();
|
|
36
|
+
// ── jcs_kats ─────────────────────────────────────────────────────
|
|
37
|
+
const JCS_KAT_INPUTS = [
|
|
38
|
+
{
|
|
39
|
+
name: 'sorted_keys',
|
|
40
|
+
value: { zebra: 1, alpha: 2, nested: { b: 2, a: 1 } },
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'null_preserved',
|
|
44
|
+
value: { present: 'x', absent: null, list: [1, null, 'z'] },
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'unicode_emoji_cyrillic',
|
|
48
|
+
value: { emoji: '🔐🙂', mixed: 'привіт 🌍', мова: 'українська' },
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'number_forms',
|
|
52
|
+
value: { float: 3.14, int: 42, negative: -7, zero: 0 },
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'empty_structures',
|
|
56
|
+
value: { arr: [], obj: {} },
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
const jcs_kats = JCS_KAT_INPUTS.map(({ name, value }) => {
|
|
60
|
+
const canonical = canonicalizeJCS(value);
|
|
61
|
+
return { name, value, canonical, sha256: sha256hex(canonical) };
|
|
62
|
+
});
|
|
63
|
+
// ── word_handle_cases ────────────────────────────────────────────
|
|
64
|
+
const PROFILE_NAMES = [
|
|
65
|
+
'compact',
|
|
66
|
+
'default',
|
|
67
|
+
'high_assurance',
|
|
68
|
+
];
|
|
69
|
+
const round_trips = [];
|
|
70
|
+
for (let d = 0; d < 4; d++) {
|
|
71
|
+
const digest = sha256hex(`wh-fixture:${d}`);
|
|
72
|
+
for (const profile of PROFILE_NAMES) {
|
|
73
|
+
const words = encodeProfile(digest, profile);
|
|
74
|
+
const res = decodeProfile(words, profile);
|
|
75
|
+
assert.equal(res.checksumOk, true);
|
|
76
|
+
assert.equal(res.prefixBits, PROFILES[profile].prefixBits);
|
|
77
|
+
round_trips.push({
|
|
78
|
+
digest,
|
|
79
|
+
profile,
|
|
80
|
+
words,
|
|
81
|
+
prefix_hex: res.prefixHex,
|
|
82
|
+
prefix_bits: res.prefixBits,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Substitution negative: one data word replaced, deterministically.
|
|
87
|
+
const subDigest = sha256hex('wh-fixture:0');
|
|
88
|
+
const subWords = encodeProfile(subDigest, 'default');
|
|
89
|
+
const subPos = seedUint32('rf-wh-substitute-pos') % PROFILES.default.dataWords;
|
|
90
|
+
const subOrig = WORD_TO_INDEX.get(subWords[subPos]);
|
|
91
|
+
const subDelta = 1 + (seedUint32('rf-wh-substitute-delta') % (WORDS.length - 1));
|
|
92
|
+
const subMutated = subWords.slice();
|
|
93
|
+
subMutated[subPos] = WORDS[(subOrig + subDelta) % WORDS.length];
|
|
94
|
+
const subRes = decodeProfile(subMutated, 'default');
|
|
95
|
+
assert.equal(subRes.checksumOk, false);
|
|
96
|
+
assert.deepEqual(subRes.outOfLexicon, []);
|
|
97
|
+
const substitution_negative = {
|
|
98
|
+
digest: subDigest,
|
|
99
|
+
profile: 'default',
|
|
100
|
+
words: subMutated,
|
|
101
|
+
substituted_index: subPos,
|
|
102
|
+
checksum_ok: false,
|
|
103
|
+
};
|
|
104
|
+
// Transposition negative: first adjacent differing data word pair swapped.
|
|
105
|
+
const trDigest = sha256hex('wh-fixture:1');
|
|
106
|
+
const trWords = encodeProfile(trDigest, 'default');
|
|
107
|
+
let trPair = -1;
|
|
108
|
+
for (let i = 0; i < PROFILES.default.dataWords - 1; i++) {
|
|
109
|
+
if (trWords[i] !== trWords[i + 1]) {
|
|
110
|
+
trPair = i;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
assert.ok(trPair >= 0, 'no adjacent differing data words in transposition base');
|
|
115
|
+
const trMutated = trWords.slice();
|
|
116
|
+
trMutated[trPair] = trWords[trPair + 1];
|
|
117
|
+
trMutated[trPair + 1] = trWords[trPair];
|
|
118
|
+
const trRes = decodeProfile(trMutated, 'default');
|
|
119
|
+
assert.equal(trRes.checksumOk, false);
|
|
120
|
+
assert.deepEqual(trRes.outOfLexicon, []);
|
|
121
|
+
const transposition_negative = {
|
|
122
|
+
digest: trDigest,
|
|
123
|
+
profile: 'default',
|
|
124
|
+
words: trMutated,
|
|
125
|
+
swapped_indices: [trPair, trPair + 1],
|
|
126
|
+
checksum_ok: false,
|
|
127
|
+
};
|
|
128
|
+
// Out-of-lexicon negative: two words replaced with non-lexicon strings.
|
|
129
|
+
const olDigest = sha256hex('wh-fixture:2');
|
|
130
|
+
const olWords = encodeProfile(olDigest, 'default');
|
|
131
|
+
const olMutated = olWords.slice();
|
|
132
|
+
olMutated[1] = 'notaword';
|
|
133
|
+
olMutated[3] = 'xxqqzz';
|
|
134
|
+
const olRes = decodeProfile(olMutated, 'default');
|
|
135
|
+
assert.deepEqual(olRes.outOfLexicon, [1, 3]);
|
|
136
|
+
assert.equal(olRes.prefixHex, null);
|
|
137
|
+
assert.equal(olRes.checksumOk, false);
|
|
138
|
+
const out_of_lexicon_negative = {
|
|
139
|
+
digest: olDigest,
|
|
140
|
+
profile: 'default',
|
|
141
|
+
words: olMutated,
|
|
142
|
+
out_of_lexicon: [1, 3],
|
|
143
|
+
checksum_ok: false,
|
|
144
|
+
};
|
|
145
|
+
const word_handle_cases = {
|
|
146
|
+
round_trips,
|
|
147
|
+
substitution_negative,
|
|
148
|
+
transposition_negative,
|
|
149
|
+
out_of_lexicon_negative,
|
|
150
|
+
};
|
|
151
|
+
// ── seed_kats ────────────────────────────────────────────────────
|
|
152
|
+
const seedKatPresent = {
|
|
153
|
+
name: 'presentation_digest_present',
|
|
154
|
+
content_digest: `sha256:${sha256hex('rf-seed-content:0')}`,
|
|
155
|
+
presentation_digest: `sha256:${sha256hex('rf-seed-presentation:0')}`,
|
|
156
|
+
nonce: 'seed-kat-nonce-0',
|
|
157
|
+
version: '1',
|
|
158
|
+
seed: '',
|
|
159
|
+
};
|
|
160
|
+
seedKatPresent.seed = deriveSeed(seedKatPresent.content_digest, seedKatPresent.presentation_digest, seedKatPresent.nonce, seedKatPresent.version);
|
|
161
|
+
const seedKatNull = {
|
|
162
|
+
name: 'presentation_digest_null',
|
|
163
|
+
content_digest: `sha256:${sha256hex('rf-seed-content:1')}`,
|
|
164
|
+
presentation_digest: null,
|
|
165
|
+
nonce: 'seed-kat-nonce-1',
|
|
166
|
+
version: '1',
|
|
167
|
+
seed: '',
|
|
168
|
+
};
|
|
169
|
+
seedKatNull.seed = deriveSeed(seedKatNull.content_digest, null, seedKatNull.nonce, seedKatNull.version);
|
|
170
|
+
const seed_kats = [seedKatPresent, seedKatNull];
|
|
171
|
+
// ── sampler_cases and record_case ────────────────────────────────
|
|
172
|
+
// The record fixture: sampler case 1 uses the record's DERIVED seed so
|
|
173
|
+
// that the recorded spans, the record's span_commitments, and
|
|
174
|
+
// verifyAgainstSource all agree over the same source text.
|
|
175
|
+
const RECORD_PRIVATE_KEY = sha256hex('read-fidelity fixture key v1');
|
|
176
|
+
const RECORD_CONTENT_DIGEST = `sha256:${sha256hex('read-fidelity fixture content v1')}`;
|
|
177
|
+
const RECORD_NONCE = 'fixture-nonce-1';
|
|
178
|
+
const RECORD_VERSION = '1';
|
|
179
|
+
const RECORD_SEED = deriveSeed(RECORD_CONTENT_DIGEST, null, RECORD_NONCE, RECORD_VERSION);
|
|
180
|
+
const CASE1_SOURCE = 'The quick brown fox jumps over the lazy dog while the verifier ' +
|
|
181
|
+
'samples spans for readback and checks every one of them.';
|
|
182
|
+
const CASE1_N = 5;
|
|
183
|
+
const CASE1_SPAN_LEN = 12;
|
|
184
|
+
const CASE2_SOURCE = 'Приймач 🔐 читає текст 🙂 без спотворень і повертає його точно 🌍 назад.';
|
|
185
|
+
const CASE2_N = 4;
|
|
186
|
+
const CASE2_SPAN_LEN = 7;
|
|
187
|
+
const CASE3_SOURCE = 'Readback sampling draws n spans at seed-determined positions and ' +
|
|
188
|
+
'compares exact strings. The record carries only commitments, never ' +
|
|
189
|
+
'the raw span texts, so a verifier with the source can recompute ' +
|
|
190
|
+
'everything while the record alone reveals little.';
|
|
191
|
+
const CASE3_N = 8;
|
|
192
|
+
const CASE3_SPAN_LEN = 15;
|
|
193
|
+
const samplerCaseDefs = [
|
|
194
|
+
{ name: 'case1_ascii', source: CASE1_SOURCE, seed: RECORD_SEED, n: CASE1_N, span_len: CASE1_SPAN_LEN },
|
|
195
|
+
{ name: 'case2_emoji_cyrillic', source: CASE2_SOURCE, seed: sha256hex('rf-sampler-seed:2'), n: CASE2_N, span_len: CASE2_SPAN_LEN },
|
|
196
|
+
{ name: 'case3_longer', source: CASE3_SOURCE, seed: sha256hex('rf-sampler-seed:3'), n: CASE3_N, span_len: CASE3_SPAN_LEN },
|
|
197
|
+
];
|
|
198
|
+
const sampler_cases = samplerCaseDefs.map((def) => ({
|
|
199
|
+
...def,
|
|
200
|
+
spans: sampleSpans(def.source, def.seed, def.n, def.span_len),
|
|
201
|
+
}));
|
|
202
|
+
const case1Spans = sampler_cases[0].spans;
|
|
203
|
+
const case1Texts = case1Spans.map((s) => s.text);
|
|
204
|
+
const responses = case1Texts;
|
|
205
|
+
const record = createReadFidelityReceipt({
|
|
206
|
+
content_digest: RECORD_CONTENT_DIGEST,
|
|
207
|
+
presentation_digest: null,
|
|
208
|
+
challenge: {
|
|
209
|
+
nonce: RECORD_NONCE,
|
|
210
|
+
seed: RECORD_SEED,
|
|
211
|
+
algorithm: 'span_sample_v1',
|
|
212
|
+
version: RECORD_VERSION,
|
|
213
|
+
span_len: CASE1_SPAN_LEN,
|
|
214
|
+
span_commitments: commitSpans(case1Texts),
|
|
215
|
+
},
|
|
216
|
+
response_digest: `sha256:${sha256hex(canonicalizeJCS(responses))}`,
|
|
217
|
+
k: CASE1_N,
|
|
218
|
+
n: CASE1_N,
|
|
219
|
+
scoring_method: 'exact_match_v1',
|
|
220
|
+
model_claim: 'example-model-v1',
|
|
221
|
+
runtime_claim: 'example-runtime-v1',
|
|
222
|
+
verification_method: 'asserted',
|
|
223
|
+
challenge_issued_at: '2026-07-04T00:00:00Z',
|
|
224
|
+
response_observed_at: '2026-07-04T00:00:00Z',
|
|
225
|
+
receipt_issued_at: '2026-07-04T00:00:00Z',
|
|
226
|
+
}, RECORD_PRIVATE_KEY);
|
|
227
|
+
// Self-checks before writing: the fixture must pass its own library.
|
|
228
|
+
assert.equal(record.attester, publicKeyFromPrivate(RECORD_PRIVATE_KEY));
|
|
229
|
+
assert.deepEqual(verifyReadFidelityReceipt(record), { valid: true });
|
|
230
|
+
const against = verifyAgainstSource(record, CASE1_SOURCE);
|
|
231
|
+
assert.equal(against.valid, true);
|
|
232
|
+
assert.equal(against.signature_valid, true);
|
|
233
|
+
assert.equal(against.seed_valid, true);
|
|
234
|
+
assert.deepEqual(against.commitment_matches, case1Spans.map(() => true));
|
|
235
|
+
// Ed25519 is deterministic: re-signing the body yields the identical sig.
|
|
236
|
+
assert.equal(sign(canonicalNoSig(record), RECORD_PRIVATE_KEY), record.sig);
|
|
237
|
+
const canonical_no_sig = canonicalNoSig(record);
|
|
238
|
+
assert.equal(edVerify(canonical_no_sig, record.sig, record.attester), true);
|
|
239
|
+
// The record's source text and responses are not duplicated here: the
|
|
240
|
+
// source is sampler_cases[0].source and the responses are its span
|
|
241
|
+
// texts (k = n by construction).
|
|
242
|
+
const record_case = {
|
|
243
|
+
private_key_hex: RECORD_PRIVATE_KEY,
|
|
244
|
+
record,
|
|
245
|
+
canonical_no_sig_sha256: sha256hex(canonical_no_sig),
|
|
246
|
+
signature_valid: true,
|
|
247
|
+
};
|
|
248
|
+
// ── write ────────────────────────────────────────────────────────
|
|
249
|
+
const vectors = {
|
|
250
|
+
lexicon_id: LEXICON_ID,
|
|
251
|
+
lexicon_profile: LEXICON_PROFILE,
|
|
252
|
+
jcs_kats,
|
|
253
|
+
word_handle_cases,
|
|
254
|
+
seed_kats,
|
|
255
|
+
sampler_cases,
|
|
256
|
+
record_case,
|
|
257
|
+
};
|
|
258
|
+
const outPath = fileURLToPath(new URL('./read-fidelity-receipt-v0.1-vectors.json', import.meta.url));
|
|
259
|
+
writeFileSync(outPath, `${JSON.stringify(vectors, null, 2)}\n`, 'utf8');
|
|
260
|
+
console.log(`wrote ${outPath}`);
|
|
261
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../../../src/v2/read_fidelity_receipt/vectors/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"word_handles.test.d.ts","sourceRoot":"","sources":["../../../../../src/v2/word_handles/__tests__/word_handles.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
|
|
2
|
+
// ══════════════════════════════════════════════════════════════════
|
|
3
|
+
// word_handles codec (v2): tests
|
|
4
|
+
// ══════════════════════════════════════════════════════════════════
|
|
5
|
+
// Deterministic throughout: every derived digest, substituted
|
|
6
|
+
// position, replacement word, and transposition site comes from
|
|
7
|
+
// sha256 over a fixed label plus a counter. No wall clock, no
|
|
8
|
+
// randomness.
|
|
9
|
+
// ══════════════════════════════════════════════════════════════════
|
|
10
|
+
import { describe, it } from 'node:test';
|
|
11
|
+
import assert from 'node:assert/strict';
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
import { encode, decode, encodeProfile, decodeProfile, minUniquePrefixBits, PROFILES, LEXICON_PROFILE, LEXICON_ID, LEXICON_NAME, WORDS, canonicalWordlistText, } from '../index.js';
|
|
15
|
+
function sha256Hex(s) {
|
|
16
|
+
return createHash('sha256').update(s, 'utf8').digest('hex');
|
|
17
|
+
}
|
|
18
|
+
/** First 4 bytes of sha256(label) as a big-endian uint32 seed. */
|
|
19
|
+
function seedUint32(label) {
|
|
20
|
+
return createHash('sha256').update(label, 'utf8').digest().readUInt32BE(0);
|
|
21
|
+
}
|
|
22
|
+
const WORD_TO_INDEX = (() => {
|
|
23
|
+
const m = new Map();
|
|
24
|
+
for (let i = 0; i < WORDS.length; i++)
|
|
25
|
+
m.set(WORDS[i], i);
|
|
26
|
+
return m;
|
|
27
|
+
})();
|
|
28
|
+
/** Expected prefixHex: first ceil(prefixBits/8) bytes, pad bits zeroed. */
|
|
29
|
+
function expectedPrefixHex(digestHex, prefixBits) {
|
|
30
|
+
const byteLen = Math.ceil(prefixBits / 8);
|
|
31
|
+
const bytes = Buffer.from(digestHex.slice(0, byteLen * 2), 'hex');
|
|
32
|
+
const rem = prefixBits % 8;
|
|
33
|
+
if (rem !== 0) {
|
|
34
|
+
bytes[byteLen - 1] &= (0xff << (8 - rem)) & 0xff;
|
|
35
|
+
}
|
|
36
|
+
return bytes.toString('hex');
|
|
37
|
+
}
|
|
38
|
+
const PROFILE_NAMES = [
|
|
39
|
+
'compact',
|
|
40
|
+
'default',
|
|
41
|
+
'high_assurance',
|
|
42
|
+
];
|
|
43
|
+
describe('lexicon integrity', () => {
|
|
44
|
+
it('has exactly 2048 words', () => {
|
|
45
|
+
assert.equal(WORDS.length, 2048);
|
|
46
|
+
});
|
|
47
|
+
it('is strictly sorted ascending (implies unique words)', () => {
|
|
48
|
+
for (let i = 1; i < WORDS.length; i++) {
|
|
49
|
+
assert.ok(WORDS[i - 1] < WORDS[i], `words not strictly sorted at index ${i}: ${WORDS[i - 1]} vs ${WORDS[i]}`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
it('has unique 4-character prefixes', () => {
|
|
53
|
+
const prefixes = new Set(WORDS.map((w) => w.slice(0, 4)));
|
|
54
|
+
assert.equal(prefixes.size, WORDS.length);
|
|
55
|
+
});
|
|
56
|
+
it('canonical wordlist text hashes to LEXICON_ID', () => {
|
|
57
|
+
assert.equal(LEXICON_NAME, 'aps-handle-en-v1');
|
|
58
|
+
const hex = createHash('sha256')
|
|
59
|
+
.update(canonicalWordlistText(), 'utf8')
|
|
60
|
+
.digest('hex');
|
|
61
|
+
assert.equal(`sha256:${hex}`, LEXICON_ID);
|
|
62
|
+
});
|
|
63
|
+
it('has zero overlap with the bip39_english.txt disjointness input', () => {
|
|
64
|
+
// lexicon-source/bip39_english.txt is a pinned DISJOINTNESS input to
|
|
65
|
+
// the lexicon generator; no lexicon word may appear in it.
|
|
66
|
+
const raw = readFileSync(new URL('../lexicon-source/bip39_english.txt', import.meta.url), 'utf8');
|
|
67
|
+
const banned = new Set(raw
|
|
68
|
+
.split('\n')
|
|
69
|
+
.map((w) => w.trim())
|
|
70
|
+
.filter((w) => w.length > 0));
|
|
71
|
+
assert.equal(banned.size, 2048);
|
|
72
|
+
const overlap = WORDS.filter((w) => banned.has(w));
|
|
73
|
+
assert.deepEqual(overlap, []);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('round-trip across all three profiles', () => {
|
|
77
|
+
it('round-trips 200 derived digests per profile', () => {
|
|
78
|
+
for (let t = 0; t < 200; t++) {
|
|
79
|
+
const digest = sha256Hex(`wh-roundtrip:${t}`);
|
|
80
|
+
for (const name of PROFILE_NAMES) {
|
|
81
|
+
const p = PROFILES[name];
|
|
82
|
+
const words = encodeProfile(digest, name);
|
|
83
|
+
assert.equal(words.length, p.dataWords + p.checksumWords);
|
|
84
|
+
const res = decodeProfile(words, name);
|
|
85
|
+
assert.equal(res.checksumOk, true);
|
|
86
|
+
assert.equal(res.prefixBits, p.prefixBits);
|
|
87
|
+
assert.equal(res.prefixHex, expectedPrefixHex(digest, p.prefixBits));
|
|
88
|
+
assert.equal(res.failedWordIndex, null);
|
|
89
|
+
assert.deepEqual(res.outOfLexicon, []);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
it('accepts a sha256: prefix and raw bytes as equivalent inputs', () => {
|
|
94
|
+
const digest = sha256Hex('wh-input-forms:0');
|
|
95
|
+
const fromHex = encode(digest);
|
|
96
|
+
const fromPrefixed = encode(`sha256:${digest}`);
|
|
97
|
+
const fromBytes = encode(new Uint8Array(Buffer.from(digest, 'hex')));
|
|
98
|
+
assert.deepEqual(fromPrefixed, fromHex);
|
|
99
|
+
assert.deepEqual(fromBytes, fromHex);
|
|
100
|
+
});
|
|
101
|
+
it('exposes the profile table and lexicon profile id', () => {
|
|
102
|
+
assert.equal(LEXICON_PROFILE, 'single-list-v1');
|
|
103
|
+
assert.deepEqual(PROFILES.compact, {
|
|
104
|
+
name: 'compact',
|
|
105
|
+
dataWords: 4,
|
|
106
|
+
checksumWords: 1,
|
|
107
|
+
prefixBits: 44,
|
|
108
|
+
});
|
|
109
|
+
assert.deepEqual(PROFILES.default, {
|
|
110
|
+
name: 'default',
|
|
111
|
+
dataWords: 6,
|
|
112
|
+
checksumWords: 1,
|
|
113
|
+
prefixBits: 66,
|
|
114
|
+
});
|
|
115
|
+
assert.deepEqual(PROFILES.high_assurance, {
|
|
116
|
+
name: 'high_assurance',
|
|
117
|
+
dataWords: 8,
|
|
118
|
+
checksumWords: 2,
|
|
119
|
+
prefixBits: 88,
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
describe('substitution detection (default profile, 600 trials)', () => {
|
|
124
|
+
it('detects >= 597 of 600 single data word substitutions', () => {
|
|
125
|
+
const dataWords = PROFILES.default.dataWords;
|
|
126
|
+
let detected = 0;
|
|
127
|
+
for (let t = 0; t < 600; t++) {
|
|
128
|
+
const digest = sha256Hex(`wh-substitute:${t}`);
|
|
129
|
+
const words = encode(digest);
|
|
130
|
+
const pos = seedUint32(`wh-substitute-pos:${t}`) % dataWords;
|
|
131
|
+
const origIdx = WORD_TO_INDEX.get(words[pos]);
|
|
132
|
+
const delta = 1 + (seedUint32(`wh-substitute-word:${t}`) % (WORDS.length - 1));
|
|
133
|
+
const mutated = words.slice();
|
|
134
|
+
mutated[pos] = WORDS[(origIdx + delta) % WORDS.length];
|
|
135
|
+
const res = decode(mutated);
|
|
136
|
+
if (!res.checksumOk)
|
|
137
|
+
detected++;
|
|
138
|
+
if (res.failedWordIndex !== null) {
|
|
139
|
+
assert.equal(res.failedWordIndex, pos, `trial ${t}: localized ${res.failedWordIndex}, substituted ${pos}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
assert.ok(detected >= 597, `detected ${detected} of 600, expected >= 597`);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe('transposition detection (default profile, 300 trials)', () => {
|
|
146
|
+
it('detects >= 297 of 300 adjacent differing data word swaps', () => {
|
|
147
|
+
const dataWords = PROFILES.default.dataWords;
|
|
148
|
+
let detected = 0;
|
|
149
|
+
for (let t = 0; t < 300; t++) {
|
|
150
|
+
const digest = sha256Hex(`wh-transpose:${t}`);
|
|
151
|
+
const words = encode(digest);
|
|
152
|
+
const start = seedUint32(`wh-transpose-pos:${t}`) % (dataWords - 1);
|
|
153
|
+
let pair = -1;
|
|
154
|
+
for (let k = 0; k < dataWords - 1; k++) {
|
|
155
|
+
const i = (start + k) % (dataWords - 1);
|
|
156
|
+
if (words[i] !== words[i + 1]) {
|
|
157
|
+
pair = i;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
assert.ok(pair >= 0, `trial ${t}: no adjacent differing data words`);
|
|
162
|
+
const mutated = words.slice();
|
|
163
|
+
const tmp = mutated[pair];
|
|
164
|
+
mutated[pair] = mutated[pair + 1];
|
|
165
|
+
mutated[pair + 1] = tmp;
|
|
166
|
+
const res = decode(mutated);
|
|
167
|
+
if (!res.checksumOk)
|
|
168
|
+
detected++;
|
|
169
|
+
}
|
|
170
|
+
assert.ok(detected >= 297, `detected ${detected} of 300, expected >= 297`);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
describe('high_assurance profile (2 checksum words)', () => {
|
|
174
|
+
it('round-trips and reports both checksum words through decode', () => {
|
|
175
|
+
const digest = sha256Hex('wh-high-assurance:0');
|
|
176
|
+
const words = encodeProfile(digest, 'high_assurance');
|
|
177
|
+
assert.equal(words.length, 10);
|
|
178
|
+
const res = decode(words, 2);
|
|
179
|
+
assert.equal(res.checksumOk, true);
|
|
180
|
+
assert.equal(res.prefixBits, 88);
|
|
181
|
+
assert.equal(res.prefixHex, expectedPrefixHex(digest, 88));
|
|
182
|
+
});
|
|
183
|
+
it('detects data word substitutions and localizes them precisely', () => {
|
|
184
|
+
const dataWords = PROFILES.high_assurance.dataWords;
|
|
185
|
+
let localized = 0;
|
|
186
|
+
for (let t = 0; t < 40; t++) {
|
|
187
|
+
const digest = sha256Hex(`wh-ha-substitute:${t}`);
|
|
188
|
+
const words = encodeProfile(digest, 'high_assurance');
|
|
189
|
+
const pos = seedUint32(`wh-ha-substitute-pos:${t}`) % dataWords;
|
|
190
|
+
const origIdx = WORD_TO_INDEX.get(words[pos]);
|
|
191
|
+
const delta = 1 + (seedUint32(`wh-ha-substitute-word:${t}`) % (WORDS.length - 1));
|
|
192
|
+
const mutated = words.slice();
|
|
193
|
+
mutated[pos] = WORDS[(origIdx + delta) % WORDS.length];
|
|
194
|
+
const res = decodeProfile(mutated, 'high_assurance');
|
|
195
|
+
assert.equal(res.checksumOk, false, `trial ${t}: substitution not detected`);
|
|
196
|
+
if (res.failedWordIndex !== null) {
|
|
197
|
+
assert.equal(res.failedWordIndex, pos);
|
|
198
|
+
localized++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// With two checksum words a wrong position is coincidentally fixable
|
|
202
|
+
// with probability about 2047 * 2^-22, so localization is almost
|
|
203
|
+
// always unique.
|
|
204
|
+
assert.ok(localized >= 35, `localized ${localized} of 40, expected >= 35`);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('out-of-lexicon words', () => {
|
|
208
|
+
it('reports indices and suppresses decoding', () => {
|
|
209
|
+
const digest = sha256Hex('wh-out-of-lexicon:0');
|
|
210
|
+
const words = encode(digest);
|
|
211
|
+
const mutated = words.slice();
|
|
212
|
+
mutated[2] = 'notaword';
|
|
213
|
+
mutated[4] = 'Abacus'; // case-sensitive: 'abacus' is in the lexicon, this is not
|
|
214
|
+
const res = decode(mutated);
|
|
215
|
+
assert.deepEqual(res.outOfLexicon, [2, 4]);
|
|
216
|
+
assert.equal(res.prefixHex, null);
|
|
217
|
+
assert.equal(res.prefixBits, null);
|
|
218
|
+
assert.equal(res.checksumOk, false);
|
|
219
|
+
assert.equal(res.failedWordIndex, null);
|
|
220
|
+
});
|
|
221
|
+
it('does not trim whitespace (exact code-unit equality)', () => {
|
|
222
|
+
const digest = sha256Hex('wh-out-of-lexicon:1');
|
|
223
|
+
const words = encode(digest);
|
|
224
|
+
const mutated = words.slice();
|
|
225
|
+
mutated[0] = `${mutated[0]} `;
|
|
226
|
+
const res = decode(mutated);
|
|
227
|
+
assert.deepEqual(res.outOfLexicon, [0]);
|
|
228
|
+
assert.equal(res.checksumOk, false);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
describe('prefixHex padding semantics', () => {
|
|
232
|
+
it('66-bit decode returns 18 hex chars with pad bits zero and prefixBits 66', () => {
|
|
233
|
+
const allOnes = 'ff'.repeat(32);
|
|
234
|
+
const res = decode(encode(allOnes, 66, 1));
|
|
235
|
+
assert.equal(res.prefixBits, 66);
|
|
236
|
+
assert.equal(res.prefixHex?.length, 18);
|
|
237
|
+
// 66 data bits into 9 bytes: the final byte keeps its top 2 bits and
|
|
238
|
+
// pads the low 6 with zeros, so an all-ones input ends in "c0".
|
|
239
|
+
assert.equal(res.prefixHex, 'ffffffffffffffffc0');
|
|
240
|
+
});
|
|
241
|
+
it('44-bit and 88-bit cases pad to the byte boundary', () => {
|
|
242
|
+
const allOnes = 'ff'.repeat(32);
|
|
243
|
+
const compact = decode(encode(allOnes, 44, 1));
|
|
244
|
+
assert.equal(compact.prefixBits, 44);
|
|
245
|
+
assert.equal(compact.prefixHex, 'fffffffffff0');
|
|
246
|
+
const high = decode(encode(allOnes, 88, 2), 2);
|
|
247
|
+
assert.equal(high.prefixBits, 88);
|
|
248
|
+
assert.equal(high.prefixHex, 'ff'.repeat(11));
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
describe('minUniquePrefixBits', () => {
|
|
252
|
+
it('returns 44 when 44-bit prefixes are already pairwise distinct', () => {
|
|
253
|
+
const digests = [0, 1, 2, 3].map((i) => sha256Hex(`wh-unique:${i}`));
|
|
254
|
+
assert.equal(minUniquePrefixBits(digests), 44);
|
|
255
|
+
});
|
|
256
|
+
it('moves to the next multiple of 11 that separates shared 44-bit prefixes', () => {
|
|
257
|
+
// Shared first 48 bits (12 hex chars), differing at bit 48: 44-bit
|
|
258
|
+
// prefixes collide, 55-bit prefixes differ.
|
|
259
|
+
const shared = '0123456789ab';
|
|
260
|
+
const digests = [`${shared}0${'0'.repeat(19)}`, `${shared}f${'0'.repeat(19)}`];
|
|
261
|
+
assert.equal(minUniquePrefixBits(digests), 55);
|
|
262
|
+
// Shared first 56 bits (14 hex chars), differing at bit 56: 55-bit
|
|
263
|
+
// prefixes collide too, 66-bit prefixes differ.
|
|
264
|
+
const shared56 = '0123456789abcd';
|
|
265
|
+
const digests56 = [`${shared56}0${'0'.repeat(17)}`, `${shared56}f${'0'.repeat(17)}`];
|
|
266
|
+
assert.equal(minUniquePrefixBits(digests56), 66);
|
|
267
|
+
});
|
|
268
|
+
it('rounds startBits up to a multiple of 11', () => {
|
|
269
|
+
const digests = [0, 1].map((i) => sha256Hex(`wh-startbits:${i}`));
|
|
270
|
+
assert.equal(minUniquePrefixBits(digests, 50), 55);
|
|
271
|
+
});
|
|
272
|
+
it('strips sha256: prefixes and returns 44 for a single digest', () => {
|
|
273
|
+
const digest = sha256Hex('wh-single:0');
|
|
274
|
+
assert.equal(minUniquePrefixBits([`sha256:${digest}`]), 44);
|
|
275
|
+
});
|
|
276
|
+
it('throws on duplicates and on empty input', () => {
|
|
277
|
+
const digest = sha256Hex('wh-duplicate:0');
|
|
278
|
+
assert.throws(() => minUniquePrefixBits([`sha256:${digest}`, digest]), /duplicate digests/);
|
|
279
|
+
assert.throws(() => minUniquePrefixBits([]), /at least one digest/);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
describe('encode and decode validation errors', () => {
|
|
283
|
+
const digest = sha256Hex('wh-validate:0');
|
|
284
|
+
it('rejects prefixBits that is not a positive multiple of 11', () => {
|
|
285
|
+
assert.throws(() => encode(digest, 12), /positive multiple of 11/);
|
|
286
|
+
assert.throws(() => encode(digest, 0), /positive multiple of 11/);
|
|
287
|
+
assert.throws(() => encode(digest, -11), /positive multiple of 11/);
|
|
288
|
+
assert.throws(() => encode(digest, 11.5), /positive multiple of 11/);
|
|
289
|
+
});
|
|
290
|
+
it('rejects input shorter than prefixBits bits', () => {
|
|
291
|
+
assert.throws(() => encode('abcd', 66), /supplies 16 bits/);
|
|
292
|
+
assert.throws(() => encode(new Uint8Array(8), 88, 2), /supplies 64 bits/);
|
|
293
|
+
});
|
|
294
|
+
it('rejects checksumWords outside {1, 2}', () => {
|
|
295
|
+
assert.throws(() => encode(digest, 66, 0), /checksumWords must be 1 or 2/);
|
|
296
|
+
assert.throws(() => encode(digest, 66, 3), /checksumWords must be 1 or 2/);
|
|
297
|
+
assert.throws(() => encode(digest, 66, 1.5), /checksumWords must be 1 or 2/);
|
|
298
|
+
assert.throws(() => decode(encode(digest), 3), /checksumWords must be 1 or 2/);
|
|
299
|
+
});
|
|
300
|
+
it('rejects non-hex string input', () => {
|
|
301
|
+
assert.throws(() => encode('xyz'), /non-hex characters/);
|
|
302
|
+
});
|
|
303
|
+
it('rejects word counts that leave no data words', () => {
|
|
304
|
+
assert.throws(() => decode([WORDS[0]], 1), /leaves no data words/);
|
|
305
|
+
assert.throws(() => decode([WORDS[0], WORDS[1]], 2), /leaves no data words/);
|
|
306
|
+
});
|
|
307
|
+
it('rejects profile word count mismatches', () => {
|
|
308
|
+
const words = encodeProfile(digest, 'default');
|
|
309
|
+
assert.throws(() => decodeProfile(words.slice(0, 5), 'default'), /expects 7 words, got 5/);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
//# sourceMappingURL=word_handles.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"word_handles.test.js","sourceRoot":"","sources":["../../../../../src/v2/word_handles/__tests__/word_handles.test.ts"],"names":[],"mappings":"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"}
|