Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rolling xor #452

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 84 additions & 16 deletions src/core/operations/XOR.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -21,27 +20,36 @@ class XOR extends Operation {

this.name = "XOR";
this.module = "Default";
this.description = "XOR the input with the given key.<br>e.g. <code>fe023da5</code><br><br><strong>Options</strong><br><u>Null preserving:</u> If the current byte is 0x00 or the same as the key, skip it.<br><br><u>Scheme:</u><ul><li>Standard - key is unchanged after each round</li><li>Input differential - key is set to the value of the previous unprocessed byte</li><li>Output differential - key is set to the value of the previous processed byte</li><li>Cascade - key is set to the input byte shifted by one</li></ul>";
this.description =
"XOR the input with the given key.<br>e.g. <code>fe023da5</code><br><br><strong>Options</strong><br><u>Null preserving:</u> If the current byte is 0x00 or the same as the key, skip it.<br><br><u>Scheme:</u><ul><li>Standard - key is unchanged after each round</li><li>Input differential - key is set to the value of the previous unprocessed byte</li><li>Output differential - key is set to the value of the previous processed byte</li><li>Cascade - key is set to the input byte shifted by one</li><li>Rolling - key is set to the value of itself added with the current position in the bytes</li>Rolling cumulative - key is set to the value of itself added with a cumulative addition of the position in the bytes<li></li><li>Rolling cumulative (self) - key is set to the value of itself XOR'd with the previous chunk of bytes (where the chunk size is equal to key size)</li></ul>";
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,
},
];
}

Expand All @@ -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);
}

Expand Down Expand Up @@ -83,7 +152,6 @@ class XOR extends Operation {
highlightReverse(pos, args) {
return pos;
}

}

export default XOR;
99 changes: 75 additions & 24 deletions tests/operations/tests/BitwiseOp.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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"] },
],
},
Copy link
Collaborator

@mattnotmitt mattnotmitt Feb 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs some tests for Rolling... schemes.

]);