Converted RC4, RC4Drop and Derive EVP

This commit is contained in:
Matt C 2018-05-14 18:30:52 +01:00
parent 4008dbf38a
commit 24e4e268dc
5 changed files with 345 additions and 155 deletions

View File

@ -2,11 +2,14 @@
* Cipher functions.
*
* @author Matt C [matt@artemisbot.uk]
* @author n1474335 [n1474335@gmail.com]
*
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
*/
import OperationError from "../errors/OperationError";
import CryptoJS from "crypto-js";
/**
* Affine Cipher Encode operation.
@ -60,3 +63,19 @@ export function genPolybiusSquare (keyword) {
return polybius;
}
/**
* A mapping of string formats to their classes in the CryptoJS library.
*
* @private
* @constant
*/
export const format = {
"Hex": CryptoJS.enc.Hex,
"Base64": CryptoJS.enc.Base64,
"UTF8": CryptoJS.enc.Utf8,
"UTF16": CryptoJS.enc.Utf16,
"UTF16LE": CryptoJS.enc.Utf16LE,
"UTF16BE": CryptoJS.enc.Utf16BE,
"Latin1": CryptoJS.enc.Latin1,
};

View File

@ -0,0 +1,144 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import CryptoJS from "crypto-js";
/**
* Derive EVP key operation
*/
class DeriveEVPKey extends Operation {
/**
* DeriveEVPKey constructor
*/
constructor() {
super();
this.name = "Derive EVP key";
this.module = "Ciphers";
this.description = "EVP is a password-based key derivation function (PBKDF) used extensively in OpenSSL. In many applications of cryptography, user security is ultimately dependent on a password, and because a password usually can't be used directly as a cryptographic key, some processing is required.<br><br>A salt provides a large set of keys for any given password, and an iteration count increases the cost of producing keys from a password, thereby also increasing the difficulty of attack.<br><br>If you leave the salt argument empty, a random salt will be generated.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Passphrase",
"type": "toggleString",
"value": "",
"toggleValues": ["UTF8", "Latin1", "Hex", "Base64"]
},
{
"name": "Key size",
"type": "number",
"value": 128
},
{
"name": "Iterations",
"type": "number",
"value": 1
},
{
"name": "Hashing function",
"type": "option",
"value": ["SHA1", "SHA256", "SHA384", "SHA512", "MD5"]
},
{
"name": "Salt",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
keySize = args[1] / 32,
iterations = args[2],
hasher = args[3],
salt = Utils.convertToByteString(args[4].string, args[4].option),
key = CryptoJS.EvpKDF(passphrase, salt, {
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,
});
return key.toString(CryptoJS.enc.Hex);
}
}
export default DeriveEVPKey;
/**
* Overwriting the CryptoJS OpenSSL key derivation function so that it is possible to not pass a
* salt in.
* @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. If set to false, no salt will be added.
*
* @returns {CipherParams} A cipher params object with the key, IV, and salt.
*
* @static
*
* @example
* // Randomly generates a salt
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
* // Uses the salt 'saltsalt'
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
* // Does not use a salt
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, false);
*/
CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) {
// Generate random salt if no salt specified and not set to false
// This line changed from `if (!salt) {` to the following
if (salt === undefined || salt === null) {
salt = CryptoJS.lib.WordArray.random(64/8);
}
// Derive key and IV
const key = CryptoJS.algo.EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt);
// Separate key and IV
const 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 });
};
/**
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
* the hex string.
*
* @param {string} hexStr
* @returns {CryptoJS.lib.WordArray}
*/
CryptoJS.enc.Hex.parse = function (hexStr) {
// Remove whitespace
hexStr = hexStr.replace(/\s/g, "");
// Shortcut
const hexStrLength = hexStr.length;
// Convert
const words = [];
for (let i = 0; i < hexStrLength; i += 2) {
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
}
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
};

View File

