We will give you some of the code you need, and we'll ask you to provide certain functions missing from the code we provide. You can download the code we are providing.
We are giving you two fully implemented code files, on which you should build all of the crypto functionality you need to do this assignment. The file TrueRandomness.java gives you access to truly random bits, which have been extracted from the unpredictable state of the local system. The file PRF.java gives you access to a pseudo-random function, as described in lecture. These are the only two crypto primitives you are allowed to use---any other crypto you use must be built (by you) on top of these two files. Specifically, you may not use any other crypto libraries, not even the ones that are part of the standard Java libraries.
In this assignment you will implement four facilities, by modifying four Java code files. You will modify PRGen.java to implement a pseudo-random generator. You will modify StreamCryptor.java to implement a stream cipher. You will modify DataSealer.java to implement a facility for encrypting and integrity-marking blocks of data, and DataUnsealer.java to implement the corresponding facility for integrity-checking and decrypting data. In each case, we have provided you with a code file in which some parts of "stubbed out". You will replace the stubbed out pieces with code that actually works and provides the required security guarantee. We have put a comment saying "IMPLEMENT THIS" everywhere that you have to supply code.
PRGen.Your PRGen class should implement the following API:
public class PRGen extends Random { public PRGen(byte[] key) //creates a new PRGen protected int next(int bits) //generates the next pseudorandom number }
You do not need to implement any other methods of the Random class. The long key that is used by the Random class will not be used by your PRGen class.
As stated in the Random spec "The general contract of next is that it returns an int value and if the argument bits is between 1 and 32 (inclusive), then that many low-order bits of the returned value will be (approximately) independently chosen bit values, each of which is (approximately) equally likely to be 0 or 1." For example, if you call next(4), it will return an int between 0 and 15. If you call next(32), it will return an int between -2,147,483,648 and 2,147,483,647 (the highest order bit determines the sign).
Your PRGen must obey the following three properties:
StreamCryptor.Your StreamCryptor class should implement the following API:
public class StreamCryptor { public StreamCryptor(byte[] key, boolean encrypting) public byte cryptByte(byte in) //encrypts the next byte public void cryptBytes(byte[] inBuf, int inOffset, //encrypts next numBytes byte[] outBuf, int outOffset, //in the inBuf, writing int numBytes) //results to the outBuf }This class encrypts or decrypts a stream of bytes, using a stream cipher. Each instance of this class will be used to encrypt or decrypt data, but not both.
DataSealer.Your DataSealer class should implement the following API:
public class DataSealer { public DataSealer(byte[] key) public byte[] seal(byte[] in) //uses key to create sealed ersion of input data }
This class is used to "seal" a sequence of values. "Sealing" protects the confidentiality of a value, so that the only way to recover the initial value is to "unseal" the value using the same key that was used to seal it. At the same time, "sealing" protects the integrity of a value, so that a party unsealing the value using the same key (that was used to seal it) can verify that nobody has tampered with the value since it was sealed.
If a Sealer is used to seal a sequence of values, then those values should be unsealed by a single Unsealer, in the same order that they were sealed. If such values are unsealed in a different order, or by more than one Unsealer, errors will result (as described in the Unsealer class).
DataUnsealer.Your DataUnsealer class should implement the following API:
public class DataUnsealer { public DataUnsealer(byte[] key) public byte[] unseal(byte[] in) //uses key to create unsealed version of input data }
This class is used to "unseal" a sequence of values. The value passed as in will normally have been created by calling seal() in a DataSealer that was initialized with the same key as this DataUnsealer.
If the integrity of the sealed value cannot be verified, or if the sealed value is out of sequence (i.e., it was not the next value sealed by the corresponding DataSealer), then this method returns null. Otherwise it returns a newly allocated byte-array containing the value that was originally passed to seal().
If this method ever returns null, then all subsequent calls to this method must also return null--something unexpected has gone wrong, and we must always return null in the future, just to be safe.
Tips for Getting Started. This list may grow in response to Piazza questions.
Advice on testing crypto code. As always, it's important to test your code. But you should be aware that crypto code presents different testing issues than other code does. Testing can sanity-check your code, but it can't verify that your code has the desired security properties. For example, if your code is encrypting data for confidentiality, you can test whether the ciphertext is the right size, and you can test whether the ciphertext looks kind of randomish, and you can test whether different plaintexts yield different ciphertexts. But you can't test whether there is a way for an adversary to recover the plaintext. So by all means test your code --- if you don't, it's almost certain not to work --- but remember that passing the tests is not enough.
Submitting your solution. You should submit any code files that you modified or created. You don't need to submit any files that you did not modify. You may submit any number of times. Only your most recent submission will be graded. Submit your code using this link.
Copyright 1998-2012, Edward W. Felten.