Repository navigation
Conversation
Encrypt successful ScreenLogic connections in an AES-GCM vault under noBackupFilesDir using an Android Keystore key (alias westcurrent.saved_pools.aes.v1). Recent pools, one-tap reconnect, password-only prompts, rename/remove, and fail-closed vault recovery. Consent defaults are off. Passwords are optional and never land in ordinary preferences, logs, or UI state. Existing SiteSession remains the only connection engine. No auto-connect on launch. No equipment features added. Fable did not implement security or architecture.
Remove long-lived decrypted profile caching; zero temporary plaintext byte arrays after each repository call. Replace homemade file writes with Android AtomicFile (SimulatedAtomicVaultFileStore for JVM tests). Introduce ConnectionAttemptResult so save/password/last-success updates bind only to the exact installed attempt. ViewModel request epochs invalidate stale decrypt and password prompts. Pre-install controller identity validation rejects mismatches without publishing site data. Add PasswordSaveDecision, provisional identity upgrade, profile-limit rejection, explicit preferred flag, local Find-on-Wi-Fi recovery, precise Keystore health mapping, and non-saveable system code/address form state. Fable did not implement security or architecture. PR remains draft.
Carry PasswordPromptReason through replace flows so remember-off removes a known-bad stored password after successful override. Use PRESERVE_EXISTING on normal merges and Save current when Remember is off. Persist expected controller identity for the session and enforce it on install, automatic reconnect, and explicit retry. Reject when expected identity is known but the candidate reports none. Fix provisional merge so identity-null connections never merge into known-identity profiles. Own local-recovery discovery with epoch cancel; restrict Find on Wi-Fi to local reachability failures only. Expose route-specific password status and forget actions. Read AtomicFile only via openRead recovery. Complete Keystore exception mapping. Zero codec intermediate UTF-8 buffers. Add Phase 1.6.2 tests and androidTest Keystore suite. Fable did not implement security or architecture. PR remains draft.
Real-device save failed because encrypt passed a caller IV via GCMParameterSpec while setRandomizedEncryptionRequired(true). Android Keystore rejects that path. Refactor: platform-neutral AesGcmVaultEnvelope pack/parse; JVM SecretKeyVaultCipher keeps encryptWithCallerIv; Android encrypt init without IV, pack cipher.iv into WCAE envelope. Decrypt still supplies the stored IV. No plaintext fallback; randomized encryption remains required. Unit tests: 324 green. Instrumentation suite updated for the Keystore-generated-IV path. Physical retest pending Frankie.
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.
Summary
Phase 1.6 + 1.6.1 + 1.6.2 + Keystore-generated-IV device fix for securely saved pools and one-tap reconnect.
Fable did not implement security or architecture.
This PR is draft and remains unmerged.
Physical validation status (2026-07-13) — partial pass
Reproduced failure → fix → device pass
4d463e6bb59e3e3032c1b792b6d6330c69795e62— Android Keystore encrypt uses platform-generated IV (cipher.init(ENCRYPT_MODE, key)then packcipher.iv); no plaintext/software fallback; randomized encryption remains required.Physical validation is therefore partially passed, not fully complete.
Passed on device (confirmed only)
Still pending on device / process
Details:
docs/PHASE_1_6_SAVED_POOLS_SECURE_RECONNECT.md§16.Device fix: Android Keystore encryption IV
Root cause
AesGcmVaultEnvelope.encrypt()generated a caller IV and called:Android Keystore keys use
setRandomizedEncryptionRequired(true), which rejects caller-provided IVs on encrypt. JVM unit tests did not catch this because software AES keys accept caller IVs.Corrected design
Platform-neutral envelope (
AesGcmVaultEnvelope):pack/parseonly.Android Keystore encrypt:
init(ENCRYPT_MODE, key)without caller IV → AAD →doFinal→ validatecipher.iv(12 bytes) → pack WCAE.Decrypt: stored IV via
GCMParameterSpeconly.JVM / tests:
encryptWithCallerIvmay injectSecureRandom.Tests and build (code head
4d463e6)%TEMP%\westcurrent-app-build\outputs\apk\debug\app-debug.apk4d463e6Phase 1.6.2 corrections
Password replacement context
PasswordPromptReason:MISSING_PASSWORD|REPLACE_REJECTED_STORED_PASSWORDReady.usedStoredPasswordon the override attemptREMOVE_EXISTINGafter successSTORE_NEWPassword preservation on merges
STORE_NEW; Remember off →PRESERVE_EXISTINGReconnect identity enforcement
connectWithSessionFactory(same injectable factory)Missing-candidate-identity policy
Rejected(CONTROLLER_IDENTITY_UNAVAILABLE)before publishProvisional merge rules
Owned local-recovery discovery
discoverLocalQuiet) does not publish global ConnectingLocal-only DHCP recovery
SCREENLOGIC_LOCALreachability failuresRoute-specific password management
remotePasswordStatus/localPasswordStatusAtomicFile recovery-safe reading
openRead(); FileNotFound → emptyKeystore mapping
Intermediate plaintext zeroing
writeString/readStringzero UTF-8 tempsZeroingByteArrayOutputStreamfor encode bufferSHAs
4d463e6bb59e3e3032c1b792b6d6330c69795e625b0c4b91768aee25586dbb1bebca0fe0eb705597148707688e78662e454cf4bbf11f08dc26ad9924Documentation
docs/PHASE_1_6_SAVED_POOLS_SECURE_RECONNECT.md(§4 Keystore IV note + §16 physical validation log)Explicit statements