letsencrypt / letsencrypt/boulder
mtca/mtpublisher: store signed note lines in `checkpoints` table
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:
- Store the
timestamped_signaturedata type from tlog-cosignature, which istimestamp || signature - 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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