Skip to content

Commit 9929ab7

Browse files
add hillCipher at cryptography section (trekhleb#424)
* add hillCipher.js and its test case first commit * add README.md * update style Co-authored-by: Oleksii Trekhleb <[email protected]>
1 parent e105460 commit 9929ab7

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Hill Cipher
2+
3+
* The Hill cipher is a polygraphic substitution cipher based on linear algebra.
4+
Each letter is represented by a number modulo 26.
5+
6+
* 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.
7+
Consider the message 'ACT', and the key below (or GYB/NQK/URP in letters):
8+
| 6 24 1 |
9+
| 13 16 10|
10+
| 20 17 15|
11+
The message is the vector:
12+
| 0 |
13+
| 2 |
14+
| 19 |
15+
Thus the enciphered vector is given by
16+
| 6 24 1 | | 0 | | 67 | | 15 |
17+
| 13 16 10| | 2 | = | 222 | ≡ | 14 | (mod 26)
18+
| 20 17 15| | 19 | | 319 | | 7 |
19+
which corresponds to a ciphertext of 'POH'.
20+
21+
* Decryption: to decrypt the message, each block is multiplied by the inverse of the matrix used for encryption.
22+
23+
24+
## Reference
25+
- [Wikipedia] https://en.wikipedia.org/wiki/Hill_cipher
26+
- [GeeksforGeeks]https://www.geeksforgeeks.org/hill-cipher/
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import hillCipherEncrypt from '../hillCipher';
2+
3+
describe('hillCipher', () => {
4+
it('should throw an error when the length of the keyString does not equal to the power of length of the message ', () => {
5+
const invalidLenghOfkeyString = () => {
6+
hillCipherEncrypt('hello', 'helloworld');
7+
};
8+
9+
expect(invalidLenghOfkeyString).toThrowError();
10+
});
11+
it('should throw an error when message or keyString contains none letter character', () => {
12+
const invalidCharacterInMessage = () => {
13+
hillCipherEncrypt('hell3', 'helloworld');
14+
};
15+
const invalidCharacterInKeyString = () => {
16+
hillCipherEncrypt('hello', 'hel12world');
17+
};
18+
expect(invalidCharacterInMessage).toThrowError();
19+
expect(invalidCharacterInKeyString).toThrowError();
20+
});
21+
it('should encrypt passed message using Hill Cipher', () => {
22+
expect(hillCipherEncrypt('ACT', 'GYBNQKURP')).toBe('POH');
23+
expect(hillCipherEncrypt('GFG', 'HILLMAGIC')).toBe('SWK');
24+
});
25+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
/**
3+
* generate key matrix from given keyString
4+
*
5+
* @param {integer} length
6+
* @param {string} keyString
7+
* @return {Array[][]} keyMatrix
8+
*/
9+
const generateKeyMatrix = (length, keyString) => {
10+
const keyMatrix = [];
11+
let keyStringIndex = 0;
12+
for (let i = 0; i < length; i += 1) {
13+
const keyMatrixRow = [];
14+
for (let j = 0; j < length; j += 1) {
15+
keyMatrixRow.push((keyString.codePointAt(keyStringIndex)) % 65);
16+
keyStringIndex += 1;
17+
}
18+
keyMatrix.push(keyMatrixRow);
19+
}
20+
return keyMatrix;
21+
};
22+
23+
/**
24+
* generate message vector from given message
25+
*
26+
* @param {*} message
27+
* @return {Array} messageVector
28+
*/
29+
const generateMessageVector = (message) => {
30+
const messageVector = [];
31+
for (let i = 0; i < message.length; i += 1) {
32+
messageVector.push(message.codePointAt(i) % 65);
33+
}
34+
return messageVector;
35+
};
36+
37+
/**
38+
* validate data and encrypt message from given message and keyString
39+
*
40+
* @param {string} message plaintext
41+
* @param {string} keyString
42+
* @return {string} cipherString
43+
*
44+
*/
45+
46+
export default function hillCipherEncrypt(message, keyString) {
47+
const length = keyString.length ** (0.5);
48+
// keyString.length must equal to square of message.length
49+
if (!Number.isInteger(length) && length !== message.length) {
50+
throw new Error('invalid key string length');
51+
}
52+
// keyString and messange can only contain letters
53+
if (!(/^[a-zA-Z]+$/.test(message)) || !(/^[A-Za-z]+$/.test(keyString))) {
54+
throw new Error('messange and key string can only contain letters');
55+
}
56+
57+
const keyMatrix = generateKeyMatrix(length, keyString);
58+
const messageVector = generateMessageVector(message);
59+
let ciperString = '';
60+
for (let row = 0; row < length; row += 1) {
61+
let item = 0;
62+
for (let column = 0; column < length; column += 1) {
63+
item += keyMatrix[row][column] * messageVector[column];
64+
}
65+
ciperString += String.fromCharCode((item % 26) + 65);
66+
}
67+
return ciperString;
68+
}

0 commit comments

Comments
 (0)