diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js
index 9d51ff52..779f91cf 100644
--- a/src/core/config/OperationConfig.js
+++ b/src/core/config/OperationConfig.js
@@ -3951,12 +3951,20 @@ const OperationConfig = {
"PGP Encrypt": {
module: "PGP",
manualBake: true,
- description: "",
+ description: [
+ "Input: the message you want to encrypt.",
+ "
",
+ "Arguments: the ASCII-armoured PGP public key of the recipient.",
+ "
",
+ "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
+ "
",
+ "This function relies on kbpgp.js for the implementation of PGP.",
+ ].join("\n"),
inputType: "string",
outputType: "string",
args: [
{
- name: "Public key",
+ name: "Public key of recipient",
type: "text",
value: ""
},
@@ -3965,20 +3973,98 @@ const OperationConfig = {
"PGP Decrypt": {
module: "PGP",
manualBake: true,
- description: "",
+ description: [
+ "Input: the ASCII-armoured PGP message you want to decrypt.",
+ "
",
+ "Arguments: the ASCII-armoured PGP private key of the recipient, ",
+ "(and the private key password if necessary).",
+ "
",
+ "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
+ "
",
+ "This function relies on kbpgp.js for the implementation of PGP.",
+ ].join("\n"),
inputType: "string",
outputType: "string",
args: [
{
- name: "Private key",
+ name: "Private key of recipient",
type: "text",
value: ""
},
{
- name: "Passphrase",
+ name: "Private key passphrase",
+ type: "string",
+ value: ""
+ },
+ ]
+ },
+ "PGP Sign": {
+ module: "PGP",
+ manualBake: true,
+ description: [
+ "Input: the cleartext you want to sign.",
+ "
",
+ "Arguments: the ASCII-armoured private key of the signer (plus the private key password if necessary)",
+ "and the ASCII-armoured PGP public key of the recipient.",
+ "
",
+ "This operation uses PGP to produce an encrypted digital signature.",
+ "
",
+ "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
+ "
",
+ "This function relies on kbpgp.js for the implementation of PGP.",
+ ].join("\n"),
+ inputType: "string",
+ outputType: "string",
+ args: [
+ {
+ name: "Private key of signer",
type: "text",
value: ""
},
+ {
+ name: "Private key passphrase",
+ type: "string",
+ value: ""
+ },
+ {
+ name: "Public key of recipient",
+ type: "text",
+ value: ""
+ },
+ ]
+ },
+ "PGP Verify": {
+ module: "PGP",
+ description: [
+ "Input: the ASCII-armoured encrypted PGP message you want to verify.",
+ "
",
+ "Arguments: the ASCII-armoured PGP public key of the signer, ",
+ "the ASCII-armoured private key of the recipient (and the private key password if necessary).",
+ "
",
+ "This operation uses PGP to decrypt and verify an encrypted digital signature.",
+ "
",
+ "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
+ "
",
+ "This function relies on kbpgp.js for the implementation of PGP.",
+ ].join("\n"),
+ inputType: "string",
+ outputType: "string",
+ args: [
+ {
+ name: "Public key of signer",
+ type: "text",
+ value: "",
+ },
+ {
+ name: "Private key of recipient",
+ type: "text",
+ value: "",
+ },
+ {
+ name: "Private key password",
+ type: "string",
+ value: "",
+ },
]
},
};
diff --git a/src/core/config/modules/PGP.js b/src/core/config/modules/PGP.js
index 1e74b73a..702141d3 100644
--- a/src/core/config/modules/PGP.js
+++ b/src/core/config/modules/PGP.js
@@ -8,6 +8,7 @@ import PGP from "../../operations/PGP.js";
* - kbpgp
*
* @author tlwr [toby@toby.codes]
+ * @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
@@ -17,6 +18,8 @@ OpModules.PGP = {
"Generate PGP Key Pair": PGP.runGenerateKeyPair,
"PGP Encrypt": PGP.runEncrypt,
"PGP Decrypt": PGP.runDecrypt,
+ "PGP Sign": PGP.runSign,
+ "PGP Verify": PGP.runVerify,
};
export default OpModules;
diff --git a/src/core/operations/PGP.js b/src/core/operations/PGP.js
index dd8acf09..87ee53d9 100755
--- a/src/core/operations/PGP.js
+++ b/src/core/operations/PGP.js
@@ -11,6 +11,7 @@ const KEY_TYPES = ["RSA", "ECC"];
* PGP operations.
*
* @author tlwr [toby@toby.codes]
+ * @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
@@ -21,10 +22,12 @@ const PGP = {
/**
* Validate PGP Key Size
+ *
+ * @private
* @param {string} keySize
* @returns {Integer}
*/
- validateKeySize(keySize, keyType) {
+ _validateKeySize(keySize, keyType) {
if (KEY_SIZES.indexOf(keySize) < 0) {
throw `Invalid key size ${keySize}, must be in ${JSON.stringify(KEY_SIZES)}`;
}
@@ -46,10 +49,12 @@ const PGP = {
/**
* Get size of subkey
+ *
+ * @private
* @param {Integer} keySize
* @returns {Integer}
*/
- getSubkeySize(keySize) {
+ _getSubkeySize(keySize) {
return {
1024: 1024,
2048: 1024,
@@ -64,18 +69,65 @@ const PGP = {
/**
* Validate PGP Key Type
+ *
+ * @private
* @param {string} keyType
* @returns {string}
*/
- validateKeyType(keyType) {
+ _validateKeyType(keyType) {
if (KEY_TYPES.indexOf(keyType) >= 0) return keyType.toLowerCase();
throw `Invalid key type ${keyType}, must be in ${JSON.stringify(KEY_TYPES)}`;
},
+ /**
+ * Import private key and unlock if necessary
+ *
+ * @private
+ * @param {string} privateKey
+ * @param {string} [passphrase]
+ * @returns {Object}
+ */
+ async _importPrivateKey (privateKey, passphrase) {
+ try {
+ const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
+ armored: privateKey,
+ });
+ if (key.is_pgp_locked() && passphrase) {
+ if (passphrase) {
+ await promisify(key.unlock_pgp, key)({
+ passphrase
+ });
+ } else if (!passphrase) {
+ throw "Did not provide passphrase with locked private key.";
+ }
+ }
+ return key;
+ } catch (err) {
+ throw `Could not import private key: ${err}`;
+ }
+ },
+
+ /**
+ * Import public key
+ *
+ * @private
+ * @param {string} publicKey
+ * @returns {Object}
+ */
+ async _importPublicKey (publicKey) {
+ try {
+ const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
+ armored: publicKey,
+ });
+ return key;
+ } catch (err) {
+ throw `Could not import public key: ${err}`;
+ }
+ },
+
/**
* Generate PGP Key Pair operation.
*
- * @author tlwr [toby@toby.codes]
* @param {string} input
* @param {Object[]} args
* @returns {string}
@@ -87,8 +139,8 @@ const PGP = {
name = args[3],
email = args[4];
- keyType = PGP.validateKeyType(keyType);
- keySize = PGP.validateKeySize(keySize, keyType);
+ keyType = PGP._validateKeyType(keyType);
+ keySize = PGP._validateKeySize(keySize, keyType);
let userIdentifier = "";
if (name) userIdentifier += name;
@@ -109,11 +161,11 @@ const PGP = {
expire_in: 0
},
subkeys: [{
- nbits: PGP.getSubkeySize(keySize),
+ nbits: PGP._getSubkeySize(keySize),
flags: kbpgp.const.openpgp.sign_data,
expire_in: 86400 * 365 * 8
}, {
- nbits: PGP.getSubkeySize(keySize),
+ nbits: PGP._getSubkeySize(keySize),
flags: kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
expire_in: 86400 * 365 * 2
}],
@@ -134,6 +186,13 @@ const PGP = {
});
},
+ /**
+ * PGP Encrypt operation.
+ *
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
async runEncrypt(input, args) {
let plaintextMessage = input,
plainPubKey = args[0];
@@ -160,31 +219,21 @@ const PGP = {
return encryptedMessage.toString();
},
+ /**
+ * PGP Decrypt operation.
+ *
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
async runDecrypt(input, args) {
let encryptedMessage = input,
privateKey = args[0],
passphrase = args[1],
keyring = new kbpgp.keyring.KeyRing();
- let key, plaintextMessage;
-
- try {
- key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
- armored: privateKey,
- });
- if (key.is_pgp_locked() && passphrase) {
- if (passphrase) {
- await promisify(key.unlock_pgp, key)({
- passphrase
- });
- } else if (!passphrase) {
- throw "Did not provide passphrase with locked private key.";
- }
- }
- } catch (err) {
- throw `Could not import private key: ${err}`;
- }
-
+ let plaintextMessage;
+ const key = await PGP._importPrivateKey(privateKey, passphrase);
keyring.add_key_manager(key);
try {
@@ -198,6 +247,93 @@ const PGP = {
return plaintextMessage.toString();
},
+
+ /**
+ * PGP Sign Message operation.
+ *
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ async runSign(input, args) {
+ let message = input,
+ privateKey = args[0],
+ passphrase = args[1],
+ publicKey = args[2];
+
+ let signedMessage;
+ const privKey = await PGP._importPrivateKey(privateKey, passphrase);
+ const pubKey = await PGP._importPublicKey(publicKey);
+
+ try {
+ signedMessage = await promisify(kbpgp.box)({
+ msg: message,
+ encrypt_for: pubKey,
+ sign_with: privKey
+ });
+ } catch (err) {
+ throw `Couldn't sign message: ${err}`;
+ }
+
+ return signedMessage;
+ },
+
+ /**
+ * PGP Verify Message operation.
+ *
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ async runVerify(input, args) {
+ let signedMessage = input,
+ publicKey = args[0],
+ privateKey = args[1],
+ passphrase = args[2],
+ keyring = new kbpgp.keyring.KeyRing();
+
+ let unboxedLiterals;
+ const privKey = await PGP._importPrivateKey(privateKey, passphrase);
+ const pubKey = await PGP._importPublicKey(publicKey);
+ keyring.add_key_manager(privKey);
+ keyring.add_key_manager(pubKey);
+
+ try {
+ unboxedLiterals = await promisify(kbpgp.unbox)({
+ armored: signedMessage,
+ keyfetch: keyring
+ });
+ const ds = unboxedLiterals[0].get_data_signer();
+ if (ds) {
+ const km = ds.get_key_manager();
+ if (km) {
+ const signer = km.get_userids_mark_primary()[0].components;
+ let text = "Signed by ";
+ if (signer.email || signer.username || signer.comment) {
+ if (signer.username) {
+ text += `${signer.username} `;
+ }
+ if (signer.comment) {
+ text += `${signer.comment} `;
+ }
+ if (signer.email) {
+ text += `<${signer.email}>`;
+ }
+ text += "\n";
+ }
+ text += [
+ `PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
+ `Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`,
+ "----------------------------------"
+ ].join("\n");
+ text += unboxedLiterals.toString();
+ return text.trim();
+ }
+ }
+ } catch (err) {
+ throw `Couldn't verify message: ${err}`;
+ }
+ },
};
export default PGP;