Hangmango is a hangman game that operates over tcp sockets. It features a robust server that accepts multiple concurrent connections and a client that implements a basic command line interface.
Building a working Hangmango client and server requires execution of go build
for both the client and server directories.
With the GOPATH variable set as /home/yourusername/go
, there are two options to fetch the source prior to building:
-
go get -v -u github.com/tgmars/hangmango
NOTE: this will require settingHTTPS_PROXY
proxy envar if behind a corporate proxy.OR
-
Extract the .zip into your $GOPATH, this will create the required folders and extract files in the correct structure.
Following either of the options above, the following structure should be present within your $GOPATH.
user@host:~/go/src/github.com/tgmars/hangmango$
.
├── app
│ ├── client
│ │ └── client.go
│ ├── server
│ │ ├── hangman.go
│ │ └── server.go
│ └── wordlist.txt
├── readme.md
├── startClient.sh
└── startServer.sh
From ~/go/src/github.com/tgmars/hangmango
, execute the following commands to build hangmanclient and hangmanserver into the intended locations. startClient.sh
and startServer.sh
require these locations to execute.
cd app/client/;go build -o ../hangmanclient;cd ../..;
cd app/server/;go build -o ../hangmanserver;cd ../..;
You are now ready to run Hangmango!
Hangmango has two binaries for execution, hangmanserver
and hangmanclient
, these will parse command line arguments with help text. For simple usage, startClient.sh
and startServer.sh
are both scripts that will execute the binaries, taking an IP address and port number as positional arguments.
startServer.sh int_port_to_listen_on
startClient.sh server_ip_address server_port_to_connect_to
Usage of ../hangmanserver:
-lport int
Port to listen for incoming connections on. (default 4444)
-wordlist string
Path to a newline separated list of words to use as a valid set of answers in a hangman game. (optional)
Usage of ../hangmanclient:
-dhost string
Hangmango server IPv4 address to connect to. (default "127.0.0.1")
-dport int
Port that the target Hangmango server is listening on. (default 4444)
The following section describes the wordlist feature and considerations applied in regards to security and architecture of the client-server model.
A hardcoded list of default words to be selected from for a game of Hangmango includes apple hello laminate sorcerer willow
, to expand this list the contents of the included wordlist.txt
should be edited. It must contain newline separated words. The default contents of wordlist.txt
is here these are extra words for hangman tangible tarantula fantastic
.
Whilst there is no protection against MiTM attacks until encryption is implemented, data validation has been considered in the development of both the client and server. Messages must match regex identifiers, messages greater than specified buffers (at the server) result in errors that are handled gracefully, and information of server operation is logged and verbosely presented to STDOUT. Encryption is a work in progress and is documented under the Encryption header below.
Hangmango uses a ClientManager struct and Golang's concept of channels to 'register' and 'unregister' clients from the server. A channel allows us to manipulate data in a concurrency safe manner within Goroutines. Upon receipt of a valid START GAME
message, a new client object is created. Within that client object, a hangman game state is created and associated with the current connection.
Both client and server initate their send() and receive() functions as Goroutines. Within each of these Goroutines, data that is transferred over sockets is directed to the data channel for each client. Data that conforms to the required length is then read off of the data channel for further processing per the hangman protocol. Running these functions as Goroutines enables us to scale out for concurrent client connections with ease.
The code responsible for implementing the rules of the hangman game are stored in hangman.go
. A new hangman game is created for each valid incoming connection. New games select words from the list, seeded with the current time.
The hangmango protocol is per the specifications of UNE's COSC540 assessment 2. Namely:
Each message in the protocol consists of a string of ASCII characters followed by the line feed character (ASCII code 10). Other than the START GAME
message, all messages from the client should be case insensitive. Other than the GAME OVER
message, all messages from the server should be lowercase.
The game begins with the client sending a START GAME
message to the server.
The server responds to the START GAME
message with the first hint. From this point on, any single character message consisting of a letter from A-Z or a-z from the client is treated as a letter guess, and any message consisting of more than one letter from A-Z or a-z is treated as a word guess.
After a letter guess, the server responds with a hint if there are still letters in the secret word that have not yet been guessed.
After a word guess, the server responds with a hint if the guess was different from the secret word.
Once the client has guessed the secret word (by either sending a correct word guess or guessing each letter in the secret word), the server sends the client's score followed by a GAME OVER
message and then ends the connection.
Any other message sent to or from the client is considered an error, and should result in the receiving party dropping the connection. In particular, any client guess that includes characters outside the range of A-Z or a-z must be considered an error by the server.
Prior to operating the layer 7 hangman protocol, we establish an encrypted session betweent the client and server.
- Client is bundled with a public key certificate used for verifying messages sent from the server.
- Client retrieves the public key from the specified Hangmango server, by sending a
PUBKEYREQ
message to the server, along with its own public key. The server responds with it's RSA public key. - Server stores the public key of the client in memoey.
- Server sends a
PUBKEYRESP
that includes its own public key to the Hangmango client. - Client and server progress to play game over the hangman protocol.
- Following a PUBKEYREQ message while the client is already running, the client should make their serverPubKey = (rsa.PublicKey{})
NOTE: Some client and server side validation on data received over sockets will need modifying to account for increased data sizes due to encryption and transmission of public keys.
We do not support Perfect Forward Secrecy (PFS), a symmetric private key is exchanged between client and server following public key based encryption being established. Different keypairs are used for signing and encryption. We do assume that a CA would verify the certificate held by the client. Certificate held by the client needs to be a chain including the CA?
Encryption - Encryption will increase the cost for an attacker for conduct a MitM attack on Hangmango communicates. Public key encryption has been chosen as it scales well in terms of cost of implementation and security. Without a verification of the public key by a CA, and checks that valid certificates are used, the server could be impersonated and the key exchange intercepted, allowing for an attacker masquerade as a valid server.
Hashing - A hash calculated using details of the source, destination and contents of the message is calculated and sent along with the message. If the message has been modified in transit, or a client or servers address has been manipulated, the computed hash at the destination of the message will not match and be discarded. A failed hash message requires a modification to the protocol to pass a retry message back to the sender. Hashing will assure the integrity of the message.
Signing - The encrypted messages must be signed to ensure their authenticity.
Sequencing - A message sequence could be implemented to ensure that clients only receive the message currently intended for them. Using a nonce in the AEAD GCM encryption means that replay attacks won't be effective; nor can sending two
Verified authenticity of the key exchange Because two keypairs are used for encryption and signing, it's important to verify that when a hangmango client requests a new key from a server, that they're interacting with a server that can demonstrate itself as an authenticate hangmango server, trusted to distribute a hangmango public key for encrypted key exchanges.
Authenticated encryption AEAD encryption in GCM mode (with nonce) is used following a verified key exchange. The nonce is sent along with each encryptedMessage{} struct as the B field to prevent replay attacks on the protocol.
Encrypt private.pem on disk
Strict sequence numbers to ensure message order is maintained
From - (https://en.wikipedia.org/wiki/Authenticated_encryption) Security guarantees
In addition to protecting message integrity and confidentiality, authenticated encryption can provide security against chosen ciphertext attack. In these attacks, an adversary attempts to gain an advantage against a cryptosystem (e.g., information about the secret decryption key) by submitting carefully chosen ciphertexts to some "decryption oracle" and analyzing the decrypted results. Authenticated encryption schemes can recognize improperly-constructed ciphertexts and refuse to decrypt them. This, in turn, prevents the attacker from requesting the decryption of any ciphertext unless it was generated correctly using the encryption algorithm, thus implying that the plaintext is already known. Implemented correctly, authenticated encryption removes the usefulness of the decryption oracle, by preventing an attacker from gaining useful information that the attacker does not already possess.
Many specialized authenticated encryption modes have been developed for use with symmetric block ciphers. However, authenticated encryption can be generically constructed by combining an encryption scheme and a message authentication code (MAC), provided that:
The encryption scheme is semantically secure under a chosen plaintext attack. The MAC function is unforgeable under a chosen message attack.