From a6bc29d5b3ffa38277ce584b5e9b38bf89c19753 Mon Sep 17 00:00:00 2001 From: thestreamcode Date: Sat, 1 Aug 2026 22:44:58 +0200 Subject: [PATCH 1/2] fix(generator): honor "exclude similar characters" for guaranteed characters The password generator filters look-alike characters out of the pool it draws from, but the one character it guarantees per selected set was picked from the unfiltered alphabet. A generated password could therefore still contain i, l, 1, L, o, 0 or O right after the user excluded them; a digits-only password could be returned containing 0 or 1. Filter every alphabet up front and build the draw pool from the filtered sets, so the guarantee and the pool cannot disagree. Empty alphabets are dropped rather than sampled, and the guaranteed prefix is capped at the requested length so the function can no longer return more characters than asked for (reachable only through direct API use: the in-app slider starts at 8). Also document the accepted cryptographic trade-offs in docs/security.md under a new "Known Limitations" section - shared AES/HMAC key, PBKDF2-SHA1 backup KDF, six-digit master PIN keyspace, no unlock throttling, opt-in screenshot protection, transition-based auto-lock, Android 13+ clipboard previews and the trusted OTA channel. No cryptographic behavior changed and no stored data format was touched. Remove the unimported Divider component and the unused JSX.IntrinsicElements augmentation, and correct the nativewind-env.d.ts header: the className augmentations serve react-native-web, not NativeWind, which is not a dependency. Verified with bun run verify: format, typecheck, lint, 195 tests across 29 suites, expo-doctor 20/20. Co-Authored-By: Claude Opus 5 (1M context) --- .gitignore | 4 +- AGENTS.md | 5 +- CHANGELOG.md | 43 ++++++++++++++ docs/security.md | 57 ++++++++++++++++++ nativewind-env.d.ts | 14 ++--- src/__tests__/services/CryptoService.test.ts | 62 ++++++++++++++++++++ src/components/ui/divider.tsx | 20 ------- src/services/crypto/cryptoService.ts | 45 ++++++++------ src/services/utils/clipboardService.ts | 6 +- 9 files changed, 204 insertions(+), 52 deletions(-) delete mode 100644 src/components/ui/divider.tsx diff --git a/.gitignore b/.gitignore index 87c9470..ee27eb2 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ test-passwords.json .claude/ .opencode/ .agents/ +.codex/ # Testing coverage/ @@ -69,6 +70,3 @@ build/ *.apk *.aab *.ipa - -# Claude Code local settings -.claude/settings.local.json diff --git a/AGENTS.md b/AGENTS.md index 6a93950..7c3716b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,6 +56,9 @@ - Local secrets live in `.secrets/` and must never be committed. - The project requires no local environment variables. Keep `.env.example` non-secret, never put credentials in `EXPO_PUBLIC_*`, and use EAS/GitHub secret stores for automation credentials. - Treat imported files and KDF metadata as untrusted. Preserve backup/file-size and KDF-cost bounds when changing validation or crypto code. +- Password generation must apply every enabled option to every character set. The set that guarantees one character per class is the easy place to reintroduce an excluded character; keep it filtered and keep `src/__tests__/services/CryptoService.test.ts` generator coverage green. +- Read `docs/security.md` "Known Limitations" before touching crypto. The shared AES/HMAC key and the PBKDF2 backup KDF are known, documented trade-offs, **not** bugs to fix in place: changing either silently makes every existing vault and every exported `KS1-PW1` backup undecryptable. Any change there needs a new format version plus a migration that runs in an authenticated session. +- Do not alter the KS1/KS1-PW1 payload layout, the storage keys in `storageService.ts`, or the default KDF parameters of an existing vault. Vaults carry their own KDF parameters, so new capabilities (a longer master password, for instance) can be added without breaking old data — prefer that route. - Persist encrypted storage mutations before updating decrypted caches. Reset only Keysoft-owned keys; do not use `AsyncStorage.clear()`. - Clear backup passwords/ciphertext from UI state and remove temporary export files after sharing. @@ -102,7 +105,7 @@ - `README.md` is the public project overview and setup guide. - `CHANGELOG.md` tracks notable release and unreleased changes. - `docs/architecture.md` documents system structure and data flow. -- `docs/security.md` documents the cryptographic and storage model. +- `docs/security.md` documents the cryptographic and storage model, plus the accepted trade-offs under "Known Limitations". - `docs/development.md` documents local workflow, coding standards, and verification. - `docs/release.md` documents release readiness, Android permissions, and security checks. - Update `README.md`, `CHANGELOG.md`, and the relevant `docs/` file for significant changes. diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d1ad69..00b929f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,49 @@ All notable project changes are documented here. ## [Unreleased] +### Fixed + +- The password generator now applies "exclude similar characters" to every selected + character set. The one character guaranteed per set was previously picked from the + unfiltered alphabet, so a generated password could still contain `i`, `l`, `1`, `L`, + `o`, `0`, or `O` after the user excluded them — a digits-only password, for example, + could still be handed back containing `0` or `1`. +- The password generator no longer returns more characters than requested when the + requested length is shorter than the number of selected character sets. The in-app + slider starts at 8, so this was only reachable through direct API use. + +### Removed + +- `src/components/ui/divider.tsx`. The component had no importers anywhere in the + application or the test suite, and its only styling was a Tailwind class left over from + a NativeWind setup the project no longer uses. +- The unused `JSX.IntrinsicElements` augmentation in `nativewind-env.d.ts`, which declared + a `style` intrinsic element as `any`. No JSX `