From 30fb3aee8e28dea23f47a3a86d3d04aee17ee46f Mon Sep 17 00:00:00 2001 From: Matt Raymer Date: Sat, 24 May 2025 23:37:09 -0400 Subject: [PATCH] docs: enhance secure storage implementation documentation Add detailed platform implementations, usage examples, and error handling: - Add comprehensive platform-specific implementations for Web (Dexie) and Capacitor (SQLite) - Include detailed database initialization and security features - Add practical usage examples for account and settings management - Document error handling strategies and edge cases - Add concurrency management and data integrity checks - Include platform transition handling This update provides a complete reference for implementing secure storage across different platforms while maintaining backward compatibility. --- docs/secure-storage-implementation.md | 2077 +++++++++++++++++++++++++ 1 file changed, 2077 insertions(+) create mode 100644 docs/secure-storage-implementation.md diff --git a/docs/secure-storage-implementation.md b/docs/secure-storage-implementation.md new file mode 100644 index 00000000..01c3816b --- /dev/null +++ b/docs/secure-storage-implementation.md @@ -0,0 +1,2077 @@ +# Secure Storage Implementation Guide for TimeSafari App + +## Overview + +This document outlines the implementation of secure storage for the TimeSafari app using Capacitor solutions. Two primary storage options are provided: + +1. **SQLite with SQLCipher Encryption (Primary Solution)**: + - Utilizes `@capacitor-community/sqlite` plugin with SQLCipher for 256-bit AES encryption. + - Supports web and native platforms (iOS, Android). + - Ideal for complex queries and relational data. + - Platform-specific SQLCipher implementations: + - **Android**: Native SQLCipher library. + - **iOS**: SQLCipher as a drop-in SQLite replacement. + - **Web**: WebAssembly-based SQLCipher. + +2. **Capacitor Preferences API (For Small Data)**: + - Built-in Capacitor solution for lightweight key-value storage. + - Uses platform-specific secure storage (e.g., Keychain on iOS, EncryptedSharedPreferences on Android). + - Limited to small datasets; no query capabilities. + +## Architecture + +### Directory Structure + +``` +src/services/ +├── storage/ # Storage services +│ ├── SQLiteService.ts # Core SQLite service +│ ├── EncryptionService.ts # SQLCipher encryption handling +│ ├── KeyManagementService.ts # Secure key management +│ ├── platforms/ # Platform-specific implementations +│ │ ├── WebStorageService.ts +│ │ ├── CapacitorStorageService.ts +│ │ └── ElectronStorageService.ts +│ ├── migrations/ # Database migration scripts +│ │ ├── 001_initial.ts # Initial schema setup +│ │ └── 002_encryption.ts # Encryption configuration +│ └── types/ +│ └── storage.types.ts # TypeScript type definitions +└── PlatformService.ts # Existing platform service interface +``` + +### Platform Service Integration + +The storage implementation integrates with the existing `PlatformService` interface to provide platform-specific storage operations. This allows for consistent API usage across platforms while maintaining platform-specific optimizations. + +### Storage Service Interface + +```typescript +// src/services/PlatformService.ts +import { Account } from '../db/tables/accounts'; +import { Contact } from '../db/tables/contacts'; +import { Settings } from '../db/tables/settings'; +import { Secret } from '../db/tables/secret'; + +export interface PlatformService { + // ... existing platform methods ... + + // Storage Operations + // Secret Database + openSecretDatabase(): Promise; + getMasterSecret(): Promise; + setMasterSecret(secret: Secret): Promise; + + // Accounts Database + openAccountsDatabase(): Promise; + getAccountsCount(): Promise; + getAllAccounts(): Promise; + getAccountByDid(did: string): Promise; + addAccount(account: Account): Promise; + updateAccountSettings(did: string, settings: Partial): Promise; + + // Settings Operations + getDefaultSettings(): Promise; + getAccountSettings(did: string): Promise; + updateSettings(key: string, changes: Partial): Promise; + addSettings(settings: Settings): Promise; + getSettingsCount(): Promise; + + // Contacts Operations + getAllContacts(): Promise; + addContact(contact: Contact): Promise; + + // Database Management + deleteDatabase(): Promise; + importDatabase(data: Blob): Promise; + exportDatabase(): Promise; + + // Migration Support + isFirstInstall(): Promise; + needsMigration(): Promise; + performMigration(): Promise; + + // Platform Detection + isCapacitor(): boolean; + isElectron(): boolean; + isWeb(): boolean; + getCapabilities(): { + hasFileSystem: boolean; + hasSecureStorage: boolean; + hasBiometrics: boolean; + isIOS: boolean; + isAndroid: boolean; + }; +} +``` + +### Platform-Specific Implementations + +1. **Web Platform (Dexie)** + ```typescript + // src/services/platforms/WebPlatformService.ts + export class WebPlatformService implements PlatformService { + // ... existing web platform methods ... + + // Secret Database + async openSecretDatabase(): Promise { + await secretDB.open(); + } + + async getMasterSecret(): Promise { + return await secretDB.secret.get(MASTER_SECRET_KEY); + } + + async setMasterSecret(secret: Secret): Promise { + await secretDB.secret.put(secret); + } + + // Accounts Database + async openAccountsDatabase(): Promise { + const accountsDB = await accountsDBPromise; + await accountsDB.open(); + } + + async getAccountsCount(): Promise { + const accountsDB = await accountsDBPromise; + return await accountsDB.accounts.count(); + } + + // ... implement other storage methods using Dexie ... + } + ``` + +2. **Capacitor Platform (SQLite)** + ```typescript + // src/services/platforms/CapacitorPlatformService.ts + export class CapacitorPlatformService implements PlatformService { + private sqliteService: SQLiteService; + private keyManagement: KeyManagementService; + + constructor() { + this.sqliteService = SQLiteService.getInstance(); + this.keyManagement = KeyManagementService.getInstance(); + } + + // Secret Database + async openSecretDatabase(): Promise { + await this.sqliteService.initialize({ + database: 'timesafari_secret.db', + encrypted: true, + version: 1 + }); + } + + async getMasterSecret(): Promise { + const result = await this.sqliteService.query( + 'SELECT * FROM secret WHERE id = ?', + [MASTER_SECRET_KEY] + ); + return result.value?.[0]; + } + + async setMasterSecret(secret: Secret): Promise { + await this.sqliteService.query( + 'INSERT OR REPLACE INTO secret (id, secret) VALUES (?, ?)', + [secret.id, secret.secret] + ); + } + + // Accounts Database + async openAccountsDatabase(): Promise { + await this.sqliteService.initialize({ + database: 'timesafari_accounts.db', + encrypted: true, + version: 1, + key: await this.keyManagement.getEncryptionKey() + }); + } + + async getAccountsCount(): Promise { + const result = await this.sqliteService.query<{ count: number }>( + 'SELECT COUNT(*) as count FROM accounts' + ); + return result.value?.[0]?.count ?? 0; + } + + // ... implement other storage methods using SQLite ... + } + ``` + +### Usage Example + +```typescript +// src/services/storage/StorageService.ts +import { PlatformServiceFactory } from '../PlatformServiceFactory'; + +export class StorageService { + private static instance: StorageService | null = null; + private platformService: PlatformService; + + private constructor() { + this.platformService = PlatformServiceFactory.getInstance(); + } + + static getInstance(): StorageService { + if (!StorageService.instance) { + StorageService.instance = new StorageService(); + } + return StorageService.instance; + } + + async initialize(): Promise { + // Initialize secret database + await this.platformService.openSecretDatabase(); + + // Initialize accounts database + await this.platformService.openAccountsDatabase(); + + // Check if migration is needed + if (await this.platformService.needsMigration()) { + await this.platformService.performMigration(); + } + } + + async getAccountByDid(did: string): Promise { + return await this.platformService.getAccountByDid(did); + } + + async updateAccountSettings(did: string, settings: Partial): Promise { + await this.platformService.updateAccountSettings(did, settings); + } + + // ... implement other storage methods delegating to platformService ... +} +``` + +### Migration Strategy + +The platform service interface supports a smooth migration from Dexie to SQLite: + +1. **Web Platform**: + - Continues using Dexie implementation + - No changes to existing code + - Maintains backward compatibility + +2. **Capacitor Platform**: + - Uses SQLite with SQLCipher + - Implements platform-specific security + - Handles migration from Dexie if needed + +3. **Migration Process**: + ```typescript + // src/services/storage/migration/MigrationService.ts + export class MigrationService { + async migrateFromDexieToSQLite(): Promise { + // 1. Export data from Dexie + const accounts = await this.platformService.getAllAccounts(); + const settings = await this.platformService.getAllSettings(); + const contacts = await this.platformService.getAllContacts(); + + // 2. Initialize SQLite + await this.platformService.openAccountsDatabase(); + + // 3. Import data to SQLite + for (const account of accounts) { + await this.platformService.addAccount(account); + } + + // 4. Import settings and contacts + for (const setting of settings) { + await this.platformService.addSettings(setting); + } + for (const contact of contacts) { + await this.platformService.addContact(contact); + } + + // 5. Verify migration + await this.verifyMigration(); + } + } + ``` + +## Dependencies + +Verified and updated dependencies for the implementation: + +- **Node.js Dependencies**: + ```bash + npm install @capacitor-community/sqlite@6.0.0 + npm install @capacitor/core@6.1.2 + npm install @capacitor/preferences@6.0.2 + npm install @jlongster/sql.js@1.10.3 --save-dev # For web SQLCipher + ``` + +- **Android (build.gradle)**: + ```gradle + implementation 'net.zetetic:android-database-sqlcipher:4.6.1' + ``` + +- **iOS (Podfile)**: + ```ruby + pod 'SQLCipher', '~> 4.6.0' + ``` + +Run `npx cap sync` after installing dependencies to update native projects. + +## Type Definitions + +```typescript +// src/services/storage/types/storage.types.ts + +export interface StorageOptions { + encrypted?: boolean; // Whether to use encryption (default: true) + database: string; // Database name + version: number; // Migration version + key?: string; // Encryption key (optional) +} + +export interface StorageResult { + success: boolean; // Operation success status + error?: string; // Error message if operation failed + value?: T; // Stored/retrieved value +} + +export interface StorageService { + initialize(options: StorageOptions): Promise; + setItem(key: string, value: T): Promise>; + getItem(key: string): Promise>; + removeItem(key: string): Promise>; + query(sql: string, params?: any[]): Promise>; +} +``` + +## Implementation Details + +### 1. SQLite Service + +```typescript +// src/services/storage/SQLiteService.ts +import { CapacitorSQLite, SQLiteConnection, SQLiteDBConnection } from '@capacitor-community/sqlite'; +import { EncryptionService } from './EncryptionService'; + +export class SQLiteService implements StorageService { + private static instance: SQLiteService; + private connection: SQLiteConnection; + private db?: SQLiteDBConnection; + private encryptionService: EncryptionService; + + private constructor() { + this.connection = new SQLiteConnection(CapacitorSQLite); + this.encryptionService = new EncryptionService(); + } + + static getInstance(): SQLiteService { + if (!SQLiteService.instance) { + SQLiteService.instance = new SQLiteService(); + } + return SQLiteService.instance; + } + + async initialize(options: StorageOptions): Promise { + try { + this.db = await this.connection.createConnection( + options.database, + options.encrypted ?? true, + options.encrypted ? 'encryption' : 'no-encryption', + options.version, + false + ); + + await this.db.open(); + + if (options.encrypted) { + const encryptionKey = options.key ?? await this.encryptionService.getEncryptionKey(); + await this.db.execute(`PRAGMA key = '${encryptionKey}'`); + } + + await this.runMigrations(); + } catch (error) { + throw new Error(`Database initialization failed: ${(error as Error).message}`); + } + } + + async setItem(key: string, value: T): Promise> { + if (!this.db) throw new Error('Database not initialized'); + try { + const encryptedValue = await this.encryptionService.encrypt(JSON.stringify(value)); + await this.db.run( + 'INSERT OR REPLACE INTO storage (key, value) VALUES (?, ?)', + [key, encryptedValue] + ); + return { success: true, value }; + } catch (error) { + return { success: false, error: (error as Error).message }; + } + } + + async getItem(key: string): Promise> { + if (!this.db) throw new Error('Database not initialized'); + try { + const result = await this.db.query('SELECT value FROM storage WHERE key = ?', [key]); + if (!result.values?.length) { + return { success: false, error: 'Key not found' }; + } + const decryptedValue = await this.encryptionService.decrypt(result.values[0].value); + return { success: true, value: JSON.parse(decryptedValue) as T }; + } catch (error) { + return { success: false, error: (error as Error).message }; + } + } + + async removeItem(key: string): Promise> { + if (!this.db) throw new Error('Database not initialized'); + try { + await this.db.run('DELETE FROM storage WHERE key = ?', [key]); + return { success: true }; + } catch (error) { + return { success: false, error: (error as Error).message }; + } + } + + async query(sql: string, params: any[] = []): Promise> { + if (!this.db) throw new Error('Database not initialized'); + try { + const result = await this.db.query(sql, params); + return { success: true, value: (result.values || []) as T[] }; + } catch (error) { + return { success: false, error: (error as Error).message }; + } + } + + private async runMigrations(): Promise { + if (!this.db) throw new Error('Database not initialized'); + await this.db.execute(` + CREATE TABLE IF NOT EXISTS storage ( + key TEXT PRIMARY KEY, + value TEXT + ) + `); + // Add additional migration scripts from migrations/ folder as needed + } +} +``` + +### 2. Encryption Service + +```typescript +// src/services/storage/EncryptionService.ts +import { SQLiteDBConnection } from '@capacitor-community/sqlite'; +import { Capacitor } from '@capacitor/core'; + +export class EncryptionService { + private encryptionKey: string | null = null; + + async getEncryptionKey(): Promise { + if (!this.encryptionKey) { + // In a real implementation, use platform-specific secure storage + // This is a simplified example + this.encryptionKey = await this.generateKey(); + } + return this.encryptionKey; + } + + async initialize(db: SQLiteDBConnection): Promise { + const key = await this.getEncryptionKey(); + await db.execute(`PRAGMA key = '${key}'`); + await db.execute('PRAGMA cipher_default_kdf_iter = 64000'); + await db.execute('PRAGMA cipher_page_size = 4096'); + } + + async encrypt(value: string): Promise { + if (Capacitor.isNativePlatform()) { + // Use platform-specific encryption (e.g., iOS Keychain, Android Keystore) + return value; // Placeholder for native encryption + } + // Web Crypto API for web platform + const encoder = new TextEncoder(); + const key = await this.getWebCryptoKey(); + const iv = crypto.getRandomValues(new Uint8Array(12)); + const encrypted = await crypto.subtle.encrypt( + { name: 'AES-GCM', iv }, + key, + encoder.encode(value) + ); + return btoa(String.fromCharCode(...new Uint8Array(iv), ...new Uint8Array(encrypted))); + } + + async decrypt(value: string): Promise { + if (Capacitor.isNativePlatform()) { + // Use platform-specific decryption + return value; // Placeholder for native decryption + } + // Web Crypto API for web platform + const data = Uint8Array.from(atob(value), c => c.charCodeAt(0)); + const iv = data.slice(0, 12); + const encrypted = data.slice(12); + const key = await this.getWebCryptoKey(); + const decrypted = await crypto.subtle.decrypt( + { name: 'AES-GCM', iv }, + key, + encrypted + ); + return new TextDecoder().decode(decrypted); + } + + private async generateKey(): Promise { + const key = crypto.getRandomValues(new Uint8Array(32)); + return btoa(String.fromCharCode(...key)); + } + + private async getWebCryptoKey(): Promise { + const key = await this.getEncryptionKey(); + return crypto.subtle.importKey( + 'raw', + Uint8Array.from(atob(key), c => c.charCodeAt(0)), + { name: 'AES-GCM' }, + false, + ['encrypt', 'decrypt'] + ); + } +} +``` + +### 3. Preferences Service + +```typescript +// src/services/storage/PreferencesService.ts +import { Preferences } from '@capacitor/preferences'; +import { StorageService, StorageResult } from './types/storage.types'; + +export class PreferencesService implements StorageService { + async initialize(): Promise { + // No initialization needed for Preferences API + } + + async setItem(key: string, value: T): Promise> { + try { + await Preferences.set({ key, value: JSON.stringify(value) }); + return { success: true, value }; + } catch (error) { + return { success: false, error: (error as Error).message }; + } + } + + async getItem(key: string): Promise> { + try { + const { value } = await Preferences.get({ key }); + if (!value) { + return { success: false, error: 'Key not found' }; + } + return { success: true, value: JSON.parse(value) as T }; + } catch (error) { + return { success: false, error: (error as Error).message }; + } + } + + async removeItem(key: string): Promise> { + try { + await Preferences.remove({ key }); + return { success: true }; + } catch (error) { + return { success: false, error: (error as Error).message }; + } + } + + async query(): Promise> { + return { success: false, error: 'Query not supported in Preferences API' }; + } +} +``` + +### 4. Key Management Service + +```typescript +// src/services/storage/KeyManagementService.ts +import { Capacitor } from '@capacitor/core'; + +interface SecureKeyOptions { + useBiometrics: boolean; + keySize: number; + keyAlgorithm: 'AES-GCM' | 'AES-CBC'; +} + +export class KeyManagementService { + private static instance: KeyManagementService; + + private constructor() {} + + static getInstance(): KeyManagementService { + if (!KeyManagementService.instance) { + KeyManagementService.instance = new KeyManagementService(); + } + return KeyManagementService.instance; + } + + async generateSecureKey(options: SecureKeyOptions): Promise { + const key = await this.generateRandomKey(options.keySize); + if (Capacitor.isNativePlatform()) { + return Capacitor.getPlatform() === 'ios' + ? this.encryptWithSecureEnclave(key, options) + : this.encryptWithAndroidKeystore(key, options); + } + return this.encryptWithWebCrypto(key, options); + } + + private async generateRandomKey(keySize: number): Promise { + const key = crypto.getRandomValues(new Uint8Array(keySize / 8)); + return btoa(String.fromCharCode(...key)); + } + + private async encryptWithSecureEnclave(key: string, options: SecureKeyOptions): Promise { + // iOS Secure Enclave implementation (placeholder) + // Use Keychain with biometric protection + return key; // Implement platform-specific code + } + + private async encryptWithAndroidKeystore(key: string, options: SecureKeyOptions): Promise { + // Android Keystore implementation (placeholder) + // Use EncryptedSharedPreferences with biometric protection + return key; // Implement platform-specific code + } + + private async encryptWithWebCrypto(key: string, options: SecureKeyOptions): Promise { + // Web Crypto API implementation + const encoder = new TextEncoder(); + const cryptoKey = await crypto.subtle.generateKey( + { name: options.keyAlgorithm, length: options.keySize }, + true, + ['encrypt', 'decrypt'] + ); + const exportedKey = await crypto.subtle.exportKey('raw', cryptoKey); + return btoa(String.fromCharCode(...new Uint8Array(exportedKey))); + } +} +``` + +### 5. Biometric Service + +```typescript +// src/services/storage/BiometricService.ts +import { Capacitor } from '@capacitor/core'; + +export class BiometricService { + async authenticate(): Promise { + if (!Capacitor.isNativePlatform()) { + return true; // Web fallback (no biometric auth) + } + try { + // Use Capacitor Biometric plugin (e.g., @capacitor-community/biometric-auth) + // Placeholder for actual implementation + return true; + } catch (error) { + console.error('Biometric authentication failed:', error); + return false; + } + } +} +``` + +## Migration Strategy + +### Two-Day Implementation Timeline (Revised) + +#### Day 1: Core Implementation and Basic Security + +**Morning (4 hours)**: +1. **Essential Setup (1 hour)** + ```bash + # Install core dependencies + npm install @capacitor-community/sqlite@6.0.0 @capacitor/core@6.1.2 + npx cap sync + ``` + +2. **Core Services Implementation (3 hours)** + - Implement basic `SQLiteService` with encryption support + - Create simplified `KeyManagementService` for platform-specific key storage + - Set up initial database schema + ```typescript + // Priority 1: Core tables + export const initialSchema = ` + CREATE TABLE IF NOT EXISTS accounts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + did TEXT UNIQUE, + publicKeyHex TEXT, + identity TEXT + ); + CREATE TABLE IF NOT EXISTS secret ( + id INTEGER PRIMARY KEY, + secret TEXT + ); + `; + ``` + +**Afternoon (4 hours)**: +1. **Platform-Specific Security (2 hours)** + - Implement basic platform detection + - Set up platform-specific key storage: + - iOS: Basic Keychain integration + - Android: Basic Keystore integration + - Web: Web Crypto API with IndexedDB + - Defer advanced features (Secure Enclave, StrongBox) to post-migration + +2. **Migration Utilities (2 hours)** + - Create basic data export from Dexie + - Implement simple data import to SQLite + - Add basic validation + ```typescript + // Simplified migration utility + export class MigrationUtils { + async migrateData(): Promise { + // 1. Export from Dexie + const data = await this.exportFromDexie(); + + // 2. Initialize SQLite + await this.initializeSQLite(); + + // 3. Import data + await this.importToSQLite(data); + + // 4. Basic validation + await this.validateMigration(); + } + } + ``` + +#### Day 2: Migration and Testing + +**Morning (4 hours)**: +1. **Migration Implementation (2 hours)** + - Implement migration script + - Add basic error handling + - Create rollback capability + ```typescript + export class DatabaseMigration { + async migrate(): Promise { + try { + // 1. Backup existing data + await this.backupExistingData(); + + // 2. Perform migration + await this.migrationUtils.migrateData(); + + // 3. Verify migration + const isValid = await this.verifyMigration(); + if (!isValid) { + await this.rollback(); + throw new Error('Migration validation failed'); + } + } catch (error) { + await this.rollback(); + throw error; + } + } + } + ``` + +2. **Basic Security Integration (2 hours)** + - Implement basic biometric check + - Add simple key derivation + - Set up basic encryption + ```typescript + export class SecurityService { + async initialize(): Promise { + // Basic security setup + await this.setupEncryption(); + await this.setupBiometrics(); + } + } + ``` + +**Afternoon (4 hours)**: +1. **Testing and Validation (2 hours)** + - Unit tests for core functionality + - Basic integration tests + - Platform-specific tests + ```typescript + describe('Database Migration', () => { + it('should migrate accounts successfully', async () => { + // Basic migration test + }); + + it('should handle encryption correctly', async () => { + // Basic encryption test + }); + }); + ``` + +2. **Documentation and Cleanup (2 hours)** + - Update documentation + - Clean up code + - Create basic backup/restore functionality + +### Post-Migration Features (To Be Implemented Later) + +1. **Enhanced Security** + - Advanced platform-specific features (Secure Enclave, StrongBox) + - Sophisticated key rotation + - Advanced biometric integration + +2. **Advanced Backup** + - Encrypted backup system + - Recovery key management + - Cross-device backup + +3. **Performance Optimization** + - Query optimization + - Index management + - Caching strategies + +### Critical Path Items (Detailed Breakdown) + +#### Build System Integration (Day 1 Morning) + +1. **Vite Configuration for Capacitor** + ```typescript + // vite.config.ts + import { defineConfig } from 'vite'; + import { Capacitor } from '@capacitor/core'; + + export default defineConfig({ + // ... existing config ... + build: { + // Ensure proper bundling for Capacitor + target: 'es2015', + rollupOptions: { + output: { + // Handle platform-specific code + format: 'es', + manualChunks: { + 'capacitor-core': ['@capacitor/core'], + 'sqlite': ['@capacitor-community/sqlite'] + } + } + } + }, + plugins: [ + // ... existing plugins ... + { + name: 'capacitor-platform', + config(config, { command }) { + // Add platform-specific environment variables + return { + define: { + 'process.env.CAPACITOR_PLATFORM': JSON.stringify(Capacitor.getPlatform()) + } + }; + } + } + ] + }); + ``` + +2. **Platform Detection and Conditional Imports** + ```typescript + // src/services/storage/platform-detection.ts + import { Capacitor } from '@capacitor/core'; + + export const isNativePlatform = Capacitor.isNativePlatform(); + export const platform = Capacitor.getPlatform(); + + // Conditional imports for platform-specific code + export const getPlatformService = async () => { + if (!isNativePlatform) { + return null; // Web platform uses existing implementation + } + + switch (platform) { + case 'ios': + return (await import('./platforms/ios')).IOSStorageService; + case 'android': + return (await import('./platforms/android')).AndroidStorageService; + default: + throw new Error(`Unsupported platform: ${platform}`); + } + }; + ``` + +#### Day 1 Critical Path + +1. **Core Database Setup (Morning)** + - [ ] SQLite plugin installation and configuration + - [ ] Basic database schema implementation + - [ ] Platform detection integration + - [ ] Vite build configuration for Capacitor + - [ ] Basic encryption setup + +2. **Platform-Specific Implementation (Afternoon)** + - [ ] iOS Keychain integration + - Basic key storage + - Error handling + - Platform detection + - [ ] Android Keystore integration + - Basic key storage + - Error handling + - Platform detection + - [ ] Web platform detection + - Skip implementation for web + - Maintain existing Dexie implementation + +3. **Migration Utilities (Afternoon)** + - [ ] Data export from Dexie + - Account data + - Secret data + - Basic validation + - [ ] SQLite import + - Table creation + - Data import + - Basic error handling + +#### Day 2 Critical Path + +1. **Migration Implementation (Morning)** + - [ ] Migration script + ```typescript + // src/services/storage/migration/MigrationScript.ts + export class MigrationScript { + async execute(): Promise { + // 1. Platform check + if (!isNativePlatform) { + console.log('Skipping migration on web platform'); + return; + } + + // 2. Backup + await this.backupExistingData(); + + // 3. Migration + await this.performMigration(); + + // 4. Verification + await this.verifyMigration(); + } + } + ``` + - [ ] Rollback mechanism + - [ ] Error handling + - [ ] Platform-specific validation + +2. **Security Integration (Morning)** + - [ ] Basic biometric check + - [ ] Key derivation + - [ ] Platform-specific encryption + - [ ] Error handling for security features + +3. **Testing and Validation (Afternoon)** + - [ ] Unit tests + ```typescript + // src/services/storage/__tests__/StorageService.spec.ts + describe('StorageService', () => { + it('should use SQLite on native platforms', async () => { + if (isNativePlatform) { + const service = await getPlatformService(); + expect(service).toBeDefined(); + // ... more tests + } + }); + + it('should skip migration on web', async () => { + if (!isNativePlatform) { + const migration = new MigrationScript(); + await migration.execute(); + // Verify web implementation unchanged + } + }); + }); + ``` + - [ ] Integration tests + - [ ] Platform-specific tests + - [ ] Migration validation + +4. **Documentation and Cleanup (Afternoon)** + - [ ] Update documentation + - [ ] Code cleanup + - [ ] Basic backup/restore + - [ ] Build system verification + +### Build System Integration Details + +1. **Vite Configuration for Capacitor** + ```typescript + // vite.config.ts + import { defineConfig } from 'vite'; + import { Capacitor } from '@capacitor/core'; + + const capacitorConfig = { + // Platform-specific entry points + input: { + main: 'src/main.ts', + capacitor: 'src/capacitor.ts' + }, + // Platform-specific output + output: { + format: 'es', + dir: 'dist', + entryFileNames: (chunkInfo) => { + return chunkInfo.name === 'capacitor' + ? 'capacitor/[name].[hash].js' + : '[name].[hash].js'; + } + } + }; + + export default defineConfig({ + // ... existing config ... + build: { + ...capacitorConfig, + rollupOptions: { + external: [ + // External dependencies for Capacitor + '@capacitor/core', + '@capacitor-community/sqlite' + ], + output: { + globals: { + '@capacitor/core': 'Capacitor', + '@capacitor-community/sqlite': 'CapacitorSQLite' + } + } + } + } + }); + ``` + +2. **Platform-Specific Entry Point** + ```typescript + // src/capacitor.ts + import { Capacitor } from '@capacitor/core'; + import { SQLiteService } from './services/storage/SQLiteService'; + + // Only initialize Capacitor-specific services on native platforms + if (Capacitor.isNativePlatform()) { + const initializeCapacitor = async () => { + const sqliteService = SQLiteService.getInstance(); + await sqliteService.initialize({ + database: 'timesafari.db', + encrypted: true, + version: 1 + }); + }; + + initializeCapacitor().catch(console.error); + } + ``` + +3. **Build Scripts** + ```json + // package.json + { + "scripts": { + "build": "vite build", + "build:capacitor": "vite build --mode capacitor", + "build:ios": "vite build --mode capacitor --platform ios", + "build:android": "vite build --mode capacitor --platform android", + "cap:sync": "npm run build:capacitor && npx cap sync", + "cap:ios": "npm run build:ios && npx cap sync ios", + "cap:android": "npm run build:android && npx cap sync android" + } + } + ``` + +4. **Environment Configuration** + ```typescript + // src/config/environment.ts + import { Capacitor } from '@capacitor/core'; + + export const environment = { + isNative: Capacitor.isNativePlatform(), + platform: Capacitor.getPlatform(), + storage: { + type: Capacitor.isNativePlatform() ? 'sqlite' : 'dexie', + // Platform-specific configuration + sqlite: { + database: 'timesafari.db', + encrypted: true, + version: 1 + } + } + }; + ``` + +### Success Criteria (Updated) + +1. **Build System** + - [ ] Vite configuration properly handles Capacitor builds + - [ ] Platform-specific code is correctly bundled + - [ ] Web implementation remains unchanged + - [ ] Build scripts work for all platforms + +2. **Day 1** + - [ ] SQLite database operational on native platforms + - [ ] Basic encryption working + - [ ] Migration utilities ready + - [ ] Platform detection working + - [ ] Build system integration complete + +3. **Day 2** + - [ ] Successful migration on native platforms + - [ ] Basic security implemented + - [ ] Core functionality tested + - [ ] Rollback capability verified + - [ ] Web implementation unaffected + +## Security Considerations + +- **Key Management**: Keys stored in iOS Keychain, Android Keystore, or Web Crypto API. Never stored in plaintext. +- **Data Protection**: 256-bit AES-GCM encryption via SQLCipher. Data bound to device and user authentication. +- **Platform-Specific**: + - **iOS**: Secure Enclave and Keychain with Face ID/Touch ID. + - **Android**: Android Keystore with BiometricPrompt. + - **Web**: Web Crypto API with secure storage fallback. + +## Performance Considerations + +- Use transactions for batch operations. +- Implement proper indexing for query performance. +- Cache frequently accessed data. +- Monitor memory usage and optimize large datasets. + +## Future Improvements + +- Key rotation support. +- Backup and restore capabilities. +- Enhanced biometric options. +- Cross-device synchronization. + +## Maintenance + +- Regular security audits. +- Platform-specific updates. +- Dependency management with semantic versioning. +- Performance monitoring. + +## Platform-Specific Implementation Details + +### iOS Implementation + +1. **Secure Enclave Integration** + ```typescript + // src/services/storage/platforms/ios/SecureEnclaveService.ts + import { Capacitor } from '@capacitor/core'; + import { Keychain } from '@capacitor-community/native-keychain'; + + export class SecureEnclaveService { + private static readonly KEYCHAIN_SERVICE = 'com.timesafari.securestorage'; + private static readonly KEYCHAIN_ACCESS_GROUP = 'group.com.timesafari.securestorage'; + + async storeKey(key: string, options: SecureKeyOptions): Promise { + const accessControl = { + // Require device to be unlocked + accessible: Keychain.Accessible.WHEN_UNLOCKED, + // Use Secure Enclave + accessControl: Keychain.AccessControl.BIOMETRY_ANY, + // Require user presence + authenticationType: Keychain.AuthenticationType.BIOMETRICS, + // Keychain access group for app extension sharing + accessGroup: this.KEYCHAIN_ACCESS_GROUP + }; + + await Keychain.set({ + key: 'sqlite_encryption_key', + value: key, + service: this.KEYCHAIN_SERVICE, + ...accessControl + }); + } + + async retrieveKey(): Promise { + const result = await Keychain.get({ + key: 'sqlite_encryption_key', + service: this.KEYCHAIN_SERVICE + }); + + if (!result.value) { + throw new Error('Encryption key not found in Keychain'); + } + + return result.value; + } + } + ``` + +2. **Face ID/Touch ID Integration** + ```typescript + // src/services/storage/platforms/ios/BiometricService.ts + import { BiometricAuth } from '@capacitor-community/biometric-auth'; + + export class IOSBiometricService { + async authenticate(): Promise { + const available = await BiometricAuth.isAvailable(); + if (!available.has) { + return false; + } + + try { + const result = await BiometricAuth.verify({ + reason: 'Authenticate to access secure data', + title: 'TimeSafari Authentication', + subtitle: 'Verify your identity', + description: 'Use Face ID or Touch ID to access your secure data', + negativeButtonText: 'Cancel' + }); + + return result.verified; + } catch (error) { + console.error('Biometric authentication failed:', error); + return false; + } + } + } + ``` + +### Android Implementation + +1. **Android Keystore Integration** + ```typescript + // src/services/storage/platforms/android/KeystoreService.ts + import { AndroidKeystore } from '@capacitor-community/android-keystore'; + + export class AndroidKeystoreService { + private static readonly KEY_ALIAS = 'timesafari_sqlite_key'; + + async storeKey(key: string, options: SecureKeyOptions): Promise { + const keyGenParameterSpec = { + keyAlias: this.KEY_ALIAS, + purposes: ['ENCRYPT', 'DECRYPT'], + blockModes: ['GCM'], + encryptionPaddings: ['NoPadding'], + keySize: 256, + userAuthenticationRequired: true, + userAuthenticationValidityDurationSeconds: -1, + // Use StrongBox if available + isStrongBoxBacked: true + }; + + await AndroidKeystore.generateKey(keyGenParameterSpec); + await AndroidKeystore.encrypt({ + keyAlias: this.KEY_ALIAS, + data: key + }); + } + + async retrieveKey(): Promise { + const result = await AndroidKeystore.decrypt({ + keyAlias: this.KEY_ALIAS + }); + + return result.decryptedData; + } + } + ``` + +2. **Biometric Integration** + ```typescript + // src/services/storage/platforms/android/BiometricService.ts + import { BiometricAuth } from '@capacitor-community/biometric-auth'; + + export class AndroidBiometricService { + async authenticate(): Promise { + const available = await BiometricAuth.isAvailable(); + if (!available.has) { + return false; + } + + try { + const result = await BiometricAuth.verify({ + reason: 'Authenticate to access secure data', + title: 'TimeSafari Authentication', + subtitle: 'Verify your identity', + description: 'Use biometric authentication to access your secure data', + negativeButtonText: 'Cancel', + // Android-specific options + allowDeviceCredential: true + }); + + return result.verified; + } catch (error) { + console.error('Biometric authentication failed:', error); + return false; + } + } + } + ``` + +### Web Implementation + +1. **Web Crypto API Integration** + ```typescript + // src/services/storage/platforms/web/WebCryptoService.ts + export class WebCryptoService { + private static readonly KEY_NAME = 'timesafari_sqlite_key'; + + async storeKey(key: string): Promise { + // Generate a master key for encrypting the SQLite key + const masterKey = await crypto.subtle.generateKey( + { name: 'AES-GCM', length: 256 }, + true, + ['encrypt', 'decrypt'] + ); + + // Encrypt the SQLite key + const iv = crypto.getRandomValues(new Uint8Array(12)); + const encryptedKey = await crypto.subtle.encrypt( + { name: 'AES-GCM', iv }, + masterKey, + new TextEncoder().encode(key) + ); + + // Store encrypted key in IndexedDB + const db = await this.getSecureDB(); + await db.put('keys', { + name: this.KEY_NAME, + iv, + data: encryptedKey + }); + + // Export and store master key in secure storage + const exportedKey = await crypto.subtle.exportKey('raw', masterKey); + await this.storeMasterKey(exportedKey); + } + + private async getSecureDB(): Promise { + // Implementation using IndexedDB with encryption + } + + private async storeMasterKey(key: ArrayBuffer): Promise { + // Store in secure storage (e.g., localStorage with encryption) + } + } + ``` + +## Key Rotation and Backup Process + +### Key Rotation + +1. **Automatic Key Rotation** + ```typescript + // src/services/storage/KeyRotationService.ts + export class KeyRotationService { + private static readonly ROTATION_INTERVAL = 30 * 24 * 60 * 60 * 1000; // 30 days + + async checkAndRotateKey(): Promise { + const lastRotation = await this.getLastRotationDate(); + if (Date.now() - lastRotation > this.ROTATION_INTERVAL) { + await this.rotateKey(); + } + } + + private async rotateKey(): Promise { + // 1. Generate new key + const newKey = await this.generateNewKey(); + + // 2. Re-encrypt database with new key + await this.reencryptDatabase(newKey); + + // 3. Store new key securely + await this.storeNewKey(newKey); + + // 4. Update rotation timestamp + await this.updateRotationDate(); + } + + private async reencryptDatabase(newKey: string): Promise { + const sqliteService = SQLiteService.getInstance(); + + // Export all data + const data = await sqliteService.exportAllData(); + + // Create new database with new key + await sqliteService.initialize({ + database: 'timesafari_new.db', + encrypted: true, + version: 1, + key: newKey + }); + + // Import data to new database + await sqliteService.importData(data); + + // Verify data integrity + await this.verifyDataIntegrity(); + + // Replace old database with new one + await sqliteService.replaceDatabase('timesafari_new.db', 'timesafari.db'); + } + } + ``` + +2. **Manual Key Rotation** + ```typescript + // src/services/storage/KeyRotationService.ts + export class KeyRotationService { + async manualRotateKey(): Promise { + // Require user authentication + const biometrics = new BiometricService(); + const authenticated = await biometrics.authenticate(); + if (!authenticated) { + throw new Error('Authentication required for key rotation'); + } + + await this.rotateKey(); + } + } + ``` + +### Backup Process + +1. **Secure Backup** + ```typescript + // src/services/storage/BackupService.ts + export class BackupService { + async createBackup(): Promise { + // 1. Export database + const data = await this.exportDatabase(); + + // 2. Export encryption keys + const keys = await this.exportKeys(); + + // 3. Create encrypted backup + const backup = await this.encryptBackup(data, keys); + + // 4. Store backup securely + return this.storeBackup(backup); + } + + private async exportDatabase(): Promise { + const sqliteService = SQLiteService.getInstance(); + return { + version: await sqliteService.getVersion(), + tables: await sqliteService.exportTables(), + metadata: await sqliteService.getMetadata() + }; + } + + private async exportKeys(): Promise { + const keyManagement = KeyManagementService.getInstance(); + return { + sqliteKey: await keyManagement.exportKey(), + backupKey: await this.generateBackupKey() + }; + } + + private async encryptBackup( + data: DatabaseExport, + keys: KeyExport + ): Promise { + // Encrypt data with backup key + const encryptedData = await this.encryptData(data, keys.backupKey); + + // Encrypt backup key with user's recovery key + const encryptedBackupKey = await this.encryptBackupKey( + keys.backupKey, + await this.getRecoveryKey() + ); + + return { + data: encryptedData, + backupKey: encryptedBackupKey, + timestamp: Date.now(), + version: '1.0' + }; + } + } + ``` + +2. **Restore Process** + ```typescript + // src/services/storage/BackupService.ts + export class BackupService { + async restoreBackup(backup: EncryptedBackup): Promise { + // 1. Verify backup integrity + await this.verifyBackup(backup); + + // 2. Decrypt backup key + const backupKey = await this.decryptBackupKey( + backup.backupKey, + await this.getRecoveryKey() + ); + + // 3. Decrypt data + const data = await this.decryptData(backup.data, backupKey); + + // 4. Restore database + await this.restoreDatabase(data); + + // 5. Verify restoration + await this.verifyRestoration(); + } + } + ``` + +## Integration with Existing Security Model + +1. **Account Security Integration** + ```typescript + // src/services/security/AccountSecurityService.ts + export class AccountSecurityService { + private storageService: StorageService; + private keyManagement: KeyManagementService; + + async initialize(): Promise { + this.storageService = await StorageServiceFactory.create(); + this.keyManagement = KeyManagementService.getInstance(); + + // Link SQLite encryption to account security + await this.linkAccountSecurity(); + } + + private async linkAccountSecurity(): Promise { + const account = await this.storageService.getActiveAccount(); + if (account) { + // Derive SQLite key from account credentials + const sqliteKey = await this.deriveSQLiteKey(account); + + // Store derived key securely + await this.keyManagement.storeKey(sqliteKey, { + useBiometrics: true, + keySize: 256, + keyAlgorithm: 'AES-GCM' + }); + } + } + + private async deriveSQLiteKey(account: Account): Promise { + // Use account credentials to derive SQLite key + const input = `${account.did}:${account.publicKeyHex}`; + return this.keyManagement.deriveKey(input); + } + } + ``` + +2. **Biometric Integration** + ```typescript + // src/services/security/BiometricSecurityService.ts + export class BiometricSecurityService { + async setupBiometricProtection(): Promise { + const biometrics = new BiometricService(); + const available = await biometrics.isAvailable(); + + if (available) { + // Enable biometric protection for SQLite + await this.keyManagement.updateKeyProtection({ + useBiometrics: true, + requireAuthentication: true + }); + + // Update app settings + await this.storageService.updateSettings({ + biometricProtection: true, + lastBiometricSetup: Date.now() + }); + } + } + } + ``` + +3. **Migration from Existing Security** + ```typescript + // src/services/security/SecurityMigrationService.ts + export class SecurityMigrationService { + async migrateToNewSecurity(): Promise { + // 1. Export existing secure data + const existingData = await this.exportExistingData(); + + // 2. Initialize new security model + await this.initializeNewSecurity(); + + // 3. Import data with new security + await this.importWithNewSecurity(existingData); + + // 4. Verify migration + await this.verifySecurityMigration(); + } + + private async exportExistingData(): Promise { + // Export data from existing secure storage + const accounts = await this.exportAccounts(); + const secrets = await this.exportSecrets(); + const settings = await this.exportSettings(); + + return { accounts, secrets, settings }; + } + + private async initializeNewSecurity(): Promise { + // Set up new security infrastructure + await this.keyManagement.initialize(); + await this.storageService.initialize({ + database: 'timesafari.db', + encrypted: true, + version: 1 + }); + } + } + ``` + +### Detailed Platform Implementations + +#### Web Platform (Dexie) + +1. **Database Initialization** + ```typescript + // src/services/platforms/WebPlatformService.ts + export class WebPlatformService implements PlatformService { + private secretDB: Dexie; + private accountsDB: Dexie | null = null; + + constructor() { + // Initialize secret database + this.secretDB = new Dexie('TimeSafariSecret'); + this.secretDB.version(1).stores({ + secret: 'id, secret' + }); + } + + async openAccountsDatabase(): Promise { + if (!this.accountsDB) { + this.accountsDB = new Dexie('TimeSafariAccounts'); + this.accountsDB.version(1).stores({ + accounts: '++id, dateCreated, derivationPath, did, $identity, $mnemonic, publicKeyHex', + settings: '++id, accountDid, *keys', + contacts: '++id, did, name, *keys' + }); + + // Apply encryption using master secret + const secret = await this.getMasterSecret(); + if (secret) { + encrypted(this.accountsDB, { secretKey: secret.secret }); + } + } + await this.accountsDB.open(); + } + + async getAccountByDid(did: string): Promise { + if (!this.accountsDB) { + throw new Error('Accounts database not initialized'); + } + return await this.accountsDB.accounts + .where('did') + .equals(did) + .first(); + } + + // ... other methods ... + } + ``` + +2. **Error Handling** + ```typescript + // src/services/platforms/WebPlatformService.ts + export class WebPlatformService implements PlatformService { + private async handleDatabaseError(operation: string, error: unknown): Promise { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + logger.error(`Database error during ${operation}:`, error); + + if (error instanceof DexieError) { + switch (error.name) { + case 'QuotaExceededError': + throw new Error('Storage quota exceeded. Please clear some space and try again.'); + case 'InvalidTableError': + throw new Error('Database schema mismatch. Please try clearing your browser data.'); + case 'ConstraintError': + throw new Error('Operation would violate database constraints.'); + default: + throw new Error(`Database error: ${errorMessage}`); + } + } + + throw new Error(`Failed to ${operation}: ${errorMessage}`); + } + + async addAccount(account: Account): Promise { + try { + if (!this.accountsDB) { + throw new Error('Accounts database not initialized'); + } + await this.accountsDB.accounts.add(account); + } catch (error) { + await this.handleDatabaseError('add account', error); + } + } + } + ``` + +#### Capacitor Platform (SQLite) + +1. **Database Initialization with Security** + ```typescript + // src/services/platforms/CapacitorPlatformService.ts + export class CapacitorPlatformService implements PlatformService { + private sqliteService: SQLiteService; + private keyManagement: KeyManagementService; + private biometricService: BiometricService; + + constructor() { + this.sqliteService = SQLiteService.getInstance(); + this.keyManagement = KeyManagementService.getInstance(); + this.biometricService = BiometricService.getInstance(); + } + + async openAccountsDatabase(): Promise { + try { + // Check biometric authentication if required + if (await this.shouldRequireBiometrics()) { + const authenticated = await this.biometricService.authenticate(); + if (!authenticated) { + throw new Error('Biometric authentication required'); + } + } + + // Get encryption key from secure storage + const key = await this.keyManagement.getEncryptionKey(); + + // Initialize SQLite with encryption + await this.sqliteService.initialize({ + database: 'timesafari_accounts.db', + encrypted: true, + version: 1, + key + }); + + // Set up database schema + await this.setupDatabaseSchema(); + } catch (error) { + await this.handleDatabaseError('open accounts database', error); + } + } + + private async setupDatabaseSchema(): Promise { + const schema = ` + CREATE TABLE IF NOT EXISTS accounts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + dateCreated TEXT NOT NULL, + derivationPath TEXT, + did TEXT UNIQUE NOT NULL, + identity TEXT, + mnemonic TEXT, + publicKeyHex TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS settings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + accountDid TEXT NOT NULL, + key TEXT NOT NULL, + value TEXT, + FOREIGN KEY (accountDid) REFERENCES accounts(did) + ); + CREATE TABLE IF NOT EXISTS contacts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + did TEXT UNIQUE NOT NULL, + name TEXT, + keys TEXT + ); + `; + await this.sqliteService.execute(schema); + } + + // ... other methods ... + } + ``` + +2. **Platform-Specific Security** + ```typescript + // src/services/platforms/CapacitorPlatformService.ts + export class CapacitorPlatformService implements PlatformService { + private async shouldRequireBiometrics(): Promise { + const capabilities = this.getCapabilities(); + if (!capabilities.hasBiometrics) { + return false; + } + + // Check user preferences + const settings = await this.getDefaultSettings(); + return settings?.requireBiometrics ?? false; + } + + private async getPlatformKey(): Promise { + const capabilities = this.getCapabilities(); + + if (capabilities.isIOS) { + // Use iOS Keychain with Secure Enclave + return await this.keyManagement.getIOSKey({ + useSecureEnclave: true, + requireBiometrics: await this.shouldRequireBiometrics() + }); + } else if (capabilities.isAndroid) { + // Use Android Keystore + return await this.keyManagement.getAndroidKey({ + useStrongBox: true, + requireBiometrics: await this.shouldRequireBiometrics() + }); + } + + throw new Error('Unsupported platform for secure key storage'); + } + } + ``` + +### Usage Examples + +1. **Account Management** + ```typescript + // src/services/AccountService.ts + export class AccountService { + constructor(private platformService: PlatformService) {} + + async createNewAccount(mnemonic: string): Promise { + try { + // Generate account details + const [address, privateHex, publicHex, derivationPath] = deriveAddress(mnemonic); + const account: Account = { + dateCreated: new Date().toISOString(), + derivationPath, + did: `did:eth:${address}`, + identity: JSON.stringify({ address, privateHex, publicHex }), + mnemonic, + publicKeyHex: publicHex + }; + + // Save account + await this.platformService.addAccount(account); + + // Set as active account + await this.platformService.updateSettings('activeDid', { value: account.did }); + + return account; + } catch (error) { + if (error instanceof Error) { + throw new Error(`Failed to create account: ${error.message}`); + } + throw error; + } + } + + async getActiveAccount(): Promise { + const settings = await this.platformService.getDefaultSettings(); + if (!settings?.activeDid) { + return undefined; + } + return await this.platformService.getAccountByDid(settings.activeDid); + } + } + ``` + +2. **Settings Management** + ```typescript + // src/services/SettingsService.ts + export class SettingsService { + constructor(private platformService: PlatformService) {} + + async updateAccountSettings(did: string, changes: Partial): Promise { + try { + // Verify account exists + const account = await this.platformService.getAccountByDid(did); + if (!account) { + throw new Error(`Account ${did} not found`); + } + + // Update settings + await this.platformService.updateAccountSettings(did, changes); + + // Log changes for audit + await this.logSettingsChange(did, changes); + } catch (error) { + await this.handleSettingsError('update account settings', error); + } + } + + private async logSettingsChange(did: string, changes: Partial): Promise { + // Implementation for audit logging + } + } + ``` + +### Error Handling and Edge Cases + +1. **Common Error Scenarios** + ```typescript + // src/services/storage/errors/StorageError.ts + export class StorageError extends Error { + constructor( + message: string, + public readonly code: string, + public readonly originalError?: unknown + ) { + super(message); + this.name = 'StorageError'; + } + } + + export const StorageErrorCodes = { + DATABASE_NOT_INITIALIZED: 'DB_NOT_INIT', + ENCRYPTION_FAILED: 'ENC_FAILED', + DECRYPTION_FAILED: 'DEC_FAILED', + QUOTA_EXCEEDED: 'QUOTA_EXCEEDED', + BIOMETRIC_REQUIRED: 'BIOMETRIC_REQUIRED', + MIGRATION_FAILED: 'MIGRATION_FAILED', + INVALID_DATA: 'INVALID_DATA' + } as const; + ``` + +2. **Error Recovery Strategies** + ```typescript + // src/services/storage/error-recovery/ErrorRecoveryService.ts + export class ErrorRecoveryService { + constructor(private platformService: PlatformService) {} + + async handleStorageError(error: StorageError): Promise { + switch (error.code) { + case StorageErrorCodes.DATABASE_NOT_INITIALIZED: + await this.reinitializeDatabase(); + break; + + case StorageErrorCodes.ENCRYPTION_FAILED: + case StorageErrorCodes.DECRYPTION_FAILED: + await this.handleEncryptionError(); + break; + + case StorageErrorCodes.QUOTA_EXCEEDED: + await this.handleQuotaExceeded(); + break; + + case StorageErrorCodes.BIOMETRIC_REQUIRED: + await this.handleBiometricRequired(); + break; + + case StorageErrorCodes.MIGRATION_FAILED: + await this.handleMigrationFailure(); + break; + + default: + throw error; // Re-throw unknown errors + } + } + + private async reinitializeDatabase(): Promise { + try { + // Attempt to reinitialize with backup + const backup = await this.platformService.exportDatabase(); + await this.platformService.deleteDatabase(); + await this.platformService.importDatabase(backup); + } catch (error) { + // If reinitialization fails, try clean initialization + await this.platformService.deleteDatabase(); + await this.platformService.openAccountsDatabase(); + } + } + + private async handleEncryptionError(): Promise { + // Attempt to recover encryption key + const newKey = await this.keyManagement.regenerateKey(); + await this.platformService.setMasterSecret({ + id: MASTER_SECRET_KEY, + secret: newKey + }); + } + + private async handleQuotaExceeded(): Promise { + // Implement cleanup strategy + const accounts = await this.platformService.getAllAccounts(); + const oldAccounts = accounts.filter(acc => + new Date(acc.dateCreated).getTime() < Date.now() - 30 * 24 * 60 * 60 * 1000 + ); + + for (const account of oldAccounts) { + await this.platformService.deleteAccount(account.did); + } + } + } + ``` + +3. **Edge Cases and Mitigations** + + a. **Concurrent Access** + ```typescript + // src/services/storage/ConcurrencyManager.ts + export class ConcurrencyManager { + private locks: Map> = new Map(); + + async withLock(key: string, operation: () => Promise): Promise { + const existingLock = this.locks.get(key); + if (existingLock) { + await existingLock; + } + + const lock = new Promise((resolve) => { + this.locks.set(key, lock); + }); + + try { + return await operation(); + } finally { + this.locks.delete(key); + lock.resolve(); + } + } + } + ``` + + b. **Data Integrity** + ```typescript + // src/services/storage/DataIntegrityService.ts + export class DataIntegrityService { + async verifyDataIntegrity(): Promise { + const accounts = await this.platformService.getAllAccounts(); + + for (const account of accounts) { + // Verify account data structure + if (!this.isValidAccount(account)) { + await this.repairAccount(account); + continue; + } + + // Verify settings exist + const settings = await this.platformService.getAccountSettings(account.did); + if (!settings) { + await this.createDefaultSettings(account.did); + } + + // Verify contacts + const contacts = await this.platformService.getAllContacts(); + const invalidContacts = contacts.filter(c => !this.isValidContact(c)); + for (const contact of invalidContacts) { + await this.repairContact(contact); + } + } + + return true; + } + + private isValidAccount(account: Account): boolean { + return ( + account.did && + account.publicKeyHex && + account.dateCreated && + (!account.identity || this.isValidJSON(account.identity)) + ); + } + + private async repairAccount(account: Account): Promise { + // Implementation for account repair + } + } + ``` + + c. **Platform Transitions** + ```typescript + // src/services/storage/PlatformTransitionService.ts + export class PlatformTransitionService { + async handlePlatformTransition(): Promise { + const capabilities = this.platformService.getCapabilities(); + + if (capabilities.isIOS) { + await this.handleIOSTransition(); + } else if (capabilities.isAndroid) { + await this.handleAndroidTransition(); + } + } + + private async handleIOSTransition(): Promise { + // Handle iOS-specific transitions + // e.g., moving from Keychain to Secure Enclave + } + + private async handleAndroidTransition(): Promise { + // Handle Android-specific transitions + // e.g., upgrading to StrongBox + } + } + ``` \ No newline at end of file