Due: Friday, June 20, 2013 by midnight.
Your assignment is to update your secure system from Assignment 1 and add three new operations designed to introduce a covert channel into the system. You will implement the channel and use it to signal information from a high level user to a low level user. Finally, you will measure and report the bandwidth of the channel.
Make sure that all members of your group understand the code and contribute equally, as much as possible, to its development. If anyone gets a free ride, they'll be very disadvantaged later. For this assignment:
java CovertChannel v inputfilename java CovertChannel inputfilenameYour output file should be named inputfilename.out. That is, take the supplied input filename and append ".out" to it. If the call has the "v" (for "verbose") option, you should also output to a file called "log" the instructions that you generate. (See below.)
CREATE subject_name object_name DESTROY subject_name object_name RUN subject_nameAgain, instructions are NOT case-sensitive.
The semantics of CREATE is that a new object is added to the state with SecurityLevel equal to the level of the creating subject. It is given an initial value of 0. If there already exists an object with that name at any level, the operation is a no-op.
DESTROY will eliminate the designated object from the state, assuming that the object exists and the subject has WRITE access to the object according to the *-property of BLP. Otherwise, the operation is a no-op.
RUN allows the named subject to execute some arbitrary private code. It has no access to any of the state objects, and so should be irrelevant to the security of the system. It models whatever processing the subject may do with the value it has just read. You should code this as a method within your SecureSubject class. For this assignment, the point of RUN is to allow Lyle to do whatever processing is necessary to input the value from Hal, add it to a byte he's creating, and if the byte is complete, write it to the output.
Note that calls to RUN will do different things, depending on the value of the local state, particularly the executing subject. That is, RUN does very different things for Lyle than for Hal. The code that RUN executes can do anything to the local state of the running Subject but must not access any of the system objects. You can have local variables and manipulate those as you like, but cannot access any of the SecureObjects.
As with the previous assignment, you will define two subjects Hal and Lyle, but no objects initially. You will generate the instructions necessary to pass information from Hal to Lyle using the covert channel we described in class. (See below.) However, generate the instructions to be executed on the fly rather than reading them from an external file. That means that your program generates instructions and executes them as needed. There is no file of instructions. (If you generated them, wrote them to a file, and then read them back in your performance would suffer quite badly from the extra IO.)
The execution of the instructions you generate should implement a covert channel passing information one bit at a time from Hal to Lyle. You can assume that Hal and Lyle are the only two subjects and have those names. Also note that you can eliminate most parsing and syntax checking since you'll be generating only correct instructions.
If your program is running with the "v" parameter, you should log the instructions that you generate one per line to a file called "log". This will allow us to ensure that you are actually using the covert channel to transfer the information. For your timing runs, don't use the "v" parameter.
Implement the following covert channel. (If you think you have a better channel that you'd like to implement, go for it, but discuss it with me first.) For this channel, to send a 0 bit Hal executes:
RUN HAL CREATE HAL OBJTo send a 1 bit, Hal just does a RUN HAL. (Note: You may not even need for HAL to do RUN. It depends on how you structure your code. If you don't need it, don't use it.) Lyle senses the bit (the presence or absence of the object) by executing:
CREATE LYLE OBJ WRITE LYLE OBJ 1 READ LYLE OBJ DESTROY LYLE OBJ RUN LYLEIf Lyle sees a value of 1 returned from the READ, then his CREATE succeeded, and Hal has not previously created the object (sending a 1 bit). If Lyle sees a value of 0 returned from the READ, then Hal has previously created the object, Lyle's CREATE has failed, and Hal has sent a 0 bit. (Notice that the value of 1 in the WRITE could have been any non-zero value.)
Lyle's RUN statement allow him to do whatever he has to do to record the bit into his internal state, add it to the byte he's creating, and output the byte if he's received the 8th bit for that byte. You definitely need RUN for Lyle. You're not allowed do this stuff in the top-level routine.
The goal is to send an arbitrary stream of bits (actually a complete ASCII file) over this channel. I advise getting your program running by sending some small fixed number of bits and making sure they arrive correctly. However, the version you will submit will take a filename parameter on the command line. Your program will read the file contents (probably one byte at a time using a ByteArrayInputStream), convert each byte to 8 bits, send them through the covert channel, reconstruct the byte on the receiving side and write it out to a file. The idea is to transfer the contents of an arbitrary ASCII file over the channel.
As an alternative to using a ByteArrayInputStream, you can read a line in the file into a String and access the bits from there. Don't read the entire file into a String. That's not scalable.
Your program may work for non-ASCII file types, but doesn't have to. In a realistic system where the subjects were running as concurrent threads, that would be very useful because Lyle would have to know when he can stop receiving. If you only worry about ASCII files, you can assume that only ASCII characters appear in the file. That means you can use a non-ASCII character to signal the end of the input. E.g., if Lyle receives a null byte 00000000, then he knows he can stop receiving. But for this version, your main program probably knows when Hal is out of bits to send and just stops generating instructions for Lyle. That is, you probably don't have to worry about sending an end byte from Hal to Lyle. I think that's true, but I'm not absolutely certain!
Ideally, reading bytes from the file, breaking them into bits, etc. should be done by the subjects themselves using their RUN operation. But you can use your main function to handle Hal's portion of that kind of thing if you want to. That is, you can treat the entire program as a big covert channel engine, feeding Hal one bit at a time, as long as you implement the covert channel according to the directions above and don't cheat. (A better thing to do is for Hal to read in the bytes, break them into bits, and send them using his RUN operation for all that, but that's harder.)
The output file should be essentially identical to the input file. Make sure to check that you preserve the first and last characters in the file. I suggest using the Linux diff utility to compare the files.
Note that line endings are represented differently in different systems. Early teletype printers used the CR and LF control characters, respectively, to move the print head left and then scroll down one line. But line termination is treated differently by different modern operating system: Macintosh uses a CR (ASCII 13); Linux uses a LF (ASCII 10); Windows uses both. Most FTP programs substitute appropriate line endings if transferring in ``text mode.'' If in ``binary mode'' no translations occur. This shouldn't matter to you unless you transferred your input file onto a different system in binary mode. In that case, your program might not recognize the end of lines correctly. This past summer, some students' programs didn't handle the end of line characters correctly so a file with lines ended up as one long line.
Finally, time your program execution on several different input files and compute the channel's average bandwidth in bits / second. Your program should compute and write to standard output the timing and bandwidth for a given run. Also, record that information for several runs, compute the average, and include that information along with the machine type and clock speed in the README file for your program. You won't be graded on the bandwidth, but we'll collect the results and see which group's program was the most efficient.
For a ballpark, one group in a recent class reported the following bandwidths for different documents:
|Pride and Prejudice||717,571 bytes||600 bits/ms|
|Metamorphosis||141,411 bytes||1126 bits/ms|
|Test||45 bytes||40 bits/ms|