Skip to content

Commit 324560a

Browse files
authored
Merge branch 'gchq:master' into feature/docker-multiplatform-build
2 parents f8b613b + 95c6406 commit 324560a

File tree

14 files changed

+5907
-3371
lines changed

14 files changed

+5907
-3371
lines changed

package-lock.json

Lines changed: 5305 additions & 3319 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
"notepack.io": "^3.0.1",
161161
"ntlm": "^0.1.3",
162162
"nwmatcher": "^1.4.4",
163-
"otp": "0.1.3",
163+
"otpauth": "9.3.6",
164164
"path": "^0.12.7",
165165
"popper.js": "^1.16.1",
166166
"process": "^0.11.10",

src/core/config/Categories.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@
274274
"Unicode Text Format",
275275
"Remove Diacritics",
276276
"Unescape Unicode Characters",
277-
"Convert to NATO alphabet"
277+
"Convert to NATO alphabet",
278+
"Convert Leet Speak"
278279
]
279280
},
280281
{
@@ -326,7 +327,9 @@
326327
"Unescape string",
327328
"Pseudo-Random Number Generator",
328329
"Sleep",
329-
"File Tree"
330+
"File Tree",
331+
"Take nth bytes",
332+
"Drop nth bytes"
330333
]
331334
},
332335
{
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* @author bartblaze []
3+
* @copyright Crown Copyright 2025
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
9+
/**
10+
* Convert Leet Speak operation
11+
*/
12+
class ConvertLeetSpeak extends Operation {
13+
/**
14+
* ConvertLeetSpeak constructor
15+
*/
16+
constructor() {
17+
super();
18+
19+
this.name = "Convert Leet Speak";
20+
this.module = "Default";
21+
this.description = "Converts to and from Leet Speak";
22+
this.infoURL = "https://wikipedia.org/wiki/Leet";
23+
this.inputType = "string";
24+
this.outputType = "string";
25+
this.args = [
26+
{
27+
name: "Direction",
28+
type: "option",
29+
value: ["To Leet Speak", "From Leet Speak"],
30+
defaultIndex: 0
31+
}
32+
];
33+
}
34+
35+
/**
36+
* @param {string} input
37+
* @param {Object[]} args
38+
* @returns {string}
39+
*/
40+
run(input, args) {
41+
const direction = args[0];
42+
if (direction === "To Leet Speak") {
43+
return input.replace(/[abcdefghijklmnopqrstuvwxyz]/gi, char => {
44+
return toLeetMap[char.toLowerCase()] || char;
45+
});
46+
} else if (direction === "From Leet Speak") {
47+
return input.replace(/[48cd3f6h1jklmn0pqr57uvwxyz]/g, char => {
48+
return fromLeetMap[char] || char;
49+
});
50+
}
51+
}
52+
}
53+
54+
const toLeetMap = {
55+
"a": "4",
56+
"b": "b",
57+
"c": "c",
58+
"d": "d",
59+
"e": "3",
60+
"f": "f",
61+
"g": "g",
62+
"h": "h",
63+
"i": "1",
64+
"j": "j",
65+
"k": "k",
66+
"l": "l",
67+
"m": "m",
68+
"n": "n",
69+
"o": "0",
70+
"p": "p",
71+
"q": "q",
72+
"r": "r",
73+
"s": "5",
74+
"t": "7",
75+
"u": "u",
76+
"v": "v",
77+
"w": "w",
78+
"x": "x",
79+
"y": "y",
80+
"z": "z"
81+
};
82+
83+
const fromLeetMap = {
84+
"4": "a",
85+
"b": "b",
86+
"c": "c",
87+
"d": "d",
88+
"3": "e",
89+
"f": "f",
90+
"g": "g",
91+
"h": "h",
92+
"1": "i",
93+
"j": "j",
94+
"k": "k",
95+
"l": "l",
96+
"m": "m",
97+
"n": "n",
98+
"0": "o",
99+
"p": "p",
100+
"q": "q",
101+
"r": "r",
102+
"5": "s",
103+
"7": "t",
104+
"u": "u",
105+
"v": "v",
106+
"w": "w",
107+
"x": "x",
108+
"y": "y",
109+
"z": "z"
110+
};
111+
112+
export default ConvertLeetSpeak;
113+
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* @author Oshawk [oshawk@protonmail.com]
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;

src/core/operations/GenerateHOTP.mjs

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@
55
*/
66

77
import Operation from "../Operation.mjs";
8-
import otp from "otp";
9-
import ToBase32 from "./ToBase32.mjs";
8+
import * as OTPAuth from "otpauth";
109

1110
/**
1211
* Generate HOTP operation
1312
*/
1413
class GenerateHOTP extends Operation {
15-
1614
/**
17-
* GenerateHOTP constructor
15+
*
1816
*/
1917
constructor() {
2018
super();
@@ -31,11 +29,6 @@ class GenerateHOTP extends Operation {
3129
"type": "string",
3230
"value": ""
3331
},
34-
{
35-
"name": "Key size",
36-
"type": "number",
37-
"value": 32
38-
},
3932
{
4033
"name": "Code length",
4134
"type": "number",
@@ -50,21 +43,26 @@ class GenerateHOTP extends Operation {
5043
}
5144

5245
/**
53-
* @param {ArrayBuffer} input
54-
* @param {Object[]} args
55-
* @returns {string}
46+
*
5647
*/
5748
run(input, args) {
58-
const otpObj = otp({
59-
name: args[0],
60-
keySize: args[1],
61-
codeLength: args[2],
62-
secret: (new ToBase32).run(input, []).split("=")[0],
49+
const secretStr = new TextDecoder("utf-8").decode(input).trim();
50+
const secret = secretStr ? secretStr.toUpperCase().replace(/\s+/g, "") : "";
51+
52+
const hotp = new OTPAuth.HOTP({
53+
issuer: "",
54+
label: args[0],
55+
algorithm: "SHA1",
56+
digits: args[1],
57+
counter: args[2],
58+
secret: OTPAuth.Secret.fromBase32(secret)
6359
});
64-
const counter = args[3];
65-
return `URI: ${otpObj.hotpURL}\n\nPassword: ${otpObj.hotp(counter)}`;
66-
}
6760

61+
const uri = hotp.toString();
62+
const code = hotp.generate();
63+
64+
return `URI: ${uri}\n\nPassword: ${code}`;
65+
}
6866
}
6967

7068
export default GenerateHOTP;

src/core/operations/GenerateTOTP.mjs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,17 @@
55
*/
66

77
import Operation from "../Operation.mjs";
8-
import otp from "otp";
9-
import ToBase32 from "./ToBase32.mjs";
8+
import * as OTPAuth from "otpauth";
109

1110
/**
1211
* Generate TOTP operation
1312
*/
1413
class GenerateTOTP extends Operation {
15-
1614
/**
17-
* GenerateTOTP constructor
15+
*
1816
*/
1917
constructor() {
2018
super();
21-
2219
this.name = "Generate TOTP";
2320
this.module = "Default";
2421
this.description = "The Time-based One-Time Password algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, is the cornerstone of Initiative For Open Authentication (OAUTH), and is used in a number of two-factor authentication systems. A TOTP is an HOTP where the counter is the current time.<br><br>Enter the secret as the input or leave it blank for a random secret to be generated. T0 and T1 are in seconds.";
@@ -31,11 +28,6 @@ class GenerateTOTP extends Operation {
3128
"type": "string",
3229
"value": ""
3330
},
34-
{
35-
"name": "Key size",
36-
"type": "number",
37-
"value": 32
38-
},
3931
{
4032
"name": "Code length",
4133
"type": "number",
@@ -55,22 +47,27 @@ class GenerateTOTP extends Operation {
5547
}
5648

5749
/**
58-
* @param {ArrayBuffer} input
59-
* @param {Object[]} args
60-
* @returns {string}
50+
*
6151
*/
6252
run(input, args) {
63-
const otpObj = otp({
64-
name: args[0],
65-
keySize: args[1],
66-
codeLength: args[2],
67-
secret: (new ToBase32).run(input, []).split("=")[0],
68-
epoch: args[3],
69-
timeSlice: args[4]
53+
const secretStr = new TextDecoder("utf-8").decode(input).trim();
54+
const secret = secretStr ? secretStr.toUpperCase().replace(/\s+/g, "") : "";
55+
56+
const totp = new OTPAuth.TOTP({
57+
issuer: "",
58+
label: args[0],
59+
algorithm: "SHA1",
60+
digits: args[1],
61+
period: args[3],
62+
epoch: args[2] * 1000, // Convert seconds to milliseconds
63+
secret: OTPAuth.Secret.fromBase32(secret)
7064
});
71-
return `URI: ${otpObj.totpURL}\n\nPassword: ${otpObj.totp()}`;
72-
}
7365

66+
const uri = totp.toString();
67+
const code = totp.generate();
68+
69+
return `URI: ${uri}\n\nPassword: ${code}`;
70+
}
7471
}
7572

7673
export default GenerateTOTP;

0 commit comments

Comments
 (0)