Bonafide

Secure user registration, authentication, and provider discovery.

Introduction

Bonafide is a protocol that allows a user agent to communicate with a service provider. It includes the following capabilities:

  • Discover basic information about a provider.
  • Register a new account with a provider.
  • Discover information about all the services offered by a provider.
  • Authenticate with a provider.
  • Destroy a user account.

Bonafide uses SRP (Secure Remote Password) for password-based authentication.

Definitions

DOMAIN is the primary domain of the provider. This is specified by the user when they enter which provider they want to sign up with.

API_BASE is the root URL of all the API calls. This is constructed from api_uri/api_version, where api_url and api_version are values found in the provider.json file.

ca.crt is used to denote the provider’s self-signed Certificate Authority certificate, which is used to sign all of the provider’s server certificates, except for DOMAIN which uses a commercial Certificate Authority.

ca_cert_uri is the special URL that specifies where to download the provider’s CA certificate (ca.crt). The value for ca_cert_uri is found in provider.json.

Configuration Files

Send If-Modified-Since header: All HTTP requests for configuration JSON files should set a correct “If-Modified-Since” header. In reply, the server may send a “304 Not Modified” response if the client has the most up to date version.

JSON files

Character encoding: JSON files are always in UTF8. When loaded in the browser, they are not displayed in UTF8, so non-ascii characters look off, but the files are correct.

GET DOMAIN/provider.json

The provider.json file includes basic information about a provider. The URL for @provider.json is always the same for all providers (`http://DOMAIN/provider.json`). This is the basic ‘bootstrap’ file that informs the user agent what URLs to use for the other actions.

This request MUST be made no more than ONCE. All subsequent requests to update provider.json must be made to GET API_BASE/provider.json.

