mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-24 08:46:19 -04:00
Refactor affine encrypt/decrypt functions
This commit is contained in:
parent
de57002c6f
commit
979de8e488
1 changed files with 154 additions and 0 deletions
|
@ -51,6 +51,141 @@ export function affineEncode(input, args) {
|
|||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic affine encrypt/decrypt operation.
|
||||
* Allows for an expanded alphabet.
|
||||
*
|
||||
* @author Barry B [profbbrown@gmail.com]
|
||||
* @param {string} input
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
* @param {string} alphabet
|
||||
* @param {function} affineFn
|
||||
* @returns {string}
|
||||
*/
|
||||
export function affineApplication(input, a, b, alphabet, affineFn) {
|
||||
alphabet = Utils.expandAlphRange(alphabet);
|
||||
let output = "";
|
||||
const modulus = alphabet.length;
|
||||
|
||||
// If the alphabet contains letters of all the same case,
|
||||
// the assumption will be to match case.
|
||||
const hasLower = /[a-z]/.test(alphabet);
|
||||
const hasUpper = /[A-Z]/.test(alphabet);
|
||||
const matchCase = (hasLower && hasUpper) ? false : true;
|
||||
|
||||
// If we are matching case, convert entire alphabet to lowercase.
|
||||
// This will simplify the encryption.
|
||||
if (matchCase) {
|
||||
for (let i = 0; i < alphabet.length; i++)
|
||||
alphabet[i] = alphabet[i].toLowerCase();
|
||||
}
|
||||
|
||||
if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
|
||||
throw new OperationError("The values of a and b can only be integers.");
|
||||
}
|
||||
|
||||
if (Utils.gcd(a, modulus) !== 1) {
|
||||
throw new OperationError("The value of `a` must be coprime to " + modulus + ".");
|
||||
}
|
||||
|
||||
// Apply affine function to each character in the input
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
let outChar = "";
|
||||
|
||||
let inChar = input[i];
|
||||
if (matchCase && isUpperCase(inChar)) inChar = inChar.toLowerCase();
|
||||
|
||||
const inVal = alphabet.indexOf(inChar);
|
||||
|
||||
if (inVal >= 0) {
|
||||
outChar = alphabet[affineFn(inVal, a, b, modulus)];
|
||||
if (matchCase && isUpperCase(input[i])) outChar = outChar.toUpperCase();
|
||||
} else {
|
||||
outChar += input[i];
|
||||
}
|
||||
|
||||
output += outChar;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the affine encryption function to p.
|
||||
*
|
||||
* @author Barry B [profbbrown@gmail.com]
|
||||
* @param {integer} p - Plaintext value
|
||||
* @param {integer} a - Multiplier coefficient
|
||||
* @param {integer} b - Addition coefficient
|
||||
* @param {integer} m - Modulus
|
||||
* @returns {integer}
|
||||
*/
|
||||
const encryptFn = function(p, a, b, m) {
|
||||
return (a * p + b) % m;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply the affine decryption function to c.
|
||||
*
|
||||
* @author Barry B [profbbrown@gmail.com]
|
||||
* @param {integer} c - Ciphertext value
|
||||
* @param {integer} a - Multiplier coefficient
|
||||
* @param {integer} b - Addition coefficient
|
||||
* @param {integer} m - Modulus
|
||||
* @returns {integer}
|
||||
*/
|
||||
const decryptFn = function(c, a, b, m) {
|
||||
return ((c + b) * a) % m;
|
||||
};
|
||||
|
||||
/**
|
||||
* Affine encrypt operation.
|
||||
* Allows for an expanded alphabet.
|
||||
*
|
||||
* @author Barry B [profbbrown@gmail.com]
|
||||
* @param {string} input
|
||||
* @param {integer} a
|
||||
* @param {integer} b
|
||||
* @param {string} alphabet
|
||||
* @returns {string}
|
||||
*/
|
||||
export function affineEncrypt(input, a, b, alphabet="a-z") {
|
||||
return affineApplication(input, a, b, alphabet, encryptFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affine Cipher Decrypt operation using the coefficients that were used to encrypt.
|
||||
* The modular inverses will be calculated.
|
||||
*
|
||||
* @author Barry B [profbbrown@gmail.com]
|
||||
* @param {string} input
|
||||
* @param {integer} a
|
||||
* @param {integer} b
|
||||
* @param {string} alphabet
|
||||
* @returns {string}
|
||||
*/
|
||||
export function affineDecrypt(input, a, b, alphabet="a-z") {
|
||||
const m = Utils.expandAlphRange(alphabet).length;
|
||||
const aInv = Utils.modInv(a, m);
|
||||
const bInv = (m - b) % m;
|
||||
return affineApplication(input, aInv, bInv, alphabet, decryptFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affine Cipher Decrypt operation using modular inverse coefficients
|
||||
* supplied by the user.
|
||||
*
|
||||
* @author Barry B [profbbrown@gmail.com]
|
||||
* @param {string} input
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
* @param {string} alphabet
|
||||
* @returns {string}
|
||||
*/
|
||||
export function affineDecryptInverse(input, a, b, alphabet="a-z") {
|
||||
return affineApplication(input, a, b, alphabet, decryptFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a polybius square for the given keyword
|
||||
*
|
||||
|
@ -86,3 +221,22 @@ export const format = {
|
|||
"UTF16BE": CryptoJS.enc.Utf16BE,
|
||||
"Latin1": CryptoJS.enc.Latin1,
|
||||
};
|
||||
|
||||
export const AFFINE_ALPHABETS = [
|
||||
{name: "Letters, match case: a-z", value: "a-z"},
|
||||
{name: "Letters, case sensitive: A-Za-z", value: "A-Za-z"},
|
||||
{name: "Word characters: A-Za-z0-9_", value: "A-Za-z0-9_"},
|
||||
{name: "Printable ASCII: sp-~", value: " -~"}
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns true if the given character is uppercase
|
||||
*
|
||||
* @private
|
||||
* @author Barry B [profbbrown@gmail.com]
|
||||
* @param {string} c - A character
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isUpperCase(c) {
|
||||
return c.toUpperCase() === c;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue