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 <trehleb@gmail.com>
This commit is contained in:
Sherlyn 2020-12-16 22:26:21 -08:00 committed by GitHub
parent e105460cd6
commit 9929ab7fc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 120 additions and 0 deletions

View File

@ -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/

View File

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

View File

@ -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;
}