diff --git a/src/core/operations/XOR.mjs b/src/core/operations/XOR.mjs index aa2288420c..b88844198e 100644 --- a/src/core/operations/XOR.mjs +++ b/src/core/operations/XOR.mjs @@ -6,13 +6,12 @@ import Operation from "../Operation.mjs"; import Utils from "../Utils.mjs"; -import { bitOp, xor, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs"; +import { bitOp, xor, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs"; /** * XOR operation */ class XOR extends Operation { - /** * XOR constructor */ @@ -21,27 +20,36 @@ class XOR extends Operation { this.name = "XOR"; this.module = "Default"; - this.description = "XOR the input with the given key.
e.g. fe023da5

Options
Null preserving: If the current byte is 0x00 or the same as the key, skip it.

Scheme:"; + this.description = + "XOR the input with the given key.
e.g. fe023da5

Options
Null preserving: If the current byte is 0x00 or the same as the key, skip it.

Scheme:"; this.infoURL = "https://wikipedia.org/wiki/XOR"; this.inputType = "ArrayBuffer"; this.outputType = "byteArray"; this.args = [ { - "name": "Key", - "type": "toggleString", - "value": "", - "toggleValues": BITWISE_OP_DELIMS + name: "Key", + type: "toggleString", + value: "", + toggleValues: BITWISE_OP_DELIMS, }, { - "name": "Scheme", - "type": "option", - "value": ["Standard", "Input differential", "Output differential", "Cascade"] + name: "Scheme", + type: "option", + value: [ + "Standard", + "Input differential", + "Output differential", + "Cascade", + "Rolling", + "Rolling cumulative", + "Rolling cumulative (self)", + ], }, { - "name": "Null preserving", - "type": "boolean", - "value": false - } + name: "Null preserving", + type: "boolean", + value: false, + }, ]; } @@ -52,9 +60,70 @@ class XOR extends Operation { */ run(input, args) { input = new Uint8Array(input); - const key = Utils.convertToByteArray(args[0].string || "", args[0].option), + const key = Utils.convertToByteArray( + args[0].string || "", + args[0].option + ), [, scheme, nullPreserving] = args; + if (scheme.startsWith("Rolling") && key.length) { + const inputChunks = Utils.chunked(input, key.length); + let runningIndex = 0; + let runningKey = key; + let xorred = null; + return inputChunks.reduce((result, current, index) => { + runningIndex += index; + switch (scheme) { + // key = key + index + case "Rolling": + return result.concat( + bitOp( + current, + key.map((x) => add(x, index)), + xor, + nullPreserving, + scheme + ) + ); + + // key = key + index + previous + case "Rolling cumulative": + return result.concat( + bitOp( + current, + key.map((x) => add(x, runningIndex)), + xor, + nullPreserving, + scheme + ) + ); + + // key = key XOR previous chunk + case "Rolling cumulative (self)": + // Xor this chunk + xorred = bitOp( + current, + runningKey, + xor, + nullPreserving, + scheme + ); + + // Update the running key for next part of loop + runningKey = bitOp( + runningKey, + current, + xor, + nullPreserving, + scheme + ); + + // Return the result with the newest xor'd chunk + return result.concat(xorred); + } + }, Utils.strToByteArray("")); // Start our reduction with an empty byte array + } + return bitOp(input, key, xor, nullPreserving, scheme); } @@ -83,7 +152,6 @@ class XOR extends Operation { highlightReverse(pos, args) { return pos; } - } export default XOR; diff --git a/tests/operations/tests/BitwiseOp.mjs b/tests/operations/tests/BitwiseOp.mjs index 50303677dd..af32c72ad1 100644 --- a/tests/operations/tests/BitwiseOp.mjs +++ b/tests/operations/tests/BitwiseOp.mjs @@ -11,40 +11,91 @@ TestRegister.addTests([ { name: "Bit shift left", input: "01010101 10101010 11111111 00000000 11110000 00001111 00110011 11001100", - expectedOutput: "10101010 01010100 11111110 00000000 11100000 00011110 01100110 10011000", + expectedOutput: + "10101010 01010100 11111110 00000000 11100000 00011110 01100110 10011000", recipeConfig: [ - { "op": "From Binary", - "args": ["Space"] }, - { "op": "Bit shift left", - "args": [1] }, - { "op": "To Binary", - "args": ["Space"] } - ] + { op: "From Binary", args: ["Space"] }, + { op: "Bit shift left", args: [1] }, + { op: "To Binary", args: ["Space"] }, + ], }, { name: "Bit shift right: Logical shift", input: "01010101 10101010 11111111 00000000 11110000 00001111 00110011 11001100", - expectedOutput: "00101010 01010101 01111111 00000000 01111000 00000111 00011001 01100110", + expectedOutput: + "00101010 01010101 01111111 00000000 01111000 00000111 00011001 01100110", recipeConfig: [ - { "op": "From Binary", - "args": ["Space"] }, - { "op": "Bit shift right", - "args": [1, "Logical shift"] }, - { "op": "To Binary", - "args": ["Space"] } - ] + { op: "From Binary", args: ["Space"] }, + { op: "Bit shift right", args: [1, "Logical shift"] }, + { op: "To Binary", args: ["Space"] }, + ], }, { name: "Bit shift right: Arithmetic shift", input: "01010101 10101010 11111111 00000000 11110000 00001111 00110011 11001100", - expectedOutput: "00101010 11010101 11111111 00000000 11111000 00000111 00011001 11100110", + expectedOutput: + "00101010 11010101 11111111 00000000 11111000 00000111 00011001 11100110", recipeConfig: [ - { "op": "From Binary", - "args": ["Space"] }, - { "op": "Bit shift right", - "args": [1, "Arithmetic shift"] }, - { "op": "To Binary", - "args": ["Space"] } - ] + { op: "From Binary", args: ["Space"] }, + { op: "Bit shift right", args: [1, "Arithmetic shift"] }, + { op: "To Binary", args: ["Space"] }, + ], + }, + { + name: "XOR: empty", + input: "", + expectedOutput: "", + recipeConfig: [ + { op: "From Binary", args: ["Space"] }, + { + op: "XOR", + args: [ + { option: "Binary", string: "11111111" }, + "Standard", + false, + ], + }, + { op: "To Binary", args: ["Space"] }, + ], + }, + { + name: "XOR: 1111111, standard, no preserve nulls", + input: "01010101 10101010 11111111 00000000 11110000 00001111 00110011 11001100", + expectedOutput: + "10101010 01010101 00000000 11111111 00001111 11110000 11001100 00110011", + recipeConfig: [ + { op: "From Binary", args: ["Space"] }, + { + op: "XOR", + args: [ + { option: "Binary", string: "11111111" }, + "Standard", + false, + ], + }, + { op: "To Binary", args: ["Space"] }, + ], + }, + { + name: "XOR: 1111111, standard, preserve nulls", + input: "01010101 10101010 11111111 00000000 11110000 00001111 00110011 11001100", + /* + * We preserve the all 1's case as well, as the `preserve nulls` option + * also preserves the bytes if they're equivalent to the key + */ + expectedOutput: + "10101010 01010101 11111111 00000000 00001111 11110000 11001100 00110011", + recipeConfig: [ + { op: "From Binary", args: ["Space"] }, + { + op: "XOR", + args: [ + { option: "Binary", string: "11111111" }, + "Standard", + true, + ], + }, + { op: "To Binary", args: ["Space"] }, + ], }, ]);