|
@@ -0,0 +1,105 @@
|
|
|
|
+/**
|
|
|
|
+ * @author Daniel Temkin [http://danieltemkin.com]
|
|
|
|
+ * @author Thomas Leplus [https://www.leplus.org]
|
|
|
|
+ * @copyright Crown Copyright 2021
|
|
|
|
+ * @license Apache-2.0
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+import Operation from "../Operation.mjs";
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * ROT8000 operation.
|
|
|
|
+ */
|
|
|
|
+class ROT8000 extends Operation {
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * ROT8000 constructor
|
|
|
|
+ */
|
|
|
|
+ constructor() {
|
|
|
|
+ super();
|
|
|
|
+ this.name = "ROT8000";
|
|
|
|
+ this.module = "Default";
|
|
|
|
+ this.description = "The simple Caesar-cypher encryption that replaces each Unicode character with the one 0x8000 places forward or back along the alphabet.";
|
|
|
|
+ this.infoURL = "https://github.com/rottytooth/rot8000";
|
|
|
|
+ this.inputType = "string";
|
|
|
|
+ this.outputType = "string";
|
|
|
|
+ this.args = [];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @param {byteArray} input
|
|
|
|
+ * @param {Object[]} args
|
|
|
|
+ * @returns {byteArray}
|
|
|
|
+ */
|
|
|
|
+ run(input, args) {
|
|
|
|
+ // Inspired from https://github.com/rottytooth/rot8000/blob/main/rot8000.js
|
|
|
|
+ // these come from the valid-code-point-transitions.json file generated from the c# proj
|
|
|
|
+ // this is done bc: 1) don't trust JS's understanging of surrogate pairs and 2) consistency with original rot8000
|
|
|
|
+ const validCodePoints = JSON.parse('{"33":true,"127":false,"161":true,"5760":false,"5761":true,"8192":false,"8203":true,"8232":false,"8234":true,"8239":false,"8240":true,"8287":false,"8288":true,"12288":false,"12289":true,"55296":false,"57344":true}');
|
|
|
|
+ const bmpSize = 0x10000;
|
|
|
|
+ const rotList = {}; // the mapping of char to rotated char
|
|
|
|
+ const hiddenBlocks = [];
|
|
|
|
+ let startBlock = 0;
|
|
|
|
+ for (const key in validCodePoints) {
|
|
|
|
+ if (Object.prototype.hasOwnProperty.call(validCodePoints, key)) {
|
|
|
|
+ if (validCodePoints[key] === true)
|
|
|
|
+ hiddenBlocks.push({ start: startBlock, end: parseInt(key, 10) - 1 });
|
|
|
|
+ else
|
|
|
|
+ startBlock = parseInt(key, 10);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ const validIntList = []; // list of all valid chars
|
|
|
|
+ let currValid = false;
|
|
|
|
+ for (let i = 0; i < bmpSize; i++) {
|
|
|
|
+ if (validCodePoints[i] !== undefined) {
|
|
|
|
+ currValid = validCodePoints[i];
|
|
|
|
+ }
|
|
|
|
+ if (currValid) validIntList.push(i);
|
|
|
|
+ }
|
|
|
|
+ const rotateNum = Object.keys(validIntList).length / 2;
|
|
|
|
+ // go through every valid char and find its match
|
|
|
|
+ for (let i = 0; i < validIntList.length; i++) {
|
|
|
|
+ rotList[String.fromCharCode(validIntList[i])] =
|
|
|
|
+ String.fromCharCode(validIntList[(i + rotateNum) % (rotateNum * 2)]);
|
|
|
|
+ }
|
|
|
|
+ let output = "";
|
|
|
|
+ for (let count = 0; count < input.length; count++) {
|
|
|
|
+ // if it is not in the mappings list, just add it directly (no rotation)
|
|
|
|
+ if (rotList[input[count]] === undefined) {
|
|
|
|
+ output += input[count];
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ // otherwise, rotate it and add it to the string
|
|
|
|
+ output += rotList[input[count]];
|
|
|
|
+ }
|
|
|
|
+ return output;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Highlight ROT8000
|
|
|
|
+ *
|
|
|
|
+ * @param {Object[]} pos
|
|
|
|
+ * @param {number} pos[].start
|
|
|
|
+ * @param {number} pos[].end
|
|
|
|
+ * @param {Object[]} args
|
|
|
|
+ * @returns {Object[]} pos
|
|
|
|
+ */
|
|
|
|
+ highlight(pos, args) {
|
|
|
|
+ return pos;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Highlight ROT8000 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 ROT8000;
|