Created
December 15, 2024 17:50
-
-
Save skorotkiewicz/53ed107d45db2cc4ff76f135b93b28e9 to your computer and use it in GitHub Desktop.
Crypto messages for postcards
This file contains 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 crypto from "node:crypto"; | |
/** | |
* Generates a compact key from a seed password | |
* @param {string} seed - The seed password used to generate the key | |
* @param {number} [length=16] - The desired length of the generated key | |
* @returns {string} A compact key derived from the seed | |
*/ | |
function generateCompactKey(seed, length = 16) { | |
// Use SHA-256 to generate a deterministic but unique key | |
const hash = crypto.createHash("sha256").update(seed).digest("hex"); | |
return hash.slice(0, length); | |
} | |
/** | |
* Encrypts a message using a seed-derived key | |
* @param {string} seed - The seed password used to generate the encryption key | |
* @param {string} message - The message to be encrypted | |
* @returns {string} The encrypted message in a URL-safe base64 format | |
*/ | |
function encode(seed, message) { | |
// Generate a compact key | |
const key = generateCompactKey(seed); | |
// Create a cipher | |
const cipher = crypto.createCipheriv("aes-128-ecb", key, Buffer.alloc(0)); | |
// Encrypt the message | |
let encrypted = cipher.update(message, "utf8", "base64"); | |
encrypted += cipher.final("base64"); | |
// Remove unsafe characters from base64 | |
return encrypted | |
.replace(/\+/g, "-") // replace + with - | |
.replace(/\//g, "_") // replace / with _ | |
.replace(/=/g, ""); // remove padding | |
} | |
/** | |
* Decrypts a message using a seed-derived key | |
* @param {string} seed - The seed password used to generate the decryption key | |
* @param {string} encodedMessage - The encrypted message to be decrypted | |
* @returns {string} The decrypted message | |
*/ | |
function decode(seed, encodedMessage) { | |
// Restore padding | |
const paddedMessage = | |
encodedMessage | |
.replace(/-/g, "+") // replace - with + | |
.replace(/_/g, "/") + // replace _ with / | |
"===".slice(0, (4 - (encodedMessage.length % 4)) % 4); | |
// Generate the same compact key | |
const key = generateCompactKey(seed); | |
// Create a decipher | |
const decipher = crypto.createDecipheriv("aes-128-ecb", key, Buffer.alloc(0)); | |
// Decrypt the message | |
let decrypted = decipher.update(paddedMessage, "base64", "utf8"); | |
decrypted += decipher.final("utf8"); | |
return decrypted; | |
} | |
// Usage example | |
const seed = "postcard2024"; | |
const message = "meet me at 4pm"; | |
try { | |
const encoded = encode(seed, message); | |
console.log("Encoded message:", encoded); | |
const decoded = decode(seed, encoded); | |
console.log("Decoded message:", decoded); | |
} catch (error) { | |
console.error("Encryption/decryption error:", error); | |
} | |
// Encoded message: bgGmGReREFMscWwms4sboQ | |
// Decoded message: meet me at 4pm |
This file contains 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 crypto from "node:crypto"; | |
/** | |
* Generates a key and Initialization Vector (IV) from a seed password | |
* @param {string} seed - The seed password used to generate the key and IV | |
* @param {number} [keyLength=32] - The desired length of the encryption key | |
* @param {number} [ivLength=16] - The desired length of the Initialization Vector | |
* @returns {Object} An object containing the generated key, IV, and salt | |
*/ | |
function generateKeyAndIV(seed, keyLength = 32, ivLength = 16) { | |
const salt = crypto.randomBytes(16); | |
const key = crypto.scryptSync(seed, salt, keyLength); | |
const iv = crypto.randomBytes(ivLength); | |
return { key, iv, salt }; | |
} | |
/** | |
* Encrypts a message using a seed-derived key and IV | |
* @param {string} seed - The seed password used to generate the encryption key | |
* @param {string} message - The message to be encrypted | |
* @returns {string} The encrypted message with salt and IV in a hex-encoded format | |
*/ | |
function encode(seed, message) { | |
// Generate key and IV | |
const { key, iv, salt } = generateKeyAndIV(seed); | |
// Create a cipher | |
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv); | |
// Encrypt the message | |
let encrypted = cipher.update(message, "utf8", "hex"); | |
encrypted += cipher.final("hex"); | |
// Return salt, IV, and encrypted message | |
return `${salt.toString("hex")}:${iv.toString("hex")}:${encrypted}`; | |
} | |
/** | |
* Decrypts a message using a seed-derived key | |
* @param {string} seed - The seed password used to generate the decryption key | |
* @param {string} encodedMessage - The encrypted message to be decrypted | |
* @returns {string} The decrypted message | |
*/ | |
function decode(seed, encodedMessage) { | |
// Split the encoded message into components | |
const [saltHex, ivHex, encryptedHex] = encodedMessage.split(":"); | |
// Convert salt and IV from hex | |
const salt = Buffer.from(saltHex, "hex"); | |
const iv = Buffer.from(ivHex, "hex"); | |
// Generate key using the same salt | |
const key = crypto.scryptSync(seed, salt, 32); | |
// Create a decipher | |
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv); | |
// Decrypt the message | |
let decrypted = decipher.update(encryptedHex, "hex", "utf8"); | |
decrypted += decipher.final("utf8"); | |
return decrypted; | |
} | |
// Usage example | |
const seed = "postcard2024"; | |
const message = "meet me at 4pm"; | |
try { | |
const encoded = encode(seed, message); | |
console.log("Encoded message:", encoded); | |
const decoded = decode(seed, encoded); | |
console.log("Decoded message:", decoded); | |
} catch (error) { | |
console.error("Encryption/decryption error:", error); | |
} | |
// Encoded message: 144f62fcf27ae1868ca855cfc55cdc17:50b07bf6f7166177ed5da0605065cc42:db98629710e02e31df718835c3a2313b | |
// Decoded message: meet me at 4pm |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment