mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-08 15:25:01 -04:00
596 lines
17 KiB
JavaScript
596 lines
17 KiB
JavaScript
import * as openpgp from "openpgp";
|
|
import Utils from "../Utils.js";
|
|
|
|
/**
|
|
* PGP operations.
|
|
*
|
|
* @author tlwr [toby@toby.codes]
|
|
* @copyright Crown Copyright 2017
|
|
* @license Apache-2.0
|
|
*
|
|
* @namespace
|
|
*/
|
|
|
|
const PGP = {
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
ARMOUR_TYPES: [
|
|
"Message",
|
|
"Public key",
|
|
"Private key",
|
|
],
|
|
|
|
|
|
/**
|
|
* @constant
|
|
* @default
|
|
*/
|
|
ARMOUR_TYPE_MAPPING: {
|
|
"Message": 3,
|
|
"Public key": 4,
|
|
"Private key": 5,
|
|
},
|
|
|
|
|
|
/**
|
|
* Encrypts the input using PGP.
|
|
*
|
|
* @param {string} input - plaintext to encrypt
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runEncrypt: function (plaintext, args) {
|
|
let publicKey = args[0],
|
|
publicKeys;
|
|
|
|
try {
|
|
publicKeys = openpgp.key.readArmored(publicKey).keys;
|
|
} catch (err) {
|
|
throw "Cannot read public key: " + err;
|
|
}
|
|
|
|
let options = {
|
|
data: plaintext,
|
|
publicKeys: publicKeys,
|
|
};
|
|
|
|
return openpgp.encrypt(options)
|
|
.then(ciphertext => ciphertext.data)
|
|
.catch(function(err) {
|
|
throw "Could not encrypt text: " + err;
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Decrypts the input using PGP.
|
|
*
|
|
* @param {string} input - ciphertext to decrypt
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runDecrypt: function (input, args) {
|
|
let privateKey = args[0],
|
|
password = args[1],
|
|
message;
|
|
|
|
try {
|
|
privateKey = openpgp.key.readArmored(privateKey).keys[0];
|
|
} catch (err) {
|
|
throw "Cannot read private key: " + err;
|
|
}
|
|
|
|
try {
|
|
message = openpgp.message.readArmored(input);
|
|
} catch (err) {
|
|
throw "Cannot read message: " + err;
|
|
}
|
|
|
|
let options = {
|
|
message: message,
|
|
privateKey: privateKey,
|
|
};
|
|
|
|
if (password) {
|
|
privateKey.decrypt(password);
|
|
}
|
|
if (privateKey.primaryKey.encrypted !== null) {
|
|
throw "Could not decrypt private key.";
|
|
}
|
|
|
|
return openpgp.decrypt(options)
|
|
.then(plaintext => plaintext.data)
|
|
.catch(function(err) {
|
|
throw "Could not decrypt message: " + err;
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Signs the input using PGP.
|
|
*
|
|
* @param {string} input - data to be signed
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runSign: function (input, args) {
|
|
let publicKey = args[0],
|
|
privateKey = args[1],
|
|
password = args[2],
|
|
publicKeys,
|
|
privateKeys;
|
|
|
|
try {
|
|
publicKeys = openpgp.key.readArmored(publicKey).keys;
|
|
} catch (err) {
|
|
throw "Could not read public key: " + err;
|
|
}
|
|
|
|
try {
|
|
privateKeys = openpgp.key.readArmored(privateKey).keys;
|
|
} catch (err) {
|
|
throw "Could not read private key: " + err;
|
|
}
|
|
|
|
if (password) {
|
|
privateKeys[0].decrypt(password);
|
|
}
|
|
if (privateKeys[0].primaryKey.encrypted !== null) {
|
|
throw "Could not decrypt private key.";
|
|
}
|
|
|
|
let options = {
|
|
data: input,
|
|
publicKeys: publicKeys,
|
|
privateKeys: privateKeys,
|
|
};
|
|
|
|
return openpgp.encrypt(options)
|
|
.then(signedData => signedData.data)
|
|
.catch(function(err) {
|
|
throw "Could not sign input: " + err;
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Verifies the signed input using PGP.
|
|
*
|
|
* @param {string} input - signed input to verify
|
|
* @param {Object[]} args
|
|
* @returns {string} - the original message, and a summary of the verification process
|
|
*/
|
|
runVerify: function (input, args) {
|
|
let publicKey = args[0],
|
|
privateKey = args[1],
|
|
password = args[2],
|
|
publicKeys,
|
|
privateKeys,
|
|
message;
|
|
|
|
try {
|
|
publicKeys = openpgp.key.readArmored(publicKey).keys;
|
|
} catch (err) {
|
|
throw "Could not read public key: " + err;
|
|
}
|
|
|
|
try {
|
|
privateKeys = openpgp.key.readArmored(privateKey).keys;
|
|
} catch (err) {
|
|
throw "Could not read private key: " + err;
|
|
}
|
|
|
|
if (password) {
|
|
privateKeys[0].decrypt(password);
|
|
}
|
|
if (privateKeys[0].primaryKey.encrypted !== null) {
|
|
throw "Could not decrypt private key.";
|
|
}
|
|
|
|
try {
|
|
message = openpgp.message.readArmored(input);
|
|
} catch (err) {
|
|
throw "Could not read encrypted message: " + err;
|
|
}
|
|
|
|
let packetContainingAlgorithms = message.packets.filterByTag(
|
|
openpgp.enums.packet.publicKeyEncryptedSessionKey
|
|
)[0];
|
|
|
|
let verification = {
|
|
verified: false,
|
|
pkAlgorithm: packetContainingAlgorithms.publicKeyAlgorithm,
|
|
sessionAlgorithm: packetContainingAlgorithms.sessionKeyAlgorithm,
|
|
author: publicKeys[0].users[0].userId.userid,
|
|
recipient: privateKeys[0].users[0].userId.userid,
|
|
keyID: "",
|
|
message: "",
|
|
};
|
|
|
|
return openpgp.decrypt({
|
|
message: message,
|
|
publicKeys: publicKeys,
|
|
privateKey: privateKeys[0],
|
|
})
|
|
.then(decrypted => {
|
|
if (decrypted.signatures) {
|
|
// valid is either true or null, casting required.
|
|
verification.verified = !!decrypted.signatures[0].valid;
|
|
verification.keyID = decrypted.signatures[0].keyid.toHex();
|
|
}
|
|
|
|
return [
|
|
"Verified: " + verification.verified,
|
|
"Key ID: " + verification.keyID,
|
|
"Encrypted for: " + verification.recipient,
|
|
"Signed by: " + verification.author,
|
|
"Signed with: " +
|
|
verification.pkAlgorithm +
|
|
"/" +
|
|
verification.sessionAlgorithm,
|
|
"\n",
|
|
decrypted.data,
|
|
].join("\n");
|
|
|
|
})
|
|
.catch(function(err) {
|
|
throw "Could not decrypt and verify message: " + err;
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Signs the input using PGP and outputs the plaintext, the raw PGP signature, and the ASCII armoured signature files.
|
|
*
|
|
* @param {string} input - data to be signed
|
|
* @param {Object[]} args
|
|
* @returns {HTML} - HTML file display of message, armoured signature, and bytes signature
|
|
*/
|
|
runSignDetached: function (input, args) {
|
|
let privateKey = args[0],
|
|
password = args[1],
|
|
privateKeys;
|
|
|
|
try {
|
|
privateKeys = openpgp.key.readArmored(privateKey).keys;
|
|
} catch (err) {
|
|
throw "Could not read private key: " + err;
|
|
}
|
|
|
|
if (password) {
|
|
privateKeys[0].decrypt(password);
|
|
}
|
|
if (privateKeys[0].primaryKey.encrypted !== null) {
|
|
throw "Could not decrypt private key.";
|
|
}
|
|
|
|
let bytes = openpgp.util.str2Uint8Array(input);
|
|
let message = openpgp.message.fromBinary(bytes);
|
|
|
|
let signedMessage = message.sign(privateKeys);
|
|
let signature = signedMessage.packets.filterByTag(openpgp.enums.packet.signature);
|
|
let rawSignatureBytes = signature.write();
|
|
|
|
let armouredMessage = openpgp.armor.encode(
|
|
openpgp.enums.armor.message,
|
|
rawSignatureBytes
|
|
);
|
|
armouredMessage = armouredMessage.replace(
|
|
"-----BEGIN PGP MESSAGE-----\r\n",
|
|
"-----BEGIN PGP SIGNATURE-----\r\n"
|
|
);
|
|
armouredMessage = armouredMessage.replace(
|
|
"-----END PGP MESSAGE-----\r\n",
|
|
"-----END PGP SIGNATURE-----\r\n"
|
|
);
|
|
|
|
let files = [{
|
|
fileName: "msg",
|
|
size: input.length,
|
|
contents: input,
|
|
bytes: bytes,
|
|
}, {
|
|
fileName: "msg.asc",
|
|
size: armouredMessage.length,
|
|
contents: armouredMessage,
|
|
bytes: openpgp.util.str2Uint8Array(armouredMessage),
|
|
}, {
|
|
fileName: "msg.sig",
|
|
size: rawSignatureBytes.length,
|
|
contents: openpgp.util.Uint8Array2str(rawSignatureBytes),
|
|
bytes: rawSignatureBytes,
|
|
}];
|
|
|
|
return Utils.displayFilesAsHTML(files);
|
|
},
|
|
|
|
|
|
/**
|
|
* Verifies the detached signature and input using PGP.
|
|
*
|
|
* @param {string} input - signed input to verify
|
|
* @param {Object[]} args
|
|
* @returns {string} - the original message, and a summary of the verification process
|
|
*/
|
|
runVerifyDetached: function (input, args) {
|
|
let publicKey = args[0],
|
|
armouredSignature = args[1],
|
|
publicKeys,
|
|
message;
|
|
|
|
try {
|
|
publicKeys = openpgp.key.readArmored(publicKey).keys;
|
|
} catch (err) {
|
|
throw "Could not read public key: " + err;
|
|
}
|
|
|
|
try {
|
|
message = openpgp.message.readSignedContent(
|
|
input,
|
|
armouredSignature
|
|
);
|
|
} catch (err) {
|
|
throw "Could not read armoured signature or message: " + err;
|
|
}
|
|
|
|
let packetContainingSignature = message.packets.filterByTag(
|
|
openpgp.enums.packet.signature
|
|
)[0];
|
|
|
|
let verification = {
|
|
verified: false,
|
|
pkAlgorithm: publicKeys[0].primaryKey.algorithm,
|
|
author: publicKeys[0].users[0].userId.userid,
|
|
date: packetContainingSignature.created,
|
|
keyID: "",
|
|
message: "",
|
|
};
|
|
|
|
return Promise.resolve(message.verify(publicKeys))
|
|
.then(function(signatures) {
|
|
if (signatures && signatures.length) {
|
|
verification.verified = !!signatures[0].valid;
|
|
verification.keyID = signatures[0].keyid.toHex();
|
|
}
|
|
|
|
return [
|
|
"Verified: " + verification.verified,
|
|
"Key ID: " + verification.keyID,
|
|
"Signed on: " + verification.date,
|
|
"Signed by: " + verification.author,
|
|
"Signed with: " + verification.pkAlgorithm,
|
|
"\n",
|
|
input,
|
|
].join("\n");
|
|
|
|
})
|
|
.catch(function(err) {
|
|
throw "Could not verify message: " + err;
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Clearsigns the input using PGP.
|
|
*
|
|
* @param {string} input - data to be signed
|
|
* @param {Object[]} args
|
|
* @returns {string}
|
|
*/
|
|
runSignCleartext: function (input, args) {
|
|
let privateKey = args[0],
|
|
password = args[1],
|
|
privateKeys;
|
|
|
|
try {
|
|
privateKeys = openpgp.key.readArmored(privateKey).keys;
|
|
} catch (err) {
|
|
throw "Could not read private key: " + err;
|
|
}
|
|
|
|
if (password) {
|
|
privateKeys[0].decrypt(password);
|
|
}
|
|
if (privateKeys[0].primaryKey.encrypted !== null) {
|
|
throw "Could not decrypt private key.";
|
|
}
|
|
|
|
let options = {
|
|
data: input,
|
|
privateKeys: privateKeys,
|
|
};
|
|
|
|
return openpgp.sign(options)
|
|
.then(signedData => signedData.data)
|
|
.catch(function(err) {
|
|
throw "Could not clearsign input: " + err;
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Verifies the clearsigned input using PGP.
|
|
*
|
|
* @param {string} input - signed input to verify
|
|
* @param {Object[]} args
|
|
* @returns {string} - the original message, and a summary of the verification process
|
|
*/
|
|
runVerifyCleartext: function (input, args) {
|
|
let publicKey = args[0],
|
|
publicKeys,
|
|
message;
|
|
|
|
try {
|
|
publicKeys = openpgp.key.readArmored(publicKey).keys;
|
|
} catch (err) {
|
|
throw "Could not read public key: " + err;
|
|
}
|
|
|
|
try {
|
|
message = openpgp.cleartext.readArmored(input);
|
|
} catch (err) {
|
|
throw "Could not read input message: " + err;
|
|
}
|
|
|
|
let packetContainingSignature = message.packets.filterByTag(
|
|
openpgp.enums.packet.signature
|
|
)[0];
|
|
|
|
let verification = {
|
|
verified: false,
|
|
pkAlgorithm: publicKeys[0].primaryKey.algorithm,
|
|
author: publicKeys[0].users[0].userId.userid,
|
|
date: packetContainingSignature.created,
|
|
keyID: "",
|
|
message: message.text,
|
|
};
|
|
|
|
return openpgp.verify({
|
|
message: message,
|
|
publicKeys: publicKeys,
|
|
})
|
|
.then(verifiedData => {
|
|
if (verifiedData.signatures) {
|
|
// valid is either true or null, casting required.
|
|
verification.verified = !!verifiedData.signatures[0].valid;
|
|
verification.keyID = verifiedData.signatures[0].keyid.toHex();
|
|
}
|
|
|
|
return [
|
|
"Verified: " + verification.verified,
|
|
"Key ID: " + verification.keyID,
|
|
"Signed on: " + verification.date,
|
|
"Signed by: " + verification.author,
|
|
"Signed with: " + verification.pkAlgorithm,
|
|
"\n",
|
|
verification.message,
|
|
].join("\n");
|
|
})
|
|
.catch(function(err) {
|
|
throw "Could not verify message: " + err;
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Generates a PGP key pair.
|
|
*
|
|
* @param {string} input is ignored
|
|
* @param {Object[]} args
|
|
* @returns {string} - armoured public key and private key separated by whitespace.
|
|
*/
|
|
runGenKeyPair: function (input, args) {
|
|
let password = args[0],
|
|
keySize = parseInt(args[1], 10),
|
|
name = args[2],
|
|
email = args[3];
|
|
|
|
let options = {
|
|
numBits: keySize,
|
|
userIds: [{name: name, email: email}],
|
|
};
|
|
|
|
if (password) {
|
|
options.passphrase = password;
|
|
}
|
|
|
|
return openpgp.generateKey(options)
|
|
.then(key => {
|
|
return [
|
|
key.publicKeyArmored,
|
|
key.privateKeyArmored,
|
|
].join(""); // Preceding and trailing newlines are already generated.
|
|
})
|
|
.catch(function(err) {
|
|
throw "Could not generate key pair: " + err;
|
|
});
|
|
},
|
|
|
|
|
|
/**
|
|
* Turns a PGP clearsigned message into a detached signature.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {HTML} - HTML file display of message, armoured signature, and bytes signature
|
|
*/
|
|
runDetachClearsig: function (input, args) {
|
|
let message;
|
|
|
|
try {
|
|
message = openpgp.cleartext.readArmored(input);
|
|
} catch (err) {
|
|
throw "Could not read input message: " + err;
|
|
}
|
|
|
|
let cleartext = message.getText();
|
|
let clearbytes = openpgp.util.str2Uint8Array(cleartext);
|
|
|
|
let signature = message.packets.filterByTag(openpgp.enums.packet.signature);
|
|
let rawSignatureBytes = signature.write();
|
|
|
|
let armouredMessage = openpgp.armor.encode(
|
|
openpgp.enums.armor.message,
|
|
rawSignatureBytes
|
|
);
|
|
armouredMessage = armouredMessage.replace(
|
|
"-----BEGIN PGP MESSAGE-----\r\n",
|
|
"-----BEGIN PGP SIGNATURE-----\r\n"
|
|
);
|
|
armouredMessage = armouredMessage.replace(
|
|
"-----END PGP MESSAGE-----\r\n",
|
|
"-----END PGP SIGNATURE-----\r\n"
|
|
);
|
|
|
|
let files = [{
|
|
fileName: "msg",
|
|
size: cleartext.length,
|
|
contents: cleartext,
|
|
bytes: clearbytes,
|
|
}, {
|
|
fileName: "msg.asc",
|
|
size: armouredMessage.length,
|
|
contents: armouredMessage,
|
|
bytes: openpgp.util.str2Uint8Array(armouredMessage),
|
|
}, {
|
|
fileName: "msg.sig",
|
|
size: rawSignatureBytes.length,
|
|
contents: openpgp.util.Uint8Array2str(rawSignatureBytes),
|
|
bytes: rawSignatureBytes,
|
|
}];
|
|
|
|
return Utils.displayFilesAsHTML(files);
|
|
},
|
|
|
|
|
|
/**
|
|
* Turns raw PGP bytes into an ASCII armoured string.
|
|
*
|
|
* @param {byteArray} input
|
|
* @param {Object[]} args
|
|
* @returns {string} - armoured public key and private key separated by whitespace.
|
|
*/
|
|
runAddArmour: function (input, args) {
|
|
let armourType = PGP.ARMOUR_TYPE_MAPPING[args[0]];
|
|
return openpgp.armor.encode(armourType, input);
|
|
},
|
|
|
|
|
|
/**
|
|
* Turns an ASCII armoured string into raw PGP bytes.
|
|
*
|
|
* @param {string} input
|
|
* @param {Object[]} args
|
|
* @returns {byteArray}
|
|
*/
|
|
runRemoveArmour: function (input, args) {
|
|
let decoded = openpgp.armor.decode(input);
|
|
let uint8bytes = decoded.data;
|
|
let bytes = Array.prototype.slice.call(uint8bytes);
|
|
return bytes;
|
|
},
|
|
};
|
|
|
|
export default PGP;
|