mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-07 15:07:11 -04:00
Merge pull request #33 from linuxgemini/linuxgemini-patch-modhex
feat(Modhex): Introduce basic Modhex conversion
This commit is contained in:
commit
4c145c50dd
6 changed files with 444 additions and 0 deletions
|
@ -10,6 +10,8 @@
|
||||||
"From Hexdump",
|
"From Hexdump",
|
||||||
"To Hex",
|
"To Hex",
|
||||||
"From Hex",
|
"From Hex",
|
||||||
|
"To Modhex",
|
||||||
|
"From Modhex",
|
||||||
"To Charcode",
|
"To Charcode",
|
||||||
"From Charcode",
|
"From Charcode",
|
||||||
"To Decimal",
|
"To Decimal",
|
||||||
|
|
65
src/core/lib/Modhex.mjs
Normal file
65
src/core/lib/Modhex.mjs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/**
|
||||||
|
* Modhex helpers.
|
||||||
|
*
|
||||||
|
* @author linuxgemini [ilteris@asenkron.com.tr]
|
||||||
|
* @copyright Crown Copyright 2020
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modhex to Hex conversion map.
|
||||||
|
*/
|
||||||
|
export const MODHEX_TO_HEX_CONVERSION_MAP = {
|
||||||
|
"c": "0",
|
||||||
|
"b": "1",
|
||||||
|
"d": "2",
|
||||||
|
"e": "3",
|
||||||
|
"f": "4",
|
||||||
|
"g": "5",
|
||||||
|
"h": "6",
|
||||||
|
"i": "7",
|
||||||
|
"j": "8",
|
||||||
|
"k": "9",
|
||||||
|
"l": "a",
|
||||||
|
"n": "b",
|
||||||
|
"r": "c",
|
||||||
|
"t": "d",
|
||||||
|
"u": "e",
|
||||||
|
"v": "f"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hex to Modhex conversion map.
|
||||||
|
*/
|
||||||
|
export const HEX_TO_MODHEX_CONVERSION_MAP = {
|
||||||
|
"0": "c",
|
||||||
|
"1": "b",
|
||||||
|
"2": "d",
|
||||||
|
"3": "e",
|
||||||
|
"4": "f",
|
||||||
|
"5": "g",
|
||||||
|
"6": "h",
|
||||||
|
"7": "i",
|
||||||
|
"8": "j",
|
||||||
|
"9": "k",
|
||||||
|
"a": "l",
|
||||||
|
"b": "n",
|
||||||
|
"c": "r",
|
||||||
|
"d": "t",
|
||||||
|
"e": "u",
|
||||||
|
"f": "v"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To Modhex delimiters.
|
||||||
|
*/
|
||||||
|
export const TO_MODHEX_DELIM_OPTIONS = ["Space", "Percent", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "None"];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From Modhex delimiters.
|
||||||
|
*/
|
||||||
|
export const FROM_MODHEX_DELIM_OPTIONS = ["Auto"].concat(TO_MODHEX_DELIM_OPTIONS);
|
123
src/core/operations/FromModhex.mjs
Normal file
123
src/core/operations/FromModhex.mjs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/**
|
||||||
|
* @author linuxgemini [ilteris@asenkron.com.tr]
|
||||||
|
* @copyright Crown Copyright 2020
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import { FROM_MODHEX_DELIM_OPTIONS, MODHEX_TO_HEX_CONVERSION_MAP } from "../lib/Modhex.mjs";
|
||||||
|
import { fromHex } from "../lib/Hex.mjs";
|
||||||
|
import Utils from "../Utils.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From Modhex operation
|
||||||
|
*/
|
||||||
|
class FromModhex extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FromModhex constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "From Modhex";
|
||||||
|
this.module = "Default";
|
||||||
|
this.description = "Converts a modhex byte string back into its raw value.";
|
||||||
|
this.infoURL = "https://en.wikipedia.org/wiki/YubiKey#ModHex";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "byteArray";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: FROM_MODHEX_DELIM_OPTIONS
|
||||||
|
}
|
||||||
|
];
|
||||||
|
this.checks = [
|
||||||
|
{
|
||||||
|
pattern: "^(?:[cbdefghijklnrtuv]{2})+$",
|
||||||
|
flags: "i",
|
||||||
|
args: ["None"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "^[cbdefghijklnrtuv]{2}(?: [cbdefghijklnrtuv]{2})*$",
|
||||||
|
flags: "i",
|
||||||
|
args: ["Space"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "^[cbdefghijklnrtuv]{2}(?:,[cbdefghijklnrtuv]{2})*$",
|
||||||
|
flags: "i",
|
||||||
|
args: ["Comma"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "^[cbdefghijklnrtuv]{2}(?:;[cbdefghijklnrtuv]{2})*$",
|
||||||
|
flags: "i",
|
||||||
|
args: ["Semi-colon"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "^[cbdefghijklnrtuv]{2}(?::[cbdefghijklnrtuv]{2})*$",
|
||||||
|
flags: "i",
|
||||||
|
args: ["Colon"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "^[cbdefghijklnrtuv]{2}(?:\\n[cbdefghijklnrtuv]{2})*$",
|
||||||
|
flags: "i",
|
||||||
|
args: ["Line feed"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: "^[cbdefghijklnrtuv]{2}(?:\\r\\n[cbdefghijklnrtuv]{2})*$",
|
||||||
|
flags: "i",
|
||||||
|
args: ["CRLF"]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a hex string into a byte array.
|
||||||
|
*
|
||||||
|
* @param {string} data
|
||||||
|
* @param {string} [delim]
|
||||||
|
* @param {number} [byteLen=2]
|
||||||
|
* @returns {byteArray}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // returns [10,20,30]
|
||||||
|
* fromModhex("cl bf bu");
|
||||||
|
*
|
||||||
|
* // returns [10,20,30]
|
||||||
|
* fromModhex("cl:bf:bu", "Colon");
|
||||||
|
*/
|
||||||
|
fromModhex(data, delim="Auto", byteLen=2) {
|
||||||
|
if (!data || data.length === 0) return [];
|
||||||
|
|
||||||
|
data = data.toLowerCase();
|
||||||
|
|
||||||
|
if (delim !== "None") {
|
||||||
|
const delimRegex = delim === "Auto" ? /[^cbdefghijklnrtuv]/gi : Utils.regexRep(delim);
|
||||||
|
data = data.replace(delimRegex, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
data = data.replace(/\s/g, "");
|
||||||
|
|
||||||
|
let hexconv = "";
|
||||||
|
for (const letter of data.split("")) {
|
||||||
|
hexconv += MODHEX_TO_HEX_CONVERSION_MAP[letter];
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = fromHex(hexconv, "None", byteLen);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {byteArray}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const delim = args[0] || "Auto";
|
||||||
|
return this.fromModhex(input, delim, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FromModhex;
|
110
src/core/operations/ToModhex.mjs
Normal file
110
src/core/operations/ToModhex.mjs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/**
|
||||||
|
* @author linuxgemini [ilteris@asenkron.com.tr]
|
||||||
|
* @copyright Crown Copyright 2020
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import { TO_MODHEX_DELIM_OPTIONS, HEX_TO_MODHEX_CONVERSION_MAP } from "../lib/Modhex.mjs";
|
||||||
|
import { toHex } from "../lib/Hex.mjs";
|
||||||
|
import Utils from "../Utils.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To Modhex operation
|
||||||
|
*/
|
||||||
|
class ToModhex extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ToModhex constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "To Modhex";
|
||||||
|
this.module = "Default";
|
||||||
|
this.description = "Converts the input string to modhex bytes separated by the specified delimiter.";
|
||||||
|
this.infoURL = "https://en.wikipedia.org/wiki/YubiKey#ModHex";
|
||||||
|
this.inputType = "ArrayBuffer";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: TO_MODHEX_DELIM_OPTIONS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Bytes per line",
|
||||||
|
type: "number",
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a byte array into a modhex string.
|
||||||
|
*
|
||||||
|
* @param {byteArray|Uint8Array|ArrayBuffer} data
|
||||||
|
* @param {string} [delim=" "]
|
||||||
|
* @param {number} [padding=2]
|
||||||
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // returns "cl bf bu"
|
||||||
|
* toModhex([10,20,30]);
|
||||||
|
*
|
||||||
|
* // returns "cl:bf:bu"
|
||||||
|
* toModhex([10,20,30], ":");
|
||||||
|
*/
|
||||||
|
toModhex(data, delim=" ", padding=2, extraDelim="", lineSize=0) {
|
||||||
|
if (!data || data.length === 0) return "";
|
||||||
|
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
||||||
|
|
||||||
|
const hexconv = toHex(data, "", padding, "", 0);
|
||||||
|
let modhexconv = "";
|
||||||
|
let output = "";
|
||||||
|
|
||||||
|
for (const letter of hexconv.split("")) {
|
||||||
|
modhexconv += HEX_TO_MODHEX_CONVERSION_MAP[letter];
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupedModhex = modhexconv.match(/.{1,2}/g);
|
||||||
|
|
||||||
|
for (let i = 0; i < groupedModhex.length; i++) {
|
||||||
|
const group = groupedModhex[i];
|
||||||
|
output += group + delim;
|
||||||
|
|
||||||
|
if (extraDelim) {
|
||||||
|
output += extraDelim;
|
||||||
|
}
|
||||||
|
// Add LF after each lineSize amount of bytes but not at the end
|
||||||
|
if ((i !== groupedModhex.length - 1) && ((i + 1) % lineSize === 0)) {
|
||||||
|
output += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the extraDelim at the end (if there is one)
|
||||||
|
// and remove the delim at the end, but if it's prepended there's nothing to remove
|
||||||
|
const rTruncLen = extraDelim.length + delim.length;
|
||||||
|
if (rTruncLen) {
|
||||||
|
// If rTruncLen === 0 then output.slice(0,0) will be returned, which is nothing
|
||||||
|
return output.slice(0, -rTruncLen);
|
||||||
|
} else {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ArrayBuffer} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const delim = Utils.charRep(args[0]);
|
||||||
|
const lineSize = args[1];
|
||||||
|
|
||||||
|
return this.toModhex(new Uint8Array(input), delim, 2, "", lineSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToModhex;
|
|
@ -66,6 +66,7 @@ import "./tests/JWTSign.mjs";
|
||||||
import "./tests/JWTVerify.mjs";
|
import "./tests/JWTVerify.mjs";
|
||||||
import "./tests/MS.mjs";
|
import "./tests/MS.mjs";
|
||||||
import "./tests/Magic.mjs";
|
import "./tests/Magic.mjs";
|
||||||
|
import "./tests/Modhex.mjs";
|
||||||
import "./tests/MorseCode.mjs";
|
import "./tests/MorseCode.mjs";
|
||||||
import "./tests/NetBIOS.mjs";
|
import "./tests/NetBIOS.mjs";
|
||||||
import "./tests/NormaliseUnicode.mjs";
|
import "./tests/NormaliseUnicode.mjs";
|
||||||
|
|
143
tests/operations/tests/Modhex.mjs
Normal file
143
tests/operations/tests/Modhex.mjs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "ASCII to Modhex stream",
|
||||||
|
input: "aberystwyth",
|
||||||
|
expectedOutput: "hbhdhgidikieifiiikifhj",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "To Modhex",
|
||||||
|
"args": [
|
||||||
|
"None",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ASCII to Modhex with colon deliminator",
|
||||||
|
input: "aberystwyth",
|
||||||
|
expectedOutput: "hb:hd:hg:id:ik:ie:if:ii:ik:if:hj",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "To Modhex",
|
||||||
|
"args": [
|
||||||
|
"Colon",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modhex stream to UTF-8",
|
||||||
|
input: "uhkgkbuhkgkbugltlkugltkc",
|
||||||
|
expectedOutput: "救救孩子",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "From Modhex",
|
||||||
|
"args": [
|
||||||
|
"Auto"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mixed case Modhex stream to UTF-8",
|
||||||
|
input: "uhKGkbUHkgkBUGltlkugltkc",
|
||||||
|
expectedOutput: "救救孩子",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "From Modhex",
|
||||||
|
"args": [
|
||||||
|
"Auto"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mutiline Modhex with comma to ASCII (Auto Mode)",
|
||||||
|
input: "fk,dc,ie,hb,ii,dc,ht,ik,ie,hg,hr,hh,dc,ie,hk,\n\
|
||||||
|
if,if,hk,hu,hi,dc,hk,hu,dc,if,hj,hg,dc,he,id,\n\
|
||||||
|
hv,if,he,hj,dc,hv,hh,dc,if,hj,hg,dc,if,hj,hk,\n\
|
||||||
|
ie,dc,hh,hk,hi,dc,if,id,hg,hg,dr,dc,ie,if,hb,\n\
|
||||||
|
id,ih,hk,hu,hi,dc,if,hv,dc,hf,hg,hb,if,hj,dr,\n\
|
||||||
|
dc,hl,ig,ie,if,dc,hd,hg,he,hb,ig,ie,hg,dc,fk,\n\
|
||||||
|
dc,he,hv,ig,hr,hf,hu,di,if,dc,ht,hb,hn,hg,dc,\n\
|
||||||
|
ig,ic,dc,ht,ik,dc,ht,hk,hu,hf,dc,ii,hj,hk,he,\n\
|
||||||
|
hj,dc,hv,hh,dc,if,hj,hg,dc,hh,hk,hi,ie,dc,fk,\n\
|
||||||
|
dc,ii,hv,ig,hr,hf,dc,he,hj,hv,hv,ie,hg,du",
|
||||||
|
expectedOutput: "I saw myself sitting in the crotch of the this fig tree, starving to death, just because I couldn't make up my mind which of the figs I would choose.",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "From Modhex",
|
||||||
|
"args": [
|
||||||
|
"Auto"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mutiline Modhex with percent to ASCII (Percent Mode)",
|
||||||
|
input: "fk%dc%ie%hb%ii%dc%ht%ik%ie%hg%hr%hh%dc%ie%hk%\n\
|
||||||
|
if%if%hk%hu%hi%dc%hk%hu%dc%if%hj%hg%dc%he%id%\n\
|
||||||
|
hv%if%he%hj%dc%hv%hh%dc%if%hj%hg%dc%if%hj%hk%\n\
|
||||||
|
ie%dc%hh%hk%hi%dc%if%id%hg%hg%dr%dc%ie%if%hb%\n\
|
||||||
|
id%ih%hk%hu%hi%dc%if%hv%dc%hf%hg%hb%if%hj%dr%\n\
|
||||||
|
dc%hl%ig%ie%if%dc%hd%hg%he%hb%ig%ie%hg%dc%fk%\n\
|
||||||
|
dc%he%hv%ig%hr%hf%hu%di%if%dc%ht%hb%hn%hg%dc%\n\
|
||||||
|
ig%ic%dc%ht%ik%dc%ht%hk%hu%hf%dc%ii%hj%hk%he%\n\
|
||||||
|
hj%dc%hv%hh%dc%if%hj%hg%dc%hh%hk%hi%ie%dc%fk%\n\
|
||||||
|
dc%ii%hv%ig%hr%hf%dc%he%hj%hv%hv%ie%hg%du",
|
||||||
|
expectedOutput: "I saw myself sitting in the crotch of the this fig tree, starving to death, just because I couldn't make up my mind which of the figs I would choose.",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "From Modhex",
|
||||||
|
"args": [
|
||||||
|
"Percent"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mutiline Modhex with semicolon to ASCII (Semi-colon Mode)",
|
||||||
|
input: "fk;dc;ie;hb;ii;dc;ht;ik;ie;hg;hr;hh;dc;ie;hk;\n\
|
||||||
|
if;if;hk;hu;hi;dc;hk;hu;dc;if;hj;hg;dc;he;id;\n\
|
||||||
|
hv;if;he;hj;dc;hv;hh;dc;if;hj;hg;dc;if;hj;hk;\n\
|
||||||
|
ie;dc;hh;hk;hi;dc;if;id;hg;hg;dr;dc;ie;if;hb;\n\
|
||||||
|
id;ih;hk;hu;hi;dc;if;hv;dc;hf;hg;hb;if;hj;dr;\n\
|
||||||
|
dc;hl;ig;ie;if;dc;hd;hg;he;hb;ig;ie;hg;dc;fk;\n\
|
||||||
|
dc;he;hv;ig;hr;hf;hu;di;if;dc;ht;hb;hn;hg;dc;\n\
|
||||||
|
ig;ic;dc;ht;ik;dc;ht;hk;hu;hf;dc;ii;hj;hk;he;\n\
|
||||||
|
hj;dc;hv;hh;dc;if;hj;hg;dc;hh;hk;hi;ie;dc;fk;\n\
|
||||||
|
dc;ii;hv;ig;hr;hf;dc;he;hj;hv;hv;ie;hg;du",
|
||||||
|
expectedOutput: "I saw myself sitting in the crotch of the this fig tree, starving to death, just because I couldn't make up my mind which of the figs I would choose.",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "From Modhex",
|
||||||
|
"args": [
|
||||||
|
"Semi-colon"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ASCII to Modhex with comma and line breaks",
|
||||||
|
input: "aberystwyth",
|
||||||
|
expectedOutput: "hb,hd,hg,id,\nik,ie,if,ii,\nik,if,hj",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "To Modhex",
|
||||||
|
"args": [
|
||||||
|
"Comma",
|
||||||
|
4
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]);
|
Loading…
Add table
Add a link
Reference in a new issue