recovery-words.integrity.test.ts

2.6 KB · raw

1// T-001: BIP39 word-list integrity2//3// Why this test exists: a single transcription error in recovery-word-list.ts4// silently shifts every subsequent word index and renders affected recovery5// phrases permanently unrestorable. There is no runtime error — the wrong word6// just maps to a valid (but different) root-key prefix, which decrypts a7// completely different vault. No test previously guarded this.89import { describe, expect, it } from "vitest";10import { WORD_LIST } from "../../shared/recovery-word-list";1112describe("T-001: BIP39 word-list integrity", () => {13  // T-001a: exact length is load-bearing — 2048 words = 2^11 indices per word,14  // which is what the BIP39 bit-packing arithmetic in recovery-words.ts assumes.15  it("T-001a: list has exactly 2048 words", () => {16    expect(WORD_LIST).toHaveLength(2048);17  });1819  // T-001b: a duplicate word at position N collapses two indices to one,20  // making N different root keys produce identical phrases (silent collision).21  it("T-001b: no duplicates — Set.size === 2048", () => {22    expect(new Set(WORD_LIST).size).toBe(2048);23  });2425  // T-001c: BIP39 requires lowercase ASCII 3–8 letters. A word with an accent,26  // uppercase letter, or number breaks the WORD_LIST.indexOf() lookup in27  // wordsToRootKeyMaterial and returns -1 → "unknown word" error on restore.28  it("T-001c: every word is lowercase ASCII only (3–8 letters)", () => {29    const badWords: string[] = [];30    for (const word of WORD_LIST) {31      if (!/^[a-z]{3,8}$/.test(word)) {32        badWords.push(word);33      }34    }35    expect(badWords).toEqual([]);36  });3738  // T-001d: canonical BIP39 position anchors.39  // Verified against the official BIP39 English wordlist40  // (https://github.com/trezor/python-mnemonic/blob/master/src/mnemonic/wordlist/english.txt).41  // A transposition, insertion, or deletion anywhere in the list shifts all42  // subsequent positions and is caught by one of these four anchors.43  it("T-001d: canonical BIP39 spot-checks", () => {44    expect(WORD_LIST[0]).toBe("abandon");    // first word45    expect(WORD_LIST[1]).toBe("ability");    // second word46    expect(WORD_LIST[2047]).toBe("zoo");     // last word47    expect(WORD_LIST[585]).toBe("empty");    // mid-list anchor48  });4950  // T-001e: the list must be in strict alphabetical order.51  // An out-of-order word would pass T-001b (no duplicates) but would break52  // any tooling or human verification that cross-references by position.53  it("T-001e: list is in canonical alphabetical order", () => {54    const sorted = [...WORD_LIST].sort((a, b) => a.localeCompare(b));55    expect(WORD_LIST).toEqual(sorted);56  });57});