浏览代码

Add Bacon cipher decoding

Karsten Silkenbäumer 6 年之前
父节点
当前提交
77b098c5fe

+ 1 - 0
src/core/config/Categories.json

@@ -85,6 +85,7 @@
             "Vigenère Decode",
             "To Morse Code",
             "From Morse Code",
+            "Bacon Cipher Decode",
             "Bifid Cipher Encode",
             "Bifid Cipher Decode",
             "Affine Cipher Encode",

+ 37 - 0
src/core/lib/Bacon.mjs

@@ -0,0 +1,37 @@
+/**
+ * Bacon resources.
+ *
+ * @author Karsten Silkenbäumer [kassi@users.noreply.github.com]
+ * @copyright Karsten Silkenbäumer 2019
+ * @license Apache-2.0
+ */
+
+/**
+ * Bacon definitions.
+ */
+
+export const BACON_ALPHABET_REDUCED = "ABCDEFGHIKLMNOPQRSTUWXYZ";
+export const BACON_ALPHABET_COMPLETE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+export const BACON_TRANSLATION_01 = "0/1";
+export const BACON_TRANSLATION_AB = "A/B";
+export const BACON_TRANSLATION_CASE = "Case";
+export const BACON_TRANSLATION_AMNZ = "A-M/N-Z first letter";
+export const BACON_TRANSLATIONS = [
+    BACON_TRANSLATION_01,
+    BACON_TRANSLATION_AB,
+    BACON_TRANSLATION_CASE,
+    BACON_TRANSLATION_AMNZ,
+];
+export const BACON_CLEARER_MAP = {
+    [BACON_TRANSLATIONS[0]]: /[^01]/g,
+    [BACON_TRANSLATIONS[1]]: /[^ABab]/g,
+    [BACON_TRANSLATIONS[2]]: /[^A-Za-z]/g,
+};
+export const BACON_NORMALIZE_MAP = {
+    [BACON_TRANSLATIONS[1]]: {
+        "A": "0",
+        "B": "1",
+        "a": "0",
+        "b": "1"
+    },
+};

+ 108 - 0
src/core/operations/BaconCipherDecode.mjs

@@ -0,0 +1,108 @@
+/**
+ * BaconCipher operation.
+ *
+* @author kassi [kassi@users.noreply.github.com]
+* @copyright Karsten Silkenbäumer 2019
+* @license Apache-2.0
+*/
+
+import Operation from "../Operation";
+import {
+    BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE,
+    BACON_TRANSLATION_CASE, BACON_TRANSLATION_AMNZ, BACON_TRANSLATIONS, BACON_CLEARER_MAP, BACON_NORMALIZE_MAP
+} from "../lib/Bacon";
+
+/**
+* BaconCipherDecode operation
+*/
+class BaconCipherDecode extends Operation {
+    /**
+    * BaconCipherDecode constructor
+    */
+    constructor() {
+        super();
+
+        this.name = "Bacon Cipher Decode";
+        this.module = "Default";
+        this.description = "Bacon's cipher or the Baconian cipher is a method of steganography(a method of hiding a secret message as opposed to just a cipher) devised by Francis Bacon in 1605.[1][2][3] A message is concealed in the presentation of text, rather than its content.";
+        this.infoURL = "https://en.wikipedia.org/wiki/Bacon%27s_cipher";
+        this.inputType = "string";
+        this.outputType = "string";
+        this.args = [
+            {
+                "name": "Alphabet",
+                "type": "option",
+                "value": [BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE]
+            },
+            {
+                "name": "Translation",
+                "type": "option",
+                "value": BACON_TRANSLATIONS
+            },
+            {
+                "name": "Invert Translation",
+                "type": "boolean",
+                "value": false
+            }
+        ];
+    }
+
+    /**
+    * @param {String} input
+    * @param {Object[]} args
+    * @returns {String}
+    */
+    run(input, args) {
+        const [alphabet, translation, invert] = args;
+        // split text into groups of 5 characters
+
+        // remove invalid characters
+        input = input.replace(BACON_CLEARER_MAP[translation], "");
+        // normalize to unique alphabet
+        if (BACON_NORMALIZE_MAP[translation] !== undefined) {
+            input = input.replace(/./g, function (c) {
+                return BACON_NORMALIZE_MAP[translation][c];
+            });
+        } else if (translation === BACON_TRANSLATION_CASE) {
+            const codeA = "A".charCodeAt(0);
+            const codeZ = "Z".charCodeAt(0);
+            input = input.replace(/./g, function (c) {
+                const code = c.charCodeAt(0);
+                if (code >= codeA && code <= codeZ) {
+                    return "1";
+                } else {
+                    return "0";
+                }
+            });
+        } else if (translation === BACON_TRANSLATION_AMNZ) {
+            const words = input.split(" ");
+            const letters = words.map(function (e) {
+                const code = e[0].toUpperCase().charCodeAt(0);
+                return code >= "N".charCodeAt(0) ? "1" : "0";
+            });
+            input = letters.join("");
+        }
+
+        if (invert) {
+            input = input.replace(/./g, function (c) {
+                return {
+                    "0": "1",
+                    "1": "0"
+                }[c];
+            });
+        }
+
+        // group into 5
+        const inputArray = input.match(/(.{5})/g) || [];
+
+        let output = "";
+        for (let index = 0; index < inputArray.length; index++) {
+            const code = inputArray[index];
+            const number = parseInt(code, 2);
+            output += number < alphabet.length ? alphabet[number] : "?";
+        }
+        return output;
+    }
+}
+
+export default BaconCipherDecode;

+ 1 - 0
tests/operations/index.mjs

