Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Bacon cipher operation #500

Merged
merged 6 commits into from
Sep 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
"Vigenère Decode",
"To Morse Code",
"From Morse Code",
"Bacon Cipher Encode",
"Bacon Cipher Decode",
"Bifid Cipher Encode",
"Bifid Cipher Decode",
"Affine Cipher Encode",
Expand Down
66 changes: 66 additions & 0 deletions src/core/lib/Bacon.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Bacon resources.
*
* @author Karsten Silkenbäumer [github.com/kassi]
* @copyright Karsten Silkenbäumer 2019
* @license Apache-2.0
*/

/**
* Bacon definitions.
*/
export const BACON_ALPHABETS = {
"Standard (I=J and U=V)": {
alphabet: "ABCDEFGHIKLMNOPQRSTUWXYZ",
codes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23]
},
"Complete": {
alphabet: "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_TRANSLATIONS_FOR_ENCODING = [
BACON_TRANSLATION_01,
BACON_TRANSLATION_AB
];
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"
},
};

/**
* Swaps zeros to ones and ones to zeros.
*
* @param {string} data
* @returns {string}
*
* @example
* // returns "11001 01010"
* swapZeroAndOne("00110 10101");
*/
export function swapZeroAndOne(string) {
return string.replace(/[01]/g, function (c) {
return {
"0": "1",
"1": "0"
}[c];
});
}
109 changes: 109 additions & 0 deletions src/core/operations/BaconCipherDecode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* BaconCipher operation.
*
* @author Karsten Silkenbäumer [github.com/kassi]
* @copyright Karsten Silkenbäumer 2019
* @license Apache-2.0
*/

import Operation from "../Operation";
import {
BACON_ALPHABETS,
BACON_TRANSLATION_CASE, BACON_TRANSLATION_AMNZ, BACON_TRANSLATIONS, BACON_CLEARER_MAP, BACON_NORMALIZE_MAP,
swapZeroAndOne
} 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": Object.keys(BACON_ALPHABETS)
},
{
"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;
const alphabetObject = BACON_ALPHABETS[alphabet];

// 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(/\s+/);
const letters = words.map(function (e) {
if (e) {
const code = e[0].toUpperCase().charCodeAt(0);
return code >= "N".charCodeAt(0) ? "1" : "0";
} else {
return "";
}
});
input = letters.join("");
}

if (invert) {
input = swapZeroAndOne(input);
}

// 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 < alphabetObject.alphabet.length ? alphabetObject.alphabet[number] : "?";
}
return output;
}
}

export default BaconCipherDecode;
103 changes: 103 additions & 0 deletions src/core/operations/BaconCipherEncode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* BaconCipher operation.
*
* @author Karsten Silkenbäumer [github.com/kassi]
* @copyright Karsten Silkenbäumer 2019
* @license Apache-2.0
*/

import Operation from "../Operation";
import {
BACON_ALPHABETS,
BACON_TRANSLATIONS_FOR_ENCODING, BACON_TRANSLATION_AB,
swapZeroAndOne
} from "../lib/Bacon";

/**
* BaconCipherEncode operation
*/
class BaconCipherEncode extends Operation {
/**
* BaconCipherEncode constructor
*/
constructor() {
super();

this.name = "Bacon Cipher Encode";
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": Object.keys(BACON_ALPHABETS)
},
{
"name": "Translation",
"type": "option",
"value": BACON_TRANSLATIONS_FOR_ENCODING
},
{
"name": "Keep extra characters",
"type": "boolean",
"value": false
},
{
"name": "Invert Translation",
"type": "boolean",
"value": false
}
];
}

/**
* @param {String} input
* @param {Object[]} args
* @returns {String}
*/
run(input, args) {
const [alphabet, translation, keep, invert] = args;

const alphabetObject = BACON_ALPHABETS[alphabet];
const charCodeA = "A".charCodeAt(0);
const charCodeZ = "Z".charCodeAt(0);

let output = input.replace(/./g, function (c) {
const charCode = c.toUpperCase().charCodeAt(0);
if (charCode >= charCodeA && charCode <= charCodeZ) {
let code = charCode - charCodeA;
if (alphabetObject.codes !== undefined) {
code = alphabetObject.codes[code];
}
const bacon = ("00000" + code.toString(2)).substr(-5, 5);
return bacon;
} else {
return c;
}
});

if (invert) {
output = swapZeroAndOne(output);
}
if (!keep) {
output = output.replace(/[^01]/g, "");
const outputArray = output.match(/(.{5})/g) || [];
output = outputArray.join(" ");
}
if (translation === BACON_TRANSLATION_AB) {
output = output.replace(/[01]/g, function (c) {
return {
"0": "A",
"1": "B"
}[c];
});
}

return output;
}
}

export default BaconCipherEncode;
1 change: 1 addition & 0 deletions tests/operations/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading