Обновление архитектуры ключей и кошельков

This commit is contained in:
AidarKC
2026-10-01 17:11:38 +03:00
parent f452ce1f1e
commit 8d22602761
51 changed files with 565 additions and 898 deletions
+30 -4
View File
@@ -12,6 +12,15 @@ function normalizeLoginStorageKey(login) {
return String(login || '').trim().toLowerCase();
}
function sanitizePersistentSecrets(keys) {
const source = keys && typeof keys === 'object' ? keys : {};
const safe = { ...source };
// Recovery/root private key is intentionally ephemeral: it may exist in RAM
// during a protected operation, but must never be persisted on the device.
delete safe.rootKey;
return safe;
}
function openDb() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
@@ -53,7 +62,8 @@ async function get(storeName, key) {
}
export async function saveEncryptedUserSecrets(login, storagePwd, keys) {
const encrypted = await encryptJsonWithStoragePwd(keys, storagePwd);
const safeKeys = sanitizePersistentSecrets(keys);
const encrypted = await encryptJsonWithStoragePwd(safeKeys, storagePwd);
const normalizedLogin = normalizeLoginStorageKey(login);
await put(STORE_SECRETS, {
login: normalizedLogin,
@@ -71,7 +81,22 @@ export async function loadEncryptedUserSecrets(login, storagePwd) {
if (!row?.encrypted) {
throw new Error('На устройстве нет сохранённых ключей для этого логина');
}
return decryptJsonWithStoragePwd(row.encrypted, storagePwd);
const decrypted = await decryptJsonWithStoragePwd(row.encrypted, storagePwd);
const safe = sanitizePersistentSecrets(decrypted);
// Одноразовая очистка контейнеров, созданных старыми версиями UI, где rootKey
// мог быть сохранён. Перезаписываем тем же storagePwd уже без Recovery private key.
if (decrypted && typeof decrypted === 'object' && Object.prototype.hasOwnProperty.call(decrypted, 'rootKey')) {
const encrypted = await encryptJsonWithStoragePwd(safe, storagePwd);
await put(STORE_SECRETS, {
login: normalizedLogin,
encrypted,
updatedAtMs: Date.now(),
});
}
return safe;
}
export async function updateEncryptedUserSecrets(login, storagePwd, updater) {
@@ -84,8 +109,9 @@ export async function updateEncryptedUserSecrets(login, storagePwd, updater) {
if (!next || typeof next !== 'object') {
throw new Error('updateEncryptedUserSecrets: updater должен вернуть объект secrets');
}
await saveEncryptedUserSecrets(login, storagePwd, next);
return next;
const safe = sanitizePersistentSecrets(next);
await saveEncryptedUserSecrets(login, storagePwd, safe);
return safe;
}
export async function saveSessionMaterial(login, material) {