@@ -26,6 +26,7 @@ global.ENVIRONMENT_IS_WEB = function() {
 import TestRegister from "./TestRegister";
 import "./tests/BCD";
 import "./tests/BSON";
+import "./tests/BaconCipher";
 import "./tests/Base58";
 import "./tests/Base64";
 import "./tests/Base62";

+ 246 - 0
tests/operations/tests/BaconCipher.mjs

@@ -0,0 +1,246 @@
+/**
+ * BaconCipher tests.
+ *
+ * @author Karsten Silkenbäumer [kassi@users.noreply.github.com]
+ * @copyright Karsten Silkenbäumer 2019
+ * @license Apache-2.0
+ */
+import TestRegister from "../TestRegister";
+import { BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE, BACON_TRANSLATIONS } from "../../../src/core/lib/Bacon";
+
+const alphabets = [BACON_ALPHABET_REDUCED, BACON_ALPHABET_COMPLETE];
+const translations = BACON_TRANSLATIONS;
+
+TestRegister.addTests([
+    {
+        name: "Bacon Decode: no input",
+        input: "",
+        expectedOutput: "",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[0], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet 0/1",
+        input: "00011 00100 00010 01101 00011 01000 01100 00110 00001 00000 00010 01101 01100 10100 01101 10000 01001 10001",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[0], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet 0/1 inverse",
+        input: "11100 11011 11101 10010 11100 10111 10011 11001 11110 11111 11101 10010 10011 01011 10010 01111 10110 01110",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[0], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet A/B lower case",
+        input: "aaabb aabaa aaaba abbab aaabb abaaa abbaa aabba aaaab aaaaa aaaba abbab abbaa babaa abbab baaaa abaab baaab",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[1], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet A/B lower case inverse",
+        input: "bbbaa bbabb bbbab baaba bbbaa babbb baabb bbaab bbbba bbbbb bbbab baaba baabb ababb baaba abbbb babba abbba",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[1], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet A/B upper case",
+        input: "AAABB AABAA AAABA ABBAB AAABB ABAAA ABBAA AABBA AAAAB AAAAA AAABA ABBAB ABBAA BABAA ABBAB BAAAA ABAAB BAAAB",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[1], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet A/B upper case inverse",
+        input: "BBBAA BBABB BBBAB BAABA BBBAA BABBB BAABB BBAAB BBBBA BBBBB BBBAB BAABA BAABB ABABB BAABA ABBBB BABBA ABBBA",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[1], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet case code",
+        input: "thiS IsaN exampLe oF ThE bacON cIpher WIth upPPercasE letters tRanSLaTiNG to OnEs anD LoWErcase To zERoes. KS",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[2], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet case code inverse",
+        input: "THIs iS An EXAMPlE Of tHe BACon CiPHER wiTH UPppERCASe LETTERS TrANslAtIng TO oNeS ANd lOweRCASE tO ZerOES. ks",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[2], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet case code",
+        input: "A little example of the Bacon Cipher to be decoded. It is a working example and shorter than my others, but it anyways works tremendously. And just that's important, correct?",
+        expectedOutput: "DECODE",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[3], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: reduced alphabet case code inverse",
+        input: "Well, there's now another example which will be not only strange to read but sound weird for everyone not knowing what the thing is about. Nevertheless, works great out of the box.",
+        expectedOutput: "DECODE",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[0], translations[3], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet 0/1",
+        input: "00011 00100 00010 01110 00011 01000 01101 00110 00001 00000 00010 01110 01101 10110 01110 10001 01010 10010",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[0], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet 0/1 inverse",
+        input: "11100 11011 11101 10001 11100 10111 10010 11001 11110 11111 11101 10001 10010 01001 10001 01110 10101 01101",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[0], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet A/B lower case",
+        input: "aaabb aabaa aaaba abbba aaabb abaaa abbab aabba aaaab aaaaa aaaba abbba abbab babba abbba baaab ababa baaba",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[1], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet A/B lower case inverse",
+        input: "bbbaa bbabb bbbab baaab bbbaa babbb baaba bbaab bbbba bbbbb bbbab baaab baaba abaab baaab abbba babab abbab",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[1], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet A/B upper case",
+        input: "AAABB AABAA AAABA ABBBA AAABB ABAAA ABBAB AABBA AAAAB AAAAA AAABA ABBBA ABBAB BABBA ABBBA BAAAB ABABA BAABA",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[1], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet A/B upper case inverse",
+        input: "BBBAA BBABB BBBAB BAAAB BBBAA BABBB BAABA BBAAB BBBBA BBBBB BBBAB BAAAB BAABA ABAAB BAAAB ABBBA BABAB ABBAB",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[1], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet case code",
+        input: "thiS IsaN exampLe oF THe bacON cIpher WItH upPPercasE letters tRanSLAtiNG tO OnES anD LOwErcaSe To ZeRoeS. kS",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[2], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet case code inverse",
+        input: "THIs iSAn EXAMPlE Of thE BACon CiPHER wiTh UPppERCASe LETTERS TrANslaTIng To zEroES and LoWERcAsE tO oNEs. Ks",
+        expectedOutput: "DECODINGBACONWORKS",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[2], true]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet case code",
+        input: "A little example of the Bacon Cipher to be decoded. It is a working example and shorter than the first, but it anyways works tremendously. And just that's important, correct?",
+        expectedOutput: "DECODE",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[3], false]
+            }
+        ],
+    },
+    {
+        name: "Bacon Decode: complete alphabet case code inverse",
+        input: "Well, there's now another example which will be not only strange to read but sound weird for everyone knowing nothing what the thing is about. Nevertheless, works great out of the box.",
+        expectedOutput: "DECODE",
+        recipeConfig: [
+            {
+                op: "Bacon Cipher Decode",
+                args: [alphabets[1], translations[3], true]
+            }
+        ],
+    },
+]);