From 2d9e8773f5dd1c6757a3b1e418eb9c7ef6fbbf72 Mon Sep 17 00:00:00 2001 From: George J Date: Wed, 22 Aug 2018 20:24:32 +0100 Subject: [PATCH 1/2] Updated Base85 operations for latest CyberChef version --- src/core/config/Categories.json | 2 + src/core/lib/Base85.mjs | 45 +++++++++++++ src/core/operations/FromBase85.mjs | 104 +++++++++++++++++++++++++++++ src/core/operations/ToBase85.mjs | 93 ++++++++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 src/core/lib/Base85.mjs create mode 100644 src/core/operations/FromBase85.mjs create mode 100644 src/core/operations/ToBase85.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 3e0d9145..d8c7975e 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -25,6 +25,8 @@ "From Base32", "To Base58", "From Base58", + "To Base85", + "From Base85", "To Base", "From Base", "To BCD", diff --git a/src/core/lib/Base85.mjs b/src/core/lib/Base85.mjs new file mode 100644 index 00000000..f7d667cc --- /dev/null +++ b/src/core/lib/Base85.mjs @@ -0,0 +1,45 @@ +/** + * Base85 resources. + * + * @author PenguinGeorge [george@penguingeorge.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +/** + * Base85 alphabet options. + */ +export const ALPHABET_OPTIONS = [ + { + name: "Standard", + value: "!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu", + }, + { + name: "Z85 (ZeroMQ)", + value: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#", + }, + { + name: "IPv6", + value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|~}", + } +]; + + +/** + * Returns the name of the alphabet, when given the alphabet. + * + * @param {string} alphabet + * @returns {string} + */ +export function alphabetName(alphabet) { + alphabet = alphabet.replace("'", "'"); + alphabet = alphabet.replace("\"", """); + alphabet = alphabet.replace("\\", "\"); + let name; + + ALPHABET_OPTIONS.forEach(function(a) { + if (escape(alphabet) === escape(a.value)) name = a.name; + }); + + return name; +} diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs new file mode 100644 index 00000000..fd8c3466 --- /dev/null +++ b/src/core/operations/FromBase85.mjs @@ -0,0 +1,104 @@ +/** + * @author PenguinGeorge [george@penguingeorge.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import {alphabetName, ALPHABET_OPTIONS} from "../lib/Base85"; + +/** + * From Base85 operation + */ +class FromBase85 extends Operation { + + /** + * From Base85 constructor + */ + constructor() { + super(); + + this.name = "From Base85"; + this.module = "Default"; + this.description = "Base85 (similar to Base64) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.

This operation decodes data from an ASCII string (with an alphabet of your choosing, presets included).

e.g. BOu!rD]j7BEbo7 becomes hello world

Base85 is commonly used in Adobe's PostScript and PDF file formats."; + this.infoURL = "https://wikipedia.org/wiki/Ascii85"; + this.inputType = "string"; + this.outputType = "byteArray"; + this.args = [ + { + name: "Alphabet", + type: "editableOption", + value: ALPHABET_OPTIONS + }, + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {byteArray} + */ + run(input, args) { + const alphabet = args[0] || ALPHABET_OPTIONS[0].value, + encoding = alphabetName(alphabet), + result = []; + + if (alphabet.length !== 85 || + [].unique.call(alphabet).length !== 85) { + throw new OperationError("Alphabet must be of length 85"); + } + + if (input.length === 0) return []; + + const matches = input.match(/<~(.+?)~>/); + if (matches !== null) input = matches[1]; + + let i = 0; + let block, blockBytes; + while (i < input.length) { + if (encoding === "Standard" && input[i] === "z") { + result.push(0, 0, 0, 0); + i++; + } else { + let digits = []; + digits = input + .substr(i, 5) + .split("") + .map((chr, idx) => { + const digit = alphabet.indexOf(chr); + if (digit < 0 || digit > 84) { + throw "Invalid character '" + chr + "' at index " + idx; + } + return digit; + }); + + block = + digits[0] * 52200625 + + digits[1] * 614125 + + (i + 2 < input.length ? digits[2] : 84) * 7225 + + (i + 3 < input.length ? digits[3] : 84) * 85 + + (i + 4 < input.length ? digits[4] : 84); + + blockBytes = [ + (block >> 24) & 0xff, + (block >> 16) & 0xff, + (block >> 8) & 0xff, + block & 0xff + ]; + + if (input.length < i + 5) { + blockBytes.splice(input.length - (i + 5), 5); + } + + result.push.apply(result, blockBytes); + i += 5; + } + } + + return result; + } + +} + +export default FromBase85; diff --git a/src/core/operations/ToBase85.mjs b/src/core/operations/ToBase85.mjs new file mode 100644 index 00000000..3179c6a3 --- /dev/null +++ b/src/core/operations/ToBase85.mjs @@ -0,0 +1,93 @@ +/** + * @author PenguinGeorge [george@penguingeorge.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import {alphabetName, ALPHABET_OPTIONS} from "../lib/Base85"; + +/** + * To Base85 operation + */ +class ToBase85 extends Operation { + + /** + * To Base85 constructor + */ + constructor() { + super(); + + this.name = "To Base85"; + this.module = "Default"; + this.description = "Base85 (similar to Base64) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.

This operation encodes data in an ASCII string (with an alphabet of your choosing, presets included).

e.g. hello world becomes BOu!rD]j7BEbo7

