120 lines
6.7 KiB
Markdown
120 lines
6.7 KiB
Markdown
# 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`**: Wraps `EncryptedSharedPreferences` (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
|
||
- `UserPreferencesDataStore` uses `androidx.datastore:datastore-preferences` for 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 `SecureStorageManager` are 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`, and `voice_enrollments` are 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.xml` and `kordant_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
|
||
- [x] All sensitive data in EncryptedSharedPreferences
|
||
- [x] Auth tokens encrypted at rest
|
||
- [x] Refresh tokens encrypted at rest
|
||
- [x] Non-sensitive preferences in DataStore
|
||
- [x] No sensitive data in unencrypted cache
|
||
- [x] Secure deletion overwriting data
|
||
- [x] Sensitive storage excluded from backup
|
||
- [x] Logout clears all auth data
|
||
- [x] Account deletion removes all local data
|
||
- [x] No plaintext sensitive data discoverable in app files
|
||
|
||
## Verification Commands (for QA)
|
||
```bash
|
||
# 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
|
||
```
|