aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)AuthorFilesLines
8 hoursdocs: add the Compose editor implementation plancomposeDanilo M.1-0/+3295
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 hoursrefactor(app): remove the legacy decrypt screenDanilo M.15-768/+6
The Compose entry view replaces DecryptActivity, its layouts, menu and field adapter, and upstream's unused ViewPasswordScreen. BasePGPActivity keeps what the editor and autofill still need. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 hoursfix(app): harden the entry passphrase promptDanilo M.3-9/+21
Ignore a submit while no prompt is showing, so OK plus IME Done cannot start two decrypts. Key the passphrase dialog on the retry count so each attempt gets a fresh field and shows the wrong password error. Note the process death gap around key import and the editor. Record the TotpModule, text selection and file IO decisions in the plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 hoursfeat(app): show the Compose entry view in MainActivityDanilo M.3-33/+301
The list navigates to EntryRoute and shortcuts start on it, replacing the legacy decrypt screen. FLAG_SECURE is on while an entry is composed and off on the list; the route is checked with isFileInRepo again before decrypting. Saving in the editor returns to the list. Password and one-time code copies start the clipboard clear timer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 hoursfeat(app): add the Compose entry screenDanilo M.4-3/+205
Header with avatar, name and folder; password, username and one-time code with a countdown bar in the first group; extra fields in the second. FieldRow can start revealed (the "Show the password" setting) and shows masked values in JetBrains Mono, as the old screen did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 hoursfeat(app): add the entry ViewModelDanilo M.2-1/+217
Ports the decrypt screen's unlock flow: passphrase cache after a biometric success, passphrase prompt with three tries, decryption off the main thread and the auto-close timer. The decrypted entry is held in memory only and dropped by the timer or when the destination is popped. A cached passphrase that fails is no longer retried in a loop. TotpModule is also installed in ViewModelComponent, so the entry factory's TotpFinder can be injected into the ViewModel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 hoursrefactor(app): make the PGP clipboard and .gpg-id helpers extensionsDanilo M.2-96/+96
The Compose entry view lives in MainActivity, which is not a BasePGPActivity. The helpers move unchanged to FragmentActivity extensions in the same file, so the existing subclasses keep calling them as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 hoursfeat(app): add the entry view model and the OTP periodDanilo M.5-3/+100
Totp now carries its period, so the entry view can draw how much of the code's lifetime is left. EntryFields groups a decrypted entry the way the spec lays it out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 hoursdocs: add the Compose entry view implementation planDanilo M.1-0/+2147
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 hoursfix(app): stop stray search focus and expose list semanticsDanilo M.1-6/+30
Out of touch mode the view system gives its default focus to the first Compose focusable, the search field, which kept it and brought the keyboard back on every return to the list. Each return now clears focus once the window has focus (earlier, the view still holds a stale touch mode and clearing refocuses the field). Search on start runs on the first of those returns only, tracked in a saveable flag, so popping back from a folder or rotating no longer refocuses it. The extended FAB label sits under clearAndSetSemantics in Material 3, so the icon now carries the "New entry" description. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursrefactor(app): remove the legacy PasswordStore listDanilo M.30-1570/+10
The Compose list replaces it. The autofill inline suggestion's attribution intent now opens LaunchActivity, behind the biometric gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursfix(app): keep list work alive across rotation and tidy search focusDanilo M.3-16/+18
- MainActivity handles orientation|screenSize itself so rotation no longer cancels store and git work running in lifecycleScope. - Search-on-start focus moved into SearchField (own FocusRequester), only at the store root, fixing "FocusRequester is not initialized". - refresh() leaves an open folder removed by a pull; used on resume and after git, move, rename and delete. - Zero-count delete uses deleteWithoutFollowingLinks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursfix(app): harden store delete, move and renameDanilo M.2-12/+57
- renameFolder stops when the move fails instead of recording and committing - delete runs its file work on the IO dispatcher - folder delete no longer follows directory symlinks (new test) - overwrite dialog is dismissed when its coroutine is cancelled Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursfix(app): compare whole path components in isInsideRepositoryDanilo M.1-2/+3
The check used a string contains on canonical paths, so a typed name such as ../store-evil/x resolved to a sibling directory whose path contains the store's and passed. New entries and folder renames could then land outside the store. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursfeat(app): show the Compose password list in MainActivityDanilo M.5-18/+467
Folders are back stack entries (ListRoute(path)), so back goes up one level. MainActivity extends BaseGitActivity for pull to sync and the commits after move, rename and delete. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursfeat(app): add the Compose password list screenDanilo M.3-0/+326
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursfeat(app): port the list's store operations out of PasswordStoreDanilo M.1-0/+159
A move now waits for the overwrite answer before it commits, and new folder names must stay inside the store. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursfeat(app): add the list rows and folder navigation modelDanilo M.3-0/+161
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursbuild(app): add hilt-navigation-compose and lifecycle-runtime-composeDanilo M.2-0/+4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 hoursdocs: add the Compose list implementation planDanilo M.1-0/+1901
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursfeat(app): route the launcher through MainActivityDanilo M.1-15/+5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursfix(app): make LegacyScreen a top-level composableDanilo M.1-10/+13
Slack's ComposeUnstableReceiver lint rejects an instance composable on the activity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursfeat(app): add the single-activity Compose hostDanilo M.2-0/+81
Its two routes hand off to the legacy list and decrypt activities until those screens are ported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursfix(ui-compose): tighten row accessibilityDanilo M.1-5/+14
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursfeat(ui-compose): add breadcrumb bar, grouped surface, entry and field rowsDanilo M.5-0/+339
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursfeat(ui-compose): add the breadcrumb modelDanilo M.3-0/+50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursbuild(app): add navigation-compose and kotlinx-serializationDanilo M.2-0/+6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursdocs: start the compose branch for the UI overhaulDanilo M.2-3/+7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursdocs: add the Compose foundation implementation planDanilo M.1-0/+885
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 hoursdocs: add the Compose UI overhaul designDanilo M.1-0/+133
Single-activity Compose rewrite with a Material 3 look and breadcrumb folder chips, done on a temporary compose branch and released only once complete. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 hourschore: rename the repository to HardenedPassDanilo M.6-10/+10
The GitHub repository is now danixland/HardenedPass and the main repository on the personal server is HardenedPass. Point the reusable workflow references, the issue template, README and AGENTS.md at the new name; GitHub redirects the old URLs, but workflow `uses:` should not rely on that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 hoursrelease: HardenedPass 0.1.4v0.1.4Danilo M.2-3/+6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 hoursfix(ui-compose): tint the copy icon with the platform attributeDanilo M.2-4/+3
ic_content_copy used AppCompat's ?attr/colorControlNormal, which ui:compose does not depend on, so building the module in release mode failed resource verification. ?android:attr/colorControlNormal is the same color under AppCompat themes and needs no dependency. A bare assembleRelease now passes; update AGENTS.md accordingly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 hoursfix(strings): name HardenedPass in autofill and biometric textsDanilo M.1-4/+4
The autofill setup dialog, the publisher-changed warning and the biometric setting still told users to look for "Password Store", which is not what the app is called in the autofill service list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 hoursfix(i18n): revise the Italian translationDanilo M.7-74/+126
The Italian strings were largely machine-translated: "commit" became "conferma" (confirm), pull and push became "trascina" and "spingi" or "premi" (drag, push a button), rebase became "ricollocare", the store became "negozio" (a shop), "pretend" became "pretendere" (a false friend), and one string showed "/0" instead of "/". Autofill had five different spellings. Rewrite those, keep Git terms in English (remote, branch, push, pull, rebase, merge, commit, detached HEAD, fast-forward), use "compilazione automatica" as Android's Italian settings do, make "repository" masculine throughout, replace the "Sei pregato di" calque with plain imperatives, name the app HardenedPass, and translate the 52 strings that fell back to English. Also drop the "TODO: Add a recovery method" that the merge-failure message showed users, in English and every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 hoursrefactor(autofill): remove the fill-OTP-from-SMS pathsDanilo M.14-58/+0
Filling OTPs from SMS used Play Services' SMS User Consent API, removed with the nonFree flavor; since then AutofillSmsActivity was a stub that never offered the dataset. Delete it, its manifest entry, the dataset builders in both response builders, its drawable and string. AutofillAction.FillOtpFromSms stays in autofill-parser: it is public, metalava-checked API and harmless for a client that never fills it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 hoursfix(editor): write a space after "username:"Danilo M.4-42/+3
Entries created in the app said "username:dave". pass users and other clients write "username: dave"; the parser already trims either form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 hoursbuild(deps): update sshj to 0.41.2 and drop eddsaDanilo M.7-16/+102
sshj 0.40 replaced the unmaintained net.i2p.crypto:eddsa 0.3.0 (last release 2019) with Bouncy Castle's Ed25519. 0.41.2 still targets Java 8. The app used eddsa directly for wrapped Ed25519 SSH keys. Generate the 32-byte seed with SecureRandom and build the key pair with sshj's Ed25519KeyFactory and Bouncy Castle's public key derivation. The wrapped format is still the raw RFC 8032 seed, so keys generated by 0.1.3 keep working; Ed25519KeyPairTest pins this with the RFC 8032 test vector. sshj 0.40 also changed setSecurityProvider(null) to reset the registerBouncyCastle flag, which undid the earlier setRegisterBouncyCastle(false): sshj then forced every operation through Bouncy Castle, which cannot sign with Android Keystore keys ("no encoding for EC private key"), and hardware-backed SSH keys failed to authenticate. Call the two in the opposite order; SshjConfigTest checks that sshj leaves provider selection to the JCA. Verified with the R8 release build on the emulator against the e2e server: a hardware ECDSA key, a newly generated wrapped Ed25519 key and an imported OpenSSH Ed25519 key all authenticated and synced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14 hoursrelease: HardenedPass 0.1.3v0.1.3Danilo M.2-3/+3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14 hoursfeat(crypto): replace androidx security-crypto with Keystore AES-GCMDanilo M.12-108/+376
security-crypto is deprecated in 1.1.0 and the app was stuck on 1.1.0-alpha06, since the stable release fails the warnings-as-errors build. It also pulled in Tink for what is a single AES-GCM operation. KeystorePreferences is a SharedPreferences whose values are AES-GCM sealed under an Android Keystore key (random IV, entry name as AAD so values cannot be swapped between entries); a value that fails to decrypt reads as absent. It backs the Git credentials (git_secrets), the proxy credentials (proxy_secrets) and the PGP passphrase cache, whose key needs the screen lock or a strong biometric within 60s and is recreated if a new biometric enrollment invalidates it. Wrapped Ed25519 SSH keys are sealed the same way into the private key file. Password generator settings are not secret and move to plain preferences. Nothing is migrated: removeSecurityCryptoData deletes the old encrypted preference files, Tink keysets, their Keystore master keys and a security-crypto wrapped Ed25519 key once on upgrade. Only 0.1.x installs from the last few hours have such data. Hardware SSH keys and imported keys are untouched. Tests: KeystorePreferencesTest (round trip, defaults, no plaintext, random IVs, AAD binding, tamper rejection, remove/clear) with a software AES key. Verified on the emulator by upgrading a 0.1.2 debug build that held a wrapped Ed25519 key, Tink keysets and a cached passphrase: the old data was deleted, a new Ed25519 key authenticated over SSH after a fingerprint unlock, and a passphrase cached on first decrypt was reused on the second with only the fingerprint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14 hoursdocs(agents): record Keystore storage and emulator gotchasDanilo M.1-3/+12
Note where secrets are stored now that security-crypto is gone, that the aps-test AVD's autofill service (upstream's debug app) grabs passphrase fields, and that worktree and main-checkout debug builds are signed with different keys. Correct the stale JGit entry: it is on 5.13, not pinned at 4.5.7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14 hoursrelease: HardenedPass 0.1.2v0.1.2Danilo M.2-2/+10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14 hoursfix(i18n): name HardenedPass in commit messagesDanilo M.7-15/+15
The add and edit commit messages said "using Android Password Store" in every locale. The Italian remove message translated "store" as "negozio" (a shop); use "archivio". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14 hoursfix(crypto): refuse to encrypt when a .gpg-id recipient key is missingDanilo M.5-8/+112
CryptoRepository.encrypt looked up every .gpg-id recipient and dropped the ones it could not find, so an entry saved on a device that lacked one recipient's key was encrypted to the others only. With a store shared between a phone key and a smartcard key, entries created on the phone could no longer be decrypted with the smartcard alone. Fail with the lookup error instead (KeyNotFoundException names the key), so the user imports the missing public key and saves again. PasswordCreationActivity also ignored the Result of encrypt and wrote the output buffer regardless: on any encryption failure it wrote an empty .gpg file, overwriting the entry when editing, and committed it. Show the error and leave the file untouched. CryptoRepositoryTest covers both the missing-recipient failure and the all-present success path, with keys generated by PGPainless (added as an app test dependency). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 hoursrelease: HardenedPass 0.1.1v0.1.1Danilo M.2-2/+9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 hoursfix(ssh): unlock screen-lock protected SSH keys with a fingerprintDanilo M.2-3/+17
A Keystore SSH key generated with "Protect with screen lock credential" was bound to AUTH_DEVICE_CREDENTIAL only, while the unlock prompt also accepted biometrics. A fingerprint passed the prompt, then signing the SSH auth request threw UserNotAuthenticatedException, sshj logged "User not authenticated" and the clone failed with "Socket closed". Bind new keys to AUTH_DEVICE_CREDENTIAL or AUTH_BIOMETRIC_STRONG, and allow only strong biometrics in the prompt on Android 11+, since weak ones never unlock Keystore keys (the androidx MasterKey keys already use credential or strong biometric). Android 9 and 10 cannot combine BIOMETRIC_STRONG with DEVICE_CREDENTIAL, so they keep the weak class. Existing protected keys still need the PIN until regenerated. Verified on the emulator (Class 3 fingerprint): a protected ECDSA key unlocked with a fingerprint authenticates and clones over SSH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 hoursrelease: HardenedPass 0.1.0v0.1.0Danilo M.2-3/+23
Set the version to 0.1.0 (versionCode 100, following the versioning plugin's major*10000 + minor*100 + patch scheme) and add a changelog section for the first release. Upstream's never-released [Unreleased] section is kept below it, renamed, since 0.1.0 ships that work too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
19 hoursci: sign and publish HardenedPass releases with the fork's own keyDanilo M.4-110/+33
Add the fork's release keystore and its signing properties, encrypted with openssl (aes-256-cbc, pbkdf2) as secrets/keystore.cipher and secrets/props.cipher; the key is the ENCRYPT_KEY repository secret. The plaintext files and the master copy never enter the repository. Rewrite deploy_github_releases.yml for the single FOSS build: on a vX.Y.Z tag it decrypts the signing secrets, builds the signed release APK, removes the secrets, and creates a draft GitHub Release with HardenedPass_<tag>.apk and its SHA-256 checksum using gh, replacing the archived actions/create-release and upload-release-asset actions. AGENTS.md records the certificate fingerprint and the procedure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
19 hoursfeat: rename the app to HardenedPass (xyz.danix.hardenedpass)Danilo M.5-9/+23
The fork gets its own application ID and name, so it installs next to upstream's app.passwordstore instead of replacing it, and could be submitted to F-Droid. Debug builds keep the .debug suffix and are labelled "HardenedPass Debug". The Kotlin package stays app.passwordstore: renaming it would be churn with no visible effect. "GrapheneOS" is kept out of the name because it is that project's trademark; "Hardened" nods to its vocabulary. README and AGENTS.md describe the fork's GrapheneOS goal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
19 hoursbuild: drop the nonFree flavor, Sentry and Play ServicesDanilo M.44-600/+23
The fork targets GrapheneOS and ships only FOSS builds. The nonFree flavor added Sentry crash reporting, wired to upstream's Sentry project, and Play Services SMS one-time-code autofill, which needs Google's servers. Neither is wanted, so remove the flavor dimension entirely: - Delete app/src/nonFree, the SentryPlugin, the sentry-stub module, the Sentry and Play Services catalog entries, and the Sentry scope setup in Application. - Move the free AutofillSmsActivity stub into main; it keeps the SMS OTP feature off exactly as the free build did. A ponytail note marks the FillOtpFromSms paths for later removal. - SlimTests no longer needs to skip a nonFree variant. - Workflows and docs use :app:assembleDebug / :app:assembleRelease. A bare assembleRelease would also verify library modules in release mode, where ui:compose fails on a pre-existing AppCompat attribute reference the old flavored task never touched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>