mirror of
https://github.com/gchq/CyberChef
synced 2025-12-10 05:13:23 +00:00
166 lines
4.3 KiB
JavaScript
166 lines
4.3 KiB
JavaScript
/**
|
|
* @author linuxgemini [ilteris@asenkron.com.tr]
|
|
* @copyright Crown Copyright 2024
|
|
* @license Apache-2.0
|
|
*/
|
|
|
|
import Utils from "../Utils.mjs";
|
|
import OperationError from "../errors/OperationError.mjs";
|
|
import { fromHex, toHex } from "./Hex.mjs";
|
|
|
|
/**
|
|
* Modhex alphabet.
|
|
*/
|
|
const MODHEX_ALPHABET = "cbdefghijklnrtuv";
|
|
|
|
|
|
/**
|
|
* Modhex alphabet map.
|
|
*/
|
|
const MODHEX_ALPHABET_MAP = MODHEX_ALPHABET.split("");
|
|
|
|
|
|
/**
|
|
* Hex alphabet to substitute Modhex.
|
|
*/
|
|
const HEX_ALPHABET = "0123456789abcdef";
|
|
|
|
|
|
/**
|
|
* Hex alphabet map to substitute Modhex.
|
|
*/
|
|
const HEX_ALPHABET_MAP = HEX_ALPHABET.split("");
|
|
|
|
|
|
/**
|
|
* 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], ":");
|
|
*/
|
|
export function toModhex(data, delim=" ", padding=2, extraDelim="", lineSize=0) {
|
|
if (!data) return "";
|
|
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
|
|
|
const regularHexString = toHex(data, "", padding, "", 0);
|
|
|
|
let modhexString = "";
|
|
for (const letter of regularHexString.split("")) {
|
|
modhexString += MODHEX_ALPHABET_MAP[HEX_ALPHABET_MAP.indexOf(letter)];
|
|
}
|
|
|
|
let output = "";
|
|
const groupingRegexp = new RegExp(`.{1,${padding}}`, "g");
|
|
const groupedModhex = modhexString.match(groupingRegexp);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert a byte array into a modhex string as efficiently as possible with no options.
|
|
*
|
|
* @param {byteArray|Uint8Array|ArrayBuffer} data
|
|
* @returns {string}
|
|
*
|
|
* @example
|
|
* // returns "clbfbu"
|
|
* toModhexFast([10,20,30]);
|
|
*/
|
|
export function toModhexFast(data) {
|
|
if (!data) return "";
|
|
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
|
|
|
const output = [];
|
|
|
|
for (let i = 0; i < data.length; i++) {
|
|
output.push(MODHEX_ALPHABET_MAP[(data[i] >> 4) & 0xf]);
|
|
output.push(MODHEX_ALPHABET_MAP[data[i] & 0xf]);
|
|
}
|
|
return output.join("");
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert a modhex 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");
|
|
*/
|
|
export function fromModhex(data, delim="Auto", byteLen=2) {
|
|
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
|
|
throw new OperationError("Byte length must be a positive integer");
|
|
|
|
// The `.replace(/\s/g, "")` an interesting workaround: Hex "multiline" tests aren't actually
|
|
// multiline. Tests for Modhex fixes that, thus exposing the issue.
|
|
data = data.toLowerCase().replace(/\s/g, "");
|
|
|
|
if (delim !== "None") {
|
|
const delimRegex = delim === "Auto" ? /[^cbdefghijklnrtuv]/gi : Utils.regexRep(delim);
|
|
data = data.split(delimRegex);
|
|
} else {
|
|
data = [data];
|
|
}
|
|
|
|
let regularHexString = "";
|
|
for (let i = 0; i < data.length; i++) {
|
|
for (const letter of data[i].split("")) {
|
|
regularHexString += HEX_ALPHABET_MAP[MODHEX_ALPHABET_MAP.indexOf(letter)];
|
|
}
|
|
}
|
|
|
|
const output = fromHex(regularHexString, "None", byteLen);
|
|
return output;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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);
|