@ -0,0 +1,88 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import CryptoJS from "crypto-js";
import { format } from "../lib/Ciphers";
/**
* RC4 operation
*/
class RC4 extends Operation {
/**
* RC4 constructor
*/
constructor() {
super();
this.name = "RC4";
this.module = "Ciphers";
this.description = "RC4 (also known as ARC4) is a widely-used stream cipher designed by Ron Rivest. It is used in popular protocols such as SSL and WEP. Although remarkable for its simplicity and speed, the algorithm's history doesn't inspire confidence in its security.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Passphrase",
"type": "toggleString",
"value": "",
"toggleValues": ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"]
},
{
"name": "Input format",
"type": "option",
"value": ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"]
},
{
"name": "Output format",
"type": "option",
"value": ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const message = format[args[1]].parse(input),
passphrase = format[args[0].option].parse(args[0].string),
encrypted = CryptoJS.RC4.encrypt(message, passphrase);
return encrypted.ciphertext.toString(format[args[2]]);
}
/**
* Highlight RC4
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight RC4 in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default RC4;

View File

@ -0,0 +1,94 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { format } from "../lib/Ciphers";
import CryptoJS from "crypto-js";
/**
* RC4 Drop operation
*/
class RC4Drop extends Operation {
/**
* RC4Drop constructor
*/
constructor() {
super();
this.name = "RC4 Drop";
this.module = "Ciphers";
this.description = "It was discovered that the first few bytes of the RC4 keystream are strongly non-random and leak information about the key. We can defend against this attack by discarding the initial portion of the keystream. This modified algorithm is traditionally called RC4-drop.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Passphrase",
"type": "toggleString",
"value": "",
"toggleValues": ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"]
},
{
"name": "Input format",
"type": "option",
"value": ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"]
},
{
"name": "Output format",
"type": "option",
"value": ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"]
},
{
"name": "Number of bytes to drop",
"type": "number",
"value": 768
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const message = format[args[1]].parse(input),
passphrase = format[args[0].option].parse(args[0].string),
drop = args[3],
encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop });
return encrypted.ciphertext.toString(format[args[2]]);
}
/**
* Highlight RC4 Drop
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight RC4 Drop in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default RC4Drop;

View File

@ -444,80 +444,6 @@ DES uses a key length of 8 bytes (64 bits).`;
},
/**
* Derive EVP key operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runEvpkdf: function (input, args) {
const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
keySize = args[1] / 32,
iterations = args[2],
hasher = args[3],
salt = Utils.convertToByteString(args[4].string, args[4].option),
key = CryptoJS.EvpKDF(passphrase, salt, {
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,
});
return key.toString(CryptoJS.enc.Hex);
},
/**
* @constant
* @default
*/
RC4_KEY_FORMAT: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"],
/**
* @constant
* @default
*/
CJS_IO_FORMAT: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"],
/**
* RC4 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRc4: function (input, args) {
let message = Cipher._format[args[1]].parse(input),
passphrase = Cipher._format[args[0].option].parse(args[0].string),
encrypted = CryptoJS.RC4.encrypt(message, passphrase);
return encrypted.ciphertext.toString(Cipher._format[args[2]]);
},
/**
* @constant
* @default
*/
RC4DROP_BYTES: 768,
/**
* RC4 Drop operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRc4drop: function (input, args) {
let message = Cipher._format[args[1]].parse(input),
passphrase = Cipher._format[args[0].option].parse(args[0].string),
drop = args[3],
encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop });
return encrypted.ciphertext.toString(Cipher._format[args[2]]);
},
/**
* @constant
* @default
@ -568,87 +494,6 @@ DES uses a key length of 8 bytes (64 bits).`;
}
},
/**
* A mapping of string formats to their classes in the CryptoJS library.
*
* @private
* @constant
*/
_format: {
"Hex": CryptoJS.enc.Hex,
"Base64": CryptoJS.enc.Base64,
"UTF8": CryptoJS.enc.Utf8,
"UTF16": CryptoJS.enc.Utf16,
"UTF16LE": CryptoJS.enc.Utf16LE,
"UTF16BE": CryptoJS.enc.Utf16BE,
"Latin1": CryptoJS.enc.Latin1,
},
};
export default Cipher;
/**
* Overwriting the CryptoJS OpenSSL key derivation function so that it is possible to not pass a
* salt in.
* @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. If set to false, no salt will be added.
*
* @returns {CipherParams} A cipher params object with the key, IV, and salt.
*
* @static
*
* @example
* // Randomly generates a salt
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
* // Uses the salt 'saltsalt'
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
* // Does not use a salt
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, false);
*/
CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) {
// Generate random salt if no salt specified and not set to false
// This line changed from `if (!salt) {` to the following
if (salt === undefined || salt === null) {
salt = CryptoJS.lib.WordArray.random(64/8);
}
// Derive key and IV
const key = CryptoJS.algo.EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt);
// Separate key and IV
const 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 });
};
/**
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
* the hex string.
*
* @param {string} hexStr
* @returns {CryptoJS.lib.WordArray}
*/
CryptoJS.enc.Hex.parse = function (hexStr) {
// Remove whitespace
hexStr = hexStr.replace(/\s/g, "");
// Shortcut
const hexStrLength = hexStr.length;
// Convert
const words = [];
for (let i = 0; i < hexStrLength; i += 2) {
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
}
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
};