Last active
March 15, 2019 20:47
-
-
Save TravisMullen/88882d1de04c882540527f3d0bd68234 to your computer and use it in GitHub Desktop.
AES - Cipher Block Chaining (CBC) - 192 bit
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
const { randomFill, createCipheriv, createDecipheriv } = require('crypto') | |
const algorithm = 'aes-192-cbc' | |
/** | |
* Nonce ================ | |
*/ | |
const _dirtyNonce = {} | |
/** dirtyNonce - used none. */ | |
const dirtyNonce = () => ( | |
Object | |
.freeze( | |
Object | |
.keys(_dirtyNonce) | |
) | |
) | |
const _nonce = () => { | |
const buf = Buffer.alloc(16) | |
return new Promise((resolve, reject) => { | |
randomFill(buf, (err, buf) => { | |
if (err) { reject(err) } console.log(buf.toString('hex')) | |
resolve(buf) | |
}) | |
}) | |
} | |
const _nonceGenerator = async () => { | |
console.time(`nonce generator`) | |
let nonce = await _nonce() | |
while (isDirty(nonce)) { | |
nonce = await _nonce() | |
} | |
console.timeEnd(`nonce generator`) | |
return nonce | |
} | |
const isDirty = nonce => { | |
const iv = nonce.toString('hex') | |
if (_dirtyNonce[iv]) { | |
return true | |
} | |
_dirtyNonce[iv] = true | |
return false | |
} | |
const isClean = nonce => !(isDirty(nonce)) | |
/** | |
* Nonce Generator - Validate provide and/or produce unique. | |
* @param {string|number} nonce Input for custom nonce. | |
* @return {string|number} Unique nonce that has passed validation. | |
*/ | |
const nonceGenerator = async nonce => { | |
// make sure IV is unique and valid length | |
let iv | |
if (nonce && | |
nonce.length === (Buffer.alloc(16, 0)).length && | |
isClean(nonce) | |
) { | |
console.log(`Supplied nonce: ${nonce.toString ? nonce.toString('hex') : nonce} | |
=> Correct IV length to match 'Buffer.alloc(16, 0)' | |
=> Unique :: Use method 'dirty()' for Array of used nonces.`) | |
iv = nonce | |
} else { | |
iv = await _nonceGenerator() | |
} | |
return iv | |
} | |
/** | |
* cipher - convert plaintext to encrypted hex string. | |
* @param {string|Buffer} options.key Key | |
* @param {string|Buffer} options.nonce Unique - will create a unique if has been used. | |
* @param {string} plaintext Payload to encrypt. | |
* @param {Boolean} persistance Should this be saved locally to a file. | |
* @return {Promise{ nonce, encrypted, chunks }} | |
*/ | |
const cipher = async ({ key, nonce }, plaintext, persistance = false) => { | |
console.time(`${algorithm}:cipher`) | |
// allow override for testing and | |
const iv = await nonceGenerator(nonce) | |
const pher = createCipheriv(algorithm, key, iv) | |
const encrypted = [] | |
return new Promise(async (resolve, reject) => { | |
pher.on('readable', () => { | |
let chunk | |
while ((chunk = pher.read()) !== null) { | |
encrypted.push(chunk.toString('hex')) | |
} | |
console.count(`cipher::reading chunk`) | |
}) | |
pher.on('end', () => { | |
console.log(`encrypted:${encrypted.join('')} | |
cipher:end`) | |
console.timeEnd(`${algorithm}:cipher`) | |
// Prints: some clear text data | |
resolve({ | |
nonce: iv, | |
encrypted: encrypted.join(''), | |
chunks: encrypted | |
}) | |
}) | |
pher.on('error', err => { | |
console.log('Encryption Cipher failed!') | |
reject(err) | |
}) | |
// could return a complete promise | |
pher.write(plaintext) | |
pher.end() | |
}) | |
} | |
/** | |
* decipher - convert plaintext to encrypted hex string. | |
* @param {string|Buffer} options.key Key | |
* @param {string|Buffer} options.nonce from cipher. | |
* @param {string} encrypted Ciphertext to decrypt. | |
* @return {Promise{ plaintext, chunks }} | |
*/ | |
const decipher = ({ key, nonce }, encrypted) => { | |
console.time(`${algorithm}:decipher`) | |
const pher = createDecipheriv(algorithm, key, nonce) | |
const plaintext = [] | |
return new Promise((resolve, reject) => { | |
pher.on('readable', () => { | |
let chunk | |
while ((chunk = pher.read()) !== null) { | |
plaintext.push(chunk.toString('utf8')) | |
} | |
console.count(`decipher::reading chunk`) | |
}) | |
pher.on('end', () => { | |
// `${plaintext}:iv` | |
console.log(`plaintext:${plaintext.join('')} | |
decipher:end`) | |
console.timeEnd(`${algorithm}:decipher`) | |
// Prints: some clear text data | |
// | |
resolve({ | |
plaintext: plaintext.join(''), | |
chunks: plaintext | |
}) | |
}) | |
pher.on('error', err => { | |
console.log('Decipher failed!') | |
reject(err) | |
}) | |
pher.write(encrypted, 'hex') | |
pher.end() | |
}) | |
} | |
module.exports = { cipher, decipher, dirtyNonce } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment