Skip to content

fix: reject word lists without exactly 2048 words instead of emitting broken checksums - #21

Open
Kshot3000 wants to merge 1 commit into
Quantus-Network:mainfrom
Kshot3000:fix/wordlist-length-validation
Open

Kshot3000 wants to merge 1 commit into
Quantus-Network:mainfrom
Kshot3000:fix/wordlist-length-validation

Conversation

@Kshot3000

Copy link
Copy Markdown

Problem

addressToChecksum (JS) and address_to_checksum (Rust) map 11-bit indices (0–2047) into whatever word list the caller passes, without checking its length:

  • JS: a truncated list silently produces a broken checkphrase. With the bundled list cut to 100 words, the Satoshi test address returns ["ahead", undefined, undefined, undefined, undefined] — an anti-poisoning checksum with four of its five words missing, and no error anywhere. An empty list returns five undefineds.
  • Rust: the same input panics with index out of bounds: the len is 100 but the index is 1016, which never tells the caller what the actual contract is.

The Dart implementation already validates the length in its constructor (ArgumentError: Word list must contain exactly 2048 words), and both the Rust and JS loaders validate the lists they ship — but the checksum functions themselves accept any list, so any caller passing a custom or accidentally truncated list hits this.

Fix

  • JS: addressToChecksum throws Word list must contain exactly 2048 words, found N before deriving.
  • Rust: address_to_checksum asserts the same condition with the same wording load_word_list already uses for its error, so the panic names the contract instead of an index.

No behavior change for valid 2048-word lists.

Tests

  • Jest: new case covering 100-word, empty, and 2049-word lists (all reject with the message above). Full suite 6/6, including all 1,171 cross-platform vectors.
  • Rust: two should_panic cases pinning the message for short and empty lists. cargo test --release 6/6; cargo fmt --check clean.

Reproduced before the fix with a scratch spec (JS result above) and a scratch Rust test (panic message above); both now fail loudly and clearly instead.


Submitted by @Kshot9000 (https://x.com/kshot9000) — independent builder in the Quantus ecosystem. Tips welcome in QTC: qznY8nwuvWcCCVys4da1oQdysyh8YUZYRjRqgk3S8Wos8kbau

cc @Quantus-Network

… broken checksums

addressToChecksum/address_to_checksum map 11-bit indices (0-2047) into
the caller's word list without checking its length. In JS a truncated
list silently produced checksums with undefined words (a 100-word list
yields ["ahead", undefined, undefined, undefined, undefined] for the
Satoshi test address) — a silently broken anti-poisoning checkphrase.
In Rust the same input died with an opaque 'index out of bounds' panic
that never names the actual contract.

The Dart implementation already validates the length in its constructor,
and both loaders validate it too; the checksum functions themselves did
not. JS now throws 'Word list must contain exactly 2048 words, found N'
and the Rust function asserts with the same message its loader uses.

Tests: new jest case (100-word, empty, and 2049-word lists all reject;
all 1171 vectors still pass) and two Rust should_panic cases pinning the
message. cargo test --release 6/6, jest 6/6, cargo fmt --check clean.
@n13 n13 added bug Something isn't working bot-review labels Oct 9, 2026

@n13 n13 left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer model: GPT-6.1 Sol

APPROVE — no blocking findings at 0a6191fbe609a23bdcae08d01dc6bc85d7cc9abc.

The Rust and JavaScript guards enforce the existing 2048-word contract before PBKDF2 runs. Valid-list derivation and public signatures are unchanged. The regression tests verify the explicit failure messages; both implementations still pass all 1,171 shared checksum vectors.

Validation passed in a detached worktree:

  • cargo test --locked --release — 6 tests passed, including the shared vectors.
  • npm ci --no-audit --no-fund — dependency installation and TypeScript build passed.
  • npm test -- --runInBand — 6 tests passed, including the shared vectors and short/empty/oversized list rejection.
  • cargo clippy --locked --all-targets --all-features -- -D warnings.
  • cargo +nightly fmt --all -- --check.
  • python3 scripts/sync_wordlists.py --check and git diff --check.

The latest GitHub CI snapshot has passing format, wordlist sync, JavaScript, Linux Rust, and Clippy/doc checks; macOS/Windows Rust and Dart jobs are still running.

@n13 n13 removed the bot-review label Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants