Vigenère cipher encryption with C++

Vigenère cipher is a substitution cipher that uses a 26 x 26 alphabetic table to either encrypt or decrypt. For example, say we wanted to encrypt the plaintext:

"DAY"

Then we would first pick an arbitrary keyword for the letter, let's go with:

"SEE"

From there on out, we would use the plaintext on the columns and keyword on the rows on the 26 x 26 table and then substitute. Ultimately, we would see that the plaintext combined with the keyword turns into:

"VEC"

Which will be our final encrypted text. An illustration of this can be seen visually as:

Now, let's move over to the C++ program and start with main.cpp.

main.cpp

#include "Vigenere.h"

Vigenere *vigenere = nullptr;

int main() {
  vigenere = new Vigenere();

  while (vigenere->isRunning()) {
    vigenere->showMenu();
    vigenere->handleEvents();
  }
  return 0;
}

In this case, we're simply creating a new instance of a Vigenere object that we're going to continously run until the user eventually ends the program. When running the program, we first want to show a menu as a user interface to display options to the user, then we will handle the user events.

Vigenere.h

#pragma once
#include <iostream>

class Vigenere {
public:
  Vigenere();
  void showMenu();
  void handleEvents();
  void encryptVigenere();
  void checkSizeOfKey(char key[], char encryptText[]);
  void encryptVigenere(char* t, char* k);
  char* getNewKey(int textLength, int keyLength, char* key);
  char* upperCase(char* s);
  int getInput();
  bool isRunning();
private:
  bool runningCheck = true;
  int selection;

  enum differentEvents {
    quit,
    encrypt
  };
};

Our Vigenere class needs to have the right amount of functions for us to base our encryption on. Let's first start out with our menu loop and text menu:

void Vigenere::handleEvents() {

  switch (getInput()) {
  case quit:
    runningCheck = false;
    break;
  case encrypt:
    encryptVigenere();
    break;
  default:
    displayInfo();
    break;
  }
}

bool Vigenere::isRunning() {
  return runningCheck;
}

void Vigenere::showMenu() {
  std::cout << "------------------------------------------" << std::endl;
  std::cout << "[0] - For ending the program              " << std::endl;
  std::cout << "[1] - Encrypt plaintext                   " << std::endl;
  std::cout << "------------------------------------------" << std::endl;
}

int Vigenere::getInput() {
  std::cout << ">";
  std::cin >> selection;
  std::cout << "------------------------------------------" << std::endl;

  return selection;
}

If we need to expand the program in the future, by for example adding the option to decrypt or display a Vigenère table visually, we could easily add more cases (and update the enum) to the handleEvents() function. Now, let's move over to the encryptVigenere() function, which will be executed if the user chooses the option of encrypting plaintext.

void Vigenere::encryptVigenere() {
  std::cout << "Do you want one or two keys?" << std::endl;

  int choice = getInput();

  if (choice == 1) {
    char encryptText[256];
    char key[256];

    std::cout << "Please enter plaintext to encrypt:" << std::endl;
    std::cout << ">                                 ";
    std::cin >> encryptText;

    std::cout << "Please enter the key:" << std::endl;
    std::cout << ">                    ";
    std::cin >> key;

    checkSizeOfKey(key, encryptText);
    encryptVigenere(upperCase(encryptText), upperCase(key));

    std::cout << "------------------------------------------" << std::endl;
    std::cout << "Your cipher:                              " << std::endl;
    for (int i = 0; i < strlen(encryptText); i++) {
      std::cout << encryptText[i];
    }
    std::cout << std::endl;

  }
  else if (choice == 2) {
    char encryptText[256];
    char key[256];
    char key2[256];

    std::cout << "Please enter plaintext to encrypt:" << std::endl;
    std::cout << ">";
    std::cin >> encryptText;

    std::cout << "Please enter the first key:" << std::endl;
    std::cout << ">";
    std::cin >> key;

    std::cout << "Please enter the second key:" << std::endl;
    std::cout << ">";
    std::cin >> key2;

    checkSizeOfKey(key, encryptText);
    encryptVigenere(upperCase(encryptText), upperCase(key));

    checkSizeOfKey(key2, encryptText);
    encryptVigenere(upperCase(encryptText), upperCase(key2));

    std::cout << "------------------------------------------" << std::endl;
    std::cout << "Your cipher:                              " << std::endl;
    for (int i = 0; i < strlen(encryptText); i++) {
      std::cout << encryptText[i];
    }
    std::cout << std::endl;

  }

}

In this function, we first ask the user to either choose one or two keys to encrypt with. Once the user has selected the number of preferred keys, the user will then be asked to enter both the plaintext and input values for the key(s). After that is done, we will need to pair the key(s) with the text that is to be encrypted. So that both the length of key and plaintext is fixed to the same length. E.g., if they key is "SEE" (size of 3) and our plaintext is "PASSWORD" (size of 8), then the key would need to be repeated ~2.6 times to match the plaintext.

void Vigenere::checkSizeOfKey(char key[], char encryptText[]) {
  int keySize = strlen(key);
  int textSize = strlen(encryptText);

  if (keySize < textSize) {
    for (int i = keySize; i < textSize; i++) {
      key[i] = key[i - keySize];
    }
    key[textSize] = '\0';
  }
}

From there on out, we would want to actually encrypt the plaintext. In order to do that, we will need to call the encryptVigenere() function that takes two parameters, namely the plaintext and key, both as uppercase text.

char* Vigenere::upperCase(char* s) {
  for (int i = 0; i < std::strlen(s); ++i) {
    s[i] = toupper(s[i]);
  }
  return s;
}

void Vigenere::encryptVigenere(char* t, char* k) {
  int textLength = strlen(t);
  int keyLength = strlen(k);

  for (int i = 0; i < textLength; i++) {
    t[i] = ((t[i] + getNewKey(textLength, keyLength, k)[i]) % 26) + 'A';
  }
}

In the encryptVigenere() function, we loop through length of the plaintext in order to substitute with the Vigenère logic. Which means that we must add the corresponding row (decided by keyword value) with the corresponding column (decided by plaintext value).

char* Vigenere::getNewKey(int textLength, int keyLength, char* key) {
  
  static char newKey[sizeof(key)];

  for (int i = 0, j = 0; i < textLength; i++, j++) {
    if (j == keyLength) {
      j = 0;
    }
    else {
      newKey[i] = key[j];
    }
  }

  return newKey;
}

Finally, when running our program we can see that we're able to fully encrypt plaintext with both one and two keys:


From there on out, we could always add more options to the program. For example, the option to decrypt any cipher, display a Vigenère table, and so forth. 

Comments

Popular posts from this blog

Minimum Spanning Trees (MST)