CIS 5357 - Network Security -
Florida State University -
Fall 2005
Instructor: Breno de Medeiros
Homework
assignment 3 (100 pts = 2 previous assignments):
Goal of
Assignment:
Implement the MTI/A0 key agreement protocol (authenticated
Diffie-Hellman Key Exchange)
Due date:
11/09/2005
References:
Motivation:
- Implement Elgamal, a common method for key agreement used as
a building block in multiple protocols.
- Implement secure network channels.
Recipe for
Success:
- Start working on the assignment as soon as possible.
Give
yourself enough time.
- Start by reading the
references.
Make sure you understand the protocol thoroughly, by drawing diagrams
of the messages, making sure you understand what cryptographic
primitives (Diffie-Hellman, symmetric encryption, hashes) are being
used.
- Follow up by familiarizing
yourself with the Java security classes, and with the BigInteger
class.
- Re-use your experience with
previous exercies.
- Carefully inspect the code for
the TCPEchoServer and TCPEchoClient in simnet.Demo.submission package.
- Test your code as
your build it. Test individual units.
- Be honest
and work individually. Do not share your
code.
Recipe(s)
for Failure:
- Do not study carefully the protocol, skim it or ask a friend
a
couple of questions and assume to have understood it.
- Download the existing code and try to hack your way to
somewhere.
- Wait until the last few days to start. You will
probably
need a considerable amount of time to become familiar with the
APIs. It is better taking it in a little at a time, and build
momentum over several weeks.
- Cheat. Share your code.
Grading of
assignment:
- MTI/A0 implemented
correctly: 50% of the grade.
- If SecureMessages
implemented correctly: 40% of grade
- Documentation: 10%
Specifications:
You should modify the TCPEchoClient and TCPEchoServer Classes in
the submission.Demo subpackage of Simnet.
The modified classes should be called TCPDHClient and TCPDHServer.
In order to generate DH public key parameters, use the classes
org.bouncycastle.crypto.generators.DHParametersGenerator an
org.bouncycastle.crypto.params.DHParameters;
DHParametersGenerator DHgen = new DHParametersGenerator();
SecureRandom srand = new SecureRandom();
DHgen.init(2048,80,srand);
DHParameters DHparam = DHgen.generateParameters();
try {
OutputStream fileout = new
FileOutputStream("DHparams.out");
ObjectOutputStream oout =
new ObjectOutputStream(fileout);
DHParametersWrapper dhwrap = new DHParametersWrapper();
dhwrap.setDHParameters(DHparam);
oout.writeObject(dhwrap);
oout.close();
} catch(IOException e) {
e.printStackTrace();
}
Execute this code immediately to generate your params once. Make
a backup copy of your params. Generating the params the
first time may take hours! Having the same parameters as another
student is considered cheating. (The probability that you will
generate the same params as somebody else by chance is probably smaller
than an elephant will suddenly materialize on your living room by
quantum effects.) This parameter is used by both Client and
Server.
Once you have generated the DH parameters, you can
generate your private key (a securely random BigInteger numBits long,
where numBits is the bitlength of q. You can call
BigInteger q = DHparam.getQ();
method on DHparam to get the value q, and compute its bitlength
as q.bitLength();
Once a client and server keys have been generated they can be saved as
DHPublicKeyParameters. You should also save your private keys
separately (as BigIntegers)
You should proceed the same way to obtain the values used in the MTI/A0
algorithm.
It is required that the TCPDHClient be invoked when one must "connect"
to a listening server (an instance of
TCPDHServer).
After the completion of the 3-way TCP handshake, the TCPDHClient must
send a message containing a single
object that implements the following interface.
===================================MTI/A0 implementation
public interface CryptographicAuthenticationMessage {
public String getSenderName(); // returns the name of
the client sending this message
public Object getAlgorithmSpecs();
// For this protocol, the return
value should be of type
// org.bouncycastle.crypto.params.DHPublicKeyParameters;
// Since this class should be serializable, you will need to keep your DHPublicKeyParameters
//using a DHPublicKeyParametersWrapper instance.
public
java.sql.Timestamp getTimestamp();
// The time this message was created
public Object[] getArguments(); // The other
arguments of this protocol.
public String getSessionID(); // In this protocol, it is
computed as the concatenation of Sender name, the special
// character sequence '::' followed by the
receiver's name, another '::' and the
// string obtained
by calling the method toString() in the Timestamp.
}
The server, at this point, should:
1) Check that it knows the Sender by name
2) Check that the public key part of the DHPublicKeyParameters returned by the getAlgorithmSpecs()
corresponds to the sender. (You should assume a few associations
of public keys and names).
3) Check that the timestamp is not too different from the current time
at the server (acceptable clock skew of at most 5 seconds).
4) Check that the sessionID is computed correctly from the Sender's
name, the server's name, and the sender's timestamp.
5) Read the value returned by getArguments().
If all steps succeed, the server continues with the protocol.
Otherwise, it should ignore the message. (And time out if no valid
message is received within 30 seconds).
Note that the returned array by getArguments() should contain a single
object, OF TYPE BigInteger, the value A = ga mod p which
specified as a random value in a DH key exchange.
The response by the server is of the same type (CryptographicAuthenticationMessage),
with the appropriate changes:
1) The sender name is the server's name
2) The DHPublicKeyParameters are the server's parameters
3) The timestamp is that of the server
4) The sessionID is THE SAME AS THE VALUE SENT BY THE CLIENT.
5) getArguments() should return a single value OF TYPE BigInteger,
namely B = gb mod p which is the random value in a DH Key
Exchange.
The shared pre-key should be computed as in the MTI/A0 key exchange,
i.e., as pre-K = gax + by mod p, where x is the private key
of the server, and y the private key of the client. More
specifically,
i.) Client computes pre-key pre-K = (Public key of server)a
By mod p.
ii.) Server computes pre-key pre-K = (Public key of client)b
Ax mod p.
The server and client then initialize a SecureRandom generator, set the
seed to be the value of the pre-key (converted to bytes using
toByteArray()), and compute a 1280-bit output. The first 512 bits
will be used as encryption key (Kenc0,Kenc1)
for AES-256. The second 512 bits will be used as authentication
keys (Kauth0,Kauth1) for
HMAC-SHA256. The next 128-bits are IV0, and the following 128-bits are IV1.
============================================End of MTI/A0 implementation
Now, each party must prove that they have computed the same
pre-key. The client starts, sending a message of type
public interface SecureMessage {
public byte[] getEncrypted();
public byte[] getAuthenticator();
}
The bytes returned by getEncrypted are computed as follows: First
compute a String contained the concatenation of
1) The number '0'
2) '::'
3) SessionID
5) '::'
5) A.toString() // A is the random challenge by the client
6) '::'
7) B.toString() // B is the random challenge by the server
Then convert the above String to a byteArray, and encrypt it using
AES-CBC with PKCS7 padding and with IV = IV0 and key Kenc0. The
output is the value of getEncrypted();
The value returned by getAuthenticator() should be the HMAC-SHA256
digest of the byte array returned, getEncrypted() computed under the
authentication key Kauth0.
The server must first verify if the authenticator is computed correctly
from the encrypted value. Then it should decrypt and check that
the contents are as expected. It should reply with a message also
of the type SecureMessage. The only difference is that
the value in (1) should be '1', the IV used in encryption should
be IV1, and the keys should by Kenc1 and Kauth1.
After this, the client should wait for messages of the type "echo X",
where X is a string, and transmit it as a SecureMessage. Here,
the String 'X' is just converted to byteArray and encrypted (without
re-setting the cipher in AES-CBC mode, still with IV = IV0), as well as
authenticated---here re-setting the MAC each time.
The Server should verify the MAC, decrypt, re-encrypt the message using
the IV = IV1, Kenc1, and authenticate it with Kauth1.
The Client must verify and decrypt the Server messages, printing the
result on screen as an echoed value. If the authentication fails,
the client prints an error.
Every time a server of client detects an error, it should ignore and
continue waiting for the next message. If idle for more than 30
seconds (including erroneous messages), the client and/or server should
close the connection using a TPC RST packet.
Deliverables:
1) TCPDHClient and TCPDHServer classes, and auxiliary classes.
2) Two serialized objects of type DHPublicKeyParametersWrapper, in files named
serverKey.public and clientKey.public; (use
ObjectOutputStream to write these)
3) Two serialized objects of type BigInteger (the private keys), in
files named serverKey.private and clientKey.private.
4) A file containing the server and client names.