6.7 KiB
6.7 KiB
Encrypted Storage Audit Report
Overview
Security audit of all local data storage in the Kordant Android app, completed as part of Android Production Readiness (Task 07).
Audit Date
2026-06-01
Classification: Sensitive vs Non-Sensitive
Sensitive Data (requires encryption at rest)
| Data | Storage Location | Encryption | Reason |
|---|---|---|---|
| Auth access token | SecureStorageManager (EncryptedSharedPreferences) |
AES-256-GCM | Session credential |
| Auth refresh token | SecureStorageManager (EncryptedSharedPreferences) |
AES-256-GCM | Long-lived credential |
| User profile (name, email, phone) | SecureStorageManager + CacheManager |
AES-256-GCM + AES-256-GCM file | PII |
| Biometric preference | SecureStorageManager (EncryptedSharedPreferences) |
AES-256-GCM | Security-sensitive setting |
| Subscription data | CacheManager (encrypted file) |
AES-256-GCM file | Payment-related info |
| Voice enrollments (biometric prints) | CacheManager (encrypted file) |
AES-256-GCM file | Biometric data |
| FCM device token | SecureStorageManager (EncryptedSharedPreferences) |
AES-256-GCM | Device identifier |
Non-Sensitive Data (plaintext acceptable)
| Data | Storage Location | Notes |
|---|---|---|
| Theme preference (system/light/dark) | UserPreferencesDataStore |
User setting, no PII |
| Dark mode toggle | UserPreferencesDataStore |
User setting |
| Notification preferences | UserPreferencesDataStore |
User setting |
| Language/locale | UserPreferencesDataStore |
User setting |
| Onboarding completion | UserPreferencesDataStore |
App state |
| Watchlist items | CacheManager (plain file) |
External entities being monitored |
| Data exposures | CacheManager (plain file) |
Public data breach records |
| Alerts | CacheManager (plain file) |
Notification records |
| Spam rules | CacheManager (plain file) |
Call filtering rules |
| Properties | CacheManager (plain file) |
Property addresses (public record) |
| Voice analysis results | CacheManager (plain file) |
Analysis output, not raw prints |
| Broker listings | CacheManager (plain file) |
Public broker data |
| Removal requests | CacheManager (plain file) |
Request status |
| Pending request queue | cacheDir/pending_requests.json |
Offline queue (transient) |
Architecture Decisions
1. Two-tier Secure Storage
SecureStorageManager: WrapsEncryptedSharedPreferences(AES-256-GCM, key in Android Keystore) for persistently stored secrets that survive app restarts (auth tokens, biometric pref, cached user profile).CacheManager(encrypted mode): AES-256-GCM file-level encryption for TTL-bounded cached responses containing PII. Uses a derived key (acceptable for transient cache data).
2. DataStore for Non-Sensitive Preferences
UserPreferencesDataStoreusesandroidx.datastore:datastore-preferencesfor non-sensitive user settings.- Replaces in-memory-only preferences (previous state: ViewModels held settings in memory without persistence).
- Excluded from encrypted storage because these settings contain no PII or credentials.
3. Secure Deletion Strategy
- Overwrite-before-remove: Sensitive keys in
SecureStorageManagerare overwritten with random data 3× before removal. - Logout: Clears auth tokens (with overwrite), cache files (with secure delete), and DataStore preferences.
- Account deletion (GDPR):
clearAllData()removes absolutely everything including biometric preferences.
4. Cache Security
- 50 MB size limit: CacheManager enforces a global 50 MB limit with LRU-like eviction (oldest files deleted first).
- Sensitive key encryption: Cache files for
current_user,subscription, andvoice_enrollmentsare AES-256-GCM encrypted. - Secure eviction: When evicting cache files, sensitive ones are overwritten with random data before deletion.
5. Backup Exclusion Strategy
kordant_secure_storage.xmlandkordant_auth_prefs.xml: Excluded from both cloud backup and device transfer because:- Master key is device-bound (Android Keystore), so backup would be undecryptable on another device.
- Auth tokens and PII should not persist across device transfers for security reasons.
kordant_user_preferences.xml(DataStore): Included in backup (non-sensitive settings).- Cache directories: Excluded from backup (rebuilt from API).
- Cached PII files: Explicitly excluded from file-level backup.
Before vs After
SharedPreferences
| Before | After |
|---|---|
Auth tokens in EncryptedSharedPreferences (but duplicated in AuthRepository and AuthInterceptor with separate instances) |
Unified SecureStorageManager singleton accessed via KordantApp.secureStorageManager |
Biometric pref in plain SharedPreferences: kordant_biometric_prefs |
Migrated to EncryptedSharedPreferences via SecureStorageManager |
| No user profile persistence | User profile persisted in SecureStorageManager (encrypted) |
DataStore
| Before | After |
|---|---|
| No DataStore usage (settings were in-memory only in ViewModels) | UserPreferencesDataStore for theme, language, notification preferences, onboarding status |
CacheManager
| Before | After |
|---|---|
All cache files in cacheDir/*.cache in plain JSON |
Sensitive keys (current_user, subscription, voice_enrollments) encrypted with AES-256-GCM on disk |
| No cache size limit | 50 MB limit with automatic eviction |
Simple file.delete() |
Secure overwrite + delete for sensitive cache files |
Backup Rules
| Before | After |
|---|---|
| Default rules (everything included) | Encrypted prefs explicitly excluded; only non-sensitive DataStore included |
Verification Checklist
- All sensitive data in EncryptedSharedPreferences
- Auth tokens encrypted at rest
- Refresh tokens encrypted at rest
- Non-sensitive preferences in DataStore
- No sensitive data in unencrypted cache
- Secure deletion overwriting data
- Sensitive storage excluded from backup
- Logout clears all auth data
- Account deletion removes all local data
- No plaintext sensitive data discoverable in app files
Verification Commands (for QA)
# Check that encrypted prefs file exists and is binary (not plaintext)
# File is at /data/data/com.kordant.android/shared_prefs/kordant_secure_storage.xml
# It should NOT contain plaintext values
# Check that unencrypted cache files don't contain PII
# Files at /data/data/com.kordant.android/cache/*.cache
# grep for email, token, name patterns - should find nothing for sensitive keys
# Verify backup exclusion
# Check backup_rules.xml and data_extraction_rules.xml exclude encrypted prefs