Base85 is commonly used in Adobe's PostScript and PDF file formats.

Options
AlphabetInclude delimiter
Adds a '<~' and '~>' delimiter to the start and end of the data. This is standard for Adobe's implementation of Base85."; + this.infoURL = "https://wikipedia.org/wiki/Ascii85"; + this.inputType = "byteArray"; + this.outputType = "string"; + this.args = [ + { + name: "Alphabet", + type: "editableOption", + value: ALPHABET_OPTIONS + }, + { + name: "Include Delimeter", + type: "boolean", + value: false + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const alphabet = args[0] || ALPHABET_OPTIONS[0].value, + encoding = alphabetName(alphabet); + let result = ""; + + if (alphabet.length !== 85 || + [].unique.call(alphabet).length !== 85) { + throw new OperationError("Error: alphabet must be of length 85"); + } + + if (input.length === 0) return ""; + + let block; + for (let i = 0; i < input.length; i += 4) { + block = ( + ((input[i]) << 24) + + ((input[i + 1] || 0) << 16) + + ((input[i + 2] || 0) << 8) + + ((input[i + 3] || 0)) + ) >>> 0; + + if (encoding !== "Standard" || block > 0) { + let digits = []; + for (let j = 0; j < 5; j++) { + digits.push(block % 85); + block = Math.floor(block / 85); + } + + digits = digits.reverse(); + + if (input.length < i + 4) { + digits.splice(input.length - (i + 4), 4); + } + + result += digits.map(digit => alphabet[digit]).join(""); + } else { + result += (encoding === "Standard") ? "z" : null; + } + } + + if (args[1] === true) result = "<~" + result + "~>"; + + return result; + } +} + +export default ToBase85; From 8e9fece77d0723ecccf268e065f97d8e5ea99ebf Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 23 Aug 2018 22:05:31 +0100 Subject: [PATCH 2/2] Tidied up Base85 ops --- src/core/lib/Base85.mjs | 6 +++--- src/core/operations/FromBase85.mjs | 7 ++++--- src/core/operations/ToBase85.mjs | 16 ++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/core/lib/Base85.mjs b/src/core/lib/Base85.mjs index f7d667cc..69140d03 100644 --- a/src/core/lib/Base85.mjs +++ b/src/core/lib/Base85.mjs @@ -12,15 +12,15 @@ export const ALPHABET_OPTIONS = [ { name: "Standard", - value: "!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu", + value: "!-u", }, { name: "Z85 (ZeroMQ)", - value: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#", + value: "0-9a-zA-Z.#\\-:+=^!/*?&<>()[]{}@%$#", }, { name: "IPv6", - value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|~}", + value: "0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|~}", } ]; diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs index fd8c3466..2fec8e2e 100644 --- a/src/core/operations/FromBase85.mjs +++ b/src/core/operations/FromBase85.mjs @@ -6,6 +6,7 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; +import Utils from "../Utils"; import {alphabetName, ALPHABET_OPTIONS} from "../lib/Base85"; /** @@ -21,7 +22,7 @@ class FromBase85 extends Operation { this.name = "From Base85"; this.module = "Default"; - this.description = "Base85 (similar to Base64) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.

