mirror of
https://github.com/trekhleb/javascript-algorithms.git
synced 2025-12-08 19:06:00 +00:00
88 lines
2.8 KiB
JavaScript
88 lines
2.8 KiB
JavaScript
import * as mtrx from '../../math/matrix/Matrix';
|
|
|
|
// The code of an 'A' character (equals to 65).
|
|
const alphabetCodeShift = 'A'.codePointAt(0);
|
|
const englishAlphabetSize = 26;
|
|
|
|
/**
|
|
* Generates key matrix from given keyString.
|
|
*
|
|
* @param {string} keyString - a string to build a key matrix (must be of matrixSize^2 length).
|
|
* @return {number[][]} keyMatrix
|
|
*/
|
|
const generateKeyMatrix = (keyString) => {
|
|
const matrixSize = Math.sqrt(keyString.length);
|
|
if (!Number.isInteger(matrixSize)) {
|
|
throw new Error(
|
|
'Invalid key string length. The square root of the key string must be an integer',
|
|
);
|
|
}
|
|
let keyStringIndex = 0;
|
|
return mtrx.generate(
|
|
[matrixSize, matrixSize],
|
|
// Callback to get a value of each matrix cell.
|
|
// The order the matrix is being filled in is from left to right, from top to bottom.
|
|
() => {
|
|
// A → 0, B → 1, ..., a → 32, b → 33, ...
|
|
const charCodeShifted = (keyString.codePointAt(keyStringIndex)) % alphabetCodeShift;
|
|
keyStringIndex += 1;
|
|
return charCodeShifted;
|
|
},
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Generates a message vector from a given message.
|
|
*
|
|
* @param {string} message - the message to encrypt.
|
|
* @return {number[][]} messageVector
|
|
*/
|
|
const generateMessageVector = (message) => {
|
|
return mtrx.generate(
|
|
[message.length, 1],
|
|
// Callback to get a value of each matrix cell.
|
|
// The order the matrix is being filled in is from left to right, from top to bottom.
|
|
(cellIndices) => {
|
|
const rowIndex = cellIndices[0];
|
|
return message.codePointAt(rowIndex) % alphabetCodeShift;
|
|
},
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Encrypts the given message using Hill Cipher.
|
|
*
|
|
* @param {string} message plaintext
|
|
* @param {string} keyString
|
|
* @return {string} cipherString
|
|
*/
|
|
export function hillCipherEncrypt(message, keyString) {
|
|
// The keyString and message can only contain letters.
|
|
const onlyLettersRegExp = /^[a-zA-Z]+$/;
|
|
if (!onlyLettersRegExp.test(message) || !onlyLettersRegExp.test(keyString)) {
|
|
throw new Error('The message and key string can only contain letters');
|
|
}
|
|
|
|
const keyMatrix = generateKeyMatrix(keyString);
|
|
const messageVector = generateMessageVector(message);
|
|
|
|
// keyString.length must equal to square of message.length
|
|
if (keyMatrix.length !== message.length) {
|
|
throw new Error('Invalid key string length. The key length must be a square of message length');
|
|
}
|
|
|
|
const cipherVector = mtrx.dot(keyMatrix, messageVector);
|
|
let cipherString = '';
|
|
for (let row = 0; row < cipherVector.length; row += 1) {
|
|
const item = cipherVector[row];
|
|
cipherString += String.fromCharCode((item % englishAlphabetSize) + alphabetCodeShift);
|
|
}
|
|
|
|
return cipherString;
|
|
}
|
|
|
|
// @TODO: Implement this method.
|
|
export const hillCipherDecrypt = () => {
|
|
throw new Error('This method is not implemented yet');
|
|
};
|