Browse Source

Merge branch 'tlv' of https://github.com/GCHQ77703/CyberChef into GCHQ77703-tlv

n1474335 7 years ago
parent
commit
a15af602e0

+ 2 - 1
src/core/config/Categories.json

@@ -53,7 +53,8 @@
             "To MessagePack",
             "From MessagePack",
             "To Braille",
-            "From Braille"
+            "From Braille",
+            "LV Decode"
         ]
     },
     {

+ 71 - 0
src/core/lib/LengthValue.mjs

@@ -0,0 +1,71 @@
+/**
+ * @author gchq77703 []
+ * @copyright Crown Copyright 2018
+ * @license Apache-2.0
+ */
+
+const defaults = {
+    location: 0,
+    bytesInLength: 1,
+    basicEncodingRules: false
+};
+
+/**
+ * Length Value library
+ */
+export default class LengthValue {
+
+    /**
+     * LengthValue constructor
+     */
+    constructor(input, options) {
+        this.input = input;
+        Object.assign(this, defaults, options);
+    }
+
+    /**
+     * @returns {Number}
+     */
+    getLength() {
+        if (this.basicEncodingRules) {
+            const bit = this.input[this.location];
+            if (bit & 0x80) {
+                this.bytesInLength = bit & ~0x80;
+            } else {
+                this.location++;
+                return bit & ~0x80;
+            }
+        }
+
+        let length = 0;
+
+        for (let i = 0; i < this.bytesInLength; i++) {
+            length += this.input[this.location] * Math.pow(Math.pow(2, 8), i);
+            this.location++;
+        }
+
+        return length;
+    }
+
+    /**
+     * @param {Number} length
+     * @returns {Number[]}
+     */
+    getValue(length) {
+        const value = [];
+
+        for (let i = 0; i < length; i++) {
+            value.push(this.input[this.location]);
+            this.location++;
+        }
+
+        return value;
+    }
+
+    /**
+     * @returns {Boolean}
+     */
+    atEnd() {
+        return this.input.length <= this.location;
+    }
+}

+ 81 - 0
src/core/operations/LVDecode.mjs

@@ -0,0 +1,81 @@
+/**
+ * @author gchq77703 []
+ * @copyright Crown Copyright 2018
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import LengthValue from "../lib/LengthValue";
+
+/**
+ * From LV Decode operation
+ */
+class LVDecode extends Operation {
+
+    /**
+     * LVDecode constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "LV Decode";
+        this.module = "Default";
+        this.description = "Converts a Length-Value (LV) encoded string into a JSON object.  Can optionally include a <code>Key</code> / <code>Type</code> entry.";
+        this.infoURL = "https://wikipedia.org/wiki/KLV";
+        this.inputType = "byteArray";
+        this.outputType = "JSON";
+        this.args = [
+            {
+                name: "Bytes in Key Value",
+                type: "option",
+                value: [
+                    "0 Bytes (No Key)",
+                    "1 Byte",
+                    "2 Bytes",
+                    "4 Bytes"
+                ]
+            },
+            {
+                name: "Bytes in Length Value",
+                type: "option",
+                value: [
+                    "1 Byte",
+                    "2 Bytes",
+                    "4 Bytes"
+                ]
+            },
+            {
+                name: "Use Basic Encoding Rules",
+                type: "boolean"
+            }
+        ];
+    }
+
+    /**
+     * @param {byteArray} input
+     * @param {Object[]} args
+     * @returns {string}
+     */
+    run(input, args) {
+        const bytesInKey = parseInt(args[0].split(" ")[0], 10);
+        const bytesInLength = parseInt(args[1].split(" ")[0], 10);
+        const basicEncodingRules = args[2];
+
+        const lv = new LengthValue(input, { bytesInLength, basicEncodingRules });
+
+        const data = [];
+
+        while (!lv.atEnd()) {
+            const key = bytesInKey ? lv.getValue(bytesInKey) : undefined;
+            const length = lv.getLength();
+            const value = lv.getValue(length);
+
+            data.push({ key, length, value });
+        }
+
+        return data;
+    }
+
+}
+
+export default LVDecode;

+ 1 - 0
test/index.mjs

@@ -71,6 +71,7 @@ import "./tests/operations/SymmetricDifference";
 import "./tests/operations/ToGeohash.mjs";
 import "./tests/operations/TranslateDateTimeFormat";
 import "./tests/operations/Magic";
+import "./tests/operations/LVDecode";
 
 let allTestsPassing = true;
 const testStatusCounts = {

+ 56 - 0
test/tests/operations/LVDecode.mjs

@@ -0,0 +1,56 @@
+/**
+ * LV Decoder tests.
+ *
+ * @author gchq77703 []
+ * @copyright Crown Copyright 2018
+ * @license Apache-2.0
+ */
+
+import TestRegister from "../../TestRegister";
+
+TestRegister.addTests([
+    {
+        name: "LVDecode: LengthValue",
+        input: "\x05\x48\x6f\x75\x73\x65\x04\x72\x6f\x6f\x6d\x04\x64\x6f\x6f\x72",
+        expectedOutput: JSON.stringify([{"length": 5, "value": [72, 111, 117, 115, 101]}, {"length": 4, "value": [114, 111, 111, 109]}, {"length": 4, "value": [100, 111, 111, 114]}]),
+        recipeConfig: [
+            {
+                "op": "LV Decode",
+                "args": ["0 Bytes (No Key)", "1 Byte", false]
+            }
+        ]
+    },
+    {
+        name: "LVDecode: LengthValue with BER",
+        input: "\x05\x48\x6f\x75\x73\x65\x04\x72\x6f\x6f\x6d\x04\x64\x6f\x6f\x72",
+        expectedOutput: JSON.stringify([{"length": 5, "value": [72, 111, 117, 115, 101]}, {"length": 4, "value": [114, 111, 111, 109]}, {"length": 4, "value": [100, 111, 111, 114]}]),
+        recipeConfig: [
+            {
+                "op": "LV Decode",
+                "args": ["0 Bytes (No Key)", "4 Bytes", false] // length value is patently wrong, should be ignored by BER.
+            }
+        ]
+    },
+    {
+        name: "LVDecode: KeyLengthValue",
+        input: "\x04\x05\x48\x6f\x75\x73\x65\x05\x04\x72\x6f\x6f\x6d\x42\x04\x64\x6f\x6f\x72",
+        expectedOutput: JSON.stringify([{"key":[4],"length":5,"value":[72,111,117,115,101]},{"key":[5],"length":4,"value":[114,111,111,109]},{"key":[66],"length":4,"value":[100,111,111,114]}]),
+        recipeConfig: [
+            {
+                "op": "LV Decode",
+                "args": ["1 Byte", "1 Byte", false]
+            }
+        ]
+    },
+    {
+        name: "LVDecode: KeyLengthValue with BER",
+        input: "\x04\x05\x48\x6f\x75\x73\x65\x05\x04\x72\x6f\x6f\x6d\x42\x04\x64\x6f\x6f\x72",
+        expectedOutput: JSON.stringify([{"key":[4],"length":5,"value":[72,111,117,115,101]},{"key":[5],"length":4,"value":[114,111,111,109]},{"key":[66],"length":4,"value":[100,111,111,114]}]),
+        recipeConfig: [
+            {
+                "op": "LV Decode",
+                "args": ["1 Byte", "4 Byte", true] // length value is patently wrong, should be ignored by BER.
+            }
+        ]
+    }
+]);