An open API service indexing awesome lists of open source software.

https://github.com/robstradling/mtc-demo

AI-generated Merkle Tree Certificates Demo
https://github.com/robstradling/mtc-demo

Last synced: 1 day ago
JSON representation

AI-generated Merkle Tree Certificates Demo

Awesome Lists containing this project

README

          

# mtc-demo

A Go implementation of **Merkle Tree Certificates** as specified in
[draft-ietf-plants-merkle-tree-certs-04](https://www.ietf.org/archive/id/draft-ietf-plants-merkle-tree-certs-04.txt).

Generated by Claude Opus 4.6 from [prompts](#prompts) listed further down this document.

Merkle Tree Certificates are a new form of X.509 certificate that integrates
public logging with certificate issuance. A CA maintains an append-only
issuance log, signs views of the log, and constructs certificates from
inclusion proofs into the log. This reduces overhead from post-quantum
signature algorithms and shorter-lived certificates while preserving
Certificate Transparency–style security properties.

## Installation

```
go install mtc/cmd/mtc-demo@latest
```

Or build from source:

```
git clone https://github.com/robstradling/mtc-demo
cd mtc-demo
go build ./cmd/mtc-demo/
```

Requires Go 1.25+ and depends on `golang.org/x/crypto/cryptobyte` and
`filippo.io/mldsa` (ML-DSA post-quantum signatures).

## Overview

The package implements the full lifecycle described in the draft:

1. **Issuance log** — the CA appends validated entries to an append-only log
2. **Checkpoints & subtrees** — the CA signs tree snapshots
3. **Cosignatures** — external cosigners attest to correct log operation
4. **Certificate construction** — inclusion proofs + cosignatures form a certificate
5. **Verification** — relying parties verify proofs against trusted cosigners or predistributed landmark subtrees

```
┌─ CA ─────────────────┐ ┌─ Authenticating Party ─┐
│ Validate request │ │ │
│ Add to issuance log │───>│ Download certificates │
│ Cosign checkpoint │ │ - TBSCertificate │
└──────┬───────────────┘ │ - inclusion proof │
│ │ - cosignatures │
▼ └────────────┬───────────┘
┌─ Cosigners ──────────┐ │
│ Verify append-only │ ▼
│ Sign subtrees │ ┌─ Relying Party ────────┐
└──────────────────────┘ │ Verify proof + sigs │
│ Check revocation │
┌─ Monitors ───────────┐ └────────────────────────┘
│ Watch for misuse │
└──────────────────────┘
```

## CLI Tool

The `mtc-demo` CLI provides subcommands for operating all MTC ecosystem
roles: CA, cosigner, mirror, relying party, and landmark manager.

State is persisted in `./mtc-state.json` (override with `MTC_STATE` env var).

### Quick Start

```sh
# Initialize a CA and issuance log
mtc-demo ca init 32473.1 1

# Generate cosigner keys
mtc-demo cosigner keygen 32473.100 ed25519
mtc-demo cosigner keygen 32473.200 p256

# Add certificate entries
mtc-demo ca add example.com
mtc-demo ca add test.example.com

# Cosign subtrees (check subtree ranges with 'ca checkpoint')
mtc-demo ca checkpoint
mtc-demo cosigner sign 32473.100 0 2
mtc-demo cosigner sign 32473.200 0 2

# Issue and verify a certificate
mtc-demo ca issue 1
mtc-demo verify cert cert-1.pem
```

### CA Operations

```
mtc-demo ca init Initialize a new CA and issuance log
mtc-demo ca info Show CA and log state
mtc-demo ca add Add a TBS certificate entry to the log
mtc-demo ca checkpoint Display current checkpoint and subtree hashes
mtc-demo ca issue Issue a certificate for a log entry
mtc-demo ca prune Prune entries below min-index
```

### Cosigner Operations

```
mtc-demo cosigner keygen Generate a key pair (ed25519, p256, p384)
mtc-demo cosigner list List configured cosigner keys
mtc-demo cosigner sign Cosign a subtree
```

### Mirror Operations

```
mtc-demo mirror inclusion Generate and verify an inclusion proof
mtc-demo mirror consistency Verify consistency with a previous checkpoint
```

### Certificate Verification

```
mtc-demo verify cert Verify a Merkle Tree Certificate
```

### Landmark Operations

```
mtc-demo landmark init Initialize landmark sequence
mtc-demo landmark allocate Allocate a landmark at current tree size
mtc-demo landmark info Show landmark sequence info
mtc-demo landmark find Find which landmark covers an entry
```

### Utilities

```
mtc-demo demo Run a full end-to-end lifecycle demo
mtc-demo hash leaf [data] Compute a Merkle leaf hash
mtc-demo hash node Compute an interior node hash
mtc-demo tree [entries...] Build a Merkle tree and show info
```

## Package Structure

| File | Spec Section | Purpose |
|------|-------------|---------|
| `trustanchorid.go` | §5.2 | `TrustAnchorID` type (base-128 OID encoding), `LogID()`, `LandmarkID()` |
| `hash.go` | RFC 9162 §2.1 | SHA-256 leaf and node hashing |
| `subtree.go` | §4.1, §4.5 | Subtree validation and interval covering |
| `tree.go` | §4.3, §4.4 | `MerkleTree` with proof generation |
| `proof.go` | §4.3.2–§4.4.3 | Proof evaluation and verification |
| `entry.go` | §5.3 | Log entry encoding (`MerkleTreeCertEntry`) with extensions |
| `cosign.go` | §5.4 | Cosignature generation and verification |
| `cert.go` | §6.1 | `MTCProof` and X.509 certificate construction |
| `verify.go` | §7.1–§7.5 | Relying party verification |
| `log.go` | §5 | `IssuanceLog` management |
| `landmark.go` | §6.3 | `LandmarkSequence` for size-optimized certificates |
| `cmd/mtc-demo/` | — | CLI tool for operating MTC ecosystem roles |

## Usage

### Creating an Issuance Log

```go
caID, _ := mtc.ParseTrustAnchorID("32473.1")
log := mtc.NewIssuanceLog(caID, 1) // CA ID + log number

// The log starts with a mandatory null entry at index 0.
// Add certificate entries:
entry := mtc.MarshalTBSCertEntry(tbsCertLogEntryContents)
index := log.AddEntry(entry)
```

### Generating and Verifying Inclusion Proofs

```go
// Generate a subtree inclusion proof for entry at `index`
// within subtree [start, end).
proof, _ := log.SubtreeInclusionProof(index, start, end)

// Compute the entry hash.
rawEntry, _ := log.Entry(index)
entryHash := mtc.HashEntry(rawEntry)

// Verify the proof against a known subtree hash.
subtreeHash, _ := log.SubtreeHash(start, end)
err := mtc.VerifySubtreeInclusionProof(proof, index, entryHash, subtreeHash, start, end)
```

### Generating and Verifying Consistency Proofs

```go
// Prove subtree [start, end) is consistent with the full tree.
proof, _ := log.SubtreeConsistencyProof(start, end)

rootHash, _ := log.CheckpointHash()
subtreeHash, _ := log.SubtreeHash(start, end)
err := mtc.VerifySubtreeConsistencyProof(proof, start, end, log.Size(), subtreeHash, rootHash)
```

Standard RFC 9162–style proofs are also available:

```go
proof, _ := log.Tree().InclusionProof(index)
err := mtc.VerifyInclusionProof(proof, index, treeSize, entryHash, rootHash)

proof, _ := log.Tree().ConsistencyProof(oldSize)
err := mtc.VerifyConsistencyProof(proof, oldSize, newSize, oldRoot, newRoot)
```

### Cosigning Subtrees

```go
cosignerID, _ := mtc.ParseTrustAnchorID("32473.1.1")

cosignerKey := &mtc.CosignerKey{
CosignerID: cosignerID,
SignatureAlgorithm: mtc.SignatureP256SHA256,
PrivateKey: ecdsaPrivateKey, // crypto.Signer
}

logID := caID.LogID(1) // derive log ID from CA ID + log number
subtreeHash, _ := log.SubtreeHash(start, end)
sig, _ := mtc.Cosign(cosignerKey, logID, uint64(start), uint64(end), &subtreeHash)

// Verify:
err := mtc.VerifyCosignature(
cosignerID, &ecdsaPrivateKey.PublicKey, mtc.SignatureP256SHA256,
logID, uint64(start), uint64(end), &subtreeHash, sig,
)
```

Supported signature algorithms:

| Constant | Algorithm |
|----------|-----------|
| `SignatureP256SHA256` | ECDSA with P-256 and SHA-256 |
| `SignatureP384SHA384` | ECDSA with P-384 and SHA-384 |
| `SignatureEd25519` | Ed25519 |

### Creating Certificates

```go
certDER, _ := mtc.CreateCertificate(
log.Tree(), // Merkle tree
issuerID, // issuer trust anchor ID
logID, // log ID (derived from CA ID + log number)
logNumber, // uint16 log number
index, // entry index
tbsCertFields, // func(b *cryptobyte.Builder) to write validity, subject, extensions
spkiDER, // DER-encoded SubjectPublicKeyInfo
start, end, // subtree for the proof
extensions, // MerkleTreeCertEntry extensions (or nil)
cosignerKeys, // []*CosignerKey
)
```

The certificate is a standard DER-encoded X.509 Certificate where:
- `signatureAlgorithm` is `id-alg-mtcProof` (experimental OID `1.3.6.1.4.1.44363.47.0`)
- `signatureValue` contains the serialized `MTCProof`
- `issuer` DN contains the CA's trust anchor ID under the experimental RDN attribute OID `1.3.6.1.4.1.44363.47.2`
- `serialNumber` encodes `(log_number << 48) | index`

### Verifying Certificates (Relying Party)

```go
cfg := &mtc.VerifierConfig{
CAID: caID,
Cosigners: []mtc.TrustedCosigner{
{
CosignerID: cosignerID,
SignatureAlgorithm: mtc.SignatureP256SHA256,
PublicKey: &ecdsaPublicKey,
},
},
Policy: &mtc.AnyNCosignerPolicy{
N: 1,
Trusted: []mtc.TrustAnchorID{cosignerID},
},
// Optional: predistributed landmark subtree hashes
TrustedSubtrees: []mtc.TrustedSubtree{ ... },
// Optional: revoked index ranges
RevokedRanges: mtc.RevokedRanges{
{Start: 0, End: 100}, // distrust serial numbers [0, 100)
},
}

err := mtc.VerifyCertificateSignature(certDER, cfg)
```

The verification procedure (§7.2):
1. Parses the `MTCProof` from the certificate's `signatureValue`
2. Checks the serial number against revoked ranges
3. Extracts log number and index from the serial number
4. Derives the log ID from the CA ID and log number
5. Reconstructs the `MerkleTreeCertEntry` (with extensions) from the `TBSCertificate`
6. Evaluates the inclusion proof to compute the expected subtree hash
7. If the subtree matches a trusted subtree (landmark-relative), accepts immediately
8. Otherwise, verifies cosignatures against the cosigner policy

### Landmark Sequences

Landmarks are periodic tree sizes that enable size-optimized
**landmark-relative certificates** (§6.3). These certificates contain only
an inclusion proof to a predistributed subtree — no signatures.

```go
caID, _ := mtc.ParseTrustAnchorID("32473.1")

// 7-day cert lifetime, 1-hour landmark interval → 169 active landmarks
maxActive := mtc.RecommendedMaxActiveLandmarks(168, 1) // 169

ls := mtc.NewLandmarkSequence(caID, 1, maxActive) // CA ID + log number + max active
ls.AllocateLandmark(1000) // landmark 1 at tree size 1000
ls.AllocateLandmark(5000) // landmark 2 at tree size 5000

// Find which landmark contains an entry.
landmarkNum, subtree, _ := ls.FindContainingLandmark(3000)

// Get the trust anchor ID for a landmark (e.g. "32473.1.1.1.42").
taID := ls.LandmarkTrustAnchorID(42)

// List currently active landmarks.
active := ls.ActiveLandmarks()
```

### Subtree Interval Covering

Given an arbitrary interval [start, end), `FindSubtrees` returns one or two
valid subtrees that efficiently cover it (§4.5):

```go
left, right, single, _ := mtc.FindSubtrees(5, 13)
// left = [4, 8) (full subtree)
// right = [8, 13) (may be partial)
```

### Log Pruning

Long-expired entries can be pruned from the log (§5.6.1):

```go
log.Prune(1000) // entries [0, 1000) are no longer available
// The tree structure is preserved — only entry data is dropped.
```

### Entry Encoding

```go
// Null entry (required at index 0):
nullEntry := mtc.MarshalNullEntry()

// TBS certificate entry from log entry contents:
entry := mtc.MarshalTBSCertEntry(tbsCertLogEntryContents)

// Build a TBSCertificateLogEntry from a DER TBSCertificate:
logEntryContents, _ := mtc.BuildTBSCertificateLogEntry(tbsCertDER)

// Compute the leaf hash for a serialized entry:
hash := mtc.HashEntry(entry)
```

## Experimental OIDs

This implementation uses the experimental OIDs defined in the draft for
early experimentation:

| OID | Name | Usage |
|-----|------|-------|
| `1.3.6.1.4.1.44363.47.0` | `id-alg-mtcProof` | Certificate signature algorithm |
| `1.3.6.1.4.1.44363.47.1` | `id-rdna-trustAnchorID` | Issuer DN attribute type |
| `1.3.6.1.4.1.44363.47.2` | `id-pe-mtcCertificationAuthority` | MTC CA extension |

These will be replaced with IANA-assigned OIDs when the specification is finalized.

## Specification Coverage

This implementation covers the following sections of
draft-ietf-plants-merkle-tree-certs-04:

- **§4** Subtrees — definition, inclusion proofs, consistency proofs, interval covering
- **§5** Issuance Logs — log structure, entries, cosigners, CA cosigners, pruning
- **§6** Certificates — certificate format, standalone certificates, landmark-relative certificates
- **§7** Relying Parties — trust anchors, signature verification, cosigner policies, trusted subtrees, revocation

Not implemented (out of scope or dependent on external protocols):
- §8 Use in TLS (trust anchor ID negotiation)
- §9 ACME Extensions
- §5.6 Log serving protocol (e.g. tlog-tiles)
- Appendix C Tiled Transparency Log extensions
- ML-DSA signature algorithms (pending Go standard library support)

## Testing

```
go test ./...
```

To run the full lifecycle demo:

```
go run ./cmd/mtc-demo/ demo
```

## License

See LICENSE file.

## References

- [draft-ietf-plants-merkle-tree-certs-04](https://www.ietf.org/archive/id/draft-ietf-plants-merkle-tree-certs-04.txt)
- [RFC 9162 — Certificate Transparency v2](https://www.rfc-editor.org/rfc/rfc9162)
- [draft-ietf-tls-trust-anchor-ids](https://datatracker.ietf.org/doc/draft-ietf-tls-trust-anchor-ids/)
- [GitHub: ietf-plants-wg/merkle-tree-certs](https://github.com/ietf-plants-wg/merkle-tree-certs)

## Prompts

- [2026-04-23] Implement draft-ietf-plants-merkle-tree-certs-03
- [2026-04-23] Create README.md with full documentation
- [2026-05-31] Update implementation to draft-ietf-plants-merkle-tree-certs-04
- [2026-06-02] Compare this repository with https://github.com/mcpherrinm/cactus. For any behaviour differences or compatibility issues found, determine if there's a bug in this repository, or in the cactus repository, or if the MTC specification is insufficiently clear. Produce a .md report of recommendations.
- [2026-06-02] Fix those bugs. Plug those conformance gaps, using filippo.io/mldsa instead of crypto/mldsa (for Go 1.26 compatibility). One git commit per item.
Update the headings in COMPARISON-WITH-CACTUS.md to indicate each item that has been fixed or mitigated.
- [2026-06-02] Create a CLI tool.
- [2026-06-02] Add CLI subcommands for operating an MTC CA, mirror, and anything else that might be missing.
- [2026-06-02] Review and update README.md as necessary.