Mutual Authentication for NSTP

Submission Deadline:

The goals of this assignment are to:

  1. Implement mutual authentication for NSTP clients and servers
  2. Understand trust relationships between participants in a PKI
  3. Learn how to detect and mitigate attacks against PKIs

NSTP Mutual Authentication

In this assignment, you will add mutual authentication to NSTP via a simple public key infrastructure. Version 4 of NSTP adds no new phases over NSTPv3. However, ClientHello and ServerHello messages now carry certificates instead of public keys. These certificates can be used in conjunction with a local certificate authority database and online certificate status servers to verify the identity of servers and, optionally, clients.

NSTPv4 protobufs are available here.


NSTP certificates are conceptually a subset of X.509 certificates. As such, they carry fields such as the subject, issuer, validity period, etc., as shown in the protobufs below.

message CertificateStore {
    repeated Certificate certificates = 1;

message Certificate {
    repeated string subjects = 1;
    uint64 valid_from = 2;
    uint32 valid_length = 3;
    repeated CertificateUsage usages = 4;
    bytes encryption_public_key = 5;
    bytes signing_public_key = 6;
    CertificateHash issuer = 7;
    bytes issuer_signature = 8;

enum CertificateUsage {

message CertificateHash {
    bytes value = 1;
    HashAlgorithm algorithm = 2;

message PrivateKey {
    CertificateHash certificate = 1;
    bytes encryption_private_key = 2;
    bytes signing_private_key = 3;

Certificate validation mirrors the standard certificate path validation algorithm for TLS. That is, for each certificate in a validation chain:

  • Certificate subjects must match expected values (see below)
  • Certificate issuers must refer to a trusted certificate, or be self-signed if it belongs to a trust root
  • Certificates must be valid at the time of verification
  • Certificates must be labeled with an appropriate usage flag
  • Certificate signatures must pass verification against the corresponding public key
  • Certificates must be labeled as valid by a status server

Certificate subject interpretation varies depending on whether a certificate is used for client or server authentication. Client certificates must contain a single subject to be interpreted as a username. Usernames that appear in valid client certificates need not be present in any other user database. However, server certificates may carry multiple subjects where one subject must be either a valid DNS name or IPv{4,6} address for the server.

Signatures are computed over each field of a certificate in the order they appear in the Certificate protobuf message, except for the trailing signature field itself. The signature algorithm used here is Ed25519 as implemented by libsodium.

Certificate Status Servers

Status servers are UDP servers that are responsible for indicating whether certificates are valid, have been revoked, or are unknown to the server. A valid status is required for a certificate to pass validation. A principal may “staple” a certificate status response to a certificate; otherwise, the counterparty must query a status server. The relevant protobufs are shown below.

message CertificateStatusRequest {
    CertificateHash certificate = 1;

message CertificateStatusResponse {
    CertificateHash certificate = 1;
    CertificateStatus status = 2;
    uint64 valid_from = 3;
    uint32 valid_length = 4;
    Certificate status_certificate = 5;
    bytes status_signature = 6;

enum CertificateStatus {
    UNKNOWN = 0;
    VALID = 1;
    REVOKED = 2;

Certificate status requests and responses use certificate hashes to uniquely identify a certificate. Certificate hashes are computed over each field of a certificate in the order they appear in the Certificate protobuf message using either SHA-256 or SHA-512, but not the “identity” hash.

Regardless of whether a certificate status response has been stapled or obtained directly, it must be validated similarly to a certificate. One additional validation constraint applies, however, in that the certificate and status server certificate must share a common trust root.

Certificate Pinning

As a supplement to certificate path validation, certificates may be “pinned.” That is, NSTP clients and servers may be provided a set of subject and certificate hash tuples that can be used to check for fraudulent certificates. Relevant protobufs are shown below.

message PinnedCertificateStore {
    repeated PinnedCertificate pinned_certificates = 1;

message PinnedCertificate {
    string subject = 1;
    CertificateHash certificate = 2;

If a presented certificate does not match a pinned certificate, then it should be rejected regardless of whether it passes validation or not.

Protocol Changes

NSTP phase transitions have been modified to accommodate authentication using a client certificate. That is, if a client successfully authenticates to a server using a certificate, then it must skip the password-based authentication phase and proceed directly to the established phase. On the other hand, if a presented client certificate does not validate, then the server must terminate the connection (optionally with an error message).

If a client does not present a certificate, then it must proceed to the password-based authentication phase as in NSTPv3.

ClientHello and ServerHello have been modified to support certificate-based authentication and optional status stapling, as shown below.

message ClientHello {
    uint32 major_version = 1;
    uint32 minor_version = 2;
    string user_agent = 3;
    bytes public_key = 4;
    Certificate certificate = 5;
    CertificateStatusResponse certificate_status = 6;

message ServerHello {
    uint32 major_version = 1;
    uint32 minor_version = 2;
    string user_agent = 3;
    Certificate certificate = 4;
    CertificateStatusResponse certificate_status = 5;

Evaluation Criteria

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

An image containing a partial test suite can be pulled from This image should be run with a path to a YAML configuration file with the format shown below.

# NSTP server configuration
  ipv4_address: ""     # IPv4 address of your NSTP server
  ipv6_address: "::0"           # Or, an IPv6 address
  port: 22300                   # NSTP server port

# Status server configuration
  ipv4_address: ""     # IPv4 address of the status server
  ipv6_address: "::0"           # Or, an IPv6 address
  port: 22301                   # Status server port

# Path to a `CertificateStore`
trusted_certificate_store: "/data/trusted_certs.db"

# Path to a `PinnedCertificateStore`
pinned_certificate_store: "/data/pinned_certs.db"

# Path to the client `Certificate`
client_certificate: "/data/client.crt"

# Path to the client `PrivateKey`
client_private_key: "/data/client.key"

# Or, valid user credentials
  name: "root"
  password: "letmein"

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 server configuration file as so:

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

The server configuration itself must use the following format:

# NSTP server configuration
  port: 22300                   # NSTP server port

# Status server configuration
  ipv4_address: ""   # IPv4 address of the status server
  ipv6_address: "..."           # Or, an IPv6 address
  port: 22301                   # Status server port

# Path to a `CertificateStore`
trusted_certificate_store: "/data/trusted_certs.db"

# Path to a `PinnedCertificateStore`
pinned_certificate_store: "/data/pinned_certs.db"

# Path to the server `Certificate`
server_certificate: "/data/server.crt"

# Path to the server `PrivateKey`
server_private_key: "/data/server.key"

# Valid user credentials
  - name: "root"
    password_hash: "$argon2id$v=19$m=102400,t=2,p=8$KaUUwhiDsFaqFYLwXuu91w$mBQqnhGcQpOn04EpsxodDQ"
  - name: "little_mac"
    password_hash: "$argon2id$v=19$m=102400,t=2,p=8$hhACIKS0ltLa23svBYAwRg$bOzZyOZDwtESuivuYLuAxg"

Submission Instructions

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

# On the project server
$ mkdir -p ~/assignments/03-mutual_authentication
$ cd ~/assignments/03-mutual_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.