Skip to content

Latest commit

 

History

History
127 lines (94 loc) · 5.74 KB

README.md

File metadata and controls

127 lines (94 loc) · 5.74 KB

Checkpoint format

This directory contains a description and supporting golang code for a reusable Checkpoint format which the TrustFabric team uses in various projects.

The format itself is heavily based on the golang sumbdb head, and corresponding signed note formats, and consists of two parts: a signed envelope, and a body.

Signed envelope

The envelope (signed note) is of the form:

  • One or more non-empty lines, each terminated by \n (the body)
  • One line consisting of just one \n (i.e. a blank line)
  • One or more signature lines, each terminated by \n

All signatures commit to the body only (including its trailing newline, but not the blank line's newline - see below for an example).

The signature(s) themselves are in the sumdb note format (concrete example below):

– <identity> <key_hint+signature_bytes> where:

  • is an emdash (U+2014)
  • <identity> gives a human-readable representation of the signing ID

and the signature_bytes are prefixed with the first 4 bytes of the SHA256 hash of the associated public key to act as a hint in identifying the correct key to verify with.

For guidance on generating keys, see the note documentation and implementation. Of particular note is that the public key and its hash commit to the algorithm identifier.

Differences from sumdb note: Whereas the golang signed note implementation currently supports only Ed25519 signatures, the format itself is not restricted to this scheme.

Checkpoint body

The checkpoint body is of the form:

<Origin string>
<Decimal log size>
<Base64 log root hash>
[otherdata]

The first 3 lines of the body MUST be present in all Checkpoints.

  • <Origin string> should be a unique identifier for the log identity which issued the checkpoint. The format SHOULD be a URI-like structure like <dns_name>[/<suffix>], where the log operator controls <dns_name>, e.g example.com/log42. This is only a recommendation, and clients MUST NOT assume that the origin is a URI following this format. This structure reduces the likelihood of origin collision, and gives clues to humans about the log operator and what is in the log. The suffix is optional and can be anything. It is used to disambiguate logs owned under the same prefix.

    The presence of this identifier forms part of the log claim, and guards against two logs producing bytewise identical checkpoints.

  • <Decimal log size> is the ASCII decimal representation of the number of leaves committed to by this checkpoint. It should not have leading zeroes.

  • <Base64 log root hash> is an RFC4684 standard encoding base-64 representation of the log root hash at the specified log size.

  • [otherdata] is opaque and optional, and, if necessary, can be used to tie extra data to the checkpoint, however its format must conform to the sumdb signed note spec (e.g. it must not contain blank lines.)

Note that golang sumdb implementation is already compatible with this [otherdata] extension (see https://github.com/golang/mod/blob/d6ab96f2441f9631f81862375ef66782fc4a9c12/sumdb/tlog/note.go#L52). If you plan to use otherdata in your log, see the section on merging checkpoints.

The first signature on a checkpoint should be from the log which issued it, but there MUST NOT be more than one signature from a log identity present on the checkpoint.

Example

An annotated example signed checkpoint in this format is shown below:

format

This checkpoint was issued by the log known as "Moon Log", the log's size is 4027504, in the other data section a timestamp is encoded as a 64bit hex value, and further application-specific data relating to the phase of the moon at the point the checkpoint was issued is supplied following that.

Merging Checkpoints

This checkpoint format allows a checkpoint that has been independently signed by multiple identities to be merged, creating a single checkpoint with multiple signatures. This is particularly useful for witnessing, where witnesses will independently check consistency of the log and produce a counter-signed copy containing two signatures: one for the log, and one for the witness.

The ability to merge signatures for the same body is a useful optimization. Clients that require N witness signatures will not be required to fetch N checkpoints. Instead they can fetch a single checkpoint and confirm it has the N required signatures (in addition to the log signature).

Note that this optimization requires the checkpoint body to be byte-equivalent. The log signature does not need to be equal; when merging, only one of the log's signatures over this body will be propagated. The core checkpoint format above allows merging for any two consistent checkpoints for the same tree size. However, if the otherdata extension is used then this can lead to checkpoints that cannot be merged, even at the same tree size.

We recommend that log operators using otherdata consider carefully what information is included in this. If data is included in otherdata that is not fixed for a given tree size, then this can easily lead to unmergeable checkpoints. The most commonly anticipated cause for this would be including the timestamp at which the checkpoint was requested within the otherdata. In this case, no two witnesses are likely to ever acquire the same checkpoint body. There may be cases where this is unavoidable, but this consequence should be considered in the design.