浏览代码

Merge branch 'rsa' of https://github.com/mattnotmitt/CyberChef into mattnotmitt-rsa

n1474335 4 年之前
父节点
当前提交
99eb1cced5

+ 5 - 0
src/core/config/Categories.json

@@ -105,6 +105,11 @@
             "Derive EVP key",
             "Bcrypt",
             "Scrypt",
+            "Generate RSA Key Pair",
+            "RSA Sign",
+            "RSA Verify",
+            "RSA Encrypt",
+            "RSA Decrypt",
             "JWT Sign",
             "JWT Verify",
             "JWT Decode",

+ 9 - 0
src/core/lib/RSA.mjs

@@ -0,0 +1,9 @@
+import forge from "node-forge/dist/forge.min.js";
+
+export const MD_ALGORITHMS = {
+    "SHA-1": forge.md.sha1,
+    "MD5": forge.md.md5,
+    "SHA-256": forge.md.sha256,
+    "SHA-384": forge.md.sha384,
+    "SHA-512": forge.md.sha512,
+};

+ 83 - 0
src/core/operations/GenerateRSAKeyPair.mjs

@@ -0,0 +1,83 @@
+/**
+ * @author Matt C [me@mitt.dev]
+ * @author gchq77703 []
+ * @copyright Crown Copyright 2018
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import forge from "node-forge/dist/forge.min.js";
+
+/**
+ * Generate RSA Key Pair operation
+ */
+class GenerateRSAKeyPair extends Operation {
+
+    /**
+     * GenerateRSAKeyPair constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "Generate RSA Key Pair";
+        this.module = "Ciphers";
+        this.description = "Generate an RSA key pair with a given number of bits";
+        this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
+        this.inputType = "string";
+        this.outputType = "string";
+        this.args = [
+            {
+                name: "RSA Key Length",
+                type: "option",
+                value: [
+                    "1024",
+                    "2048",
+                    "4096"
+                ]
+            },
+            {
+                name: "Output Format",
+                type: "option",
+                value: [
+                    "PEM",
+                    "JSON",
+                    "DER"
+                ]
+            }
+        ];
+    }
+
+    /**
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {string}
+     */
+    async run(input, args) {
+        const [keyLength, outputFormat] = args;
+
+        return new Promise((resolve, reject) => {
+            forge.pki.rsa.generateKeyPair({ bits: Number(keyLength), workers: -1, workerScript: "assets/forge/prime.worker.min.js"}, (err, keypair) => {
+                if (err) return reject(err);
+
+                let result;
+
+                switch (outputFormat) {
+                    case "PEM":
+                        result = forge.pki.publicKeyToPem(keypair.publicKey) + "\n" + forge.pki.privateKeyToPem(keypair.privateKey);
+                        break;
+                    case "JSON":
+                        result = JSON.stringify(keypair);
+                        break;
+                    case "DER":
+                        result = forge.asn1.toDer(forge.pki.privateKeyToAsn1(keypair.privateKey)).getBytes();
+                        break;
+                }
+
+                resolve(result);
+            });
+        });
+    }
+
+}
+
+export default GenerateRSAKeyPair;

+ 87 - 0
src/core/operations/RSADecrypt.mjs

@@ -0,0 +1,87 @@
+/**
+ * @author Matt C [me@mitt.dev]
+ * @copyright Crown Copyright 2020
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import Utils from "../Utils.mjs";
+import forge from "node-forge/dist/forge.min.js";
+import { MD_ALGORITHMS } from "../lib/RSA.mjs";
+
+/**
+ * RSA Decrypt operation
+ */
+class RSADecrypt extends Operation {
+
+    /**
+     * RSADecrypt constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "RSA Decrypt";
+        this.module = "Ciphers";
+        this.description = "Decrypt an RSA encrypted message with a PEM encoded private key.";
+        this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
+        this.inputType = "ArrayBuffer";
+        this.outputType = "string";
+        this.args = [
+            {
+                name: "RSA Private Key (PEM)",
+                type: "text",
+                value: "-----BEGIN RSA PRIVATE KEY-----"
+            },
+            {
+                name: "Key Password",
+                type: "text",
+                value: ""
+            },
+            {
+                name: "Encryption Scheme",
+                type: "argSelector",
+                value: [
+                    {
+                        name: "RSA-OAEP",
+                        on: [3]
+                    },
+                    {
+                        name: "RSAES-PKCS1-V1_5",
+                        off: [3]
+                    },
+                    {
+                        name: "RAW",
+                        off: [3]
+                    }]
+            },
+            {
+                name: "Message Digest Algorithm",
+                type: "option",
+                value: Object.keys(MD_ALGORITHMS)
+            }
+        ];
+    }
+
+    /**
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {string}
+     */
+    run(input, args) {
+        const [pemKey, password, scheme, md] = args;
+        if (pemKey.replace("-----BEGIN RSA PRIVATE KEY-----", "").length === 0) {
+            throw new OperationError("Please enter a private key.");
+        }
+        try {
+            const privKey = forge.pki.decryptRsaPrivateKey(pemKey, password);
+            const dMsg = privKey.decrypt(Utils.arrayBufferToStr(input), scheme, {md: MD_ALGORITHMS[md].create()});
+            return dMsg;
+        } catch (err) {
+            throw new OperationError(err);
+        }
+    }
+
+}
+
+export default RSADecrypt;

