| Advanced Programming Concepts | Spring 2008 | |
|---|---|---|---|
Term Project |
Due: Finals Week
Submit solution to franco@gauss.ececs.uc.edu
The Monitor source is available here.
List Serve:
For up-to-the-minute news, complaints, changes, and so on, be sure to join the project listserve. Do this by pointing your browser to http://helios.ececs.uc.edu/mailman/listinfo/project. Answer all the questions and submit. Then to post a note send email to project@helios.ececs.uc.edu.
Grading Policy:
It might make sense to read the rest of this page and then return here to see the grading policy. This grading policy applies to the project only. Students should read this carefully and make suggestions for refinement until they are comfortable that they completely agree with and understand the policy and they believe there will be no surprises at grading time.
My grading objective to assign high grades to students who have mastered the Java language and have demonstrated an ability to use many of its features creatively and effectively. If all students have mastered the language then all students get high grades. A student is not competing against any other student for a grade.
More is not better. A lot of bad code is no substitute for a small amount of good code. Using all features of Java does not insure a high grade. Using a subset of features well is preferred to sloppy use of many features. If you are using a feature where another would be more appropriate, some points will be lost.
High grading does not depend at all on the amount of wealth accumulated by an individual's system. Nor is it heavily dependent on strategies employed for acquiring wealth. There are some required elements that must appear in your submission in order to get a high grade. There are also some optional elements that help secure a high grade. The required elements are as follows: your system must be able to locate the Monitor, and other systems. It must be able to send and receive messages formatted below (don't look - it's not there yet). It must be able to provide for secure transmission of messages. It must make an attempt at preventing an intruder from gaining access to sensitive information. Optionally, a system may attempt to seek and succeed in finding, at least in some cases, information related to the wealth of other systems; a system may "plant" Java code on another system and run it. Any creative tricks will be appreciated as well.
Rationale:
This will use just about every aspect of the material learned especially networking and security.
Objective:
Each student is to design and build a Java system which attempts to accumulate a maximum amount of wealth through communications with other student systems and a monitor system maintained by the instructor and an aid.
Description:
| This document will describe the protocol and design used by client hosts in interacting on the Monitor server. The Monitor is a server provided to implement a game for a set of participants to play. The purpose of the game is to earn as many points as possible through the use of various interfaces and features. The participants may implement various mechanisms for attacks against other participants to prevent victims from receiving points and even for stealing points directly from other participants. |
Communication Modes with the Monitor
The Monitor will provide multiple services for interfacing from the client machines. There are two connection methods: Active and Passive. The Active Connection is established when a game participant initiates a connection to the Monitor. The Passive Connection is established when the monitor initiates a connection to a listening client.
Active Connections
The monitor provides active connections to the game participant by listening on a pre-selected port. The selected port(s) for the project is 8180 of helios.ececs.uc.edu. Active connections require participants to connect to the selected port and authenticate themselves to the listening monitor via a pre-selected cookie or password. A participant logs in over an active connection.
Passive Connections
The login protocol, described below, requires participants to start a passive server which listens for Monitor messages on a legal port of their choice. The port number is given to the Monitor during the login handshake. The passive server serves two functions. First, it is used to accumulate wealth for the participant. The Monitor connects to a participant's passive server from time to time to verify that the server is up and running. Acknowledgement of so-called alive queries is necessary for the participant to receive wealth from the Monitor. Alive queries may happen at any time, for any reason. Second, passive connections are the primary medium for any sort of out-of-band connections, such as for proxied user-to-user communications.
Communication Protocol
All communication between active client, passive client, and Monitor is in the form of line-buffered, text messages. Each text message may contain one or more lines of text, called a message group. Each line of a message group is called a Directive. The format of each directive in a message group is the following:
DIRECTIVE: body-of-directive
Possible directives are given below. The client normally responds to
the message group with a single line of text with the following
format:
COMMAND ARG1 ARG2 ARG3...
The delimiter of command and arguments is the blank character.
Possible commands are given below.
A communication sequence is a series of exchanges between Monitor and client to achieve some result. It begins with the Monitor sending a message group specifying a command that the client is expected to issue next. The client issues the command with arguments. The Monitor responds with another message group and so on until some result is achieved.
Monitor Directives
The types of directives used by the Monitor fall into the following categories: RESULT:, REQUIRE:, ERROR:, COMMENT:, and WAITING:. This list may be ammended in future revisions of the monitor (the current version is 2.9). Directive format is given above. The body of a directive specifies associated detail which informs the client of the status at the current point of the communication sequence. Message groups always end with the WAITING: directive. A common communication sequence looks something like this:
...
CLIENT> ALIVE S54DFSESX4234DX
MONITOR> RESULT: ALIVE Identity verified.
MONITOR> REQUIRE: HOST_PORT
MONITOR> WAITING:
CLIENT> HOST_PORT LOCALHOST 31337
...
One might think of the WAITING: directive as a flow-control
message from the Monitor. Upon receiving the WAITING:
directive, the client knows that the monitor is expecting it to act on
the provided information and issue a command when ready. The
following table explains the function of each directive.
| Name | Description |
| REQUIRE: ARG1 |
Indicates that the sender is demanding that the next Command from receiver
be ARG1. If the receiver sends some Command besides
ARG1, the most likely result will be a
COMMAND_ERROR: Directive, and another REQUIRE:
ARG1 Directive in the next Message Group.
|
|
|
This Directive is used to return results from the last Command issued by the
receiver. ARG1 will be the Command for which these are
results. STRING, in this case, will not be
free-form. Rather, it will be in the format as described by the
result format for the Command, ARG1. In many cases,
particularly with those commands that generate no results,
STRING will be the empty string. Note that the
RESULT: Directive does not appear when there is an error
executing a given command. See the COMMAND_ERROR Directive.
|
|
|
This Directive is used to give verbose output on the operation of various
Commands, and to give general feedback about a variety of things.
STRING is completely free-form, and more than likely will only
be useful to human eyes for debugging. Not every Message Group is guaranteed
to have a COMMENT directive.
|
PLAYER_PASSWORD_CHECKSUM: ARG1
|
This Directive is sent by the Monitor to authenticate to the player.
ARG1 is a SHA-1 digest of the player's password, represented in
hexadecimal. This directive is most useful when the player receives a
connection on its server port. The Monitor will always have this Directive
in the first Message Group that it sends during an active client connection.
However, it is up to the player to verify that ARG1 does, in
fact, authenticate the Monitor properly. Note that some players
will most surely be attempting to impersonate the Monitor!
|
| WAITING: | Indicates that the current Message Group is complete, and the sender awaits a Command from the receiver. |
|
|
Indicates that an error occurred during the processing of the last Command.
This could be because of invalid arguments, incorrect number of arguments,
an unexpected Command (e.g., if a different Command is currently
REQUIREd), or because a Command is not yet implemented. The
STRING will be a diagnostic error message.
COMMAND_ERROR only appears in a Message Group if there was an
error with the last command.
|
| TRADE: Player_1 to_trade to_amount for Player_2 for_trade for_amount | This Directive is sent by the Monitor to Player_2, on behalf of Player_1, because Player_1 wishes to offer a trade to Player_2. The resource offered for trade is given by to_trade and the amount by to_amount. Note that Player_2 will always be the receiver of the Directive, otherwise the Directive is basically meaningless. The resource desired in exchange for that offered is given by for_trade and the amount by for_amount. |
| WAR_DECLARATION: Player_1 | This Directive is sent by the Monitor to a player, on behalf of Player_1, who is declaring war on the player. Note that Player_1 will always be the receiver of the Directive, otherwise the Directive is basically meaningless. This Directive usually occurs in a message group with a REQUIRE: WAR_DEFEND. |
| WAR_TRUCE_OFFERED: Player_1 to Player_2 resource amount... | This Directive is sent by the Monitor to a player on behalf of Player_1 who is asking for the truce. The identity of the Player who will benefit from the truce is Player_2. The tail of the Directive consists of one or more resource, amount pairs offered in negotiating terms of the truce. If the truce is accepted, the amounts of those resources will be transferred from Player_1 to the player. |
RESULT: codes
When the Monitor responds to the action(s) of one of the clients, it sends a RESULT: directive to the client. This directive has the following format:
RESULT: body-of-result-directive
The above directive is the result of some operation the client
performed. The body of the directive may not necessarily be all caps
or all lowercase and may consist of many blank or comma separated
tokens. The blanks are not considered separators in the same way as
they are in the client commands. After a RESULT: is
received, a client may parse the body to determine what course of
action to take next.
REQUIRE: codes
The Monitor uses the REQUIRE: directive to demand information from a client. This directive requests that the client perform a specific action. Typically, the action is invocation of a command which has been determined by the Monitor and which is included in the REQUIRE: directive. In the sample communication sequence given above, the Monitor issues a REQUIRE: HOST_PORT directive and expects the client to respond with a HOST_PORT command. After receiving the WAITING: directive, and starting a passive server on port 31337 of the localhost, the client sends the HOST_PORT LOCALHOST 31337 command to the Monitor. More information on commands is given below.
WAITING: codes
This directive has no body. Its presence merely signifies it is waiting for the client to some specified information to the Monitor.
COMMENT: codes
The Monitor may place comments anywhere in a message group before the WAITING: directive. Comments are generally informational messages that may be of use to the client. They do not have a specific format, except that a comment message may follow the COMMENT: token. For example, a common message announcing the Monitor looks like this: COMMENT: Monitor Server Version 2.9.
COMMAND_ERROR: codes
COMMAND_ERROR: directives are sent by the Monitor in response to a failed command or connection. An error will result if a command does not succeed as expected. The connection remains alive and the client is expected to take some action to recover from the error. Most errors are designated with a COMMAND_ERROR: code to specify that they are a result of a failed command.
Client Commands
A client sends a command upon receiving the WAITING: directive from the server. A command consists of the command type followed by any arguments that might be necessary for the Monitor to execute the command successfully. Any command that a client sends without proper arguments will result in an error message directive giving a description of what arguments the command was supposed to be run with. Since the commands available to the participant are numerous, only a few key commands will be listed here. The rest will be listed in the command reference towards the end of this document. The commands that will be covered by this section are outlined here:
| Command | Description |
|
|
This command is used to send the sender's identity to the receiver. A
player's identity is not a secret---like a username, it is the method by
which others (including the Monitor) identify the player.
The participant_name field is the identity of the username.
The key-for-encryption field is optional. If it is
sent, it should be the Player's portion that is needed
to do shared-secret discovery. If successful, the message group
returned by the monitor includes
In addition the returned message group contains
|
|
|
This command is used to transmit the player's password to the monitor.
password is the player's password. The Monitor sometimes sends
REQUIRE: PASSWORD after a successful IDENT
command. The result is
|
|
|
This Command is sent to indicate to the Monitor that the
Player is alive. The alive-cookie should be the player-specific
monitor password ("cookie") that was given to the Player by the Monitor
as a RESULT: of the PASSWORD Command. If the
alive-cookie is not the right password ("cookie"), a
COMMAND_ERROR: will be returned and the player will not be
counted as alive. Otherwise, the result is
|
|
|
This Command is always sent by an active client process of a player to the
Monitor. It is used by the sender to inform receiver (usually the Monitor)
what hostname and port the sender currently lives on. The hostname
should be the fully qualified DNS name of the host where the server process
of the sender is running. The tcp_port should be the integer port
number (between 2048 and 65000, inclusive) where the server Socket for the
sender is running on. Note: This Command will only be
successful if there is currently a server process for the
sender running on the given hostname and port. The result is
|
|
|
Give the Monitor your public key for encryption so that it may
register your key in its certicate authority. The arguments are the
two components of your RSA public key. The result is
|
| QUIT |
Ends the current session. The Monitor's response includes the
directive
|
| PLAYER_STATUS |
This Command is used to check on the status of the sender's wealth.
The result is
|
| SIGN_OFF |
This command is used to tell the Monitor to "forget about the Player". The
Monitor "forgets about the Player" in two ways. First, it forgets what host
and port the Player's server is on. This means that the
HOST_PORT will be REQUIRE: the next time the
Player connects to the Monitor with an active client connection. In
addition, the Monitor will "forget about the Player" by invalidating the
current Player-specific Monitor password for the Player. This means that
the Player will get a PASSWORD command REQUIRE:
the next time that the Player connects to the Monitor with an active client
connection. Note that if you execute this command, the Monitor will
consider that you are not alive, until you again send the
HOST_PORT command. The result is
|
| CHANGE_PASSWORD old_password new_password |
This command is used to request a change in the player password and the
player-specific monitor password. The old_password is the player's old
password (the one currently in use). The new_password is the
new password that the player would like to use from this time forward.
Note that the password is only changed if the command successfully returns a
RESULT: Directive in the next Message Group. The result is
CHANGE_PASSWORD command is sent at the end of one of these
"marathon" connections, Players may be cut off before they can receive the
new Monitor password, and may be locked out of the game.
|
| GET_CERTIFICATE player_identity |
Get the certificate containing the hash of the exponent and modulus of
the public RSA key of the player with the named identity. If
MONITOR is used for player_identity then the Monitor's
hash is returned. The result is
|
| SYNTHESIZE resource |
This command is used to synthesize a complex resource. The complex
resources are WEAPONS, COMPUTERS, and
VEHICLES. The name of the resource is given by
resource. Be sure that the name of the resource matches the
name given by the PLAYER_STATUS command. See the table in
the "Synthesize" section for more information about what raw resources
are needed to synthesize the complex resources. The result is
STRING is the amount of resource held increased by
one. A COMMAND_ERROR is sent if the resource could not be
synthesized for any reason.
|
| TRADE_REQUEST Player_1 to_resource to_amount for Player_2 from_resource from_amount |
This command is used by the initiator of a trade to ask the Monitor to
confirm the trade, by making a request to the other player. The arguments
are exactly the same as those to the
TRADE: directive. Note that Player_2 may be
MONITOR. In this case, the Player is requesting a trade with
the Monitor itself. If the Monitor has the requested materials available,
the Monitor approves trades that at current market values, plus a percentage
mark-up. The result is
|
| TRADE_RESPONSE ARG1 |
This command is used to either accept or reject a trade request. This
command is most probably used in response to a Message Group that contained
the TRADE: Directive (and probably a REQUIRE:
TRADE_RESPONSE Directive as well).
ARG1 is either ACCEPT or DECLINE.
The result is
RESULT: TRADE_RESPONSE
|
| WAR_DECLARE Player_1 hostname port weapons vehicles |
This command is used by a player to declare war on
Player_1. The host name and port where
Player_1 is listening are given by hostname and
port. The number of weapons and vehicles that
Player_1 would like to commit to this war are given by
weapons and vehicles. If the host and/or port for
Player_1 are wrong, the player will automatically
loose some percentage of the weapons and vehicles committed, and the war
will not be started (in other words, the player must know where
Player_1 "lives" before declaring war). Finally, note that the
given weapons and vehicles will be tied up in that war until end of the war.
A COMMAND_ERROR will be in the resulting Message Group if
Player_1 cannot be found, or if the player has less
weapons and/or vehicles than were specified. The result is
RESULT: WAR_DECLARE War begun with ARG1
|
| WAR_DEFEND weapons vehicles |
This command is used to declare defending parameters for a war that was
declared via a WAR_DECLARATION: directive. The number
of weapons and vehicles the Player wishes to use in this war is given
by weapons and vehicles.
This command is most probably used in response to a Message Group that
contained the WAR_DECLARATION: Directive (and probably a
REQUIRE: WAR_DECLARATION Directive as well).
The result is
|
| WAR_TRUCE_OFFER Player_1 to Player_2 resource amount ... |
This command is used by the initiator of a trade to ask the Monitor to
confirm a truce, by making a request to the other player. The arguments
to WAR_TRUCE_OFFER are exactly the same as those to the
WAR_TRUCE_OFFERED: directive.
The result is
ACCEPTED, REJECTED,
NOT_ALIVE, or WAR_OVER. If ARG1 is
ACCEPTED, then the Player can expect its wealth to be changed
to reflect the truce terms. A COMMAND_ERROR occurs if a war is
not active between the two parties, an invalid resource name is given, or if
the player who has offered the truce does not have sufficient resources.
Note that weapons and vehicles already tied up in the war cannot be used in
a war trade agreement.
|
| WAR_TRUCE_RESPONSE ARG1 |
This command is used to either accept or reject a truce request. This
command is most probably used in response to a Message Group that contained
the WAR_TRUCE_OFFERED: Directive (and probably a REQUIRE:
WAR_TRUCE_RESPONSE Directive as well).
ARG1 is either ACCEPT or DECLINE.
The result is
|
| WAR_STATUS ARG1 |
This command is used find the status of current or recently fought wars.
ARG1 is the identity of the player against whom the requestor
wishes to see the war status. A COMMAND_ERROR is sent in the
resulting Message Group if the requestor has never been at war
with the requested player. The result is
|
GET_GAME_IDENTS
|
This command is used to get a list of identities of Players in the current
game. This is useful, for example, if you want to know who can be traded
with.
The result is
RESULT: GET_GAME_IDENTS ARG1 ... ARGN
There will be N arguments in the response. Each argument will be the
identity of another Player who is currently in the game.
|
RANDOM_PLAYER_HOST_PORT
|
This command is used to ask the Monitor for a host port of some random
identity. This is useful when a Player wishes to go to war with another
Player, as in these cases, it is necessary to know the host and port of the
Player to be fought. A COMMAND_ERROR occurs if you have
already asked for a random Player host and port recently.
The result is
RESULT: RANDOM_PLAYER_HOST_PORT ARG1 ARG2 ARG3
ARG1 will be the identity of the Player, ARG2 will
be the most recent host on which that Player has been seen, and
ARG3 will be the port on which that Player lives and/or the
host and port where that player was most recently seen.
|
PLAYER_STATUS_CRACK ARG1 ARG2
|
This command is used to make an attempt to get the player status information
of an enemy Player. ARG1 is the identity of the Player whose
status to crack. ARG2 is the number of computer resources to
spend on this attempt. The computer resources will be lost,
regardless of whether or not the crack attempt succeeds. The more
computer resources committed, the greater the chance of success. A
COMMAND_ERROR occurs if the Player issuing the command does not
have at least the amount of computer resources requested. A
COMMAND_ERROR also occurs if the identity is not known.
The result is
RESULT: PLAYER_STATUS_CRACK ARG1 ARG2 STRING
ARG1 will be the identity of the Player whose status was to be
cracked. ARG2 will be either SUCCEEDED or
FAILED. If ARG2 is SUCCEEDED, then
STRING will equivalent to the result string for the
PLAYER_STATUS command, however, the status will of course be
for Player, ARG1.
|
PLAYER_MONITOR_PASSWORD_CRACK ARG1 ARG2
|
This command is used to make an attempt to crack the Monitor password of
another player. ARG1 is the identity of the Player whose
Monitor password to crack. ARG2 is the number of computer
resources to spend on this attempt. The computer resources will be
lost, regardless of whether or not the crack attempt succeeds. The
more computer resources committed, the greater the chance of success. A
COMMAND_ERROR occurs if the Player issuing the command does not
have at least the amount of computer resources requested. A
COMMAND_ERROR also occurs if the identity is not known.
The result is
RESULT: PLAYER_MONITOR_PASSWORD_CRACK ARG1 ARG2 ARG3
ARG1 will be the identity of the Player whose Monitor password
was to be cracked. ARG2 will be either SUCCEEDED
or FAILED. If ARG2 is SUCCEEDED,
then ARG3 will the Monitor password of ARG1.
Otherwise, ARG3 will be the empty string.
|
Synthesis
Players are permitted to synthesize the complex resources. To perform the synthesis, Players must have in the their holdings 2 units of each of the resources needed to build the complex resource. The required raw resources to build complex resources are listed in the table below:
| Complex Resource | Raw Resources Needed |
|---|---|
WEAPONS
|
STEEL, PLASTIC, OIL
|
COMPUTERS
|
COPPER, PLASTIC, GLASS
|
VEHICLES
|
STEEL, RUBBER, GLASS, OIL
|
Logging In to the Monitor for the First Time
When you log into the Monitor for the first time your connection is not encrypted. You may already have a pre-selected participant_name and password (set by the Monitor Administrator), or you may be creating a new participant account if the Monitor provides such a facility (available in practice tournaments). Either way, you should register yourself with the Monitor to maintain some good level of security for subsequent connections.
The IDENT command
The IDENT command initiates login and is almost always the first command you will send to the monitor. No other commands are possible without first identifying yourself to the Monitor. When you first connect to the monitor you will receive a message group similar to the following.
COMMENT: Monitor Server Version 2.9
REQUIRE: IDENT
WAITING:
Upon receiving this, your client should identify itself to the
Monitor. The easiest way to accomplish this is to send the monitor
your participant_name. For example:
IDENT PLAYER
The PASSWORD command
After receiving the IDENT command, the Monitor requests a password from your active client as follows:
RESULT: IDENT PLAYER
REQUIRE: PASSWORD
WAITING:
If you are creating a new player, then this will set the stored
password from this time forward. If you already have a registered
password, then you will need to give that, or else the Monitor will
give you the error message:
COMMAND_ERROR: Invalid player password
Assuming the IDENT command was successful, the client
responds to the WAITING: directive with a command such as
the following:
PASSWORD MYLITTLESECRET
The Monitor takes this password and then generates a special cookie,
which gets returned to the client. The client must save this cookie
(to disk maybe), as it will be needed later to authenticate to the
Monitor again. This is known as the session-key. You will
need to hold onto this key and keep it secret from others. The
monitor will request this key from you in future communications.
The HOST_PORT command
The server will respond to the PASSWORD command with:
RESULT: PASSWORD VKJHJ6FSK3JGM
REQUIRE: HOST_PORT
WAITING:
In this example, the cookie chosen by the Monitor is
VKJHJ6FSK3JGM. It must be remembered! At this stage the
participant's passive server must be started and listening on a port
which it chooses randomly. Suppose the port chosen is 31337 and the
server is started on rhodes.ececs.uc.edu. Then the client
sends the following command to the Monitor:
HOST_PORT RHODES.ECECS.UC.EDU 31337
The monitor may connect to this port from different source ports, and
even (possibly) from different IP addresses. Upon connecting to the
passive server, the Monitor exchanges two pieces of secret
information. This allows a client to authenticate the Monitor, and
the Monitor to authenticate the client. The Monitor takes the SHA-1
hash of the chosen password (not the session-key), first converted to
upper case, and gives that as a base-16 number (converted to a String)
to the passive server. The Monitor also requests from the passive
server the session-key given previously by the result of the
PASSWORD command. The checksum given by the Monitor is
generated by taking the magnitude of the result of
MessageDigest.digest() as the positive magnitude of an integer.
The following shows the communication between the Monitor and the
passive server:
PLAYER_PASSWORD_CHECKSUM: 224726e0160bf4a844f445424f2c81e03cf739cb
COMMENT: Monitor Server Version 2.9
REQUIRE: IDENT
WAITING:
IDENT PLAYER
RESULT: IDENT
REQUIRE: ALIVE
WAITING:
The ALIVE command
The passive server uses the checksum to authenticate the Monitor, then sends the session-key to the Monitor in an ALIVE command as follows:
ALIVE VKJHJ6FSK3JGM
RESULT: ALIVE Identity verified
REQUIRE: QUIT
WAITING
The QUIT command
The passive server responds with the QUIT command. This ends the passive session.
QUIT
This is the end of verification. The Monitor then sends a message
group to the active client telling it whether it succeeded in
authenticating a passive server. This looks as follows:
RESULT: HOST_PORT Command succeeded
REQUIRE: COMMAND
WAITING:
Getting a Certificate (using RMI)
Certificates are obtained using RMI. The Monitor starts a new RMI registry listening on port 1099, and provides the CertRegistry service. The CertRemote interface looks like this:
public interface CertRemote extends Remote {
PlayerCertificate getCert(String ident);
}
The above is available from CertRemote.java. It should be compiled and the resulting class file should be accessible to a client's code. Also, you must be the stub file and put it in your classpath. The stub file is CertRemoteImpl_Stub.class.
The getCert(String) method should be called to grab the certificate of a player. For instance:
CertRemote r =
(CertRemote)(Naming.lookup("rmi://helios.ececs.uc.edu/CertRegistry"));
PlayerCertificate c = r.getCert("FRANCO");
The above stores FRANCO's certificate into object c.
If the invocation of getCert() returns null, it is because the certificate of the player given in the argument does not exist.
Observe that using the string MONITOR as an argument to getCert() returns the Monitor's certificate.
RSA Key Exchange Using IDENT
The client's first IDENT command, which intends to set up encryption, operates differently from all the others. The objective is to use the first IDENT and RESULT: exchange to build a secret secret which is used to encrypt subsequent messages. The following shows what happens on the initial IDENT and RESULT: exchange. It is assumed that the Monitor's certificate has been obtained according to the previous section and resides in PlayerCertificate montCert.
After the participant starts the active client to make the first connection, the following message group is received:
COMMENT: Monitor Server Version 2.9
REQUIRE: IDENT
WAITING:
The client uses the following code to read the Monitor's certificate, make an RSA public-private key pair, and encrypt the modulus of the RSA public key with the public key of the Monitor:
RSA myKey = new RSA(256);
BigInteger m = myKey.publicKey().getModulus();
String myHalf = monCert.getPublicKey().encrypt(m).toString(32);
The PlayerCertificate class is provided here. The RSA class is provided here. The result (myHalf) is sent to the Monitor as the last argument of an IDENT command as exemplified by the following:
IDENT FRANCO 87jkg543gffd8673gdh
The Monitor responds with a message group such as the following:
RESULT: IDENT g5jh673gfu2468673gdh
WAITING:
which contains a 256 bit number that was generated by the Monitor and RSA
encrypted using the modulus of the public key that was sent over in the
previous command and the exponent of 65537 (official project exponent).
Call the String representing the number that is parsed from the
RESULT: directive, srv. The client decrypts
srv and creates the shared secret using the following:
String number = srv.split("\\s+")[2];
BigInteger srvHalf = myKey.decryptNum(new BigInteger(number, 32));
byte myHalf[] = myKey.publicKey().getModulus().toByteArray();
byte monHalf[] = srvHalf.toByteArray();
Assembling the secret key for Karn encryption
The following code is used to construct the shared secret:
int keySize = 512;
ByteArrayOutputStream bos = new ByteArrayOutputStream(keySize/8);
for (int i=0; i < keySize/16; i++) {
bos.write(monHalf[i]);
bos.write(myHalf[i]);
}
BigInteger sharedSecret = new BigInteger(1, bos.toByteArray());
The monHalf and myHalf arrays must be used in the order
shown or else the resulting secret will not match that of the Monitor.
Once the shared secret is established, encrypted communication can
proceed using the following:
KarnBufferedReader karnIn = new KarnBufferedReader(in, sharedSecret);
KarnPrintWriter karnOut = new KarnPrintWriter(out, true, sharedSecret);
where in and out represent the streams used in the
connection to the Monitor. The KarnBufferedReader class can
be obtained here and the
KarnPrintWriter class can be obtained here. Now one can do
karnIn.readLine() to decrypt incoming messages, line-by-line,
and karnOut.println(..) to encrypt.
Registering a Certificate
The MAKE_CERTIFICATE command is used to register a public encryption key for use in RSA encryption facilities with the Monitor. The MAKE_CERTIFICATE command takes two arguments: the public encryption key exponenet (commonly called e) followed by the public key modulus (commonly called n). The monitor does not perform any sort of checks to verify that the numbers are cryptographically strong. Such is the responsibility of the key generation method used. Each number is expected to be in base-32 numeric format (BigInteger.toString(32)), and should be a positive number. The Monitor responds with a new number which is the monitor-signed hash of the supplied key. This number is used to authenticate the Monitor. Here is an example.
The client uses the following code to build an RSA private key:
RSA myPrivateKey = new RSA(); /* Default is 512-bit key size */
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("ParticipantKey"));
oos.writeObject(myPrivateKey);
oos.close();
Now the MAKE_CERTIFICATE command is invoked (note: replace
karnOut below with out or something appropriate if you
have not started to encrypt yet):
karnOut.println("MAKE_CERTIFICATE " +
myPrivateKey.publicKey().getExponent().toString(32) +
" " + myPrivateKey.publicKey().getModulus().toString(32));
The response of the Monitor should be something like this:
RESULT: CERTIFICATE 02m21jhbb23jjm23jj23bbh32h
WAITING:
This is only an example - the values above are not actually generated values.
The Monitor handles the MAKE_CERTIFICATE command using the following code:
MessageDigest mdsha = MessageDigest.getInstance("SHA-1");
mdsha.update(myPublicKey.getExponent().toByteArray());
mdsha.update(myPublicKey.getModulus().toByteArray());
BigInteger m = new BigInteger(1, mdsha.digest());
String certNumber = monitorPrivateKey.signNum(m).toString(32);
where monitorPrivateKey is of type RSA
and myPublicKey, of type PubRSA,
is what was sent over previously from the client. This has the effect of
creating a value which facilitates verification that the Monitor has
registered the certificate, provided one has the Monitor's public key. A
second client can use the encrypt function of the Monitor's public key to
compare the hash with what has been received from a first client. As long
as the second client can trust the Monitor's public key, the identity is
considered authenticated.
Assuming encryption is enabled, the client checks the hash with the following:
String s[] = karnIn.readLine().split("\\s+");
MessageDigest mdsha = MessageDigest.getInstance("SHA-1");
mdsha.update(myPublicKey.getExponent().toByteArray());
mdsha.update(myPublicKey.getModulus().toByteArray());
BigInteger m = new BigInteger(1, mdsha.digest());
BigInteger p = new BigInteger(s[2], 32);
BigInteger certNumber = monitorPublicKey.encrypt(p);
if (m.compareTo(certNumber) == 0) { "got it!" } else { "don't got it" }
Authenticated Connection Encryption
In order to authenticate the client end of a connection, the Monitor must have some information to prove that the client who he says he is. This is provided by the challenge which operates as follows:
The specific sequence of actions is as follows. From the Monitor comes:
COMMENT: Monitor Server Version 2.9
REQUIRE: IDENT
WAITING:
The client generates a random 256-bit value to be encrypted by the
Monitor's public RSA key using the following code:
SecureRandom sr = new SecureRandom();
BigInteger participantHalf = new BigInteger(256, sr);
out.println("IDENT " + playerName + " " +
monCert.getPublicKey().encrypt(participantHalf).toString(32));
where monCert and karnOut are as above.
The Monitor responds with the same directive but different meaning.
RESULT: IDENT dsafsdf7daf6d7fsda5dsa5fds4fsd5f4sd
Assuming encryption is enabled, the client breaks this up:
String s[] = karnIn.readLine().split("\\s+");
where karnIn is as above. Next, the client creates the Monitor
key half using the previously generated (and stored) private RSA key
which the client created.
BigInteger monitorHalf = new BigInteger(myPrivateKey.decrypt(s[2]), 32);
byte myHalf[] = participantHalf.toByteArray();
byte monHalf[] = monitorHalf.toByteArray();
From this point on, the halves get reassembled and communication proceeds
exactly as it does in the Unauthenticated connection.
It is important to know that after a participant registers a new key with the
Monitor, via MAKE_CERTIFICATE, there are no longer any Unauthenticated
Encrypted connections allowed. The presence of a player certificate tells
the monitor to use that certificate's public key to encrypt the value sent
back to the client. This corresponds to challenge steps #3 and #4 above.
By this point you should have a completely operational client that is up and running and receiving encrypted connections from the monitor. This typically puts the connection into command mode.
The Command Mode
Once authentication and login have completed, the active client is in Command Mode. At this stage, the participant is at the root of the command interface and has the freedom to commit any sort of actions that the Monitor accepts. There are numerous commands available to the participant, and they are covered later on. This section will focus on a few commands that are useful to the neophyte.
The HELP command
Very important to the novice participant is the HELP command. This command provides a list of all of the commands that can br accepted by the Monitor.