Skip to content

Commit 7cc3e58

Browse files
authored
Merge branch 'master' into take-nth-bytes
2 parents 26a16b6 + dcf0bbb commit 7cc3e58

File tree

9 files changed

+499
-10
lines changed

9 files changed

+499
-10
lines changed

src/core/config/Categories.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@
234234
"Parse User Agent",
235235
"Parse IP range",
236236
"Parse IPv6 address",
237+
"IPv6 Transition Addresses",
237238
"Parse IPv4 header",
238239
"Strip IPv4 header",
239240
"Parse TCP",
@@ -326,7 +327,8 @@
326327
"Pseudo-Random Number Generator",
327328
"Sleep",
328329
"File Tree",
329-
"Take nth bytes"
330+
"Take nth bytes",
331+
"Drop nth bytes"
330332
]
331333
},
332334
{

src/core/operations/BlowfishDecrypt.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ class BlowfishDecrypt extends Operation {
7676
Blowfish's key length needs to be between 4 and 56 bytes (32-448 bits).`);
7777
}
7878

79-
if (iv.length !== 8) {
80-
throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes`);
79+
if (mode !== "ECB" && iv.length !== 8) {
80+
throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes.`);
8181
}
8282

8383
input = Utils.convertToByteString(input, inputType);

src/core/operations/BlowfishEncrypt.mjs

+3-3
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ class BlowfishEncrypt extends Operation {
7272

7373
if (key.length < 4 || key.length > 56) {
7474
throw new OperationError(`Invalid key length: ${key.length} bytes
75-
75+
7676
Blowfish's key length needs to be between 4 and 56 bytes (32-448 bits).`);
7777
}
7878

79-
if (iv.length !== 8) {
80-
throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes`);
79+
if (mode !== "ECB" && iv.length !== 8) {
80+
throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes.`);
8181
}
8282

8383
input = Utils.convertToByteString(input, inputType);