+ 88 - 0
src/core/operations/RSAEncrypt.mjs

@@ -0,0 +1,88 @@
+/**
+ * @author Matt C [me@mitt.dev]
+ * @copyright Crown Copyright 2020
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import Utils from "../Utils.mjs";
+import forge from "node-forge/dist/forge.min.js";
+import { MD_ALGORITHMS } from "../lib/RSA.mjs";
+
+/**
+ * RSA Encrypt operation
+ */
+class RSAEncrypt extends Operation {
+
+    /**
+     * RSAEncrypt constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "RSA Encrypt";
+        this.module = "Ciphers";
+        this.description = "Encrypt a message with a PEM encoded RSA public key.";
+        this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
+        this.inputType = "string";
+        this.outputType = "ArrayBuffer";
+        this.args = [
+            {
+                name: "RSA Public Key (PEM)",
+                type: "text",
+                value: "-----BEGIN RSA PUBLIC KEY-----"
+            },
+            {
+                name: "Encryption Scheme",
+                type: "argSelector",
+                value: [
+                    {
+                        name: "RSA-OAEP",
+                        on: [2]
+                    },
+                    {
+                        name: "RSAES-PKCS1-V1_5",
+                        off: [2]
+                    },
+                    {
+                        name: "RAW",
+                        off: [2]
+                    }]
+            },
+            {
+                name: "Message Digest Algorithm",
+                type: "option",
+                value: Object.keys(MD_ALGORITHMS)
+            }
+        ];
+    }
+
+    /**
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {string}
+     */
+    run(input, args) {
+        const [pemKey, scheme, md] = args;
+
+        if (pemKey.replace("-----BEGIN RSA PUBLIC KEY-----", "").length === 0) {
+            throw new OperationError("Please enter a public key.");
+        }
+        try {
+            // Load public key
+            const pubKey = forge.pki.publicKeyFromPem(pemKey);
+            // Encrypt message
+            const eMsg = pubKey.encrypt(input, scheme, {md: MD_ALGORITHMS[md].create()});
+            return Utils.strToArrayBuffer(eMsg);
+        } catch (err) {
+            if (err.message === "RSAES-OAEP input message length is too long.") {
+                throw new OperationError(`RSAES-OAEP input message length (${err.length}) is longer than the maximum allowed length (${err.maxLength}).`);
+            }
+            throw new OperationError(err);
+        }
+    }
+
+}
+
+export default RSAEncrypt;

+ 74 - 0
src/core/operations/RSASign.mjs

@@ -0,0 +1,74 @@
+/**
+ * @author Matt C [me@mitt.dev]
+ * @author gchq77703 []
+ * @copyright Crown Copyright 2020
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import OperationError from "../errors/OperationError";
+import forge from "node-forge/dist/forge.min.js";
+import { MD_ALGORITHMS } from "../lib/RSA.mjs";
+
+/**
+ * RSA Sign operation
+ */
+class RSASign extends Operation {
+
+    /**
+     * RSASign constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "RSA Sign";
+        this.module = "Ciphers";
+        this.description = "Sign a plaintext message with a PEM encoded RSA key.";
+        this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
+        this.inputType = "string";
+        this.outputType = "string";
+        this.args = [
+            {
+                name: "RSA Private Key (PEM)",
+                type: "text",
+                value: "-----BEGIN RSA PRIVATE KEY-----"
+            },
+            {
+                name: "Key Password",
+                type: "text",
+                value: ""
+            },
+            {
+                name: "Message Digest Algorithm",
+                type: "option",
+                value: Object.keys(MD_ALGORITHMS)
+            }
+        ];
+    }
+
+    /**
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {string}
+     */
+    run(input, args) {
+        const [key, password, mdAlgo] = args;
+        if (key.replace("-----BEGIN RSA PRIVATE KEY-----", "").length === 0) {
+            throw new OperationError("Please enter a private key.");
+        }
+        try {
+            const privateKey = forge.pki.decryptRsaPrivateKey(key, password);
+            // Generate message hash
+            const md = MD_ALGORITHMS[mdAlgo].create();
+            md.update(input, "utf8");
+            // Sign message hash
+            const sig = privateKey.sign(md);
+            return sig;
+        } catch (err) {
+            throw new OperationError(err);
+        }
+    }
+
+}
+
+export default RSASign;

