mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-10 11:09:43 +08:00
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:
parent
e105460cd6
commit
9929ab7fc1
27
src/algorithms/cryptography/hillCipher/README.md
Normal file
27
src/algorithms/cryptography/hillCipher/README.md
Normal 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/
|
||||||
|
|
@ -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');
|
||||||
|
});
|
||||||
|
});
|
68
src/algorithms/cryptography/hillCipher/hillCipher.js
Normal file
68
src/algorithms/cryptography/hillCipher/hillCipher.js
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user