Executive Summary

In this project you are writing a utility called "Vault" that provides secret, authenticated storage. The conceptual setup is "real world", although the particular cryptographic algorithms we use are not, because they are text-based historical techniques, rather than modern digital ones. The key points are:
  1. data is stored by labels, so you give commands like "get the data associated with the label psptnum"
  2. there are multiple users storing data in a single file
  3. users are authenticated so that nobody sees any data other than their own
  4. cryptography keeps the data in the file secret, even from someone who can read the file and who has a copy of the Vault program and its source code
  5. the labels themselves are secured by encryption, so that someone who steals the file can get usernames but not data or the labels under which the data is stored.
This is probably a bit easier to see with an example:
file vault0 sample run
user joe shift+vigenere ?5d`54=22d5Boqqv   
user ann shift+caesar 2018^midGO_NAVY_
data joe vigenere LjTmZ8o*s=CEWyi.q
data joe caesar z53y5vJKDIHDHO
data ann caesar instrument_french_horn
~/$ java Vault vault0
username:  joe
password:           ← password not echoed to the screen!
Access granted!
>  labels
ssnum
combo
> get ssnum
892-88-1263
> quit
This project is designed to exercise your ability to write good Object-oriented code in Java, which will include good use of encapsulation, data-hiding, inheritance, polymorphism, class hierarchies, interfaces and exception handling.

The project has three main pieces: encryption algortihms, hash algorithms, and the vault. If you do a good job of following OOP best-practices in producing your solutions to the first two parts, you should be OK on the third. Otherwise ...

Honor

The course policy and the instructions it references, most pertinently COMPSCIDEPTINST 1531.1C, spell out what kinds of assistance are permissable for programming projects. The instructor can give explicit permission for things that would otherwise not be allowed. For this project, you have explicit permission
  1. to get help from both Spring 2016 IC211 instructors (any assistance must be documented though), and
  2. to use general purpose Java resources online (though such use must be documented). Resources that might specifically address this project, like the code of programs that do encryption with the Vigenere cipher, are not allowed.
Put together the instructions referenced in the course policy and the explicit permissions above, and what you get is summarized as:

Rules and important extra information

  1. Labels, data and passwords are restricted to ASCII characters in the range 42-122 inclusive. I.e. they may only include:
    *+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
    I've carefully chosen encryption and hashing algrithms that produce results that are in that restricted character set as well. Any encrypted entry in a vault-file and any user-entered label, data or password that is outside of these bounds is an error and should result in some kind of message.
  2. Sometimes we will want to represent strings as String objects, other times as objects of type char[]. Just in case you didn't know here's how to convert back and forth:
    String str = "foo";
    char[] buff = str.toCharArray(); // convert String to char[]
    String rts = new String(buff);   // convert char[] to String
  3. You'll be converting chars to ints and ints to chars. Please remember that you must explictly cast to do this in Java, e.g. (int)str.charAt(0) + 7, or (char)(i + 3).

Part 1 [up to 25pts]: Encryption

The encryption we will be using in this project is symmetric encryption (see si110 symmetric encryption notes). The three symmetric encryption algorithms you must support are clear, caesar, and vigenere (all described later). A Part 1 solution will be a program TestEncryptors that allows the user to encrypt a test message of their choice using any one of the three algorithms above. The program itself is not the important product here: correct implmentations of the three encryption algorithms with a single coherent interface and a well-thought out system of error-handling is the real point.

example 1example 2example 3
~/$ java TestEncryptors
algorithm: clear
password : grumpy
message  : This_is_just_test:1234
plain : This_is_just_test:1234
cipher: This_is_just_test:1234
decryp: This_is_just_test:1234
~/$ java TestEncryptors
algorithm: caesar
password : grumpy
message  : This_is_just_test:1234
plain : This_is_just_test:1234
cipher: y<=G3=G3>IGH3H9GH_VWXY
decryp: This_is_just_test:1234
~/$ java TestEncryptors
algorithm: vigenere
password : grumpy
message  : This_is_just_test:1234
plain : This_is_just_test:1234
cipher: @_ceTg_VdghrKk_ei8nz-w
decryp: This_is_just_test:1234

Your project will benefit tremendously from following good object oriented design principals. To help you out, I'm going to impose more structure on this first part of the assignment. Here are the rules and recommendations:

If you would like to start this project before we cover "interfaces", i.e. start over Spring Break, consider looking at the alternative starting point, which is completely equivalent, but uses abstract classes rather than interfaces.
  1. Start off with the program defined by the following three files, and make them the basis for your solution. They are my gift to you, and you can (and should) modify them to suit your needs.
    • Encryptor.java - an interface for objects that provide encryption functionality.
    • Clear.java - an implementation of Encryptor providing the clear algorithm, which is to not change the plaintext at all.
    • TestEncryptors.java - a driver program that matches the basic input/output functionality of Part 1. As you implement more Encryptors, you just put an "add" line in for them, and they become part of thie test driver.
    Download with the following:
    curl -O http://www.usna.edu/Users/cs/wcbrown/courses/S16IC211/proj/p02/init/Encryptor.java
    curl -O http://www.usna.edu/Users/cs/wcbrown/courses/S16IC211/proj/p02/init/Clear.java
    curl -O http://www.usna.edu/Users/cs/wcbrown/courses/S16IC211/proj/p02/init/TestEncryptors.java
  2. Add an implmentation of the Encryptor interface for the Caesar-shift cipher (algorithm name caesar): see Caesar-shift details.
    Add an implmentation of the Encryptor interface for the Vigenere cipher (algorithm name vigenere): see Vigenere details.
  3. Each of these three implementations should throw exceptions when error situations occur: for the TestEncryptors program all errors should result in exceptions thrown out of main() and vomited onto the screen. The types and accompanying messages of these exceptions should be informative!
    Important: it's up to you to define a nice hierarchy of exceptions that will allow you to handle errors nicely in the coming sections.
  4. Test thoroughly! To help you do so, here are some test vectors:
      algorithm   password     plaintext        ciphertext     note
      clear       whale65]     987:test;DOG     987:test;DOG
      caesar      whale65]     987:test;DOG     ?>=@zkyzAJUM
      vigenere    whale65]     987:test;DOG     5vn+^q-V7158
      clear       10$More      987:test;DOG                    error $ not allowed in key
      caesar      10$More      987:test;DOG                    error $ not allowed in key
      vigenere    10$More      987:test;DOG                    error $ not allowed in key
      clear       hone_5^      1$L0VE$cats$                    error $ not allowed in plaintext
      caesar      hone_5^      1$L0VE$cats$                    error $ not allowed in plaintext
      vigenere    hone_5^      1$L0VE$cats$                    error $ not allowed in plaintext

Part 2 [up to 50pts]: Hashing

Encryption takes a plaintext message and a key and produces a ciphertext message in such a way that it can, if you know the key, eventually be decrypted to recover the plaintext message. A different kind of operation called hashing, which simply takes a message and generates a fixed length random-looking output called the "digest", or the "hash" of the method. Encryption provides confidentiality. Hashing provides integrity. Hashing, as it turns out, is a critical tool in verifying passwords. In this context, the "message" to be hashed is the user's password. See the SI110 notes on hashing a passwords for more information.

A Part 2 solution is a program TestHashers that allows the user to hash a test message (which will the "password") of their choice using one of the following three algorithms:

Dr. Brown thought: For next time, if there is a next time, call change the "clear hash" to "padding" hash.
clear, shift+caesar, and shift+vigenere. The program itself is not the important product here: correct implmentations of the three hashing algorithms with a single coherent interface and a well-thought out system of error-handling is the real point.
Note: Please take note of the nice design we laid out for you for the encryption stuff in Part 1, and try to do something similar here!

example 1example 2example 3
~/$ java TestHashers
algorithm: clear
password : grumpy
password read : grumpy
hash computed : grumpyxxxxxxxxxx
~/$ java TestHashers
algorithm: shift+caesar
password : grumpy
password read : grumpy
hash computed : hxgZorxKIJQw51,`
~/$ java TestHashers
algorithm: shift+vigenere
password : grumpy
password read : grumpy
hash computed : U@P9yh>l0N<;_o]o

  1. The clear algorithm is to have the hash of the message/password be the the first 16 characters of message itself (add x's to the back to pad out to 16 characters if the message is shorter). The shift+ algorithm adapts a symmetric encryption algorithm to produce a hash, which gives us shift+caesar and shift+vigenere (you could offer shift+clear if you wanted to): see shift+ details.
  2. Each of these three hash implementations should throw exceptions when error situations occur: for the TestHashers program all errors should result in exceptions thrown out of main() and vomited onto the screen. The types and accompanying messages of these exceptions should be informative!
    Important: it's up to you to define a nice hierarchy of exceptions that will allow you to handle errors nicely in the coming sections.
  3. Test thoroughly! To help you do so, here are some test vectors:
    algorithm       password  hash               note
    clear           whale65]  whale65]xxxxxxxx
    shift+caesar    whale65]  V^n]PehnA?@Gm+xs
    shift+vigenere  whale65]  C+HK`8fzqA+4P3Q[
    clear           10$more                      error $ not allowed in key
    shift+caesar    10$more                      error $ not allowed in key
    shift+vigenere  10$more                      error $ not allowed in key

Part 3 [up to 75pts]: Authentication

A Part 3 solution is a program that takes a vault-file that contains only "user" entries (format described below), queries the user for a username and password, and replys with either the message "Access granted!" or "Access denied!" depending on whether the username and password pass authentication. If access is granted, the program prints a > prompt and processes commands ... although the only command it understands is quit, which immediately quits the program.

file vault3 example 1a, 1b example 2a, 2b example 3a, 3b
user aviv clear princessxxxxxxxx
user mcdowell shift+caesar FDELr0,x[csbUjms
user brown shift+vigenere 7sM7;9/D:@R_c]3i
~/$ java Vault vault3
username: aviv
password: 123456
Access denied!
~/$ java Vault vault3
username: aviv
password: princess
Access granted!
> quit
~/$ java Vault vault3
username: mcdowell
password: StandDesk
Access denied!
~/$ java Vault vault3
username: mcdowell
password: Cuddly1
Access granted!
> quit
~/$ java Vault vault3
username: brown
password: G0N4\/y
Access denied!
~/$ java Vault vault3
username: brown
password: OOP-C-day-Z
Access granted!
> quit

Make sure you have a nice clean design for your solution, and test thoroughly to make sure you meet all of the following requirements:
  1. The program must run as java Vault <filename>, and it's prompts and output messages must match the format of the examples above exactly. This also means that the password should not be echoed to the screen. See TestEncryptors.java for an example of how to do this.
  2. No error should result in a an exception being thown out of main and dumped to the screen. Instead, all error conditions should be caught and result in meaningful error messages. Here is a list of some possible errors and the required output/behavior:
    • called with no command-line argument: output "usage: java Vault <filename>" and exit.
    • called with filename that can't be found/opened: output "Error! File 'name' could not be opened." and exit.
    • input file not in the proper format, i.e. could not be read in as a number of "user" records, one per line: output "Error! File 'name' improperly formatted." and exit. (see vault3.1 for example) Note: this message comes before either the username or the password are read.
    • username not found or password does not match or password contains a character outside ASCII range 42-122: these are not treated as errors, "Access denied!" is the proper message for this case. Note: this message always comes after both the username and the password are read.
    • input file specifies a hash algorithm for the given username that is not supported: output "Error! Hash algorithm 'hashalg' not supported." and exit. (see vault3.2 and login with username=aviv and password=princess for example) Note: this message always comes after both the username and the password are read.

Part 4 [up to 85pts]: Adding users

A Part 4 solution builds on top of a Part 3 solution to add the ability to add new users to the vault-file. In this case, the program is run as
java Vault -au filename
where the -au option indicates that you want to add a user. The user is prompted to enter username, password and a hash algorithm as shown in the examples below.

1. file vault4 2. add user 3. new file vault4 4. authenticate
user aviv clear princessxxxxxxxx
~/$ java Vault -au vault4
username: roche
password: Tuba+Time
Hash algorithm: shift+caesar
user aviv clear princessxxxxxxxx
user roche shift+caesar *0TRSZ/>:5iq0pcx
~/$ java Vault vault4
username: roche
password: Tuba+Time
Access granted!
> quit

We haven't really done anything with writing files yet. Here's some example code. Note that you need to make sure to close the file. That's important. You'll also want to make sure you close the input file immediately after reading it at the beginning of your program.
PrintWriter pw = null;
try 
{
  pw = new PrintWriter(new File(fname));
} 
catch (FileNotFoundException fnfe) 
{
  fnfe.printStackTrace();
} 

// Do whatever you need to do

if (pw != null) pw.close();
Obviously, we're adding functionality to your Part 3 solution, so all the part three stuff should work as before. The only really new things are:
  1. The usage message should now read: "usage: java Vault [-au] <filename>"
  2. When adding a user, if the password contains characters outside ASCII values 42-122, output "Error! Invalid symbol 'x' in password." and exit. Note: this message only comes after the username, password and algorithm have all been read.
  3. if the user specifies a hash algorithm that is not supported output "Error! Hash algorithm 'hashalg' not supported." and exit. Note: this message only comes after the username, password and algorithm have all been read.
  4. If the given username already appears in the input file, output "Error! Username 'username' already in use." and exit. Note: this message only comes after the username, password and algorithm have all been read.

Note: Writing to files is a bit new. See the annotation to the right for details. PrintWriter objects give you your usual print, println and printf. When a new user is added, you completely overwrite the vaultfile. That means you have to write entries for all the existing users as well as for the new user.

Part 5 [up to 100pts]: Viewing data

A Part 5 solution builds on top of a Part 4 solution in two ways
  1. The inputfile now contains "data" entries mixed in with the "user" entries. Each data entry is a line of the form
    data username encalg cipertext
    where encalg can be one of clear, caesar, or vigenere, and ciphertext is a ciphertext-string that decrypts (with the user's password as key and using encryption algorithm encalg) to a plaintext string of the form label_text, where both the label and the text come from the ASCII range 42-122, but the label does not include the '_' character. For example, suppose there is a user crabbe with password FlappyBirds. We might have a data entry like this:
    data crabbe caesar ,//=0>>*j@8-0=*P*c=48,@7/*l7J
    
    Note: decrypt with caesar and password FlappyBirds gives: address_Number_4_Grimauld_Pl.
                                                              \_____/ \___________________/
                                                               label          text
    This means that the entry represents the label "address" with the associated text "Number_4_Grimauld_Pl.".
  2. Once a user has been successfully authenticated, that user enters into a little command shell that process the following commands:
    • labels — this should print out all the labels associated with this user. Note that the only way to do this is to go through all data entries with the matching username, decrypt (with the same password that the user authenticated with), split the resulting plaintext into everything before the first "_" and everything after, and take the "before" part as the label.
    • get label — this should print out the text associated with label for the current user. Note: you decrypt entries for the current user with the same password that the user authenticated with.
    • quit — this should quit the program

file vault5a sample run 1 vault5b sample run 2
user aviv clear princessxxxxxxxx
data aviv clear weakness_backhand
data aviv caesar bZa`WQwtspu+vpyyxx
data aviv vigenere [XdX*Kf\TF\XG.ajZZb\WX
~/$ java Vault vault5a
username: aviv
password: princess
Access granted!
> labels
weakness
phone
faveBand
> get weakness
backhand
> get faveBand
One_Direction
> quit
user aviv shift+caesar /+wZbraTilrECDKq
user mcdowell shift+vigenere fW*XrW]7P_uMnoAZ
data aviv vigenere [XdX*Kf\TF\XG.ajZZb\WX
data mcdowell caesar HGCTAvQWNTQRJQDKC
data aviv caesar bZa`WQwtspu+vpyyxx
$ java Vault vault5b
username: mcdowell
password: StandDesk
Access granted!
> labels
fear
> get fear
Coulrophobia
> get faveBand
> done
Unknown command 'done'.
> quit

Other requirements and hints:
  1. If the user enters an unknown command (i.e. not add, label or quit), output the message "Unknown command 'comm'." and continue reading more commands.
  2. If the user enters a label for the "get" command that is not matched by any entry, nothing should be printed at all. Just return the prompt and wait for the next command.
  3. If in decrypting an entry (whether for the "labels" command or the "get" command) there is any kind of error (e.g. invalid character in the input or the output, or the decrypted string does not have a '_' in it) print out the message "Error! corrupted entry 'ciphertext'in vault file.", and continue processing the other data entries as if the one that caused the error didn't exist.
    Note: try the file vault5c, logging in as user mcdowell with password StandDesk. If you give the labels command, the last entry for user mcdowell should give this kind of error.
  4. Hint: A combination of String's indexOf() and substring() methods make it easy to break up the decrypted string into the label (i.e. what comes before the first '_') and the text (i.e. what comes after the first '_').

Part 6 [up to 10pts Extra Credit]: Adding data

A Part 6 solution builds on a Part 5 solution to add the last piece of core functionality: the ability to add new data entries. In particular, one new command is added to the command shell the user gets after a successful login:
add encalg label text
This command should add a data entry for the current user, with the encryption algorithm given by encalg, and form the ciphertext by encryption label_text with algorithm encalg using the password the user authenticated with as the key for the encryption.
Note: if the label entered by the user is already in use by that user, the given text replaces the text in the old entry with that label. In particular, this means that we never gert duplicate labels in data entries for a given user.

file vault6a before sample run file vault6a after
user aviv clear princessxxxxxxxx
user roche shift+caesar *0TRSZ/>:5iq0pcx
data roche caesar dgn]WZac]Wja\af_
$ java Vault vault6a
username: roche
password: Tuba+Time
Access granted!
> add vigenere ssnum 123-45-6789
> quit
user aviv clear princessxxxxxxxx
user roche shift+caesar *0TRSZ/>:5iq0pcx
data roche caesar dgn]WZac]Wja\af_
data roche vigenere LmU[n8punW.md7aw+

What makes this challenging is the sheer number of things that can go wrong with this command — all of which should be handled gracefully.
  1. The user enters an unsupported encryption algorithm. In this case, the program should output "Error! Encryption algorithm 'encalg' not supported.", print the prompt and await the next command from the user.
  2. The user enters a label with characters outside ASCII 42-122, or which includes a '_' character, which is not allowed in labels. In this case, the program should output "Error! Label 'label' is invalid.", print the prompt and await the next command from the user.
  3. The user enters text that includes characters outside ASCI 42-122. In this case, the program should output "Error! Invalid character 'X' in text.", print the prompt and await the next command from the user.

Part 7 [up to 5pts Extra Credit]: "Full" shift+ hashing

Shift+ hashing as implemented in Part 2 is limited to message/passwords of at most 16 characters. If you give it longer messages/passwords, it simply ignores everything after the 16th character. Look at the full shift+ hashing details and extend your implementation to handle longer messages/passwords.

~/$ java TestHashers
algorithm: shift+vigenere
password : ThisIsExactly_16
password read : ThisIsExactly_16
hash computed : 4rgZDs.8u1bjr5A/
~/$ java TestHashers
algorithm: shift+vigenere
password : ThisIsExactly_16_plus_some
password read : ThisIsExactly_16_plus_some
hash computed : K>0F_<6vvHfYP:c^

When to submit

Project 2 is due by 1330 on Tuesday, April 5th. Late projects incurr a penalty of 3^N points, where N is the number of days late.

What to submit

Your electronic submission must include:
  1. all .java file required to compile and run your program - including the ones you were given in the assignment
  2. a file named README with your name and alpha code, which step you are submitting for (including any extra credit), and any issues you know your program has.
Your paper submission must inlcude:
  1. A completed copy of this coversheet on paper.
  2. A printout of your README file plus all of your .java files all done as a single codeprint-generated file! Please make sure that your name and alpha code appear in evey single file .java file.

How to submit

Get the New Submit Script
There is a shiney new submit script that should make things work a whole lot better for all of us. To install it on your VM or your lab account (you should ultimately do it both places) do the following:
  1. Go to new submit script download page and click the Download Peronalized SubmissionScript button (you may have to login as part of this).
  2. Save downloaded script in ~/bin.
  3. Give the command
    chmod 700 ~/bin/submit
That's it! You can test that this has workded by the following:
~/$ submit -h
usage: submit -c=<course> -p=<proj_hw_name> <file1> ... <filek>
Submits source, documentation, and other files.
 Example: submit -c=IC210 -p=lab01 lab01.cpp README.txt
The paper portions of your submission should be stapled together and slid under your prof's door. For electronic submission, continue to use the same method from the previous projects, except that the submit script and the accompanying website has been upgraded and improved by LCDR Kenny. You'll need to get the new submit script (instructions in the box to the right), and pay careful attention as you copy&paste the submit command shown below. Within the directory on your Virtual Machine where you have been saving your project, type:
submit -c=IC211 -p=proj02 *.java README

How projects are graded / how to maximize your points

Things to keep in mind to receive full marks:
  1. Submit all required items (as described above) on time.
  2. Do not submit any .java files that do not compile! Whichever Step you submit, your project should compile and run correctly at that step. If it doesn't, you should be submitting the previous step.
  3. Make sure your output matches the example output in all ways.
  4. Use good object-oriented design! The means adhering to the principles of ecapsulation, data/information hiding, inheritance and polymorphism.
  5. Practice good Java Style. This means proper indentation, use of whitespace within lines of code, logical organization of chunks of code, proper naming of classes, methods, fields and constants in accordance with Java conventions. This means good commenting: your name & alpha in every file, and descriptive comments for each public method at a minimum!
Most important: Look at the the grading sheet!