From 728d9066fedda71093b516f9a378e8df76d0cf5b Mon Sep 17 00:00:00 2001 From: tlwr Date: Sun, 8 Oct 2017 21:39:11 +0100 Subject: [PATCH] WIP inital recommit of PGP operations --- package-lock.json | 143 ++++--- package.json | 2 + src/core/config/OperationConfig.js | 348 ++++++++++++++++ src/core/config/modules/OpModules.js | 2 + src/core/config/modules/PGP.js | 36 ++ src/core/operations/PGP.js | 596 +++++++++++++++++++++++++++ test/index.js | 1 + test/tests/operations/PGP.js | 457 ++++++++++++++++++++ 8 files changed, 1534 insertions(+), 51 deletions(-) create mode 100644 src/core/config/modules/PGP.js create mode 100644 src/core/operations/PGP.js create mode 100644 test/tests/operations/PGP.js diff --git a/package-lock.json b/package-lock.json index 43bbb206..0815bcde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,15 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "HTML_CodeSniffer": { - "version": "github:squizlabs/HTML_CodeSniffer#d209ce54876657858a8a01528ad812cd234f37f0", - "dev": true - }, - "JSONSelect": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz", - "integrity": "sha1-oI7cxn6z/L6Z7WMIVTRKDPKCu40=" - }, "abab": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", @@ -22,8 +13,7 @@ "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { "version": "1.3.4", @@ -41,12 +31,12 @@ "integrity": "sha1-IJ4W63DAlaA79/yCnsrLfHeS9e4=", "dev": true, "requires": { - "HTML_CodeSniffer": "github:squizlabs/HTML_CodeSniffer#d209ce54876657858a8a01528ad812cd234f37f0", "axios": "0.9.1", "bluebird": "3.5.0", "chalk": "1.1.3", "commander": "2.11.0", "glob": "7.1.2", + "HTML_CodeSniffer": "github:squizlabs/HTML_CodeSniffer#d209ce54876657858a8a01528ad812cd234f37f0", "jsdom": "9.12.0", "mkdirp": "0.5.1", "phantomjs-prebuilt": "2.1.15", @@ -1022,8 +1012,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base64-js": { "version": "1.2.1", @@ -1140,7 +1129,6 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -1620,8 +1608,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.0", @@ -2330,6 +2317,14 @@ "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", "dev": true }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "0.4.19" + } + }, "end-of-stream": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", @@ -3193,7 +3188,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", - "dev": true, "requires": { "glob": "5.0.15" }, @@ -3202,7 +3196,6 @@ "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, "requires": { "inflight": "1.0.6", "inherits": "2.0.3", @@ -3414,8 +3407,7 @@ "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "grunt": { "version": "1.0.1", @@ -3489,6 +3481,17 @@ "shelljs": "0.5.3" } }, + "grunt-cli": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", + "requires": { + "findup-sync": "0.3.0", + "grunt-known-options": "1.1.0", + "nopt": "3.0.6", + "resolve": "1.1.7" + } + }, "grunt-concurrent": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/grunt-concurrent/-/grunt-concurrent-2.3.1.tgz", @@ -3630,8 +3633,7 @@ "grunt-known-options": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz", - "integrity": "sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk=", - "dev": true + "integrity": "sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk=" }, "grunt-legacy-log": { "version": "1.0.0", @@ -3861,6 +3863,10 @@ "wbuf": "1.7.2" } }, + "HTML_CodeSniffer": { + "version": "github:squizlabs/HTML_CodeSniffer#d209ce54876657858a8a01528ad812cd234f37f0", + "dev": true + }, "html-comment-regex": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", @@ -4064,8 +4070,7 @@ "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, "icss-replace-symbols": { "version": "1.1.0", @@ -4162,8 +4167,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "indent-string": { "version": "2.1.0", @@ -4190,7 +4194,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "1.4.0", "wrappy": "1.0.2" @@ -4199,8 +4202,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.4", @@ -4526,8 +4528,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-svg": { "version": "2.1.0", @@ -4591,12 +4592,12 @@ "resolved": "https://registry.npmjs.org/jison/-/jison-0.4.13.tgz", "integrity": "sha1-kEFwfWIkE2f1iDRTK58ZwsNvrHg=", "requires": { - "JSONSelect": "0.4.0", "cjson": "0.2.1", "ebnf-parser": "0.1.10", "escodegen": "0.0.21", "esprima": "1.0.4", "jison-lex": "0.2.1", + "JSONSelect": "0.4.0", "lex-parser": "0.1.4", "nomnom": "1.5.2" }, @@ -4860,6 +4861,11 @@ } } }, + "JSONSelect": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz", + "integrity": "sha1-oI7cxn6z/L6Z7WMIVTRKDPKCu40=" + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -5279,7 +5285,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "1.1.8" } @@ -5370,6 +5375,15 @@ "lower-case": "1.1.4" } }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "0.1.12", + "is-stream": "1.1.0" + } + }, "node-forge": { "version": "0.6.33", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.6.33.tgz", @@ -5415,6 +5429,14 @@ } } }, + "node-localstorage": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-1.3.0.tgz", + "integrity": "sha1-LkNqro3Mms6XtDxlwWwNV3vgpVw=", + "requires": { + "write-file-atomic": "1.3.4" + } + }, "node-md6": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/node-md6/-/node-md6-0.1.0.tgz", @@ -5440,7 +5462,6 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, "requires": { "abbrev": "1.1.1" } @@ -5573,7 +5594,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1.0.2" } @@ -5587,6 +5607,15 @@ "mimic-fn": "1.1.0" } }, + "openpgp": { + "version": "2.5.11", + "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-2.5.11.tgz", + "integrity": "sha1-02yBnAkY4BLH0egSbkVRvAofJzg=", + "requires": { + "node-fetch": "1.7.3", + "node-localstorage": "1.3.0" + } + }, "opn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz", @@ -5783,8 +5812,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -7415,8 +7443,7 @@ "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" }, "resolve-from": { "version": "1.0.1", @@ -7690,6 +7717,11 @@ "is-fullwidth-code-point": "2.0.0" } }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" + }, "sntp": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", @@ -7948,6 +7980,15 @@ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "dev": true }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -7975,15 +8016,6 @@ } } }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -9043,8 +9075,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "0.2.1", @@ -9055,6 +9086,16 @@ "mkdirp": "0.5.1" } }, + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" + } + }, "xml-char-classes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", diff --git a/package.json b/package.json index 31042928..40045e61 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "esprima": "^4.0.0", "exif-parser": "^0.1.12", "google-code-prettify": "^1.0.5", + "grunt-cli": "^1.2.0", "jquery": "^3.2.1", "js-crc": "^0.2.0", "js-sha3": "^0.6.1", @@ -89,6 +90,7 @@ "moment": "^2.18.1", "moment-timezone": "^0.5.13", "node-md6": "^0.1.0", + "openpgp": "^2.5.11", "otp": "^0.1.3", "sladex-blowfish": "^0.8.1", "sortablejs": "^1.6.1", diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index 06a3a2d8..3370dbec 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -27,6 +27,7 @@ import MAC from "../operations/MAC.js"; import MorseCode from "../operations/MorseCode.js"; import NetBIOS from "../operations/NetBIOS.js"; import PublicKey from "../operations/PublicKey.js"; +import PGP from "../operations/PGP.js"; import Punycode from "../operations/Punycode.js"; import Rotate from "../operations/Rotate.js"; import SeqUtils from "../operations/SeqUtils.js"; @@ -3845,6 +3846,353 @@ const OperationConfig = { } ] }, + "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 relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Public key", + 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 relies on OpenPGP.js for the implementation of PGP.

