mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-20 14:56:19 -04:00
First Cryptochef branch commit.
This commit is contained in:
parent
415c59d74c
commit
f79ca6cf02
36 changed files with 7134 additions and 4 deletions
140
src/core/operations/DecryptKeystoreFile.mjs
Normal file
140
src/core/operations/DecryptKeystoreFile.mjs
Normal file
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* Decrypts ETH keystore files, given the password.
|
||||
*
|
||||
* @author dgoldenberg [virtualcurrency@mitre.org]
|
||||
* @copyright MITRE 2023
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import JSSHA3 from "js-sha3";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {fromHex} from "../lib/Hex.mjs";
|
||||
import scryptsy from "scryptsy";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import forge from "node-forge";
|
||||
|
||||
|
||||
/**
|
||||
* JPath expression operation
|
||||
*/
|
||||
class DecryptKeystoreFile extends Operation {
|
||||
|
||||
/**
|
||||
* Decrypt Keystore constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Decrypt Keystore File";
|
||||
this.module = "Crypto";
|
||||
this.description = "Attempts to decrypt the given ETH keystore file, with the passed in password. Will return the private key if successful, error out if not.";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.infoURL = "https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition";
|
||||
this.args = [
|
||||
{
|
||||
name: "password",
|
||||
type: "string",
|
||||
value: ""
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const password = args[0];
|
||||
let jsonObj, dkey;
|
||||
// We parse the JSON object first and throw an error if its not JSON.
|
||||
try {
|
||||
jsonObj = JSON.parse(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Invalid input, not JSON. Data: ${err.message}`);
|
||||
}
|
||||
|
||||
// We then check for a crypto property, and that crypto should have a kdf and kdfparams property.
|
||||
if (!Object.prototype.hasOwnProperty.call(jsonObj, "crypto") || !Object.prototype.hasOwnProperty.call(jsonObj.crypto, "kdfparams") || !Object.prototype.hasOwnProperty.call(jsonObj.crypto, "kdf")) {
|
||||
throw new OperationError(`Error. Invalid JSON blob, missing either a crypto, crypto.kdf or crypto.kdfparams object.`);
|
||||
}
|
||||
const kdfParams = jsonObj.crypto.kdfparams;
|
||||
const kdfType = jsonObj.crypto.kdf;
|
||||
|
||||
// We compute the kdf.
|
||||
if (kdfType === "scrypt") {
|
||||
try {
|
||||
// We compute the salt, and the compute the scrypt.
|
||||
const salt = Buffer.from(Utils.convertToByteArray(kdfParams.salt, "hex"));
|
||||
const data = scryptsy(password, salt, kdfParams.n, kdfParams.r, kdfParams.p, kdfParams.dklen,
|
||||
p =>{
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage(`Progress: ${p.percent.toFixed(0)}%`);
|
||||
});
|
||||
// Result of the SCRYPT in hex.
|
||||
dkey = data.toString("hex");
|
||||
} catch (err) {
|
||||
throw new OperationError("Error: " + err.toString());
|
||||
}
|
||||
} else if (kdfType === "pbkdf2") {
|
||||
// If the kdf is PBKDF2, we check to make sure it has a prf property, and that property is hmac-sha256
|
||||
if (!Object.prototype.hasOwnProperty.call(kdfParams, "prf") || kdfParams.prf !== "hmac-sha256") {
|
||||
throw new OperationError(`Error with PBKDF2. Either HMAC function not present, or is not hmac-sha256. It is: ` + JSON.stringify(kdfParams));
|
||||
}
|
||||
// We compute the pbkdf2 and cast to hex.
|
||||
const iterations = kdfParams.c;
|
||||
const keyLength = kdfParams.dklen;
|
||||
const salt = Utils.convertToByteString(kdfParams.salt, "hex");
|
||||
dkey = forge.util.bytesToHex(forge.pkcs5.pbkdf2(password, salt, iterations, keyLength, "sha256"));
|
||||
|
||||
} else {
|
||||
// If there's a different KDF, we err out.
|
||||
throw new OperationError("We don't support KDF type " + kdfType + " ... yet.");
|
||||
}
|
||||
|
||||
const mackey = dkey.slice(-32,);
|
||||
const decryptionkey = dkey.slice(0, 32);
|
||||
const ciphertext = jsonObj.crypto.ciphertext;
|
||||
const hmac = jsonObj.crypto.mac;
|
||||
const algo = JSSHA3.keccak256;
|
||||
|
||||
const testmac = algo(fromHex(mackey + ciphertext, "hex"));
|
||||
if (testmac === hmac) {
|
||||
// If the MAC passes, we can decrypt.
|
||||
// We check for the right data to decrypt.
|
||||
if (!Object.prototype.hasOwnProperty.call(jsonObj.crypto, "cipherparams") || !Object.prototype.hasOwnProperty.call(jsonObj.crypto.cipherparams, "iv")) {
|
||||
throw new OperationError("We are missing needed cipherparams and IV.");
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(jsonObj.crypto, "ciphertext") || !Object.prototype.hasOwnProperty.call(jsonObj.crypto.cipherparams, "iv")) {
|
||||
throw new OperationError("We are the ciphertext");
|
||||
}
|
||||
// We grab the key, and IV, and ciphertext
|
||||
const key = Utils.convertToByteString(decryptionkey, "hex");
|
||||
const iv = Utils.convertToByteString(jsonObj.crypto.cipherparams.iv, "hex");
|
||||
const cipherinput = Utils.convertToByteString(jsonObj.crypto.ciphertext, "hex");
|
||||
// We create the decryptor.
|
||||
const decipher = forge.cipher.createDecipher("AES-CTR", key);
|
||||
// We do the decryption.
|
||||
decipher.start({
|
||||
iv: iv.length === 0 ? "" : iv,
|
||||
tag: undefined,
|
||||
additionalData: undefined
|
||||
});
|
||||
decipher.update(forge.util.createBuffer(cipherinput));
|
||||
const result = decipher.finish();
|
||||
if (result) {
|
||||
return decipher.output.toHex();
|
||||
} else {
|
||||
throw new OperationError("Unable to decrypt keystore with these parameters.");
|
||||
}
|
||||
} else {
|
||||
// In this case the MAC failed so we error out.
|
||||
throw new OperationError("MAC error. We got: " + testmac + " , but we wanted. " + hmac);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DecryptKeystoreFile;
|
Loading…
Add table
Add a link
Reference in a new issue