Skip to content

Commit 8f24744

Browse files
Added playfair cipher
1 parent 136e6c0 commit 8f24744

File tree

1 file changed

+243
-0
lines changed

1 file changed

+243
-0
lines changed

ciphers/playfair_cipher.cpp

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/**
2+
* @file playfair_cipher.cpp
3+
* @brief Implementation of [Playfair cipher](https://en.wikipedia.org/wiki/Playfair_cipher) algorithm.
4+
*
5+
* @details
6+
* The Playfair cipher is a digraph substitution cipher, meaning that it encrypts pairs of letters (digraphs)
7+
* instead of individual letters. It uses a 5x5 grid filled with the letters of the alphabet (combining 'I' and 'J')
8+
* and a keyword to create the grid. Each pair of letters in the plaintext is encrypted by locating the letters in the grid.
9+
*
10+
* ### Algorithm
11+
* The encryption process works by first finding the letters of the digraph in the 5x5 grid:
12+
* - If the two letters are in the same row, each letter is replaced by the letter to its immediate right (wrapping to the leftmost letter if needed).
13+
* - If the two letters are in the same column, each letter is replaced by the letter immediately below it (wrapping to the topmost letter if needed).
14+
* - If the letters form a rectangle, each letter is replaced by the letter in its row at the other corner of the rectangle.
15+
*
16+
* Decryption reverses these steps using the same key.
17+
*
18+
* For Example:
19+
* If the key is "PLAYFAIR", the 5x5 grid will be:
20+
* ```
21+
* P L A Y F
22+
* I R B C D
23+
* E G H K M
24+
* N O Q S T
25+
* U V W X Z
26+
* ```
27+
* To encrypt the plaintext "HELLO", it would be first split into digraphs "HE", "LX", and "LO" (since "LL" forms a duplicate digraph, an 'X' is inserted).
28+
* The encrypted pairs would then be calculated using the rules above.
29+
*
30+
* \note This implementation assumes that all text is in lowercase and only handles alphabetic characters ('a'-'z'). The letter 'j' is replaced with 'i'.
31+
*
32+
* @author [Jayditya Dhaka]
33+
*/
34+
35+
36+
#include <iostream>
37+
#include <vector>
38+
#include <string>
39+
#include <cassert>
40+
41+
/** \namespace ciphers
42+
* \brief Algorithms for encryption and decryption
43+
*/
44+
namespace ciphers {
45+
/** \namespace playfair
46+
* \brief Functions for [Playfair cipher](https://en.wikipedia.org/wiki/Playfair_cipher) algorithm.
47+
*/
48+
namespace playfair {
49+
50+
namespace {
51+
/**
52+
* This function cleans the input string: converts to lowercase, replaces 'j' with 'i', and removes non-alphabet characters.
53+
* @param input the string to clean
54+
* @return cleaned string with only lowercase alphabetic characters
55+
*/
56+
std::string cleanString(const std::string &input) {
57+
std::string cleaned;
58+
for (char c : input) {
59+
if (std::isalpha(c)) {
60+
cleaned += std::tolower(c == 'j' ? 'i' : c); // Replace 'j' with 'i' and ensure lowercase
61+
}
62+
}
63+
return cleaned;
64+
}
65+
66+
/**
67+
* This function removes duplicate characters from the string while preserving order.
68+
* @param input the string from which duplicates will be removed
69+
* @return string without duplicates
70+
*/
71+
std::string removeDuplicates(const std::string &input) {
72+
std::string result;
73+
for (char c : input) {
74+
if (result.find(c) == std::string::npos) {
75+
result += c;
76+
}
77+
}
78+
return result;
79+
}
80+
81+
/**
82+
* This function formats the plain text by handling duplicate characters and appending 'z' if needed.
83+
* @param text the plain text to format
84+
* @return formatted text with no duplicate letters in pairs
85+
*/
86+
std::string formatPlainText(const std::string &text) {
87+
std::string cleanedText = cleanString(text);
88+
std::string formattedText;
89+
90+
for (size_t i = 0; i < cleanedText.length(); ++i) {
91+
formattedText += cleanedText[i];
92+
if (i < cleanedText.length() - 1 && cleanedText[i] == cleanedText[i + 1]) {
93+
formattedText += 'x'; // Insert 'x' between repeating characters
94+
}
95+
}
96+
97+
if (formattedText.length() % 2 != 0) {
98+
formattedText += 'z'; // Add 'z' if text length is odd
99+
}
100+
101+
return formattedText;
102+
}
103+
104+
/**
105+
* This function creates a Playfair cipher grid based on the cleaned key phrase.
106+
* @param key the key phrase for the Playfair cipher
107+
* @return 5x5 grid of characters for Playfair cipher
108+
*/
109+
std::vector<std::vector<char>> createPlayfairGrid(const std::string &key) {
110+
std::vector<std::vector<char>> grid(5, std::vector<char>(5));
111+
size_t index = 0;
112+
for (int row = 0; row < 5; ++row) {
113+
for (int col = 0; col < 5; ++col) {
114+
grid[row][col] = key[index++];
115+
}
116+
}
117+
return grid;
118+
}
119+
120+
/**
121+
* This function returns the coordinates of a character in the Playfair grid.
122+
* @param c the character to find in the grid
123+
* @param grid the 5x5 Playfair grid
124+
* @return coordinates of the character in the grid as a pair (row, column)
125+
*/
126+
std::pair<int, int> getCharacterCoordinates(char c, const std::vector<std::vector<char>> &grid) {
127+
for (int row = 0; row < 5; ++row) {
128+
for (int col = 0; col < 5; ++col) {
129+
if (grid[row][col] == c) {
130+
return {row, col};
131+
}
132+
}
133+
}
134+
return {-1, -1}; // Should never reach here with valid input
135+
}
136+
} // unnamed namespace
137+
138+
/**
139+
* Encrypts the plain text using the Playfair cipher.
140+
* @param plainText the text to encrypt
141+
* @param keyPhrase the key used for encryption
142+
* @return encrypted text
143+
*/
144+
std::string encrypt(const std::string &plainText, const std::string &keyPhrase) {
145+
std::string formattedText = formatPlainText(plainText);
146+
std::string cleanedKey = cleanString(keyPhrase) + "abcdefghiklmnopqrstuvwxyz";
147+
cleanedKey = removeDuplicates(cleanedKey);
148+
auto grid = createPlayfairGrid(cleanedKey);
149+
150+
std::string encryptedText;
151+
for (size_t i = 0; i < formattedText.length(); i += 2) {
152+
char char1 = formattedText[i];
153+
char char2 = formattedText[i + 1];
154+
std::pair<int, int> coord1 = getCharacterCoordinates(char1, grid);
155+
std::pair<int, int> coord2 = getCharacterCoordinates(char2, grid);
156+
157+
int row1 = coord1.first, col1 = coord1.second;
158+
int row2 = coord2.first, col2 = coord2.second;
159+
160+
if (row1 == row2) {
161+
col1 = (col1 + 1) % 5;
162+
col2 = (col2 + 1) % 5;
163+
} else if (col1 == col2) {
164+
row1 = (row1 + 1) % 5;
165+
row2 = (row2 + 1) % 5;
166+
} else {
167+
std::swap(col1, col2);
168+
}
169+
170+
encryptedText += grid[row1][col1];
171+
encryptedText += grid[row2][col2];
172+
}
173+
174+
return encryptedText;
175+
}
176+
177+
/**
178+
* Decrypts the encrypted text using the Playfair cipher.
179+
* @param cipherText the text to decrypt
180+
* @param keyPhrase the key used for decryption
181+
* @return decrypted text
182+
*/
183+
std::string decrypt(const std::string &cipherText, const std::string &keyPhrase) {
184+
std::string cleanedKey = cleanString(keyPhrase) + "abcdefghiklmnopqrstuvwxyz";
185+
cleanedKey = removeDuplicates(cleanedKey);
186+
auto grid = createPlayfairGrid(cleanedKey);
187+
188+
std::string decryptedText;
189+
for (size_t i = 0; i < cipherText.length(); i += 2) {
190+
char char1 = cipherText[i];
191+
char char2 = cipherText[i + 1];
192+
std::pair<int, int> coord1 = getCharacterCoordinates(char1, grid);
193+
std::pair<int, int> coord2 = getCharacterCoordinates(char2, grid);
194+
195+
int row1 = coord1.first, col1 = coord1.second;
196+
int row2 = coord2.first, col2 = coord2.second;
197+
198+
if (row1 == row2) {
199+
col1 = (col1 - 1 + 5) % 5;
200+
col2 = (col2 - 1 + 5) % 5;
201+
} else if (col1 == col2) {
202+
row1 = (row1 - 1 + 5) % 5;
203+
row2 = (row2 - 1 + 5) % 5;
204+
} else {
205+
std::swap(col1, col2);
206+
}
207+
208+
decryptedText += grid[row1][col1];
209+
decryptedText += grid[row2][col2];
210+
}
211+
212+
return decryptedText;
213+
}
214+
215+
} // namespace playfair
216+
} // namespace ciphers
217+
218+
/**
219+
* Function to test the Playfair cipher algorithm.
220+
*/
221+
void test() {
222+
// Test 1
223+
std::string text1 = "HEYO";
224+
std::string encrypted1 = ciphers::playfair::encrypt(text1, "OLDTAVERN");
225+
std::string decrypted1 = ciphers::playfair::decrypt(encrypted1, "OLDTAVERN");
226+
std::cout << "Original text: " << text1;
227+
std::cout << " , Encrypted text (key = OLDTAVERN): " << encrypted1;
228+
std::cout << " , Decrypted text: " << decrypted1 << std::endl;
229+
230+
// Test 2
231+
std::string text2 = "HELLO";
232+
std::string encrypted2 = ciphers::playfair::encrypt(text2, "OLDTAVERN");
233+
std::string decrypted2 = ciphers::playfair::decrypt(encrypted2, "OLDTAVERN");
234+
std::cout << "Original text: " << text2;
235+
std::cout << " , Encrypted text (key = OLDTAVERN): " << encrypted2;
236+
std::cout << " , Decrypted text: " << decrypted2 << std::endl;
237+
}
238+
239+
/** Driver Code */
240+
int main() {
241+
test();
242+
return 0;
243+
}

0 commit comments

Comments
 (0)