+ 77 - 0
src/core/operations/RSAVerify.mjs

@@ -0,0 +1,77 @@
+/**
+ * @author Matt C [me@mitt.dev]
+ * @copyright Crown Copyright 2020
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import forge from "node-forge/dist/forge.min.js";
+import { MD_ALGORITHMS } from "../lib/RSA.mjs";
+
+/**
+ * RSA Verify operation
+ */
+class RSAVerify extends Operation {
+
+    /**
+     * RSAVerify constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "RSA Verify";
+        this.module = "Ciphers";
+        this.description = "Verify a message against a signature and a public PEM encoded RSA key.";
+        this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)";
+        this.inputType = "string";
+        this.outputType = "string";
+        this.args = [
+            {
+                name: "RSA Public Key (PEM)",
+                type: "text",
+                value: "-----BEGIN RSA PUBLIC KEY-----"
+            },
+            {
+                name: "Message",
+                type: "text",
+                value: ""
+            },
+            {
+                name: "Message Digest Algorithm",
+                type: "option",
+                value: Object.keys(MD_ALGORITHMS)
+            }
+        ];
+    }
+
+    /**
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {string}
+     */
+    run(input, args) {
+        const [pemKey, message, mdAlgo] = args;
+        if (pemKey.replace("-----BEGIN RSA PUBLIC KEY-----", "").length === 0) {
+            throw new OperationError("Please enter a public key.");
+        }
+        try {
+            // Load public key
+            const pubKey = forge.pki.publicKeyFromPem(pemKey);
+            // Generate message digest
+            const md = MD_ALGORITHMS[mdAlgo].create();
+            md.update(message, "utf8");
+            // Compare signed message digest and generated message digest
+            const result = pubKey.verify(md.digest().bytes(), input);
+            return result ? "Verified OK" : "Verification Failure";
+        } catch (err) {
+            if (err.message === "Encrypted message length is invalid.") {
+                throw new OperationError(`Signature length (${err.length}) does not match expected length based on key (${err.expected}).`);
+            }
+            throw new OperationError(err);
+        }
+    }
+
+}
+
+export default RSAVerify;

+ 2 - 1
tests/operations/index.mjs

@@ -105,7 +105,8 @@ import "./tests/Unicode.mjs";
 
 // Cannot test operations that use the File type yet
 // import "./tests/SplitColourChannels.mjs";
