UNPKG

dedpaste

Version:

CLI pastebin application using Cloudflare Workers and R2

304 lines (258 loc) 8.05 kB
// Key database structure and management functions import fs from 'fs'; import path from 'path'; import { promises as fsPromises } from 'fs'; import { homedir } from 'os'; import crypto from 'crypto'; import inquirer from 'inquirer'; // Constants const DEFAULT_KEY_DIR = path.join(homedir(), '.dedpaste', 'keys'); const FRIENDS_KEY_DIR = path.join(homedir(), '.dedpaste', 'friends'); const KEY_DB_PATH = path.join(homedir(), '.dedpaste', 'keydb.json'); const PGP_KEY_DIR = path.join(homedir(), '.dedpaste', 'pgp'); const KEYBASE_KEY_DIR = path.join(homedir(), '.dedpaste', 'keybase'); // Ensure directories exist async function ensureDirectories() { await fsPromises.mkdir(DEFAULT_KEY_DIR, { recursive: true }); await fsPromises.mkdir(FRIENDS_KEY_DIR, { recursive: true }); await fsPromises.mkdir(PGP_KEY_DIR, { recursive: true }); await fsPromises.mkdir(KEYBASE_KEY_DIR, { recursive: true }); return { DEFAULT_KEY_DIR, FRIENDS_KEY_DIR, PGP_KEY_DIR, KEYBASE_KEY_DIR }; } // Initialize key database if it doesn't exist async function initKeyDatabase() { if (!fs.existsSync(KEY_DB_PATH)) { const defaultDb = { keys: { self: null, friends: {}, pgp: {}, keybase: {} }, default_friend: null, last_used: null }; await fsPromises.writeFile(KEY_DB_PATH, JSON.stringify(defaultDb, null, 2)); return defaultDb; } // Read existing database const db = JSON.parse(await fsPromises.readFile(KEY_DB_PATH, 'utf8')); // Ensure new properties exist (for upgrades) if (!db.keys.pgp) { db.keys.pgp = {}; } if (!db.keys.keybase) { db.keys.keybase = {}; } return db; } // Load key database async function loadKeyDatabase() { await ensureDirectories(); return initKeyDatabase(); } // Save key database async function saveKeyDatabase(db) { await fsPromises.writeFile(KEY_DB_PATH, JSON.stringify(db, null, 2)); } // Generate key fingerprint function generateFingerprint(keyContent) { const hash = crypto.createHash('sha256'); hash.update(keyContent); const digest = hash.digest('hex'); // Format as colon-separated pairs return digest.match(/.{2}/g).join(':'); } // Generate a new key pair async function generateKeyPair() { const { DEFAULT_KEY_DIR } = await ensureDirectories(); const privateKeyPath = path.join(DEFAULT_KEY_DIR, 'private.pem'); const publicKeyPath = path.join(DEFAULT_KEY_DIR, 'public.pem'); // Generate RSA key pair const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); // Write keys to files await fsPromises.writeFile(privateKeyPath, privateKey); await fsPromises.writeFile(publicKeyPath, publicKey); // Update key database const db = await loadKeyDatabase(); db.keys.self = { private: privateKeyPath, public: publicKeyPath, fingerprint: generateFingerprint(publicKey), created: new Date().toISOString() }; await saveKeyDatabase(db); return { privateKeyPath, publicKeyPath, privateKey, publicKey }; } // Add a friend's public key async function addFriendKey(name, keyContent) { const { FRIENDS_KEY_DIR } = await ensureDirectories(); const keyPath = path.join(FRIENDS_KEY_DIR, `${name}.pem`); // Write key to file await fsPromises.writeFile(keyPath, keyContent); // Update key database const db = await loadKeyDatabase(); db.keys.friends[name] = { public: keyPath, fingerprint: generateFingerprint(keyContent), added: new Date().toISOString(), last_used: new Date().toISOString() }; // Set as default if it's the first friend if (!db.default_friend) { db.default_friend = name; } await saveKeyDatabase(db); return keyPath; } // List all keys async function listKeys() { const db = await loadKeyDatabase(); return db; } // Get a specific key async function getKey(type, name) { const db = await loadKeyDatabase(); if (type === 'self') { return db.keys.self; } else if (type === 'friend') { return db.keys.friends[name]; } else if (type === 'pgp') { return db.keys.pgp[name]; } else if (type === 'keybase') { return db.keys.keybase[name]; } else if (type === 'any' && name) { // Try to find the key in any of the collections return db.keys.friends[name] || db.keys.pgp[name] || db.keys.keybase[name] || null; } return null; } // Remove a key async function removeKey(type, name) { const db = await loadKeyDatabase(); if (type === 'friend' && db.keys.friends[name]) { // Remove the key file await fsPromises.unlink(db.keys.friends[name].public); // Remove from database delete db.keys.friends[name]; // Update default friend if needed if (db.default_friend === name) { const friendNames = Object.keys(db.keys.friends); db.default_friend = friendNames.length > 0 ? friendNames[0] : null; } await saveKeyDatabase(db); return true; } else if (type === 'pgp' && db.keys.pgp[name]) { // Remove the key file await fsPromises.unlink(db.keys.pgp[name].path); // Remove from database delete db.keys.pgp[name]; await saveKeyDatabase(db); return true; } else if (type === 'keybase' && db.keys.keybase[name]) { // Remove the key file await fsPromises.unlink(db.keys.keybase[name].path); // Remove from database delete db.keys.keybase[name]; await saveKeyDatabase(db); return true; } else if (type === 'any') { // Try to remove from any collection if (db.keys.friends[name]) { return await removeKey('friend', name); } else if (db.keys.pgp[name]) { return await removeKey('pgp', name); } else if (db.keys.keybase[name]) { return await removeKey('keybase', name); } } return false; } // Update last used timestamp async function updateLastUsed(name) { const db = await loadKeyDatabase(); if (db.keys.friends[name]) { db.keys.friends[name].last_used = new Date().toISOString(); db.last_used = name; await saveKeyDatabase(db); } } /** * Add a PGP key to the database * @param {string} name - Name for the key * @param {Object} keyInfo - Key information * @returns {Promise<string>} - Path to the stored key */ async function addPgpKey(name, keyInfo) { const { PGP_KEY_DIR } = await ensureDirectories(); const keyPath = path.join(PGP_KEY_DIR, `${name}.asc`); // Write key to file await fsPromises.writeFile(keyPath, keyInfo.key); // Update key database const db = await loadKeyDatabase(); db.keys.pgp[name] = { path: keyPath, type: 'pgp', fingerprint: keyInfo.keyId, email: keyInfo.email, added: new Date().toISOString(), last_used: new Date().toISOString() }; await saveKeyDatabase(db); return keyPath; } /** * Add a Keybase key to the database * @param {string} name - Name for the key * @param {Object} keyInfo - Key information * @returns {Promise<string>} - Path to the stored key */ async function addKeybaseKey(name, keyInfo) { const { KEYBASE_KEY_DIR } = await ensureDirectories(); const keyPath = path.join(KEYBASE_KEY_DIR, `${name}.asc`); // Write key to file await fsPromises.writeFile(keyPath, keyInfo.key); // Update key database const db = await loadKeyDatabase(); db.keys.keybase[name] = { path: keyPath, type: 'keybase', username: keyInfo.username, fingerprint: keyInfo.keyId, email: keyInfo.email, added: new Date().toISOString(), last_used: new Date().toISOString() }; await saveKeyDatabase(db); return keyPath; } // Export functions export { ensureDirectories, loadKeyDatabase, generateKeyPair, addFriendKey, addPgpKey, addKeybaseKey, listKeys, getKey, removeKey, updateLastUsed, DEFAULT_KEY_DIR, FRIENDS_KEY_DIR, PGP_KEY_DIR, KEYBASE_KEY_DIR };