Files
CipherStream/SourceFiles/Playfair.cpp

570 lines
14 KiB
C++

//Ciphers/Headers/Playfair.cpp
//Matthew Ellison
// Created: 4-25-18
//Modified: 5-16-18
//This file contains the implementation of the Playfair class
//It is designed to encrypt and decrypt strings using the Playfair cipher
#include "../Headers/Playfair.hpp"
#include <string>
#include <cctype>
/**
* @brief The current library's version number
*
*/
const std::string Playfair::version = "1.0.1";
///The letter that needs replaced for the cipher to work
char Playfair::REPLACED = 'J';
///The letter that replaces REPLACED
char Playfair::REPLACER = 'I';
///The letter that is added to the end to make the message of even length and placed between 2 ajoining letters that are the same
char Playfair::DOUBLED = 'X';
/**
* @brief Construct a new Playfair object
*
*/
Playfair::Playfair(){
reset();
}
/**
* @brief Destroy the Playfair object
*
*/
Playfair::~Playfair(){
}
/**
* @brief Uses keyword to set the characters in grid correctly
*
*/
void Playfair::createGrid(){
unsigned int row, column;
bool found = false;
row = column = 0;
//Add any new leters from the keyword to the grid
//If you reach row 5 then the entire grid has been filled
char current;
for(unsigned int cnt = 0;(cnt < keyword.size()) && (row < 5);++cnt){
current = keyword[cnt];
//If the current letter needs to be replaced, do so
if(current == REPLACED){
current = REPLACER;
}
//Search the grid for the current letter
found = checkGrid(current);
//If the letter is not in the grid add it
if(!found){
grid[row][column] = keyword[cnt];
++column;
//If the column number is too high reset it and advance the row
if(column >= 5){
column = 0;
++row;
}
}
}
//Until you reach the end of the grid you need to start with the first letter and try to add them to the grid
current = 'A';
while((row < 5) && (current <= 'Z')){
//Start here
//Make sure the is not the replaced letter
if(current == REPLACED){
++current;
}
//Check the grid for the current letter
found = checkGrid(current);
//If the letter was not found in the grid add it to the current location
if(!found){
grid[row][column] = current;
++column;
//If you have reached the end of the row move to the beginning of the next one
if(column >= 5){
column = 0;
++row;
}
}
++current;
}
//Put a check here that row == 5. If it doesn't then there is a problem
//TODO: Make this a propper exception throw rather than this
if(row < 5){
throw invalidGrid("smaller", row);
}
else if(row > 5){
throw invalidGrid("larger", row);
}
}
/**
* @brief Checks if a letter is in the grid
*
* @param letter The letter you want to check for
* @return true If the letter is found
* @return false If the letter is not found
*/
bool Playfair::checkGrid(const char letter) const{
//Step through every element in the grid and check for it
for(int rowCnt = 0;rowCnt < 5;++rowCnt){
for(int colCnt = 0;colCnt < 5;++colCnt){
//If you find the letter in the grid return true
if(letter == grid[rowCnt][colCnt]){
return true;
}
//If you reach a \0 then there are no more symbols past that and no reason to continue checking
else if(grid[rowCnt][colCnt] == '\0'){
return false;
}
}
}
//If it did not trigger the true, then it must be false
return false;
}
/**
* @brief Searches the grid for a letter
*
* @param letter The letter you wish to find
* @param row The row that the letter is found in
* @param col The column that the letter is found in
*/
void Playfair::searchGrid(char letter, int& row, int& col){
//Start here
//Check if letter needs to be replaced
if(letter == REPLACED){
letter = REPLACER;
}
//Go through every element in the grid until you find the correct letter
for(row = 0;row < 5;++row){
for(col = 0;col < 5;++col){
//If it is the correct letter you just need to return (row and col are passed by reference)
if(grid[row][col] == letter){
return;
}
}
}
//If letter was not found you need to throw an error
throw letterNotFound(letter);
}
/**
* @brief Makes sure all variables are blank
*
*/
void Playfair::reset(){
inputString = outputString = keyword = "";
//Clear the grid
for(int cnt1 = 0;cnt1 < 5;++cnt1){
for(int cnt2 = 0;cnt2 < 5;++cnt2){
grid[cnt1][cnt2] = '\0';
}
}
}
/**
* @brief Encodes inputString using the rules of the Playfair cipher and stores it in outputString
*
* @return outputString
*/
std::string Playfair::encode(){
outputString = "";
char letter1, letter2;
int row1, col1, row2, col2;
//Step through every element in the input string and encode a pair
for(unsigned int cnt = 1;cnt < inputString.size();cnt += 2){
//Grab two elements from the input string and search the grid for them
letter1 = inputString[cnt - 1];
letter2 = inputString[cnt];
//Get the location in the grid of each letter
try{
searchGrid(letter1, row1, col1);
searchGrid(letter2, row2, col2);
}
//Catch any error that might have occurred
catch(letterNotFound error){
std::string errorTemp = "There was an error int he grid\n";
errorTemp += error.getLetter();
errorTemp += " was not found";
return errorTemp;
}
//If the letters are in the same row, shift the column right by 1
if(row1 == row2){
++col1;
++col2;
//Wrap around
if(col1 > 4){
col1 -= 5;
}
if(col2 > 4){
col2 -= 5;
}
}
//If the letters are in the same column, shift the row down by 1
else if(col1 == col2){
++row1;
++row2;
//Wrap around
if(row1 > 4){
row1 -= 5;
}
if(row2 > 4){
row2 -= 4;
}
}
//If neither of the above then treat them like oposite corners in a square and swap their column variables
else{
std::swap(col1, col2);
}
//Get the new letters
letter1 = grid[row1][col1];
letter2 = grid[row2][col2];
//Add the new strings to the outputString
outputString += letter1;
outputString += letter2;
}
return outputString;
}
/**
* @brief Uses keyword to set the grid, encodes input using the rules of the Playfair cipher, and returns the new message
*
* @param keyword The keyword used to create the grid
* @param input The message that needs encoded
* @return The encoded message
*/
std::string Playfair::encode(std::string keyword, std::string input){
try{
setKeyword(keyword);
}
//Try to catch any error that might have occurred
catch(invalidGrid error){
std::string errorString = "There was an error setting up the grid.\nThe grid is too ";
errorString += error.getType();
errorString += " with ";
errorString += std::to_string(error.getSize());
errorString += " rows";
return errorString;
}
setInputString(input);
return encode();
}
/**
* @brief Decodes inputString using the rules of the Playfair cipher and stores it in outputString
*
* @return outputString
*/
std::string Playfair::decode(){
outputString = "";
char letter1, letter2;
int row1, col1, row2, col2;
//Step through every element in the input string and encode a pair
for(unsigned int cnt = 1;cnt < inputString.size();cnt += 2){
//Grab two elements from the input string and search the grid for them
letter1 = inputString[cnt - 1];
letter2 = inputString[cnt];
//Get the location in the grid of each letter
try{
searchGrid(letter1, row1, col1);
searchGrid(letter2, row2, col2);
}
//Catch any error that might have occurred
catch(letterNotFound error){
std::string errorTemp = "There was an error int he grid\n";
errorTemp += error.getLetter();
errorTemp += " was not found";
return errorTemp;
}
//If the letters are in the same row, shift the column left by 1
if(row1 == row2){
--col1;
--col2;
//Wrap around
if(col1 < 0){
col1 += 5;
}
if(col2 < 0){
col2 += 5;
}
}
//If the letters are in the same column, shift the row up by 1
else if(col1 == col2){
--row1;
--row2;
//Wrap around
if(row1 < 0){
row1 += 5;
}
if(row2 < 0){
row2 += 4;
}
}
//If neither of the above then treat them like oposite corners in a square and swap their column variables
else{
std::swap(col1, col2);
}
//Get the new letters
letter1 = grid[row1][col1];
letter2 = grid[row2][col2];
//Add the new strings to the outputString
outputString += letter1;
outputString += letter2;
}
return outputString;
}
/**
* @brief Uses keyword to set the grid, decodes input using the rules of the Playfair cipher, and returns the new message
*
* @param keyword The keyword used to create the grid
* @param input The message that needs decoded
* @return The decoded message
*/
std::string Playfair::decode(std::string keyword, std::string input){
try{
setKeyword(keyword);
}
//Try to catch any error that might have occurred
catch(invalidGrid error){
std::string errorString = "There was an error setting up the grid.\nThe grid is too ";
errorString += error.getType();
errorString += " with ";
errorString += std::to_string(error.getSize());
errorString += " rows";
return errorString;
}
setInputString(input);
return decode();
}
/**
* @brief Removes all invalid characters and sets the keyword
*
* @param key The string that is used for the keyword
*/
void Playfair::setKeyword(std::string key){
//Make sure the keyword is blank
keyword = "";
//Remove all punctuation and make everything capital
for(unsigned int cnt = 0;cnt < key.size();++cnt){
//If it is an upper case letter, just add it to the keyword
if(isupper(key[cnt])){
keyword += key[cnt];
}
//If it is a lower case letter, make it upper case and add it to the keyword
else if(islower(key[cnt])){
keyword += toupper(key[cnt]);
}
//If it is not a letter, then just ignore it
}
//Create the grid from the new keyword
createGrid();
}
/**
* @brief Returns the current keyword
*
* @return The current keyword
*/
std::string Playfair::getKeyword() const{
return keyword;
}
/**
* @brief Removes all invalid characters and sets inputString
*
* @param input The message that needs to be encoded
*/
void Playfair::setInputString(std::string input){
//Make sure inputString is empty
inputString = "";
//Remove all punctuation and make everything capital
for(unsigned int cnt = 0;cnt < input.size();++cnt){
char temp = input[cnt];
if(isupper(temp)){
//If the letter is the character than needs replaced, replace it
if(temp == REPLACED){
temp = REPLACER;
}
inputString += temp;
}
else if(islower(temp)){
temp = toupper(temp);
//If the letter is the character than needs replaced, replace it
if(temp == REPLACED){
temp = REPLACER;
}
inputString += temp;
}
//If it is not a letter do nothing to it
}
//Check if anything is doubled and needs bumped down by DOUBLED
for(unsigned int cnt = 1;cnt < inputString.size();){
char letter1, letter2;
//Get two adjacent letters and compare them
letter1 = inputString[cnt - 1];
letter2 = inputString[cnt];
//If they are the same insert DOUBLED between them
if(letter1 == letter2){
inputString.insert((inputString.begin() + cnt), DOUBLED);
}
//If they are not the same, do nothing
//Advance the counter by 2
cnt += 2;
}
//Check if the string length is odd and add DOUBLED to the end if it is
if((inputString.size() % 2) == 1){
inputString += DOUBLED;
}
}
/**
* @brief Returns the current inputString
*
* @return The current message that needs encoded/decoded
*/
std::string Playfair::getInputString() const{
return inputString;
}
/**
* @brief Returns the current outputString
*
* @return The encoded/decoded message
*/
std::string Playfair::getOutputString() const{
return outputString;
}
/**
* @brief Returns a representation of the current grid
*
* @return A string representation of the grid
*/
std::string Playfair::getGrid() const{
std::string temp;
for(int row = 0;row < 5;++row){
for(int col = 0;col < 5;++col){
temp += grid[row][col];
temp += " ";
}
temp += '\n';
}
return temp;
}
/**
* @brief Returns REPLACED
*
* @return The letter that needs replaced for the cipher to work
*/
char Playfair::getReplaced(){
return REPLACED;
}
/**
* @brief Makes sure replaced is a valid character and sets REPLACED
*
* @param replaced The letter to replace REPLACED
*/
void Playfair::setReplaced(const char replaced){
//Make sure the letter is in the correct range
if(replaced >= 'A' && replaced <= 'Z'){
//Make sure the letter is not the same as the one that is replacing it
if(replaced != REPLACER){
REPLACED = replaced;
}
}
else if(replaced >= 'a' && replaced <= 'z'){
REPLACED = toupper(replaced);
}
}
/**
* @brief Returned REPLACER
*
* @return The letter that replaces REPLACED
*/
char Playfair::getReplacer(){
return REPLACER;
}
/**
* @brief Makes sure replacer is a valid character and sets REPLACER
*
* @param replacer The letter to replace REPLACER
*/
void Playfair::setReplacer(const char replacer){
//Make sure the letter is in the correct range
if(replacer >= 'A' && replacer <='Z'){
//Make sure the letter is not the same as the one that it is replacing
if(replacer != REPLACED){
REPLACER = replacer;
}
}
else if(replacer >= 'a' && replacer <= 'z'){
REPLACER = toupper(replacer);
}
}
/**
* @brief Return DOUBLED
*
* @return The letter that is added to the end to make the message of even length and placed between 2 ajoining letters that are the same
*/
char Playfair::getDoubled(){
return DOUBLED;
}
/**
* @brief Makes sure doubled is a valid character and sets DOUBLED
*
* @param doubled The letter to replace DOUBLED
*/
void Playfair::setDoubled(const char doubled){
//Make sure the letter is in the correct range
if(doubled >= 'A' && doubled <= 'Z'){
DOUBLED = doubled;
}
else if(doubled >= 'a' && doubled <= 'z'){
DOUBLED = toupper(doubled);
}
}
/**
* @brief Returns a string containing the version information
*
* @return The version information
*/
std::string Playfair::getVersion(){
return version;
}