mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-22 15:56:16 -04:00
Add Base94 encode and decode operations
Base94 is a notation for encoding arbitrary byte data using a restricted set of symbols and is found primarily in the finance/ATM technology space.
This commit is contained in:
parent
22fe5a6ae7
commit
16577751eb
4 changed files with 340 additions and 0 deletions
|
@ -29,6 +29,8 @@
|
||||||
"Show Base64 offsets",
|
"Show Base64 offsets",
|
||||||
"To Base85",
|
"To Base85",
|
||||||
"From Base85",
|
"From Base85",
|
||||||
|
"To Base94",
|
||||||
|
"From Base94",
|
||||||
"To Base",
|
"To Base",
|
||||||
"From Base",
|
"From Base",
|
||||||
"To BCD",
|
"To BCD",
|
||||||
|
|
178
src/core/lib/Base94.mjs
Normal file
178
src/core/lib/Base94.mjs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/**
|
||||||
|
* Base94 functions.
|
||||||
|
*
|
||||||
|
* @author sganson@trustedsecurity.com]
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Utils from "../Utils.mjs";
|
||||||
|
import OperationError from "../errors/OperationError.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base94's the input byte array, returning a string.
|
||||||
|
* Every four bytes of input are converted to five bytes of
|
||||||
|
* Base94 encoded output.
|
||||||
|
*
|
||||||
|
* @param {ArrayBuffer} data
|
||||||
|
* @param {boolean} [strictLength="true"]
|
||||||
|
* @returns {string}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // returns "@Z<[+/- >5$@3z&T!Qh*|F.q+ZWIz&#J<[+][[4+trr# "
|
||||||
|
* // toBase94([48, 65, 6c, 6c, 6f, 20, 57, 6f, 72, 6c, 64, 21]);
|
||||||
|
* // e.g. toBase94(ToHex("Hello World!"))
|
||||||
|
*/
|
||||||
|
export function toBase94(data, strictLength=true) {
|
||||||
|
|
||||||
|
if (!data) return "";
|
||||||
|
|
||||||
|
if (data instanceof ArrayBuffer) {
|
||||||
|
data = new Uint8Array(data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new OperationError(`Invalid - Input not instanceof ArrayBuffer.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dataModLen = data.length % 4;
|
||||||
|
|
||||||
|
if (dataModLen > 0 && strictLength)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new OperationError(`Invalid - Input byte length must be a multiple of 4.`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = "", i = 0, j = 0, acc = 0;
|
||||||
|
|
||||||
|
let dataPad = new Uint8Array(data.length + (dataModLen > 0 ? (4 - dataModLen) : 0));
|
||||||
|
|
||||||
|
dataPad.set(data,0);
|
||||||
|
|
||||||
|
while (i < dataPad.length) {
|
||||||
|
|
||||||
|
acc = 0;
|
||||||
|
|
||||||
|
for(j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
|
||||||
|
acc *= 256;
|
||||||
|
|
||||||
|
acc += dataPad[i + (3 - j)];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
|
||||||
|
output += String.fromCharCode((acc % 94)+32);
|
||||||
|
|
||||||
|
acc = Math.floor(acc / 94);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 4;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un-Base94's the input string, returning a byte array.
|
||||||
|
* Every five bytes of Base94 encoded input are converted to
|
||||||
|
* four bytes of output.
|
||||||
|
*
|
||||||
|
* @param {string} data // Base94 encoded string
|
||||||
|
* @param {boolean} [strictLength="true"]
|
||||||
|
* @param {boolean} [removeInvalidChars="false"]
|
||||||
|
* @returns {byteArray}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // returns [48, 65, 6c, 6c, 6f, 20, 57, 6f, 72, 6c, 64, 21]
|
||||||
|
* // fromBase94("@Z<[+/- >5$@3z&T!Qh*|F.q+ZWIz&#J<[+][[4+trr# ", true, true);
|
||||||
|
* // e.g. fromHex(fromBase94(....)); -> Hello World!
|
||||||
|
*/
|
||||||
|
export function fromBase94(data, strictLength=true, removeInvalidChars=false) {
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data == "string") {
|
||||||
|
|
||||||
|
data = Utils.strToByteArray(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
throw new OperationError(`Invalid - typeof base94 input is not a string.`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const re = new RegExp("[^\x20-\x7e]", "g");
|
||||||
|
|
||||||
|
if(re.test(data))
|
||||||
|
{
|
||||||
|
if (removeInvalidChars) {
|
||||||
|
data = data.replace(re, "");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new OperationError(`Invalid content in Base94 string.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stringModLen = data.length % 5;
|
||||||
|
|
||||||
|
if (stringModLen > 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(strictLength)
|
||||||
|
{
|
||||||
|
throw new OperationError(`Invalid - Input string length must be a multiple of 5.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
stringModLen = 5 - stringModLen;
|
||||||
|
|
||||||
|
while(stringModLen > 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
data.push(32);
|
||||||
|
|
||||||
|
stringModLen -= 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = [], i = 0, j = 0, acc = 0;
|
||||||
|
|
||||||
|
while (i < data.length) {
|
||||||
|
|
||||||
|
acc = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
|
||||||
|
acc = (acc * 94) + data[i + 4 - j] - 32;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
|
||||||
|
output.push(acc % 256);
|
||||||
|
|
||||||
|
acc = Math.floor(acc / 256);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 5;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
|
||||||
|
}
|
84
src/core/operations/FromBase94.mjs
Normal file
84
src/core/operations/FromBase94.mjs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* @author sganson@trustedsecurity.com]
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import {fromBase94} from "../lib/Base94.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From Base94 operation
|
||||||
|
*/
|
||||||
|
class FromBase94 extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FromBase94 constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "From Base94";
|
||||||
|
this.module = "Default";
|
||||||
|
this.description = "Base94 is a notation for encoding arbitrary byte data using a restricted set of symbols and is found primarily in the finance/ATM technology space.<br/><br/>This operation decodes an ASCII Base94 string returning a byteArray.<br/><br/>e.g. <code>@Z<[+/- >5$@3z&T!Qh*|F.q+ZWIz&#J<[+][[4+trr# </code> becomes <code>[48, 65, 6c, 6c, 6f, 20, 57, 6f, 72, 6c, 64, 21]</code><br/><br/>This is a no frills, no soft toilet paper implementation. It's string in, byteArray out.<br/><br/>By default, input length is expected to by a multiple of 5. Unchecking 'Strict length' will pad non mod 5 length input with space(s).<br/><br/>Base94 encoded content is expected to be in ASCII range '0x20 thru 0x7e'. Leaving 'Remove Invalid Chars' unchecked will enforce this.";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "byteArray";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Strict length",
|
||||||
|
type: "boolean",
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Remove Invalid Chars",
|
||||||
|
type: "boolean",
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {byteArray}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
|
||||||
|
const [strictLength,removeInvalidChars] = args;
|
||||||
|
|
||||||
|
return fromBase94(input, strictLength, removeInvalidChars);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight to Base94
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlight(pos, args) {
|
||||||
|
pos[0].start = Math.ceil(pos[0].start / 4 * 5);
|
||||||
|
pos[0].end = Math.floor(pos[0].end / 4 * 5);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight from Base94
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlightReverse(pos, args) {
|
||||||
|
pos[0].start = Math.floor(pos[0].start / 5 * 4);
|
||||||
|
pos[0].end = Math.ceil(pos[0].end / 5 * 4);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FromBase94;
|
76
src/core/operations/ToBase94.mjs
Normal file
76
src/core/operations/ToBase94.mjs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* @author sganson@trustedsecurity.com]
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import {toBase94} from "../lib/Base94.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To Base64 operation
|
||||||
|
*/
|
||||||
|
class ToBase94 extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ToBase94 constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "To Base94";
|
||||||
|
this.module = "Default";
|
||||||
|
this.description = "Base94 is a notation for encoding arbitrary byte data using a restricted set of symbols and is found primarily in the finance/ATM technology space.<br/><br/>This operation encodes raw data into an ASCII Base94 string.<br/><br/>e.g. <code>[48, 65, 6c, 6c, 6f, 20, 57, 6f, 72, 6c, 64, 21]</code> becomes <code>@Z<[+/- >5$@3z&T!Qh*|F.q+ZWIz&#J<[+][[4+trr# </code><br/><br/>This is a no frills, no soft toilet paper implementation. It's ArrayBuffer in, string out.<br/><br/>By default, input length is expected to by a multiple of 4. Unchecking 'Strict length' will pad non mod 4 length input with zero(es).";
|
||||||
|
this.inputType = "ArrayBuffer";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Strict length",
|
||||||
|
type: "boolean",
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ArrayBuffer} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const [strictLength] = args;
|
||||||
|
return toBase94(input,strictLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight to Base94
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlight(pos, args) {
|
||||||
|
pos[0].start = Math.floor(pos[0].start / 4 * 5);
|
||||||
|
pos[0].end = Math.ceil(pos[0].end / 4 * 5);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight from Base94
|
||||||
|
*
|
||||||
|
* @param {Object[]} pos
|
||||||
|
* @param {number} pos[].start
|
||||||
|
* @param {number} pos[].end
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {Object[]} pos
|
||||||
|
*/
|
||||||
|
highlightReverse(pos, args) {
|
||||||
|
pos[0].start = Math.ceil(pos[0].start / 5 * 4);
|
||||||
|
pos[0].end = Math.floor(pos[0].end / 5 * 4);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToBase94;
|
Loading…
Add table
Add a link
Reference in a new issue