In this assignment, you will add functionality to the code you wrote for Assignments 1 and 2, to reach the goal of implementing a secure facility for client-server communication across the Internet.
As before, we will give you some of the code you need, and we will ask you to provide certain functions missing from the code we provide. As before, you must not use any cryptographic libraries; the only primitives you may use are the ones we gave you, and ones you implemented from scratch yourself. Your solution will build on the functionality that you implemented for Assignment 2, but we will test your solution with our own implementation of the Assignment 2 functionality. Your solution must work correctly when we do this — this shouldn’t be a problem as long as you respect the API boundaries between the different classes we have given you. To help you mimic the testing setup that we will use, we are providing a pre-compiled JAR file (library) containing our reference solution to Assignment 2. We recommend that you use this instead of your own solution to Assignment 2.
In this assignment you will implement a secure channel abstraction that can be used by two programs, a client and a server, to communicate across a network, with the confidentiality and integrity of messages guaranteed. We have given you a class InsecureChannel
that implements a channel that works but is not secure: everything is sent in unprotected cleartext. We have also given you stubbed-out code for the SecureChannel
class that extends InsecureChannel
and (once you have modified it) will protect security and confidentiality.
To facilitate the testing process, we have provided the following files:
Util432s.java
- provides a few methods used by the other testing files (feel free to use these methods for debugging).ChannelTest.java
- provides a demonstration that the InsecureChannel
class works correctlySecureChannelTest.java
- used to test your SecureChannel
class (once implemented). Note that this class does NOT test security properties, instead testing only basic functionality. Also, note the commented out lines that give an example of how you can use Util432s
for debugging.InsecureChannel.java.debug
- provides a special version of InsecureChannel
and provides the ability to echo channel transmissions to the screen. To use this class, simply rename the file InsecureChannel.java
and place it in the same directory as SecureChannel.java
.Your AuthEncryptor
class should implement the following API:
public class AuthEncryptor {
public AuthEncryptor(byte[] key)
public byte[] authEncrypt(byte[] in, byte[] nonce, boolean includeNonce)
}
This class is used to perform authenticated encryption on values. Authenticated encryption protects the confidentiality of a value, so that the only way to recover the initial value is to decrypt the value using the same key
and nonce
that was used to encrypt it. At the same time, authenticated encryption protects the integrity of a value, so that a party possessing the same key
and nonce
(that were used to authenticate it) can verify that nobody has tampered with the value.
Code that uses AuthEncryptor
will be required to pass in a different nonce
for every call to the authEncrypt
method. The AuthEncryptor
class is not required to detect violations of this rule; it is the responsibility of the code that uses AuthEncryptor
to avoid re-using a nonce
with the same AuthEncryptor
instance.
includeNonce
is true, then the nonce
should be included (in plaintext form) in the output of authEncrypt
. If includeNonce
is false, then the nonce
should still be used in calculating the output, but the nonce
itself should not be copied into the output. (Presumably the party who will decrypt the message already knows what the nonce
will be.)
Your AuthDecryptor
class should implement the following API:
public class AuthDecryptor {
public AuthDecryptor(byte[] key)
public byte[] authDecrypt(byte[] in)
public byte[] authDecrypt(byte[] in, byte[] nonce)
}
The value passed as in
will normally have been created by calling authEncrypt()
with the same nonce
in an AuthEncryptor
that was initialized with the same key as this AuthDecryptor
.
If the integrity of the input value cannot be verified (that is, if the input value could not have been created by calling authEncrypt()
with the same nonce
in an AuthEncryptor
that was initialized with the same key as this AuthDecryptor
), then this method returns null
. Otherwise it returns a newly allocated byte-array containing the plaintext value that was originally passed to authEncrypt()
.
If the nonce
is included in the message, then the message should be processed with the authDecrypt(byte[] in)
method. Otherwise, the nonce
should be provided along with the ciphertext to authDecrypt(byte[] in, byte[] nonce)
.
Your SecureChannel
class should implement the following API:
public class SecureChannel extends InsecureChannel {
public SecureChannel(InputStream inStr, OutputStream outStr,
PRGen rand, boolean iAmServer,
RSAKey serverKey) throws IOException
public void sendMessage(byte[] message) throws IOException
public byte[] receiveMessage() throws IOException
}
The constructor will contain the vast majority of your code. Its role is to set up the secure channel such that the sendMessage
and receiveMessage
methods can do their jobs. These methods should provide authenticated encryption for the messages that pass over the channel, ensuring that messages arrive at the receiving end in the same order that they were send on the sending end. Furthermore, when the client is setting up its channel, it should also authenticate the server’s identity, and should take whatever steps are necessary to detect any man-in-the-middle. If one of the two parties (server or client) detects a potential security problem during channel construction, that party should close the channel by calling close()
. To ensure foward secrecy, the close()
method must delete any secret values associated with the channel. You can assume the serverKey
(public key) passed to the constructor of SecureChannel
on the client side of the communication is verified externally in some way (for example via a trusted certificate).
The underlying InsecureChannel
will normally deliver messages in the same order they were sent. But note that an adversary might try to reorder messages. The receiveMessage
method should return null
if an invalid or out-of-order message shows up.
ChannelTest.java
, which will provide you with a better understanding of how InsecureChannel
(and SecureChannel
) are intended to be used. In particular, two instances of InsecureChannel
will be created (one for the server → client channel and one for the client → server channel), each of which connects up two data streams (one input and one output data stream). Messages are sent through the channel using the sendMessage()
method, and whenever a message is sent via a channel, it stays there until a corresponding receiveMessage()
call is made. Luckily for you, you will not need to think about InputStreams
or OutputStreams
at all. That is all taken of in InsecureChannel.java
and in the main function of the ChannelTests
.InputStreams
and OutputStreams
, don’t worry, you won’t be dealing with them very closely. The same goes for runnable classes and threads.InsecureChannel.java.debug
over InsecureChannel.java
and running ChannelTest
, which will let you see the raw traffic being sent over the channel.SecureChannelTest.java
to get a feeling for how the SecureChannel
instantiations differ.Report.txt
file. This report should succinctly demonstrate that you have considered both the threats against your implementation and the specific techniques you use to defend against them. One way to format this is to describe a threat in a sentence or two, followed by a sentence or two that explains how your implementation defends against it; repeating this format for each threat.SecureChannelTest
, if a thread calls readMessage
and there is no message available, it will wait until a message becomes available.Submit your files to the Gradescope:
Report.txt
Threat model and Mitigation Description (see Getting Started for requirements).AuthEncryptor.java
- Source code file containing your implementation of the AuthEncryptor
class.AuthDecryptor.java
- Source code file containing your implementation of the AuthDecryptor
class.SecureChannel.java
- Source code file containing your implementation of the SecureChannel
class.