diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c96618..86ba6848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master). +### [9.13.0] - 2020-02-13 +- 'Rail Fence Cipher Encode' and 'Rail Fence Cipher Decode' operations added [@Flavsditz] | [#948] + ### [9.12.0] - 2019-12-20 - 'Normalise Unicode' operation added [@matthieuxyz] | [#912] @@ -200,6 +203,7 @@ All major and minor version changes will be documented in this file. Details of +[9.13.0]: https://github.com/gchq/CyberChef/releases/tag/v9.13.0 [9.12.0]: https://github.com/gchq/CyberChef/releases/tag/v9.12.0 [9.11.0]: https://github.com/gchq/CyberChef/releases/tag/v9.11.0 [9.10.0]: https://github.com/gchq/CyberChef/releases/tag/v9.10.0 @@ -286,6 +290,7 @@ All major and minor version changes will be documented in this file. Details of [@VirtualColossus]: https://github.com/VirtualColossus [@cbeuw]: https://github.com/cbeuw [@matthieuxyz]: https://github.com/matthieuxyz +[@Flavsditz]: https://github.com/Flavsditz [#95]: https://github.com/gchq/CyberChef/pull/299 [#173]: https://github.com/gchq/CyberChef/pull/173 @@ -350,3 +355,4 @@ All major and minor version changes will be documented in this file. Details of [#653]: https://github.com/gchq/CyberChef/pull/653 [#865]: https://github.com/gchq/CyberChef/pull/865 [#912]: https://github.com/gchq/CyberChef/pull/912 +[#948]: https://github.com/gchq/CyberChef/pull/948 diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 53ca796d..0d68efe8 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -95,6 +95,8 @@ "Affine Cipher Decode", "A1Z26 Cipher Encode", "A1Z26 Cipher Decode", + "Rail Fence Cipher Encode", + "Rail Fence Cipher Decode", "Atbash Cipher", "Substitute", "Derive PBKDF2 key", diff --git a/src/core/operations/RailFenceCipherDecode.mjs b/src/core/operations/RailFenceCipherDecode.mjs new file mode 100644 index 00000000..d98742b8 --- /dev/null +++ b/src/core/operations/RailFenceCipherDecode.mjs @@ -0,0 +1,88 @@ +/** + * @author Flavio Diez [flaviofdiez+cyberchef@gmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Rail Fence Cipher Decode operation + */ +class RailFenceCipherDecode extends Operation { + + /** + * RailFenceCipherDecode constructor + */ + constructor() { + super(); + + this.name = "Rail Fence Cipher Decode"; + this.module = "Ciphers"; + this.description = "Decodes Strings that were created using the Rail fence Cipher provided a key and an offset"; + this.infoURL = "https://wikipedia.org/wiki/Rail_fence_cipher"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Key", + type: "number", + value: 2 + }, + { + name: "Offset", + type: "number", + value: 0 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [key, offset] = args; + + let cipher = input; + + if (key < 2) { + throw new OperationError("Key has to be bigger than 2"); + } else if (key > cipher.length) { + throw new OperationError("Key should be smaller than the cipher's length"); + } + + if (offset < 0) { + throw new OperationError("Offset has to be a positive integer"); + } + + const cycle = (key - 1) * 2; + + const rest = cipher.length % key; + + if (rest !== 0) { + cipher = cipher + (" ".repeat(key - rest)); + } + + const plaintext = new Array(cipher.length); + + let j = 0; + let x, y; + + for (y = 0; y < key; y++) { + for (x = 0; x < cipher.length; x++) { + if ((y + x + offset) % cycle === 0 || (y - x - offset) % cycle === 0) { + plaintext[x] = cipher[j++]; + } + } + } + + return plaintext.join("").trim(); + } + +} + + +export default RailFenceCipherDecode; diff --git a/src/core/operations/RailFenceCipherEncode.mjs b/src/core/operations/RailFenceCipherEncode.mjs new file mode 100644 index 00000000..03651f85 --- /dev/null +++ b/src/core/operations/RailFenceCipherEncode.mjs @@ -0,0 +1,74 @@ +/** + * @author Flavio Diez [flaviofdiez+cyberchef@gmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Rail Fence Cipher Encode operation + */ +class RailFenceCipherEncode extends Operation { + + /** + * RailFenceCipherEncode constructor + */ + constructor() { + super(); + + this.name = "Rail Fence Cipher Encode"; + this.module = "Ciphers"; + this.description = "Encodes Strings using the Rail fence Cipher provided a key and an offset"; + this.infoURL = "https://wikipedia.org/wiki/Rail_fence_cipher"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Key", + type: "number", + value: 2 + }, + { + name: "Offset", + type: "number", + value: 0 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [key, offset] = args; + + const plaintext = input; + if (key < 2) { + throw new OperationError("Key has to be bigger than 2"); + } else if (key > plaintext.length) { + throw new OperationError("Key should be smaller than the plain text's length"); + } + + if (offset < 0) { + throw new OperationError("Offset has to be a positive integer"); + } + + const cycle = (key - 1) * 2; + const rows = new Array(key).fill(""); + + for (let pos = 0; pos < plaintext.length; pos++) { + const rowIdx = key - 1 - Math.abs(cycle / 2 - (pos + offset) % cycle); + + rows[rowIdx] += plaintext[pos]; + } + + return rows.join("").trim(); + } + +} + +export default RailFenceCipherEncode; diff --git a/tests/operations/tests/Ciphers.mjs b/tests/operations/tests/Ciphers.mjs index 62cbaa54..47453cf7 100644 --- a/tests/operations/tests/Ciphers.mjs +++ b/tests/operations/tests/Ciphers.mjs @@ -418,4 +418,114 @@ TestRegister.addTests([ } ], }, + { + name: "Rail Fence Cipher Decode: normal", + input: "Cytgah sTEAto rtn rsligcdsrporpyi H r fWiigo ovn oe", + expectedOutput: "Cryptography is THE Art of Writing or solving codes", + recipeConfig: [ + { + "op": "Rail Fence Cipher Decode", + "args": [2, 0] + } + ], + }, + { + name: "Rail Fence Cipher Decode: key has to be bigger than 2", + input: "Cytgah sTEAto rtn rsligcdsrporpyi H r fWiigo ovn oe", + expectedOutput: "Key has to be bigger than 2", + recipeConfig: [ + { + "op": "Rail Fence Cipher Decode", + "args": [1, 0] + } + ], + }, + { + name: "Rail Fence Cipher Decode: key has to be smaller than input's length", + input: "shortinput", + expectedOutput: "Key should be smaller than the cipher's length", + recipeConfig: [ + { + "op": "Rail Fence Cipher Decode", + "args": [22, 0] + } + ], + }, + { + name: "Rail Fence Cipher Decode: offset should be positive", + input: "shortinput", + expectedOutput: "Offset has to be a positive integer", + recipeConfig: [ + { + "op": "Rail Fence Cipher Decode", + "args": [2, -1] + } + ], + }, + { + name: "Rail Fence Cipher Decode: Normal with Offset non-null", + input: "51746026813793592840", + expectedOutput: "12345678901234567890", + recipeConfig: [ + { + "op": "Rail Fence Cipher Decode", + "args": [4, 2] + } + ], + }, + { + name: "Rail Fence Cipher Encode: normal", + input: "Cryptography is THE Art of Writing or solving codes", + expectedOutput: "Cytgah sTEAto rtn rsligcdsrporpyi H r fWiigo ovn oe", + recipeConfig: [ + { + "op": "Rail Fence Cipher Encode", + "args": [2, 0] + } + ], + }, + { + name: "Rail Fence Cipher Encode: key has to be bigger than 2", + input: "Cryptography is THE Art of Writing or solving codes", + expectedOutput: "Key has to be bigger than 2", + recipeConfig: [ + { + "op": "Rail Fence Cipher Encode", + "args": [1, 0] + } + ], + }, + { + name: "Rail Fence Cipher Encode: key has to be smaller than input's length", + input: "shortinput", + expectedOutput: "Key should be smaller than the plain text's length", + recipeConfig: [ + { + "op": "Rail Fence Cipher Encode", + "args": [22, 0] + } + ], + }, + { + name: "Rail Fence Cipher Encode: offset should be positive", + input: "shortinput", + expectedOutput: "Offset has to be a positive integer", + recipeConfig: [ + { + "op": "Rail Fence Cipher Encode", + "args": [2, -1] + } + ], + }, + { + name: "Rail Fence Cipher Encode: Normal with Offset non-null", + input: "12345678901234567890", + expectedOutput: "51746026813793592840", + recipeConfig: [ + { + "op": "Rail Fence Cipher Encode", + "args": [4, 2] + } + ], + }, ]);