letsencrypt / letsencrypt/boulder

mtca/mtpublisher: store signed note lines in `checkpoints` table

Open
#8,991 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
5.8k
Forks
649
Avg merge
3d 23h
Merged PRs (30d)
24

Description

Right now the checkpoints table fields mtcaSignature and mirrorSignature hold ML-DSA-44 signatures over a CosignedMessage with timestamp and start both zero.

tlog-cosignature says timestamp can be zero (in general): https://github.com/C2SP/C2SP/blob/f1ed4e2e25942390bf979939c89adea895175a41/tlog-witness.md

timestamp is timestamped_signature.timestamp. These two values MAY be zero if the cosigner doesn't make any statement as to the tree being the largest observed at time of signing.

However, tlog-witness puts a different requirement specifically on add-checkpoint: https://github.com/C2SP/C2SP/blob/f1ed4e2e25942390bf979939c89adea895175a41/tlog-witness.md#add-checkpoint

The cosignature MUST NOT omit the timestamp, i.e. the timestamp MUST NOT be zero.

We will be using add-checkpoint from tlog-mirror, not tlog-witness, but tlog-mirror's add-entries incorporates tlog-witness's add-checkpoint behavior: https://github.com/C2SP/C2SP/blob/f1ed4e2e25942390bf979939c89adea895175a41/tlog-mirror.md#add-entries

The response body MUST be formatted as in a witness's successful add-checkpoint response.

So signature lines we get from mirrors will always have a nonzero timestamp. If we fetch mirror checkpoints instead, the mirror could technically choose to sign a checkpoint with a zero timestamp, but that seems unlikely. And is out of our control anyhow.

Since these fields need a way to carry a per-signature timestamp, we need to store something other than an ML-DSA-44 signature. That's because, in order to verify these signatures, we need to know the timestamp they were signed over. And that will certainly be different between the CA and the mirror signature. A couple options:

  1. Store the timestamped_signature data type from tlog-cosignature, which is timestamp || signature
  2. Store the signed-note signature line format, which additionally incorporates a key name and key ID.

I think we should do (2), since the majority of operations we will do with these values are done in the signed-note format:

  • In the MTCA, write a checkpoint file with the CA signature attached.
  • In the publisher, send the checkpoint file with CA signature to a mirror.
  • In the MTCA, verify a signature from any trusted mirror before sequencing a new checkpoint. Technically this doesn't have to be verified using the signed-note format; we could construct a CosignedMessage directly from the checkpoint state and a timestamped_signature.

This also suggests a format divide: the checkpoints table, used for tlog operations, uses signed-note format. The checkpointSubtrees table, used for X.509 operations, uses ML-DSA signatures over CosignedMessages, since those go into MTCProofs directly (and implicitly have timestamp zero).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing how the checkpoints table is written and read in the MTCA and publisher, then compare that with the checkpointSubtrees handling. Use the cited tlog-cosignature, tlog-witness, tlog-mirror, and signed-note formats to define the stored value; done means checkpoint operations preserve per-signature timestamps and verification continues to work.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, cryptography, databases
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.