diff --git a/.babelrc b/.babelrc index 92a857c..094c059 100644 --- a/.babelrc +++ b/.babelrc @@ -5,7 +5,7 @@ "chrome": 40, "firefox": 35, "edge": 14, - "node": "6.5", + "node": "6.5" }, "modules": false, "useBuiltIns": true diff --git a/Gruntfile.js b/Gruntfile.js index 7a9890f..b767a09 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -318,7 +318,7 @@ module.exports = function (grunt) { chunks: false, modules: false, entrypoints: false, - warningsFilter: /source-map/, + warningsFilter: [/source-map/, /dependency is an expression/], } }, start: { diff --git a/package-lock.json b/package-lock.json index 8a220bc..20c6d90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1197,6 +1197,11 @@ "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", "dev": true }, + "bn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bn/-/bn-1.0.1.tgz", + "integrity": "sha1-oVOCXmsessLbdyYUmwR6B84KO7M=" + }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", @@ -1499,6 +1504,11 @@ "integrity": "sha1-/TVGSkA/b5EXwt42Cez/nK4ABYg=", "dev": true }, + "bzip-deflate": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bzip-deflate/-/bzip-deflate-1.0.0.tgz", + "integrity": "sha1-sC2wB+83vrzCk4Skssb08PTHlsk=" + }, "cacache": { "version": "10.0.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", @@ -2525,8 +2535,7 @@ "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" }, "deep-extend": { "version": "0.4.2", @@ -2915,16 +2924,23 @@ } }, "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", - "dev": true + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", + "integrity": "sha1-eILzCt3lskDM+n99eMVIMwlRrkI=" }, "es6-promise-polyfill": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/es6-promise-polyfill/-/es6-promise-polyfill-1.2.0.tgz", "integrity": "sha1-84kl8jyz4+jObNqP93T867sJDN4=" }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "4.0.5" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -3957,7 +3973,7 @@ "dev": true, "requires": { "commondir": "1.0.1", - "make-dir": "1.2.0", + "make-dir": "1.1.0", "pkg-dir": "2.0.0" } }, @@ -6081,6 +6097,24 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "iced-error": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/iced-error/-/iced-error-0.0.12.tgz", + "integrity": "sha1-4KhhRigXzwzpdLE/ymEtOg1dEL4=" + }, + "iced-lock": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/iced-lock/-/iced-lock-1.1.0.tgz", + "integrity": "sha1-YRbvHKs6zW5rEIk7snumIv0/3nI=", + "requires": { + "iced-runtime": "1.0.3" + } + }, + "iced-runtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/iced-runtime/-/iced-runtime-1.0.3.tgz", + "integrity": "sha1-LU9PuZmreqVDCxk8d6f85BGDGc4=" + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -7030,12 +7064,69 @@ "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.6.tgz", "integrity": "sha1-RYbwCCPjAuWZokSke+IMYardD88=" }, + "kbpgp": { + "version": "2.0.76", + "resolved": "https://registry.npmjs.org/kbpgp/-/kbpgp-2.0.76.tgz", + "integrity": "sha1-qKtufM8279812BNdfJb/bpSLMAI=", + "requires": { + "bn": "1.0.1", + "bzip-deflate": "1.0.0", + "deep-equal": "1.0.1", + "iced-error": "0.0.12", + "iced-lock": "1.1.0", + "iced-runtime": "1.0.3", + "keybase-ecurve": "1.0.0", + "keybase-nacl": "1.0.10", + "minimist": "1.2.0", + "pgp-utils": "0.0.34", + "purepack": "1.0.4", + "triplesec": "3.0.26", + "tweetnacl": "0.13.3" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "tweetnacl": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz", + "integrity": "sha1-1ii1bzvMPVrnS6nUwacE3vWrS1Y=" + } + } + }, "kew": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", "dev": true }, + "keybase-ecurve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keybase-ecurve/-/keybase-ecurve-1.0.0.tgz", + "integrity": "sha1-xrxyrdpGA/0xhP7n6ZaU7Y/WmtI=", + "requires": { + "bn": "1.0.1" + } + }, + "keybase-nacl": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/keybase-nacl/-/keybase-nacl-1.0.10.tgz", + "integrity": "sha1-OGWDHpSBUWSI33y9mJRn6VDYeos=", + "requires": { + "iced-runtime": "1.0.3", + "tweetnacl": "0.13.3", + "uint64be": "1.0.1" + }, + "dependencies": { + "tweetnacl": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz", + "integrity": "sha1-1ii1bzvMPVrnS6nUwacE3vWrS1Y=" + } + } + }, "killable": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", @@ -7523,9 +7614,9 @@ "dev": true }, "make-dir": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", - "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", + "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", "dev": true, "requires": { "pify": "3.0.0" @@ -7853,6 +7944,14 @@ "moment": "2.20.1" } }, + "more-entropy": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/more-entropy/-/more-entropy-0.0.7.tgz", + "integrity": "sha1-Z7/G96hvJvvDeqyD/UbYjGHRCbU=", + "requires": { + "iced-runtime": "1.0.3" + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -8605,13 +8704,22 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "pgp-utils": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/pgp-utils/-/pgp-utils-0.0.34.tgz", + "integrity": "sha1-2E9J98GTteC5QV9cxcKmle15DCM=", + "requires": { + "iced-error": "0.0.12", + "iced-runtime": "1.0.3" + } + }, "phantomjs-prebuilt": { "version": "2.1.16", "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz", "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", "dev": true, "requires": { - "es6-promise": "4.2.4", + "es6-promise": "4.0.5", "extract-zip": "1.6.6", "fs-extra": "1.0.0", "hasha": "2.2.0", @@ -9601,8 +9709,7 @@ "progress": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" }, "promise": { "version": "7.3.1", @@ -9682,6 +9789,11 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, + "purepack": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/purepack/-/purepack-1.0.4.tgz", + "integrity": "sha1-CGKC/ZOShfWGZLqam7oxzbFlzNI=" + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -11663,6 +11775,18 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "triplesec": { + "version": "3.0.26", + "resolved": "https://registry.npmjs.org/triplesec/-/triplesec-3.0.26.tgz", + "integrity": "sha1-3/K7R1ikIzcuc5o5fYmR8Fl9CsE=", + "requires": { + "iced-error": "0.0.12", + "iced-lock": "1.1.0", + "iced-runtime": "1.0.3", + "more-entropy": "0.0.7", + "progress": "1.1.8" + } + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -11820,6 +11944,11 @@ } } }, + "uint64be": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uint64be/-/uint64be-1.0.1.tgz", + "integrity": "sha1-H3FUIC8qG4rzU4cd2mUb80zpPpU=" + }, "underscore": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", diff --git a/package.json b/package.json index 6d9e75b..da3da80 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.3", + "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.6.1", "css-loader": "^0.28.10", "eslint": "^4.18.1", @@ -81,6 +82,7 @@ "ctph.js": "0.0.5", "diff": "^3.4.0", "escodegen": "^1.9.1", + "es6-promisify": "^5.0.0", "esmangle": "^1.0.1", "esprima": "^4.0.0", "exif-parser": "^0.1.12", @@ -95,6 +97,7 @@ "jsrsasign": "8.0.6", "lodash": "^4.17.5", "loglevel": "^1.6.1", + "kbpgp": "^2.0.76", "loglevel-message-prefix": "^3.0.0", "moment": "^2.20.1", "moment-timezone": "^0.5.14", diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index 61026d3..b2c404f 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -114,6 +114,11 @@ const Categories = [ "Hex to PEM", "Hex to Object Identifier", "Object Identifier to Hex", + "Generate PGP Key Pair", + "PGP Encrypt", + "PGP Decrypt", + "PGP Encrypt and Sign", + "PGP Decrypt and Verify", ] }, { diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js old mode 100755 new mode 100644 index 71d3c46..32095c3 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -28,6 +28,7 @@ import MAC from "../operations/MAC.js"; import MorseCode from "../operations/MorseCode.js"; import NetBIOS from "../operations/NetBIOS.js"; import PHP from "../operations/PHP.js"; +import PGP from "../operations/PGP.js"; import PublicKey from "../operations/PublicKey.js"; import Punycode from "../operations/Punycode.js"; import Regex from "../operations/Regex.js"; @@ -4159,6 +4160,150 @@ const OperationConfig = { outputType: "string", args: [] }, + "Generate PGP Key Pair": { + module: "PGP", + description: "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.", + inputType: "string", + outputType: "string", + args: [ + { + name: "Key type", + type: "option", + value: PGP.KEY_TYPES + }, + { + name: "Password (optional)", + type: "string", + value: "" + }, + { + name: "Name (optional)", + type: "string", + value: "" + }, + { + name: "Email (optional)", + type: "string", + value: "" + }, + ] + }, + "PGP Encrypt": { + module: "PGP", + 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 uses the Keybase implementation of PGP.", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Public key of recipient", + type: "text", + value: "" + }, + ] + }, + "PGP Decrypt": { + module: "PGP", + 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 uses the Keybase implementation of PGP.", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Private key of recipient", + type: "text", + value: "" + }, + { + name: "Private key passphrase", + type: "string", + value: "" + }, + ] + }, + "PGP Encrypt and Sign": { + module: "PGP", + 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 uses the Keybase 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 Decrypt and 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 uses the Keybase 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 new file mode 100644 index 0000000..89246cb --- /dev/null +++ b/src/core/config/modules/PGP.js @@ -0,0 +1,25 @@ +import PGP from "../../operations/PGP.js"; + + +/** + * PGP module. + * + * Libraries: + * - kbpgp + * + * @author tlwr [toby@toby.codes] + * @author Matt C [matt@artemisbot.uk] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +let OpModules = typeof self === "undefined" ? {} : self.OpModules || {}; + +OpModules.PGP = { + "Generate PGP Key Pair": PGP.runGenerateKeyPair, + "PGP Encrypt": PGP.runEncrypt, + "PGP Decrypt": PGP.runDecrypt, + "PGP Encrypt and Sign": PGP.runSign, + "PGP Decrypt and Verify": PGP.runVerify, +}; + +export default OpModules; diff --git a/src/core/operations/PGP.js b/src/core/operations/PGP.js new file mode 100755 index 0000000..95fbdcc --- /dev/null +++ b/src/core/operations/PGP.js @@ -0,0 +1,358 @@ +import * as kbpgp from "kbpgp"; +import promisify from "es6-promisify"; + + +/** + * PGP operations. + * + * @author tlwr [toby@toby.codes] + * @author Matt C [matt@artemisbot.uk] + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + * + * @namespace + */ +const PGP = { + + /** + * @constant + * @default + */ + KEY_TYPES: ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384"], + + + /** + * Get size of subkey + * + * @private + * @param {number} keySize + * @returns {number} + */ + _getSubkeySize(keySize) { + return { + 1024: 1024, + 2048: 1024, + 4096: 2048, + 256: 256, + 384: 256, + }[keySize]; + }, + + + /** + * Progress callback + * + * @private + */ + _ASP: new kbpgp.ASP({ + "progress_hook": info => { + let msg = ""; + + switch (info.what) { + case "guess": + msg = "Guessing a prime"; + break; + case "fermat": + msg = "Factoring prime using Fermat's factorization method"; + break; + case "mr": + msg = "Performing Miller-Rabin primality test"; + break; + case "passed_mr": + msg = "Passed Miller-Rabin primality test"; + break; + case "failed_mr": + msg = "Failed Miller-Rabin primality test"; + break; + case "found": + msg = "Prime found"; + break; + default: + msg = `Stage: ${info.what}`; + } + + if (ENVIRONMENT_IS_WORKER()) + self.sendStatusMessage(msg); + } + }), + + + /** + * 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. + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + runGenerateKeyPair(input, args) { + let [keyType, keySize] = args[0].split("-"), + password = args[1], + name = args[2], + email = args[3], + userIdentifier = ""; + + if (name) userIdentifier += name; + if (email) userIdentifier += ` <${email}>`; + + let flags = kbpgp.const.openpgp.certify_keys; + flags |= kbpgp.const.openpgp.sign_data; + flags |= kbpgp.const.openpgp.auth; + flags |= kbpgp.const.openpgp.encrypt_comm; + flags |= kbpgp.const.openpgp.encrypt_storage; + + let keyGenerationOptions = { + userid: userIdentifier, + ecc: keyType === "ecc", + primary: { + "nbits": keySize, + "flags": flags, + "expire_in": 0 + }, + subkeys: [{ + "nbits": PGP._getSubkeySize(keySize), + "flags": kbpgp.const.openpgp.sign_data, + "expire_in": 86400 * 365 * 8 + }, { + "nbits": PGP._getSubkeySize(keySize), + "flags": kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage, + "expire_in": 86400 * 365 * 2 + }], + asp: PGP._ASP + }; + + return new Promise(async (resolve, reject) => { + try { + const unsignedKey = await promisify(kbpgp.KeyManager.generate)(keyGenerationOptions); + await promisify(unsignedKey.sign, unsignedKey)({}); + let signedKey = unsignedKey; + let privateKeyExportOptions = {}; + if (password) privateKeyExportOptions.passphrase = password; + const privateKey = await promisify(signedKey.export_pgp_private, signedKey)(privateKeyExportOptions); + const publicKey = await promisify(signedKey.export_pgp_public, signedKey)({}); + resolve(privateKey + "\n" + publicKey.trim()); + } catch (err) { + reject(`Error whilst generating key pair: ${err}`); + } + }); + }, + + + /** + * PGP Encrypt operation. + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + async runEncrypt(input, args) { + let plaintextMessage = input, + plainPubKey = args[0], + key, + encryptedMessage; + + if (!plainPubKey) return "Enter the public key of the recipient."; + + try { + key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({ + armored: plainPubKey, + }); + } catch (err) { + throw `Could not import public key: ${err}`; + } + + try { + encryptedMessage = await promisify(kbpgp.box)({ + "msg": plaintextMessage, + "encrypt_for": key, + "asp": PGP._ASP + }); + } catch (err) { + throw `Couldn't encrypt message with provided public key: ${err}`; + } + + 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(), + plaintextMessage; + + if (!privateKey) return "Enter the private key of the recipient."; + + const key = await PGP._importPrivateKey(privateKey, passphrase); + keyring.add_key_manager(key); + + try { + plaintextMessage = await promisify(kbpgp.unbox)({ + armored: encryptedMessage, + keyfetch: keyring, + asp: PGP._ASP + }); + } catch (err) { + throw `Couldn't decrypt message with provided private key: ${err}`; + } + + 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], + signedMessage; + + if (!privateKey) return "Enter the private key of the signer."; + if (!publicKey) return "Enter the public key of the recipient."; + 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, + "asp": PGP._ASP + }); + } 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(), + unboxedLiterals; + + if (!publicKey) return "Enter the public key of the signer."; + if (!privateKey) return "Enter the private key of the recipient."; + 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, + asp: PGP._ASP + }); + 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()}`, + "----------------------------------\n" + ].join("\n"); + text += unboxedLiterals.toString(); + return text.trim(); + } else { + return "Could not identify a key manager."; + } + } else { + return "The data does not appear to be signed."; + } + } catch (err) { + return `Couldn't verify message: ${err}`; + } + }, +}; + +export default PGP; diff --git a/webpack.config.js b/webpack.config.js index 362bea7..d4b27da 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -109,7 +109,7 @@ module.exports = { chunks: false, modules: false, entrypoints: false, - warningsFilter: /source-map/, + warningsFilter: [/source-map/, /dependency is an expression/], }, node: { fs: "empty"