Converted Affine/Atbash operations to mjs & added tests
parent
6987e6b1b9
commit
f87666f659
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Cipher functions.
|
||||||
|
*
|
||||||
|
* @author Matt C [matt@artemisbot.uk]
|
||||||
|
* @copyright Crown Copyright 2018
|
||||||
|
* @license Apache-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Affine Cipher Encode operation.
|
||||||
|
*
|
||||||
|
* @author Matt C [matt@artemisbot.uk]
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function affineEncode(input, args) {
|
||||||
|
const alphabet = "abcdefghijklmnopqrstuvwxyz",
|
||||||
|
a = args[0],
|
||||||
|
b = args[1];
|
||||||
|
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.";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
if (alphabet.indexOf(input[i]) >= 0) {
|
||||||
|
// Uses the affine function ax+b % m = y (where m is length of the alphabet)
|
||||||
|
output += alphabet[((a * alphabet.indexOf(input[i])) + b) % 26];
|
||||||
|
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
|
||||||
|
// Same as above, accounting for uppercase
|
||||||
|
output += alphabet[((a * alphabet.indexOf(input[i].toLowerCase())) + b) % 26].toUpperCase();
|
||||||
|
} else {
|
||||||
|
// Non-alphabetic characters
|
||||||
|
output += input[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
* @author Matt C [matt@artemisbot.uk]
|
||||||
|
* @copyright Crown Copyright 2018
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import Utils from "../Utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Affine Cipher Decode operation
|
||||||
|
*/
|
||||||
|
class AffineCipherDecode extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AffineCipherDecode constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Affine Cipher Decode";
|
||||||
|
this.module = "Ciphers";
|
||||||
|
this.description = "The Affine cipher is a type of monoalphabetic substitution cipher. To decrypt, each letter in an alphabet is mapped to its numeric equivalent, decrypted by a mathematical function, and converted back to a letter.";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "number",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "b",
|
||||||
|
"type": "number",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const alphabet = "abcdefghijklmnopqrstuvwxyz",
|
||||||
|
a = args[0],
|
||||||
|
b = args[1],
|
||||||
|
aModInv = Utils.modInv(a, 26); // Calculates modular inverse of a
|
||||||
|
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.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils.gcd(a, 26) !== 1) {
|
||||||
|
return "The value of a must be coprime to 26.";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
if (alphabet.indexOf(input[i]) >= 0) {
|
||||||
|
// Uses the affine decode function (y-b * A') % m = x (where m is length of the alphabet and A' is modular inverse)
|
||||||
|
output += alphabet[Utils.mod((alphabet.indexOf(input[i]) - b) * aModInv, 26)];
|
||||||
|
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
|
||||||
|
// Same as above, accounting for uppercase
|
||||||
|
output += alphabet[Utils.mod((alphabet.indexOf(input[i].toLowerCase()) - b) * aModInv, 26)].toUpperCase();
|
||||||
|
} else {
|
||||||
|
// Non-alphabetic characters
|
||||||
|
output += input[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight Affine Cipher Decode
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlight(pos, args) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight Affine Cipher Decode in reverse
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlightReverse(pos, args) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AffineCipherDecode;
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* @author Matt C [matt@artemisbot.uk]
|
||||||
|
* @copyright Crown Copyright 2018
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import { affineEncode } from "../lib/Ciphers";
|
||||||
|
/**
|
||||||
|
* Affine Cipher Encode operation
|
||||||
|
*/
|
||||||
|
class AffineCipherEncode extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AffineCipherEncode constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Affine Cipher Encode";
|
||||||
|
this.module = "Ciphers";
|
||||||
|
this.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, <code>(ax + b) % 26</code>, and converted back to a letter.";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
"name": "a",
|
||||||
|
"type": "number",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "b",
|
||||||
|
"type": "number",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
return affineEncode(input, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight Affine Cipher Encode
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlight(pos, args) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight Affine Cipher Encode in reverse
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlightReverse(pos, args) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AffineCipherEncode;
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* @author Matt C [matt@artemisbot.uk]
|
||||||
|
* @copyright Crown Copyright 2016
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import { affineEncode } from "../lib/Ciphers";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atbash Cipher operation
|
||||||
|
*/
|
||||||
|
class AtbashCipher extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AtbashCipher constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Atbash Cipher";
|
||||||
|
this.module = "Ciphers";
|
||||||
|
this.description = "Atbash is a mono-alphabetic substitution cipher originally used to encode the Hebrew alphabet. It has been modified here for use with the Latin alphabet.";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
return affineEncode(input, [25, 25]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight Atbash Cipher
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlight(pos, args) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight Atbash Cipher in reverse
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlightReverse(pos, args) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AtbashCipher;
|
|
@ -30,8 +30,9 @@ import "./tests/operations/Base64";
|
||||||
// import "./tests/operations/BitwiseOp.js";
|
// import "./tests/operations/BitwiseOp.js";
|
||||||
// import "./tests/operations/BSON.js";
|
// import "./tests/operations/BSON.js";
|
||||||
// import "./tests/operations/ByteRepr.js";
|
// import "./tests/operations/ByteRepr.js";
|
||||||
|
import "./tests/operations/CartesianProduct";
|
||||||
// import "./tests/operations/CharEnc.js";
|
// import "./tests/operations/CharEnc.js";
|
||||||
// import "./tests/operations/Cipher.js";
|
import "./tests/operations/Ciphers";
|
||||||
// import "./tests/operations/Code.js";
|
// import "./tests/operations/Code.js";
|
||||||
// import "./tests/operations/Compress.js";
|
// import "./tests/operations/Compress.js";
|
||||||
// import "./tests/operations/DateTime.js";
|
// import "./tests/operations/DateTime.js";
|
||||||
|
@ -44,16 +45,15 @@ import "./tests/operations/Base64";
|
||||||
// import "./tests/operations/PHP.js";
|
// import "./tests/operations/PHP.js";
|
||||||
// import "./tests/operations/NetBIOS.js";
|
// import "./tests/operations/NetBIOS.js";
|
||||||
// import "./tests/operations/OTP.js";
|
// import "./tests/operations/OTP.js";
|
||||||
|
import "./tests/operations/PowerSet";
|
||||||
// import "./tests/operations/Regex.js";
|
// import "./tests/operations/Regex.js";
|
||||||
import "./tests/operations/Rotate.mjs";
|
import "./tests/operations/Rotate";
|
||||||
// import "./tests/operations/StrUtils.js";
|
// import "./tests/operations/StrUtils.js";
|
||||||
// import "./tests/operations/SeqUtils.js";
|
// import "./tests/operations/SeqUtils.js";
|
||||||
import "./tests/operations/SetUnion";
|
|
||||||
import "./tests/operations/SetIntersection";
|
|
||||||
import "./tests/operations/SetDifference";
|
import "./tests/operations/SetDifference";
|
||||||
|
import "./tests/operations/SetIntersection";
|
||||||
|
import "./tests/operations/SetUnion";
|
||||||
import "./tests/operations/SymmetricDifference";
|
import "./tests/operations/SymmetricDifference";
|
||||||
import "./tests/operations/CartesianProduct";
|
|
||||||
import "./tests/operations/PowerSet";
|
|
||||||
|
|
||||||
let allTestsPassing = true;
|
let allTestsPassing = true;
|
||||||
const testStatusCounts = {
|
const testStatusCounts = {
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
/**
|
||||||
|
* Cipher tests.
|
||||||
|
*
|
||||||
|
* @author Matt C [matt@artemisbot.uk]
|
||||||
|
* @author n1474335 [n1474335@gmail.com]
|
||||||
|
*
|
||||||
|
* @copyright Crown Copyright 2018
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import TestRegister from "../../TestRegister";
|
||||||
|
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "Affine Encode: no input",
|
||||||
|
input: "",
|
||||||
|
expectedOutput: "",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Affine Cipher Encode",
|
||||||
|
args: [1, 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Affine Encode: no effect",
|
||||||
|
input: "some keys are shaped as locks. index[me]",
|
||||||
|
expectedOutput: "some keys are shaped as locks. index[me]",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Affine Cipher Encode",
|
||||||
|
args: [1, 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Affine Encode: normal",
|
||||||
|
input: "some keys are shaped as locks. index[me]",
|
||||||
|
expectedOutput: "vhnl tldv xyl vcxelo xv qhrtv. zkolg[nl]",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Affine Cipher Encode",
|
||||||
|
args: [23, 23]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Affine Decode: no input",
|
||||||
|
input: "",
|
||||||
|
expectedOutput: "",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Affine Cipher Decode",
|
||||||
|
args: [1, 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Affine Decode: no effect",
|
||||||
|
input: "vhnl tldv xyl vcxelo xv qhrtv. zkolg[nl]",
|
||||||
|
expectedOutput: "vhnl tldv xyl vcxelo xv qhrtv. zkolg[nl]",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Affine Cipher Decode",
|
||||||
|
args: [1, 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Affine Decode: normal",
|
||||||
|
input: "vhnl tldv xyl vcxelo xv qhrtv. zkolg[nl]",
|
||||||
|
expectedOutput: "some keys are shaped as locks. index[me]",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Affine Cipher Decode",
|
||||||
|
args: [23, 23]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Atbash: no input",
|
||||||
|
input: "",
|
||||||
|
expectedOutput: "",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Atbash Cipher",
|
||||||
|
args: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Atbash: normal",
|
||||||
|
input: "old slow slim horn",
|
||||||
|
expectedOutput: "low hold horn slim",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
op: "Atbash Cipher",
|
||||||
|
args: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
Loading…
Reference in New Issue