Files
crowd-funder-from-jason/docs/secure-storage-implementation.md
Matt Raymer 30fb3aee8e 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.
2025-05-24 23:37:09 -04:00

2077 lines
62 KiB
Markdown

# 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<void>;
getMasterSecret(): Promise<Secret | undefined>;
setMasterSecret(secret: Secret): Promise<void>;
// Accounts Database
openAccountsDatabase(): Promise<void>;
getAccountsCount(): Promise<number>;
getAllAccounts(): Promise<Account[]>;
getAccountByDid(did: string): Promise<Account | undefined>;
addAccount(account: Account): Promise<void>;
updateAccountSettings(did: string, settings: Partial<Settings>): Promise<void>;
// Settings Operations
getDefaultSettings(): Promise<Settings | undefined>;
getAccountSettings(did: string): Promise<Settings | undefined>;
updateSettings(key: string, changes: Partial<Settings>): Promise<void>;
addSettings(settings: Settings): Promise<void>;
getSettingsCount(): Promise<number>;
// Contacts Operations
getAllContacts(): Promise<Contact[]>;
addContact(contact: Contact): Promise<void>;
// Database Management
deleteDatabase(): Promise<void>;
importDatabase(data: Blob): Promise<void>;
exportDatabase(): Promise<Blob>;
// Migration Support
isFirstInstall(): Promise<boolean>;
needsMigration(): Promise<boolean>;
performMigration(): Promise<void>;
// 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<void> {
await secretDB.open();
}
async getMasterSecret(): Promise<Secret | undefined> {
return await secretDB.secret.get(MASTER_SECRET_KEY);
}
async setMasterSecret(secret: Secret): Promise<void> {
await secretDB.secret.put(secret);
}
// Accounts Database
async openAccountsDatabase(): Promise<void> {
const accountsDB = await accountsDBPromise;
await accountsDB.open();
}
async getAccountsCount(): Promise<number> {
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<void> {
await this.sqliteService.initialize({
database: 'timesafari_secret.db',
encrypted: true,
version: 1
});
}
async getMasterSecret(): Promise<Secret | undefined> {
const result = await this.sqliteService.query<Secret>(
'SELECT * FROM secret WHERE id = ?',
[MASTER_SECRET_KEY]
);
return result.value?.[0];
}
async setMasterSecret(secret: Secret): Promise<void> {
await this.sqliteService.query(
'INSERT OR REPLACE INTO secret (id, secret) VALUES (?, ?)',
[secret.id, secret.secret]
);
}
// Accounts Database
async openAccountsDatabase(): Promise<void> {
await this.sqliteService.initialize({
database: 'timesafari_accounts.db',
encrypted: true,
version: 1,
key: await this.keyManagement.getEncryptionKey()
});
}
async getAccountsCount(): Promise<number> {
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<void> {
// 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<Account | undefined> {
return await this.platformService.getAccountByDid(did);
}
async updateAccountSettings(did: string, settings: Partial<Settings>): Promise<void> {
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<void> {
// 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<T> {
success: boolean; // Operation success status
error?: string; // Error message if operation failed
value?: T; // Stored/retrieved value
}
export interface StorageService {
initialize(options: StorageOptions): Promise<void>;
setItem<T>(key: string, value: T): Promise<StorageResult<T>>;
getItem<T>(key: string): Promise<StorageResult<T>>;
removeItem(key: string): Promise<StorageResult<void>>;
query<T>(sql: string, params?: any[]): Promise<StorageResult<T[]>>;
}
```
## 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<void> {
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<T>(key: string, value: T): Promise<StorageResult<T>> {
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<T>(key: string): Promise<StorageResult<T>> {
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<StorageResult<void>> {
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<T>(sql: string, params: any[] = []): Promise<StorageResult<T[]>> {
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<void> {
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<string> {
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<void> {
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<string> {
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<string> {
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<string> {
const key = crypto.getRandomValues(new Uint8Array(32));
return btoa(String.fromCharCode(...key));
}
private async getWebCryptoKey(): Promise<CryptoKey> {
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<void> {
// No initialization needed for Preferences API
}
async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
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<T>(key: string): Promise<StorageResult<T>> {
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<StorageResult<void>> {
try {
await Preferences.remove({ key });
return { success: true };
} catch (error) {
return { success: false, error: (error as Error).message };
}
}
async query<T>(): Promise<StorageResult<T[]>> {
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<string> {
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<string> {
const key = crypto.getRandomValues(new Uint8Array(keySize / 8));
return btoa(String.fromCharCode(...key));
}
private async encryptWithSecureEnclave(key: string, options: SecureKeyOptions): Promise<string> {
// iOS Secure Enclave implementation (placeholder)
// Use Keychain with biometric protection
return key; // Implement platform-specific code
}
private async encryptWithAndroidKeystore(key: string, options: SecureKeyOptions): Promise<string> {
// Android Keystore implementation (placeholder)
// Use EncryptedSharedPreferences with biometric protection
return key; // Implement platform-specific code
}
private async encryptWithWebCrypto(key: string, options: SecureKeyOptions): Promise<string> {
// 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<boolean> {
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<void> {
// 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<void> {
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<void> {
// 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<void> {
// 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<void> {
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<string> {
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<boolean> {
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<void> {
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<string> {
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<boolean> {
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<void> {
// 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<IDBDatabase> {
// Implementation using IndexedDB with encryption
}
private async storeMasterKey(key: ArrayBuffer): Promise<void> {
// 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<void> {
const lastRotation = await this.getLastRotationDate();
if (Date.now() - lastRotation > this.ROTATION_INTERVAL) {
await this.rotateKey();
}
}
private async rotateKey(): Promise<void> {
// 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<void> {
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<void> {
// 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<BackupResult> {
// 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<DatabaseExport> {
const sqliteService = SQLiteService.getInstance();
return {
version: await sqliteService.getVersion(),
tables: await sqliteService.exportTables(),
metadata: await sqliteService.getMetadata()
};
}
private async exportKeys(): Promise<KeyExport> {
const keyManagement = KeyManagementService.getInstance();
return {
sqliteKey: await keyManagement.exportKey(),
backupKey: await this.generateBackupKey()
};
}
private async encryptBackup(
data: DatabaseExport,
keys: KeyExport
): Promise<EncryptedBackup> {
// 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<void> {
// 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<void> {
this.storageService = await StorageServiceFactory.create();
this.keyManagement = KeyManagementService.getInstance();
// Link SQLite encryption to account security
await this.linkAccountSecurity();
}
private async linkAccountSecurity(): Promise<void> {
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<string> {
// 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<void> {
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<void> {
// 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<SecureData> {
// 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<void> {
// 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<void> {
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<Account | undefined> {
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<never> {
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<void> {
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<void> {
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<void> {
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<boolean> {
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<string> {
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<Account> {
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<Account | undefined> {
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<Settings>): Promise<void> {
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<Settings>): Promise<void> {
// 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<void> {
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<void> {
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<void> {
// 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<void> {
// 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<string, Promise<void>> = new Map();
async withLock<T>(key: string, operation: () => Promise<T>): Promise<T> {
const existingLock = this.locks.get(key);
if (existingLock) {
await existingLock;
}
const lock = new Promise<void>((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<boolean> {
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<void> {
// Implementation for account repair
}
}
```
c. **Platform Transitions**
```typescript
// src/services/storage/PlatformTransitionService.ts
export class PlatformTransitionService {
async handlePlatformTransition(): Promise<void> {
const capabilities = this.platformService.getCapabilities();
if (capabilities.isIOS) {
await this.handleIOSTransition();
} else if (capabilities.isAndroid) {
await this.handleAndroidTransition();
}
}
private async handleIOSTransition(): Promise<void> {
// Handle iOS-specific transitions
// e.g., moving from Keychain to Secure Enclave
}
private async handleAndroidTransition(): Promise<void> {
// Handle Android-specific transitions
// e.g., upgrading to StrongBox
}
}
```