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'); };