Skip to content

Instantly share code, notes, and snippets.

@brandond
Last active October 18, 2020 13:24
Show Gist options
  • Save brandond/78b0197baa6fdafcbd11ce1a4ed7d085 to your computer and use it in GitHub Desktop.
Save brandond/78b0197baa6fdafcbd11ce1a4ed7d085 to your computer and use it in GitHub Desktop.
PBKDF2 support for CryptoJS
#! /usr/bin/env node
'use strict';
var CryptoJS = require('crypto-js');
/**
* CryptoJS extensions for PBKDF2
*
* The OpenSSL formatter has a hardcoded salt size that is insufficient for use
* with this algorithm, and the Hex formatter does not serialize the salt at all.
* To remedy this, we provide two alternative formatters:
* * CryptoJS.format.JSON: A human-readable escaped JSON string.
* * CryptoJS.format.Base64URL: A URL-safe Base64 string, inspired by the OpenSSL format.
*
* @example
* require("kdf-pbkdf2.js");
* var CryptoJS = require("crypto-js");
*
* var encrypted = CryptoJS.AES.encrypt("MySecretData", "password", { kdf: CryptoJS.kdf.PBKDF2, format: CryptoJS.format.Base64URL }).toString();
* console.log("Encrypted string:", encrypted);
*
* var decrypted = CryptoJS.AES.decrypt(encrypted, "password", { kdf: CryptoJS.kdf.PBKDF2, format: CryptoJS.format.Base64URL }).toString(CryptoJS.enc.Utf8);
* console.log("Decrypted string:", decrypted);
*/
// RFC 4648 Base 64 Encoding with URL and Filename Safe Alphabet
CryptoJS.enc.Base64URL = {
parse: CryptoJS.enc.Base64.parse,
stringify: CryptoJS.enc.Base64.stringify,
_map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_='
};
CryptoJS.kdf.PBKDF2 = {
/**
* Derives a key and IV from a password in a manner compatible with RFC 2898
* @param {string} password The password to derive from.
* @param {number} keySize The size in words of the key to generate.
* @param {number} ivSize The size in words of the IV to generate.
* @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly.
*
* @return {CipherParams} A cipher params object with the key, IV, and salt.
*
* @static
*
* @example
*
* var derivedParams = CryptoJS.kdf.PBKDF2.execute('Password', 256/32, 128/32);
* var derivedParams = CryptoJS.kdf.PBKDF2.execute('Password', 256/32, 128/32, 'saltsalt');
*/
execute: function (password, keySize, ivSize, salt) {
// Generate random salt
if (! salt) {
salt = CryptoJS.lib.WordArray.random(128/8);
}
// Derive key and IV
var key = CryptoJS.algo.PBKDF2.create({ keySize: keySize + ivSize, hasher: CryptoJS.algo.SHA256, iterations: 10 }).compute(password, salt);
// Separate key and IV
var iv = CryptoJS.lib.WordArray.create(key.words.slice(keySize), ivSize * 4);
key.sigBytes = keySize * 4;
// Return params
return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt });
}
}
CryptoJS.format.JSON = {
/*
* Converts a cipher params object to a JSON string.
*
* @param {CipherParams} cipherParams The cipher params object.
*
* @return {string} The serialized JSON string.
*
* @static
*
* @example
*
* var jsonString = CryptoJS.format.JSON.stringify(cipherParams);
*/
stringify: function (cipherParams) {
// Shortcuts
var ciphertext = cipherParams.ciphertext;
var salt = cipherParams.salt;
var obj = { ciphertext: ciphertext.toString(CryptoJS.enc.Base64) };
if(salt){
obj.salt = salt.toString(CryptoJS.enc.Base64);
}
return JSON.stringify(obj)
},
/*
* Converts a JSON string to a cipher params object.
*
* @param {string} JsonStr The JSON string.
*
* @return {CipherParams} The cipher params object.
*
* @static
*
* @example
*
* var cipherParams = CryptoJS.format.JSON.parse(JsonString);
*/
parse: function (jsonStr) {
var obj = JSON.parse(jsonStr);
var salt;
var ciphertext = CryptoJS.enc.Base64.parse(obj.ciphertext);
if(obj.salt){
salt = CryptoJS.enc.Base64.parse(obj.salt);
}
return CryptoJS.lib.CipherParams.create({ ciphertext: ciphertext, salt: salt });
}
}
CryptoJS.format.Base64URL = {
/*
* Converts a cipher params object to a URL-Safe Base64 String.
*
* @param {CipherParams} cipherParams The cipher params object.
*
* @return {string} The serialized Base64URL string.
*
* @static
*
* @example
*
* var b64UrlString = CryptoJS.format.Base64URL.stringify(cipherParams);
*/
stringify: function (cipherParams) {
// Shortcuts
var ciphertext = cipherParams.ciphertext;
var salt = cipherParams.salt;
if(salt){
var wordArray = CryptoJS.lib.WordArray.create([0x4272696e, 0x65645f5f, salt.words.length]).concat(salt).concat(ciphertext);
} else {
var wordArray = ciphertext;
}
return wordArray.toString(CryptoJS.enc.Base64URL);
},
/*
* Converts a URL-Safe Base64 string to a cipher params object.
*
* @param {string} b64Str The Base64URL string.
*
* @return {CipherParams} The cipher params object.
*
* @static
*
* @example
*
* var cipherParams = CryptoJS.format.Base64URL.parse(b64UrlString);
*/
parse: function (b64Str) {
// Parse base64
var ciphertext = CryptoJS.enc.Base64URL.parse(b64Str);
// Shortcut
var ciphertextWords = ciphertext.words;
if (ciphertextWords[0] == 0x4272696e && ciphertextWords[1] == 0x65645f5f) {
// Extract salt length
var saltEnd = 3 + ciphertextWords[2];
// Extract salt
var salt = CryptoJS.lib.WordArray.create(ciphertextWords.slice(3, saltEnd));
// Remove salt from ciphertext
ciphertextWords.splice(0, saltEnd);
ciphertext.sigBytes -= (4 * saltEnd);
}
return CryptoJS.lib.CipherParams.create({ ciphertext: ciphertext, salt: salt });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment