Converted RC4, RC4Drop and Derive EVP
This commit is contained in:
parent
4008dbf38a
commit
24e4e268dc
@ -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,
|
||||
};
|
||||
|
144
src/core/operations/DeriveEVPKey.mjs
Normal file
144
src/core/operations/DeriveEVPKey.mjs
Normal 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);
|
||||
};
|
88
src/core/operations/RC4.mjs
Normal file
88
src/core/operations/RC4.mjs
Normal 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;
|
94
src/core/operations/RC4Drop.mjs
Normal file
94
src/core/operations/RC4Drop.mjs
Normal 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;
|
@ -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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user