Here is an example provider.json (from https://demo.bitmask.net/provider.json):

{
  "api_uri": "api.demo.bitmask.net:4430",
  "api_version": "1",
  "ca_cert_fingerprint": "SHA256: 0f17c033115f6b76ff67871872303ff65034efe7dd1b910062ca323eb4da5c7e",
  "ca_cert_uri": "demo.bitmask.net/ca.crt",
  "default_language": "en",
  "description": {
    "en": "A demonstration provider."
  },
  "domain": "demo.bitmask.net",
  "enrollment_policy": "open",
  "languages": [
    "en"
  ],
  "name": {
    "en": "Bitmask"
  },
  "services": [
    "openvpn"
  ]
}

GET API_BASE/provider.json

Used for checking for updates to provider.json after the initial bootstrap provider.json has been loaded from GET DOMAIN/provider.json.

GET API_BASE/configs.json

A request to configs.json lists the available configuration files. The available service codes are listed in “services” in provider.json. A provider can use whatever service codes they want, but the user agent will only respond to the ones that it understands.

For example:

{
  "services":{
    "soledad":"/1/configs/soledad-service.json",
    "eip":"/1/configs/eip-service.json",
    "smtp":"/1/configs/smtp-service.json"
  }
}

GET API_BASE/config/eip-service.json

e.g. https://api.bitmask.net:4430/1/config/eip-service.json

This file defines the “encrypted internet proxy” capabilities and gateways. The actual URL that should be used to fetch this is specified in the response from /1/configs.json.

Provider Keys

GET $ca_cert_uri

e.g. https://demo.bitmask.net/ca.crt

The value for ca_cert_uri is contained in provider.json.

This request returns the file ca.crt, the provider’s self-signed CA certificate.

If ca_cert_uri specifies an HTTPS connection, the client must allow TLS connections even if the authenticity of the server certificate cannot be established. This is the only request where the authenticity of the TLS certificate can (and should) be ignored. Instead, after this file is downloaded, it’s fingerprint MUST be checked against the value ca_cert_fingerprint in provider.json.

REST API

Version

The API_BASE for the webapp API is constructed from api_uri and api_version from provider.json.

For example, given this in provider.json:

{
  "api_uri": "api.bitmask.net:4430",
  "api_version": "1",
}

The API_BASE would be https://api.bitmask.net:4430/1

The API_VERSION will increment if breaking changes to the api are made. The API might be enhanced without incrementing the version. For Version 1 this may include sending additional data in json responses.

Session

Handshake

The handshake starts the authentication process between a client and a provider. In the following example, values A and B are part of the two-step SRP authentication process.

POST API_BASE/sessions(.json)
Query params: {"A": "12…345", "login": "swq055"}
Response: 200 {"B": "17…651", "salt": "A13CDE"}

If the query_params leaves out the A parameter, then no B parameter will be included in the response and only the salt for the given login will be sent in the reply.

POST API_BASE/sessions(.json)
Query params: {"login": "swq055"}
Response: 200 {"salt": "A13CDE"}

Authenticate

Finishes the authentication handshake, after which the user will be successfully authenticated assuming there were no errors in the process. This needs to be run after the Handshake.

PUT API_BASE/sessions/:login(.json)
Query params: {"client_auth": "123…45", "A": "12…345"}
Response: 200 {"M2": "A123BC", "id": "234863", "token": "Aenfw893-zh"}
Error Response: 500 {"field":"password","error":"wrong password"}

Variables:

  • A: same as A param from the first Handshake request (POST).
  • client_auth: SRP authentication value M, calculated by client.
  • M2: Server response for SRP.
  • id: User id for updating user record
  • token: Unique identifier used to authenticate the user (until the session expires).

Token Authentication

Tokens returned by the authentication request are used to authenticate further requests to the API, and are stored as a hash in the Couch database.

Soledad (https://leap.se/en/docs/design/soledad) directly queries the couch database to ensure the authentication of a user. It compares a hash of the token to the one stored in the database. Hashing prevents timing attacks.

Logout

Destroy the current session and invalidate the token. Requires authentication.

DELETE API_BASE/logout(.json)
Query params: {"login": "swq055"}
Response: 204 NO CONTENT

User Certificates

Get a VPN client certificate

The client certificate will be a “free” certificate unless the requesting client is authenticated. This certificate includes no identifying information that associates it with the user. Depending on the service level of the user, the certificate may have a common name that indicates that the certificate is valid for rate limited or unlimited bandwidth.

POST API_BASE/cert
Response: 200 PEM ENCODED CERT

The response also includes the corresponding private key.

Get an SMTP client certificate

The client certificate will include the user’s email address and the fingerprint will be stored with the identity of the user as well as the date it was created. This is so that a provider can shut down a user’s account if it is sending large amounts of spam. Authentication is required in order to receive an SMTP client certificate.

POST API_BASE/smtp_cert
Response: 200 PEM ENCODED CERT

The response also includes the corresponding private key, PEM encoded.

Users

Signup

Create a new user.

POST API_BASE/users(.json)
Query params: {"user[password_salt]": "5A...21", "user[password_verifier]": "12...45", "user[login]": "that_s_me"}
Response: 200 {"password_salt":"5A...21","login":"that_s_me"}

Update user record

Update information about the user. Requires authentication.

PUT API_BASE/users/:uid(.json)
Query params: {"user[param1]": "value1", "user[param2]": "value2" }
Response: 204 NO CONTENT

Possible parameters to update:

  • login (requires password_verifier)
  • password_verifier combined with salt
  • public_key

Rules

One-time bootstrap: The file DOMAIN/provider.json should be requested at most ONCE. If a provider.json file already exists in the code base (a “pre-seeded” provider), then this bootstrap request MUST NOT be made, ever.

Updating provider.json: The client should check for updates to provider.json on a regular basis, but no more than once per day. This regular update check MUST be performed using the api_uri contained in the current provider.json, but never using the DOMAIN/provider.json URL that is used only for the one-time bootstrap.

Updating ca.crt: The stored self-signed CA certificate ca.crt MUST be redownloaded when, and ONLY when, the fingerprint value ca_cert_fingerprint in provider.json changes.

Updating service configs: All service configs should be checked for updates on a regular basis, but no more than once per day.

TLS Certificate Validation: There are two types of TLS connections for Bonafide:

  1. DOMAIN connections: These connections must be over TLS and the server’s TLS certificate must be validated using an established Certificate Authority. The primary example is the one-time bootstrap request to DOMAIN/provider.json. It is also possible that the ca_cert_uri may include DOMAIN.
  2. API connections: These connections, to api_uri value specified in provider.json, MUST use TLS and the server’s TLS certificate MUST be validated ONLY using the provider’s CA certificate, ca.crt. The one exception is if the ca_cert_uri points to an api_uri address. In this one case, there might be no way to authenticate the server’s TLS certificate and the connection should proceed without any validation (the client must still check the fingerprint on the subsequently downloaded CA certificate).

TODO

Changes that should be made for future versions:

  • Get rid of DOMAIN/provider.json. Instead, replace it with a more minimal DOMAIN/bootstrap.json that includes just the API url, CA url, and CA cert fingerprint. Why? it makes it easier to have a minimal bootstrap.json file for when you need to manually put this file in place on your webserver. The full provider.json can be loaded from the API.
  • Actually, lets change the name of provider.json to something else. It is confusing, since there is a different provider.json that the sysadmins edit. We could call the bonafide one ‘provider-information.json’ or something.
  • There should be support for multiple CA certificates.
  • Unify the file format for specifying servers. Currently, different service configs use different formats.
  • Add JSON schema.