A third-party source review of v00030 surfaced 12 findings plus a handful of polish items. Everything below is fixed in v00032, which also ships an encrypted configuration backup/restore feature to smooth the signing-key migration.
TL;DR for existing users: v00032 changes the APK signing key. Install
v00031 first and use Settings → Backup & Restore → Export to save your
keys, recipients, and settings, then install v00032 and Import them.
v00031 download: https://git.smesh.lol/cipherkeys-releases/raw/cipherkeys-v00031.apk
INTERNET, no networking code anywhere in the tree.For a keyboard this is the property that matters most, and it means none of the findings below were remotely exploitable.
validateDistributionUrl=true.decryptInternal checks metadata.isEncrypted before returning — a trap manyPGP frontends fall into.
doDecrypt returned only plaintext and never read metadata.verifiedSignatures,
so a message from anyone holding your public key was indistinguishable in the UI
from one sent by a trusted contact.
→ Decrypt now adds the sender's cert via addVerificationCert(), verifies the
signature, and surfaces the signer's identity (or an explicit "Message is not
signed" warning) in the panel.
onPaste built verification certs from the user's own keys instead of the
recipient list, so a message signed by a real correspondent could never verify,
and the banner was generic "Message valid".
→ Paste verification now uses recipient public keys and shows whose signature
it is.
allowBackup="true" — FIXEDArmored secret key rings were stored verbatim in plain prefs and Android's
backup (Seedvault / Google Drive / D2D) copied them off-device.
→ Key storage now uses EncryptedSharedPreferences (Android Keystore-backed
AES-256-GCM master key) with a one-time migration from the old plain store, and
allowBackup="false".
storePassword = "android" at a well-known path meant anyone with that public
keystore could ship an update Android accepts as the same app.
→ A dedicated release keystore (RSA-4096) with a real password is now used,
referenced out-of-repo via environment variables / ~/.gradle/gradle.properties.
No confirmation, no passphrase check, no sensitive-clipboard flag, sitting right
beside "Copy Public".
→ Now a confirmation dialog that actually verifies the passphrase (PGPainless
unlock), marks the clip EXTRA_IS_SENSITIVE, and clears it after 30 s.
handleSend was the only send path without a pendingAction guard, so a
passphrase could overwrite the saved message and get signed/committed as a
cleartext-signed block.
→ handleSend now early-dispatches through the same pending-action routing as
the encrypt/decrypt handlers.
Log.* calls interpolated decrypted plaintext and key IDs, and release wasn't
minified so all logs shipped.
→ Plaintext/key-ID interpolations removed from log statements.
encryptAndSend(cachedPassphrase) signed only when the cache was warm, so the
same message could be encrypted unsigned with no warning.
→ If a signing key is selected and the cache is cold, send now prompts for the
passphrase instead of silently proceeding unsigned.
The isEncryptedMessage check was a bare substring test, so quoted ciphertext
plus commentary passed as "already encrypted".
→ The check is anchored to the trimmed start and requires a matching
-----END PGP MESSAGE-----.
An unprotected key (or a mistyped passphrase) was accepted at import and only failed later. → Import now attempts a real unlock with the supplied passphrase and rejects keys with no S2K protection.
It cleared only on screen-off, surviving app/IME switches. → Added a 5-minute idle timeout in addition to the screen-off clear.
FLAG_SECURE — FIXEDKey material and decrypted plaintext were screenshot- and recents-visible.
→ FLAG_SECURE set on the settings activity; decrypted-text copy is marked
sensitive on the clipboard.
listKeys() now fills in created and algorithm (was blank in the UI).florisboard git submodule.app generates/supports; it would only desync for v6 keys, which aren't used.
Because the signing key changes in this release, v00032 adds a way to move your configuration:
recipients, and preferences (PGP-symmetric, password) into one file saved via the system file picker.
the password, and restores everything (skipping duplicates).
v00032 is signed with a new key, so existing installs can't update in place.
Export your configuration.
Obtainium and Zapstore will offer v00032 directly; do the v00031 backup first if you have existing keys you want to keep.