From 74ae77f17ad92ff1790d2737a0e2400d45985b54 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Mon, 1 Feb 2021 19:15:32 +0000 Subject: [PATCH] Tidied up and added tests for RSA operations --- src/core/config/Categories.json | 10 ++--- src/core/lib/RSA.mjs | 10 ++++- src/core/operations/GeneratePGPKeyPair.mjs | 2 +- src/core/operations/GenerateRSAKeyPair.mjs | 12 ++++-- src/core/operations/RSADecrypt.mjs | 9 ++--- src/core/operations/RSAEncrypt.mjs | 11 +++--- src/core/operations/RSASign.mjs | 6 +-- src/core/operations/RSAVerify.mjs | 2 +- tests/operations/index.mjs | 5 +-- tests/operations/samples/Ciphers.mjs | 9 +++++ tests/operations/tests/RSA.mjs | 44 +++++++++++----------- 11 files changed, 70 insertions(+), 50 deletions(-) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 70c63b33..1afa6dcb 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -105,11 +105,6 @@ "Derive EVP key", "Bcrypt", "Scrypt", - "Generate RSA Key Pair", - "RSA Sign", - "RSA Verify", - "RSA Encrypt", - "RSA Decrypt", "JWT Sign", "JWT Verify", "JWT Decode", @@ -139,6 +134,11 @@ "PGP Verify", "PGP Encrypt and Sign", "PGP Decrypt and Verify", + "Generate RSA Key Pair", + "RSA Sign", + "RSA Verify", + "RSA Encrypt", + "RSA Decrypt", "Parse SSH Host Key" ] }, diff --git a/src/core/lib/RSA.mjs b/src/core/lib/RSA.mjs index 0dab67a3..9037379c 100644 --- a/src/core/lib/RSA.mjs +++ b/src/core/lib/RSA.mjs @@ -1,4 +1,12 @@ -import forge from "node-forge/dist/forge.min.js"; +/** + * RSA resources. + * + * @author Matt C [me@mitt.dev] + * @copyright Crown Copyright 2021 + * @license Apache-2.0 + */ + +import forge from "node-forge"; export const MD_ALGORITHMS = { "SHA-1": forge.md.sha1, diff --git a/src/core/operations/GeneratePGPKeyPair.mjs b/src/core/operations/GeneratePGPKeyPair.mjs index bb366b6f..5943ce31 100644 --- a/src/core/operations/GeneratePGPKeyPair.mjs +++ b/src/core/operations/GeneratePGPKeyPair.mjs @@ -25,7 +25,7 @@ class GeneratePGPKeyPair extends Operation { this.name = "Generate PGP Key Pair"; this.module = "PGP"; - this.description = "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys."; + this.description = "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.

WARNING: Cryptographic operations in CyberChef should not be relied upon to provide security in any situation. No guarantee is offered for their correctness. We advise you not to use keys generated from CyberChef in operational contexts."; this.infoURL = "https://wikipedia.org/wiki/Pretty_Good_Privacy"; this.inputType = "string"; this.outputType = "string"; diff --git a/src/core/operations/GenerateRSAKeyPair.mjs b/src/core/operations/GenerateRSAKeyPair.mjs index 57cfcdd0..f263eb23 100644 --- a/src/core/operations/GenerateRSAKeyPair.mjs +++ b/src/core/operations/GenerateRSAKeyPair.mjs @@ -5,8 +5,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import forge from "node-forge/dist/forge.min.js"; +import Operation from "../Operation.mjs"; +import forge from "node-forge"; /** * Generate RSA Key Pair operation @@ -21,7 +21,7 @@ class GenerateRSAKeyPair extends Operation { this.name = "Generate RSA Key Pair"; this.module = "Ciphers"; - this.description = "Generate an RSA key pair with a given number of bits"; + this.description = "Generate an RSA key pair with a given number of bits.

WARNING: Cryptographic operations in CyberChef should not be relied upon to provide security in any situation. No guarantee is offered for their correctness. We advise you not to use keys generated from CyberChef in operational contexts."; this.infoURL = "https://wikipedia.org/wiki/RSA_(cryptosystem)"; this.inputType = "string"; this.outputType = "string"; @@ -56,7 +56,11 @@ class GenerateRSAKeyPair extends Operation { 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) => { + 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; diff --git a/src/core/operations/RSADecrypt.mjs b/src/core/operations/RSADecrypt.mjs index eb24aeab..5b32b790 100644 --- a/src/core/operations/RSADecrypt.mjs +++ b/src/core/operations/RSADecrypt.mjs @@ -6,8 +6,7 @@ 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 forge from "node-forge"; import { MD_ALGORITHMS } from "../lib/RSA.mjs"; /** @@ -25,7 +24,7 @@ class RSADecrypt extends Operation { 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.inputType = "string"; this.outputType = "string"; this.args = [ { @@ -75,8 +74,8 @@ class RSADecrypt extends Operation { } try { const privKey = forge.pki.decryptRsaPrivateKey(pemKey, password); - const dMsg = privKey.decrypt(Utils.arrayBufferToStr(input), scheme, {md: MD_ALGORITHMS[md].create()}); - return dMsg; + const dMsg = privKey.decrypt(input, scheme, {md: MD_ALGORITHMS[md].create()}); + return forge.util.decodeUtf8(dMsg); } catch (err) { throw new OperationError(err); } diff --git a/src/core/operations/RSAEncrypt.mjs b/src/core/operations/RSAEncrypt.mjs index e788a668..859ce132 100644 --- a/src/core/operations/RSAEncrypt.mjs +++ b/src/core/operations/RSAEncrypt.mjs @@ -6,8 +6,7 @@ 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 forge from "node-forge"; import { MD_ALGORITHMS } from "../lib/RSA.mjs"; /** @@ -26,7 +25,7 @@ class RSAEncrypt extends Operation { 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.outputType = "string"; this.args = [ { name: "RSA Public Key (PEM)", @@ -72,9 +71,11 @@ class RSAEncrypt extends Operation { try { // Load public key const pubKey = forge.pki.publicKeyFromPem(pemKey); + // https://github.com/digitalbazaar/forge/issues/465#issuecomment-271097600 + const plaintextBytes = forge.util.encodeUtf8(input); // Encrypt message - const eMsg = pubKey.encrypt(input, scheme, {md: MD_ALGORITHMS[md].create()}); - return Utils.strToArrayBuffer(eMsg); + const eMsg = pubKey.encrypt(plaintextBytes, scheme, {md: MD_ALGORITHMS[md].create()}); + return 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}).`); diff --git a/src/core/operations/RSASign.mjs b/src/core/operations/RSASign.mjs index 6161c5fe..25160f53 100644 --- a/src/core/operations/RSASign.mjs +++ b/src/core/operations/RSASign.mjs @@ -5,9 +5,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import forge from "node-forge/dist/forge.min.js"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import forge from "node-forge"; import { MD_ALGORITHMS } from "../lib/RSA.mjs"; /** diff --git a/src/core/operations/RSAVerify.mjs b/src/core/operations/RSAVerify.mjs index e1a8b0cb..89b7d81f 100644 --- a/src/core/operations/RSAVerify.mjs +++ b/src/core/operations/RSAVerify.mjs @@ -6,7 +6,7 @@ import Operation from "../Operation.mjs"; import OperationError from "../errors/OperationError.mjs"; -import forge from "node-forge/dist/forge.min.js"; +import forge from "node-forge"; import { MD_ALGORITHMS } from "../lib/RSA.mjs"; /** diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 50079b82..d20ccc61 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -101,12 +101,11 @@ import "./tests/CipherSaber2.mjs"; import "./tests/Colossus.mjs"; import "./tests/ParseObjectIDTimestamp.mjs"; import "./tests/Unicode.mjs"; - +import "./tests/RSA.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: { diff --git a/tests/operations/samples/Ciphers.mjs b/tests/operations/samples/Ciphers.mjs index a1363d83..2d975131 100644 --- a/tests/operations/samples/Ciphers.mjs +++ b/tests/operations/samples/Ciphers.mjs @@ -1,3 +1,12 @@ +/** + * Various types of input data for use in tests + * + * @author n1474335 [n1474335@gmail.com] + * @author Matt C [me@mitt.dev] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + 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 = "Шанцы на высвятленне таго, што адбываецца на самай справе ў сусвеце настолькі выдаленыя, адзінае, што трэба зрабіць, гэта павесіць пачуццё яго і трымаць сябе занятымі."; diff --git a/tests/operations/tests/RSA.mjs b/tests/operations/tests/RSA.mjs index 2ade5647..33e7139f 100644 --- a/tests/operations/tests/RSA.mjs +++ b/tests/operations/tests/RSA.mjs @@ -79,8 +79,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-1, UTF-8", - input: UTF8_TEXT, - expectedOutput: UTF8_TEXT, + input: UTF8_TEXT.substr(0, 100), + expectedOutput: UTF8_TEXT.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt", @@ -94,8 +94,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-1, All bytes", - input: ALL_BYTES, - expectedOutput: ALL_BYTES, + input: ALL_BYTES.substr(0, 100), + expectedOutput: ALL_BYTES.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt", @@ -139,8 +139,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/MD5, UTF-8", - input: UTF8_TEXT, - expectedOutput: UTF8_TEXT, + input: UTF8_TEXT.substr(0, 100), + expectedOutput: UTF8_TEXT.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt", @@ -154,8 +154,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/MD5, All bytes", - input: ALL_BYTES, - expectedOutput: ALL_BYTES, + input: ALL_BYTES.substr(0, 100), + expectedOutput: ALL_BYTES.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt", @@ -199,8 +199,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-256, UTF-8", - input: UTF8_TEXT, - expectedOutput: UTF8_TEXT, + input: UTF8_TEXT.substr(0, 100), + expectedOutput: UTF8_TEXT.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt", @@ -214,8 +214,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-256, All bytes", - input: ALL_BYTES, - expectedOutput: ALL_BYTES, + input: ALL_BYTES.substr(0, 100), + expectedOutput: ALL_BYTES.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt", @@ -259,8 +259,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-384, UTF-8", - input: UTF8_TEXT, - expectedOutput: UTF8_TEXT, + input: UTF8_TEXT.substr(0, 80), + expectedOutput: UTF8_TEXT.substr(0, 80), recipeConfig: [ { "op": "RSA Encrypt", @@ -274,8 +274,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-384, All bytes", - input: ALL_BYTES, - expectedOutput: ALL_BYTES, + input: ALL_BYTES.substr(0, 100), + expectedOutput: ALL_BYTES.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt", @@ -304,8 +304,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-512, ASCII", - input: ASCII_TEXT, - expectedOutput: ASCII_TEXT, + input: ASCII_TEXT.substr(0, 100), + expectedOutput: ASCII_TEXT.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt", @@ -319,8 +319,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-512, UTF-8", - input: UTF8_TEXT, - expectedOutput: UTF8_TEXT, + input: UTF8_TEXT.substr(0, 60), + expectedOutput: UTF8_TEXT.substr(0, 60), recipeConfig: [ { "op": "RSA Encrypt", @@ -334,8 +334,8 @@ TestRegister.addTests([ }, { name: "RSA Encrypt/Decrypt: RSA-OAEP/SHA-512, All bytes", - input: ALL_BYTES, - expectedOutput: ALL_BYTES, + input: ALL_BYTES.substr(0, 100), + expectedOutput: ALL_BYTES.substr(0, 100), recipeConfig: [ { "op": "RSA Encrypt",