Authenticating NSTP Clients

Submission Deadline:

The goals of this assignment are to:

  1. Implement client authentication and data confidentiality for a network protocol
  2. Understand how to securely transmit, store, and verify passwords
  3. Learn how to defend against online password guessing attacks

NSTP Client Authentication

In this assignment, you will be augmenting NSTP to support client authentication. NSTP clients will now be required to present authentication credentials to the server before they can load or store data. In order to protect credentials in transit from passive network attackers, you will need to add session key derivation and message encryption to the protocol as well.

Version 3 of NSTP adds an additional phase over NSTPv2. Previously, there was an initialization phase where ClientHello and ServerHello messages were exchanged prior to entering the established phase where ping, load, and store requests could be issued by clients. Now, there are three protocol phases:

  1. Initialization. Clients and servers exchange messages containing versioning information as well as public keys for session key negotiation. This phase necessarily takes place in the clear.
  2. Authentication. Using session keys negotiated in the initialization phase, clients present authentication credentials over an symmetrically encrypted channel for verification at the server.
  3. Established. Clients that successfully authenticate can issue ping, load, and store requests over the encrypted channel.

NSTPv3 protobufs are available here.

Data Confidentiality

In order to securely transmit authentication credentials, NSTPv3 modifies the structure of several protobuf messages from the prior version. In particular, NSTPMessages are modified to support ClientHello, ServerHello, ErrorMessage, or EncryptedMessage payloads. The initialization messages hold an additional field to transmit public key material for the server and client. These keys are used to derive symmetric session keys. Error messages are also supported for cleartext transmission. All other messages must be transmitted as an EncryptedMessage payload which consists of a ciphertext produced using the negotiated session keys as well as a cleartext nonce. EncryptedMessage ciphertexts are expected to decrypt to a DecryptedMessage protobuf. An overview of these protobuf messages is shown below.

message NSTPMessage {
    oneof message_ {
        ClientHello client_hello = 1;
        ServerHello server_hello = 2;
        ErrorMessage error_message = 3;
        EncryptedMessage encrypted_message = 4;

message ClientHello {
    uint32 major_version = 1;
    uint32 minor_version = 2;
    string user_agent = 3;
    bytes public_key = 4;

message ServerHello {
    uint32 major_version = 1;
    uint32 minor_version = 2;
    string user_agent = 3;
    bytes public_key = 4;

message EncryptedMessage {
    bytes ciphertext = 1;
    bytes nonce = 2;

message DecryptedMessage {
    oneof message_ {
        ErrorMessage error_message = 1;
        PingRequest ping_request = 2;
        PingResponse ping_response = 3;
        LoadRequest load_request = 4;
        LoadResponse load_response = 5;
        StoreRequest store_request = 6;
        StoreResponse store_response = 7;
        AuthenticationRequest auth_request = 8;
        AuthenticationResponse auth_response = 9;

You will use libsodium to perform key negotiation and to symmetrically encrypt messages. Bindings to libsodium exist for a number of popular languages.

Password Verification

Client authentication is performed using usernames and passwords. After session keys have been derived, clients submit an AuthenticationRequest message carrying their credentials. Servers must check these credentials against a database of authorized users and respond with an AuthenticationResponse message indicating the result. Alternatively, an ErrorMessage can be sent prior to disconnecting a client on a terminal failure.

message AuthenticationRequest {
    string username = 1;
    string password = 2;

message AuthenticationResponse {
    bool authenticated = 1;

The user database is a text file where each line consists of a username and password hash pair separated by a colon. Password hashes are given in modular crypt format. You must support MD5, SHA256, SHA512, and Argon2id. For example:


Servers must allow for multiple authentication requests within the same session until one succeeds. However, servers must also implement rate-limiting heuristics to prevent successful online password guessing attacks, and must disconnect clients that fail to authenticate under a reasonable threshold of attempts.

Access Control Policy

NSTPv3 servers must now label values with the username that stored them as well as a label indicating whether the value is publicly-accessible or not. If a value is not public, then that value must exist in a user-specific namespace such that it is only readable or writable by the owning user. The public label on a value can be set in an updated StoreRequest message as shown here.

message StoreRequest {
    string key = 1;
    bytes value = 2;
    bool public = 3;

Evaluation Criteria

Your server will be evaluated on its adherence to the NSTPv3 protocol and associated requirements as outlined above.

An image containing a partial test suite can be pulled from Running this image with a server address, port, username, and password as arguments will execute the test suite as a network client accessing your server. The test suite will report whether any violations of the protocol or other requirements were detected.

Your submission must contain a Dockerfile in the repository root that builds a container for your server when the following sequence of commands is performed:

$ git clone ${your_repo_url} submission
$ cd submission
$ docker build -t submission .

Your server should be invoked by default when running the resulting container image (use the ENTRYPOINT Dockerfile directive). Grading of your solution will take place using an automated test suite that will expect your server to use a provided user database as so:

$ docker run -d -v ${shared_dir}:${shared_dir} submission ${shared_dir}/${user_database}

Submission Instructions

Push the source code for your server to a git repository on the project server at ~/assignments/02-client_authentication.

# On the project server
$ mkdir -p ~/assignments/02-client_authentication
$ cd ~/assignments/02-client_authentication
$ git init --bare

# On your development machine
$ cd ${repo_path}
$ git remote add submission ${user}
$ git push -u submission --all
$ git push -u submission --tags

You may tag the commit you wish to be considered for grading with the tag solution; otherwise, the latest commit on master will be treated as such.