From 2d6a56343b3f0b77e34b09a5c8cfdc97c2c2181d Mon Sep 17 00:00:00 2001 From: Matt C Date: Fri, 11 May 2018 16:32:19 +0100 Subject: [PATCH] Converted substitute operation, added tests & moved to OperationError --- src/core/lib/Ciphers.mjs | 3 +- src/core/operations/AffineCipherDecode.mjs | 5 +- src/core/operations/BifidCipherDecode.mjs | 4 +- src/core/operations/BifidCipherEncode.mjs | 3 +- src/core/operations/Substitute.mjs | 65 ++++++++++++++++++++++ src/core/operations/VigenèreDecode.mjs | 6 +- src/core/operations/VigenèreEncode.mjs | 5 +- test/tests/operations/Ciphers.mjs | 46 ++++++++++++++- 8 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 src/core/operations/Substitute.mjs diff --git a/src/core/lib/Ciphers.mjs b/src/core/lib/Ciphers.mjs index 4e4f7581..4b1de628 100644 --- a/src/core/lib/Ciphers.mjs +++ b/src/core/lib/Ciphers.mjs @@ -6,6 +6,7 @@ * @license Apache-2.0 * */ +import OperationError from "../errors/OperationError"; /** * Affine Cipher Encode operation. @@ -22,7 +23,7 @@ export function affineEncode(input, args) { let output = ""; if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) { - return "The values of a and b can only be integers."; + throw new OperationError("The values of a and b can only be integers."); } for (let i = 0; i < input.length; i++) { diff --git a/src/core/operations/AffineCipherDecode.mjs b/src/core/operations/AffineCipherDecode.mjs index 7d58aa24..3d418453 100644 --- a/src/core/operations/AffineCipherDecode.mjs +++ b/src/core/operations/AffineCipherDecode.mjs @@ -6,6 +6,7 @@ import Operation from "../Operation"; import Utils from "../Utils"; +import OperationError from "../errors/OperationError"; /** * Affine Cipher Decode operation @@ -50,11 +51,11 @@ class AffineCipherDecode extends Operation { let output = ""; if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) { - return "The values of a and b can only be integers."; + throw new OperationError("The values of a and b can only be integers."); } if (Utils.gcd(a, 26) !== 1) { - return "The value of `a` must be coprime to 26."; + throw new OperationError("The value of `a` must be coprime to 26."); } for (let i = 0; i < input.length; i++) { diff --git a/src/core/operations/BifidCipherDecode.mjs b/src/core/operations/BifidCipherDecode.mjs index 75c495b7..a78507e6 100644 --- a/src/core/operations/BifidCipherDecode.mjs +++ b/src/core/operations/BifidCipherDecode.mjs @@ -6,7 +6,7 @@ import Operation from "../Operation"; import { genPolybiusSquare } from "../lib/Ciphers"; - +import OperationError from "../errors/OperationError"; /** * Bifid Cipher Decode operation */ @@ -48,7 +48,7 @@ class BifidCipherDecode extends Operation { trans = ""; if (!/^[A-Z]+$/.test(keywordStr) && keyword.length > 0) - return "The key must consist only of letters in the English alphabet"; + throw new OperationError("The key must consist only of letters in the English alphabet"); const polybius = genPolybiusSquare(keywordStr); diff --git a/src/core/operations/BifidCipherEncode.mjs b/src/core/operations/BifidCipherEncode.mjs index b7cc8f91..11050349 100644 --- a/src/core/operations/BifidCipherEncode.mjs +++ b/src/core/operations/BifidCipherEncode.mjs @@ -5,6 +5,7 @@ */ import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; import { genPolybiusSquare } from "../lib/Ciphers"; /** @@ -50,7 +51,7 @@ class BifidCipherEncode extends Operation { if (!/^[A-Z]+$/.test(keywordStr) && keyword.length > 0) - return "The key must consist only of letters in the English alphabet"; + throw new OperationError("The key must consist only of letters in the English alphabet"); const polybius = genPolybiusSquare(keywordStr); diff --git a/src/core/operations/Substitute.mjs b/src/core/operations/Substitute.mjs new file mode 100644 index 00000000..c2d114a2 --- /dev/null +++ b/src/core/operations/Substitute.mjs @@ -0,0 +1,65 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import Utils from "../Utils"; + +/** + * Substitute operation + */ +class Substitute extends Operation { + + /** + * Substitute constructor + */ + constructor() { + super(); + + this.name = "Substitute"; + this.module = "Ciphers"; + this.description = "A substitution cipher allowing you to specify bytes to replace with other byte values. This can be used to create Caesar ciphers but is more powerful as any byte value can be substituted, not just letters, and the substitution values need not be in order.

Enter the bytes you want to replace in the Plaintext field and the bytes to replace them with in the Ciphertext field.

Non-printable bytes can be specified using string escape notation. For example, a line feed character can be written as either \n or \x0a.

Byte ranges can be specified using a hyphen. For example, the sequence 0123456789 can be written as 0-9."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Plaintext", + "type": "binaryString", + "value": "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + }, + { + "name": "Ciphertext", + "type": "binaryString", + "value": "XYZABCDEFGHIJKLMNOPQRSTUVW" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const plaintext = Utils.expandAlphRange(args[0]).join(""), + ciphertext = Utils.expandAlphRange(args[1]).join(""); + let output = "", + index = -1; + + if (plaintext.length !== ciphertext.length) { + output = "Warning: Plaintext and Ciphertext lengths differ\n\n"; + } + + for (let i = 0; i < input.length; i++) { + index = plaintext.indexOf(input[i]); + output += index > -1 && index < ciphertext.length ? ciphertext[index] : input[i]; + } + + return output; + } + +} + +export default Substitute; diff --git a/src/core/operations/VigenèreDecode.mjs b/src/core/operations/VigenèreDecode.mjs index 81ccb8b2..b1512a3e 100644 --- a/src/core/operations/VigenèreDecode.mjs +++ b/src/core/operations/VigenèreDecode.mjs @@ -5,7 +5,7 @@ */ import Operation from "../Operation"; - +import OperationError from "../errors/OperationError"; /** * Vigenère Decode operation */ @@ -45,8 +45,8 @@ class VigenèreDecode extends Operation { msgIndex, chr; - if (!key) return "No key entered"; - if (!/^[a-zA-Z]+$/.test(key)) return "The key must consist only of letters"; + if (!key) throw new OperationError("No key entered"); + if (!/^[a-zA-Z]+$/.test(key)) throw new OperationError("The key must consist only of letters"); for (let i = 0; i < input.length; i++) { if (alphabet.indexOf(input[i]) >= 0) { diff --git a/src/core/operations/VigenèreEncode.mjs b/src/core/operations/VigenèreEncode.mjs index 8881624d..9172ed06 100644 --- a/src/core/operations/VigenèreEncode.mjs +++ b/src/core/operations/VigenèreEncode.mjs @@ -5,6 +5,7 @@ */ import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; /** * Vigenère Encode operation @@ -45,8 +46,8 @@ class VigenèreEncode extends Operation { msgIndex, chr; - if (!key) return "No key entered"; - if (!/^[a-zA-Z]+$/.test(key)) return "The key must consist only of letters"; + if (!key) throw new OperationError("No key entered"); + if (!/^[a-zA-Z]+$/.test(key)) throw new OperationError("The key must consist only of letters"); for (let i = 0; i < input.length; i++) { if (alphabet.indexOf(input[i]) >= 0) { diff --git a/test/tests/operations/Ciphers.mjs b/test/tests/operations/Ciphers.mjs index 1fca5f71..553216ef 100644 --- a/test/tests/operations/Ciphers.mjs +++ b/test/tests/operations/Ciphers.mjs @@ -3,7 +3,7 @@ * * @author Matt C [matt@artemisbot.uk] * @author n1474335 [n1474335@gmail.com] - * + * * @copyright Crown Copyright 2018 * @license Apache-2.0 */ @@ -308,4 +308,48 @@ TestRegister.addTests([ } ], }, + { + name: "Substitute: no pt/ct", + input: "flee at once. we are discovered!", + expectedOutput: "flee at once. we are discovered!", + recipeConfig: [ + { + "op": "Substitute", + "args": ["", ""] + } + ], + }, + { + name: "Substitute: no input", + input: "", + expectedOutput: "", + recipeConfig: [ + { + "op": "Substitute", + "args": ["abcdefghijklmnopqrstuvwxyz", "zebrascdfghijklmnopqtuvwxy"] + } + ], + }, + { + name: "Substitute: uneven pt/ct", + input: "flee at once. we are discovered!", + expectedOutput: "Warning: Plaintext and Ciphertext lengths differ\n\nsiaa zq lkba. va zoa rfpbluaoar!", + recipeConfig: [ + { + "op": "Substitute", + "args": ["abcdefghijklmnopqrstuvwxyz", "zebrascdfghijklmnopqtuvwx"] + } + ], + }, + { + name: "Substitute: normal", + input: "flee at once. we are discovered!", + expectedOutput: "siaa zq lkba. va zoa rfpbluaoar!", + recipeConfig: [ + { + "op": "Substitute", + "args": ["abcdefghijklmnopqrstuvwxyz", "zebrascdfghijklmnopqtuvwxy"] + } + ], + }, ]);