import * as util from 'util' import * as readline from 'readline' import { webcrypto } from 'node:crypto' import { stdin as input, stdout as output } from 'process' const { subtle } = webcrypto const rl = readline.createInterface({ input, output }) rl.input.on('keypress', (c) => { if (c.charCodeAt() === 127) { const len = rl.line.length readline.moveCursor(rl.output, -len, 0) readline.clearLine(rl.output, 1) return } readline.moveCursor(rl.output, -1, 0) readline.clearLine(rl.output, 1) }) const question = util.promisify(rl.question).bind(rl) ;(async () => { try { let password = await question('password for decrypt: ') password = password.trim() let data = await question('encrypted base64 data: ') data = data.trim() const encoder = new TextEncoder() const keyMaterial = await subtle.importKey( 'raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey', 'deriveBits'] ) const salt = encoder.encode(password) const bits = await subtle.deriveBits({ name: 'PBKDF2', hash: 'SHA-512', salt, iterations: 1000 }, keyMaterial, 256) const key = await subtle.deriveKey( { name: 'PBKDF2', hash: 'SHA-512', salt, iterations: 100, }, keyMaterial, { name: 'AES-GCM', length: 256, }, true, ['encrypt', 'decrypt'] ) const ciphertext = Buffer.from(data, 'base64') const decrypted = await subtle.decrypt( { name: 'AES-GCM', iv: bits }, key, ciphertext ) const decoder = new TextDecoder() console.log('original: ', decoder.decode(decrypted)) } catch (err) { console.error('rejected', err) } process.exit(1) })()