Encrypt migration export files with AES-256-GCM#467
Merged
torlando-tech merged 8 commits intotorlando-tech:mainfrom Feb 15, 2026
Merged
Encrypt migration export files with AES-256-GCM#467torlando-tech merged 8 commits intotorlando-tech:mainfrom
torlando-tech merged 8 commits intotorlando-tech:mainfrom
Conversation
The .columba export file contains private keys and all user data in plaintext. Since this file transits between devices (email, cloud, etc.), it is the most exposed attack vector. Changes: - Add MigrationCrypto utility: AES-256-GCM encryption with PBKDF2 key derivation (600k iterations, 16-byte salt, 12-byte IV) - Export now requires a user-chosen password (min 8 chars) and encrypts the ZIP before sharing - Import auto-detects encrypted vs legacy (plaintext ZIP) files using magic bytes: 0x02 = encrypted, 0x50 0x4B = legacy ZIP - Password dialog with confirm field for export, single field for import - Wrong password detection via GCM auth tag mismatch - Full backward compatibility: old unencrypted .columba files import without password prompt - 16 new unit tests for MigrationCrypto (round-trip, wrong password, format detection, unicode passwords, edge cases) - All 68 existing migration tests updated and passing
…reen in Codecov - Add MigrationImporterEncryptionTest (8 tests): isEncryptedExport for plaintext/encrypted/empty/invalid files, previewMigration with correct password, wrong password, missing password, and plaintext fallback - Add MigrationScreen.kt to .codecov.yml ignore list (Compose UI screen with no testable business logic, like other ignored UI files) - Total: 76 migration-related tests passing
Replace the .codecov.yml ignore approach with proper test coverage: - Add MigrationScreenPasswordDialogTest (16 tests): title/description display, export/import mode differences, password validation (min length, mismatch), confirm/cancel callbacks, show/hide toggle, disabled button when empty, wrong password error display - Add integration tests: Export button triggers password dialog, PasswordRequired state shows import dialog, WrongPassword state shows error - Make PasswordDialog internal (was private) for direct testability - Revert .codecov.yml to original (no ignore changes needed) - Total: 92 migration-related tests passing
Contributor
Greptile SummaryThis PR adds AES-256-GCM encryption to
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant Screen as MigrationScreen
participant VM as MigrationViewModel
participant Exporter as MigrationExporter
participant Importer as MigrationImporter
participant Crypto as MigrationCrypto
note over User,Crypto: Export Flow
User->>Screen: Tap "Export All Data"
Screen->>Screen: Show PasswordDialog (confirm mode)
User->>Screen: Enter & confirm password
Screen->>VM: exportData(password)
VM->>Exporter: exportData(password, onProgress, includeAttachments)
Exporter->>Exporter: createExportZip(bundle, attachments)
Exporter->>Crypto: encryptFile(zipFile, password)
Crypto->>Crypto: PBKDF2 key derivation (600k iterations)
Crypto->>Crypto: AES-256-GCM encrypt
Crypto-->>Exporter: Encrypted file
Exporter-->>VM: Result(Uri)
VM-->>Screen: ExportComplete state
Screen->>User: Share sheet
note over User,Crypto: Import Flow (Encrypted File)
User->>Screen: Select .columba file
Screen->>VM: previewImport(uri)
VM->>Importer: isEncryptedExport(uri)
Importer->>Crypto: isEncrypted(header)
Crypto-->>Importer: true
Importer-->>VM: Result(true)
VM-->>Screen: PasswordRequired state
Screen->>Screen: Show PasswordDialog (import mode)
User->>Screen: Enter password
Screen->>VM: previewImport(uri, password)
VM->>Importer: previewMigration(uri, password)
Importer->>Crypto: decrypt(rawBytes, password)
Crypto->>Crypto: PBKDF2 + AES-GCM decrypt
Crypto-->>Importer: Decrypted ZIP bytes
Importer-->>VM: PreviewWithData (preview + cached zipBytes)
VM-->>Screen: ImportPreview state
User->>Screen: Confirm import
Screen->>VM: importData(uri, password)
VM->>Importer: importData(uri, password, cachedZipBytes)
Note over Importer: Reuses cached bytes,<br/>skips redundant decryption
Importer-->>VM: ImportResult.Success
VM-->>Screen: ImportComplete state
Last reviewed commit: bb2a84a |
readMigrationBundle now returns the decrypted ZIP bytes alongside the parsed bundle. importAttachments reuses those bytes directly instead of re-reading and re-decrypting the file from the URI. This halves peak memory usage for encrypted imports with attachments.
- ImplicitDefaultLocale: use Locale.ROOT in String.format calls - ThrowsCount: suppress in decrypt() and readMigrationBundle() where multiple distinct error conditions must propagate differently - SwallowedException: log WrongPasswordException and PasswordRequiredException before handling in ViewModel - NoRelaxedMocks: suppress in MigrationImporterEncryptionTest setUp() for infrastructure deps not exercised by encryption tests
CI enforces no new @Suppress("NoRelaxedMocks"). Since the encryption tests only exercise isEncryptedExport and previewMigration, the infrastructure deps (database, protocol, etc.) are never called and strict mocks work without any stubs.
Contributor
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…pted bytes, redact password - Call PBEKeySpec.clearPassword() and zero keyBytes after key derivation to minimize password/key material lingering in heap memory - Cache decrypted ZIP bytes from preview for reuse during import, avoiding redundant PBKDF2 (600k iterations) + AES-GCM decryption on each import - Override toString() on ImportPreview to redact password from debug logs - Add PreviewWithData wrapper to carry decrypted bytes alongside preview - Update all 40 migration tests for new return types Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Owner
|
@greptileai review |
app/src/main/java/com/lxmf/messenger/migration/MigrationCrypto.kt
Outdated
Show resolved
Hide resolved
… in memory Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Owner
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.
Problem
The
.columbaexport file contains private keys, messages, contacts, and all user data in plaintext. Since this file transits between devices (email, cloud storage, messaging apps…), it is the most exposed attack vector.Solution
All
.columbaexports are now encrypted with AES-256-GCM using a user-chosen password, with key derivation via PBKDF2-HMAC-SHA256 (600,000 iterations).Encrypted file format:
Changes
MigrationCrypto.kt(new): AES-256-GCM encryption/decryption utility with PBKDF2 key derivation, format detection, and custom exceptionsMigrationExporter.kt:exportData()now takes apasswordparameter and encrypts the ZIP after creationMigrationImporter.kt: Auto-detects encrypted vs legacy files (ZIP magic bytes0x50 0x4Bvs version byte0x02), decrypts when needed, handles wrong password and missing password errorsMigrationViewModel.kt: NewPasswordRequiredandWrongPasswordUI states, password flow for export and importMigrationScreen.kt: Password dialog with confirmation for export, single-field prompt for import, validation (min 8 chars, mismatch check), wrong password error displayBackward compatibility
Unencrypted
.columbafiles from older versions import without any password prompt — detection is automatic via the first two bytes of the file.Tests
92 migration-related tests passing, including:
MigrationCrypto(round-trip, wrong password, unicode, edge cases)MigrationImporterencryption (format detection, encrypted preview, wrong/missing password)PasswordDialog(validation, modes, callbacks, integration with screen states)