// Ouvre un conteneur pm-encrypted-export-v1 avec Node, pour distinguer un // mauvais mot de passe d'un defaut du client Delphi. // // node try-open.js // // Utilise les memes primitives que le navigateur. Si Node y arrive et pas // Delphi, le probleme est dans le client ; si les deux echouent, c'est le // mot de passe. const fs = require('node:fs'); const c = require('node:crypto'); const [, , file, pwd] = process.argv; if (!file || pwd === undefined) { console.error('usage: node try-open.js '); process.exit(2); } const raw = fs.readFileSync(file, 'utf8').replace(/^/, ''); let v; try { v = JSON.parse(raw); } catch (e) { console.error('Ce fichier n\'est pas du JSON : ' + e.message); process.exit(1); } console.log('format : ' + v.format); console.log('kdf : ' + v.kdf + ' / ' + v.kdf_iterations + ' iterations'); console.log('mot de passe : ' + pwd.length + ' caracteres, ' + Buffer.byteLength(pwd, 'utf8') + ' octets UTF-8'); const salt = Buffer.from(v.kdf_salt, 'base64'); const iv = Buffer.from(v.iv, 'base64'); const ct = Buffer.from(v.ciphertext, 'base64'); const key = c.pbkdf2Sync(Buffer.from(pwd, 'utf8'), salt, v.kdf_iterations || 600000, 32, 'sha256'); console.log('cle derivee : ' + key.toString('hex')); try { const d = c.createDecipheriv('aes-256-gcm', key, iv); d.setAuthTag(ct.subarray(ct.length - 16)); const pt = Buffer.concat([d.update(ct.subarray(0, ct.length - 16)), d.final()]).toString('utf8'); const obj = JSON.parse(pt); const live = (obj.entries || []).filter(e => !e.deleted).length; const trash = (obj.entries || []).filter(e => e.deleted).length; console.log('==> OUVERT : ' + live + ' entrees, ' + trash + ' en corbeille'); console.log(' exporte le ' + (obj.exported_at || obj.snapshot_at || '?')); } catch (e) { console.log('==> ECHEC : ' + e.message); console.log(' Le tag GCM ne verifie pas : mot de passe faux, ou fichier altere.'); }