Overnight transcoding, music discovery/import, system health reports, stats page generator, and bookmark management. Secrets stored in /etc/automation/ — not in repo.
35 lines
1.2 KiB
JavaScript
35 lines
1.2 KiB
JavaScript
#!/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);
|
|
}
|