1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#!/usr/bin/env node
// Decrypt Floccus bookmarks
// Usage: node decrypt_bookmarks.js [password]
const crypto = require('crypto');
const fs = require('fs');
const BOOKMARKS_PATH = '/var/www/webdav/bookmarks.xbel';
const password = process.argv[2] || process.env.BOOKMARKS_PASSWORD || JSON.parse(require('fs').readFileSync('/etc/automation/bookmarks.json', 'utf8')).password;
const data = JSON.parse(fs.readFileSync(BOOKMARKS_PATH, 'utf8'));
const ciphertext = Buffer.from(data.ciphertext, 'base64');
const salt = data.salt; // Floccus uses salt as UTF-8 string, not hex-decoded
// Floccus encryption: PBKDF2-SHA256, 250000 iterations
const key = crypto.pbkdf2Sync(password, salt, 250000, 32, 'sha256');
// 16-byte IV at start, then ciphertext, then 16-byte GCM tag at end
const iv = ciphertext.slice(0, 16);
const encrypted = ciphertext.slice(16, -16);
const tag = ciphertext.slice(-16);
try {
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
let decrypted = decipher.update(encrypted);
decrypted = Buffer.concat([decrypted, decipher.final()]);
console.log(decrypted.toString('utf8'));
} catch (e) {
console.error('Decryption failed:', e.message);
process.exit(1);
}
|