Files
securebit-chat/example_persistent_storage.js
lockbitchat 6bed472e09 feat(security): implement core crypto and logging hardening
- removed all logging of raw IV, JWK, session tokens, verification codes
  → logs now only include error codes, timestamps and short non-reversible SHA-256 hashes (first 4 bytes)
- replaced global master key storage with PBKDF2-derived non-extractable AES key
  → master key lives only in session memory and is cleared on timeout/focus-out
- added password-based derivation (PBKDF2) for master key initialization
- migrated persistent key storage to AES-GCM wrapped ciphertext in IndexedDB
  → JWK export → encrypt with session master key → store ciphertext + IV + metadata
  → unwrap + import as non-extractable on restore
- removed busy-wait loops and direct `window.gc()` calls
  → replaced with async non-blocking cleanup via setTimeout/Promise/WebWorker
2025-10-02 03:25:38 -04:00

171 lines
8.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Пример использования новой системы персистентного хранения ключей
import {
EnhancedSecureWebRTCManager,
SecureMasterKeyManager,
SecureIndexedDBWrapper,
SecurePersistentKeyStorage
} from './src/network/EnhancedSecureWebRTCManager.js';
// Создание менеджера WebRTC с персистентным хранением
const webrtcManager = new EnhancedSecureWebRTCManager(
(message) => console.log('Received:', message),
(status) => console.log('Status:', status),
(keyData) => console.log('Key exchange:', keyData),
(verificationData) => console.log('Verification required:', verificationData)
);
// Настройка callback для запроса пароля
webrtcManager.setMasterKeyPasswordCallback((isRetry, callback) => {
const message = isRetry ?
'Неверный пароль. Введите мастер-пароль повторно:' :
'Введите мастер-пароль для разблокировки хранилища ключей:';
// В реальном приложении используйте безопасный UI
const password = prompt(message);
callback(password);
});
// Настройка callback для истечения сессии
webrtcManager.setMasterKeySessionExpiredCallback((reason) => {
console.warn(`Сессия мастер-ключа истекла: ${reason}`);
alert(`Сессия заблокирована: ${reason}. Потребуется повторный ввод пароля.`);
});
// Демонстрация работы с персистентным хранением
async function demonstratePersistentStorage() {
try {
console.log('=== Демонстрация персистентного хранения ключей ===');
// 1. Создание и сохранение extractable ключа
console.log('\n1. Создание extractable ключа для демонстрации...');
const testKey = await crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 256
},
true, // extractable = true для персистентного хранения
['encrypt', 'decrypt']
);
// 2. Сохранение ключа (автоматически зашифруется и сохранится в IndexedDB)
console.log('2. Сохранение ключа в персистентное хранилище...');
const keyId = 'demo_persistent_key_' + Date.now();
const stored = await webrtcManager._secureKeyStorage.storeKey(keyId, testKey, {
purpose: 'demonstration',
algorithm: 'AES-GCM'
});
if (stored) {
console.log('✅ Ключ успешно сохранен в зашифрованном виде в IndexedDB');
} else {
console.log('❌ Ошибка сохранения ключа');
return;
}
// 3. Получение статистики хранилища
console.log('\n3. Статистика хранилища:');
const stats = await webrtcManager._secureKeyStorage.getStorageStats();
console.log('Статистика:', JSON.stringify(stats, null, 2));
// 4. Список всех ключей
console.log('\n4. Список всех ключей:');
const allKeys = await webrtcManager._secureKeyStorage.listAllKeys();
console.log('Ключи:', JSON.stringify(allKeys, null, 2));
// 5. Симуляция перезапуска приложения (очистка памяти)
console.log('\n5. Симуляция перезапуска приложения...');
webrtcManager._secureKeyStorage._keyReferences.clear();
console.log('Память очищена. Ключи остались только в IndexedDB.');
// 6. Восстановление ключа из IndexedDB
console.log('\n6. Восстановление ключа из IndexedDB...');
const restoredKey = await webrtcManager._secureKeyStorage.retrieveKey(keyId);
if (restoredKey) {
console.log('✅ Ключ успешно восстановлен из IndexedDB как non-extractable');
console.log('Тип ключа:', restoredKey.type);
console.log('Алгоритм:', restoredKey.algorithm.name);
console.log('Extractable:', restoredKey.extractable); // Должно быть false
} else {
console.log('❌ Ошибка восстановления ключа');
}
// 7. Тест шифрования с восстановленным ключом
console.log('\n7. Тест шифрования с восстановленным ключом...');
const testData = new TextEncoder().encode('Тестовые данные для шифрования');
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
restoredKey,
testData
);
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
restoredKey,
encrypted
);
const decryptedText = new TextDecoder().decode(decrypted);
console.log('✅ Шифрование/расшифровка успешно:', decryptedText);
// 8. Очистка демонстрационных данных
console.log('\n8. Очистка демонстрационных данных...');
await webrtcManager._secureKeyStorage.deleteKey(keyId);
console.log('✅ Демонстрационный ключ удален');
console.log('\n=== Демонстрация завершена ===');
} catch (error) {
console.error('Ошибка демонстрации:', error);
}
}
// Демонстрация безопасности
async function demonstrateSecurity() {
console.log('\n=== Демонстрация безопасности ===');
try {
// Попытка прямого доступа к IndexedDB
const request = indexedDB.open('SecureKeyStorage');
request.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(['encrypted_keys'], 'readonly');
const store = transaction.objectStore('encrypted_keys');
const getAllRequest = store.getAll();
getAllRequest.onsuccess = () => {
const keys = getAllRequest.result;
console.log('🔍 Прямой доступ к IndexedDB:');
if (keys.length > 0) {
const firstKey = keys[0];
console.log('📦 Найден зашифрованный ключ:');
console.log('- ID:', firstKey.keyId);
console.log('- Зашифрованные данные (первые 20 байт):',
Array.from(firstKey.encryptedData.slice(0, 20))
.map(b => b.toString(16).padStart(2, '0')).join(' '));
console.log('- IV:', Array.from(firstKey.iv)
.map(b => b.toString(16).padStart(2, '0')).join(' '));
console.log('✅ Данные зашифрованы - невозможно извлечь JWK без мастер-ключа!');
} else {
console.log('📭 Нет сохраненных ключей в IndexedDB');
}
};
};
} catch (error) {
console.error('Ошибка проверки безопасности:', error);
}
}
// Запуск демонстрации
console.log('Запуск демонстрации персистентного хранения ключей...');
console.log('При первом запуске будет запрошен мастер-пароль.');
// Последовательный запуск демонстраций
demonstratePersistentStorage()
.then(() => demonstrateSecurity())
.catch(error => console.error('Ошибка:', error));