-
+// Cannot test as minified forge does not support node
+// import "./tests/RSA.mjs";
 const testStatus = {
     allTestsPassing: true,
     counts: {

+ 22 - 0
tests/operations/samples/Ciphers.mjs

@@ -0,0 +1,22 @@
+export const ASCII_TEXT = "A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools.";
+
+export const UTF8_TEXT = "Шанцы на высвятленне таго, што адбываецца на самай справе ў сусвеце настолькі выдаленыя, адзінае, што трэба зрабіць, гэта павесіць пачуццё яго і трымаць сябе занятымі.";
+
+export const ALL_BYTES = [
+    "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+    "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
+    "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
+    "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
+    "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+    "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
+    "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
+    "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
+    "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
+    "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
+    "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
+    "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
+    "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
+    "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
+    "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
+    "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+].join("");

+ 1 - 24
tests/operations/tests/PGP.mjs

@@ -6,30 +6,7 @@
  * @license Apache-2.0
  */
 import TestRegister from "../../lib/TestRegister.mjs";
-
-const ASCII_TEXT = "A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools.";
-
-const UTF8_TEXT = "Шанцы на высвятленне таго, што адбываецца на самай справе ў сусвеце настолькі выдаленыя, адзінае, што трэба зрабіць, гэта павесіць пачуццё яго і трымаць сябе занятымі.";
-
-const ALL_BYTES = [
-    "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
-    "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
-    "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
-    "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
-    "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
-    "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
-    "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
-    "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
-    "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
-    "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
-    "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
-    "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
-    "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
-    "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
-    "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
-    "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
-].join("");
-
+import {ASCII_TEXT, UTF8_TEXT, ALL_BYTES} from "../samples/Ciphers.mjs";
 
 // RSA-1024
 const ALICE_PRIVATE = `-----BEGIN PGP PRIVATE KEY BLOCK-----

+ 350 - 0
tests/operations/tests/RSA.mjs

@@ -0,0 +1,350 @@
+/**
+ * RSA tests.
+ *
+ * @author Matt C [me@mitt.dev]
+ * @copyright Crown Copyright 2020
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+import {ASCII_TEXT, UTF8_TEXT, ALL_BYTES} from "../samples/Ciphers.mjs";
+
+const PEM_PRIV_2048 = `-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAwfaUOpUEutKyU3wkCv6kYunz4MqxzSuTSckRz1IxwZtwIiqq
++ejkM6ioXPyGadfFNvG0JVOgr1q4KQglq0vXaYG57HZ8iinXnHgy1vr8i+fWYITB
+RMrEDySaQh3sxVj8NudPDoTIxZwUcIUu/N53pUmI08ADxXPA+ZymPyZhZyxrj5Jq
+2O2QuRu+R7K44NDweP/rETbGo5+QAPydm6UqBzTky/ohv6EGhjyqnaskTWwLWK6P
+dKva8rEMb8nNJvhoTJDLYUfNjB7DFnWxgWuR/KVkXGAHX99J/wh6QTS+bsyJ2/Mw
+Df6NWdh3iP7msLNl/GqL+HunhHjrthvvWlODDwIDAQABAoIBAApKwLvJC3q0UmUO
+qcTxlRxwiJHNf5jA7qxUIH9NP7mju1P8ypy/KFi7Ys+oUKOOIPdU5Pe0E8sqN6pp
+tcH8oL4G9awf72TPapLxZ9UzdTIhR6VQdgbl8XhSO2M1vkoMejmZlX7SOesOaKE9
+1+vwDA43tCx0PF7+UOeN0d549WMphvw3VkSInO/MYpobCGra4YdrhYOhFMyLEGgA
+zCyVUOxi538tyyFtK2EEQdcMtvVA6SECjF4xD/qrme0LelIj/L1Uhiu+SOzYt4y+
+QLHL6zhJVfOejWxjeI7BhodkTV2D53n4svfizRgyYEb6iLPW3nlMYIlAksYaxxB9
+nR3sMHECgYEA9RU+8J5A8RnBcwnlc2X1xEW2PN7+A1MeWPQwFqRwIokgvGbCtwjG
+PwwNUYJCTBhfGhsISeCBOSYrDGTHsNH+tqFW2zlq61BolYl56jb1KgWzMOX8dak4
+sgXIuBbvyuFNk08VMIzwcA76ka/Iuu/nN9ZOM2UYpdpGG+CTOoIFULECgYEAyppm
+I+yAtrUn/BFmwmC8va4vqXlBFjvdkfX/71ywCpHIouLucMV7bILJu0nSCpmL1A7R
+DT6qo0p5g+Dxl/+O2VyC5D89PBvcuT1+HtEZGLOoKZnojbSrwDApGbzQi57GoQR6
+/SRjsdAmoelY8PFz2s2ZLJ4NkrZXYvkT1Tu8/78CgYEA4MAvC/HUlEWORbTZmk3y
+Z5+WU5QbVWkv91tXjiwWOVWPk7aY8ck2JDMlM45ExgvDiuknXLhpSMNbzu3MwraQ
+42JpiHjLOChxAFEmYEct5O99OGZwcmZQ+9CaFVfTZzXeMizfvbpB9EGIP3n4lpXS
+cD4zUKZxSAc3K/FyksERpsECgYEAhQPXeVBltQ68oKaAE6/VWqcIjbiY/dLyBkk+
+7dSpk1bhJefdadaN0NERRtARgXoLrn7Hy21QNILJwsaldwiGrbgqC1Zlipg0Ur3H
+ls3rLyeMiTuNzbNHa5dy9H3dYT0t5Tr+0EHa3jvtkTGVfiLX0FhZb0yZVrA2MTmc
+RsvAqxsCgYAgXy4qytgfzo5/bBt306NbtMEW3dWBWF77HAz4N1LynKZRUrAAK4rz
+BVmXFUaNQOg0q8WJG+iFF79u2UnL8iZ5GoPMcpvifsZgef1OHnQnFrfyXSr0fXIm
+xq8eZS0DpLvKGffCW03B9VDRHanE37Tng8lbgOtaufuVzFa1bCuLUA==
+-----END RSA PRIVATE KEY-----`;
+
+const PEM_PUB_2048 = `-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwfaUOpUEutKyU3wkCv6k
+Yunz4MqxzSuTSckRz1IxwZtwIiqq+ejkM6ioXPyGadfFNvG0JVOgr1q4KQglq0vX
+aYG57HZ8iinXnHgy1vr8i+fWYITBRMrEDySaQh3sxVj8NudPDoTIxZwUcIUu/N53
+pUmI08ADxXPA+ZymPyZhZyxrj5Jq2O2QuRu+R7K44NDweP/rETbGo5+QAPydm6Uq
+BzTky/ohv6EGhjyqnaskTWwLWK6PdKva8rEMb8nNJvhoTJDLYUfNjB7DFnWxgWuR
+/KVkXGAHX99J/wh6QTS+bsyJ2/MwDf6NWdh3iP7msLNl/GqL+HunhHjrthvvWlOD
+DwIDAQAB
+-----END PUBLIC KEY-----`;
+
+TestRegister.addTests([
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-1, nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-1"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-1"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-1, ASCII",
+        input: ASCII_TEXT,
+        expectedOutput: ASCII_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-1"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-1"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-1, UTF-8",
+        input: UTF8_TEXT,
+        expectedOutput: UTF8_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-1"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-1"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-1, All bytes",
+        input: ALL_BYTES,
+        expectedOutput: ALL_BYTES,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-1"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-1"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/MD5, nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "MD5"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "MD5"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/MD5, ASCII",
+        input: ASCII_TEXT,
+        expectedOutput: ASCII_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "MD5"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "MD5"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/MD5, UTF-8",
+        input: UTF8_TEXT,
+        expectedOutput: UTF8_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "MD5"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "MD5"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/MD5, All bytes",
+        input: ALL_BYTES,
+        expectedOutput: ALL_BYTES,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "MD5"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "MD5"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-256, nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-256"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-256"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-256, ASCII",
+        input: ASCII_TEXT,
+        expectedOutput: ASCII_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-256"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-256"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-256, UTF-8",
+        input: UTF8_TEXT,
+        expectedOutput: UTF8_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-256"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-256"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-256, All bytes",
+        input: ALL_BYTES,
+        expectedOutput: ALL_BYTES,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-256"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-256"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-384, nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-384"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-384"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-384, ASCII",
+        input: ASCII_TEXT,
+        expectedOutput: ASCII_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-384"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-384"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-384, UTF-8",
+        input: UTF8_TEXT,
+        expectedOutput: UTF8_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-384"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-384"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-384, All bytes",
+        input: ALL_BYTES,
+        expectedOutput: ALL_BYTES,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-384"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-384"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-512, nothing",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-512"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-512"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-512, ASCII",
+        input: ASCII_TEXT,
+        expectedOutput: ASCII_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-512"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-512"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-512, UTF-8",
+        input: UTF8_TEXT,
+        expectedOutput: UTF8_TEXT,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-512"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-512"]
+            }
+        ]
+    },
+    {
+        name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-512, All bytes",
+        input: ALL_BYTES,
+        expectedOutput: ALL_BYTES,
+        recipeConfig: [
+            {
+                "op": "RSA Encrypt",
+                "args": [PEM_PUB_2048, "RSA-OAEP", "SHA-512"]
+            },
+            {
+                "op": "RSA Decrypt",
+                "args": [PEM_PRIV_2048, "", "RSA-OAEP", "SHA-512"]
+            }
+        ]
+    },
+]);

+ 8 - 0
webpack.config.js

@@ -75,6 +75,10 @@ module.exports = {
                     context: "node_modules/tesseract.js-core/",
                     from: "tesseract-core.wasm.js",
                     to: "assets/tesseract"
+                }, {
+                    context: "node_modules/node-forge/dist",
+                    from: "prime.worker.min.js",
+                    to: "assets/forge/"
                 }
             ]
         })
@@ -116,6 +120,10 @@ module.exports = {
                     additionalCode: "var jQuery = false;"
                 }
             },
+            {
+                test: /prime.worker.min.js$/,
+                use: "raw-loader"
+            },
             {
                 test: /bootstrap-material-design/,
                 loader: "imports-loader",