Friday 28 September 2018

Vigenere Cipher - code - C++

This is a follow up to my article on the Vigenere Cipher. Here, I have given the c++ code for encrypting / decrypting a text file using the Vigenere Cipher. The user has to select whether to ecrypt or decrypt, and they must enter the key. The whole text is then encrypted or decrypted using the key repeated until the length of the text is reached.

I hope the comments explain things sufficiently in the code, but I will provide an additional explanation after the code. The code is given below:

/* 
 * File:   main.cpp
 * Author: 88615XC
 *
 * Created on September 28, 2018, 3:34 PM
 */

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstring>

#define CODE_ARR_SIZE 63                //Size of the code array

std::string line;                       //For reading data from file
std::string outLine;                    //For coded data
std::string inCmd;                      //For reading data input from console
std::string key;                        //To store the key
int key_marker = 0;                     //marks current position along the key
char cipher[CODE_ARR_SIZE]={'0','1','2','3','4','5','6','7','8','9','A','a',
'B','b','C','c','D','d','E','e','F','f','G','g','H','h','I','i','J','j','K','k',
'L','l','M','m','N','n','O','o','P','p','Q','q','R','r','S','s','T','t','U','u',
'V','v','W','w','X','x','Y','y','Z','z',' '};


using namespace std;

/*
 * Returns the array index corresponding to the character
 */
int getNumber(char char1){
    for (int i = 0; i<CODE_ARR_SIZE; i++){
        if (char1==cipher[i]){
            return i;
        }
    }
    return -1;
}
/*
 * Returns the array index corresponding to the character
 */
char getChar(int num){
    if ((num>=0)&&(num<CODE_ARR_SIZE)){
        return cipher[num];
    }
    return '|';
}
/*
 * codes a line from the input line with given key.
 */
void encryptLine(void){
    int tmp1, tmp2;                     //Temporary variables
    char tmp3;
    outLine.clear();                    //Clear output string
    //Loop through the line one character at a time
    for (int i = 0; i<line.length(); i++){
        tmp1 = getNumber(line[i]);        //get numerical equivalent
        if (tmp1 != -1){                //If character is in code array
            //Get numerical equivalent for key. No validity checking needed,
            //As non-alphanumeric characters are already removed.
            tmp2 = getNumber(key[key_marker]);
            key_marker++;                //Update key marker
            //If key marker overflows, set it back to 0
            if (key_marker >= key.length()) key_marker=0;
            tmp1 = tmp1 + tmp2;         //Add the two values
            tmp1 = tmp1%CODE_ARR_SIZE;  //Get modulus
            tmp3 = getChar(tmp1);       //Get corresponding character
            outLine.push_back(tmp3);    //Add to output string
        }
        else{
            //If character is not in code array (periods, commas, etc.) they 
            //will be added to the output string as it is. If neccassary to 
            //remove them, can be done like with the key in main.
            outLine.push_back(line[i]);
        }
    }
}
/*
 * decodes the input line using the given key
 */
void decryptLine(void){
    int tmp1, tmp2;                     //Temporary variables
    char tmp3;
    outLine.clear();                    //Clear output line
    for (int i = 0; i<line.length(); i++){
        tmp1 = getNumber(line[i]);        //get numerical equivalent
        if (tmp1 != -1){                //If character is in code array
            //Get numerical equivalent for key. No validity checking needed,
            //As non-alphanumeric characters are already removed.
            tmp2 = getNumber(key[key_marker]);
            key_marker++;               //Update key marker
            //If key marker overflows, set it back to 0
            if (key_marker >= key.length()) key_marker=0;
            tmp1 = tmp1 - tmp2;         //Subtract key value from character
            tmp1 = tmp1%CODE_ARR_SIZE;  //Get modulus
            //If the value is negative, add CODE_ARR_SIZE to it
            if (tmp1<0) tmp1=tmp1+CODE_ARR_SIZE;
            tmp3 = getChar(tmp1);       //Get corresponding character 
            outLine.push_back(tmp3);    //Add to output string
        }
        else{
            //If character is not in code array (periods, commas, etc.) they 
            //will be added to the output string as it is. If neccassary to 
            //remove them, can be done like with the key in main.
            outLine.push_back(line[i]);
        }
    }
}
/*
 * 
 */
