Merge pull request #33 from linuxgemini/linuxgemini-patch-modhex

feat(Modhex): Introduce basic Modhex conversion
This commit is contained in:
Autumn 2023-12-22 18:37:57 +00:00 committed by GitHub
commit 4c145c50dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 444 additions and 0 deletions

View File

@ -10,6 +10,8 @@
"From Hexdump",
"To Hex",
"From Hex",
"To Modhex",
"From Modhex",
"To Charcode",
"From Charcode",
"To Decimal",

65
src/core/lib/Modhex.mjs Normal file
View 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);

View 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;

View 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;

View File

@ -66,6 +66,7 @@ import "./tests/JWTSign.mjs";
import "./tests/JWTVerify.mjs";
import "./tests/MS.mjs";
import "./tests/Magic.mjs";
import "./tests/Modhex.mjs";
import "./tests/MorseCode.mjs";
import "./tests/NetBIOS.mjs";
import "./tests/NormaliseUnicode.mjs";

View 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
]
}
]
},
]);