See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Private key", + type: "text", + value: "", + }, + { + name: "Private key password", + type: "string", + value: "", + }, + ] + }, + "PGP Sign": { + module: "PGP", + description: [ + "Input: the cleartext you want to sign.", + "

", + "Arguments: the ASCII-armoured PGP public key of the recipient, ", + "the ASCII-armoured private key of the signer (and the private key password if necessary).", + "

", + "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 OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Public key", + type: "text", + value: "", + }, + { + name: "Private key", + type: "text", + value: "", + }, + { + name: "Private key password", + type: "string", + 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 OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Public key", + type: "text", + value: "", + }, + { + name: "Private key", + type: "text", + value: "", + }, + { + name: "Private key password", + type: "string", + value: "", + }, + ] + }, + "Sign PGP Detached": { + module: "PGP", + description: [ + "Input: the cleartext you want to sign.", + "

", + "Arguments: the ASCII-armoured PGP private key of the signer, ", + " (and the private key password if necessary).", + "

", + "This operation uses PGP to produce a cleartext message and its digital signature. ", + "It outputs 3 files as HTML: ", + "the cleartext message (msg), ", + "the ASCII-armoured signature (msg.asc), and ", + "the raw bytes of the signature (msg.sig).", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "HTML", + args: [ + { + name: "Private key", + type: "text", + value: "", + }, + { + name: "Private key password", + type: "string", + value: "", + }, + ] + }, + "Verify PGP Detached": { + module: "PGP", + description: [ + "Input: the cleartext of the message you want to verify.", + "

", + "Arguments: the ASCII-armoured PGP public key of the sender, ", + "the ASCII-armoured signature of the message.", + "

", + "This operation uses PGP to verify a detached cleartext PGP signature.", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Public key", + type: "text", + value: "", + }, + { + name: "Signature", + type: "text", + value: "", + }, + ] + }, + "Sign PGP Cleartext": { + module: "PGP", + description: [ + "Input: the cleartext of the message you want to sign.", + "

", + "Arguments: the ASCII-armoured PGP private key of the signer, ", + "(and the private key password if necessary).", + "

", + "This operation uses PGP to produce a digital signature.", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Private key", + type: "text", + value: "", + }, + { + name: "Private key password", + type: "string", + value: "", + }, + ] + }, + "Verify PGP Cleartext": { + module: "PGP", + description: [ + "Input: the ASCII-armoured cleartext signature you want to verify.", + "

", + "Arguments: the ASCII-armoured PGP public key of the sender.", + "

", + "This operation uses PGP to verify a cleartext digital signature.", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Public key", + type: "text", + value: "", + }, + ] + }, + "Generate PGP Key Pair": { + module: "PGP", + description: [ + "Input is ignored.", + "

", + "This operation generates a PGP key pair.", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "string", + args: [ + { + name: "Password", + type: "string", + value: "", + }, + { + name: "Key size", + type: "option", + value: ["1024", "2048", "4096"], + }, + { + name: "Name", + type: "string", + value: "", + }, + { + name: "Email", + type: "string", + value: "", + }, + ] + }, + "Detach PGP Cleartext": { + module: "PGP", + description: [ + "Input: the ASCII-armoured cleartext signature you want to verify.", + "

", + "This operation will detach the cleartext message from the signature.", + "It outputs 3 files as HTML: ", + "the cleartext message (msg), ", + "the ASCII-armoured signature (msg.asc), and ", + "the raw bytes of the signature (msg.sig).", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "HTML", + args: [ + ], + }, + "Add PGP ASCII Armour": { + module: "PGP", + description: [ + "Input: the raw PGP bytes of the message or key that you want to add ASCII-armour to.", + "

", + "Arguments: the kind of the raw bytes: message / public key / private key.", + "

", + "This operation will output the ASCII-armoured input.", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "byteArray", + outputType: "string", + args: [ + { + name: "Armour type", + type: "option", + value: PGP.ARMOUR_TYPES, + }, + ], + }, + "Remove PGP ASCII Armour": { + module: "PGP", + description: [ + "Input: the ASCII-armoured PGP message or key that you want to remove the ASCII-armour from.", + "

", + "This operation will output the raw bytes of the PGP packets.", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function relies on OpenPGP.js for the implementation of PGP.", + "

", + "See more at https://openpgpjs.org/", + ].join("\n"), + inputType: "string", + outputType: "byteArray", + args: [ + ], + }, }; diff --git a/src/core/config/modules/OpModules.js b/src/core/config/modules/OpModules.js index 2d79753e..a29c6fc8 100644 --- a/src/core/config/modules/OpModules.js +++ b/src/core/config/modules/OpModules.js @@ -17,6 +17,7 @@ import HashingModule from "./Hashing.js"; import HTTPModule from "./HTTP.js"; import ImageModule from "./Image.js"; import JSBNModule from "./JSBN.js"; +import PGPModule from "./PublicKey.js"; import PublicKeyModule from "./PublicKey.js"; import ShellcodeModule from "./Shellcode.js"; @@ -32,6 +33,7 @@ Object.assign( HTTPModule, ImageModule, JSBNModule, + PGPModule, PublicKeyModule, ShellcodeModule ); diff --git a/src/core/config/modules/PGP.js b/src/core/config/modules/PGP.js new file mode 100644 index 00000000..a50d5eb1 --- /dev/null +++ b/src/core/config/modules/PGP.js @@ -0,0 +1,36 @@ +import PGP from "../../operations/PGP.js"; + +/** + * PGP module. + * + * Libraries: + * - openpgp + * - crypto-js + * + * @author tlwr [toby@toby.codes] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +let OpModules = typeof self === "undefined" ? {} : self.OpModules || {}; + +OpModules.PGP = { + "PGP Encrypt": PGP.runEncrypt, + "PGP Decrypt": PGP.runDecrypt, + + "PGP Sign": PGP.runSign, + "PGP Verify": PGP.runVerify, + + "Sign PGP Detached": PGP.runSignDetached, + "Verify PGP Detached": PGP.runVerifyDetached, + + "Sign PGP Cleartext": PGP.runSignCleartext, + "Verify PGP Cleartext": PGP.runVerifyCleartext, + + "Generate PGP Key Pair": PGP.runGenKeyPair, + "Detach PGP Cleartext": PGP.runVerifyCleartext, + + "Add PGP ASCII Armour": PGP.runAddArmour, + "Remove PGP ASCII Armour": PGP.runAddArmour, +}; + +export default OpModules; diff --git a/src/core/operations/PGP.js b/src/core/operations/PGP.js new file mode 100644 index 00000000..e66304aa --- /dev/null +++ b/src/core/operations/PGP.js @@ -0,0 +1,596 @@ +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; diff --git a/test/index.js b/test/index.js index 773a5b14..327861c1 100644 --- a/test/index.js +++ b/test/index.js @@ -25,6 +25,7 @@ import "./tests/operations/Hash.js"; import "./tests/operations/Image.js"; import "./tests/operations/MorseCode.js"; import "./tests/operations/MS.js"; +import "./tests/operations/PGP.js"; import "./tests/operations/StrUtils.js"; import "./tests/operations/SeqUtils.js"; diff --git a/test/tests/operations/PGP.js b/test/tests/operations/PGP.js new file mode 100644 index 00000000..265722e1 --- /dev/null +++ b/test/tests/operations/PGP.js @@ -0,0 +1,457 @@ +/** + * PGP tests. + * + * @author tlwr [toby@toby.codes] + * + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister.js"; + +const CYBERCHEF_GENERATED_KEY_PAIRS = [ + { + name: "CyberChef 1", + size: 1024, + pub: [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----", + "Version: OpenPGP.js v2.3.6", + "Comment: http://openpgpjs.org", + "", + "xo0EWMQszAEEAMY6F0o6jL6TrVVXDkqJwNVJRR6tQKr+LIt7plEJoaTRVDfL", + "1jetdhPg+YiE7xZI8ygf6fhsrot4ccUN1QdtwedAz6GH0xjFzpL1i4/7/f3U", + "ItJ0p1MO4Amy8Tei/AtXXMTy/YwE77V1AMcv7OYFw9va5S0PD87XoKK6rx7z", + "GFMTABEBAAHNAjw+wrUEEAEIACkFAljELMwGCwkHCAMCCRAar/puuym6vQQV", + "CAIKAxYCAQIZAQIbAwIeAQAA8/QEAKDVde34L3rvFECzUOFPA5w4w4gbwkg+", + "YwPa084WvMTdo/wBEiEhj+7P+/5eN/U96yuHD48+Cmm5AHBaaf+K1b2LbNe7", + "3PP5rV1rMcooUGeIhq7SFw0BdPZTLoCNbkBKFCpvrS/F4SuUQF7g+fVyOyve", + "zVWew+E41ZC3vsDd63Y3zo0EWMQszAEEANYNy0yka1+c3Oe2U1GqoAHXv05p", + "VnlHAZ+28JGs8Thq5ns0K8bxI+Fn9CFpPO7Vofrr40V3vweoJVK2Eiq2gN/X", + "QdxPHckcpYbFKTAZIXt2MAZfb027JxWZid5lyYSCwvY+BK7x5X4jdY5KfbKu", + "7WDivOkq8MhomSiX+QYDV8qrABEBAAHCnwQYAQgAEwUCWMQszAkQGq/6brsp", + "ur0CGwwAAIjWA/9WhpbfM+oA08m5XwXgCkuRfTymIkexzn17dZOngTzaGcbK", + "vpS3QN164XNu229JNKTrsdgn5zeeq3AqhQ63hTMbePajvUYSssHPqKB8qQlp", + "OUY/rcFEUXMirIkKBGByYBmlz56Ai855wJoSOrZJA6yfnGepyV5ChcG/cEmB", + "dH/6bA==", + "=4nW6", + "-----END PGP PUBLIC KEY BLOCK-----", + ].join("\n"), + sec: [ + "-----BEGIN PGP PRIVATE KEY BLOCK-----", + "Version: OpenPGP.js v2.3.6", + "Comment: http://openpgpjs.org", + "", + "xcEYBFjELMwBBADGOhdKOoy+k61VVw5KicDVSUUerUCq/iyLe6ZRCaGk0VQ3", + "y9Y3rXYT4PmIhO8WSPMoH+n4bK6LeHHFDdUHbcHnQM+hh9MYxc6S9YuP+/39", + "1CLSdKdTDuAJsvE3ovwLV1zE8v2MBO+1dQDHL+zmBcPb2uUtDw/O16Ciuq8e", + "8xhTEwARAQABAAP6A0jnJeW+e1H7J1Tf+cA6n84tBQsd7Td1CYKtCN69/Psz", + "CBGqpRWMxVuPBwIc7COdU+bje6hhZBJE4F0QUKUy91iQRssy4MzOYmZbdZaa", + "eTT81MdYb6QPYdTvPBVxjeLJBL7mKB+hM2Z8SvtJMDBdLlprf/XIdZKxD/NB", + "R+q66OECAPPsaMb+Yv1F30pEJZkATWvUSQS57HzWoBaNGxGkDqcik7+2q3DU", + "fWe0586HfMFQ3ba1ziNy2SWYJDAqMAe0QekCANAKgQJwww75GGK1RwNFZRoJ", + "Sb/Jzx3RVbwy1xqfVbadTuvf2+oSBLy/+eGXglwrok08e2BvYWMmhB+uJSJb", + "M5sCAItUBCJqTszPQPZdIOi7rGomnL2fijBDAUz+kWAWBPcIf8zzexKl7Ebq", + "dxc621BD5xjDjE7x1Z5XX/Rd2Lt+PvOdyM0CPD7CtQQQAQgAKQUCWMQszAYL", + "CQcIAwIJEBqv+m67Kbq9BBUIAgoDFgIBAhkBAhsDAh4BAADz9AQAoNV17fgv", + "eu8UQLNQ4U8DnDjDiBvCSD5jA9rTzha8xN2j/AESISGP7s/7/l439T3rK4cP", + "jz4KabkAcFpp/4rVvYts17vc8/mtXWsxyihQZ4iGrtIXDQF09lMugI1uQEoU", + "Km+tL8XhK5RAXuD59XI7K97NVZ7D4TjVkLe+wN3rdjfHwRgEWMQszAEEANYN", + "y0yka1+c3Oe2U1GqoAHXv05pVnlHAZ+28JGs8Thq5ns0K8bxI+Fn9CFpPO7V", + "ofrr40V3vweoJVK2Eiq2gN/XQdxPHckcpYbFKTAZIXt2MAZfb027JxWZid5l", + "yYSCwvY+BK7x5X4jdY5KfbKu7WDivOkq8MhomSiX+QYDV8qrABEBAAEAA/0e", + "rqd/eunxMJjxlc7nm9+HpBdF9A9zHtx6ukxNdU62WYxkCJxlzdbozm/OAjm7", + "ul+XigxvvrRhMpb2/iYofTSHnj+6yGGghCic6BtstJOU7qepMrX+IKh3TNEp", + "YNU8z0E1fSd9fMOx1hnTZwaTroii9CzM0i4YH3pSjze7Ir7cIQIA8Cg8sBmG", + "IDhe7SBq5xcG2V4iNqiK5gHXbQrcit9/XJFqIeda5Ec7lRjpa6vG5f1xeT1w", + "KdBil2L4prnD6XDAEwIA5Cy51YIjizFyKormqQNGR1fdAl+6T/qReUcw5Cmw", + "cDU7tUujZwZz/utmjOcadq8JR2LG6rNwLzeMgDNCCKAOCQH/RX0h3eLXcpWq", + "jGBH3mJbukSLH/98ybP5LV+4jg0q5iXOOjUIXxFsPElyZZHUBvpoRrKbRG/f", + "PzOpx7akqEOuDJ/Dwp8EGAEIABMFAljELMwJEBqv+m67Kbq9AhsMAACI1gP/", + "VoaW3zPqANPJuV8F4ApLkX08piJHsc59e3WTp4E82hnGyr6Ut0DdeuFzbttv", + "STSk67HYJ+c3nqtwKoUOt4UzG3j2o71GErLBz6igfKkJaTlGP63BRFFzIqyJ", + "CgRgcmAZpc+egIvOecCaEjq2SQOsn5xnqcleQoXBv3BJgXR/+mw=", + "=8R+g", + "-----END PGP PRIVATE KEY BLOCK-----", + ].join("\n"), + }, +]; + +const PGP_TEST_KEY_PAIRS = [ + { + keyID: "a9510d8fd7e352f5", + name: "CyberChef nopw 1024 ", + size: 1024, + pub: [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----", + "", + "mI0EWL7mbwEEANVcA5s+pXliwZBZXvfjy69JlPkX8dWRTZLuqZPz5XKGb9E1RKxQ", + "jELZ9bBIv/t0HDoiwh1XJok3SRMPzLPHK9lLXaa/l7716CkRddTvkd3cjFA3yAEL", + "X97+4nPVIajYUPDy6ovCrRru2bXyW2cjHKErlI0GJ37Hbbpj/GXSQL6HABEBAAG0", + "JUN5YmVyQ2hlZiBub3B3IDEwMjQgPHRvYnlAdG9ieS5jb2Rlcz6ItwQTAQgAIQUC", + "WL7mbwIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRCpUQ2P1+NS9ZhxA/49", + "sXqWYoi34HMtHfVejPpPSXrFxY+ZjgM1cMVe5m4cumBeZVVYaEH9/3xDlrhfrlMi", + "xCRGNqfIRwavIbNWyBshrjVnZ1PR/hvp2P3tV0ciV51lJi6ApO46fsmsWZw84cOA", + "ejQ1giBjuLuu4du52+MVbcko2yEJ2tOS2WMPnYO7RbiNBFi+5m8BBAClMs6KZuXd", + "2mhmz3vGcczlA0vv2k7lZjdT7LmbYLt/ir7Hdqg7z7l84wO1qPNQc6pRkLVd1rMX", + "8fh+RtLUVAuvATpRJCAdaeslcxaH7o0p5DbjrRmiDmnaLDl/S0xMhI137XaKdUS7", + "ZRuGFZVfqXeFerpbjrlUlhzAcHua8aAmHQARAQABiJ8EGAEIAAkFAli+5m8CGwwA", + "CgkQqVENj9fjUvUA6AP/b9jZvaQ7w6mdlCvCrFpfp3vBZN1E0vLIMdRKRMmNR8D7", + "pmZ4RcmMJxjVTLkB6Uc90+9Tbb2ysykQaTTw3YdpmyoGmy3qgH3opd/IdZzEAJyL", + "oBC+u8G7nATB58m4xmBYL1G5hn8nCBbNAiz1rrbXmWmVExQCVAXp7T50vPBdyFY=", + "=xCxV", + "-----END PGP PUBLIC KEY BLOCK-----", + ].join("\n"), + sec: [ + "-----BEGIN PGP PRIVATE KEY BLOCK-----", + "", + "lQHYBFi+5m8BBADVXAObPqV5YsGQWV7348uvSZT5F/HVkU2S7qmT8+Vyhm/RNUSs", + "UIxC2fWwSL/7dBw6IsIdVyaJN0kTD8yzxyvZS12mv5e+9egpEXXU75Hd3IxQN8gB", + "C1/e/uJz1SGo2FDw8uqLwq0a7tm18ltnIxyhK5SNBid+x226Y/xl0kC+hwARAQAB", + "AAP7BLFYuDDhPrQDIgsdyzzsuIDPbj6G9z9+hsN/sLrZlEFbBt67c95+Sbzm6h1O", + "SdgcoZiPgJig0L3OyR1kO5ypBG4jhSgbaTKRHjP4KsOWoPuXmL1p8XLzZHOGp3hS", + "/recAxhqdbbO7uim+nbaPdmby9v0XrMAkFkBTU+D+ZlmC3ECAODTUZ7i+vRD6R3x", + "NzN2uhomWy94I7SkugTjcTw6BrzvdffFKtW7Yp6MbPdCpRIhuKy/QhG1LCUGwJKQ", + "vvNe5z8CAPLxrsx6jiFHpzAtUBYNBY5M3t805OYRFbs3mJ3LgsUP0wVOnODqo5TA", + "anevkFoakvcKJ8d0OlbO0/ewm4wd3rkB+wRQrllfYFfLqGzUlZDP15NKoHFk8T/w", + "LWg6nDm8pxXlhptaA8V4rVK1HrrPU56GBbzXftqL6iDpbYh9ZPR/U/qoXLQlQ3li", + "ZXJDaGVmIG5vcHcgMTAyNCA8dG9ieUB0b2J5LmNvZGVzPoi3BBMBCAAhBQJYvuZv", + "AhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEKlRDY/X41L1mHED/j2xepZi", + "iLfgcy0d9V6M+k9JesXFj5mOAzVwxV7mbhy6YF5lVVhoQf3/fEOWuF+uUyLEJEY2", + "p8hHBq8hs1bIGyGuNWdnU9H+G+nY/e1XRyJXnWUmLoCk7jp+yaxZnDzhw4B6NDWC", + "IGO4u67h27nb4xVtySjbIQna05LZYw+dg7tFnQHYBFi+5m8BBAClMs6KZuXd2mhm", + "z3vGcczlA0vv2k7lZjdT7LmbYLt/ir7Hdqg7z7l84wO1qPNQc6pRkLVd1rMX8fh+", + "RtLUVAuvATpRJCAdaeslcxaH7o0p5DbjrRmiDmnaLDl/S0xMhI137XaKdUS7ZRuG", + "FZVfqXeFerpbjrlUlhzAcHua8aAmHQARAQABAAP8C0IAnaGbME4WMkzDyHIbPqTF", + "s9qX6NMumbthVLY4C4jhgnH9egH9x9sn9rpvm7/Iz2vXUvCdiNgLu/3zQoFYawsn", + "zpTQREm6CyITOLxyc5TrckodSlx6eoCezvnTzI/PvXrUSSqEP9c9hvcLuKm+aeZz", + "fclhPfDOL/EQx337aeECAMU0UXXb95hQqSdRMcsGkpoAlAIEM664/S9eBvXrJYWw", + "1Pfgms7Axi/tZ4ebkc3urRW/YKAt423Ixmdd5rOwIDECANZzoOU8LxreLcLQOoA9", + "SWswRWUeyQTvH4yskT2iIXen22CO1pp7ruy07vSxHE+yyR3t2ZHKPJU864J39d4k", + "da0CAJS838KMD8h+Zv1C/jhKkYjpnDRuE+Lj7qVNHCJqsFSzpJAbJ09VpwSGbUso", + "Ptc/oyXgzb91+sLKfsIwGa7ZysarEIifBBgBCAAJBQJYvuZvAhsMAAoJEKlRDY/X", + "41L1AOgD/2/Y2b2kO8OpnZQrwqxaX6d7wWTdRNLyyDHUSkTJjUfA+6ZmeEXJjCcY", + "1Uy5AelHPdPvU229srMpEGk08N2HaZsqBpst6oB96KXfyHWcxACci6AQvrvBu5wE", + "wefJuMZgWC9RuYZ/JwgWzQIs9a6215lplRMUAlQF6e0+dLzwXchW", + "=S56e", + "-----END PGP PRIVATE KEY BLOCK-----", + ].join("\n"), + }, + { + keyID: "02da58ca894c4cc7", + name: "CyberChef pw 1024 ", + size: 1024, + password: "2NSRJYTzgsTVJfih", + pub: [ + "-----BEGIN PGP PUBLIC KEY BLOCK-----", + "", + "mI0EWL7mswEEAMcw0PSzmNfh4MkGwejsyreXb34FqEjYMc1GmswvC5uuLp0KzbC6", + "HecV1GyyPQ7kKshWVLloShN3KtgSZF7DJ8/Hioiv4Q5HJYPY4AYwqECi+/C4W1FA", + "tJEbItdEIxw0IgHOX64X5kMptVV9J5bGc7DqkqUHgapXJI+yLZel0s83ABEBAAG0", + "I0N5YmVyQ2hlZiBwdyAxMDI0IDx0b2J5QHRvYnkuY29kZXM+iLcEEwEIACEFAli+", + "5rMCGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQAtpYyolMTMeOGwP+NvLU", + "GHhHaIFwI+VMB655CiD6QuRWnurcrXq7qZYl3flyBehhZdtgGBx9vynffElEFEFz", + "cbqzv8unkDTRdPqTA0gM2wOaC/L6Kuy4ErjQD7A4i5rC/IG0ja+3DQeKLShGGtaD", + "bG0H0mJl7gzlqR3/fxgLxi/JaCtqapjtZDF/GTC4jQRYvuazAQQA5oNC4NeWKBKU", + "m9H/QZyxKRWGaaQRKLrmSGVkQgJlzo3pnNjJMFUaMcQaJve+PZGay7y9sCOFpumf", + "6v1ERY+qw4ZOyCRnRqHJOxdLrbTPSmOwIUYkYFkNzARGpdy/FqWCByXDXea5qsb1", + "DJRT9WUte3Bbx1YvzsqrZMqsExgwgdcAEQEAAYifBBgBCAAJBQJYvuazAhsMAAoJ", + "EALaWMqJTEzH7woEAKIKQS8bEf8iHaVf7aGEWWOShN10gb+xpWD4V5r8H6hlNpIk", + "xwQEbj8glTJBEJoD5xO1tdrHmOJ7BVO1d7otdgXpjBAOxn0ngW4MAB8BO+Qa5U/t", + "M+6yuL8REwE5YUnvIyhgvhiHsBhlLeMNXtwCrF94WyDzyb+hwVwwi/PFtI0y", + "=djdC", + "-----END PGP PUBLIC KEY BLOCK-----", + ].join("\n"), + sec: [ + "-----BEGIN PGP PRIVATE KEY BLOCK-----", + "", + "lQIGBFi+5rMBBADHMND0s5jX4eDJBsHo7Mq3l29+BahI2DHNRprMLwubri6dCs2w", + "uh3nFdRssj0O5CrIVlS5aEoTdyrYEmRewyfPx4qIr+EORyWD2OAGMKhAovvwuFtR", + "QLSRGyLXRCMcNCIBzl+uF+ZDKbVVfSeWxnOw6pKlB4GqVySPsi2XpdLPNwARAQAB", + "/gcDAmIhPcvaoYd359ZKWcYduPFREt173KIgJMOSo4VVl9N2CA0EicF81xC/X+Fx", + "mr3aZYsqMOpm/GxY8mUyrCgQJTvq36Y/IIMNkTTbgposTX4ESuAUPkZlX6NGTH+6", + "9wKPTPGiQfxME6QUGw2ROl0mfwh5tIAccKJDJFRrOq1SXZcwJsHemdGPN8pnMrEj", + "A148htTPGx5usPc1/EJ2AdjKppQ2V+1byXsy37vgEXpXMe+pA9d2zsRsKxgh91up", + "sfGFsXGBoW/R0JLCe2DDXPGNmYIj3xguhCR6KV7dc7YzgfmXeAX8RUACADYgz5tS", + "Otot6kau1aIsO3/qY58kGpbOvWblad+302hA9QUginxWISMjddr/zenSRK7cSMnK", + "g9ezM2QdREw0hG/t9lxLYlKPDZ8DPMOVApOIXHmG3O7/Wh/fvVomRTiz85je2ONn", + "EgrctzOEZYUn612SpW5lEtxuzNOQamkT0fXnDLZ8wdElxEqcAWjv1920I0N5YmVy", + "Q2hlZiBwdyAxMDI0IDx0b2J5QHRvYnkuY29kZXM+iLcEEwEIACEFAli+5rMCGwMF", + "CwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQAtpYyolMTMeOGwP+NvLUGHhHaIFw", + "I+VMB655CiD6QuRWnurcrXq7qZYl3flyBehhZdtgGBx9vynffElEFEFzcbqzv8un", + "kDTRdPqTA0gM2wOaC/L6Kuy4ErjQD7A4i5rC/IG0ja+3DQeKLShGGtaDbG0H0mJl", + "7gzlqR3/fxgLxi/JaCtqapjtZDF/GTCdAgYEWL7mswEEAOaDQuDXligSlJvR/0Gc", + "sSkVhmmkESi65khlZEICZc6N6ZzYyTBVGjHEGib3vj2Rmsu8vbAjhabpn+r9REWP", + "qsOGTsgkZ0ahyTsXS620z0pjsCFGJGBZDcwERqXcvxalggclw13muarG9QyUU/Vl", + "LXtwW8dWL87Kq2TKrBMYMIHXABEBAAH+BwMCEFHJC21rjdLnthrJ9pdbFW2+YRtB", + "Y80jrNzKhEAzmSJ+O0yAX2SuaGUSaJD6W+5nBk8L9+MoarDT+aISHuW8rhQMv2gv", + "SIwKwY/4nJLltinvKTskoBM5nILjXkvjItHcOpO3A2SCETD+H0+gglhaig7FC/gl", + "Cn306uSZ/Kdc2VEjOeMLv26srrkZYCU8BqF1GkuJXN4pQQnRKLg5ne6lhg/hD9T2", + "zE246ZnvfG4GD8nJElFy/hnW1nhZfuKSFpWyKqPyfoy5eBNVMxISYhJbYE9h2sO3", + "KLzQ03b/b4YGJzVyFaa+sU6z+hZnEtkw+CubpH1UXu6VXT8vMnglNajR1NP50NH2", + "Wq2z/45Utkmv/cF0SynKYGDTiB+KO4H4i4vK7prH18WkN7nKYdvsE42IYCP2iSg6", + "jVBTdV5bw6MHi43h5XfgLH1Hw9RU4RMHPMNHMfZ8Yl9us93SYukeOtTlPESqxM55", + "hSI2+eheYVzUleRe4FNRDIifBBgBCAAJBQJYvuazAhsMAAoJEALaWMqJTEzH7woE", + "AKIKQS8bEf8iHaVf7aGEWWOShN10gb+xpWD4V5r8H6hlNpIkxwQEbj8glTJBEJoD", + "5xO1tdrHmOJ7BVO1d7otdgXpjBAOxn0ngW4MAB8BO+Qa5U/tM+6yuL8REwE5YUnv", + "IyhgvhiHsBhlLeMNXtwCrF94WyDzyb+hwVwwi/PFtI0y", + "=j2j0", + "-----END PGP PRIVATE KEY BLOCK-----", + ].join("\n"), + }, +]; + +PGP_TEST_KEY_PAIRS.forEach(function(keyPair) { + if (keyPair.password) { + TestRegister.addTests( + ["", "nottherightpassword"].map(function(incorrectPW) { + let testName = "PGP Encrypt, PGP Decrypt: ensure error for incorrect password (pw [$pw], $ks, $name)"; + testName = testName.replace("$ks", keyPair.size); + testName = testName.replace("$pw", incorrectPW); + + return { + name: testName, + input: "hello world", + expectedError: true, + recipeConfig: [ + { + op: "PGP Encrypt", + args: [keyPair.pub], + }, + { + op: "PGP Decrypt", + args: [keyPair.sec, incorrectPW], + }, + ], + }; + }) + ); + } +}); + +["", "hello world"].forEach(function(input) { + TestRegister.addTests( + PGP_TEST_KEY_PAIRS.map(function(keyPair) { + let testName = "PGP Encrypt, PGP Decrypt ($pw, $ks) '$input'"; + testName = testName.replace("$ks", keyPair.size); + testName = testName.replace("$pw", keyPair.password ? "pw" : "no pw"); + testName = testName.replace("$input", input); + + return { + name: testName, + input: input, + expectedOutput: input, + recipeConfig: [ + { + op: "PGP Encrypt", + args: [keyPair.pub], + }, + { + op: "PGP Decrypt", + args: [keyPair.sec, keyPair.password], + }, + ], + }; + }) + ); +}); + +TestRegister.addTests([{ + name: "PGP Encrypt, PGP Decrypt: fails when incorrect password, empty string (1024)", + input: "", + expectedError: true, + recipeConfig: [ + { + op: "PGP Encrypt", + args: [PGP_TEST_KEY_PAIRS[1].pub], + }, + { + op: "PGP Decrypt", + args: [PGP_TEST_KEY_PAIRS[1].sec, "gibberish"], + }, + ], +}]); + +TestRegister.addTests([{ + name: "PGP Encrypt, PGP Decrypt: fails when incorrect password, hello world (1024)", + input: "hello world", + expectedError: true, + recipeConfig: [ + { + op: "PGP Encrypt", + args: [PGP_TEST_KEY_PAIRS[1].pub], + }, + { + op: "PGP Decrypt", + args: [PGP_TEST_KEY_PAIRS[1].sec, "gibberish"], + }, + ], +}]); + +["hello world"].forEach(function(input) { + [ + [PGP_TEST_KEY_PAIRS[0], PGP_TEST_KEY_PAIRS[1]], + [PGP_TEST_KEY_PAIRS[1], PGP_TEST_KEY_PAIRS[0]], + ].forEach(function(pairOfKeyPairs) { + let alice = pairOfKeyPairs[0], + bob = pairOfKeyPairs[1]; + + let testName = "PGP Sign ($alice), PGP Verify ($bob) '$input'"; + testName = testName.replace("$alice", alice.name); + testName = testName.replace("$bob", bob.name); + testName = testName.replace("$input", input); + + TestRegister.addTests([{ + name: testName, + input: input, + expectedOutput: [ + "Verified: true", + "Key ID: " + alice.keyID, + "Encrypted for: " + bob.name, + "Signed by: " + alice.name, + "Signed with: rsa_encrypt_sign/aes256", + "\n", + input, + ].join("\n"), + recipeConfig: [ + { + op: "PGP Sign", + args: [bob.pub, alice.sec, alice.password], + }, + { + op: "PGP Verify", + args: [alice.pub, bob.sec, bob.password], + }, + ], + }]); + }); +}); + +["", "hello world"].forEach(function(input) { + TestRegister.addTests( + PGP_TEST_KEY_PAIRS.map(function(keyPair) { + let testName = "Sign PGP Cleartext, Verify PGP Cleartext ($pw, $ks) '$input'"; + testName = testName.replace("$ks", keyPair.size); + testName = testName.replace("$pw", keyPair.password ? "pw" : "no pw"); + testName = testName.replace("$input", input); + + return { + name: testName, + input: input, + expectedOutput: [ + "Verified: true", + "Key ID: " + keyPair.keyID, + "Signed by: " + keyPair.name, + "Signed with: rsa_encrypt_sign", + "\n", + input, + ].join("\n"), + recipeConfig: [ + { + op: "Sign PGP Cleartext", + args: [keyPair.sec, keyPair.password], + }, + { + op: "Verify PGP Cleartext", + args: [keyPair.pub], + }, + { + op: "Find / Replace", + args: [ + {option: "Regex", string: "Signed on: .*\r?\n"}, + "", + true, + false, + false, + ], + }, + ], + }; + }) + ); +}); + +TestRegister.addTests(CYBERCHEF_GENERATED_KEY_PAIRS.map(function(keyPair) { + let testName = "Remove PGP ASCII Armour, Add PGP ASCII Armour: Public Key '$name'"; + testName = testName.replace("$name", keyPair.name); + + return { + name: testName, + input: keyPair.pub, + expectedOutput: keyPair.pub, + ignoreWhitespace: true, + recipeConfig: [ + { + op: "Remove PGP ASCII Armour", + args: [], + }, + { + op: "Add PGP ASCII Armour", + args: ["Public key"], + }, + ], + }; +})); + +TestRegister.addTests(CYBERCHEF_GENERATED_KEY_PAIRS.map(function(keyPair) { + let testName = "Remove PGP ASCII Armour, Add PGP ASCII Armour: Private Key '$name'"; + testName = testName.replace("$name", keyPair.name); + + return { + name: testName, + input: keyPair.sec, + expectedOutput: keyPair.sec, + ignoreWhitespace: true, + recipeConfig: [ + { + op: "Remove PGP ASCII Armour", + args: [], + }, + { + op: "Add PGP ASCII Armour", + args: ["Private key"], + }, + ], + }; +})); + +PGP_TEST_KEY_PAIRS.forEach(function(keyPair) { + TestRegister.addTests( + ["", "hello world"].map(function(message, messageIndex) { + let testName = "PGP Encrypt, Remove PGP ASCII Armour, Add PGP ASCII Armour, PGP Decrypt: Message $message '$name'"; + testName = testName.replace("$message", messageIndex); + testName = testName.replace("$name", keyPair.name); + + return { + name: testName, + input: message, + expectedOutput: message, + ignoreWhitespace: true, + recipeConfig: [ + { + op: "PGP Encrypt", + args: [keyPair.pub], + }, + { + op: "Remove PGP ASCII Armour", + args: [], + }, + { + op: "To Hex", + args: ["None"], + }, + { + op: "From Hex", + args: ["None"], + }, + { + op: "Add PGP ASCII Armour", + args: ["Message"], + }, + { + op: "PGP Decrypt", + args: [keyPair.sec, keyPair.password], + }, + ], + }; + }) + ); +});