Files
Kordant/docs/encrypted-storage-audit-report.md
2026-06-01 12:58:34 -04:00

120 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```