From 848777d7513abade71526efccf5f56f7b2f03f3a Mon Sep 17 00:00:00 2001 From: Sosthene Date: Wed, 16 Jul 2025 17:20:40 +0200 Subject: [PATCH] Add import/exportUserDataBackup --- src/services/service.ts | 97 ++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/src/services/service.ts b/src/services/service.ts index 9020242..0f73a91 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -6,7 +6,7 @@ import ModalService from './modal.service'; import Database from './database.service'; import { navigate } from '../router'; import { storeData, retrieveData, testData } from './storage.service'; -import { BackUp } from '~/models/backup.model'; +import { UserDataBackUp } from '~/models/backup.model'; import { PDFDocument, rgb, StandardFonts } from 'pdf-lib'; export const U32_MAX = 4294967295; @@ -1210,48 +1210,68 @@ export default class Services { this.notifications = notifications; } - async importJSON(backup: BackUp): Promise { - const device = JSON.stringify(backup.device); + public async importUserDataBackup(backup: UserDataBackUp): Promise { + try { + if (typeof backup.user_data.device === 'string') { + throw new Error('Must decrypt backup first'); + } + // Reset current device + await this.resetDevice(); - // Reset current device - await this.resetDevice(); + await this.saveDeviceInDatabase(backup.user_data.device); - await this.saveDeviceInDatabase(device); - - this.restoreDevice(device); + this.restoreDevice(backup.user_data.device); - // TODO restore secrets and processes from file - const secretsStore = backup.secrets; - await this.restoreSecretsFromBackUp(secretsStore); + const secretsStore = backup.user_data.secrets as SecretsStore; + await this.restoreSecretsFromBackUp(secretsStore); - const processes = backup.processes; - await this.restoreProcessesFromBackUp(processes); + const processes = backup.user_data.processes as Record; + await this.restoreProcessesFromBackUp(processes); + } catch (e) { + // TODO reset everything + throw new Error(`Failed to import user data backup: ${e}`); + } } - public async createBackUp(): Promise { - // Get the device from indexedDB - const deviceStr = await this.getDeviceFromDatabase(); - if (!deviceStr) { - console.error('No device loaded'); - return null; + public async exportUserDataBackup(password: string | undefined | null): Promise { + try { + const userData = { + device: this.dumpDeviceFromMemory(), + secrets: await this.getAllSecrets(), + processes: await this.getProcesses(), + }; + + // Now hash the content of user data to produce a checksum + const textAsBuffer = new TextEncoder().encode(JSON.stringify(userData)); + const hashBuffer = await window.crypto.subtle.digest("SHA-256", textAsBuffer); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hash = hashArray + .map((item) => item.toString(16).padStart(2, "0")) + .join(""); + + if (password) { + // We'll let the sdk handle the pbkdf and encryption + // this.sdkClient.encrypt_backup_data(password, JSON.stringify(userData)); + // metadata.encrypted = true; + // userData + // textAsBuffer = enc.encode(cipher); + throw new Error('Encryption not implemented yet'); + } + + const metadata = { + encrypted: false, + checksum: hash, + } + + return { + version: 1, + exported_at: new Date(), + user_data: userData, + metadata, + } + } catch (e) { + throw new Error(`Failed to export user data backup: ${e}`); } - - const device: Device = JSON.parse(deviceStr); - - // Get the processes - const processes = await this.getProcesses(); - - // Get the shared secrets - const secrets = await this.getAllSecrets(); - - // Create a backup object - const backUp = { - device: device, - secrets: secrets, - processes: processes, - }; - - return backUp; } // Device 1 wait Device 2 @@ -1490,6 +1510,11 @@ export default class Services { const membersList = this.getAllMembers(); try { const res = this.sdkClient.request_data(processId, stateIds, roles, membersList); + // If ciphers_to_send is empty, it means that there's nobody to ask the data to + if (res.ciphers_to_send.length === 0) { + console.warn('Data unavailable from peers'); + return; + } await this.handleApiReturn(res); } catch (e) { console.error(e);