Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/core/lib/Base32.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,9 @@ export const ALPHABET_OPTIONS = [
name: "Hex Extended", // https://www.rfc-editor.org/rfc/rfc4648#section-7
value: "0-9A-V=",
},
{
name: "Crockford's alphabet", // https://www.crockford.com/base32.html
value: "0-9A-HJKMNP-TV-Z=",
},
];

23 changes: 19 additions & 4 deletions src/core/operations/FromBase32.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import {ALPHABET_OPTIONS} from "../lib/Base32.mjs";

Expand Down Expand Up @@ -42,12 +43,17 @@ class FromBase32 extends Operation {
{
pattern: "^(?:[A-Z2-7]{8})+(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}={1})?$",
flags: "",
args: ["A-Z2-7=", false]
args: [ALPHABET_OPTIONS.find(opt => opt.name === "Standard").value, false]
},
{
pattern: "^(?:[0-9A-V]{8})+(?:[0-9A-V]{2}={6}|[0-9A-V]{4}={4}|[0-9A-V]{5}={3}|[0-9A-V]{7}={1})?$",
flags: "",
args: ["0-9A-V=", false]
args: [ALPHABET_OPTIONS.find(opt => opt.name === "Hex Extended").value, false]
},
{
pattern: "^(?:[0-9A-TV-Za-tv-z]{8})+(?:[0-9A-TV-Za-tv-z]{2}={6}|[0-9A-TV-Za-tv-z]{4}={4}|[0-9A-TV-Za-tv-z]{5}={3}|[0-9A-TV-Za-tv-z]{7}=)?$",
flags: "",
args: [ALPHABET_OPTIONS.find(opt => opt.name === "Crockford's alphabet").value, false]
}
];
}
Expand All @@ -60,11 +66,20 @@ class FromBase32 extends Operation {
run(input, args) {
if (!input) return [];

const alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
const alphabet = Utils.expandAlphRange(args[0]).join(""),
removeNonAlphChars = args[1],
output = [];

if (alphabet.length !== 33) {
throw new OperationError("Alphabet must be of length 33"); // 32 characters + 1 padding
}
const isCrockford = alphabet === Utils.expandAlphRange(ALPHABET_OPTIONS.find(opt => opt.name === "Crockford's alphabet").value).join("");
if (isCrockford) {
input = input
.replace(/[oO]/g, "0")
.replace(/[iIlL]/g, "1")
.toUpperCase();
}
let chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
Expand Down
7 changes: 6 additions & 1 deletion src/core/operations/ToBase32.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {ALPHABET_OPTIONS} from "../lib/Base32.mjs";
import OperationError from "../errors/OperationError.mjs";

/**
* To Base32 operation
Expand Down Expand Up @@ -43,7 +44,11 @@ class ToBase32 extends Operation {
if (!input) return "";
input = new Uint8Array(input);

const alphabet = args[0] ? Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
const alphabet = Utils.expandAlphRange(args[0]).join("");
if (alphabet.length !== 33) {
throw new OperationError("Alphabet must be of length 33"); // 32 characters + 1 padding
}

let output = "",
chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
Expand Down
216 changes: 216 additions & 0 deletions tests/operations/tests/Base32.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const STANDARD_OUT = "JBCUYTCPEBBECU2FGMZA====";
const EXTENDED_INP = "HELLO BASE32 EXTENDED";
const EXTENDED_OUT = "912KOJ2F41142KQ56CP20HAOAH2KSH258G======";

// Example Crockford's alphabet Tests
const CROCKFORD_INP = "HELLO BASE32 CROCKFORD";
const CROCKFORD_OUT = "912MRK2F41142MT56CS20GTJ9X1MPHJFA920====";

// All Bytes
const ALL_BYTES = [
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
Expand All @@ -38,6 +42,9 @@ const ALL_BYTES = [

const ALL_BYTES_EXTENDED_OUT = "000G40O40K30E209185GO38E1S8124GJ2GAHC5OO34D1M70T3OFI08924CI2A9H750KIKAPC5KN2UC1H68PJ8D9M6SS3IEHR7GUJSFQ085146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB9DLONAUBTG62OJ3CHIMCPR8D5L6MR3DDPNN0SBIEDQ7ATJNF1SNKURSFLV7V041GA1O91C6GU48J2KBHI6OT3SGI699754LIQBPH6CQJEE9R7KVK2GQ58T4KMJAFA59LALQPBDELUOB3CLJMIQRDDTON6TBNF5TNQVS1GE2OF2CBHM7P34SLIUCPN7CVK6HQB9T9LEMQVCDJMMRRJETTNV0S7HE7P75SRJUHQFATFMERRNFU3OV5SVKUNRFFU7PVBTVPVFUVS======";
const ALL_BYTES_STANDARD_OUT = "AAAQEAYEAUDAOCAJBIFQYDIOB4IBCEQTCQKRMFYYDENBWHA5DYPSAIJCEMSCKJRHFAUSUKZMFUXC6MBRGIZTINJWG44DSOR3HQ6T4P2AIFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLJNVYXK6L5QGCYTDMRSWMZ3INFVGW3DNNZXXA4LSON2HK5TXPB4XU634PV7H7AEBQKBYJBMGQ6EITCULRSGY5D4QSGJJHFEVS2LZRGM2TOOJ3HU7UCQ2FI5EUWTKPKFJVKV2ZLNOV6YLDMVTWS23NN5YXG5LXPF5X274BQOCYPCMLRWHZDE4VS6MZXHM7UGR2LJ5JVOW27MNTWW33TO55X7A4HROHZHF43T6R2PK5PWO33XP6DY7F47U6X3PP6HZ7L57Z7P674======";
const ALL_BYTES_CROCKFORD_OUT = "000G40R40M30E209185GR38E1W8124GK2GAHC5RR34D1P70X3RFJ08924CJ2A9H750MJMASC5MQ2YC1H68SK8D9P6WW3JEHV7GYKWFT085146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB9DNRQAYBXG62RK3CHJPCSV8D5N6PV3DDSQQ0WBJEDT7AXKQF1WQMYVWFNZ7Z041GA1R91C6GY48K2MBHJ6RX3WGJ699754NJTBSH6CTKEE9V7MZM2GT58X4MPKAFA59NANTSBDENYRB3CNKPJTVDDXRQ6XBQF5XQTZW1GE2RF2CBHP7S34WNJYCSQ7CZM6HTB9X9NEPTZCDKPPVVKEXXQZ0W7HE7S75WVKYHTFAXFPEVVQFY3RZ5WZMYQVFFY7SZBXZSZFYZW======";

const WRONG_ALPHABET = "Alphabet must be of length 33";

TestRegister.addTests([
{
Expand All @@ -62,6 +69,17 @@ TestRegister.addTests([
},
],
},
{
name: "To Base32 Crockford: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[2].value],
},
],
},
{
name: "From Base32 Standard: nothing",
input: "",
Expand All @@ -84,6 +102,17 @@ TestRegister.addTests([
},
],
},
{
name: "From Base32 Crockford: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "To Base32 Standard: " + STANDARD_INP,
input: STANDARD_INP,
Expand All @@ -106,6 +135,17 @@ TestRegister.addTests([
},
],
},
{
name: "To Base32 Crockford: " + CROCKFORD_INP,
input: CROCKFORD_INP,
expectedOutput: CROCKFORD_OUT,
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[2].value],
},
],
},
{
name: "From Base32 Standard: " + STANDARD_OUT,
input: STANDARD_OUT,
Expand All @@ -128,6 +168,17 @@ TestRegister.addTests([
},
],
},
{
name: "From Base32 Crockford: " + CROCKFORD_OUT,
input: CROCKFORD_OUT,
expectedOutput: CROCKFORD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "To Base32 Hex Standard: All Bytes",
input: ALL_BYTES,
Expand All @@ -150,6 +201,17 @@ TestRegister.addTests([
},
],
},
{
name: "To Base32 Crockford: All Bytes",
input: ALL_BYTES,
expectedOutput: ALL_BYTES_CROCKFORD_OUT,
recipeConfig: [
{
op: "To Base32",
args: [ALPHABET_OPTIONS[2].value],
},
],
},
{
name: "From Base32 Hex Standard: All Bytes",
input: ALL_BYTES_STANDARD_OUT,
Expand All @@ -172,5 +234,159 @@ TestRegister.addTests([
},
],
},
{
name: "From Base32 Crockford: All Bytes",
input: ALL_BYTES_CROCKFORD_OUT,
expectedOutput: ALL_BYTES,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "To Base32 Standard: wrong alphabet",
input: STANDARD_INP,
expectedOutput: WRONG_ALPHABET,
recipeConfig: [
{
op: "To Base32",
args: [""],
},
],
},
{
name: "To Base32 Hex Extended: wrong alphabet",
input: EXTENDED_INP,
expectedOutput: WRONG_ALPHABET,
recipeConfig: [
{
op: "To Base32",
args: [""],
},
],
},
{
name: "To Base32 Crockford: wrong alphabet",
input: CROCKFORD_INP,
expectedOutput: WRONG_ALPHABET,
recipeConfig: [
{
op: "To Base32",
args: [""],
},
],
},
{
name: "From Base32 Standard: wrong alphabet",
input: STANDARD_OUT,
expectedOutput: WRONG_ALPHABET,
recipeConfig: [
{
op: "From Base32",
args: ["", false],
},
],
},
{
name: "From Base32 Hex Extended: wrong alphabet",
input: EXTENDED_OUT,
expectedOutput: WRONG_ALPHABET,
recipeConfig: [
{
op: "From Base32",
args: ["", false],
},
],
},
{
name: "From Base32 Crockford: wrong alphabet",
input: CROCKFORD_OUT,
expectedOutput: WRONG_ALPHABET,
recipeConfig: [
{
op: "From Base32",
args: ["", false],
},
],
},
{
name: "From Base32 Crockford: " + CROCKFORD_OUT.replaceAll("0", "o"),
input: CROCKFORD_OUT.replaceAll("0", "o"),
expectedOutput: CROCKFORD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "From Base32 Crockford: " + CROCKFORD_OUT.replaceAll("0", "O"),
input: CROCKFORD_OUT.replaceAll("0", "O"),
expectedOutput: CROCKFORD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "From Base32 Crockford: " + CROCKFORD_OUT.replaceAll("1", "I"),
input: CROCKFORD_OUT.replaceAll("1", "I"),
expectedOutput: CROCKFORD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "From Base32 Crockford: " + CROCKFORD_OUT.replaceAll("1", "i"),
input: CROCKFORD_OUT.replaceAll("1", "i"),
expectedOutput: CROCKFORD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "From Base32 Crockford: " + CROCKFORD_OUT.replaceAll("1", "L"),
input: CROCKFORD_OUT.replaceAll("1", "L"),
expectedOutput: CROCKFORD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "From Base32 Crockford: " + CROCKFORD_OUT.replaceAll("1", "l"),
input: CROCKFORD_OUT.replaceAll("1", "l"),
expectedOutput: CROCKFORD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
{
name: "From Base32 Crockford: " + CROCKFORD_OUT.toLowerCase(),
input: CROCKFORD_OUT.toLowerCase(),
expectedOutput: CROCKFORD_INP,
recipeConfig: [
{
op: "From Base32",
args: [ALPHABET_OPTIONS[2].value, false],
},
],
},
]);

Loading