CS161 Programming Assignment 6: Interfaces

Due - 10/16 @ 8pm, Late - 10/18 @ 8am


1. Goal

The goal of this assignment is to understand the concept of Java interfaces while implementing classical substitution ciphers. A cipher is used to encrypt and decrypt information. Encryption refers to the process or algorithm that converts plaintext into a coded or encrypted version, called cipher text. Decryption refers to the conversion of cipher text to plaintext. For details on what a cipher is, refer to this link on Wikipedia.


2. Description

In this assignment, you will define one Java interface called Cipher, which contains two methods, encrypt and decrypt. You will create two different implementations of this interface in the classes, CaesarCipher and PolyAlphabeticCipher. You will also use these ciphers in a third class called Sentry.


2.1 The Cipher interface

Cipher is an interface containing the following methods:
  1. public String encrypt(String plaintext); // Takes the String argument called plaintext and returns its encrypted version.

  2. public String decrypt(String ciphertext); // Takes the ciphertext, which is encrypted, and returns its decrypted version as a plaintext String.


2.2 The CaesarCipher class

The CaesarCipher class implements the Cipher interface using the Caesar cipher algorithm. You may refer to this link on Wikipedia for a detailed description. The Caesar cipher is one of the simplest classical encryption algorithms, and belongs to the category of substitution ciphers.

Each character in the plaintext is subsituted with a character coming a fixed number of places down the alphabet. Spaces remain unchanged. For example, using a shift of 3 characters, we get the following table of character substitutions:

Original
character
Substituted
character
Original
character
Substituted
character
A D a d
B E b e
C F c f
... ... ... ...
Z C z c

Thus, a plaintext string "Cab" is encrypted as "Fde". A cipher text string "Fde" is decrypted as "Cab" by substituting each character with the character coming 3 position earlier in the alphabet. Also note that uppercaese letters are treated separately, i.e. Z is encrypted as C, and z is encrypted as c.

For this assignment assume that the plaintext to be encrypted can contain the letters A-Z, a-z, and spaces (but no tabs or other whitespace). No punctuation characters will be used. The ciphertext used as an argument to the decrypt method can only contain characters that correspond to valid substitutions of the characters specified for the plaintext.

Constructor:

List of attributes:

List of Methods:


2.3 PolyAlphabeticCipher class

The PolyAlphabeticCipher class implements the Cipher interface as described below.

Our version of the cipher requires the conversion of plaintext to ciphertext with the help of a single key. For this assignment assume that the key is a string containing only the letters A-Z (uppercase). The key is applied repeatedly such that every character in the plaintext is paired with a character in the key. For example, if the plaintext is "The enemy was spotted" and the key is "COFFEE", then you need to apply the key as "COFFEECOFFEECOFFEECOF" because there are 21 characters in the plaintext. Note that the key is required four times, the last one being incomplete. Assume the value of combination of the key and the character will not exceed 127.

The code values for the characters in the plaintext are their Unicode values. You don't need to know these values. You can obtain the code for any char ch by casting ch to an int: (int)ch.

The code values of the characters in the key are not their Unicode values. Instead, they are: A=0, B=1, C=2, ... , Z=25. That is, if ch is a character in the key, then the code for that character ch is
((int)ch - (int)'A').

The code for every letter in the plaintext is added to the code of a key character to produce a character in the cipher text.

Here is an example using the plaintext "The enemy was spotted" and the key "COFFEE".

  Plaintext:                The enemy was spotted
                            +++++++++++++++++++++
  Key applied repeatedly:   COFFEECOFFEECOFFEECOF
                            =====================
  Ciphertext:               Vvj%irg{~%{eu.xusxvsi

Decryption of a ciphertext is done by reversing the process. We subtract the code of the key characters from the code of characters in the ciphertext.

For this assignment assume that the plaintext to be encrypted can contain the letters A-Z, a-z, and spaces (but no tabs or other whitespace). No punctuation characters will be used. The ciphertext used as an argument to the decrypt method can only contain characters that correspond to the substitutions of the characters specified for the plaintext.

Constructor:

List of attributes:

List of methods:


2.4 Sentry class

The Sentry class is used to encrypt and decrypt text files using a given cipher. The Sentry class stores information on which cipher to use and can encrypt the contents of a text file. The encryption method treats each line as a string and uses the encrypt method of the associated cipher. The method uses recursion to process the lines from the input file in reverse order, and encrypts each line separately. The encrypted lines are printed in an output text file. The output file thus contains the encrypted version of each line in the input file, but the lines appear in the reverse order. Assume that the lines in the text file only contain characters as described above in the two cipher algorithms.

For example, suppose that a Caesar cipher is used with a shift parameter of 3. Given this plaintext file, here is the resulting encrypted file. If you decrypt the encrypted file, you should get back the original input file.

Decryption works in a corresponding way. It recursively reverses the order of lines in an encrypted file while decrypting each line separately, and finally produces a file containing the decrypted lines.

Constructor:

List of attributes:

List of methods:

  1. public void encrypt(String inputFileName, String outputFileName) // encrypts the contents of the file referred to by inputFileName, and produces an output file called outputFileName, which contains the encrypted text as described above. Catch all file processing related exceptions, and print the error message and exit in the catch block as shown below:
    System.err.println(e);
    System.exit(0);
    

  2. public void decrypt(String inputFileName, String outputFileName) // decrypts the contents of the file referred to by inputFileName, and produces an output file called outputFileName, which contains the decrypted text as described above. Catch all file processing related exceptions, and print the error message and exit in the catch block as shown below:
    System.err.println(e);
    System.exit(0);
    


3. General Information

None of the methods above should be declared static. In fact, compilation of your program will fail if the method signatures in your program are different than above.

Keep in mind that we will not test your main method -- the methods you implement will be tested directly. Ideally, you should create your own main methods, perhaps one in each class, to test each class separately. You can also write another main method in a separate class that tests the four classes together.

Here is a sample barebones main method for the CaesarCipher class. It does not test all the methods, nor does it test for various situations.

public static void main(String[] args){
    CaesarCipher cipher = new CaesarCipher(3);
    String plainText = "The enemy was spotted";
    System.out.println("Plaintext: " + plainText);
    String cipherText = cipher.encrypt(plainText);
    System.out.println("After encryption, cipherText: " + cipherText);
    String backToPlainText = cipher.decrypt(cipherText);
    System.out.println("After decryption, plainText: " + backToPlainText);
    if(!plainText.equals(backToPlainText))
        System.err.println("Fix your program!");
}

Here is a sample barebones main method for the Sentry class. It does not test all the methods, nor does it test for various situations.

public static void main(String[] args) {
    Sentry sentry = new Sentry(new CaesarCipher(3));
    sentry.encrypt("encrypt-input.txt", "encrypt-output.txt");
    sentry.decrypt("encrypt-output.txt", "decrypt-output.txt");
}

Submission

Create a single jar file called P6.jar from the four Java files (Cipher, CaesarCipher, PolyAlphabeticCipher, and Sentry. Ensure they are the .java files, not the .class files.

Submit the P6.jar file via the checkin tab on the website. This system performs preliminary testing of your program - successfully passing these tests will guarantee a 50% grade. Final grading will be performed using an extended set of test cases.