src/core/operations/DropNthBytes.mjs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* @author Oshawk [[email protected]]
3+
* @copyright Crown Copyright 2019
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
10+
/**
11+
* Drop nth bytes operation
12+
*/
13+
class DropNthBytes extends Operation {
14+
15+
/**
16+
* DropNthBytes constructor
17+
*/
18+
constructor() {
19+
super();
20+
21+
this.name = "Drop nth bytes";
22+
this.module = "Default";
23+
this.description = "Drops every nth byte starting with a given byte.";
24+
this.infoURL = "";
25+
this.inputType = "byteArray";
26+
this.outputType = "byteArray";
27+
this.args = [
28+
{
29+
name: "Drop every",
30+
type: "number",
31+
value: 4
32+
},
33+
{
34+
name: "Starting at",
35+
type: "number",
36+
value: 0
37+
},
38+
{
39+
name: "Apply to each line",
40+
type: "boolean",
41+
value: false
42+
}
43+
];
44+
}
45+
46+
/**
47+
* @param {byteArray} input
48+
* @param {Object[]} args
49+
* @returns {byteArray}
50+
*/
51+
run(input, args) {
52+
const n = args[0];
53+
const start = args[1];
54+
const eachLine = args[2];
55+
56+
if (parseInt(n, 10) !== n || n <= 0) {
57+
throw new OperationError("'Drop every' must be a positive integer.");
58+
}
59+
if (parseInt(start, 10) !== start || start < 0) {
60+
throw new OperationError("'Starting at' must be a positive or zero integer.");
61+
}
62+
63+
let offset = 0;
64+
const output = [];
65+
for (let i = 0; i < input.length; i++) {
66+
if (eachLine && input[i] === 0x0a) {
67+
output.push(0x0a);
68+
offset = i + 1;
69+
} else if (i - offset < start || (i - (start + offset)) % n !== 0) {
70+
output.push(input[i]);
71+
}
72+
}
73+
74+
return output;
75+
}
76+
77+
}
78+
79+
export default DropNthBytes;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/**
2+
* @author jb30795 [[email protected]]
3+
* @copyright Crown Copyright 2024
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
9+
/**
10+
* IPv6 Transition Addresses operation
11+
*/
12+
class IPv6TransitionAddresses extends Operation {
13+
14+
/**
15+
* IPv6TransitionAddresses constructor
16+
*/
17+
constructor() {
18+
super();
19+
20+
this.name = "IPv6 Transition Addresses";
21+
this.module = "Default";
22+
this.description = "Converts IPv4 addresses to their IPv6 Transition addresses. IPv6 Transition addresses can also be converted back into their original IPv4 address. MAC addresses can also be converted into the EUI-64 format, this can them be appended to your IPv6 /64 range to obtain a full /128 address.<br><br>Transition technologies enable translation between IPv4 and IPv6 addresses or tunneling to allow traffic to pass through the incompatible network, allowing the two standards to coexist.<br><br>Only /24 ranges and currently handled. Remove headers to easily copy out results.";
23+
this.infoURL = "https://wikipedia.org/wiki/IPv6_transition_mechanism";
24+
this.inputType = "string";
25+
this.outputType = "string";
26+
this.args = [
27+
{
28+
"name": "Ignore ranges",
29+
"type": "boolean",
30+
"value": true
31+
},
32+
{
33+
"name": "Remove headers",
34+
"type": "boolean",
35+
"value": false
36+
}
37+
];
38+
}
39+
40+
/**
41+
* @param {string} input
42+
* @param {Object[]} args
43+
* @returns {string}
44+
*/
45+
run(input, args) {
46+
const XOR = {"0": "2", "1": "3", "2": "0", "3": "1", "4": "6", "5": "7", "6": "4", "7": "5", "8": "a", "9": "b", "a": "8", "b": "9", "c": "e", "d": "f", "e": "c", "f": "d"};
47+
48+
/**
49+
* Function to convert to hex
50+
*/
51+
function hexify(octet) {
52+
return Number(octet).toString(16).padStart(2, "0");
53+
}
54+
55+
/**
56+
* Function to convert Hex to Int
57+
*/
58+
function intify(hex) {
59+
return parseInt(hex, 16);
60+
}
61+
62+
/**
63+
* Function converts IPv4 to IPv6 Transtion address
64+
*/
65+
function ipTransition(input, range) {
66+
let output = "";
67+
const HEXIP = input.split(".");
68+
69+
/**
70+
* 6to4
71+
*/
72+
if (!args[1]) {
73+
output += "6to4: ";
74+
}
75+
output += "2002:" + hexify(HEXIP[0]) + hexify(HEXIP[1]) + ":" + hexify(HEXIP[2]);
76+
if (range) {
77+
output += "00::/40\n";
78+
} else {
79+
output += hexify(HEXIP[3]) + "::/48\n";
80+
}
81+
82+
/**
83+
* Mapped
84+
*/
85+
if (!args[1]) {
86+
output += "IPv4 Mapped: ";
87+
}
88+
output += "::ffff:" + hexify(HEXIP[0]) + hexify(HEXIP[1]) + ":" + hexify(HEXIP[2]);
89+
if (range) {
90+
output += "00/120\n";
91+
} else {
92+
output += hexify(HEXIP[3]) + "\n";
93+
}
94+
95+
/**
96+
* Translated
97+
*/
98+
if (!args[1]) {
99+
output += "IPv4 Translated: ";
100+
}
101+
output += "::ffff:0:" + hexify(HEXIP[0]) + hexify(HEXIP[1]) + ":" + hexify(HEXIP[2]);
102+
if (range) {
103+
output += "00/120\n";
104+
} else {
105+
output += hexify(HEXIP[3]) + "\n";
106+
}
107+
108+
/**
109+
* Nat64
110+
*/
111+
if (!args[1]) {
112+
output += "Nat 64: ";
113+
}
114+
output += "64:ff9b::" + hexify(HEXIP[0]) + hexify(HEXIP[1]) + ":" + hexify(HEXIP[2]);
115+
if (range) {
116+
output += "00/120\n";
117+
} else {
118+
output += hexify(HEXIP[3]) + "\n";
119+
}
120+
121+
return output;
122+
}
123+
124+
/**
125+
* Convert MAC to EUI-64
126+
*/
127+
function macTransition(input) {
128+
let output = "";
129+
const MACPARTS = input.split(":");
130+
if (!args[1]) {
131+
output += "EUI-64 Interface ID: ";
132+
}
133+
const MAC = MACPARTS[0] + MACPARTS[1] + ":" + MACPARTS[2] + "ff:fe" + MACPARTS[3] + ":" + MACPARTS[4] + MACPARTS[5];
134+
output += MAC.slice(0, 1) + XOR[MAC.slice(1, 2)] + MAC.slice(2);
135+
136+
return output;
137+
}
138+
139+
140+
/**
141+
* Convert IPv6 address to its original IPv4 or MAC address
142+
*/
143+
function unTransition(input) {
144+
let output = "";
145+
let hextets = "";
146+
147+
/**
148+
* 6to4
149+
*/
150+
if (input.startsWith("2002:")) {
151+
if (!args[1]) {
152+
output += "IPv4: ";
153+
}
154+
output += String(intify(input.slice(5, 7))) + "." + String(intify(input.slice(7, 9)))+ "." + String(intify(input.slice(10, 12)))+ "." + String(intify(input.slice(12, 14))) + "\n";
155+
} else if (input.startsWith("::ffff:") || input.startsWith("0000:0000:0000:0000:0000:ffff:") || input.startsWith("::ffff:0000:") || input.startsWith("0000:0000:0000:0000:ffff:0000:") || input.startsWith("64:ff9b::") || input.startsWith("0064:ff9b:0000:0000:0000:0000:")) {
156+
/**
157+
* Mapped/Translated/Nat64
158+
*/
159+
hextets = /:([0-9a-z]{1,4}):[0-9a-z]{1,4}$/.exec(input)[1].padStart(4, "0") + /:([0-9a-z]{1,4})$/.exec(input)[1].padStart(4, "0");
160+
if (!args[1]) {
161+
output += "IPv4: ";
162+
}
163+
output += intify(hextets.slice(-8, -7) + hextets.slice(-7, -6)) + "." +intify(hextets.slice(-6, -5) + hextets.slice(-5, -4)) + "." +intify(hextets.slice(-4, -3) + hextets.slice(-3, -2)) + "." +intify(hextets.slice(-2, -1) + hextets.slice(-1,)) + "\n";
164+
} else if (input.slice(-12, -7).toUpperCase() === "FF:FE") {
165+
/**
166+
* EUI-64
167+
*/
168+
if (!args[1]) {
169+
output += "Mac Address: ";
170+
}
171+
const MAC = (input.slice(-19, -17) + ":" + input.slice(-17, -15) + ":" + input.slice(-14, -12) + ":" + input.slice(-7, -5) + ":" + input.slice(-4, -2) + ":" + input.slice(-2,)).toUpperCase();
172+
output += MAC.slice(0, 1) + XOR[MAC.slice(1, 2)] + MAC.slice(2) + "\n";
173+
}
174+
175+
return output;
176+
}
177+
178+
179+
/**
180+
* Main
181+
*/
182+
let output = "";
183+
let inputs = input.split("\n");
184+
// Remove blank rows
185+
inputs = inputs.filter(Boolean);
186+
187+
for (let i = 0; i < inputs.length; i++) {
188+
// if ignore ranges is checked and input is a range, skip
189+
if ((args[0] && !inputs[i].includes("/")) || (!args[0])) {
190+
if (/^[0-9]{1,3}(?:\.[0-9]{1,3}){3}$/.test(inputs[i])) {
191+
output += ipTransition(inputs[i], false);
192+
} else if (/\/24$/.test(inputs[i])) {
193+
output += ipTransition(inputs[i], true);
194+
} else if (/^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/.test(inputs[i])) {
195+
output += macTransition(inputs[i]);
196+
} else if (/^((?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(inputs[i])) {
197+
output += unTransition(inputs[i]);
198+
} else {
199+
output = "Enter compressed or expanded IPv6 address, IPv4 address or MAC Address.";
200+
}
201+
}
202+
}
203+
204+
return output;
205+
}
206+
207+
}
208+
209+
export default IPv6TransitionAddresses;

tests/operations/index.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import "./tests/Crypt.mjs";
5959
import "./tests/CSV.mjs";
6060
import "./tests/DateTime.mjs";
6161
import "./tests/DefangIP.mjs";
62+
import "./tests/DropNthBytes.mjs";
6263
import "./tests/ECDSA.mjs";
6364
import "./tests/ELFInfo.mjs";
6465
import "./tests/Enigma.mjs";

tests/operations/tests/Crypt.mjs

+16-4
Original file line numberDiff line numberDiff line change
@@ -1579,19 +1579,31 @@ DES uses a key length of 8 bytes (64 bits).`,
15791579
from Crypto.Cipher import Blowfish
15801580
import binascii
15811581
1582-
input_data = b"The quick brown fox jumps over the lazy dog."
1582+
# Blowfish cipher parameters - key, mode, iv, segment_size, nonce
15831583
key = binascii.unhexlify("0011223344556677")
1584-
iv = binascii.unhexlify("0000000000000000")
15851584
mode = Blowfish.MODE_CBC
1585+
kwargs = {}
1586+
iv = binascii.unhexlify("ffeeddccbbaa9988")
1587+
if mode in [Blowfish.MODE_CBC, Blowfish.MODE_CFB, Blowfish.MODE_OFB]:
1588+
kwargs = {"iv": iv}
1589+
if mode == Blowfish.MODE_CFB:
1590+
kwargs["segment_size"] = 64
1591+
if mode == Blowfish.MODE_CTR:
1592+
nonce = binascii.unhexlify("0000000000000000")
1593+
nonce = nonce[:7]
1594+
kwargs["nonce"] = nonce
1595+
1596+
cipher = Blowfish.new(key, mode, **kwargs)
15861597
1598+
# Input data and padding
1599+
input_data = b"The quick brown fox jumps over the lazy dog."
15871600
if mode == Blowfish.MODE_ECB or mode == Blowfish.MODE_CBC:
15881601
padding_len = 8-(len(input_data) & 7)
15891602
for i in range(padding_len):
15901603
input_data += bytes([padding_len])
15911604
1592-
cipher = Blowfish.new(key, mode) # set iv, nonce, segment_size etc. here
1605+
# Encrypted text
15931606
cipher_text = cipher.encrypt(input_data)
1594-
15951607
cipher_text = binascii.hexlify(cipher_text).decode("UTF-8")
15961608
15971609
print("Encrypted: {}".format(cipher_text))

0 commit comments

Comments
 (0)