Created
February 26, 2025 20:50
-
-
Save lynsei/0f61dfe7819921e64a0f0330bc2a6d93 to your computer and use it in GitHub Desktop.
[deno-keepass]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { encode as btoa, decode as atob } from "https://deno.land/[email protected]/encoding/base64.ts"; | |
| import kdbxweb from "npm:kdbxweb"; | |
| // KeePass Configuration | |
| const KEEPASS_DB_PATH = "/path/to/your/database.kdbx"; | |
| const KEEPASS_MASTER_PASSWORD = "your-master-password"; // Secure this in an env variable | |
| const TARGET_ENTRY_TITLE = "AES-GCM Key"; | |
| const TARGET_ENTRY_URL = "keepass://AES-Key"; | |
| // Read the KeePass database | |
| async function readKeepassFile(): Promise<ArrayBuffer> { | |
| const data = await Deno.readFile(KEEPASS_DB_PATH); | |
| return data.buffer; | |
| } | |
| // Fetch encryption key from KeePass | |
| async function fetchKeyFromKeepass(): Promise<CryptoKey> { | |
| const credentials = new kdbxweb.Credentials(kdbxweb.ProtectedValue.fromString(KEEPASS_MASTER_PASSWORD)); | |
| const dbData = await readKeepassFile(); | |
| const db = await kdbxweb.Kdbx.load(dbData, credentials); | |
| for (const group of db.getDefaultGroup().groups) { | |
| for (const entry of group.entries) { | |
| if ( | |
| entry.fields.Title === TARGET_ENTRY_TITLE && | |
| entry.fields.URL === TARGET_ENTRY_URL | |
| ) { | |
| const rawKey = atob(entry.fields.Password); | |
| return await crypto.subtle.importKey( | |
| "raw", | |
| new Uint8Array([...rawKey].map((c) => c.charCodeAt(0))), | |
| { name: "AES-GCM" }, | |
| false, | |
| ["encrypt", "decrypt"] | |
| ); | |
| } | |
| } | |
| } | |
| console.error("Error: Could not retrieve encryption key from KeePass."); | |
| Deno.exit(1); | |
| } | |
| // Static IV (Should ideally be randomized per encryption) | |
| const IV = new Uint8Array([21, 42, 63, 84, 105, 126, 147, 168, 189, 210, 231, 252]); | |
| // Encrypt function | |
| async function encrypt(text: string): Promise<string> { | |
| const key = await fetchKeyFromKeepass(); | |
| const encoded = new TextEncoder().encode(text); | |
| const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv: IV }, key, encoded); | |
| return btoa(String.fromCharCode(...new Uint8Array(encrypted))); | |
| } | |
| // Decrypt function | |
| async function decrypt(encryptedText: string): Promise<string> { | |
| const key = await fetchKeyFromKeepass(); | |
| const encryptedData = new Uint8Array([...atob(encryptedText)].map((char) => char.charCodeAt(0))); | |
| const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv: IV }, key, encryptedData); | |
| return new TextDecoder().decode(decrypted); | |
| } | |
| // Command-line argument handling | |
| if (Deno.args.length < 2) { | |
| console.error("Usage: deno run --allow-read encryptor.ts -e|-d <text>"); | |
| Deno.exit(1); | |
| } | |
| const mode = Deno.args[0]; | |
| const inputText = Deno.args[1]; | |
| if (mode === "-e") { | |
| console.log(await encrypt(inputText)); | |
| } else if (mode === "-d") { | |
| console.log(await decrypt(inputText)); | |
| } else { | |
| console.error("Invalid option. Use -e for encryption or -d for decryption."); | |
| Deno.exit(1); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment