From 91c6f682e79650e4d440dc3191a31ffcd860df64 Mon Sep 17 00:00:00 2001 From: Matt C Date: Wed, 21 Jun 2017 22:28:17 +0100 Subject: [PATCH 1/3] Added Bifid Cipher Encode & Decode Bifid Cipher + Tests --- src/core/config/Categories.js | 2 + src/core/config/OperationConfig.js | 30 ++++++ src/core/operations/Cipher.js | 143 +++++++++++++++++++++++++++-- test/index.js | 1 + test/tests/operations/Cipher.js | 78 ++++++++++++++++ 5 files changed, 248 insertions(+), 6 deletions(-) create mode 100644 test/tests/operations/Cipher.js 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 718e23e7..1f755756 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: "Alphabet Key", + 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: "Alphabet Key", + 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/Cipher.js b/src/core/operations/Cipher.js index 8db28d0b..7cffd807 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} @@ -593,6 +593,137 @@ const Cipher = { return Cipher.runAffineEnc(input, [25, 25]); }, + /** + * Generates a polybius square for the given keyword + * + * @author Matt C [matt@artemisbot.uk] + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + genPolybiusSquare: function (keyword) { + const alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; + let polybius = [], + polString = ""; + keyword.split("").unique().forEach(letter => { + polString += letter; + }); + polString = `${polString}${alpha}`.split("").unique().join(""); + for (let i = 0; i < 5; i++) { + polybius[i] = polString.substr(i*5, 5).split(""); + } + 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 keyword = args[0].toUpperCase().replace("J", "I"), + alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; + let output = "", + xCo = [], + yCo = [], + structure = [], + count = 0, + trans; + if (keyword.split("").unique().length > 25) return "The alphabet keyword must be less than 25 characters."; + if (!/^[a-zA-Z]+$/.test(keyword) && keyword.split("").unique().length > 0) return "The key must consist only of letters"; + const polybius = Cipher.genPolybiusSquare(keyword); + 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); + } + }); + trans = `${yCo.join("")}${xCo.join("")}`; + structure.forEach(pos => { + if (typeof pos === "boolean") { + let coords = trans.substr(2*count, 2).split(""); + if (pos) { + output += polybius[coords[0]][coords[1]]; + } else { + output += 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 keyword = args[0].toUpperCase().replace("J", "I"), + alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; + let output = "", + structure = [], + count = 0, + trans = ""; + if (keyword.split("").unique().length > 25) return "The alphabet keyword must be less than 25 characters."; + if (!/^[a-zA-Z]+$/.test(keyword) && keyword.split("").unique().length > 0) return "The key must consist only of letters"; + const polybius = Cipher.genPolybiusSquare(keyword); + 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]]; + if (pos) { + output += polybius[coords[0]][coords[1]]; + } else { + output += polybius[coords[0]][coords[1]].toLocaleLowerCase(); + } + count++; + } else { + output += pos; + } + }); + return output; + }, /** * @constant 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/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"] + } + ], + }, +]); From 246480daefae130c97f32e615fd9a05ec6e6e11c Mon Sep 17 00:00:00 2001 From: Matt C Date: Thu, 22 Jun 2017 17:13:31 +0100 Subject: [PATCH 2/3] Fixed styling errors --- src/core/Utils.js | 6 +++--- src/core/operations/ByteRepr.js | 4 ++-- src/core/operations/Cipher.js | 10 +++++----- src/core/operations/Rotate.js | 2 +- test/tests/operations/ByteRepr.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/core/Utils.js b/src/core/Utils.js index 09d577ac..c4b4d65a 100755 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -1048,7 +1048,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} @@ -1061,7 +1061,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} @@ -1077,7 +1077,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/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 7cffd807..aa87a795 100755 --- a/src/core/operations/Cipher.js +++ b/src/core/operations/Cipher.js @@ -596,12 +596,12 @@ const Cipher = { /** * Generates a polybius square for the given keyword * + * @private * @author Matt C [matt@artemisbot.uk] - * @param {string} input - * @param {Object[]} args + * @param {string} keyword * @returns {string} */ - genPolybiusSquare: function (keyword) { + _genPolybiusSquare: function (keyword) { const alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; let polybius = [], polString = ""; @@ -634,7 +634,7 @@ const Cipher = { trans; if (keyword.split("").unique().length > 25) return "The alphabet keyword must be less than 25 characters."; if (!/^[a-zA-Z]+$/.test(keyword) && keyword.split("").unique().length > 0) return "The key must consist only of letters"; - const polybius = Cipher.genPolybiusSquare(keyword); + const polybius = Cipher._genPolybiusSquare(keyword); input.replace("J", "I").split("").forEach((letter) => { let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0, polInd; @@ -689,7 +689,7 @@ const Cipher = { trans = ""; if (keyword.split("").unique().length > 25) return "The alphabet keyword must be less than 25 characters."; if (!/^[a-zA-Z]+$/.test(keyword) && keyword.split("").unique().length > 0) return "The key must consist only of letters"; - const polybius = Cipher.genPolybiusSquare(keyword); + const polybius = Cipher._genPolybiusSquare(keyword); input.replace("J", "I").split("").forEach((letter) => { let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0, polInd; 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/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 */ From 323928ff86d9bfeb36d26b0a7f7e74eb5083055d Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 28 Jun 2017 19:54:34 +0100 Subject: [PATCH 3/3] Tidied up Bifid operations --- src/core/config/OperationConfig.js | 4 +- src/core/operations/Cipher.js | 82 +++++++++++++++++++----------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index 0f9a8379..b2eb621c 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -1521,7 +1521,7 @@ const OperationConfig = { outputType: "string", args: [ { - name: "Alphabet Key", + name: "Keyword", type: "string", value: "" } @@ -1536,7 +1536,7 @@ const OperationConfig = { outputType: "string", args: [ { - name: "Alphabet Key", + name: "Keyword", type: "string", value: "" } diff --git a/src/core/operations/Cipher.js b/src/core/operations/Cipher.js index f14df699..63c70843 100755 --- a/src/core/operations/Cipher.js +++ b/src/core/operations/Cipher.js @@ -593,25 +593,24 @@ const Cipher = { return Cipher.runAffineEnc(input, [25, 25]); }, + /** * Generates a polybius square for the given keyword * * @private * @author Matt C [matt@artemisbot.uk] - * @param {string} keyword + * @param {string} keyword - Must be upper case * @returns {string} */ _genPolybiusSquare: function (keyword) { const alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; - let polybius = [], - polString = ""; - keyword.split("").unique().forEach(letter => { - polString += letter; - }); - polString = `${polString}${alpha}`.split("").unique().join(""); + const polArray = `${keyword}${alpha}`.split("").unique(); + let polybius = []; + for (let i = 0; i < 5; i++) { - polybius[i] = polString.substr(i*5, 5).split(""); + polybius[i] = polArray.slice(i*5, i*5 + 5); } + return polybius; }, @@ -624,20 +623,28 @@ const Cipher = { * @returns {string} */ runBifidEnc: function (input, args) { - const keyword = args[0].toUpperCase().replace("J", "I"), + const keywordStr = args[0].toUpperCase().replace("J", "I"), + keyword = keywordStr.split("").unique(), alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; + let output = "", xCo = [], yCo = [], structure = [], - count = 0, - trans; - if (keyword.split("").unique().length > 25) return "The alphabet keyword must be less than 25 characters."; - if (!/^[a-zA-Z]+$/.test(keyword) && keyword.split("").unique().length > 0) return "The key must consist only of letters"; - const polybius = Cipher._genPolybiusSquare(keyword); - input.replace("J", "I").split("").forEach((letter) => { + 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()); @@ -646,6 +653,7 @@ const Cipher = { yCo.push(i); } } + if (alpha.split("").indexOf(letter) >= 0) { structure.push(true); } else if (alpInd) { @@ -655,20 +663,22 @@ const Cipher = { structure.push(letter); } }); - trans = `${yCo.join("")}${xCo.join("")}`; + + const trans = `${yCo.join("")}${xCo.join("")}`; + structure.forEach(pos => { if (typeof pos === "boolean") { let coords = trans.substr(2*count, 2).split(""); - if (pos) { - output += polybius[coords[0]][coords[1]]; - } else { - output += polybius[coords[0]][coords[1]].toLocaleLowerCase(); - } + + output += pos ? + polybius[coords[0]][coords[1]] : + polybius[coords[0]][coords[1]].toLocaleLowerCase(); count++; } else { output += pos; } }); + return output; }, @@ -681,18 +691,27 @@ const Cipher = { * @returns {string} */ runBifidDec: function (input, args) { - const keyword = args[0].toUpperCase().replace("J", "I"), + const keywordStr = args[0].toUpperCase().replace("J", "I"), + keyword = keywordStr.split("").unique(), alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; + let output = "", structure = [], count = 0, trans = ""; - if (keyword.split("").unique().length > 25) return "The alphabet keyword must be less than 25 characters."; - if (!/^[a-zA-Z]+$/.test(keyword) && keyword.split("").unique().length > 0) return "The key must consist only of letters"; - const polybius = Cipher._genPolybiusSquare(keyword); + + 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()); @@ -700,6 +719,7 @@ const Cipher = { trans += `${i}${polInd}`; } } + if (alpha.split("").indexOf(letter) >= 0) { structure.push(true); } else if (alpInd) { @@ -709,22 +729,24 @@ const Cipher = { structure.push(letter); } }); + structure.forEach(pos => { if (typeof pos === "boolean") { let coords = [trans[count], trans[count+trans.length/2]]; - if (pos) { - output += polybius[coords[0]][coords[1]]; - } else { - output += polybius[coords[0]][coords[1]].toLocaleLowerCase(); - } + + output += pos ? + polybius[coords[0]][coords[1]] : + polybius[coords[0]][coords[1]].toLocaleLowerCase(); count++; } else { output += pos; } }); + return output; }, + /** * @constant * @default