From 9929ab7fc14fae9e5d685f689583939895ab68a8 Mon Sep 17 00:00:00 2001 From: Sherlyn <31661914+rollingcomma@users.noreply.github.com> Date: Wed, 16 Dec 2020 22:26:21 -0800 Subject: [PATCH] add hillCipher at cryptography section (#424) * add hillCipher.js and its test case first commit * add README.md * update style Co-authored-by: Oleksii Trekhleb --- .../cryptography/hillCipher/README.md | 27 ++++++++ .../hillCipher/_test_/hillCipher.test.js | 25 +++++++ .../cryptography/hillCipher/hillCipher.js | 68 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 src/algorithms/cryptography/hillCipher/README.md create mode 100644 src/algorithms/cryptography/hillCipher/_test_/hillCipher.test.js create mode 100644 src/algorithms/cryptography/hillCipher/hillCipher.js diff --git a/src/algorithms/cryptography/hillCipher/README.md b/src/algorithms/cryptography/hillCipher/README.md new file mode 100644 index 00000000..99e73fa1 --- /dev/null +++ b/src/algorithms/cryptography/hillCipher/README.md @@ -0,0 +1,27 @@ +# Hill Cipher + +* The Hill cipher is a polygraphic substitution cipher based on linear algebra. +Each letter is represented by a number modulo 26. + +* Encryption: to encrypt a message, each block of n letters (considered as an n-component vector) is multiplied by an invertible n × n matrix, against modulus 26. +Consider the message 'ACT', and the key below (or GYB/NQK/URP in letters): + | 6 24 1 | + | 13 16 10| + | 20 17 15| +The message is the vector: + | 0 | + | 2 | + | 19 | +Thus the enciphered vector is given by + | 6 24 1 | | 0 | | 67 | | 15 | + | 13 16 10| | 2 | = | 222 | ≡ | 14 | (mod 26) + | 20 17 15| | 19 | | 319 | | 7 | +which corresponds to a ciphertext of 'POH'. + +* Decryption: to decrypt the message, each block is multiplied by the inverse of the matrix used for encryption. + + +## Reference +- [Wikipedia] https://en.wikipedia.org/wiki/Hill_cipher +- [GeeksforGeeks]https://www.geeksforgeeks.org/hill-cipher/ + diff --git a/src/algorithms/cryptography/hillCipher/_test_/hillCipher.test.js b/src/algorithms/cryptography/hillCipher/_test_/hillCipher.test.js new file mode 100644 index 00000000..7c50f913 --- /dev/null +++ b/src/algorithms/cryptography/hillCipher/_test_/hillCipher.test.js @@ -0,0 +1,25 @@ +import hillCipherEncrypt from '../hillCipher'; + +describe('hillCipher', () => { + it('should throw an error when the length of the keyString does not equal to the power of length of the message ', () => { + const invalidLenghOfkeyString = () => { + hillCipherEncrypt('hello', 'helloworld'); + }; + + expect(invalidLenghOfkeyString).toThrowError(); + }); + it('should throw an error when message or keyString contains none letter character', () => { + const invalidCharacterInMessage = () => { + hillCipherEncrypt('hell3', 'helloworld'); + }; + const invalidCharacterInKeyString = () => { + hillCipherEncrypt('hello', 'hel12world'); + }; + expect(invalidCharacterInMessage).toThrowError(); + expect(invalidCharacterInKeyString).toThrowError(); + }); + it('should encrypt passed message using Hill Cipher', () => { + expect(hillCipherEncrypt('ACT', 'GYBNQKURP')).toBe('POH'); + expect(hillCipherEncrypt('GFG', 'HILLMAGIC')).toBe('SWK'); + }); +}); diff --git a/src/algorithms/cryptography/hillCipher/hillCipher.js b/src/algorithms/cryptography/hillCipher/hillCipher.js new file mode 100644 index 00000000..ac497a41 --- /dev/null +++ b/src/algorithms/cryptography/hillCipher/hillCipher.js @@ -0,0 +1,68 @@ + +/** + * generate key matrix from given keyString + * + * @param {integer} length + * @param {string} keyString + * @return {Array[][]} keyMatrix + */ +const generateKeyMatrix = (length, keyString) => { + const keyMatrix = []; + let keyStringIndex = 0; + for (let i = 0; i < length; i += 1) { + const keyMatrixRow = []; + for (let j = 0; j < length; j += 1) { + keyMatrixRow.push((keyString.codePointAt(keyStringIndex)) % 65); + keyStringIndex += 1; + } + keyMatrix.push(keyMatrixRow); + } + return keyMatrix; +}; + +/** + * generate message vector from given message + * + * @param {*} message + * @return {Array} messageVector + */ +const generateMessageVector = (message) => { + const messageVector = []; + for (let i = 0; i < message.length; i += 1) { + messageVector.push(message.codePointAt(i) % 65); + } + return messageVector; +}; + +/** + * validate data and encrypt message from given message and keyString + * + * @param {string} message plaintext + * @param {string} keyString + * @return {string} cipherString + * + */ + +export default function hillCipherEncrypt(message, keyString) { + const length = keyString.length ** (0.5); + // keyString.length must equal to square of message.length + if (!Number.isInteger(length) && length !== message.length) { + throw new Error('invalid key string length'); + } + // keyString and messange can only contain letters + if (!(/^[a-zA-Z]+$/.test(message)) || !(/^[A-Za-z]+$/.test(keyString))) { + throw new Error('messange and key string can only contain letters'); + } + + const keyMatrix = generateKeyMatrix(length, keyString); + const messageVector = generateMessageVector(message); + let ciperString = ''; + for (let row = 0; row < length; row += 1) { + let item = 0; + for (let column = 0; column < length; column += 1) { + item += keyMatrix[row][column] * messageVector[column]; + } + ciperString += String.fromCharCode((item % 26) + 65); + } + return ciperString; +}