-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat: Add playfair cipher #2813
Closed
Closed
Changes from 8 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
8f24744
Added playfair cipher
JaydityaDhaka 7333371
Merge branch 'master' into master
JaydityaDhaka 9221252
Update playfair_cipher.cpp
JaydityaDhaka e82c87b
Merge branch 'master' into master
JaydityaDhaka 966fedd
Update playfair_cipher.cpp
JaydityaDhaka 87c25d6
Merge branch 'master' into master
JaydityaDhaka cc97737
Merge branch 'master' into master
JaydityaDhaka a0e4469
Merge branch 'master' into master
JaydityaDhaka ead232c
Merge branch 'master' into master
JaydityaDhaka f30bbe6
Merge branch 'master' into master
JaydityaDhaka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
/** | ||
* @file playfair_cipher.cpp | ||
* @brief Implementation of [Playfair cipher](https://en.wikipedia.org/wiki/Playfair_cipher) algorithm. | ||
* | ||
* @details | ||
* The Playfair cipher is a digraph substitution cipher, meaning that it encrypts pairs of letters (digraphs) | ||
* instead of individual letters. It uses a 5x5 grid filled with the letters of the alphabet (combining 'I' and 'J') | ||
* and a keyword to create the grid. Each pair of letters in the plaintext is encrypted by locating the letters in the grid. | ||
* | ||
* @algorithm | ||
* The encryption process works by first finding the letters of the digraph in the 5x5 grid: | ||
* - 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). | ||
* - 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). | ||
* - If the letters form a rectangle, each letter is replaced by the letter in its row at the other corner of the rectangle. | ||
* | ||
* Decryption reverses these steps using the same key. | ||
* | ||
* @example | ||
* For Example: | ||
* If the key is "PLAYFAIR", the 5x5 grid will be: | ||
* ``` | ||
* P L A Y F | ||
* I R B C D | ||
* E G H K M | ||
* N O Q S T | ||
* U V W X Z | ||
* ``` | ||
* 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). | ||
* The encrypted pairs would then be calculated using the rules above. | ||
* | ||
* \note This implementation assumes that all text is in lowercase and only handles alphabetic characters ('a'-'z'). The letter 'j' is replaced with 'i'. | ||
* | ||
* @author [Jayditya Dhaka] | ||
*/ | ||
|
||
|
||
#include <iostream> // For input/output stream operations | ||
#include <vector> // For using vectors to create the 5x5 grid | ||
#include <string> // For handling strings | ||
#include <cassert> // For assertions in case of unexpected input | ||
|
||
/** @namespace ciphers | ||
* @brief Algorithms for encryption and decryption | ||
*/ | ||
namespace ciphers { | ||
/** @namespace playfair | ||
* @brief Functions for [Playfair cipher](https://en.wikipedia.org/wiki/Playfair_cipher) algorithm. | ||
*/ | ||
namespace playfair { | ||
|
||
namespace { | ||
/** | ||
* This function cleans the input string: converts to lowercase, replaces 'j' with 'i', and removes non-alphabet characters. | ||
* @param input the string to clean | ||
* @return cleaned string with only lowercase alphabetic characters | ||
*/ | ||
std::string cleanString(const std::string &input) { | ||
std::string cleaned; | ||
for (char c : input) { | ||
if (std::isalpha(c)) { | ||
cleaned += std::tolower(c == 'j' ? 'i' : c); // Replace 'j' with 'i' and ensure lowercase | ||
} | ||
} | ||
return cleaned; | ||
} | ||
|
||
/** | ||
* This function removes duplicate characters from the string while preserving order. | ||
* @param input the string from which duplicates will be removed | ||
* @return string without duplicates | ||
*/ | ||
std::string removeDuplicates(const std::string &input) { | ||
std::string result; | ||
for (char c : input) { | ||
if (result.find(c) == std::string::npos) { | ||
result += c; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* This function formats the plain text by handling duplicate characters and appending 'z' if needed. | ||
* @param text the plain text to format | ||
* @return formatted text with no duplicate letters in pairs | ||
*/ | ||
std::string formatPlainText(const std::string &text) { | ||
std::string cleanedText = cleanString(text); | ||
std::string formattedText; | ||
|
||
for (size_t i = 0; i < cleanedText.length(); ++i) { | ||
formattedText += cleanedText[i]; | ||
if (i < cleanedText.length() - 1 && cleanedText[i] == cleanedText[i + 1]) { | ||
formattedText += 'x'; // Insert 'x' between repeating characters | ||
} | ||
} | ||
|
||
if (formattedText.length() % 2 != 0) { | ||
formattedText += 'z'; // Add 'z' if text length is odd | ||
} | ||
|
||
return formattedText; | ||
} | ||
|
||
/** | ||
* This function creates a Playfair cipher grid based on the cleaned key phrase. | ||
* @param key the key phrase for the Playfair cipher | ||
* @return 5x5 grid of characters for Playfair cipher | ||
*/ | ||
std::vector<std::vector<char>> createPlayfairGrid(const std::string &key) { | ||
std::vector<std::vector<char>> grid(5, std::vector<char>(5)); | ||
size_t index = 0; | ||
for (int row = 0; row < 5; ++row) { | ||
for (int col = 0; col < 5; ++col) { | ||
grid[row][col] = key[index++]; | ||
} | ||
} | ||
return grid; | ||
} | ||
|
||
/** | ||
* This function returns the coordinates of a character in the Playfair grid. | ||
* @param c the character to find in the grid | ||
* @param grid the 5x5 Playfair grid | ||
* @return coordinates of the character in the grid as a pair (row, column) | ||
*/ | ||
std::pair<int, int> getCharacterCoordinates(char c, const std::vector<std::vector<char>> &grid) { | ||
for (int row = 0; row < 5; ++row) { | ||
for (int col = 0; col < 5; ++col) { | ||
if (grid[row][col] == c) { | ||
return {row, col}; | ||
} | ||
} | ||
} | ||
return {-1, -1}; // Should never reach here with valid input | ||
} | ||
} // unnamed namespace | ||
|
||
/** | ||
* Encrypts the plain text using the Playfair cipher. | ||
* @param plainText the text to encrypt | ||
* @param keyPhrase the key used for encryption | ||
* @return encrypted text | ||
*/ | ||
std::string encrypt(const std::string &plainText, const std::string &keyPhrase) { | ||
std::string formattedText = formatPlainText(plainText); | ||
std::string cleanedKey = cleanString(keyPhrase) + "abcdefghiklmnopqrstuvwxyz"; | ||
cleanedKey = removeDuplicates(cleanedKey); | ||
auto grid = createPlayfairGrid(cleanedKey); | ||
|
||
std::string encryptedText; | ||
for (size_t i = 0; i < formattedText.length(); i += 2) { | ||
char char1 = formattedText[i]; | ||
char char2 = formattedText[i + 1]; | ||
std::pair<int, int> coord1 = getCharacterCoordinates(char1, grid); | ||
std::pair<int, int> coord2 = getCharacterCoordinates(char2, grid); | ||
|
||
int row1 = coord1.first, col1 = coord1.second; | ||
int row2 = coord2.first, col2 = coord2.second; | ||
|
||
if (row1 == row2) { | ||
col1 = (col1 + 1) % 5; | ||
col2 = (col2 + 1) % 5; | ||
} else if (col1 == col2) { | ||
row1 = (row1 + 1) % 5; | ||
row2 = (row2 + 1) % 5; | ||
} else { | ||
std::swap(col1, col2); | ||
} | ||
|
||
encryptedText += grid[row1][col1]; | ||
encryptedText += grid[row2][col2]; | ||
} | ||
|
||
return encryptedText; | ||
} | ||
|
||
/** | ||
* Decrypts the encrypted text using the Playfair cipher. | ||
* @param cipherText the text to decrypt | ||
* @param keyPhrase the key used for decryption | ||
* @return decrypted text | ||
*/ | ||
std::string decrypt(const std::string &cipherText, const std::string &keyPhrase) { | ||
std::string cleanedKey = cleanString(keyPhrase) + "abcdefghiklmnopqrstuvwxyz"; | ||
cleanedKey = removeDuplicates(cleanedKey); | ||
auto grid = createPlayfairGrid(cleanedKey); | ||
|
||
std::string decryptedText; | ||
for (size_t i = 0; i < cipherText.length(); i += 2) { | ||
char char1 = cipherText[i]; | ||
char char2 = cipherText[i + 1]; | ||
std::pair<int, int> coord1 = getCharacterCoordinates(char1, grid); | ||
std::pair<int, int> coord2 = getCharacterCoordinates(char2, grid); | ||
|
||
int row1 = coord1.first, col1 = coord1.second; | ||
int row2 = coord2.first, col2 = coord2.second; | ||
|
||
if (row1 == row2) { | ||
col1 = (col1 - 1 + 5) % 5; | ||
col2 = (col2 - 1 + 5) % 5; | ||
} else if (col1 == col2) { | ||
row1 = (row1 - 1 + 5) % 5; | ||
row2 = (row2 - 1 + 5) % 5; | ||
} else { | ||
std::swap(col1, col2); | ||
} | ||
|
||
decryptedText += grid[row1][col1]; | ||
decryptedText += grid[row2][col2]; | ||
} | ||
|
||
return decryptedText; | ||
} | ||
|
||
} // namespace playfair | ||
} // namespace ciphers | ||
|
||
void test() { | ||
// Test 1 | ||
std::string text1 = "heyo"; | ||
std::string encrypted1 = ciphers::playfair::encrypt(text1, "oldtavern"); | ||
std::string decrypted1 = ciphers::playfair::decrypt(encrypted1, "oldtavern"); | ||
assert(text1 == decrypted1); | ||
|
||
// Test 2 | ||
std::string text2 = "maid"; | ||
std::string encrypted2 = ciphers::playfair::encrypt(text2, "oldtavern"); | ||
std::string decrypted2 = ciphers::playfair::decrypt(encrypted2, "oldtavern"); | ||
assert(text2 == decrypted2); | ||
} | ||
|
||
/** Driver Code */ | ||
int main() { | ||
test(); | ||
return 0; | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lint your file using clang tidy please, Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
file does not seem linted!