UNPKG

safevibe

Version:

Safevibe CLI - Simple personal secret vault for AI developers and amateur vibe coders

109 lines (108 loc) 3.57 kB
import { createHash, randomBytes } from "node:crypto"; import { readFile, writeFile } from "node:fs/promises"; /** * Generate a new X25519 key pair * For demo: generates from random bytes */ export function generateKeyPair() { const privateKeyBytes = randomBytes(32); const publicKeyBytes = randomBytes(32); // In real X25519, this would be derived return { privateKey: privateKeyBytes.toString("hex"), publicKey: publicKeyBytes.toString("hex"), }; } /** * Derive a key pair from a seed (for deterministic generation) */ export function deriveKeyPair(seed) { const hash = createHash("sha256").update(seed).digest(); const privateKey = hash.toString("hex"); // In real X25519, public key would be scalar multiplication const publicKeyHash = createHash("sha256").update(hash).update("public").digest(); const publicKey = publicKeyHash.toString("hex"); return { privateKey, publicKey }; } /** * Encrypt data using the private key * For demo: simple XOR cipher with nonce */ export function encryptData(data, privateKey) { const nonce = randomBytes(12).toString("hex"); const key = Buffer.from(privateKey, "hex"); const nonceBytes = Buffer.from(nonce, "hex"); // Create encryption key by hashing private key + nonce const encKey = createHash("sha256") .update(key) .update(nonceBytes) .digest(); // Simple XOR encryption const dataBytes = Buffer.from(data, "utf-8"); const encrypted = Buffer.alloc(dataBytes.length); for (let i = 0; i < dataBytes.length; i++) { encrypted[i] = dataBytes[i] ^ encKey[i % encKey.length]; } return { ciphertext: encrypted.toString("base64"), nonce, }; } /** * Decrypt data using the private key */ export function decryptData(ciphertext, nonce, privateKey) { const key = Buffer.from(privateKey, "hex"); const nonceBytes = Buffer.from(nonce, "hex"); // Recreate encryption key const encKey = createHash("sha256") .update(key) .update(nonceBytes) .digest(); // Decrypt using XOR const encryptedBytes = Buffer.from(ciphertext, "base64"); const decrypted = Buffer.alloc(encryptedBytes.length); for (let i = 0; i < encryptedBytes.length; i++) { decrypted[i] = encryptedBytes[i] ^ encKey[i % encKey.length]; } return decrypted.toString("utf-8"); } /** * Hash a secret name for storage */ export function hashSecretName(name) { return createHash("sha256").update(name).digest("hex"); } /** * Save key pair to file securely */ export async function saveKeyPair(keyPair, filePath) { const keyData = { publicKey: keyPair.publicKey, privateKey: keyPair.privateKey, generated: new Date().toISOString(), algorithm: "demo-x25519", // In production: "x25519" }; await writeFile(filePath, JSON.stringify(keyData, null, 2), { mode: 0o600 }); } /** * Load key pair from file */ export async function loadKeyPair(filePath) { const keyData = JSON.parse(await readFile(filePath, "utf-8")); if (!keyData.publicKey || !keyData.privateKey) { throw new Error("Invalid key file format"); } return { publicKey: keyData.publicKey, privateKey: keyData.privateKey, }; } /** * Generate a deterministic project key from user key + project ID */ export function deriveProjectKey(userPrivateKey, projectId) { return createHash("sha256") .update(Buffer.from(userPrivateKey, "hex")) .update(projectId) .digest("hex"); }