mirror of
https://github.com/gchq/CyberChef.git
synced 2025-05-10 00:05:11 -04:00
add base91 support
This commit is contained in:
parent
c9d9730726
commit
4b235e1166
5 changed files with 242 additions and 0 deletions
|
@ -29,6 +29,8 @@
|
|||
"From Base62",
|
||||
"To Base85",
|
||||
"From Base85",
|
||||
"To Base91",
|
||||
"From Base91",
|
||||
"To Base",
|
||||
"From Base",
|
||||
"To BCD",
|
||||
|
|
126
src/core/lib/Base91.mjs
Normal file
126
src/core/lib/Base91.mjs
Normal file
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* Base91 resources.
|
||||
*
|
||||
* @author idevlab [idevlab@outlook.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Base91 alphabet options.
|
||||
*/
|
||||
export const ALPHABET_OPTIONS = [
|
||||
{
|
||||
name: "Standard",
|
||||
value: "A-Za-z0-9!#$%&()*+,./:;<=>?@[]^_`{|}~\"",
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Base91's the input byte array using the given alphabet, returning a string.
|
||||
*
|
||||
* @param {byteArray|Uint8Array|ArrayBuffer|string} data
|
||||
* @param {string} alphabet
|
||||
* @returns {string}
|
||||
*
|
||||
*/
|
||||
export function toBase91(data, alphabet) {
|
||||
if (!data) return "";
|
||||
if (data instanceof ArrayBuffer) {
|
||||
data = new Uint8Array(data);
|
||||
}
|
||||
if (typeof data == "string") {
|
||||
data = Utils.strToByteArray(data);
|
||||
}
|
||||
|
||||
alphabet = Utils.expandAlphRange(alphabet).join("");
|
||||
if (alphabet.length !== 91 && alphabet.length !== 92) { // Allow for padding
|
||||
throw new OperationError(`Invalid Base91 alphabet length (${alphabet.length}): ${alphabet}`);
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
throw new OperationError("base91: Missing data to encode.");
|
||||
}
|
||||
const raw = Buffer.isBuffer(data) ? data : typeof data === "number" ? Buffer.from(data.toString()) : Buffer.from(data);
|
||||
let ret = "";
|
||||
|
||||
let n = 0;
|
||||
let b = 0;
|
||||
|
||||
for (let i = 0; i < raw.length; i++) {
|
||||
b |= raw[i] << n;
|
||||
n += 8;
|
||||
|
||||
if (n > 13) {
|
||||
let v = b & 8191;
|
||||
if (v > 88) {
|
||||
b >>= 13;
|
||||
n -= 13;
|
||||
} else {
|
||||
v = b & 16383;
|
||||
b >>= 14;
|
||||
n -= 14;
|
||||
}
|
||||
ret += alphabet[v % 91] + alphabet[v / 91 | 0];
|
||||
}
|
||||
}
|
||||
|
||||
if (n) {
|
||||
ret += alphabet[b % 91];
|
||||
if (n > 7 || b > 90) ret += alphabet[b / 91 | 0];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UnBase91's the input string using the given alphabet, returning a byte array.
|
||||
*
|
||||
* @param {string} data
|
||||
* @param {string} alphabet
|
||||
* @param {string} [returnType="string"] - Either "string" or "byteArray"
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
export function fromBase91(data, alphabet, returnType) {
|
||||
|
||||
if (!data) {
|
||||
return returnType === "string" ? "" : [];
|
||||
}
|
||||
|
||||
alphabet = alphabet || "A-Za-z0-9+/=";
|
||||
alphabet = Utils.expandAlphRange(alphabet).join("");
|
||||
if (alphabet.length !== 91 && alphabet.length !== 92) // Allow for padding
|
||||
throw new OperationError(`Invalid Base91 alphabet length (${alphabet.length}): ${alphabet}`);
|
||||
|
||||
const raw = "" + (data || "");
|
||||
|
||||
let b = 0;
|
||||
let n = 0;
|
||||
let v = -1;
|
||||
const output = [];
|
||||
for (let i = 0; i < raw.length; i++) {
|
||||
const p = alphabet.indexOf(raw[i]);
|
||||
if (p === -1) continue;
|
||||
if (v < 0) {
|
||||
v = p;
|
||||
} else {
|
||||
v += p * 91;
|
||||
b |= v << n;
|
||||
n += (v & 8191) > 88 ? 13 : 14;
|
||||
do {
|
||||
output.push(b & 0xff);
|
||||
b >>= 8;
|
||||
n -= 8;
|
||||
} while (n > 7);
|
||||
v = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (v > -1)
|
||||
output.push((b | v << n) & 0xff);
|
||||
|
||||
return returnType === "string" ? Utils.byteArrayToUtf8(output) : output;
|
||||
}
|
46
src/core/operations/FromBase91.mjs
Normal file
46
src/core/operations/FromBase91.mjs
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @author idevlab [idevlab@outlook.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import { ALPHABET_OPTIONS, fromBase91 } from "../lib/Base91.mjs";
|
||||
/**
|
||||
* From Base91 operation
|
||||
*/
|
||||
class FromBase91 extends Operation {
|
||||
|
||||
/**
|
||||
* FromBase91 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "From Base91";
|
||||
this.module = "Default";
|
||||
this.description = "BasE91 is an encoding method that uses ASCII characters. Similar to base64, it has the advantage of limiting the size of the encoded data and to be used as a cipher.";
|
||||
this.infoURL = "http://base91.sourceforge.net/";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [
|
||||
{
|
||||
name: "Alphabet",
|
||||
type: "editableOption",
|
||||
value: ALPHABET_OPTIONS
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [alphabet] = args;
|
||||
return fromBase91(input, alphabet, "byteArray");
|
||||
}
|
||||
}
|
||||
|
||||
export default FromBase91;
|
46
src/core/operations/ToBase91.mjs
Normal file
46
src/core/operations/ToBase91.mjs
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @author idevlab [idevlab@outlook.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import { ALPHABET_OPTIONS, toBase91 } from "../lib/Base91.mjs";
|
||||
/**
|
||||
* To Base91 operation
|
||||
*/
|
||||
class ToBase91 extends Operation {
|
||||
|
||||
/**
|
||||
* ToBase91 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "To Base91";
|
||||
this.module = "Default";
|
||||
this.description = "BasE91 is an encoding method that uses ASCII characters. Similar to base64, it has the advantage of limiting the size of the encoded data and to be used as a cipher.";
|
||||
this.infoURL = "http://base91.sourceforge.net/";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Alphabet",
|
||||
type: "editableOption",
|
||||
value: ALPHABET_OPTIONS
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const alphabet = args[0];
|
||||
return toBase91(input, alphabet);
|
||||
}
|
||||
}
|
||||
|
||||
export default ToBase91;
|
22
tests/operations/tests/Base91.mjs
Normal file
22
tests/operations/tests/Base91.mjs
Normal file
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* Base64 tests.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "From Base91: First",
|
||||
input: "lM_1Z<jNG",
|
||||
expectedOutput: "idevlab",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Base64"
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
Loading…
Add table
Add a link
Reference in a new issue