-
Notifications
You must be signed in to change notification settings - Fork 31.1k
Expand file tree
/
Copy pathplayfairCipher.js
More file actions
109 lines (90 loc) · 2.75 KB
/
playfairCipher.js
File metadata and controls
109 lines (90 loc) · 2.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Generate a 5x5 Playfair cipher key matrix based on a given keyword.
// Combine 'I' and 'J' into a single letter, as per the standard Playfair rules.
const generateKeyMatrix = (key) => {
const alphabet = 'abcdefghiklmnopqrstuvwxyz'; // 'j' is merged with 'i'
const filteredKey = Array.from(new Set(
key.toLowerCase().replace(/[^a-z]/g, '').replace(/j/g, 'i'),
));
const matrixSet = new Set(filteredKey);
alphabet.split('').forEach((ch) => matrixSet.add(ch));
const matrixArr = Array.from(matrixSet);
const matrix = [];
while (matrixArr.length) {
matrix.push(matrixArr.splice(0, 5));
}
return matrix;
};
// Find letter position in 5x5 matrix
const findPosition = (matrix, char) => {
for (let row = 0; row < 5; row += 1) {
const col = matrix[row].indexOf(char);
if (col !== -1) return [row, col];
}
return [-1, -1];
};
// Prepare text into digraphs
const prepareText = (text, forDecryption = false) => {
const cleanText = text.toLowerCase().replace(/[^a-z]/g, '').replace(/j/g, 'i');
const pairs = [];
let i = 0;
while (i < cleanText.length) {
const first = cleanText[i];
let second = cleanText[i + 1];
if (!forDecryption) {
if (first === second) {
second = 'x'; // insert x for duplicate
i += 1;
} else {
i += 2;
}
} else {
i += 2;
}
if (!second) second = 'x'; // pad last char
pairs.push(first + second);
}
return pairs;
};
const playfairEncrypt = (plaintext, key) => {
const matrix = generateKeyMatrix(key);
const pairs = prepareText(plaintext);
let ciphertext = '';
pairs.forEach((pair) => {
const [a, b] = pair.split('');
let [rowA, colA] = findPosition(matrix, a);
let [rowB, colB] = findPosition(matrix, b);
if (rowA === rowB) {
colA = (colA + 1) % 5;
colB = (colB + 1) % 5;
} else if (colA === colB) {
rowA = (rowA + 1) % 5;
rowB = (rowB + 1) % 5;
} else {
[colA, colB] = [colB, colA];
}
ciphertext += matrix[rowA][colA] + matrix[rowB][colB];
});
return ciphertext;
};
const playfairDecrypt = (ciphertext, key) => {
const matrix = generateKeyMatrix(key);
const pairs = prepareText(ciphertext, true);
let plaintext = '';
pairs.forEach((pair) => {
const [a, b] = pair.split('');
let [rowA, colA] = findPosition(matrix, a);
let [rowB, colB] = findPosition(matrix, b);
if (rowA === rowB) {
colA = (colA + 4) % 5; // move left
colB = (colB + 4) % 5;
} else if (colA === colB) {
rowA = (rowA + 4) % 5; // move up
rowB = (rowB + 4) % 5;
} else {
[colA, colB] = [colB, colA];
}
plaintext += matrix[rowA][colA] + matrix[rowB][colB];
});
return plaintext;
};
export { playfairEncrypt, playfairDecrypt, generateKeyMatrix };