pwmanager 2.4: browser generator, enforced vault integrity, honest passphrase entropy - #8
Open
Sebby1770 wants to merge 1 commit into
Open
pwmanager 2.4: browser generator, enforced vault integrity, honest passphrase entropy#8Sebby1770 wants to merge 1 commit into
Sebby1770 wants to merge 1 commit into
Conversation
…nest passphrase entropy Adds a static web generator and fixes four defects in the CLI, three of which weakened guarantees the README already advertised. Web generator (web/, deployed to GitHub Pages) - Password and passphrase generation entirely client-side, no build step. - crypto.getRandomValues with rejection sampling — no modulo bias, and no Math.random fallback; it refuses to generate without a CSPRNG. - Shares the CLI's presets, symbol set, lookalike set, wordlist and entropy thresholds. tests/test_web_parity.py fails if either side drifts, including a check that every flag the site suggests actually exists in `gen`. - CSP allows exactly one outbound destination (the HIBP range API) and it is only contacted on an explicit click, sending five hex characters of a hash. - Nothing generated is stored: localStorage holds interface preferences only. Fixes - unlock() computed the vault HMAC and threw the result away, so a vault edited outside pwmanager opened silently. It now raises VaultIntegrityError after decryption succeeds, so a wrong password is still a wrong password. - The HMAC covered only salt+ciphertext, leaving the unencrypted version and kdf fields unauthenticated. It is now domain-separated and length-prefixed over all four; legacy vaults still open and upgrade on the next save. - Vault files were written through the process umask (usually 0644). They are now created 0600 including the temp file, and fsynced around the rename. - save() re-read the file for its salt without checking the read worked; it now refuses to overwrite a vault it cannot parse. - password_entropy_bits scored dictionary passphrases per character, claiming ~150 bits for a five-word phrase worth ~55. Recognised wordlist phrases now use words * log2(listSize); random passwords are unchanged. Also - `gen` gained --no-lower/--no-upper/--no-digits/--separator/--capitalize/--count. - `doctor` reports vault file permissions. - CI runs the Node generator tests and fails on a stale web/wordlist.js. 129 pytest tests and 35 Node tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the web generator and fixes four defects in the CLI — three of which weakened guarantees the README already advertised.
The website
A static generator at
web/, deployed to GitHub Pages by.github/workflows/pages.yml. Five files, no build step, no dependencies.strength_label.pwmanager gencommand for the current settings, so the site doubles as CLI documentation.Security properties, all tested:
crypto.getRandomValueswith rejection sampling; a distribution test would fail a biased implementationMath.randompath — without a CSPRNG it raiseslocalStorageholds interface preferences under one key; never a secretdefault-src 'none',connect-srclimited to the HIBP endpointgenerator.jsis shared verbatim withtests/js/run.mjs, so the browser code is covered by 35 tests rather than only by eyeballing.Fixes
The vault integrity check was a no-op.
unlock()computed the file HMAC and then discarded the result:A vault modified outside pwmanager opened without complaint. It now raises
VaultIntegrityError— but only after the ciphertext decrypts, so an ordinary wrong password is still reported as a wrong password rather than as tampering.The HMAC did not cover
versionorkdf. Both are stored unencrypted and Fernet does not authenticate them, so edits went undetected. The MAC is now domain-separated and length-prefixed over all four plain fields. Vaults carrying the older salt+ciphertext MAC still open and are upgraded on the next save.Vault files were written through the process umask, typically
0644— readable by every local account. They are now created0600from the start, temp file included, andfsynced before and after the atomic rename.doctorreports the mode of an existing vault;Vault.tighten_permissions()fixes one.save()could destroy a vault. It re-read the file for its salt without checking the read succeeded. It now refuses to overwrite a vault it cannot parse and explains why.Passphrase strength was overstated by roughly 3x.
password_entropy_bitsapplied a per-character model to dictionary passphrases, reporting ~150 bits for a five-word phrase actually worth ~55. Recognisable wordlist phrases are now scoredwords * log2(listSize); random passwords are unchanged. This is visible in the UI — a five-word passphrase reads "Reasonable, 55.0 bits", not "Very strong".Also
pwmanager gengained--no-lower,--no-upper,--no-digits,--separator,--capitalize,--count N. The site's CLI hints previously suggested flags that did not exist; a test now fails if it ever suggests one again.web/wordlist.jshas drifted frompwmanager/data/eff_short.txt.Verification
-rw-------; flipping itsversionfield makesverifyfail with the integrity error; restoring it makesverifypass again.Note
PR #3 (
feat/v2.0-sqlite-integrity-and-packaging, opened July) predates the 2.2/2.3 merges and would delete tests that now exist onmain. It is stale and worth closing rather than merging.🤖 Generated with Claude Code