From 5b58e95c0a7b956cf91d59fb837ce235c198d99d Mon Sep 17 00:00:00 2001 From: r4mos Date: Mon, 8 Sep 2025 16:58:31 +0200 Subject: [PATCH 1/9] Add Crockford's alphabet --- src/core/lib/Base32.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/lib/Base32.mjs b/src/core/lib/Base32.mjs index 92b76eca74..c772dfca61 100644 --- a/src/core/lib/Base32.mjs +++ b/src/core/lib/Base32.mjs @@ -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: "0123456789ABCDEFGHJKMNPQRSTVWXYZ=", + }, ]; From d01e208c0d18bfbb3288c535dd8e220bcb5a9c69 Mon Sep 17 00:00:00 2001 From: r4mos Date: Mon, 8 Sep 2025 17:40:27 +0200 Subject: [PATCH 2/9] Add Crockford's tests --- tests/operations/tests/Base32.mjs | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tests/operations/tests/Base32.mjs b/tests/operations/tests/Base32.mjs index 760cdf141f..28913b8551 100644 --- a/tests/operations/tests/Base32.mjs +++ b/tests/operations/tests/Base32.mjs @@ -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", @@ -38,6 +42,7 @@ const ALL_BYTES = [ const ALL_BYTES_EXTENDED_OUT = "000G40O40K30E209185GO38E1S8124GJ2GAHC5OO34D1M70T3OFI08924CI2A9H750KIKAPC5KN2UC1H68PJ8D9M6SS3IEHR7GUJSFQ085146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB9DLONAUBTG62OJ3CHIMCPR8D5L6MR3DDPNN0SBIEDQ7ATJNF1SNKURSFLV7V041GA1O91C6GU48J2KBHI6OT3SGI699754LIQBPH6CQJEE9R7KVK2GQ58T4KMJAFA59LALQPBDELUOB3CLJMIQRDDTON6TBNF5TNQVS1GE2OF2CBHM7P34SLIUCPN7CVK6HQB9T9LEMQVCDJMMRRJETTNV0S7HE7P75SRJUHQFATFMERRNFU3OV5SVKUNRFFU7PVBTVPVFUVS======"; const ALL_BYTES_STANDARD_OUT = "AAAQEAYEAUDAOCAJBIFQYDIOB4IBCEQTCQKRMFYYDENBWHA5DYPSAIJCEMSCKJRHFAUSUKZMFUXC6MBRGIZTINJWG44DSOR3HQ6T4P2AIFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLJNVYXK6L5QGCYTDMRSWMZ3INFVGW3DNNZXXA4LSON2HK5TXPB4XU634PV7H7AEBQKBYJBMGQ6EITCULRSGY5D4QSGJJHFEVS2LZRGM2TOOJ3HU7UCQ2FI5EUWTKPKFJVKV2ZLNOV6YLDMVTWS23NN5YXG5LXPF5X274BQOCYPCMLRWHZDE4VS6MZXHM7UGR2LJ5JVOW27MNTWW33TO55X7A4HROHZHF43T6R2PK5PWO33XP6DY7F47U6X3PP6HZ7L57Z7P674======"; +const ALL_BYTES_CROCKFORD_OUT = "000G40R40M30E209185GR38E1W8124GK2GAHC5RR34D1P70X3RFJ08924CJ2A9H750MJMASC5MQ2YC1H68SK8D9P6WW3JEHV7GYKWFT085146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB9DNRQAYBXG62RK3CHJPCSV8D5N6PV3DDSQQ0WBJEDT7AXKQF1WQMYVWFNZ7Z041GA1R91C6GY48K2MBHJ6RX3WGJ699754NJTBSH6CTKEE9V7MZM2GT58X4MPKAFA59NANTSBDENYRB3CNKPJTVDDXRQ6XBQF5XQTZW1GE2RF2CBHP7S34WNJYCSQ7CZM6HTB9X9NEPTZCDKPPVVKEXXQZ0W7HE7S75WVKYHTFAXFPEVVQFY3RZ5WZMYQVFFY7SZBXZSZFYZW======"; TestRegister.addTests([ { @@ -62,6 +67,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: "", @@ -84,6 +100,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, @@ -106,6 +133,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, @@ -128,6 +166,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, @@ -150,6 +199,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, @@ -172,5 +232,16 @@ 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], + }, + ], + }, ]); From 4814601b9cf9c786cd415665c587f44567a4722d Mon Sep 17 00:00:00 2001 From: r4mos Date: Mon, 8 Sep 2025 18:04:44 +0200 Subject: [PATCH 3/9] Add Crockford's checks --- src/core/operations/FromBase32.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/operations/FromBase32.mjs b/src/core/operations/FromBase32.mjs index 8ee0f1f872..b729943da1 100644 --- a/src/core/operations/FromBase32.mjs +++ b/src/core/operations/FromBase32.mjs @@ -48,6 +48,11 @@ class FromBase32 extends Operation { 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] + }, + { + pattern: "^(?:(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){8})+(?:(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){2}={6}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){4}={4}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){5}={3}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){7}={1})?$", + flags: "", + args: ["0123456789ABCDEFGHJKMNPQRSTVWXYZ=", false] } ]; } From 56a0cfe0de2c9ec9e2ecb31700619d1a01f884b6 Mon Sep 17 00:00:00 2001 From: r4mos Date: Tue, 9 Sep 2025 09:22:46 +0200 Subject: [PATCH 4/9] References to dictionaries are added instead of reproducing them --- src/core/operations/FromBase32.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/operations/FromBase32.mjs b/src/core/operations/FromBase32.mjs index b729943da1..863475a021 100644 --- a/src/core/operations/FromBase32.mjs +++ b/src/core/operations/FromBase32.mjs @@ -42,17 +42,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: "^(?:(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){8})+(?:(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){2}={6}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){4}={4}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){5}={3}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){7}={1})?$", flags: "", - args: ["0123456789ABCDEFGHJKMNPQRSTVWXYZ=", false] + args: [ALPHABET_OPTIONS.find(opt => opt.name === "Crockford's alphabet").value, false] } ]; } From 67dc0f741298854a2a340de50b1e732be0d597e0 Mon Sep 17 00:00:00 2001 From: r4mos Date: Tue, 9 Sep 2025 09:45:55 +0200 Subject: [PATCH 5/9] Simplify regular expresion for magic and alphabet range --- src/core/lib/Base32.mjs | 2 +- src/core/operations/FromBase32.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/lib/Base32.mjs b/src/core/lib/Base32.mjs index c772dfca61..e436186672 100644 --- a/src/core/lib/Base32.mjs +++ b/src/core/lib/Base32.mjs @@ -21,7 +21,7 @@ export const ALPHABET_OPTIONS = [ }, { name: "Crockford's alphabet", // https://www.crockford.com/base32.html - value: "0123456789ABCDEFGHJKMNPQRSTVWXYZ=", + value: "0-9A-HJKMNP-TV-Z=", }, ]; diff --git a/src/core/operations/FromBase32.mjs b/src/core/operations/FromBase32.mjs index 863475a021..06674919f6 100644 --- a/src/core/operations/FromBase32.mjs +++ b/src/core/operations/FromBase32.mjs @@ -50,7 +50,7 @@ class FromBase32 extends Operation { args: [ALPHABET_OPTIONS.find(opt => opt.name === "Hex Extended").value, false] }, { - pattern: "^(?:(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){8})+(?:(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){2}={6}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){4}={4}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){5}={3}|(?:[0Oo]|[1IiLl]|[2-9]|[ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz]){7}={1})?$", + 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] } From b1105b0e210a88b39fd053ca76f1264f6b5abd4b Mon Sep 17 00:00:00 2001 From: r4mos Date: Tue, 9 Sep 2025 10:15:10 +0200 Subject: [PATCH 6/9] Check that the entered dictionary is valid to remove the default dictionary --- src/core/operations/FromBase32.mjs | 8 +++- src/core/operations/ToBase32.mjs | 7 ++- tests/operations/tests/Base32.mjs | 68 ++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/core/operations/FromBase32.mjs b/src/core/operations/FromBase32.mjs index 06674919f6..20944cca6b 100644 --- a/src/core/operations/FromBase32.mjs +++ b/src/core/operations/FromBase32.mjs @@ -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"; @@ -65,11 +66,14 @@ 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 + } + let chr1, chr2, chr3, chr4, chr5, enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8, i = 0; diff --git a/src/core/operations/ToBase32.mjs b/src/core/operations/ToBase32.mjs index 44eb8b4848..9e7255c2a4 100644 --- a/src/core/operations/ToBase32.mjs +++ b/src/core/operations/ToBase32.mjs @@ -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 @@ -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, diff --git a/tests/operations/tests/Base32.mjs b/tests/operations/tests/Base32.mjs index 28913b8551..0afb3787f5 100644 --- a/tests/operations/tests/Base32.mjs +++ b/tests/operations/tests/Base32.mjs @@ -44,6 +44,8 @@ const ALL_BYTES_EXTENDED_OUT = "000G40O40K30E209185GO38E1S8124GJ2GAHC5OO34D1M70T const ALL_BYTES_STANDARD_OUT = "AAAQEAYEAUDAOCAJBIFQYDIOB4IBCEQTCQKRMFYYDENBWHA5DYPSAIJCEMSCKJRHFAUSUKZMFUXC6MBRGIZTINJWG44DSOR3HQ6T4P2AIFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLJNVYXK6L5QGCYTDMRSWMZ3INFVGW3DNNZXXA4LSON2HK5TXPB4XU634PV7H7AEBQKBYJBMGQ6EITCULRSGY5D4QSGJJHFEVS2LZRGM2TOOJ3HU7UCQ2FI5EUWTKPKFJVKV2ZLNOV6YLDMVTWS23NN5YXG5LXPF5X274BQOCYPCMLRWHZDE4VS6MZXHM7UGR2LJ5JVOW27MNTWW33TO55X7A4HROHZHF43T6R2PK5PWO33XP6DY7F47U6X3PP6HZ7L57Z7P674======"; const ALL_BYTES_CROCKFORD_OUT = "000G40R40M30E209185GR38E1W8124GK2GAHC5RR34D1P70X3RFJ08924CJ2A9H750MJMASC5MQ2YC1H68SK8D9P6WW3JEHV7GYKWFT085146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB9DNRQAYBXG62RK3CHJPCSV8D5N6PV3DDSQQ0WBJEDT7AXKQF1WQMYVWFNZ7Z041GA1R91C6GY48K2MBHJ6RX3WGJ699754NJTBSH6CTKEE9V7MZM2GT58X4MPKAFA59NANTSBDENYRB3CNKPJTVDDXRQ6XBQF5XQTZW1GE2RF2CBHP7S34WNJYCSQ7CZM6HTB9X9NEPTZCDKPPVVKEXXQZ0W7HE7S75WVKYHTFAXFPEVVQFY3RZ5WZMYQVFFY7SZBXZSZFYZW======"; +const WRONG_ALPHABET = "Alphabet must be of length 33" + TestRegister.addTests([ { name: "To Base32 Standard: nothing", @@ -243,5 +245,71 @@ TestRegister.addTests([ }, ], }, + { + 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], + }, + ], + }, ]); From d7915b1a1501de1222dcb10c0d413653df88c232 Mon Sep 17 00:00:00 2001 From: r4mos Date: Tue, 9 Sep 2025 10:40:26 +0200 Subject: [PATCH 7/9] Crockford equivalences are added in decoding and tests --- src/core/operations/FromBase32.mjs | 8 ++- tests/operations/tests/Base32.mjs | 79 +++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/core/operations/FromBase32.mjs b/src/core/operations/FromBase32.mjs index 20944cca6b..9e2e81a108 100644 --- a/src/core/operations/FromBase32.mjs +++ b/src/core/operations/FromBase32.mjs @@ -73,7 +73,13 @@ class FromBase32 extends Operation { 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; diff --git a/tests/operations/tests/Base32.mjs b/tests/operations/tests/Base32.mjs index 0afb3787f5..3e62e7924f 100644 --- a/tests/operations/tests/Base32.mjs +++ b/tests/operations/tests/Base32.mjs @@ -44,7 +44,7 @@ const ALL_BYTES_EXTENDED_OUT = "000G40O40K30E209185GO38E1S8124GJ2GAHC5OO34D1M70T const ALL_BYTES_STANDARD_OUT = "AAAQEAYEAUDAOCAJBIFQYDIOB4IBCEQTCQKRMFYYDENBWHA5DYPSAIJCEMSCKJRHFAUSUKZMFUXC6MBRGIZTINJWG44DSOR3HQ6T4P2AIFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLJNVYXK6L5QGCYTDMRSWMZ3INFVGW3DNNZXXA4LSON2HK5TXPB4XU634PV7H7AEBQKBYJBMGQ6EITCULRSGY5D4QSGJJHFEVS2LZRGM2TOOJ3HU7UCQ2FI5EUWTKPKFJVKV2ZLNOV6YLDMVTWS23NN5YXG5LXPF5X274BQOCYPCMLRWHZDE4VS6MZXHM7UGR2LJ5JVOW27MNTWW33TO55X7A4HROHZHF43T6R2PK5PWO33XP6DY7F47U6X3PP6HZ7L57Z7P674======"; const ALL_BYTES_CROCKFORD_OUT = "000G40R40M30E209185GR38E1W8124GK2GAHC5RR34D1P70X3RFJ08924CJ2A9H750MJMASC5MQ2YC1H68SK8D9P6WW3JEHV7GYKWFT085146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB9DNRQAYBXG62RK3CHJPCSV8D5N6PV3DDSQQ0WBJEDT7AXKQF1WQMYVWFNZ7Z041GA1R91C6GY48K2MBHJ6RX3WGJ699754NJTBSH6CTKEE9V7MZM2GT58X4MPKAFA59NANTSBDENYRB3CNKPJTVDDXRQ6XBQF5XQTZW1GE2RF2CBHP7S34WNJYCSQ7CZM6HTB9X9NEPTZCDKPPVVKEXXQZ0W7HE7S75WVKYHTFAXFPEVVQFY3RZ5WZMYQVFFY7SZBXZSZFYZW======"; -const WRONG_ALPHABET = "Alphabet must be of length 33" +const WRONG_ALPHABET = "Alphabet must be of length 33"; TestRegister.addTests([ { @@ -311,5 +311,82 @@ TestRegister.addTests([ }, ], }, + { + 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], + }, + ], + }, ]); From 663f7480ad9e7e40a43c460ec90664270a3ce748 Mon Sep 17 00:00:00 2001 From: r4mos Date: Thu, 25 Sep 2025 13:13:33 +0200 Subject: [PATCH 8/9] Add effective key bits option with 128 by default to RC2 operation --- src/core/operations/RC2Decrypt.mjs | 12 +++++++++--- src/core/operations/RC2Encrypt.mjs | 13 +++++++++---- tests/operations/tests/Crypt.mjs | 6 ++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/core/operations/RC2Decrypt.mjs b/src/core/operations/RC2Decrypt.mjs index c9ff1bf496..7e35f22aa0 100644 --- a/src/core/operations/RC2Decrypt.mjs +++ b/src/core/operations/RC2Decrypt.mjs @@ -32,6 +32,11 @@ class RC2Decrypt extends Operation { "value": "", "toggleValues": ["Hex", "UTF8", "Latin1", "Base64"] }, + { + name: "Effective key bits", + type: "number", + value: 128 + }, { "name": "IV", "type": "toggleString", @@ -58,9 +63,10 @@ class RC2Decrypt extends Operation { */ run(input, args) { const key = Utils.convertToByteString(args[0].string, args[0].option), - iv = Utils.convertToByteString(args[1].string, args[1].option), - [,, inputType, outputType] = args, - decipher = forge.rc2.createDecryptionCipher(key); + effective = args[1], + iv = Utils.convertToByteString(args[2].string, args[2].option), + [,,, inputType, outputType] = args, + decipher = forge.rc2.createDecryptionCipher(key, effective); input = Utils.convertToByteString(input, inputType); diff --git a/src/core/operations/RC2Encrypt.mjs b/src/core/operations/RC2Encrypt.mjs index 88dae5b1ed..add1555f95 100644 --- a/src/core/operations/RC2Encrypt.mjs +++ b/src/core/operations/RC2Encrypt.mjs @@ -8,7 +8,6 @@ import Operation from "../Operation.mjs"; import Utils from "../Utils.mjs"; import forge from "node-forge"; - /** * RC2 Encrypt operation */ @@ -33,6 +32,11 @@ class RC2Encrypt extends Operation { "value": "", "toggleValues": ["Hex", "UTF8", "Latin1", "Base64"] }, + { + name: "Effective key bits", + type: "number", + value: 128 + }, { "name": "IV", "type": "toggleString", @@ -59,9 +63,10 @@ class RC2Encrypt extends Operation { */ run(input, args) { const key = Utils.convertToByteString(args[0].string, args[0].option), - iv = Utils.convertToByteString(args[1].string, args[1].option), - [,, inputType, outputType] = args, - cipher = forge.rc2.createEncryptionCipher(key); + effective = args[1], + iv = Utils.convertToByteString(args[2].string, args[2].option), + [,,, inputType, outputType] = args, + cipher = forge.rc2.createEncryptionCipher(key, effective); input = Utils.convertToByteString(input, inputType); diff --git a/tests/operations/tests/Crypt.mjs b/tests/operations/tests/Crypt.mjs index 504f64b92e..893f58ab08 100644 --- a/tests/operations/tests/Crypt.mjs +++ b/tests/operations/tests/Crypt.mjs @@ -1488,6 +1488,7 @@ Triple DES uses a key length of 24 bytes (192 bits).`, "op": "RC2 Encrypt", "args": [ {"option": "Hex", "string": ""}, + 128, {"option": "Hex", "string": ""}, "Hex", "Hex" ] @@ -1503,6 +1504,7 @@ Triple DES uses a key length of 24 bytes (192 bits).`, "op": "RC2 Encrypt", "args": [ {"option": "Hex", "string": "eb970554bb213430f4bb4e5988a6a218"}, + 128, {"option": "Hex", "string": "ae817c784a097e0c"}, "Hex", "Hex" ] @@ -1518,6 +1520,7 @@ Triple DES uses a key length of 24 bytes (192 bits).`, "op": "RC2 Encrypt", "args": [ {"option": "Hex", "string": "eb970554bb213430f4bb4e5988a6a218"}, + 128, {"option": "Hex", "string": ""}, "Hex", "Hex" ] @@ -1533,6 +1536,7 @@ Triple DES uses a key length of 24 bytes (192 bits).`, "op": "RC2 Decrypt", "args": [ {"option": "Hex", "string": ""}, + 128, {"option": "Hex", "string": ""}, "Hex", "Hex" ] @@ -1548,6 +1552,7 @@ Triple DES uses a key length of 24 bytes (192 bits).`, "op": "RC2 Decrypt", "args": [ {"option": "Hex", "string": "eb970554bb213430f4bb4e5988a6a218"}, + 128, {"option": "Hex", "string": "ae817c784a097e0c"}, "Hex", "Hex" ] @@ -1563,6 +1568,7 @@ Triple DES uses a key length of 24 bytes (192 bits).`, "op": "RC2 Decrypt", "args": [ {"option": "Hex", "string": "eb970554bb213430f4bb4e5988a6a218"}, + 128, {"option": "Hex", "string": ""}, "Hex", "Hex" ] From 581863f4e3876a65607376fb1a37e1858eb5d22a Mon Sep 17 00:00:00 2001 From: r4mos Date: Thu, 25 Sep 2025 13:24:34 +0200 Subject: [PATCH 9/9] Add test to try other effective key bits in RC2 operation --- tests/operations/tests/Crypt.mjs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/operations/tests/Crypt.mjs b/tests/operations/tests/Crypt.mjs index 893f58ab08..281d76d66b 100644 --- a/tests/operations/tests/Crypt.mjs +++ b/tests/operations/tests/Crypt.mjs @@ -1575,6 +1575,38 @@ Triple DES uses a key length of 24 bytes (192 bits).`, } ], }, + { + name: "RC2 Encrypt: RC2-ECB, Binary with 80 effective key bits", + input: "7a0e643132750e96d805d11e9e48e281fa39a41039286423cc1c045e5442b40bf1c3f2822bded3f9c8ef11cb25da64dda9c7ab87c246bd305385150c98f31465c2a6180fe81d31ea289b916504d5a12e1de26cb10adba84a0cb0c86f94bc14bc554f3018", + expectedOutput: "449d87545fc7364103e8311c732be765039e99410ef51a916481fd0879a1e32761af3c73a0129ea2768a3dd95ce8ebe49c780d8118417f659415376d97d2919838bef757be28379a1d561d7b7d94e16753a944ee90fef6c9acc8c680f39d3c5566f59340cd361167", + recipeConfig: [ + { + "op": "RC2 Encrypt", + "args": [ + {"option": "Hex", "string": "eb970554bb213430f4bb4e5988a6a218"}, + 80, + {"option": "Hex", "string": ""}, + "Hex", "Hex" + ] + } + ], + }, + { + name: "RC2 Decrypt: RC2-ECB, Binary with 80 effective key bits", + input: "449d87545fc7364103e8311c732be765039e99410ef51a916481fd0879a1e32761af3c73a0129ea2768a3dd95ce8ebe49c780d8118417f659415376d97d2919838bef757be28379a1d561d7b7d94e16753a944ee90fef6c9acc8c680f39d3c5566f59340cd361167", + expectedOutput: "7a0e643132750e96d805d11e9e48e281fa39a41039286423cc1c045e5442b40bf1c3f2822bded3f9c8ef11cb25da64dda9c7ab87c246bd305385150c98f31465c2a6180fe81d31ea289b916504d5a12e1de26cb10adba84a0cb0c86f94bc14bc554f3018", + recipeConfig: [ + { + "op": "RC2 Decrypt", + "args": [ + {"option": "Hex", "string": "eb970554bb213430f4bb4e5988a6a218"}, + 80, + {"option": "Hex", "string": ""}, + "Hex", "Hex" + ] + } + ], + }, /* The following expectedOutputs are generated with this Python script with pyCryptoDome