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