3c1525e46f
GET in 80 ms, decryption in 3893 ms, entries and trash counted, second call answers 304. try-open.js opens a container with Node so a failure can be attributed: a GCM tag that does not verify means wrong password OR altered file, and the app cannot tell which.
52 lines
2.0 KiB
JavaScript
52 lines
2.0 KiB
JavaScript
// 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 <fichier> <mot-de-passe>
|
||
//
|
||
// 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 <fichier> <mot-de-passe>');
|
||
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.');
|
||
}
|