# 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 } } ```