int main(int argc, char** argv) {
    ifstream input1;
    ofstream output1;
    
    //Open the input and output files
    input1.open("input1.txt",ios::in);
    output1.open("output1.txt",ios::out);
    
    //The user chooses whether the input file is coded or decoded.
    cout<<"Enter E to encrypt, D to decrypt, Q to quit:"<<endl;
    cin>>inCmd;
    //If a quit command is received, the program exits.
    if ((inCmd[0]=='Q')||(inCmd[0]=='q')){
        cout<<"Quit command received"<<endl;
        return EXIT_SUCCESS;
    }
    else if ((inCmd[0]=='E')||(inCmd[0]=='e')){
        cout<<"encrypt mode"<<endl;
    }
    else if ((inCmd[0]=='D')||(inCmd[0]=='d')){
        cout<<"decrypt mode"<<endl;
    }
    //If the command is not valid, the program exits.
    else{
        cout<<"Command not understood"<<endl;
        return EXIT_FAILURE;
    }
    
    //The user enters the key. No spaces allowed. characters that are not in the
    //specified range will be ignored.
    cout<<"Enter Key. Characters 0-9, A-Z, a-z:"<<endl;
    cin>>key;
    if (key.length()==0){               //Impossible but just in case
        cout<<"key length 0. Exiting."<<endl;
        return EXIT_FAILURE;
    }
    else{
        //Check every character to see whether it is alphanumeric. If any 
        //non-alphanumeric characters are detected, remove them.
        int j = 0;
        for (int i = 0; i<key.length(); i++){
            if (isalnum(key[i])){ 
                j++;
            }
            else{
                for (int k=i+1;k<key.length();k++){
                    key[k-1] = key[k];
                }
                key.erase(key.length()-1);
                i--;
            }
        }
        if (j==0){                  
            //If no alphanumeric characters are detected, key is invalid.
            cout<<"key not alphanumeric. Exiting."<<endl;
            return EXIT_FAILURE;
        }
        else{
            cout<<"Key :"<<key<<", Key length :"<<key.length()<<endl;
        }
    }
    
    while (!(input1.eof())){            //Loop until end of input file
        getline(input1,line);           //Read a line from input file
        if ((inCmd[0]=='E')||(inCmd[0]=='e')){
            encryptLine();                 //If code mode, encode input file
        }
        else if ((inCmd[0]=='D')||(inCmd[0]=='d')){
            decryptLine();               //If decode mode, decode input file
        }
        output1<<outLine<<endl;         //Write result to output file
    }
    
    input1.close();
    output1.close();

    return 0;
}

Results:
For the input file, the following text was pasted into input1.txt:

This is a test.
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
01234567891011121314151617181920

The purpose was to test the entire range of possible characters. The console output on running this file through it on encrypt mode:

Enter E to encrypt, D to decrypt, Q to quit:
encrypt mode
Enter Key. Characters 0-9, A-Z, a-z:
Key :Testing, Key length :7


Please note that my IDE does not show the characters that I enter.

The key used in this case is 'Testing'. If I enter something like '..T.e.s.t..ing.', the periods would be removed, and the key would still be 'Testing'. The encrypted result is in output1.txt is:

lRalI17sKSmSf9.
Ykz2rxr9rBDy8ygyIKAGAnAPRH
TN0N37U UCUdf1b1J1kmcicQcr
tJoIVHvXmSHTFTUjOItgTWJqHwFwUNog

To decode this, rename the file input1.txt, and put it through the program in decode mode using the same key. Console output:

Enter E to encrypt, D to decrypt, Q to quit:
decrypt mode
Enter Key. Characters 0-9, A-Z, a-z:
Key :Testing, Key length :7


The decrypted result is:

Enter E to encrypt, D to decrypt, Q to quit:
decrypt mode
Enter Key. Characters 0-9, A-Z, a-z:
Key :Testing, Key length :7

So, for further explanation -

  • includes: iostream is needed for console input and output. fstream is needed for reading from and writing to files. cstring is needed for all the string operations used in the program.
  • The code array size is defined as a constant because it's needed to initialize the cipher array. You can change it if you want to expand or reduce the cipher array (and include punctuation marks or other symbols, if you want to).
  • variables: line is for reading the input data, and outLine for storing the ecrypted or decrypted data. key is the key. inCmd could be a character if necessary, but I went with a string here. key_marker is used to track which character of the key is used at the moment. It does not reset when a line is done, so if a line ends in the middle of the key character, it wraps around and continues where it left off. The cipher array includes all characters that will be included in the cipher - here it lists the numbers 0-9, all letters, both uppercase and lowercase, and the space character,
  • getNumber and getChar are both used to scan the cipher array. getNumber checks whether the char is in the array, and returns the array index if it is and -1 if it isn't. getChar reverses the process and returns '|' if the number doesn't correspond to an array index.
  • encryptLine and decryptLine encrypt/decrypt a line at a time. They go through one character at a time. encryptLine adds the key, and decryptLine subtracts it. Both use the modulus function to put the value back in the 0-CODE-ARR_SIZE range. The decrypt function also needs to correct for possible negative numbers. The result is then appended to outLine. This is repeated for every character in the line.
  • The main program defines the input and output files, and then opens them. Then the user is prompted to select whether to run the encryption program or the decryption program. If the user selected encryption or decryption, then they are prompted to enter the key. If the key contains at least one alphanumeric character, it will be used. However, using a single character is not recommended, because then it will be the same as a Caesar cipher, and will be relatively easy to crack.
  • Once the key is obtained, the program will then read the input file one line at a time, and put it through the encryption or decryption program (as the user selected before), and write the result to the output file.
I hope this helps. See you next time!






No comments:

Post a Comment

How to write a character who is smarter than you

We all have that one character (or few) who is significantly smarter than the writer. So, as a writer, how do you write such a character con...