Releases: r00tedbrain-backup/Securechatskeyboard
SecureChats Keyboard BWT v3.0.1
SecureChats Keyboard BWT v3.0.1
Critical bugfix release -- Resolves a FATAL CRASH introduced in v3.0.0 that affected all users on every keyboard invocation.
Critical Fix: Application Crash on Launch
The Problem
After upgrading to libsignal 0.86.5 in v3.0.0, the app crashed immediately every time the keyboard was activated:
FATAL EXCEPTION: main
java.lang.IllegalArgumentException: protocol address is invalid:
1e5e3bdf-2d8d-4e8d-8204-64ea2ba387bd.7642
at org.signal.libsignal.internal.Native.ProtocolAddress_New(Native Method)
at org.signal.libsignal.protocol.SignalProtocolAddress.<init>
at ...SignalProtocolMain.initializeProtocol
at ...LatinIME.onStartInputInternal
This crash made the keyboard completely unusable for all users.
Root Cause
libsignal 0.86.5 introduced two strict validations on SignalProtocolAddress that the previous code violated:
| Constraint | Required by libsignal 0.86.5 | Old code behavior |
|---|---|---|
deviceId range |
1 - 127 inclusive | Random().nextInt(10000) generated 0 - 9999 |
name format |
Valid UUID (ServiceId) | Older stored data had UUID.deviceId format |
The native Rust layer (ProtocolAddress_New) rejects any address where deviceId > 127 or name is not a valid UUID, throwing an unrecoverable IllegalArgumentException.
The Fix
Added a sanitization layer that intercepts all SignalProtocolAddress construction:
sanitizeAddressName(String)-- Extracts a clean UUID from legacyUUID.deviceIdstored formats. Falls back to generating a new UUID if the name is completely invalid.sanitizeDeviceId(int)-- Maps any out-of-range deviceId into the valid 1-127 range using modulo arithmetic.createSafeAddress(String, int)-- Factory method used by all code paths that create aSignalProtocolAddress.
All 5 call sites that previously used new SignalProtocolAddress(name, deviceId) directly have been migrated to createSafeAddress():
| File | Method | Context |
|---|---|---|
SignalProtocolMain.java |
initializeProtocol() |
New account creation |
SignalProtocolMain.java |
extractContactFromEnvelope() |
Incoming message processing |
JsonUtil.java |
SignalProtocolAddressDeserializer |
SharedPreferences reload |
Contact.java |
Constructor | Contact deserialization from JSON |
E2EEStripView.java |
addContact() |
UI contact creation |
Migration Compatibility
Users upgrading from older versions (pre-v3.0.0) that had addresses stored in the legacy UUID.deviceId format will have their data automatically migrated on first load -- the sanitizer strips the .deviceId suffix and remaps the deviceId to a valid range. No data loss occurs.
iOS: Keyboard Performance Overhaul
Major rewrite of KeyboardView.swift rendering pipeline to eliminate frame drops and animation jank:
| Optimization | Impact |
|---|---|
Pre-computed shadowPath on all keys |
Eliminates offscreen rendering (~9ms/frame for 30 keys) |
CATransaction.setDisableActions(true) |
Kills 0.25s implicit CoreAnimation animations |
shouldRasterize = true + rasterizationScale |
GPU caches composited key bitmaps |
isOpaque = true everywhere |
Skips alpha blending calculations |
autoresizingMask instead of Auto Layout |
Fewer constraint solver passes |
Shift icon UIImageView reuse |
No allocation/layout churn on shift toggle |
CADisplayLink for delete repeat |
Frame-synced, jitter-free key repeat |
Batched CATransaction blocks |
Single compositing pass per state change |
iOS marketing version bumped to 9.1.0.
Version Details
| Platform | Version |
|---|---|
| Android versionCode | 13 (up from 2; exceeds Play Store versionCode 12) |
| Android versionName | 3.0.1 |
| iOS Marketing Version | 9.1.0 |
Verification
Tested on physical ZTE device (arm64-v8a, Android 15):
- Fresh install (no SharedPreferences):
initializeProtocol()creates address with valid UUID + deviceId in range 1-127. Kyber/PQC prekey generation succeeds. - Existing account reload: Deserializer sanitizes legacy format and loads account without crash.
- No regressions: Encryption, decryption, contact management, and session establishment all function correctly.
Checksums
SHA-256: 7cd3c9fddc885ea3687efb902338d7197a2c0ba73d9bc8deffdaf217b7d5b23f
File: SecureChatKeyboard-release-20260224.apk
Size: 18 MB
Download
| Platform | Link |
|---|---|
| Android APK | Attached below |
| iOS | App Store |
SecureChats Keyboard BWT v3.0.0
SecureChats Keyboard BWT v3.0.0
Post-Quantum Encrypted Keyboard for Android β End-to-end encryption with Signal Protocol + Kyber PQC, directly from your keyboard. No servers. No phone numbers. No internet required.
What's New in v3.0.0
All Dependencies Updated to Latest Stable Versions
| Library | Previous | New | Purpose |
|---|---|---|---|
libsignal-android |
0.73.2 | 0.86.5 | Signal Protocol core (E2EE + native PQXDH/Kyber) |
jackson-databind |
2.14.1 | 2.18.2 | JSON serialization engine |
jackson-datatype-jsr310 |
2.14.1 | 2.18.2 | Java 8+ date/time support |
protobuf-javalite |
3.21.12 | 3.25.5 | Protocol Buffers (libsignal dependency) |
desugar_jdk_libs |
2.0.3 | 2.1.4 | Java 8+ API backport for Android |
security-crypto |
1.1.0-alpha03 | 1.1.0-alpha06 | EncryptedSharedPreferences |
bcprov-ext-jdk18on |
1.78.1 | 1.78.1 | BouncyCastle PQC (already latest) |
libsignal API Migration (Breaking Changes Resolved)
The upgrade from libsignal 0.73.2 to 0.86.5 involved significant API breaking changes that were fully migrated:
Curve.generateKeyPair()replaced byECKeyPair.generate()Curve.calculateSignature(key, data)replaced bykey.calculateSignature(data)Curve.decodePoint(bytes, offset)replaced bynew ECPublicKey(bytes, offset)markKyberPreKeyUsed(int)updated tomarkKyberPreKeyUsed(int, int, ECPublicKey)for PQXDH session supportIdentityKeyPair(byte[])now throws checkedInvalidKeyException(properly handled)- Removed deprecated
Curveutility class andPairimports entirely
Build Verification
assembleDebugβ BUILD SUCCESSFULcompileDebugUnitTestJavaWithJavacβ BUILD SUCCESSFUL- All 9 modified source files compile cleanly with zero errors
Core Features
Hybrid Post-Quantum Encryption
- Signal Protocol (X3DH + Double Ratchet) β Industry-standard E2EE with Perfect Forward Secrecy
- Kyber-1024 (PQXDH) β Native libsignal post-quantum key encapsulation, resistant to Shor's algorithm
- Kyber-512 (BouncyCastle) β Secondary PQC layer via KEM encapsulation/decapsulation
- Dual key rotation β Both ECC and Kyber pre-keys rotate independently every 2 days
Zero-Trust Architecture
- No internet permission β The keyboard requests zero network permissions. All cryptography is fully offline
- No server β Key bundles are shared directly through any messenger (WhatsApp, Telegram, Signal, SMS, etc.)
- No phone numbers β Users identified by randomized UUIDs
- No plaintext storage β All data encrypted at rest with AES-256-GCM via Android Keystore hardware-backed MasterKey
- No backup leakage β
android:allowBackup=falseprevents cloud backup of encryption keys
Steganography
- Fairy Tale Mode β Encrypted messages hidden inside invisible Unicode characters, appended to decoy fairy tale text (Cinderella, Rapunzel)
- 16 zero-width Unicode characters map 4-bit groups after GZIP compression and JSON minification
- Messages appear as innocent fairy tale excerpts to any observer
Full Keyboard
- 49 keyboard layouts β QWERTY, QWERTZ, AZERTY, Dvorak, Colemak, Cyrillic, Arabic, Hindi, Bengali, Thai, Georgian, Armenian, and more
- 89 language localizations β Full i18n coverage
- Customizable β Dark/light themes, custom color picker, adjustable height, haptic and sound feedback
- E2EE control strip β Dedicated encryption interface above the keyboard with 6 switchable views
Contact Verification
- SHA-512 numeric fingerprints β 12-segment security codes with animated reveal
- Visual comparison β Compare 12 five-digit codes between devices to verify identity
- Per-contact history deletion β Cryptographic erasure of message history
Technical Specifications
| Spec | Value |
|---|---|
| compileSdk | 35 (Android 15) |
| targetSdk | 35 |
| minSdk | 26 (Android 8.0 Oreo) |
| Java | 17 |
| Architectures | arm64-v8a, armeabi-v7a |
| License | GPL-3.0 |
| F-Droid | Compatible (no proprietary dependencies) |
Protocol Stores
| Store | Purpose |
|---|---|
| IdentityKeyStore | Permanent identity key pair and trust decisions |
| PreKeyStore | One-time pre-keys (2 keys, auto-renewed) |
| SignedPreKeyStore | Signed pre-keys with 2-day rotation |
| SessionStore | Per-contact session state |
| SenderKeyStore | Group messaging sender keys |
| KyberPreKeyStore | Native libsignal Kyber-1024 pre-keys |
| BCKyberPreKeyStore | BouncyCastle Kyber-512 pre-keys |
| PreKeyMetadataStore | Rotation schedules and active key IDs |
Message Flow
Alice Bob
| |
|--- PreKeyBundle (ECC + Kyber keys) --->| Invite
| |
|<-- PreKeySignalMessage ----------------| Session established
| |
|--- SignalMessage ---------------------->| Double Ratchet
|<-- SignalMessage -----------------------|
| ... |
|--- SignalMessage + Updated PreKeys --->| Auto key rotation (every 2 days)
| |
Files Changed in This Release
app/build.gradle β Updated all dependency versions + packaging rules
SignalProtocolMain.java β Migrated Curve to ECKeyPair/ECPrivateKey API
PreKeyEntity.java β Migrated Curve.decodePoint to new ECPublicKey
PreKeyResponse.java β Updated version comments
KyberPreKeyStoreImpl.java β Updated markKyberPreKeyUsed signature (3 params)
SignalProtocolStoreImpl.java β Updated markKyberPreKeyUsed delegation
JsonUtil.java β Wrapped IdentityKeyPair InvalidKeyException
KeyUtil.java β Removed stale try-catch and unused import
SignalProtocolTest.java β Migrated test Curve calls
Important Notes
- This keyboard is designed to work on top of any existing messenger. It encrypts text before you send it.
- The keyboard itself never connects to the internet. Encryption keys are shared as text messages through your existing apps.
- Based on the open-source KryptEY project (https://github.com/amnesica/KryptEY) by mellitopia and amnesica, with significant enhancements.