Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Specification and Implementation #1

merged 10 commits into from Nov 22, 2019


Copy link

@paragonie-scott paragonie-scott commented Nov 22, 2019

There are two main APIs that you will be working with:

  • Scribe - Writes updates to the primary Chronicle (which fans out to the replicas).
  • Synchronizer - Pulls updates from a pool of Chronicle replicas.

At the protocol level, you have:

  • Action: Can be performed against a database (anything that implements DbInterface).
  • Message: Encapsulate an Action. Optional signature, no provider identifier (for testing).
  • SignedMessage: Encapsulate a Message and bind them to a "provider". Mandatory signature, which is tied to a public key corresponding to a provider.
  • Packet: Abstracts the contents of an HTTP request body.

The lifecycle will look like this:

  1. Some developer will, from a higher layer of abstraction, create an Action specific to what they want to accomplish.
  2. That action will be signed with the developer's secret key into a SignedMessage.
  3. The SignedMessage can be serialized and shipped off alongside some other action (i.e. an HTTP POST request to the update server).
  4. Some optional magic happens, depending on what the ecosystem needs. (Validation, worker queues, etc.)
  5. The update server, after verifying the sanity of the SignedMessage, uses the Scribe to publish it to the primary Chronicle.
  6. The primary Chronicle records the new record and returns an HTTP response to the update server.
  7. Replica instances mirror the new record from the primary Chronicle, verifying the integrity of each record as they make a copy.
  8. Another developer in the universe needs either public keys for a specific provider, or the metadata (including signature) for an update to a dependency (e.g. WordPress plugin). Thus, they query a trusted key server (which can be a federated, standalone server; or it can be their local WordPress blog (each WP site can decide!)) for the current state of affairs.
  9. The update server queries the local database and returns the requested data, which has a freshness guarantee as long as step 10 goes smoothly.
  10. At some point in time (asynchronous to the rest of the lifecycle), the key server queries a pool of Chronicle replicas for new records, using the Synchronizer. For each new record served by a source Chronicle, the Synchronizer will do the following steps:
    1. Extract the SignedMessage from the Response (child class of Packet).
    2. Verify that a quorum (e.g. minimum of 3 out of 10) of the other Chronicle replicas (i.e. not the source) sees the same summary hash for the SignedMessage.
    3. Check that the signature is valid. There are four possible outcomes:
      1. New provider never seen before. If it describes an "Append Key" action, it succeeds.
      2. Signed by one of the provider's existing signing keys. Check signature; succeed if valid.
      3. Signed by the "super provider". Check signature; succeed if valid.
      4. Invalid.
    4. Extracts the Message from the SignedMessage.
    5. Parses the Message to construct an Action.
    6. Performs the Action on the database, which adds public keys or update metadata; or marks existing entries as revoked.
  11. The end developer from step 8 gets the information they needed from the update server, and does whatever they needed to do with it.

The important thing is, most developers will only see steps 1, 8, and 11. And step 1 will be performed by their tooling, not by their own manual code.

And yet, despite allowing APIs that look as simple as wp_get_public_keys($vendor_name), this design allows a robust and straightforward security analysis. As long as the well-studied primitive (Ed25519, BLAKE2b) remain secure:

  1. You can have total transparency into which vendors possess which public keys, and which are still trusted.
  2. You can have total transparency into when software updates are released (along with important metadata, such as signatures), and which are still trusted.
  3. An attacker that does not possess one of the publisher's signing keys or the Super Publisher's signing keys cannot forge any Action on behalf of another publisher.
  4. An attacker cannot selectively target malicious Actions (which may contain either software updates or public keys); the only way to attack this system is to announce your presence to the entire ecosystem.
  5. Even though the Super Publisher (which exists to cope with conditions of secret key compromise by other publishers) can bypass the identity binding of SignedMessages, all of their signatures are unavoidably disclosed (see step 4).

This lets you plug in a summary hash from a Chronicle and query a pool
of other Chronicle instances to ensure they have a record of it stored.

The envisioned use case:

1. Have a local-ish replica of the WordPress Chronicle.
2. For each update of the local-ish Chronicle, cross-reference against
   other instances to ensure some quorum (e.g. at least 3 of 5) of
   Chronicle instances see the same record.

This implements part 2 of that use case.

Part 1 (having a source Chronicle that is parsed to assemble Action
objects, which should allow anyone to deterministically reproduce the
current snapshot of trusted public keys and software updates) is the
next thing to be developed.

Continuing on:

3. Developer tools for signing releases.
4, Infrastructure plumbing for ensuring updates are streamed to the
   source Chronicle that seeds part 1.

And then we will develop other open source projects that consume
libgossamer in some which way.
Signatures are hex-encoded and consists of a 4-byte header which
identifies the hash and signature algorithms being employed.

Currently supported:

- Ed25519 + SHA384 (WordPress core used this)
- Ed25519 + BLAKE2b

In the future, we may employ something like FALCOLN or SPHINCS+.
- Added a layer of indirection with SignedMessage
  - You can create a SignedMessage from a Message or Action
  - You can verify that a SignedMessage was signed by the
    relevant provider OR by the "super provider" (i.e. break glass
    feature for letting core teams cope with key compromise)
- Added Synchronizer API for downloading updates from a Chronicle
- Added Source API for streaming updates from a pool of Chronicle
  replicas (used by the Synchronizer)
- Added Scribe API for publishing updates to the source Chronicle
  (not currently used)
- DbInterface: Persist metadata about the last successful summary hash
  (or Merkle root, when we get around to Trillian support)
- HttpInterface: Added post() method
- Action: Added a lot of get*() and with*() methods
@paragonie-scott paragonie-scott changed the title V0.1 dev Initial Specification and Implementation Nov 22, 2019
@paragonie-scott paragonie-scott merged commit 8c7ee4c into master Nov 22, 2019
@paragonie-scott paragonie-scott deleted the v0.1-dev branch Nov 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet
None yet

Successfully merging this pull request may close these issues.

None yet

2 participants