Skip to content

Commit

Permalink
Merge pull request #33 from linuxgemini/linuxgemini-patch-modhex
Browse files Browse the repository at this point in the history
feat(Modhex): Introduce basic Modhex conversion
  • Loading branch information
bee-san authored Dec 22, 2023
2 parents 73716a2 + 758ff9b commit 4c145c5
Show file tree
Hide file tree
Showing 6 changed files with 444 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"From Hexdump",
"To Hex",
"From Hex",
"To Modhex",
"From Modhex",
"To Charcode",
"From Charcode",
"To Decimal",
Expand Down
65 changes: 65 additions & 0 deletions src/core/lib/Modhex.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Modhex helpers.
*
* @author linuxgemini [[email protected]]
* @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 changes: 123 additions & 0 deletions src/core/operations/FromModhex.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* @author linuxgemini [[email protected]]
* @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 changes: 110 additions & 0 deletions src/core/operations/ToModhex.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* @author linuxgemini [[email protected]]
* @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;
1 change: 1 addition & 0 deletions tests/operations/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading

0 comments on commit 4c145c5

Please sign in to comment.