diff --git a/src/core/Utils.js b/src/core/Utils.js index 64d536cc..150e8293 100755 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -1050,7 +1050,7 @@ const Utils = { /** * Actual modulo function, since % is actually the remainder function in JS. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {number} x * @param {number} y * @returns {number} @@ -1063,7 +1063,7 @@ const Utils = { /** * Finds the greatest common divisor of two numbers. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {number} x * @param {number} y * @returns {number} @@ -1079,7 +1079,7 @@ const Utils = { /** * Finds the modular inverse of two values. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {number} x * @param {number} y * @returns {number} diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index 2484b834..ef88c524 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -89,6 +89,8 @@ const Categories = [ "Vigenère Decode", "To Morse Code", "From Morse Code", + "Bifid Cipher Encode", + "Bifid Cipher Decode", "Affine Cipher Encode", "Affine Cipher Decode", "Atbash Cipher", diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index cac54b9e..b2eb621c 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -1512,6 +1512,36 @@ const OperationConfig = { } ] }, + "Bifid Cipher Encode": { + description: "The Bifid cipher is a cipher which uses a Polybius square in conjunction with transposition, which can be fairly difficult to decipher without knowing the alphabet keyword.", + run: Cipher.runBifidEnc, + highlight: true, + highlightReverse: true, + inputType: "string", + outputType: "string", + args: [ + { + name: "Keyword", + type: "string", + value: "" + } + ] + }, + "Bifid Cipher Decode": { + description: "The Bifid cipher is a cipher which uses a Polybius square in conjunction with transposition, which can be fairly difficult to decipher without knowing the alphabet keyword.", + run: Cipher.runBifidDec, + highlight: true, + highlightReverse: true, + inputType: "string", + outputType: "string", + args: [ + { + name: "Keyword", + type: "string", + value: "" + } + ] + }, "Affine Cipher Encode": { description: "The Affine cipher is a type of monoalphabetic substitution cipher, wherein each letter in an alphabet is mapped to its numeric equivalent, encrypted using simple mathematical function, (ax + b) % 26, and converted back to a letter.", run: Cipher.runAffineEnc, diff --git a/src/core/operations/ByteRepr.js b/src/core/operations/ByteRepr.js index 032de126..5e6f2aa8 100755 --- a/src/core/operations/ByteRepr.js +++ b/src/core/operations/ByteRepr.js @@ -58,7 +58,7 @@ const ByteRepr = { /** * To Octal operation. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {byteArray} input * @param {Object[]} args * @returns {string} @@ -72,7 +72,7 @@ const ByteRepr = { /** * From Octal operation. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {string} input * @param {Object[]} args * @returns {byteArray} diff --git a/src/core/operations/Cipher.js b/src/core/operations/Cipher.js index 95ebebca..63c70843 100755 --- a/src/core/operations/Cipher.js +++ b/src/core/operations/Cipher.js @@ -407,7 +407,7 @@ const Cipher = { /** * Vigenère Encode operation. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {string} input * @param {Object[]} args * @returns {string} @@ -454,7 +454,7 @@ const Cipher = { /** * Vigenère Decode operation. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {string} input * @param {Object[]} args * @returns {string} @@ -508,7 +508,7 @@ const Cipher = { /** * Affine Cipher Encode operation. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {string} input * @param {Object[]} args * @returns {string} @@ -540,9 +540,9 @@ const Cipher = { /** - * Affine Cipher Encode operation. + * Affine Cipher Decode operation. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {string} input * @param {Object[]} args * @returns {string} @@ -584,7 +584,7 @@ const Cipher = { /** * Atbash Cipher Encode operation. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {string} input * @param {Object[]} args * @returns {string} @@ -594,6 +594,159 @@ const Cipher = { }, + /** + * Generates a polybius square for the given keyword + * + * @private + * @author Matt C [matt@artemisbot.uk] + * @param {string} keyword - Must be upper case + * @returns {string} + */ + _genPolybiusSquare: function (keyword) { + const alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; + const polArray = `${keyword}${alpha}`.split("").unique(); + let polybius = []; + + for (let i = 0; i < 5; i++) { + polybius[i] = polArray.slice(i*5, i*5 + 5); + } + + return polybius; + }, + + /** + * Bifid Cipher Encode operation + * + * @author Matt C [matt@artemisbot.uk] + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + runBifidEnc: function (input, args) { + const keywordStr = args[0].toUpperCase().replace("J", "I"), + keyword = keywordStr.split("").unique(), + alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; + + let output = "", + xCo = [], + yCo = [], + structure = [], + count = 0; + + if (keyword.length > 25) + return "The alphabet keyword must be less than 25 characters."; + + if (!/^[a-zA-Z]+$/.test(keywordStr) && keyword.length > 0) + return "The key must consist only of letters"; + + const polybius = Cipher._genPolybiusSquare(keywordStr); + + input.replace("J", "I").split("").forEach(letter => { + let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0, + polInd; + + if (alpInd) { + for (let i = 0; i < 5; i++) { + polInd = polybius[i].indexOf(letter.toLocaleUpperCase()); + if (polInd >= 0) { + xCo.push(polInd); + yCo.push(i); + } + } + + if (alpha.split("").indexOf(letter) >= 0) { + structure.push(true); + } else if (alpInd) { + structure.push(false); + } + } else { + structure.push(letter); + } + }); + + const trans = `${yCo.join("")}${xCo.join("")}`; + + structure.forEach(pos => { + if (typeof pos === "boolean") { + let coords = trans.substr(2*count, 2).split(""); + + output += pos ? + polybius[coords[0]][coords[1]] : + polybius[coords[0]][coords[1]].toLocaleLowerCase(); + count++; + } else { + output += pos; + } + }); + + return output; + }, + + /** + * Bifid Cipher Decode operation + * + * @author Matt C [matt@artemisbot.uk] + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + runBifidDec: function (input, args) { + const keywordStr = args[0].toUpperCase().replace("J", "I"), + keyword = keywordStr.split("").unique(), + alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; + + let output = "", + structure = [], + count = 0, + trans = ""; + + if (keyword.length > 25) + return "The alphabet keyword must be less than 25 characters."; + + if (!/^[a-zA-Z]+$/.test(keywordStr) && keyword.length > 0) + return "The key must consist only of letters"; + + const polybius = Cipher._genPolybiusSquare(keywordStr); + + input.replace("J", "I").split("").forEach((letter) => { + let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0, + polInd; + + if (alpInd) { + for (let i = 0; i < 5; i++) { + polInd = polybius[i].indexOf(letter.toLocaleUpperCase()); + if (polInd >= 0) { + trans += `${i}${polInd}`; + } + } + + if (alpha.split("").indexOf(letter) >= 0) { + structure.push(true); + } else if (alpInd) { + structure.push(false); + } + } else { + structure.push(letter); + } + }); + + structure.forEach(pos => { + if (typeof pos === "boolean") { + let coords = [trans[count], trans[count+trans.length/2]]; + + output += pos ? + polybius[coords[0]][coords[1]] : + polybius[coords[0]][coords[1]].toLocaleLowerCase(); + count++; + } else { + output += pos; + } + }); + + return output; + }, + + /** * @constant * @default diff --git a/src/core/operations/Rotate.js b/src/core/operations/Rotate.js index f046d1fa..d66dbdae 100755 --- a/src/core/operations/Rotate.js +++ b/src/core/operations/Rotate.js @@ -135,7 +135,7 @@ const Rotate = { /** * ROT47 operation. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @param {byteArray} input * @param {Object[]} args * @returns {byteArray} diff --git a/test/index.js b/test/index.js index dbf1393d..adb41f64 100644 --- a/test/index.js +++ b/test/index.js @@ -14,6 +14,7 @@ import TestRegister from "./TestRegister.js"; import "./tests/operations/Base58.js"; import "./tests/operations/ByteRepr.js"; import "./tests/operations/CharEnc.js"; +import "./tests/operations/Cipher.js"; import "./tests/operations/Code.js"; import "./tests/operations/Compress.js"; import "./tests/operations/DateTime.js"; diff --git a/test/tests/operations/ByteRepr.js b/test/tests/operations/ByteRepr.js index 3a29add2..0c57d1fc 100644 --- a/test/tests/operations/ByteRepr.js +++ b/test/tests/operations/ByteRepr.js @@ -1,7 +1,7 @@ /** * ByteRepr tests. * - * @author Matt C [matt@artemisbot.pw] + * @author Matt C [matt@artemisbot.uk] * @copyright Crown Copyright 2017 * @license Apache-2.0 */ diff --git a/test/tests/operations/Cipher.js b/test/tests/operations/Cipher.js new file mode 100644 index 00000000..2a8993f0 --- /dev/null +++ b/test/tests/operations/Cipher.js @@ -0,0 +1,78 @@ +/** + * Cipher tests. + * + * @author Matt C [matt@artemisbot.uk] + * + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister.js"; + +TestRegister.addTests([ + { + name: "Bifid Cipher Encode: no input", + input: "", + expectedOutput: "", + recipeConfig: [ + { + "op": "Bifid Cipher Encode", + "args": ["nothing"] + } + ], + }, + { + name: "Bifid Cipher Encode: no key", + input: "We recreate conditions similar to the Van-Allen radiation belt in our secure facilities.", + expectedOutput: "Vq daqcliho rmltofvlnc qbdhlcr nt qdq Fbm-Rdkkm vuoottnoi aitp al axf tdtmvt owppkaodtx.", + recipeConfig: [ + { + "op": "Bifid Cipher Encode", + "args": [""] + } + ], + }, + { + name: "Bifid Cipher Encode: normal", + input: "We recreate conditions similar to the Van-Allen radiation belt in our secure facilities.", + expectedOutput: "Wc snpsigdd cpfrrcxnfi hikdnnp dm crc Fcb-Pdeug vueageacc vtyl sa zxm crebzp lyoeuaiwpv.", + recipeConfig: [ + { + "op": "Bifid Cipher Encode", + "args": ["Schrodinger"] + } + ], + }, + { + name: "Bifid Cipher Decode: no input", + input: "", + expectedOutput: "", + recipeConfig: [ + { + "op": "Bifid Cipher Decode", + "args": ["nothing"] + } + ], + }, + { + name: "Bifid Cipher Decode: no key", + input: "Vq daqcliho rmltofvlnc qbdhlcr nt qdq Fbm-Rdkkm vuoottnoi aitp al axf tdtmvt owppkaodtx.", + expectedOutput: "We recreate conditions similar to the Van-Allen radiation belt in our secure facilities.", + recipeConfig: [ + { + "op": "Bifid Cipher Decode", + "args": [""] + } + ], + }, + { + name: "Bifid Cipher Decode: normal", + input: "Wc snpsigdd cpfrrcxnfi hikdnnp dm crc Fcb-Pdeug vueageacc vtyl sa zxm crebzp lyoeuaiwpv.", + expectedOutput: "We recreate conditions similar to the Van-Allen radiation belt in our secure facilities.", + recipeConfig: [ + { + "op": "Bifid Cipher Decode", + "args": ["Schrodinger"] + } + ], + }, +]);