mirror of
https://github.com/gchq/CyberChef
synced 2025-01-16 06:23:53 +00:00
Added 'Strict mode' to 'From Base64' operation
This commit is contained in:
parent
f9a6402825
commit
b78bb2d3d6
4 changed files with 78 additions and 68 deletions
|
@ -82,72 +82,74 @@ export function toBase64(data, alphabet="A-Za-z0-9+/=") {
|
|||
* // returns [72, 101, 108, 108, 111]
|
||||
* fromBase64("SGVsbG8=", null, "byteArray");
|
||||
*/
|
||||
export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true) {
|
||||
export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true, strictMode=false) {
|
||||
if (!data) {
|
||||
return returnType === "string" ? "" : [];
|
||||
}
|
||||
|
||||
alphabet = alphabet || "A-Za-z0-9+/=";
|
||||
alphabet = Utils.expandAlphRange(alphabet).join("");
|
||||
|
||||
// Confirm alphabet is a valid length
|
||||
if (alphabet.length !== 64 && alphabet.length !== 65) { // Allow for padding
|
||||
throw new OperationError(`Invalid Base64 alphabet length (${alphabet.length}): ${alphabet}`);
|
||||
throw new OperationError(`Error: Base64 alphabet should be 64 characters long, or 65 with a padding character. Found ${alphabet.length}: ${alphabet}`);
|
||||
}
|
||||
|
||||
const output = [];
|
||||
let chr1, chr2, chr3,
|
||||
encChr1, encChr2, encChr3, encChr4,
|
||||
enc1, enc2, enc3, enc4,
|
||||
i = 0;
|
||||
|
||||
// Remove non-alphabet characters
|
||||
if (removeNonAlphChars) {
|
||||
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
|
||||
data = data.replace(re, "");
|
||||
}
|
||||
|
||||
if (data.length % 4 === 1) {
|
||||
throw new OperationError(`Invalid Base64 input length (${data.length}): it won't be 4n+1`);
|
||||
}
|
||||
if (strictMode) {
|
||||
// Check for incorrect lengths (even without padding)
|
||||
if (data.length % 4 === 1) {
|
||||
throw new OperationError(`Error: Invalid Base64 input length (${data.length}). Cannot be 4n+1, even without padding chars.`);
|
||||
}
|
||||
|
||||
if (alphabet.length === 65) {
|
||||
const pad = alphabet.charAt(64);
|
||||
const padPos = data.indexOf(pad);
|
||||
if (padPos >= 0) {
|
||||
// padding character should appear only at the end of the input
|
||||
// there should be only one or two padding character(s) if it exists
|
||||
if (padPos < data.length - 2 || data.charAt(data.length - 1) !== pad) {
|
||||
throw new OperationError("Invalid Base64 input: padding character misused");
|
||||
}
|
||||
if (data.length % 4 !== 0) {
|
||||
throw new OperationError("Invalid Base64 input: padded not to multiple of 4");
|
||||
if (alphabet.length === 65) { // Padding character included
|
||||
const pad = alphabet.charAt(64);
|
||||
const padPos = data.indexOf(pad);
|
||||
if (padPos >= 0) {
|
||||
// Check that the padding character is only used at the end and maximum of twice
|
||||
if (padPos < data.length - 2 || data.charAt(data.length - 1) !== pad) {
|
||||
throw new OperationError(`Error: Base64 padding character (${pad}) not used in the correct place.`);
|
||||
}
|
||||
|
||||
// Check that input is padded to the correct length
|
||||
if (data.length % 4 !== 0) {
|
||||
throw new OperationError("Error: Base64 not padded to a multiple of 4.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const output = [];
|
||||
let chr1, chr2, chr3,
|
||||
enc1, enc2, enc3, enc4,
|
||||
i = 0;
|
||||
|
||||
while (i < data.length) {
|
||||
encChr1 = data.charAt(i++);
|
||||
encChr2 = data.charAt(i++);
|
||||
encChr3 = data.charAt(i++);
|
||||
encChr4 = data.charAt(i++);
|
||||
enc1 = alphabet.indexOf(data.charAt(i++));
|
||||
enc2 = alphabet.indexOf(data.charAt(i++));
|
||||
enc3 = alphabet.indexOf(data.charAt(i++));
|
||||
enc4 = alphabet.indexOf(data.charAt(i++));
|
||||
|
||||
enc1 = alphabet.indexOf(encChr1);
|
||||
enc2 = alphabet.indexOf(encChr2);
|
||||
enc3 = alphabet.indexOf(encChr3);
|
||||
enc4 = alphabet.indexOf(encChr4);
|
||||
|
||||
if (enc1 < 0 || enc2 < 0 || enc3 < 0 || enc4 < 0) {
|
||||
throw new OperationError("Invalid Base64 input: contains non-alphabet char(s)");
|
||||
if (strictMode && (enc1 < 0 || enc2 < 0 || enc3 < 0 || enc4 < 0)) {
|
||||
throw new OperationError("Error: Base64 input contains non-alphabet char(s)");
|
||||
}
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output.push(chr1);
|
||||
|
||||
if (encChr3 !== "" && enc3 !== 64) {
|
||||
if (chr1 < 256) {
|
||||
output.push(chr1);
|
||||
}
|
||||
if (chr2 < 256 && enc3 !== 64) {
|
||||
output.push(chr2);
|
||||
}
|
||||
if (encChr4 !== "" && enc4 !== 64) {
|
||||
if (chr3 < 256 && enc4 !== 64) {
|
||||
output.push(chr3);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,93 +34,98 @@ class FromBase64 extends Operation {
|
|||
name: "Remove non-alphabet chars",
|
||||
type: "boolean",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "Strict mode",
|
||||
type: "boolean",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9+/=", true]
|
||||
args: ["A-Za-z0-9+/=", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*[A-Z\\d\\-_]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9-_", true]
|
||||
args: ["A-Za-z0-9-_", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9+\\-=", true]
|
||||
args: ["A-Za-z0-9+\\-=", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z=", true]
|
||||
args: ["./0-9A-Za-z=", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*[A-Z\\d_.]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9_.", true]
|
||||
args: ["A-Za-z0-9_.", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9._-", true]
|
||||
args: ["A-Za-z0-9._-", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["0-9a-zA-Z+/=", true]
|
||||
args: ["0-9a-zA-Z+/=", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["0-9A-Za-z+/=", true]
|
||||
args: ["0-9A-Za-z+/=", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
|
||||
flags: "",
|
||||
args: [" -_", false]
|
||||
args: [" -_", false, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*[A-Z\\d+\\-]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["+\\-0-9A-Za-z", true]
|
||||
args: ["+\\-0-9A-Za-z", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$",
|
||||
flags: "",
|
||||
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true]
|
||||
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["N-ZA-Mn-za-m0-9+/=", true]
|
||||
args: ["N-ZA-Mn-za-m0-9+/=", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*[A-Z\\d./]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z", true]
|
||||
args: ["./0-9A-Za-z", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}CC|[A-Z=\\d\\+/]{3}C)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true]
|
||||
args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true]
|
||||
args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}22|[A-Z=\\d\\+/]{3}2)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true]
|
||||
args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true, false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true]
|
||||
args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true, false]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@ -131,9 +136,9 @@ class FromBase64 extends Operation {
|
|||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [alphabet, removeNonAlphChars] = args;
|
||||
const [alphabet, removeNonAlphChars, strictMode] = args;
|
||||
|
||||
return fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
|
||||
return fromBase64(input, alphabet, "byteArray", removeNonAlphChars, strictMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* To Upper case operation
|
||||
|
@ -38,27 +39,29 @@ class ToUpperCase extends Operation {
|
|||
*/
|
||||
run(input, args) {
|
||||
if (!args || args.length === 0) {
|
||||
throw new OperationException("No capitalization scope was provided.");
|
||||
throw new OperationError("No capitalization scope was provided.");
|
||||
}
|
||||
|
||||
const scope = args[0];
|
||||
|
||||
if (scope === "All") {
|
||||
return input.toUpperCase();
|
||||
}
|
||||
|
||||
const scopeRegex = {
|
||||
"Word": /(\b\w)/gi,
|
||||
"Sentence": /(?:\.|^)\s*(\b\w)/gi,
|
||||
"Paragraph": /(?:\n|^)\s*(\b\w)/gi
|
||||
}[ scope ];
|
||||
if (scopeRegex !== undefined) {
|
||||
// Use the regexes to capitalize the input.
|
||||
return input.replace(scopeRegex, function(m) {
|
||||
return m.toUpperCase();
|
||||
});
|
||||
}
|
||||
else {
|
||||
// The selected scope was invalid.
|
||||
}[scope];
|
||||
|
||||
if (scopeRegex === undefined) {
|
||||
throw new OperationError("Unrecognized capitalization scope");
|
||||
}
|
||||
|
||||
// Use the regex to capitalize the input
|
||||
return input.replace(scopeRegex, function(m) {
|
||||
return m.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,7 +68,7 @@ TestRegister.addTests([
|
|||
{
|
||||
name: "Magic Chain: Base64",
|
||||
input: "WkVkV2VtUkRRbnBrU0Vwd1ltMWpQUT09",
|
||||
expectedMatch: /From_Base64\('A-Za-z0-9\+\/=',true\)\nFrom_Base64\('A-Za-z0-9\+\/=',true\)\nFrom_Base64\('A-Za-z0-9\+\/=',true\)/,
|
||||
expectedMatch: /From_Base64\('A-Za-z0-9\+\/=',true,false\)\nFrom_Base64\('A-Za-z0-9\+\/=',true,false\)\nFrom_Base64\('A-Za-z0-9\+\/=',true,false\)/,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Magic",
|
||||
|
@ -79,7 +79,7 @@ TestRegister.addTests([
|
|||
{
|
||||
name: "Magic Chain: Hex -> Hexdump -> Base64",
|
||||
input: "MDAwMDAwMDAgIDM3IDM0IDIwIDM2IDM1IDIwIDM3IDMzIDIwIDM3IDM0IDIwIDMyIDMwIDIwIDM3ICB8NzQgNjUgNzMgNzQgMjAgN3wKMDAwMDAwMTAgIDMzIDIwIDM3IDM0IDIwIDM3IDMyIDIwIDM2IDM5IDIwIDM2IDY1IDIwIDM2IDM3ICB8MyA3NCA3MiA2OSA2ZSA2N3w=",
|
||||
expectedMatch: /From_Base64\('A-Za-z0-9\+\/=',true\)\nFrom_Hexdump\(\)\nFrom_Hex\('Space'\)/,
|
||||
expectedMatch: /From_Base64\('A-Za-z0-9\+\/=',true,false\)\nFrom_Hexdump\(\)\nFrom_Hex\('Space'\)/,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Magic",
|
||||
|
|
Loading…
Reference in a new issue