This commit is contained in:
Sebastian Ganson 2024-05-03 11:01:46 +01:00 committed by GitHub
commit 245b8e19dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 326 additions and 0 deletions

View File

@ -35,6 +35,8 @@
"From Base92",
"To Base85",
"From Base85",
"To Base94",
"From Base94",
"To Base",
"From Base",
"To BCD",

164
src/core/lib/Base94.mjs Normal file
View File

@ -0,0 +1,164 @@
/**
* 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.`);
}
const 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;
const 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;
}
}
const output = [];
let 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;
}

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

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