mirror of https://github.com/gchq/CyberChef.git
786 lines
19 KiB
JavaScript
Executable File
786 lines
19 KiB
JavaScript
Executable File
import Utils from "../Utils.js";
|
|
import CryptoApi from "babel-loader!crypto-api";
|
|
import MD6 from "node-md6";
|
|
import * as SHA3 from "js-sha3";
|
|
import Checksum from "./Checksum.js";
|
|
import ctph from "ctph.js";
|
|
import ssdeep from "ssdeep.js";
|
|
import bcrypt from "bcryptjs";
|
|
import scrypt from "scryptsy";
|
|
|
|
|
|
/**
|
|
* Hashing operations.
|
|
*
|
|
* @author n1474335 [n1474335@gmail.com]
|
|
* @copyright Crown Copyright 2016
|
|
* @license Apache-2.0
|
|
*
|
|
* @namespace
|
|
*/
|
|
const Hash = {
|
|
|
|
/**
|
|
* Generic hash function.
|
|
*
|
|
* @param {string} name
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object} [options={}]
|
|
* @returns {string}
|
|
*/
|
|
runHash: function(name, input, options={}) {
|
|
const msg = Utils.arrayBufferToStr(input, false),
|
|
hasher = CryptoApi.getHasher(name, options);
|
|
hasher.update(msg);
|
|
return CryptoApi.encoder.toHex(hasher.finalize());
|
|
},
|
|
|
|
|
|
/**
|
|
* MD2 operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runMD2: function (input, args) {
|
|
return Hash.runHash("md2", input);
|
|
},
|
|
|
|
|
|
/**
|
|
* MD4 operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runMD4: function (input, args) {
|
|
return Hash.runHash("md4", input);
|
|
},
|
|
|
|
|
|
/**
|
|
* MD5 operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runMD5: function (input, args) {
|
|
return Hash.runHash("md5", input);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
MD6_SIZE: 256,
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
MD6_LEVELS: 64,
|
|
|
|
/**
|
|
* MD6 operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runMD6: function (input, args) {
|
|
const size = args[0],
|
|
levels = args[1],
|
|
key = args[2];
|
|
|
|
if (size < 0 || size > 512)
|
|
return "Size must be between 0 and 512";
|
|
if (levels < 0)
|
|
return "Levels must be greater than 0";
|
|
|
|
return MD6.getHashOfText(input, size, key, levels);
|
|
},
|
|
|
|
|
|
/**
|
|
* SHA0 operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runSHA0: function (input, args) {
|
|
return Hash.runHash("sha0", input);
|
|
},
|
|
|
|
|
|
/**
|
|
* SHA1 operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runSHA1: function (input, args) {
|
|
return Hash.runHash("sha1", input);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SHA2_SIZE: ["512", "256", "384", "224", "512/256", "512/224"],
|
|
|
|
/**
|
|
* SHA2 operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runSHA2: function (input, args) {
|
|
const size = args[0];
|
|
return Hash.runHash("sha" + size, input);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SHA3_SIZE: ["512", "384", "256", "224"],
|
|
|
|
/**
|
|
* SHA3 operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runSHA3: function (input, args) {
|
|
const size = parseInt(args[0], 10);
|
|
let algo;
|
|
|
|
switch (size) {
|
|
case 224:
|
|
algo = SHA3.sha3_224;
|
|
break;
|
|
case 384:
|
|
algo = SHA3.sha3_384;
|
|
break;
|
|
case 256:
|
|
algo = SHA3.sha3_256;
|
|
break;
|
|
case 512:
|
|
algo = SHA3.sha3_512;
|
|
break;
|
|
default:
|
|
return "Invalid size";
|
|
}
|
|
|
|
return algo(input);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
KECCAK_SIZE: ["512", "384", "256", "224"],
|
|
|
|
/**
|
|
* Keccak operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runKeccak: function (input, args) {
|
|
const size = parseInt(args[0], 10);
|
|
let algo;
|
|
|
|
switch (size) {
|
|
case 224:
|
|
algo = SHA3.keccak224;
|
|
break;
|
|
case 384:
|
|
algo = SHA3.keccak384;
|
|
break;
|
|
case 256:
|
|
algo = SHA3.keccak256;
|
|
break;
|
|
case 512:
|
|
algo = SHA3.keccak512;
|
|
break;
|
|
default:
|
|
return "Invalid size";
|
|
}
|
|
|
|
return algo(input);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SHAKE_CAPACITY: ["256", "128"],
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SHAKE_SIZE: 512,
|
|
|
|
/**
|
|
* Shake operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runShake: function (input, args) {
|
|
const capacity = parseInt(args[0], 10),
|
|
size = args[1];
|
|
let algo;
|
|
|
|
if (size < 0)
|
|
return "Size must be greater than 0";
|
|
|
|
switch (capacity) {
|
|
case 128:
|
|
algo = SHA3.shake128;
|
|
break;
|
|
case 256:
|
|
algo = SHA3.shake256;
|
|
break;
|
|
default:
|
|
return "Invalid size";
|
|
}
|
|
|
|
return algo(input, size);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
RIPEMD_SIZE: ["320", "256", "160", "128"],
|
|
|
|
/**
|
|
* RIPEMD operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runRIPEMD: function (input, args) {
|
|
const size = args[0];
|
|
return Hash.runHash("ripemd" + size, input);
|
|
},
|
|
|
|
|
|
/**
|
|
* HAS-160 operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runHAS: function (input, args) {
|
|
return Hash.runHash("has160", input);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
WHIRLPOOL_VARIANT: ["Whirlpool", "Whirlpool-T", "Whirlpool-0"],
|
|
|
|
/**
|
|
* Whirlpool operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runWhirlpool: function (input, args) {
|
|
const variant = args[0].toLowerCase();
|
|
return Hash.runHash(variant, input);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SNEFRU_ROUNDS: ["8", "4", "2"],
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SNEFRU_SIZE: ["256", "128"],
|
|
|
|
/**
|
|
* Snefru operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runSnefru: function (input, args) {
|
|
return Hash.runHash("snefru", input, {
|
|
rounds: args[0],
|
|
length: args[1]
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* CTPH operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runCTPH: function (input, args) {
|
|
return ctph.digest(input);
|
|
},
|
|
|
|
|
|
/**
|
|
* SSDEEP operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runSSDEEP: function (input, args) {
|
|
return ssdeep.digest(input);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
DELIM_OPTIONS: ["Line feed", "CRLF", "Space", "Comma"],
|
|
|
|
/**
|
|
* Compare CTPH hashes operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {Number}
|
|
*/
|
|
runCompareCTPH: function (input, args) {
|
|
const samples = input.split(Utils.charRep[args[0]]);
|
|
if (samples.length !== 2) throw "Incorrect number of samples.";
|
|
return ctph.similarity(samples[0], samples[1]);
|
|
},
|
|
|
|
|
|
/**
|
|
* Compare SSDEEP hashes operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {Number}
|
|
*/
|
|
runCompareSSDEEP: function (input, args) {
|
|
const samples = input.split(Utils.charRep[args[0]]);
|
|
if (samples.length !== 2) throw "Incorrect number of samples.";
|
|
return ssdeep.similarity(samples[0], samples[1]);
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
HMAC_FUNCTIONS: [
|
|
"MD2",
|
|
"MD4",
|
|
"MD5",
|
|
"SHA0",
|
|
"SHA1",
|
|
"SHA224",
|
|
"SHA256",
|
|
"SHA384",
|
|
"SHA512",
|
|
"SHA512/224",
|
|
"SHA512/256",
|
|
"RIPEMD128",
|
|
"RIPEMD160",
|
|
"RIPEMD256",
|
|
"RIPEMD320",
|
|
"HAS160",
|
|
"Whirlpool",
|
|
"Whirlpool-0",
|
|
"Whirlpool-T",
|
|
"Snefru"
|
|
],
|
|
|
|
/**
|
|
* HMAC operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runHMAC: function (input, args) {
|
|
const key = args[0],
|
|
hashFunc = args[1].toLowerCase(),
|
|
msg = Utils.arrayBufferToStr(input, false),
|
|
hasher = CryptoApi.getHasher(hashFunc);
|
|
|
|
// Horrible shim to fix constructor bug. Reported in nf404/crypto-api#8
|
|
hasher.reset = () => {
|
|
hasher.state = {};
|
|
const tmp = new hasher.constructor();
|
|
hasher.state = tmp.state;
|
|
};
|
|
|
|
const mac = CryptoApi.getHmac(CryptoApi.encoder.fromUtf(key), hasher);
|
|
mac.update(msg);
|
|
return CryptoApi.encoder.toHex(mac.finalize());
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
BCRYPT_ROUNDS: 10,
|
|
|
|
/**
|
|
* Bcrypt operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runBcrypt: async function (input, args) {
|
|
const rounds = args[0];
|
|
const salt = await bcrypt.genSalt(rounds);
|
|
|
|
return await bcrypt.hash(input, salt, null, p => {
|
|
// Progress callback
|
|
if (ENVIRONMENT_IS_WORKER())
|
|
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Bcrypt compare operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runBcryptCompare: async function (input, args) {
|
|
const hash = args[0];
|
|
|
|
const match = await bcrypt.compare(input, hash, null, p => {
|
|
// Progress callback
|
|
if (ENVIRONMENT_IS_WORKER())
|
|
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
|
|
});
|
|
|
|
return match ? "Match: " + input : "No match";
|
|
},
|
|
|
|
|
|
/**
|
|
* Bcrypt parse operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runBcryptParse: async function (input, args) {
|
|
try {
|
|
return `Rounds: ${bcrypt.getRounds(input)}
|
|
Salt: ${bcrypt.getSalt(input)}
|
|
Password hash: ${input.split(bcrypt.getSalt(input))[1]}
|
|
Full hash: ${input}`;
|
|
} catch (err) {
|
|
return "Error: " + err.toString();
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
KEY_FORMAT: ["Hex", "Base64", "UTF8", "Latin1"],
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SCRYPT_ITERATIONS: 16384,
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SCRYPT_MEM_FACTOR: 8,
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SCRYPT_PARALLEL_FACTOR: 1,
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
SCRYPT_KEY_LENGTH: 64,
|
|
|
|
/**
|
|
* Scrypt operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runScrypt: function (input, args) {
|
|
const salt = Utils.convertToByteString(args[0].string || "", args[0].option),
|
|
iterations = args[1],
|
|
memFactor = args[2],
|
|
parallelFactor = args[3],
|
|
keyLength = args[4];
|
|
|
|
try {
|
|
const data = scrypt(
|
|
input, salt, iterations, memFactor, parallelFactor, keyLength,
|
|
p => {
|
|
// Progress callback
|
|
if (ENVIRONMENT_IS_WORKER())
|
|
self.sendStatusMessage(`Progress: ${p.percent.toFixed(0)}%`);
|
|
}
|
|
);
|
|
|
|
return data.toString("hex");
|
|
} catch (err) {
|
|
return "Error: " + err.toString();
|
|
}
|
|
},
|
|
|
|
|
|
/**
|
|
* Generate all hashes operation.
|
|
*
|
|
* @param {ArrayBuffer} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runAll: function (input, args) {
|
|
const arrayBuffer = input,
|
|
str = Utils.arrayBufferToStr(arrayBuffer, false),
|
|
byteArray = new Uint8Array(arrayBuffer),
|
|
output = "MD2: " + Hash.runMD2(arrayBuffer, []) +
|
|
"\nMD4: " + Hash.runMD4(arrayBuffer, []) +
|
|
"\nMD5: " + Hash.runMD5(arrayBuffer, []) +
|
|
"\nMD6: " + Hash.runMD6(str, []) +
|
|
"\nSHA0: " + Hash.runSHA0(arrayBuffer, []) +
|
|
"\nSHA1: " + Hash.runSHA1(arrayBuffer, []) +
|
|
"\nSHA2 224: " + Hash.runSHA2(arrayBuffer, ["224"]) +
|
|
"\nSHA2 256: " + Hash.runSHA2(arrayBuffer, ["256"]) +
|
|
"\nSHA2 384: " + Hash.runSHA2(arrayBuffer, ["384"]) +
|
|
"\nSHA2 512: " + Hash.runSHA2(arrayBuffer, ["512"]) +
|
|
"\nSHA3 224: " + Hash.runSHA3(arrayBuffer, ["224"]) +
|
|
"\nSHA3 256: " + Hash.runSHA3(arrayBuffer, ["256"]) +
|
|
"\nSHA3 384: " + Hash.runSHA3(arrayBuffer, ["384"]) +
|
|
"\nSHA3 512: " + Hash.runSHA3(arrayBuffer, ["512"]) +
|
|
"\nKeccak 224: " + Hash.runKeccak(arrayBuffer, ["224"]) +
|
|
"\nKeccak 256: " + Hash.runKeccak(arrayBuffer, ["256"]) +
|
|
"\nKeccak 384: " + Hash.runKeccak(arrayBuffer, ["384"]) +
|
|
"\nKeccak 512: " + Hash.runKeccak(arrayBuffer, ["512"]) +
|
|
"\nShake 128: " + Hash.runShake(arrayBuffer, ["128", 256]) +
|
|
"\nShake 256: " + Hash.runShake(arrayBuffer, ["256", 512]) +
|
|
"\nRIPEMD-128: " + Hash.runRIPEMD(arrayBuffer, ["128"]) +
|
|
"\nRIPEMD-160: " + Hash.runRIPEMD(arrayBuffer, ["160"]) +
|
|
"\nRIPEMD-256: " + Hash.runRIPEMD(arrayBuffer, ["256"]) +
|
|
"\nRIPEMD-320: " + Hash.runRIPEMD(arrayBuffer, ["320"]) +
|
|
"\nHAS-160: " + Hash.runHAS(arrayBuffer, []) +
|
|
"\nWhirlpool-0: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool-0"]) +
|
|
"\nWhirlpool-T: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool-T"]) +
|
|
"\nWhirlpool: " + Hash.runWhirlpool(arrayBuffer, ["Whirlpool"]) +
|
|
"\nSSDEEP: " + Hash.runSSDEEP(str) +
|
|
"\nCTPH: " + Hash.runCTPH(str) +
|
|
"\n\nChecksums:" +
|
|
"\nFletcher-8: " + Checksum.runFletcher8(byteArray, []) +
|
|
"\nFletcher-16: " + Checksum.runFletcher16(byteArray, []) +
|
|
"\nFletcher-32: " + Checksum.runFletcher32(byteArray, []) +
|
|
"\nFletcher-64: " + Checksum.runFletcher64(byteArray, []) +
|
|
"\nAdler-32: " + Checksum.runAdler32(byteArray, []) +
|
|
"\nCRC-16: " + Checksum.runCRC16(str, []) +
|
|
"\nCRC-32: " + Checksum.runCRC32(str, []);
|
|
|
|
return output;
|
|
},
|
|
|
|
|
|
/**
|
|
* Analyse hash operation.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runAnalyse: function(input, args) {
|
|
input = input.replace(/\s/g, "");
|
|
|
|
let output = "",
|
|
byteLength = input.length / 2,
|
|
bitLength = byteLength * 8,
|
|
possibleHashFunctions = [];
|
|
|
|
if (!/^[a-f0-9]+$/i.test(input)) {
|
|
return "Invalid hash";
|
|
}
|
|
|
|
output += "Hash length: " + input.length + "\n" +
|
|
"Byte length: " + byteLength + "\n" +
|
|
"Bit length: " + bitLength + "\n\n" +
|
|
"Based on the length, this hash could have been generated by one of the following hashing functions:\n";
|
|
|
|
switch (bitLength) {
|
|
case 4:
|
|
possibleHashFunctions = [
|
|
"Fletcher-4",
|
|
"Luhn algorithm",
|
|
"Verhoeff algorithm",
|
|
];
|
|
break;
|
|
case 8:
|
|
possibleHashFunctions = [
|
|
"Fletcher-8",
|
|
];
|
|
break;
|
|
case 16:
|
|
possibleHashFunctions = [
|
|
"BSD checksum",
|
|
"CRC-16",
|
|
"SYSV checksum",
|
|
"Fletcher-16"
|
|
];
|
|
break;
|
|
case 32:
|
|
possibleHashFunctions = [
|
|
"CRC-32",
|
|
"Fletcher-32",
|
|
"Adler-32",
|
|
];
|
|
break;
|
|
case 64:
|
|
possibleHashFunctions = [
|
|
"CRC-64",
|
|
"RIPEMD-64",
|
|
"SipHash",
|
|
];
|
|
break;
|
|
case 128:
|
|
possibleHashFunctions = [
|
|
"MD5",
|
|
"MD4",
|
|
"MD2",
|
|
"HAVAL-128",
|
|
"RIPEMD-128",
|
|
"Snefru",
|
|
"Tiger-128",
|
|
];
|
|
break;
|
|
case 160:
|
|
possibleHashFunctions = [
|
|
"SHA-1",
|
|
"SHA-0",
|
|
"FSB-160",
|
|
"HAS-160",
|
|
"HAVAL-160",
|
|
"RIPEMD-160",
|
|
"Tiger-160",
|
|
];
|
|
break;
|
|
case 192:
|
|
possibleHashFunctions = [
|
|
"Tiger",
|
|
"HAVAL-192",
|
|
];
|
|
break;
|
|
case 224:
|
|
possibleHashFunctions = [
|
|
"SHA-224",
|
|
"SHA3-224",
|
|
"ECOH-224",
|
|
"FSB-224",
|
|
"HAVAL-224",
|
|
];
|
|
break;
|
|
case 256:
|
|
possibleHashFunctions = [
|
|
"SHA-256",
|
|
"SHA3-256",
|
|
"BLAKE-256",
|
|
"ECOH-256",
|
|
"FSB-256",
|
|
"GOST",
|
|
"Grøstl-256",
|
|
"HAVAL-256",
|
|
"PANAMA",
|
|
"RIPEMD-256",
|
|
"Snefru",
|
|
];
|
|
break;
|
|
case 320:
|
|
possibleHashFunctions = [
|
|
"RIPEMD-320",
|
|
];
|
|
break;
|
|
case 384:
|
|
possibleHashFunctions = [
|
|
"SHA-384",
|
|
"SHA3-384",
|
|
"ECOH-384",
|
|
"FSB-384",
|
|
];
|
|
break;
|
|
case 512:
|
|
possibleHashFunctions = [
|
|
"SHA-512",
|
|
"SHA3-512",
|
|
"BLAKE-512",
|
|
"ECOH-512",
|
|
"FSB-512",
|
|
"Grøstl-512",
|
|
"JH",
|
|
"MD6",
|
|
"Spectral Hash",
|
|
"SWIFFT",
|
|
"Whirlpool",
|
|
];
|
|
break;
|
|
case 1024:
|
|
possibleHashFunctions = [
|
|
"Fowler-Noll-Vo",
|
|
];
|
|
break;
|
|
default:
|
|
possibleHashFunctions = [
|
|
"Unknown"
|
|
];
|
|
break;
|
|
}
|
|
|
|
return output + possibleHashFunctions.join("\n");
|
|
},
|
|
|
|
};
|
|
|
|
export default Hash;
|