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.