瀏覽代碼

Added PGP Sign/Verify operations

Matt C 7 年之前
父節點
當前提交
7554cbda72
共有 3 個文件被更改,包括 257 次插入32 次删除
  1. 91 5
      src/core/config/OperationConfig.js
  2. 3 0
      src/core/config/modules/PGP.js
  3. 163 27
      src/core/operations/PGP.js

+ 91 - 5
src/core/config/OperationConfig.js

@@ -3951,12 +3951,20 @@ const OperationConfig = {
     "PGP Encrypt": {
     "PGP Encrypt": {
         module: "PGP",
         module: "PGP",
         manualBake: true,
         manualBake: true,
-        description: "",
+        description: [
+            "Input: the message you want to encrypt.",
+            "<br><br>",
+            "Arguments: the ASCII-armoured PGP public key of the recipient.",
+            "<br><br>",
+            "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
+            "<br><br>",
+            "This function relies on kbpgp.js for the implementation of PGP.",
+        ].join("\n"),
         inputType: "string",
         inputType: "string",
         outputType: "string",
         outputType: "string",
         args: [
         args: [
             {
             {
-                name: "Public key",
+                name: "Public key of recipient",
                 type: "text",
                 type: "text",
                 value: ""
                 value: ""
             },
             },
@@ -3965,22 +3973,100 @@ const OperationConfig = {
     "PGP Decrypt": {
     "PGP Decrypt": {
         module: "PGP",
         module: "PGP",
         manualBake: true,
         manualBake: true,
-        description: "",
+        description: [
+            "Input: the ASCII-armoured PGP message you want to decrypt.",
+            "<br><br>",
+            "Arguments: the ASCII-armoured PGP private key of the recipient, ",
+            "(and the private key password if necessary).",
+            "<br><br>",
+            "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
+            "<br><br>",
+            "This function relies on kbpgp.js for the implementation of PGP.",
+        ].join("\n"),
         inputType: "string",
         inputType: "string",
         outputType: "string",
         outputType: "string",
         args: [
         args: [
             {
             {
-                name: "Private key",
+                name: "Private key of recipient",
                 type: "text",
                 type: "text",
                 value: ""
                 value: ""
             },
             },
             {
             {
-                name: "Passphrase",
+                name: "Private key passphrase",
+                type: "string",
+                value: ""
+            },
+        ]
+    },
+    "PGP Sign": {
+        module: "PGP",
+        manualBake: true,
+        description: [
+            "Input: the cleartext you want to sign.",
+            "<br><br>",
+            "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.",
+            "<br><br>",
+            "This operation uses PGP to produce an encrypted digital signature.",
+            "<br><br>",
+            "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
+            "<br><br>",
+            "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",
                 type: "text",
                 value: ""
                 value: ""
             },
             },
         ]
         ]
     },
     },
+    "PGP Verify": {
+        module: "PGP",
+        description: [
+            "Input: the ASCII-armoured encrypted PGP message you want to verify.",
+            "<br><br>",
+            "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).",
+            "<br><br>",
+            "This operation uses PGP to decrypt and verify an encrypted digital signature.",
+            "<br><br>",
+            "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
+            "<br><br>",
+            "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: "",
+            },
+        ]
+    },
 };
 };
 
 
 
 

+ 3 - 0
src/core/config/modules/PGP.js

@@ -8,6 +8,7 @@ import PGP from "../../operations/PGP.js";
  *  - kbpgp
  *  - kbpgp
  *
  *
  * @author tlwr [toby@toby.codes]
  * @author tlwr [toby@toby.codes]
+ * @author Matt C [matt@artemisbot.uk]
  * @copyright Crown Copyright 2017
  * @copyright Crown Copyright 2017
  * @license Apache-2.0
  * @license Apache-2.0
  */
  */
@@ -17,6 +18,8 @@ OpModules.PGP = {
     "Generate PGP Key Pair": PGP.runGenerateKeyPair,
     "Generate PGP Key Pair": PGP.runGenerateKeyPair,
     "PGP Encrypt":           PGP.runEncrypt,
     "PGP Encrypt":           PGP.runEncrypt,
     "PGP Decrypt":           PGP.runDecrypt,
     "PGP Decrypt":           PGP.runDecrypt,
+    "PGP Sign":              PGP.runSign,
+    "PGP Verify":            PGP.runVerify,
 };
 };
 
 
 export default OpModules;
 export default OpModules;

+ 163 - 27
src/core/operations/PGP.js

@@ -11,6 +11,7 @@ const KEY_TYPES = ["RSA", "ECC"];
  * PGP operations.
  * PGP operations.
  *
  *
  * @author tlwr [toby@toby.codes]
  * @author tlwr [toby@toby.codes]
+ * @author Matt C [matt@artemisbot.uk]
  * @copyright Crown Copyright 2016
  * @copyright Crown Copyright 2016
  * @license Apache-2.0
  * @license Apache-2.0
  *
  *
@@ -21,10 +22,12 @@ const PGP = {
 
 
     /**
     /**
      * Validate PGP Key Size
      * Validate PGP Key Size
+     * 
+     * @private
      * @param {string} keySize
      * @param {string} keySize
      * @returns {Integer}
      * @returns {Integer}
      */
      */
-    validateKeySize(keySize, keyType) {
+    _validateKeySize(keySize, keyType) {
         if (KEY_SIZES.indexOf(keySize) < 0) {
         if (KEY_SIZES.indexOf(keySize) < 0) {
             throw `Invalid key size ${keySize}, must be in ${JSON.stringify(KEY_SIZES)}`;
             throw `Invalid key size ${keySize}, must be in ${JSON.stringify(KEY_SIZES)}`;
         }
         }
@@ -46,10 +49,12 @@ const PGP = {
 
 
     /**
     /**
      * Get size of subkey
      * Get size of subkey
+     * 
+     * @private
      * @param {Integer} keySize
      * @param {Integer} keySize
      * @returns {Integer}
      * @returns {Integer}
      */
      */
-    getSubkeySize(keySize) {
+    _getSubkeySize(keySize) {
         return {
         return {
             1024: 1024,
             1024: 1024,
             2048: 1024,
             2048: 1024,
@@ -64,18 +69,65 @@ const PGP = {
 
 
     /**
     /**
      * Validate PGP Key Type
      * Validate PGP Key Type
+     * 
+     * @private
      * @param {string} keyType
      * @param {string} keyType
      * @returns {string}
      * @returns {string}
      */
      */
-    validateKeyType(keyType) {
+    _validateKeyType(keyType) {
         if (KEY_TYPES.indexOf(keyType) >= 0) return keyType.toLowerCase();
         if (KEY_TYPES.indexOf(keyType) >= 0) return keyType.toLowerCase();
         throw `Invalid key type ${keyType}, must be in ${JSON.stringify(KEY_TYPES)}`;
         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.
      * Generate PGP Key Pair operation.
      *
      *
-     * @author tlwr [toby@toby.codes]
      * @param {string} input
      * @param {string} input
      * @param {Object[]} args
      * @param {Object[]} args
      * @returns {string}
      * @returns {string}
@@ -87,8 +139,8 @@ const PGP = {
             name     = args[3],
             name     = args[3],
             email    = args[4];
             email    = args[4];
 
 
-        keyType = PGP.validateKeyType(keyType);
-        keySize = PGP.validateKeySize(keySize, keyType);
+        keyType = PGP._validateKeyType(keyType);
+        keySize = PGP._validateKeySize(keySize, keyType);
 
 
         let userIdentifier = "";
         let userIdentifier = "";
         if (name) userIdentifier += name;
         if (name) userIdentifier += name;
@@ -109,11 +161,11 @@ const PGP = {
                 expire_in: 0
                 expire_in: 0
             },
             },
             subkeys: [{
             subkeys: [{
-                nbits: PGP.getSubkeySize(keySize),
+                nbits: PGP._getSubkeySize(keySize),
                 flags: kbpgp.const.openpgp.sign_data,
                 flags: kbpgp.const.openpgp.sign_data,
                 expire_in: 86400 * 365 * 8
                 expire_in: 86400 * 365 * 8
             }, {
             }, {
-                nbits: PGP.getSubkeySize(keySize),
+                nbits: PGP._getSubkeySize(keySize),
                 flags: kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
                 flags: kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
                 expire_in: 86400 * 365 * 2
                 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) {
     async runEncrypt(input, args) {
         let plaintextMessage = input,
         let plaintextMessage = input,
             plainPubKey      = args[0];
             plainPubKey      = args[0];
@@ -160,31 +219,21 @@ const PGP = {
         return encryptedMessage.toString();
         return encryptedMessage.toString();
     },
     },
 
 
+    /**
+     * PGP Decrypt operation.
+     *
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {string}
+     */
     async runDecrypt(input, args) {
     async runDecrypt(input, args) {
         let encryptedMessage = input,
         let encryptedMessage = input,
             privateKey  = args[0],
             privateKey  = args[0],
             passphrase = args[1],
             passphrase = args[1],
             keyring          = new kbpgp.keyring.KeyRing();
             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);
         keyring.add_key_manager(key);
 
 
         try {
         try {
@@ -198,6 +247,93 @@ const PGP = {
 
 
         return plaintextMessage.toString();
         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;
 export default PGP;