Chaque version rapproche Fialka de l'invulnérabilité de son ancêtre M-125.
Each release brings Fialka closer to the invulnerability of its M-125 ancestor.
Fondations : chiffrement E2E, contacts via QR, conversations persistantes.
Full Double Ratchet X25519, remplacement de P-256 par Curve25519.
Backup BIP-39, restauration, suppression complète, détection de comptes morts.
/users/{uid}, /inbox/{hash}, /conversations/{id}removeOldUserByPublicKey() supprime l'ancien nœud /users/ orphelin.read et .write restreints au niveau $conversationId5 thèmes, animations complètes, CoordinatorLayout, zéro couleur hardcodée.
attrs.xml complet : toolbar, bulles, avatars, badges, input bar, surfaces, dividersbackgroundTint (base blanche + tint)?attr/colorToolbarBackground, elevation 0dpHideBottomViewOnScrollBehavior masque le FAB au scroll?attr/ (theme-aware)Durcissement sécuritaire complet : chiffrement renforcé, anti-analyse de trafic, partage de fichiers E2E.
isMinifyEnabled=true, isShrinkResources=true, repackaging en releaseLog.d(), Log.v(), Log.i() supprimés par ProGuard (assumenosideeffects)senderUid = HMAC-SHA256(conversationId, UID) tronqué 128 bits — Firebase ne peut plus corréler le même utilisateur entre conversations\u0007\u001B\u0003)/encrypted_files/processedFirebaseKeys empêche la désynchronisation ratchet quand 2 listeners traitent le même messageParamètres repensés Signal/Telegram, PIN 6 chiffres, sous-écran Confidentialité, performance PIN.
Dispatchers.Default, zéro freeze UISignature par message Ed25519, badge ✅/⚠️, durcissement Firebase rules, nettoyage clés de signature.
ciphertext_UTF8 || conversationId_UTF8 || createdAt_bigEndian8 — anti-falsification + anti-replaySecurity.removeProvider("BC") + insertProviderAt(BouncyCastleProvider(), 1) dans Application.onCreate()/signing_keys/{SHA256_hash} et /users/{uid}/signingPublicKeycreatedAt = System.currentTimeMillis() (pas ServerValue.TIMESTAMP) pour cohérence signature/conversations/$id/participants lisible uniquement par les membres (plus par tous les authentifiés)/signing_keys/{hash} supprimé à la suppression de compteMigration complète Material Design 3, intégration Tor (SOCKS5 + VPN TUN), icônes d'attachement inline style Session, permissions Android 13+, durcissement Firebase et logs.
Theme.MaterialComponents vers Theme.Material3.Dark.NoActionBar / Theme.Material3.Light.NoActionBarcolorPrimaryContainer, colorOnPrimary, colorSecondary, colorSurfaceVariant, colorOutline, colorSurfaceContainerHigh/Medium/Low, colorError, etc. sur les 5 thèmesWidget.Material3.TextInputLayout.OutlinedBox (Onboarding, Restore, AddContact)Widget.Material3.Button.TextButton / OutlinedButton (TorBootstrap, Onboarding, Profile)enableOnBackInvokedCallback="true" dans le manifestmaxSdkVersion="32" pour Android 12 et inférieurdatabase.goOnline() après auth.signOut() (corrige l'erreur de permission Firebase)useAppLanguage() par setLanguageCode(Locale.getDefault().language) explicite (corrige X-Firebase-Locale null)signingKeyPublished + markSigningKeyPublished() élimine la publication redondante entre OnboardingViewModel et ConversationsViewModelLog.w(), Log.e(), Log.wtf() dans assumenosideeffects (en plus de d/v/i) — suppression totale des logs en releaseFirebaseRelay.kt et ChatRepository.kt n'affichent plus de chemins Firebase ou d'identifiants dans les logsStateFlow<TorState> (IDLE, STARTING, BOOTSTRAPPING(%), CONNECTED, ERROR, DISCONNECTED)127.0.0.1:9050 quand Tor activéFialkaApplication.onCreate() démarre Tor si activéstartDestination du nav graph, choix Tor/Normal au premier lancement?attr/ du thème actifPQXDH hybride (ML-KEM-1024 + X25519), DeviceSecurityManager StrongBox, QR deep link v2, vérification d'empreinte indépendante, corrections de désynchronisation ratchet.
fialka://contact?key=<X25519>&kem=<ML-KEM-1024-pubKey>&name=<displayName> — clé ML-KEM encodée dans le QRstoreDisplayName → no-op), supprimé des Firebase rulessyncExistingMessages() à l'acceptation d'un contact pour déclencher correctement l'init PQXDHConcurrentHashMap.putIfAbsent() + éviction LRU pour empêcher les race conditions sur les listeners FirebasefingerprintEvent: "verified:<timestamp>" (push seulement, pas de sync d'état)lastDeliveredAt sur ConversationversionCode 5, versionName "3.4.0"Photos éphémères one-shot, écran de restauration repensé avec grille BIP-39, vérification d'empreinte par QR code, améliorations UI.
oneShotOpened=1 immédiat dans Room (empêche la re-visualisation) ; Phase 2 : suppression physique du fichier après 5 secondes (délai pour l'app de visualisation)Handler.postDelayed), empêchant le contournement par retour arrièreAutoCompleteTextView en grille 3×8 avec numérotationCustomScannerActivity que l'invitation de contact (torche, orientation libre)ignoreCase → dialogue ✅ match ou ❌ MITM warninggetSharedFingerprintHex() — Nouvelle méthode dans CryptoManager retournant le SHA-256 hex brut des clés publiques triéesoneShotOpened sur MessageLocalflagOneShotOpened() — Nouvelle requête DAO : UPDATE messages SET oneShotOpened = 1 WHERE localId = :messageIdversionCode 6, versionName "3.4.1"/signing_keys/{hash}, /mlkem_keys/{hash}, /inbox/{hash}/{convId} imposent !data.exists() — empêche l'écrasement de clés et le replay de demandessenderUid.length === 32, ciphertext non-vide + max 65536, iv non-vide + max 100, createdAt <= now + 60000hkdfExtractExpand() efface IKM, hkdfExpand() efface PRK + expandInput après usageprivateKeyToMnemonic() et mnemonicToPrivateKey() effacent tous les tableaux d'octets intermédiaires et nettoient le StringBuilderderiveRootKeyPQXDH() exige les deux entrées de 32 octets exactementderiveConversationId() utilise "|" pour éviter les collisions de concaténation de clésMainActivity, LockScreenActivity, RestoreFragment et le dialog mnemonic — bloque screenshots, enregistrement d'écran, aperçu tâchesTYPE_TEXT_VARIATION_PASSWORDonDestroyView()parseInvite() : whitelist paramètres, limites de taille, rejet doublons, rejet caractères de contrôle, validation Base64, max 4000 charsEXTRA_IS_SENSITIVE + auto-effacement 30 secondes via Handler.postDelayedfd.sync()) avant File.delete()saveFileLocally() appelle fileBytes.fill(0) après écritureSecureFileManager.secureDelete()deleteStaleConversation() écrase le répertoire de fichiers de la conversationdeleteExpiredMessages() supprime les fichiers associés en premiersendMessage() avec require() sur tous les champs (conversationId, ciphertext, iv, taille senderUid, createdAt)/^[0-9a-f]{32}$/) et format conversationId{type: "new_message", sync: "1"} — zéro fuite de metadataMyFirebaseMessagingService affiche « Nouveau message reçu » (pas de nom, pas d'ID conversation)<application> — bloque tout trafic HTTP non chiffréMainActivity et LockScreenActivity — protection tapjackingresource.metadata['uploaderUid'] == request.auth.uid requis pour supprimeruploadEncryptedFile() attache uploaderUid dans les StorageMetadataTriple Ratchet post-quantique (SPQR), chiffrement alternatif ChaCha20-Poly1305, modèle de menace documenté.
DoubleRatchet.pqRatchetStep() : mixe un secret ML-KEM frais dans le rootKey via HKDF (info: Fialka-SPQR-pq-ratchet)PQ_RATCHET_INTERVAL = 10 messages : toutes les 10 messages, le sender effectue un ML-KEM encaps et upgrape le rootKeysendMessage(), quand le compteur atteint 10 et que PQXDH est initialisé : mlkemEncaps(remoteMlkemPublicKey) → pqRatchetStep(rootKey, ssPQ) → nouveau rootKey + kemCiphertext attaché au messagereceiveMessage(), détection du kemCiphertext sur une session déjà PQ-initialisée : mlkemDecaps() → pqRatchetStep() → rootKey upgradé, compteur réinitialisépqRatchetCounter dans RatchetState (Room entity), incrémenté à chaque message envoyékemCiphertext), distingué du PQXDH initial par pqxdhInitializedencryptChaCha() / decryptChaCha() — Implémentation complète via BouncyCastle ChaCha20Poly1305 AEAD (nonce 12 octets, tag 16 octets)hasHardwareAes() détecte la présence de l'extension ARMv8 Crypto ; ChaCha20 est sélectionné automatiquement sur les appareils sans accélération matérielle AEScipherSuite — Nouveau champ dans FirebaseMessage (0 = AES-GCM, 1 = ChaCha20) ; le receiver déchiffre avec le bon algorithme automatiquementcipherSuite (= 0) sont déchiffrés en AES-GCM comme avantpqRatchetCounter sur RatchetStateversionCode 7, versionName "3.5"Suppression totale de Firebase, infrastructure P2P pure via Tor Hidden Services, système Mailbox offline 4 modes, identité « 1 Seed → Tout », ML-DSA-44, pipeline de livraison avec statuts, refresh UI 2026.
versionCode 8·versionName "4.0"· Room v24
0xF1 0xA1), 13 types de trames (P2P + commandes Mailbox), écriture/lecture avec timeoutTYPE_MESSAGE, TYPE_CONTACT_REQ, TYPE_KEY_BUNDLE, TYPE_CONTACT_REQ_RESPONSE, etc.DeliveryResult (DIRECT / MAILBOX / QUEUED)SHA3-256(pubkey Ed25519) → Base58 (ex : Fa3x...9Z)libtor.so pour Tor v3 Hidden Services réelskillOrphanedTor() — Lit le cookie d'auth AVANT suppression (AUTHENTICATE <hex>), empêche les démons orphelinsgetPendingMessages() inclut les messages bloqués en STATUS_SENDING.onion personnel comme boîte aux lettres asynchrone_fetching: StateFlow<Boolean> pour bloquer l'UI pendant le fetchmailboxOnion — senderMailboxOnion inclus dans tous les messages ; P2PServer met à jour participantMailboxOnion à la réceptiontotalDeposited, totalFetched, totalDataProcessed persistés en SharedPreferences (blobs supprimés après livraison)MessageLocal.deliveryStatus — SENT(0) / MAILBOX(1) / FAILED(2) / PENDING(3)OutboxMessage.messageLocalId — Liaison retour vers MessageLocal pour mise à jour du statutFAILED → OutboxDao.resetRetryForMessage() relance la tentativefetchBanner + ProgressBar dans le chat, input désactivé pendant le fetch MailboxSettingsAdapter + SettingsViewModel, recherche + filtres catégorie (Apparence, Notifications, Confidentialité, Sécurité, Réseau, À propos)ConstraintLayout → LinearLayoutEd25519 · ML-DSA-44 · ML-KEM-1024 en monospacebg_key_boxgetPendingMessages() inclut STATUS_SENDING bloqués (récupération après crash/reboot)resolveRecipientEd25519 — Fallback sur publicKey si signingPublicKey absentsendContactRequest — Retourne Boolean, logs completskillOrphanedTor — Lecture du cookie auth AVANT suppression du fichierdeliveryStatus sur MessageLocal, messageLocalId + fallbackOnion sur OutboxMessage, migrations v18→v24MailboxBlob, MailboxMember, MailboxInvite (mode MAILBOX uniquement)versionCode 8, versionName "4.0"Remplacement de
security-cryptoparFialkaSecurePrefs(Keystore direct), correction crash SQLCipher 4.14.1, correction NPEDurationSelectorBottomSheet, fiabilité complète du transport (5 corrections).
security-crypto — androidx.security:security-crypto:1.1.0-alpha06 et MasterKey.Builder / EncryptedSharedPreferences entièrement retirésFialkaSecurePrefs — Nouvelle implémentation object utilisant l’Android Keystore directement : AES-256-GCM, alias clé fialka_ks_{name}, fichier préférences {name}_v2CryptoManager, FialkaDatabase, MailboxDatabase, AppMode, AppLockManager, MailboxClientManager migrés vers FialkaSecurePrefs.open()System.loadLibrary("sqlcipher") — sqlcipher-android:4.14.1 supprime l’initialiseur statique ; FialkaApplication.onCreate() charge la bibliothèque native en premierFialkaDatabase.getInstance() et MailboxDatabase.buildDatabase() appellent également loadLibrary en garde défensivecontext supprimé — DurationSelectorBottomSheet n’accepte plus de Context externe ; BottomSheetDialogFragment dispose de son propre contexteshow() simplifié — Suppression de fragmentManager.findFragmentById(R.id.nav_host_fragment)?.requireContext()!! qui retournait null depuis childFragmentManagerRETRY_INTERVAL_MS 60 s → 15 s ; plafond MAX_RETRY_DELAY_MS 30 min → 3 min (backoff : 15 s / 30 s / 1 min / 2 min / 3 min)DELIVERY_FAILED sur messages épuisés — Les messages atteignant 50 tentatives reçoivent le statut DELIVERY_FAILED AVANT suppression de la file (corrige le sablier ⏳ permanent)processOutboxForContact() — Si le dépôt P2P échoue, dépôt automatique via Mailbox avec mise à jour vers DELIVERY_MAILBOXbroadcastPresence() — Après la boucle des contacts hors-ligne, processOutbox() est appelé pour traiter les messages en attenteMailboxClientManager — Base 10 s, 5 s si messages reçus (+OutboxManager.flushNow()), backoff jusqu’à 60 s sur erreur
versionCode 11·versionName "4.1.0-alpha"· Correction sécurité + couverture tests + protection UX
P2PServer.handleContactRequest() — Vérifie maintenant la signature Ed25519 sur les demandes de contact entrantes. Auparavant, senderSigningPublicKey était reçu mais jamais vérifié — n'importe qui pouvait usurper une identité.senderPubKey(UTF-8) || 0x00 || conversationId(UTF-8) || createdAt(big-endian 8 octets) — séparé par domaine, déterministesendContactRequest() — Signe maintenant le payload avec FialkaNative.ed25519Sign() et inclut le champ requestSignaturerequestSignature sont encore acceptés (fenêtre de migration progressive)CryptoManagerPureTest — 20 tests : déterminisme/commutativité/unicité de deriveConversationId, encodage timestamp buildSignedData, pseudonymat cross-conversation hashSenderUidRatchetSimulationTest — 16 tests : ratchet bidirectionnel, unicité 500 clés, intervalle SPQR=10, correction pqRatchetStep, symétrie initiator/responderDoubleRatchetTest — 2 tests JNI marqués @Ignore (nécessitent libfialka_core.so — à migrer en tests instrumentés)FialkaDatabase.needsDestructiveMigration() — Détecte quand une mise à jour de schéma Room déclencherait fallbackToDestructiveMigration (DROP ALL TABLES)MainActivity — Affiche un AlertDialog bloquant avant tout accès DB lors d'une mise à jour : l'utilisateur doit confirmer explicitement la perte de données ou quitterFialkaDatabase.recordCurrentVersion() — Persiste la version du schéma dans des SharedPreferences simples après ouverture confirméeversion.properties — Source unique de vérité pour VERSION_CODE + VERSION_NAME ; build.gradle.kts le lit automatiquement — modifier uniquement version.properties pour bumper la versionversionCode 11Camouflage avancé, plausible deniability, messages vocaux E2E, sealed sender, améliorations messagerie.
<activity-alias> dans le manifest (enable/disable dynamique via PackageManager)FLAG_SECURE sur toutes les fenêtresflagNoPersonalizedLearning sur tous les champs de saisie — le clavier ne mémorise/apprend rienFoundations: E2E encryption, contacts via QR, persistent conversations.
Full Double Ratchet X25519, replaced P-256 with Curve25519.
BIP-39 backup, restore, full deletion, dead account detection.
/users/{uid}, /inbox/{hash}, /conversations/{id}removeOldUserByPublicKey() removes the orphaned old /users/ node.read and .write restricted at $conversationId level5 themes, full animations, CoordinatorLayout, zero hardcoded colors.
attrs.xml: toolbar, bubbles, avatars, badges, input bar, surfaces, dividersbackgroundTint (white base + tint)?attr/colorToolbarBackground, elevation 0dpHideBottomViewOnScrollBehavior hides the FAB on scroll?attr/ (theme-aware)Complete security hardening: reinforced encryption, traffic analysis countermeasures, E2E file sharing.
isMinifyEnabled=true, isShrinkResources=true, repackaging in release buildsLog.d(), Log.v(), Log.i() removed by ProGuard (assumenosideeffects)senderUid = HMAC-SHA256(conversationId, UID) truncated to 128 bits — Firebase cannot correlate the same user across conversations\u0007\u001B\u0003)/encrypted_files/ pathprocessedFirebaseKeys prevents ratchet desync when 2 listeners process the same messageSignal/Telegram-style settings, 6-digit PIN, Privacy sub-screen, PIN performance.
Dispatchers.Default, zero UI freezePer-message Ed25519 signatures, ✅/⚠️ badge, Firebase rules hardening, signing key cleanup.
ciphertext_UTF8 || conversationId_UTF8 || createdAt_bigEndian8 — anti-forgery + anti-replaySecurity.removeProvider("BC") + insertProviderAt(BouncyCastleProvider(), 1) in Application.onCreate()/signing_keys/{SHA256_hash} and /users/{uid}/signingPublicKeycreatedAt = System.currentTimeMillis() (not ServerValue.TIMESTAMP) for signature consistency/conversations/$id/participants readable only by members (no longer by all authenticated users)/signing_keys/{hash} deleted on account deletionFull Material Design 3 migration, Tor integration (SOCKS5 + VPN TUN), Session-style inline attachment icons, Android 13+ permissions, Firebase & log hardening.
Theme.MaterialComponents to Theme.Material3.Dark.NoActionBar / Theme.Material3.Light.NoActionBarcolorPrimaryContainer, colorOnPrimary, colorSecondary, colorSurfaceVariant, colorOutline, colorSurfaceContainerHigh/Medium/Low, colorError, etc. across all 5 themesWidget.Material3.TextInputLayout.OutlinedBox (Onboarding, Restore, AddContact)Widget.Material3.Button.TextButton / OutlinedButton (TorBootstrap, Onboarding, Profile)enableOnBackInvokedCallback="true" in manifest for Android 13+maxSdkVersion="32" for Android 12 and belowdatabase.goOnline() after auth.signOut() (fixes Firebase permission error)useAppLanguage() with explicit setLanguageCode(Locale.getDefault().language) (fixes X-Firebase-Locale null)signingKeyPublished flag + markSigningKeyPublished() eliminates redundant publish between OnboardingViewModel and ConversationsViewModelLog.w(), Log.e(), Log.wtf() to assumenosideeffects (on top of d/v/i) — total log suppression in releaseFirebaseRelay.kt and ChatRepository.kt no longer print Firebase paths or identifiers in logsStateFlow<TorState> (IDLE, STARTING, BOOTSTRAPPING(%), CONNECTED, ERROR, DISCONNECTED)127.0.0.1:9050 when Tor enabledFialkaApplication.onCreate() starts Tor if enabledstartDestination of nav graph, Tor/Normal choice on first launch?attr/ from active themeHybrid PQXDH (ML-KEM-1024 + X25519), StrongBox DeviceSecurityManager, QR deep link v2, independent fingerprint verification, ratchet desync fixes.
fialka://contact?key=<X25519>&kem=<ML-KEM-1024-pubKey>&name=<displayName> — ML-KEM key encoded in QRstoreDisplayName → no-op), removed from Firebase rulessyncExistingMessages() on contact acceptance to properly trigger PQXDH initConcurrentHashMap.putIfAbsent() + LRU eviction to prevent race conditions on Firebase listenersfingerprintEvent: "verified:<timestamp>" (push only, no state sync)lastDeliveredAt column on ConversationversionCode 5, versionName "3.4.0"One-shot ephemeral photos, redesigned restore screen with BIP-39 grid, QR code fingerprint verification, UI improvements.
oneShotOpened=1 flag in Room (prevents re-viewing); Phase 2: physical file deletion after 5 seconds (delay for viewer app to load)Handler.postDelayed), preventing back-navigation circumventionAutoCompleteTextView cells in 3×8 grid with numberingCustomScannerActivity as contact invitation (torch, free orientation)ignoreCase → ✅ match dialog or ❌ MITM warning dialoggetSharedFingerprintHex() method — New CryptoManager method returning raw SHA-256 hex of sorted public keysoneShotOpened column on MessageLocalflagOneShotOpened() — New DAO query: UPDATE messages SET oneShotOpened = 1 WHERE localId = :messageIdversionCode 6, versionName "3.4.1"/signing_keys/{hash}, /mlkem_keys/{hash}, /inbox/{hash}/{convId} now enforce !data.exists() — prevents key overwrite and contact request replaysenderUid.length === 32, ciphertext non-empty + max 65536, iv non-empty + max 100, createdAt <= now + 60000hkdfExtractExpand() zeros IKM, hkdfExpand() zeros PRK + expandInput after useprivateKeyToMnemonic() and mnemonicToPrivateKey() zero all intermediate byte arrays and clear StringBuilderderiveRootKeyPQXDH() requires both inputs exactly 32 bytesderiveConversationId() uses "|" separator to prevent key concatenation collisionsMainActivity, LockScreenActivity, RestoreFragment, and mnemonic dialog — blocks screenshots, screen recording, task switcherTYPE_TEXT_VARIATION_PASSWORDonDestroyView()parseInvite(): parameter whitelist, length limits, duplicate rejection, control char rejection, Base64 validation, 4000-char maxEXTRA_IS_SENSITIVE flag + 30-second auto-clear via Handler.postDelayedfd.sync()) before File.delete()saveFileLocally() calls fileBytes.fill(0) after writingSecureFileManager.secureDelete()deleteStaleConversation() securely wipes conversation files directorydeleteExpiredMessages() securely deletes associated files firstsendMessage() has require() on all fields (conversationId, ciphertext, iv, senderUid length, createdAt)/^[0-9a-f]{32}$/) and conversationId format{type: "new_message", sync: "1"} — zero metadata leakageMyFirebaseMessagingService shows "Nouveau message reçu" (no sender name, no conversation ID)<application> — blocks all unencrypted HTTPMainActivity and LockScreenActivity — tapjacking protectionresource.metadata['uploaderUid'] == request.auth.uid required for deleteuploadEncryptedFile() attaches uploaderUid StorageMetadataPost-quantum Triple Ratchet (SPQR), alternative ChaCha20-Poly1305 cipher, documented threat model.
DoubleRatchet.pqRatchetStep() function: mixes a fresh ML-KEM secret into rootKey via HKDF (info: Fialka-SPQR-pq-ratchet)PQ_RATCHET_INTERVAL = 10 messages: every 10 messages, the sender performs ML-KEM encaps and upgrades rootKeysendMessage(), when counter reaches 10 and PQXDH is initialized: mlkemEncaps(remoteMlkemPublicKey) → pqRatchetStep(rootKey, ssPQ) → new rootKey + kemCiphertext attached to messagereceiveMessage(), detects kemCiphertext on an already PQ-initialized session: mlkemDecaps() → pqRatchetStep() → rootKey upgraded, counter resetpqRatchetCounter field in RatchetState (Room entity), incremented on every sent messagekemCiphertext), distinguished from initial PQXDH by pqxdhInitialized flagencryptChaCha() / decryptChaCha() — Full implementation via BouncyCastle ChaCha20Poly1305 AEAD (12-byte nonce, 16-byte tag)hasHardwareAes() detects ARMv8 Crypto Extension; ChaCha20 is auto-selected on devices without hardware AES accelerationcipherSuite field — New field in FirebaseMessage (0 = AES-GCM, 1 = ChaCha20); receiver decrypts with the correct algorithm automaticallycipherSuite (= 0) are decrypted with AES-GCM as beforepqRatchetCounter column to RatchetStateversionCode 7, versionName "3.5"Complete Firebase removal, pure P2P infrastructure via Tor Hidden Services, 4-mode offline Mailbox system, "1 Seed → Everything" identity, ML-DSA-44 hybrid post-quantum handshake, delivery pipeline with status badges, 2026 UI refresh.
versionCode 8·versionName "4.0"· Room v24
0xF1 0xA1), 13 frame types (P2P + Mailbox commands), read/write with timeoutTYPE_MESSAGE, TYPE_CONTACT_REQ, TYPE_KEY_BUNDLE, TYPE_CONTACT_REQ_RESPONSE, etc.DeliveryResult enum (DIRECT / MAILBOX / QUEUED)SHA3-256(Ed25519 pubkey) → Base58 (e.g. Fa3x...9Z)libtor.so for real Tor v3 Hidden ServiceskillOrphanedTor() — Reads auth cookie BEFORE deletion (AUTHENTICATE <hex>), prevents orphaned daemonsgetPendingMessages() includes messages stuck in STATUS_SENDING.onion as async mailbox_fetching: StateFlow<Boolean> to block UI during fetchmailboxOnion propagation — senderMailboxOnion included in all outgoing messages; P2PServer updates participantMailboxOnion on receipttotalDeposited, totalFetched, totalDataProcessed persisted in SharedPreferences (blobs deleted after delivery)MessageLocal.deliveryStatus — SENT(0) / MAILBOX(1) / FAILED(2) / PENDING(3)OutboxMessage.messageLocalId — Back-link to MessageLocal for status updatesFAILED messages show a Resend button; OutboxDao.resetRetryForMessage() re-queues the attemptfetchBanner + ProgressBar in chat, input disabled during Mailbox fetchSettingsAdapter + SettingsViewModel, search bar + category filters (Appearance, Notifications, Privacy, Security, Network, About)ConstraintLayout → LinearLayoutEd25519 · ML-DSA-44 · ML-KEM-1024 monospace captionbg_key_box backgroundgetPendingMessages() includes STATUS_SENDING stuck messages (recovery after crash/reboot)resolveRecipientEd25519 fix — Fallback to publicKey if signingPublicKey absentsendContactRequest fix — Returns Boolean, full loggingkillOrphanedTor fix — Reads auth cookie BEFORE deleting the filedeliveryStatus on MessageLocal, messageLocalId + fallbackOnion on OutboxMessage, migrations v18→v24MailboxBlob, MailboxMember, MailboxInvite entities (MAILBOX mode only)versionCode 8, versionName "4.0"Native Rust cryptography migration — all crypto is now executed inside the
fialka-coreRust library, exposed via a JNI bridge. Complete removal of BouncyCastle.
Fialka-Core/src/ffi/mod.rs via the jni = 0.21 crateFialkaNative.kt — Kotlin bridge object, System.loadLibrary("fialka_core"), 30 external funlibfialka_core.so — compiled with cargo-ndk for arm64-v8a (906 KB) and x86_64 (984 KB)Fialka-Core/ integrated as a submodule in Fialka-AndroidCryptoManager.kt — 100% migrated: all BouncyCastle calls replaced with FialkaNativeidentityDerive → 8704-byte bundle (Ed25519, X25519, ML-KEM-1024, ML-DSA-44)encryptAes / decryptAes → FialkaNative.encryptAes / decryptAesencryptChaCha / decryptChaCha → FialkaNative (12-byte nonce, 16-byte tag)encryptFile / decryptFile → FialkaNative (format: key[32] || iv[12] || CT)hmacSha256 → FialkaNative.hmacSha256hkdfZeroSalt / key derivation → FialkaNative.hkdfZeroSalted25519Sign / ed25519Verify → FialkaNativex25519Dh → FialkaNative (strip JCA ASN.1 prefixes: X509 12 bytes, PKCS8 16 bytes)mlkemEncaps / mlkemDecaps → FialkaNativemldsaSign / mldsaVerify → FialkaNativederiveRootKeyPqxdh → FialkaNativecomputeOnion / ed25519ToX25519Raw → FialkaNativeTorTransport.kt — signEd25519 / verifyEd25519 migrated to FialkaNativebuild.gradle.kts — org.bouncycastle:bcprov-jdk18on:1.83 dependency removedandroidTestversionCode 10
versionCode 11·versionName "4.1.0-alpha"· Security fix + test coverage + UX safety
P2PServer.handleContactRequest() — Now verifies the Ed25519 signature on incoming contact requests. Previously, senderSigningPublicKey was received but never verified — anyone could spoof an identity.senderPubKey(UTF-8) || 0x00 || conversationId(UTF-8) || createdAt(big-endian 8 bytes) — domain-separated, deterministicsendContactRequest() — Now signs the payload with FialkaNative.ed25519Sign() and includes requestSignature fieldrequestSignature are still accepted (graceful migration window)CryptoManagerPureTest — 20 tests: deriveConversationId determinism/commutativity/uniqueness, buildSignedData timestamp encoding, hashSenderUid cross-conversation pseudonymityRatchetSimulationTest — 16 tests: bidirectional ratchet, 500-step key uniqueness, SPQR interval=10, pqRatchetStep correctness, initiator/responder symmetryDoubleRatchetTest — 2 JNI-dependent tests marked @Ignore (require libfialka_core.so — must run as instrumented tests)FialkaDatabase.needsDestructiveMigration() — Detects when a Room schema upgrade would trigger fallbackToDestructiveMigration (DROP ALL TABLES)MainActivity — Shows a blocking AlertDialog before any DB access on upgrade: user must explicitly confirm data loss or quitFialkaDatabase.recordCurrentVersion() — Persists schema version in plain SharedPreferences after confirmed openversion.properties — Single source of truth for VERSION_CODE + VERSION_NAME; build.gradle.kts reads it automatically — only edit version.properties to bump versionversionCode 11Replaced
security-cryptowithFialkaSecurePrefs(direct Keystore), fixed SQLCipher 4.14.1 crash, fixedDurationSelectorBottomSheetNPE, full transport reliability (5 fixes).
security-crypto removed — androidx.security:security-crypto:1.1.0-alpha06 and MasterKey.Builder / EncryptedSharedPreferences fully removedFialkaSecurePrefs — New object implementation using the Android Keystore directly: AES-256-GCM, key alias fialka_ks_{name}, prefs file {name}_v2CryptoManager, FialkaDatabase, MailboxDatabase, AppMode, AppLockManager, MailboxClientManager migrated to FialkaSecurePrefs.open()System.loadLibrary("sqlcipher") — sqlcipher-android:4.14.1 removed the static initializer; FialkaApplication.onCreate() now loads the native library firstFialkaDatabase.getInstance() and MailboxDatabase.buildDatabase() also call loadLibrary as a defensive guardcontext parameter removed — DurationSelectorBottomSheet no longer takes an external Context; BottomSheetDialogFragment provides its own contextshow() simplified — Removed fragmentManager.findFragmentById(R.id.nav_host_fragment)?.requireContext()!! which returned null from childFragmentManagerRETRY_INTERVAL_MS 60 s → 15 s; MAX_RETRY_DELAY_MS cap 30 min → 3 min (backoff: 15 s / 30 s / 1 min / 2 min / 3 min)DELIVERY_FAILED on exhausted messages — Messages reaching 50 retries are marked DELIVERY_FAILED BEFORE queue deletion (fixes permanent hourglass ⏳)processOutboxForContact() — If P2P deposit fails, automatically deposits via Mailbox and updates to DELIVERY_MAILBOXbroadcastPresence() — After the offline-contacts loop, processOutbox() is called to sweep queued messagesMailboxClientManager — Base 10 s, 5 s when messages received (+OutboxManager.flushNow()), backoff up to 60 s on errorAdvanced camouflage, plausible deniability, E2E voice messages, sealed sender, messaging improvements.
<activity-alias> in manifest (dynamic enable/disable via PackageManager)FLAG_SECURE on all windowsflagNoPersonalizedLearning on all input fields — keyboard does not learn or log anything