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 4e4f7581c..4b1de628c 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 7d58aa245..3d418453a 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 75c495b7f..a78507e64 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 b7cc8f91a..11050349e 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 000000000..c2d114a2d --- /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 81ccb8b27..b1512a3e9 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 8881624de..9172ed064 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 1fca5f71b..553216ef2 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"] + } + ], + }, ]);