Skip to content
FlauschBert edited this page Dec 7, 2018 · 22 revisions

Scored - a minimalist and safe high score server

Motivation

So what is this all about? Did you ever want to safely manage high scores (or similar information) of a game for all users? On the one hand saving the score and on the other hand showing the score to everyone. So this is it.

Used cryptographic library and stuff

The idea came to me while is was thinking about high scores for my little games (still in development) and when I stumbled upon TweetNaCl: https://tweetnacl.cr.yp.to/index.html. It is a minimalist and safe cryptographic library and delivers all needed functionality for this little project. The other thing was that I wanted to use the programming language Rust. The language is known to ensure that the compiled program is free of data races. Good for the safety of the server.

Communication between client and server

All communication between the two is done via TCP/IP. The information (commands) has to be sent symmetrically encrypted with a secret session key negotiated with Diffie-Hellmann key exchange which is also part of the TweetNaCl library.

Malformed commands

If a malformed command is sent to the server the error is returned:
STAT10|666.

Client authentication

User and password

Initial authentication and token generation

Before any high score information can be sent to the server the client has to authenticate with a unique user and password.
This has to be done with the AUTH10 command followed by username and hashed password:
AUTH10|u8:<512 bit hash of password>|u8<length in bytes>|u8:<username>.

The answer is a unique token generated out of user, password and random information:
TOKN10|u8:<512 bit hash of token>.
The token can be generated only once for a given user. If the user sends the same username and password hash combination as before the token once generated is returned again.
If the same user tries to generate a token again with another password hash the command is rejected with the error:
STAT10|003.

For all later operations the token is used. The token should be saved by the client and used for all operations.

A token can be created (initially or by removal and recreation) or retrieved only all 15 minutes by the client to avoid flooding the database with new entries and to avoid hitting a used password with brute force checks. The client is identified by the ip address (peer). If the timeout is underrun the error is returned:
Error timeout underrun: STAT10|002.

The server allows only a certain command queue depth at once to avoid flooding the memory. If the command queue depth is too deep the stream is simply dropped instantly. It would be the same as if the connection gets broken.

Changing the password

If the client wants to change the password of a user the command RETH10 has to be used:
RETH10|u8:<512 bit hash of token>|u8:<512 bit hash of new password>.
The answer is a new unique token generated and returned as above in case of success.
In case of an error the status is returned:
Error not found: STAT10|001.

Removal of user and all connected information

If the client wants to remove a user it has to send the REMV10 command:
REMV10|u8:<512 bit hash of token>.
In this case all information connected to the user is dropped too.

The answer is a status command STAT10:
Success: STAT10|000.
Error not found: STAT10|001.

Data handling on the server

The server saves both username and password hash in a user table along with the generated token as primary key. Additionally ip address and last authentication timestamp is saved per token.

Clone this wiki locally