Bläddra i källkod

Tidied run function, added some tests

VirtualColossus 5 år sedan
förälder
incheckning
55eae9910f
3 ändrade filer med 218 tillägg och 114 borttagningar
  1. 123 114
      src/core/operations/Lorenz.mjs
  2. 1 0
      tests/operations/index.mjs
  3. 94 0
      tests/operations/tests/Lorenz.mjs

+ 123 - 114
src/core/operations/Lorenz.mjs

@@ -254,8 +254,7 @@ class Lorenz extends Operation {
             x2 = args[15],
             x3 = args[16],
             x4 = args[17],
-            x5 = args[18],
-            figShifted = false;
+            x5 = args[18];
 
         this.reverseTable();
 
@@ -308,68 +307,8 @@ class Lorenz extends Operation {
         const psiSettings = chosenSetting.S; // Pin settings for Psi links (S)
         const muSettings = chosenSetting.M; // Pin settings for Motor links (M)
 
-        let ita2 = "";
-        if (mode === "Send") {
-
-            // Convert input text to ITA2 (including figure/letter shifts)
-            ita2 = Array.prototype.map.call(input, function(character) {
-                const letter = character.toUpperCase();
-
-                if (intype === "Plaintext") {
-                    if (validChars.indexOf(letter) === -1) throw new OperationError("Invalid Plaintext character : "+letter);
-
-                    if (!figShifted && figShiftedChars.indexOf(letter) !== -1) {
-                        // in letters mode and next char needs to be figure shifted
-                        figShifted = true;
-                        return "55" + figShiftArr[letter];
-                    } else if (figShifted) {
-                        // in figures mode and next char needs to be letter shifted
-                        if (letter==="\n") return "34";
-                        if (letter==="\r") return "4";
-                        if (figShiftedChars.indexOf(letter) === -1) {
-                            figShifted = false;
-                            return "88" + letter;
-                        } else {
-                            return figShiftArr[letter];
-                        }
-
-                    } else {
-                        if (letter==="\n") return "34";
-                        if (letter==="\r") return "4";
-                        return letter;
-                    }
-
-                } else {
-
-                    if (validITA2.indexOf(letter) === -1) {
-                        let errltr = letter;
-                        if (errltr==="\n") errltr = "Carriage Return";
-                        if (errltr===" ") errltr = "Space";
-                        throw new OperationError("Invalid ITA2 character : "+errltr);
-                    }
-                    return letter;
-
-                }
-
-            });
-
-        } else {
-
-            // Receive input should always be ITA2
-            ita2 = Array.prototype.map.call(input, function(character) {
-                const letter = character.toUpperCase();
-                if (validITA2.indexOf(letter) === -1) {
-                    let errltr = letter;
-                    if (errltr==="\n") errltr = "Carriage Return";
-                    if (errltr===" ") errltr = "Space";
-                    throw new OperationError("Invalid ITA2 character : "+errltr);
-                }
-                return letter;
-            });
-
-        }
-
-        const ita2Input = ita2.join("");
+        // Convert input text to ITA2 (including figure/letter shifts)
+        const ita2Input = this.convertToITA2(input, intype, mode);
 
         let thisPsi = [];
         let thisChi = [];
@@ -381,6 +320,7 @@ class Lorenz extends Operation {
         const letters = Array.prototype.map.call(ita2Input, function(character) {
             const letter = character.toUpperCase();
 
+            // Store lugs used in limitations, need these later
             let x2bptr = x2+1;
             if (x2bptr===32) x2bptr=1;
             let s1bptr = s1+1;
@@ -406,13 +346,18 @@ class Lorenz extends Operation {
                 return "";
             }
 
+            // The encipher calculation
+
+            // We calculate Bitwise XOR for each of the 5 bits across our input ( K XOR Psi XOR Chi )
             const xorSum = [];
             for (let i=0;i<=4;i++) {
                 xorSum[i] = ITA2_TABLE[letter][i] ^ thisPsi[i] ^ thisChi[i];
             }
             const resultStr = xorSum.join("");
 
-            // Move Chi wheels one back after each letter
+            // Wheel movement
+
+            // Chi wheels always move one back after each letter
             if (--x1 < 1) x1 = 41;
             if (--x2 < 1) x2 = 31;
             if (--x3 < 1) x3 = 29;
@@ -427,6 +372,8 @@ class Lorenz extends Operation {
                 if (--m37 < 1) m37 = 37;
             }
 
+            // Psi wheels only move sometimes, dependent on M37 current setting and limitations
+
             const basicmotor = m37lug;
             let totalmotor = basicmotor;
             let lim = 0;
@@ -441,7 +388,7 @@ class Lorenz extends Operation {
 
             // Limitations here
             if (model==="SZ42a") {
-                // Chi 2 one back lim - The active character of chi 2 (2nd Chi wheel) in the previous position
+                // Chi 2 one back lim - The active character of Chi 2 (2nd Chi wheel) in the previous position
                 lim = chiSettings[2][x2bptr-1];
                 if (kt) {
                     //p5 back 2
@@ -481,13 +428,13 @@ class Lorenz extends Operation {
                 }
 
             } else if (model==="SZ40") {
-                // SZ40
+                // SZ40 - just move based on the M37 motor wheel
                 totalmotor = basicmotor;
             } else {
                 throw new OperationError("Lorenz model type not recognised");
             }
 
-            // increment Psi wheels when current totalmotor active
+            // Move the Psi wheels when current totalmotor active
             if (totalmotor === 1) {
                 if (--s1 < 1) s1 = 43;
                 if (--s2 < 1) s2 = 47;
@@ -501,58 +448,16 @@ class Lorenz extends Operation {
 
             let rtnstr = self.REVERSE_ITA2_TABLE[resultStr];
             if (format==="5/8/9") {
-                if (rtnstr==="+") rtnstr="5";
-                if (rtnstr==="-") rtnstr="8";
-                if (rtnstr===".") rtnstr="9";
+                if (rtnstr==="+") rtnstr="5"; // + or 5 used to represent figure shift
+                if (rtnstr==="-") rtnstr="8"; // - or 8 used to represent letter shift
+                if (rtnstr===".") rtnstr="9"; // . or 9 used to represent space
             }
             return rtnstr;
         });
 
         const ita2output = letters.join("");
-        let output = "";
-
-        if (mode === "Receive") {
-
-            figShifted = false;
-
-            // Convert output ITA2 to plaintext (including figure/letter shifts)
-            const out = Array.prototype.map.call(ita2output, function(letter) {
-
-                if (outtype === "Plaintext") {
-
-                    if (letter === "5" || letter === "+") {
-                        figShifted = true;
-                        return;
-                    } else if (letter === "8" || letter === "-") {
-                        figShifted = false;
-                        return;
-                    } else if (letter === "9") {
-                        return " ";
-                    } else if (letter === "3") {
-                        return "\n";
-                    } else if (letter === "4") {
-                        return "";
-                    }
-
-
-                    if (figShifted) {
-                        return self.REVERSE_FIGSHIFT_TABLE[letter];
-                    } else {
-                        return letter;
-                    }
-
-                } else {
-                    return letter;
-                }
-
-            });
-            output = out.join("");
-
-        } else {
-            output = ita2output;
-        }
 
-        return output;
+        return this.convertFromITA2(ita2output, outtype, mode);
 
     }
 
@@ -587,6 +492,109 @@ class Lorenz extends Operation {
         return arr;
     }
 
+    /**
+     * Convert input plaintext to ITA2
+     */
+    convertToITA2(input, intype, mode) {
+        let result = "";
+        let figShifted = false;
+
+        for (const character of input) {
+            const letter = character.toUpperCase();
+
+            // Convert input text to ITA2 (including figure/letter shifts)
+            if (intype === "ITA2" || mode === "Receive") {
+                if (validITA2.indexOf(letter) === -1) {
+                    let errltr = letter;
+                    if (errltr==="\n") errltr = "Carriage Return";
+                    if (errltr===" ") errltr = "Space";
+                    throw new OperationError("Invalid ITA2 character : "+errltr);
+                }
+                result += letter;
+            } else {
+                if (validChars.indexOf(letter) === -1) throw new OperationError("Invalid Plaintext character : "+letter);
+
+                if (!figShifted && figShiftedChars.indexOf(letter) !== -1) {
+                    // in letters mode and next char needs to be figure shifted
+                    figShifted = true;
+                    result += "55" + figShiftArr[letter];
+                } else if (figShifted) {
+                    // in figures mode and next char needs to be letter shifted
+                    if (letter==="\n") {
+                        result += "34";
+                    } else if (letter==="\r") {
+                        result += "4";
+                    } else if (figShiftedChars.indexOf(letter) === -1) {
+                        figShifted = false;
+                        result += "88" + letter;
+                    } else {
+                        result += figShiftArr[letter];
+                    }
+
+                } else {
+                    if (letter==="\n") {
+                        result += "34";
+                    } else if (letter==="\r") {
+                        result += "4";
+                    } else {
+                        result += letter;
+                    }
+                }
+
+            }
+
+        }
+
+        return result;
+    }
+
+    /**
+     * Convert final result ITA2 to plaintext
+     */
+    convertFromITA2(input, outtype, mode) {
+        let result = "";
+        let figShifted = false;
+        for (const letter of input) {
+            if (mode === "Receive") {
+
+                // Convert output ITA2 to plaintext (including figure/letter shifts)
+                if (outtype === "Plaintext") {
+
+                    if (letter === "5" || letter === "+") {
+                        figShifted = true;
+                    } else if (letter === "8" || letter === "-") {
+                        figShifted = false;
+                    } else if (letter === "9") {
+                        result += " ";
+                    } else if (letter === "3") {
+                        result += "\n";
+                    } else if (letter === "4") {
+                        result += "";
+                    } else if (letter === "/") {
+                        result += "/";
+                    } else {
+
+                        if (figShifted) {
+                            result += this.REVERSE_FIGSHIFT_TABLE[letter];
+                        } else {
+                            result += letter;
+                        }
+
+                    }
+
+                } else {
+                    result += letter;
+                }
+
+            } else {
+                result += letter;
+            }
+        }
+
+        return result;
+
+    }
+
 }
 
 const ITA2_TABLE = {
@@ -651,6 +659,7 @@ const figShiftArr = {
     "%": "F",
     "@": "G",
     "£": "H",
+    "": "J",
     "(": "K",
     ")": "L",
     ".": "M",

+ 1 - 0
tests/operations/index.mjs

@@ -91,6 +91,7 @@ import "./tests/Protobuf.mjs";
 import "./tests/ParseSSHHostKey.mjs";
 import "./tests/DefangIP.mjs";
 import "./tests/ParseUDP.mjs";
+import "./tests/Lorenz.mjs";
 
 // Cannot test operations that use the File type yet
 // import "./tests/SplitColourChannels.mjs";

+ 94 - 0
tests/operations/tests/Lorenz.mjs

@@ -0,0 +1,94 @@
+/**
+ * Lorenz SZ40/42a/42b machine tests.
+ * @author VirtualColossus
+ * @copyright Crown Copyright 2019
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+	{
+		// Simple test first - plain text to ITA2
+		name: "Lorenz SZ40: no pattern, plain text",
+		input: "HELLO WORLD, THIS IS A TEST MESSAGE.",
+		expectedOutput: "HELLO9WORLD55N889THIS9IS9A9TEST9MESSAGE55M",
+		recipeConfig: [
+            {
+                "op": "Lorenz",
+                "args": ["SZ40", "No Pattern", false, "Send", "Plaintext", "Plaintext", "5/8/9", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ".x...xx.x.x..xxx.x.x.xxxx.x.x.x.x.x..x.xx.x", ".xx.x.xxx..x.x.x..x.xx.x.xxx.x....x.xx.x.x.x..x", ".x.x.x..xxx....x.x.xx.x.x.x..xxx.x.x..x.x.xx..x.x.x", ".xx...xxxxx.x.x.xx...x.xx.x.x..x.x.xx.x..x.x.x.x.x.x.", "xx...xx.x..x.xx.x...x.x.x.x.x.x.x.x.xx..xxxx.x.x...xx.x..x.", "x.x.x.x.x.x...x.x.x...x.x.x...x.x....", ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx...", ".x...xxx.x.xxxx.x...x.x..xxx....xx.xxxx..", "x..xxx...x.xxxx..xx..x..xx.xx..", "..xx..x.xxx...xx...xx..xx.xx.", "xx..x..xxxx..xx.xxx....x..", "xx..xx....xxxx.x..x.x.."]
+            }
+        ]
+	},
+	{
+		// KH Pattern
+		name: "Lorenz SZ40: KH pattern, plain text, all 1s",
+		input: "HELLO WORLD, THIS IS A TEST MESSAGE.",
+		expectedOutput: "VIC3TS/CUJA/3II9W9JWDI5DAFXT4SOIF3999IZD9T",
+		recipeConfig: [
+            {
+                "op": "Lorenz",
+                "args": ["SZ40", "KH Pattern", false, "Send", "Plaintext", "Plaintext", "5/8/9", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ".x...xx.x.x..xxx.x.x.xxxx.x.x.x.x.x..x.xx.x", ".xx.x.xxx..x.x.x..x.xx.x.xxx.x....x.xx.x.x.x..x", ".x.x.x..xxx....x.x.xx.x.x.x..xxx.x.x..x.x.xx..x.x.x", ".xx...xxxxx.x.x.xx...x.xx.x.x..x.x.xx.x..x.x.x.x.x.x.", "xx...xx.x..x.xx.x...x.x.x.x.x.x.x.x.xx..xxxx.x.x...xx.x..x.", "x.x.x.x.x.x...x.x.x...x.x.x...x.x....", ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx...", ".x...xxx.x.xxxx.x...x.x..xxx....xx.xxxx..", "x..xxx...x.xxxx..xx..x..xx.xx..", "..xx..x.xxx...xx...xx..xx.xx.", "xx..x..xxxx..xx.xxx....x..", "xx..xx....xxxx.x..x.x.."]
+            }
+        ]
+	},
+	{
+		// KH Pattern, Random Start
+		name: "Lorenz SZ40: KH pattern, plain text, random start",
+		input: "HELLO WORLD, THIS IS A TEST MESSAGE.",
+		expectedOutput: "KGZP5ONYCHNNOXS9SN45MIE3SC3DJBZVJUOE5SLVGI",
+		recipeConfig: [
+            {
+                "op": "Lorenz",
+                "args": ["SZ40", "KH Pattern", false, "Send", "Plaintext", "Plaintext", "5/8/9", 20, 40, 3, 9, 27, 36, 4, 1, 9, 14, 21, 8, ".x...xx.x.x..xxx.x.x.xxxx.x.x.x.x.x..x.xx.x", ".xx.x.xxx..x.x.x..x.xx.x.xxx.x....x.xx.x.x.x..x", ".x.x.x..xxx....x.x.xx.x.x.x..xxx.x.x..x.x.xx..x.x.x", ".xx...xxxxx.x.x.xx...x.xx.x.x..x.x.xx.x..x.x.x.x.x.x.", "xx...xx.x..x.xx.x...x.x.x.x.x.x.x.x.xx..xxxx.x.x...xx.x..x.", "x.x.x.x.x.x...x.x.x...x.x.x...x.x....", ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx...", ".x...xxx.x.xxxx.x...x.x..xxx....xx.xxxx..", "x..xxx...x.xxxx..xx..x..xx.xx..", "..xx..x.xxx...xx...xx..xx.xx.", "xx..x..xxxx..xx.xxx....x..", "xx..xx....xxxx.x..x.x.."]
+            }
+        ]
+	},
+	{
+		// ZMUG Pattern, Random Start
+		name: "Lorenz SZ40: ZMUG pattern, plain text, random start",
+		input: "HELLO WORLD, THIS IS A TEST MESSAGE.",
+		expectedOutput: "IQVPAANDCA3CHDNO3V/CZQ/BTPZIKW8YAAQXQGLDMV",
+		recipeConfig: [
+            {
+                "op": "Lorenz",
+                "args": ["SZ40", "ZMUG Pattern", false, "Send", "Plaintext", "Plaintext", "5/8/9", 20, 40, 3, 9, 27, 36, 4, 1, 9, 14, 21, 8, ".x...xx.x.x..xxx.x.x.xxxx.x.x.x.x.x..x.xx.x", ".xx.x.xxx..x.x.x..x.xx.x.xxx.x....x.xx.x.x.x..x", ".x.x.x..xxx....x.x.xx.x.x.x..xxx.x.x..x.x.xx..x.x.x", ".xx...xxxxx.x.x.xx...x.xx.x.x..x.x.xx.x..x.x.x.x.x.x.", "xx...xx.x..x.xx.x...x.x.x.x.x.x.x.x.xx..xxxx.x.x...xx.x..x.", "x.x.x.x.x.x...x.x.x...x.x.x...x.x....", ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx...", ".x...xxx.x.xxxx.x...x.x..xxx....xx.xxxx..", "x..xxx...x.xxxx..xx..x..xx.xx..", "..xx..x.xxx...xx...xx..xx.xx.", "xx..x..xxxx..xx.xxx....x..", "xx..xx....xxxx.x..x.x.."]
+            }
+        ]
+	},
+	{
+		// Bream Pattern, Random Start
+		name: "Lorenz SZ40: Bream pattern, plain text, random start",
+		input: "HELLO WORLD, THIS IS A TEST MESSAGE.",
+		expectedOutput: "/89OALRPJEZQGOO84WOEQZ/I9NBRZOQPBTANC8E/GK",
+		recipeConfig: [
+            {
+                "op": "Lorenz",
+                "args": ["SZ40", "BREAM Pattern", false, "Send", "Plaintext", "Plaintext", "5/8/9", 20, 40, 3, 9, 27, 36, 4, 1, 9, 14, 21, 8, ".x...xx.x.x..xxx.x.x.xxxx.x.x.x.x.x..x.xx.x", ".xx.x.xxx..x.x.x..x.xx.x.xxx.x....x.xx.x.x.x..x", ".x.x.x..xxx....x.x.xx.x.x.x..xxx.x.x..x.x.xx..x.x.x", ".xx...xxxxx.x.x.xx...x.xx.x.x..x.x.xx.x..x.x.x.x.x.x.", "xx...xx.x..x.xx.x...x.x.x.x.x.x.x.x.xx..xxxx.x.x...xx.x..x.", "x.x.x.x.x.x...x.x.x...x.x.x...x.x....", ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx...", ".x...xxx.x.xxxx.x...x.x..xxx....xx.xxxx..", "x..xxx...x.xxxx..xx..x..xx.xx..", "..xx..x.xxx...xx...xx..xx.xx.", "xx..x..xxxx..xx.xxx....x..", "xx..xx....xxxx.x..x.x.."]
+            }
+        ]
+	},
+	{
+		// KH Pattern, all 1s
+		name: "Lorenz SZ42a: KH pattern, plain text, all 1s",
+		input: "HELLO WORLD, THIS IS A TEST MESSAGE.",
+		expectedOutput: "VIC3TS/ZOHUYXWLTUXPV9ZNOTW9IXJPFDLIBB5ZD9K",
+		recipeConfig: [
+            {
+                "op": "Lorenz",
+                "args": ["SZ42a", "KH Pattern", false, "Send", "Plaintext", "Plaintext", "5/8/9", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ".x...xx.x.x..xxx.x.x.xxxx.x.x.x.x.x..x.xx.x", ".xx.x.xxx..x.x.x..x.xx.x.xxx.x....x.xx.x.x.x..x", ".x.x.x..xxx....x.x.xx.x.x.x..xxx.x.x..x.x.xx..x.x.x", ".xx...xxxxx.x.x.xx...x.xx.x.x..x.x.xx.x..x.x.x.x.x.x.", "xx...xx.x..x.xx.x...x.x.x.x.x.x.x.x.xx..xxxx.x.x...xx.x..x.", "x.x.x.x.x.x...x.x.x...x.x.x...x.x....", ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx...", ".x...xxx.x.xxxx.x...x.x..xxx....xx.xxxx..", "x..xxx...x.xxxx..xx..x..xx.xx..", "..xx..x.xxx...xx...xx..xx.xx.", "xx..x..xxxx..xx.xxx....x..", "xx..xx....xxxx.x..x.x.."]
+            }
+        ]
+	},
+	{
+		// KH Pattern, all 1s
+		name: "Lorenz SZ42a: KH pattern, plain text, all 1s",
+		input: "HELLO WORLD, THIS IS A TEST MESSAGE.",
+		expectedOutput: "VIC3TS/ZOHUYXWLTUXPV9ZNOTW9IXJPFDLIBB5ZD9K",
+		recipeConfig: [
+            {
+                "op": "Lorenz",
+                "args": ["SZ42a", "KH Pattern", false, "Send", "Plaintext", "Plaintext", "5/8/9", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ".x...xx.x.x..xxx.x.x.xxxx.x.x.x.x.x..x.xx.x", ".xx.x.xxx..x.x.x..x.xx.x.xxx.x....x.xx.x.x.x..x", ".x.x.x..xxx....x.x.xx.x.x.x..xxx.x.x..x.x.xx..x.x.x", ".xx...xxxxx.x.x.xx...x.xx.x.x..x.x.xx.x..x.x.x.x.x.x.", "xx...xx.x..x.xx.x...x.x.x.x.x.x.x.x.xx..xxxx.x.x...xx.x..x.", "x.x.x.x.x.x...x.x.x...x.x.x...x.x....", ".xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx...", ".x...xxx.x.xxxx.x...x.x..xxx....xx.xxxx..", "x..xxx...x.xxxx..xx..x..xx.xx..", "..xx..x.xxx...xx...xx..xx.xx.", "xx..x..xxxx..xx.xxx....x..", "xx..xx....xxxx.x..x.x.."]
+            }
+        ]
+	},
+]);