The objective of this assignment is to demonstrate the use of pointers in a program utilizing c-strings and tokenization. The use of pointers is foundational in C++. This program provides an exercise in using pointers, passing them into functions, using them as return data types, and leveraging them in traversing arrays.
Since many of you are studying the field of Cyber Security, you may find this assignment to be especially interesting.
Your job is to write a program to encrypt outgoing messages and decrypt incoming messages using a Vigenere Cypher.
In this assignment, you will parse a string of words from a file into tokens using the strtok_s command with pointers and then use the Vigenere Cypher algorithm to encrypt and decrypt the parsed words.
If you are unfamiliar with the "Vigenere Cypher," a very good description of it is provided in this short video: https://www.youtube.com/watch?v=SkJcmCaHqS0
[Note: Don't panic as you watch the video. I included it only to provide you with a visual depiction of how this cypher works. In this assignment, you will be given the exact formulas to use to encrypt and decrypt the text.]
Before you can perform any encryption/decryption activities, the first step is to read in messages from a file and parse the messages into individual words.
The first step in encrypting/decrypting messages is to parse the messages into words before applying the encryption/decryption algorithms to them. This parsing process is called "tokenization."
Tokenization:
The process of parsing sentences into words is called tokenization. To tokenize a sentence into words, use the C++ function strtok_s. [Note: do not try to use the C++ strtok function because it has been deemed unsafe and has therefore been deprecated.]
In your "client" code (i.e. the file that contains your main function), you will need to declare a character array that will hold 1000 characters.
Read the contents of a file into this array using the getline command. To view a discussion on how to use getline with a file object, see this link: https://stackoverflow.com/questions/13035674/how-to-read-line-by-line-or-a-whole-text-file-at-once
[Note: I realize that it would be easier to read the contents of the file into a string variable; that way, you wouldn't have to tokenize the message. HOWEVER one of the objectives of this assignment is to illustrate the use of tokenization of c-strings using the strtok_s function and pointers. Therefore, you MUST use a character array to read in the data from the file and tokenize it as described below to receive credit for this assignment.]
Using the character array, your client should call the function strtok_s() to tokenize the character array into separate words. Here is an excellent discussion of tokenizing and the strtok_s function: [Note that if you scroll down the web page to the "Example" section, you will see some code that you can tweak for your own program.]
https://msdn.microsoft.com/en-us/library/ftsafwz3.aspx
There should be two classes in your program: Vigenere and Message.
Vigenere contains the encryption key and the logic for encrypting and decrypting individual words.
Message contains a vector of words that have been encrypted or decrypted and the logic for calling the functions in the Vigenere class to encrypt or decrypt a word. The Message class serves as a middle-man between your client code and the Vigenere class and holds the encrypted/decrypted results in a vector.
Viginere Class
Data Member: | string key |
Functions: | Viginere() <- constructor void setKey(string k) string getKey() string toUpperCase(string k) string encrypt(string word) string decrypt(string word) |
The Vigenere class should store an encryption key in a data member called "key." The class should have a one-argument constructor that receives a string that represents the encryption key. The encryption key must be in all capital letters for the encryption and decryption algorithms to work. Therefore, before setting the encryption key's value, it should first be converted entirely to upper case. Do this in your toUpperCase function.
To convert a string to all upper case, loop over all of the characters in the string and use the toupper function (http://www.cplusplus.com/reference/cctype/toupper/ ) to set each character in the string equal to the uppercase version of the character.
The purpose of this assignment is not to measure your ability to create encryption and decryption algorithms, but rather to demonstrate your ability to use the strtok_s function, along with pointers, to tokenize character arrays and to leverage object-oriented programming in the design of your program. Therefore, you may use the following code for the encrypt and decrypt functions in your Vigenere class.
string Vigenere::encrypt(string word)
{
string output;
for (int i = 0, j = 0; i < word.length(); ++i) {
char c = word[i];
if (c >= 'a' && c <= 'z')
c += 'A' - 'a';
else if (c < 'A' || c > 'Z')
continue;
output += (c + key[j] - 2 * 'A') % 26 + 'A'; //added 'A' to bring it in range of ASCII alphabet [ 65-90 | A-Z ]
j = (j + 1) % key.length();
}
return output;
}
string Vigenere::decrypt(string word)
{
string output;
for (int i = 0, j = 0; i < word.length(); ++i) {
char c = word[i];
if (c >= 'a' && c <= 'z')
c += 'A' - 'a';
else if (c < 'A' || c > 'Z')
continue;
output += (c - key[j] + 26) % 26 + 'A';//added 'A' to bring it in range of ASCII alphabet [ 65-90 | A-Z ]
j = (j + 1) % key.length();
}
return output;
}
[source: https://www.tutorialspoint.com/cplusplus-program-to-implement-the-vigenere-cypher ]
Message Class
Data members: | vector< string > words Vignere v |
Functions: | Message(string k) <- k is the key passed in to the constructor, which is then passed in to the Vigenere class's setKey function. void encryptWord(char* token) void decryptWord(char* token) void makeFile(string fileName) void showWords() |
The Message class should contain a vector of strings that holds the words that were either encrypted or decrypted. The class should contain functions to encrypt a word, decrypt a word, create a file containing the vector of encrypted or decrypted words, and print the vector of encrypted or decrypted words.
Main()
In main(), call a function to create a menu that displays options to Encrypt a file, Decrypt a file, and Quit. The function should be called displayMenu and should return an integer for the option selected. The menu should be re-displayed after each selection until the user enters "3" for Quit. The menu should look like the one in the screenshot below:
Vigenere Cypher
Main Menu
1 - Encrypt File
2 - Decrypt File
3 - Quit
Selection:
In main(), you will need to create an object of the Message class. You will use this object to call the functions in the Message class that will encrypt, decrypt, save, and print the words retrieved from the document.
Before you create this Message object, you should prompt the user to enter an encryption/decryption key. The same key must be used for decryption as was used for encryption. Otherwise, the algorithms will not produce the correct results. Once the user enters the key, pass this string in to the constructor of the Message class when creating your Message object. This key is ultimately converted to upper case and stored in the "key" data member in the Vigenere class.
Enter an encryption/decryption key:
ThisStringCanBeAnything
Menu Options:
Option 1 - Encrypt file
When the user selects this option, he or she should immediately be prompted to enter the name of the file to be encrypted: [Note that the file name can contain spaces.]
Enter the name of the file to encrypt: Autumn Begins in Ohio.txt
This file should already exist and contain a single paragraph of information (not to exceed 1000 characters.) Make sure to include appropriate error checking to ensure that the file exists and can be opened.
You may test with a file that contains something like this as its contents: [Capitalization and punctuation in the file will not matter as all punctuation symbols will be stripped out in the final result, and all letters will be converted to upper case.]
In the Shreve High football stadium, I think of Polacks nursing long beers in Tiltonsville, And gray faces of Negroes in the blast furnace at Benwood, And the ruptured night watchman of Wheeling Steel,Dreaming of heroes. All the proud fathers are ashamed to go home.Their women cluck like starved pullets, Dying for love.Therefore, Their sons grow suicidally beautiful At the beginning of October, And dying for love.
Your program should open the file and read its contents into a character array using the getline function. As soon as the file's contents have been read into the character array, main() should print the contents of the array to the screen.
In the Shreve High football stadium, I think of Polacks nursing long beers in Tiltonsville, And gray faces of Negroes in the blast furnace at Benwood, And the ruptured night watchman of Wheeling Steel,Dreaming of heroes. All the proud fathers are ashamed to go home.Their women cluck like starved pullets, Dying for love.Therefore, Their sons grow suicidally beautiful At the beginning of October, And dying for love.
Next, using the strtok_s function as described above, tokenize the entire message into separate words. As each word is tokenized, call the encryptWord function in the Message class (using the Message object to invoke the function and passing in each token as an argument ONE AT A TIME).
The encryptWord function in the Message class should receive the token as a char* data type and immediately cast it into a string variable. It should then pass this string into the encrypt function in the Vigenere class using the Vigenere object that is a data member in the Message class. Once the word is encrypted, the encryptWord function in the Message class should push the encrypted word onto the vector of words in the Message class.
This process is repeated until the entire file's contents have been tokenized and encrypted.
Once the entire file has been tokenized and its encrypted words have been stored in the vector of words in the Message class, main() should call the function in the Message class to create a new file that contains the encrypted words. Therefore, the user must be prompted to enter the name of the file in which the encrypted data will be stored:
A new file will be created that contains the encrypted message.
Please enter the name of the new file to create: Autumn Begins in Ohio( Encrypted).txt
The program will then create a new file based on the name that the user entered, and that file will contain the contents of the words vector in the Message class. Recall that this vector contains the words that are now encrypted. Therefore, main() should call the showWords function in the Message class to print the contents of the words vector to the screen and then call the makeFile function in the Message class to create the file and print the contents of the vector to the file.
After the file is created and its contents are displayed to the screen, the Main Menu should be redisplayed.
The new file contains the following encrypted message.
HBZ YHBZGR POQUJ TYB BU ALINGN AHTDQWRE UL MOG GHUW MOG DPVYFOZ VVUW MOG PPTD UL W VW BU XHZLJ TZ BA BZ BU ALINGN ZPDW NZ MOQK WHG HBZ WHQDA UYMSF TUL YVZYKVR NZ HBZ MYMKRAFTIS TZ PL YVZYKVR MOME MOIL MYMKRAFT TNIAPSG NZ TUL ELIV NZ GVB BUBG MLUHVA JSN UBB WLTAXEE NZ YYWE XCQD YVZ MOQFG BZ MOM DPVYFOZ MOM IVEWT TUL MOM ZSWJA YVZ CMJ TUL XCMJ TTMF
Vigenere Cypher
Main Menu
1 - Encrypt File
2 - Decrypt File
3 - Quit
Selection:
Option 2 - Decrypt file
When the user selects option 2, he or she should immediately be prompted to enter the name of the file to be decrypted: [Note that the file name can contain spaces.]
Enter the name of the file to decrypt: Autumn Begins in Ohio(Encrypted).txt
Your program should open the file and read its contents into a character array using the getline function. As soon as the file's contents have been read into the character array, main() should print the contents of the array to the screen.
HBZ YHBZGR POQUJ TYB BU ALINGN AHTDQWRE UL MOG GHUW MOG DPVYFOZ VVUW MOG PPTD UL W VW BU XHZLJ TZ BA BZ BU ALINGN ZPDW NZ MOQK WHG HBZ WHQDA UYMSF TUL YVZYKVR NZ HBZ MYMKRAFTIS TZ PL YVZYKVR MOME MOIL MYMKRAFT TNIAPSG NZ TUL ELIV NZ GVB BUBG MLUHVA JSN UBB WLTAXEE NZ YYWE XCQD YVZ MOQFG BZ MOM DPVYFOZ MOM IVEWT TUL MOM ZSWJA YVZ CMJ TUL XCMJ TTMF
Next, using the strtok_s function as described above, tokenize the entire message into separate words. As each word is tokenized, call the decryptWord function in the Message class (using the Message object to invoke the function and passing in each token as an argument ONE AT A TIME).
The decryptWord function in the Message class should receive the token as a char* data type and immediately cast it into a string variable. It should then pass this string into the decrypt function in the Vigenere class using the Vigenere object that is a data member in the Message class. Once the word is decrypted, the decryptWord function in the Message class should push the decrypted word onto the vector of words in the Message class.
This process is repeated until the entire file's contents have been tokenized and decrypted.
Once the entire file has been tokenized and its decrypted words have been stored in the vector of words in the Message class, main() should call the function in the Message class to create a new file that contains the decrypted words. Therefore, the user must next be prompted to enter the name of the file in which the decrypted data will be stored.
The program will then create a new file based on the name that the user entered, and that file will contain the contents of the words vector in the Message class. Recall that this vector contains the words that are now decrypted. Therefore, main() should call the showWords function in the Message class to print the contents of the words vector to the screen and then call the makeFile function in the Message class to create the file and print the contents of the vector to the file.
After the file is created and its contents displayed to the screen, the Main Menu should be redisplayed.
A new file will be created that contains the decrypted message.
Please enter the name of the new file to create: The Lords Prayer (Decrypted).txt
The new file contains the following decrypted message.
OUR FATHER WHICH ART IN HEAVEN HALLOWED BE THY NAME THY KINGDOM COME THY WILL BE DO NE IN EARTH AS IT IS IN HEAVEN GIVE US THIS DAY OUR DAILY BREAD AND FORGIVE US OUR TRESPASSES AS WE FORGIVE THEM THAT TRESPASS AGAINST US AND LEAD US NOT INTO TEMPTAT ION BUT DELIVER US FROM EVIL FOR THINE IS THE KINGDOM THE POWER AND THE GLORY FOR EVER AND EVER AMEN
Vigenere Cypher
Main Menu
1 - Encrypt File
2 - Decrypt File
3 - Quit
Selection:
Notice that there is a LOT of similar functionality between menu options 1 and 2. The only differences are in the functions that are called in the Message class to either encrypt or decrypt the words. This overlap of functionality represents an excellent opportunity to simplify your code into functions--thus making it more modular and generic.
Here is a link that provides some additional information on the Vigenere Cypher if you are interested in learning more about it: https://brilliant.org/wiki/vigenere-cipher/
Video: Cryptanalysis of Vigenere cipher: not just how, but why it works https://www.youtube.com/watch?v=QgHnr8-h0xI
Here is a video on how to break the Vigenere Cypher if you don't know the key: https://www.youtube.com/watch?v=LaWp_Kq0cKs&t=0s