This operation decodes data from an ASCII string (with an alphabet of your choosing, presets included).

e.g. BOu!rD]j7BEbo7 becomes hello world

Base85 is commonly used in Adobe's PostScript and PDF file formats."; + this.description = "Base85 (also called Ascii85) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.

This operation decodes data from an ASCII string (with an alphabet of your choosing, presets included).

e.g. BOu!rD]j7BEbo7 becomes hello world

Base85 is commonly used in Adobe's PostScript and PDF file formats."; this.infoURL = "https://wikipedia.org/wiki/Ascii85"; this.inputType = "string"; this.outputType = "byteArray"; @@ -40,7 +41,7 @@ class FromBase85 extends Operation { * @returns {byteArray} */ run(input, args) { - const alphabet = args[0] || ALPHABET_OPTIONS[0].value, + const alphabet = Utils.expandAlphRange(args[0]).join(""), encoding = alphabetName(alphabet), result = []; @@ -68,7 +69,7 @@ class FromBase85 extends Operation { .map((chr, idx) => { const digit = alphabet.indexOf(chr); if (digit < 0 || digit > 84) { - throw "Invalid character '" + chr + "' at index " + idx; + throw `Invalid character '${chr}' at index ${idx}`; } return digit; }); diff --git a/src/core/operations/ToBase85.mjs b/src/core/operations/ToBase85.mjs index 3179c6a3..97cc2e72 100644 --- a/src/core/operations/ToBase85.mjs +++ b/src/core/operations/ToBase85.mjs @@ -6,6 +6,7 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; +import Utils from "../Utils"; import {alphabetName, ALPHABET_OPTIONS} from "../lib/Base85"; /** @@ -21,7 +22,7 @@ class ToBase85 extends Operation { this.name = "To Base85"; this.module = "Default"; - this.description = "Base85 (similar to Base64) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.

This operation encodes data in an ASCII string (with an alphabet of your choosing, presets included).

e.g. hello world becomes BOu!rD]j7BEbo7

Base85 is commonly used in Adobe's PostScript and PDF file formats.

Options
AlphabetInclude delimiter
Adds a '<~' and '~>' delimiter to the start and end of the data. This is standard for Adobe's implementation of Base85."; + this.description = "Base85 (also called Ascii85) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.

This operation encodes data in an ASCII string (with an alphabet of your choosing, presets included).

e.g. hello world becomes BOu!rD]j7BEbo7

Base85 is commonly used in Adobe's PostScript and PDF file formats.

Options
AlphabetInclude delimiter
Adds a '<~' and '~>' delimiter to the start and end of the data. This is standard for Adobe's implementation of Base85."; this.infoURL = "https://wikipedia.org/wiki/Ascii85"; this.inputType = "byteArray"; this.outputType = "string"; @@ -32,7 +33,7 @@ class ToBase85 extends Operation { value: ALPHABET_OPTIONS }, { - name: "Include Delimeter", + name: "Include delimeter", type: "boolean", value: false } @@ -45,13 +46,14 @@ class ToBase85 extends Operation { * @returns {string} */ run(input, args) { - const alphabet = args[0] || ALPHABET_OPTIONS[0].value, - encoding = alphabetName(alphabet); + const alphabet = Utils.expandAlphRange(args[0]).join(""), + encoding = alphabetName(alphabet), + includeDelim = args[1]; let result = ""; if (alphabet.length !== 85 || [].unique.call(alphabet).length !== 85) { - throw new OperationError("Error: alphabet must be of length 85"); + throw new OperationError("Error: Alphabet must be of length 85"); } if (input.length === 0) return ""; @@ -84,9 +86,7 @@ class ToBase85 extends Operation { } } - if (args[1] === true) result = "<~" + result + "~>"; - - return result; + return includeDelim ? `<~${result}~>` : result; } }