13 releases

0.1.12 Sep 30, 2022
0.1.11 Sep 7, 2022
0.1.9 Aug 27, 2022
0.1.3 Jul 25, 2022
0.1.1 Jun 26, 2022

#672 in Network programming

MPL-2.0-no-copyleft-exception

170KB
3.5K SLoC

libmensago

A library written in Rust for interacting with keycards on the Mensago platform released under the Mozilla Public License 2.0.

Description

This library enables developers to create, sign, and verify keycards, a form of digital certificate designed specifically for the needs of the platform.

Status

libkeycard is in alpha. There may be unforeseen changes in the API and it should not be considered finalized until the 1.0 release, so developers utilizing this library should be aware that there may be breaking changes even between the current development versions, as semver will not be utilized until the 1.0 release. Reading the CHANGELOG is highly encouraged when upgrading.

Building

Building libkeycard requires the Rust toolchain. Check out the repository and run cargo build.


lib.rs:

Libkeycard is an MPL2.0-licensed library for interacting with Mensago digital certificates called keycards.

No guarantees of any kind are provided with the library even though it has been written with care.

The Mensago Identity Services design document is required reading to use this library effectively.

Usage

The main tasks for interacting with keycards can be broken down into a few tasks:

  • Verifying a keycard
  • Retrieving an encryption or signature verification key
  • Adding a new entry
  • Revoking a keycard

Verifying a Keycard

The most common case for interacting with a keycard is obtaining a person's most recent encryption or verification keys. For starters, you will need to obtain the complete keycard for the user's organization along with the user's complete keycard.

Once you have obtained a keycard's raw text data from an organization's server, verifying the organization's keycard is relatively simple:

  1. Instantiate a Keycard object using from()
  2. Call verify().

A user's keycard is more involved to verify:

  1. Obtain, instantiate, and verify the organization's keycard
  2. Obtain, instantiate, and verify the users's keycard
  3. Find the branch point in the organization's keycard for the user's root keycard entry using find()
  4. Get the root entry from the user's keycard and use it to call verify_chain() on the branch point entry

Retrieving a Key

Obtaining a key is as simple as getting the current entry of a keycard and getting the appropriate field using get_field(). Once obtained, it can be passed directly to from_string()/from_strings() for encryption or signing keys/pairs or CryptoString::from(), depending on the usage needs.

Adding a New Organization Entry

Creating a brand-new root organization entry is done during the setup process of the organization's server. New entries are added by creating a Keycard instance from the organization's keycard data and then calling chain(), which generates a new entry, signs it and returns the new entry and the new key set. From there, the new entry is uploaded to the server via the ORGCHAIN command sent by the client.

Adding a New User Entry

Creating a new root user entry is the most complicated of all the keycard-related processes because the server and the user don't trust one another.

  1. Generate a new user key set, consisting of a signing key pair for contact requests, an encryption key pair for contact requests, a signing key pair for general purpose use, an encryption key pair for general purpose use.
  2. Create a new Entry instance using new() or new_from_str().
  3. Set field data as appropriate, including the entry lifespan (expiration). The "Time-To-Live" field is set to the recommended 14 days and a timestamp is also automatically generated by the constructor.
  4. Confirm that the entry meets basic data compliance using is_data_compliant().
  5. Begin the signing process by submitting the entry data in its current form to the server to populate the Organization-Signature auth string.
  6. Set the Previous-Hash field using the value from the Hash field of the current entry on the organization's keycard.
  7. Generate a hash of all current entry data by calling hash() with the desired hash algorithm.
  8. Add a "User-Signature" signature using the contact request signing pair.
  9. Upload the entry to the server

Adding new non-root entries to a keycard is significantly less-involved.

  1. Create a new Keycard instance of the user's keycard data.
  2. Call the keycard's chain() method with the current entry's contact request signing pair to generate the chain-of-custody signature and return the new entry and key set.
  3. A client will then upload the new entry to the server for signing. A server utilizing this library will call cross_sign() to accomplish this.
  4. Generate a hash of all current entry data by calling hash() with the desired hash algorithm.
  5. Add a "User-Signature" signature using the contact request signing pair.
  6. Upload the entry to the server

Revoking a User Keycard

Revoking a user keycard is not terribly involved.

  1. Create a new Keycard instance of the user's keycard data.
  2. Call the keycard's revoke() method to create a new root entry with an updated Index field and new key set.
  3. A client will then upload the new entry to the server for signing. A server utilizing this library will call cross_sign() to accomplish this.
  4. Set the Previous-Hash field using the value from the Hash field of the current entry on the organization's keycard.
  5. Generate a hash of all current entry data by calling hash() with the desired hash algorithm.
  6. Add a "User-Signature" signature using the contact request signing pair.
  7. Upload the entry to the server using the REVOKE command

Revoking an Organization Keycard

In terms of time-to-recovery, this is the worst-case scenario, but the process itself isn't difficult at all.

  1. Create a new Keycard instance of the user's keycard data.
  2. Call the keycard's revoke() method to create a new root entry with an updated Index field and new key set.
  3. A client will then upload the new entry to the server for signing. A server utilizing this library will call cross_sign() to accomplish this.
  4. Set the Previous-Hash field using the value from the Hash field of the current entry on the organization's keycard.
  5. Generate a hash of all current entry data by calling hash() with the desired hash algorithm.
  6. Add a "User-Signature" signature using the contact request signing pair.
  7. Upload the entry to the server using the ORGREVOKE command

By sending the ORGREVOKE command, the server will replace the organization's keycard tree with a new one and send revocation requests to all users.

Dependencies

~25MB
~233K SLoC