diff --git a/README.md b/README.md index 6f4fdcf..f8fc216 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,11 @@ Merkle Tree Certificates for TLS Implementation of [Merkle Tree Certificates for TLS]( https://davidben.github.io/merkle-tree-certs/draft-davidben-tls-merkle-tree-certs.html) - -At the moment only the Certification Authority (CA) side is implemented, -and we differ from `-03` and `master` branch, by including -some [unmerged PRs](https://github.com/davidben/merkle-tree-certs/pulls). + in Go. This contains a Certification Authority (CA), Mirror, and + code to verify certificates. +This does not contain integration with TLS (yet) or the ACME bits (yet). +At the moment we differ from `-04` and `main` branch of the specification, + by including some [unmerged PRs](https://github.com/davidben/merkle-tree-certs/pulls). Demo ---- @@ -20,9 +21,11 @@ and [David's TLS working group presentation at IETF116]( Merkle Tree Certificates is an **optimisation** to the WebPKI (including [Certificate Transparency](https://certificate.transparency.dev)) -motivated by the large sizes of typical post-quantum signatures and public keys, -to reduce the number of keys and signatures -required for the common case where +motivated by the [large sizes]( + https://dadrian.io/blog/posts/pqc-signatures-2024/) of +[typical post-quantum signatures and public keys]( + https://blog.cloudflare.com/another-look-at-pq-signatures/), +to reduce the number of keys and signatures required for the common case where 1. Certificate issuance does not have to be immediate. For instance, because a certificate can be requested ahead of time for an existing domain @@ -31,11 +34,13 @@ required for the common case where 2. The relying party (eg. browser) has a trusted update mechanism. There are also several ways to use MTC without trusted update mechanism, - with various trade-offs: see the *Transparency Services* section - of the spec. + with various trade-offs: see the [Relying Party Policy]( + https://davidben.github.io/merkle-tree-certs/draft-davidben-tls-merkle-tree-certs.html#name-relying-party-policy) + section of the specification. -MTC does not aim to replace the full WebPKI, but is a first meaningful step -for improvement. +If we're not in this case (which is estimated to be +[less than 0.1%](https://www.youtube.com/watch?v=f8unMB2Qjho) of the time), +then we fall back to regular X.509 certificates. ### Intermezzo: `mtc` commandline tool @@ -57,8 +62,9 @@ The first part (TLS and the public key) is the *subject*, and the latter (domain and IP) are the *claim*. Roughly, an assertion is like a certificate without the signature. -To create an assertion, you can use the `mtc new-assertion` command. -First, let's quickly create a P-256 public key to play with. +You can create a request for an assertion to be signed with the +`mtc new-assertion-request` command. First, let's quickly create +a P-256 public key to play with. ``` $ openssl ecparam -name prime256v1 -genkey -out p256.priv @@ -67,39 +73,48 @@ $ openssl ec -in p256.priv -pubout -out p256.pub Now we create an assertion that this P-256 public key should be valid for `example.com` and `198.51.100.60`, and write it to -the `my-assertion`. +the `my-asr`. ``` -$ mtc new-assertion --tls-pem p256.pub --dns example.com --ip4 198.51.100.60 -o my-assertion -checksum: 14bc907eafd02d5be8b8cc319d87ad5afe9266a6910a18cbdcbfcee1b7af696a +$ mtc new-assertion-request --tls-pem p256.pub --dns example.com --ip4 198.51.100.60 -o my-asr ``` Let's check it using `mtc inspect`: ``` -$ mtc inspect assertion my-assertion +$ mtc inspect assertion-request my-asr +checksum 2024bdbffe399acca37d299a03c047aa33ef596ae471c17698a0566d00951bd9 +not_after unset subject_type TLS signature_scheme p256 -public_key_hash a02a1758e4c9d6511dc02f59301b9f29e41762d3d769c87a22333497984a41ef +public_key_hash 20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a dns [example.com] ip4 [198.51.100.60] +evidence-list (0 entries) ``` +An assertion request can contain two bits of extra information besides +the assertion itself. First is a `not_after` field that limits +the validity of the assertion when published. +The second is optional "evidence" that's published alongside the +assertions. In the future this could for instance be used for +serialized DNSSEC proofs. + ### Batches, merkle trees and signed validity windows -An MTC CA doesn't give you a certificate for an assertion immediately. Instead, -assertions are queued and issued in **batches** with a fixed rhythm, -for instance a batch is issused once every hour. -All assertions in a single batch are valid for the same period of time, -the **validity window**, which is, for instance, two weeks. +An MTCA doesn't give you a certificate for an assertion request immediately. +Instead, assertions are queued and issued in **batches** with a fixed rhythm, +for instance a batch is issued once every hour. +All assertions in a single batch by default are valid for the same period of +time, the **validity window**, which is, for instance, two weeks. The CA publishes these batches publicly over HTTP. For each batch, the CA computes a [Merkle tree]( https://en.wikipedia.org/wiki/Merkle_tree). -This condenses all the assertions in that batch into a single **root** hash. -For every batch, the CA signs that root together with all the roots +This condenses all the assertions in that batch into a single **tree head** hash. +For every batch, the CA signs that tree head together with all the tree heads of the currently valid batches. This signature, together with those - signed roots is called the **signed validity window** for that batch, + signed tree heads is called the **signed validity window** for that batch, which is published alongside the assertions. ### Creating a CA @@ -110,9 +125,17 @@ Let's create an MTC CA. $ mtc ca new --batch-duration 5m --lifetime 1h 62253.12.15 ca.example.com/path ``` -This creates a new MTC CA called `my-mtc-ca`, and puts the data in the -current working directory. A batch is issued every 5 minutes, and -each batch is valid for one hour. +This creates a new MTC CA in the current working directory. It's configured +to issue a batch every 5 minutes, and for each batch to be valid for an hour. +For a real CA we'd want batch durations in the order of an hour, +and a lifetime of a week or two. In this demo we shorten things a bit, so +we don't have to wait too long. + +The CA is configured to be hosted at `https://ca.example.com/path` and +to be identified by the [trust anchor identifier]( + https://datatracker.ietf.org/doc/draft-ietf-tls-trust-anchor-ids/) 62253.12.15. +You can get your own by requesting a [private enterprise number here]( + https://www.iana.org/assignments/enterprise-numbers/). Let's have a look at the files created: @@ -122,9 +145,9 @@ $ find . ./signing.key ./www ./www/mtc -./www/mtc/v1 -./www/mtc/v1/ca-params -./www/mtc/v1/batches +./www/mtc/v04b +./www/mtc/v04b/ca-params +./www/mtc/v04b/batches ./queue ./tmp ``` @@ -136,154 +159,172 @@ at `https://ca.example.com/path`. At the moment, the only file of interest is `ca-params`, which contains the information about the CA: ``` -$ mtc inspect ca-params www/mtc/v1/ca-params +$ mtc inspect ca-params www/mtc/v04b/ca-params issuer 62253.12.15 -start_time 1705677477 2024-01-19 16:17:57 +0100 CET +start_time 1745420554 2025-04-23 15:02:34 +0000 UTC batch_duration 300 5m0s life_time 3600 1h0m0s storage_window_size 24 2h0m0s validity_window_size 12 server_prefix ca.example.com/path -public_key fingerprint ml-dsa-87:85b5a617ef109e0a8d68a094c8b969f622ac4096c513fa0acd169c231ce2fad5 +public_key fingerprint ml-dsa-87:84489bcb42b411a85d163ae95e7deb92b106a75840819a985e44d0e01ae3238e ``` The `batches` folder is empty, because there are no batches issued yet. -The `queue` file contains the assertions that will be issued. +The `queue` file contains the assertion requests that will be fulfilled +during the next issuance. ### Issuing our first batch -Let's issue our first assertion. We can read the assertion from disk we've -created earlier with `mtc new-assertion`: +Let's issue our first assertion. We can read the assertion request from disk we've +created earlier with `mtc new-assertion-request`: ``` -$ mtc ca queue -i my-assertion +$ mtc ca queue -i my-asr $ mtc ca show-queue -checksum 14bc907eafd02d5be8b8cc319d87ad5afe9266a6910a18cbdcbfcee1b7af696a +checksum 91723d11282646f6e798aaeb4ac8515168657c3df4622bc646437b4187a5deb7 +not_after 2025-04-23 16:02:33 +0000 UTC subject_type TLS signature_scheme p256 -public_key_hash a02a1758e4c9d6511dc02f59301b9f29e41762d3d769c87a22333497984a41ef +public_key_hash 20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a dns [example.com] ip4 [198.51.100.60] +evidence-list (0 entries) -Total number of assertions in queue: 1 +Total number of assertion requests in queue: 1 ``` -(We can pass the checksum from `new-assertion` with `--checksum` to make sure -the assertion wasn't corrupted.) - -We can also queue an assertion ad hoc: +We can also queue an assertion request ad hoc: ``` $ mtc ca queue --tls-pem p256.pub -d other.example.com -d second.example.com -$ mtc ca show-queue | tail -n 8 - -checksum fbdea936ae7795a3fa01d44230daba351d7480eceadd086ba32938ebc88a5458 +$ mtc ca show-queue | tail -n 10 +checksum 91723d11282646f6e798aaeb4ac8515168657c3df4622bc646437b4187a5deb7 +not_after 2025-04-23 16:02:33 +0000 UTC subject_type TLS signature_scheme p256 -public_key_hash a02a1758e4c9d6511dc02f59301b9f29e41762d3d769c87a22333497984a41ef -dns [other.example.com second.example.com] +public_key_hash 20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a +dns [example.com] +ip4 [198.51.100.60] +evidence-list (0 entries) -Total number of assertions in queue: 2 +Total number of assertion requests in queue: 2 ``` Let's issue our first batch. ``` -$ mtc ca issue -2024/01/19 16:27:31 INFO Starting issuance time=2024-01-19T16:27:31.841+01:00 -2024/01/19 16:27:31 INFO Current state expectedStored=0 expectedActive=0 existingBatches=⌀ -2024/01/19 16:27:31 INFO To issue batches=0 +$ mtc ca issue +2025/04/23 17:05:20 INFO Starting issuance time=2025-04-23T15:05:20.664Z +2025/04/23 17:05:20 INFO Current state expectedStored=0 expectedActive=0 existingBatches=⌀ +2025/04/23 17:05:20 INFO To issue batches=0 ``` And let's check: ``` $ find . +``` . ./signing.key ./www ./www/mtc -./www/mtc/v1 -./www/mtc/v1/ca-params -./www/mtc/v1/batches -./www/mtc/v1/batches/0 -./www/mtc/v1/batches/0/tree -./www/mtc/v1/batches/0/abridged-assertions -./www/mtc/v1/batches/0/signed-validity-window -./www/mtc/v1/batches/0/index -./www/mtc/v1/batches/latest +./www/mtc/v04b +./www/mtc/v04b/ca-params +./www/mtc/v04b/batches +./www/mtc/v04b/batches/0 +./www/mtc/v04b/batches/0/validity-window +./www/mtc/v04b/batches/0/tree +./www/mtc/v04b/batches/0/entries +./www/mtc/v04b/batches/0/evidence +./www/mtc/v04b/batches/0/index +./www/mtc/v04b/batches/latest ./queue ./tmp ``` -We see a `0` batch has been created. `latest` is a symlink to `0`. +We see batch `0` has been created. `latest` is a symlink to to `0`. -The `abridged-assertions` is essentially the list of assertions: -the difference between a regular and abridged assertion, -is that with an abridged assertion, the public key has been replaced +Now, let's have a look at the batch. The `entries` file is essentially +the list of assertions: the difference between a regular assertion +and an entry is that with an entry, the public key has been replaced by the hash of the public key. ``` -$ mtc inspect abridged-assertions www/mtc/v1/batches/0/abridged-assertions -key 28b2216e7905ab48d5444f5b7ebf3d2386bc0444c9721fff77b0b313e734dab4 +$ mtc inspect entries www/mtc/v04b/batches/0/entries +key 0b65c8a5f69e88fd1eb58dff4d317f6173bd31773e14d99ace88a2aa7062fdd9 +not_after 2025-04-23 16:02:33 +0000 UTC subject_type TLS signature_scheme p256 -public_key_hash a02a1758e4c9d6511dc02f59301b9f29e41762d3d769c87a22333497984a41ef -dns [example.com] -ip4 [198.51.100.60] +public_key_hash 20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a +dns [other.example.com second.example.com] -key 80944a1728bc7b4cd7e583c6b24a5f413ba50b7ef5ba9d214e26c1a1974f0a19 +key 78b5ccc905b693659bf6581011f8efb17fd7aedf9ca70a196a22923f560feeca +not_after 2025-04-23 16:02:33 +0000 UTC subject_type TLS signature_scheme p256 -public_key_hash a02a1758e4c9d6511dc02f59301b9f29e41762d3d769c87a22333497984a41ef -dns [other.example.com second.example.com] +public_key_hash 20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a +dns [example.com] +ip4 [198.51.100.60] -Total number of abridged assertions: 2 +Total number of entries: 2 ``` -The `signed-validity-window` is the signed validity window: the roots of +The `validity-window` is the signed validity window: the tree heads of the currently valid batches: ``` -$ mtc inspect -ca-params www/mtc/v1/ca-params signed-validity-window www/mtc/v1/batches/0/signed-validity-window +$ mtc inspect -ca-params www/mtc/v04b/ca-params validity-window www/mtc/v04b/batches/0/validity-window signature ✅ batch_number 0 -tree_heads[-11] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-10] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-9] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-8] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-7] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-6] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-5] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-4] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-3] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-2] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-1] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[0] c005dcdb53c4e41befcf3a294b815d8b8aa0a260e9f10bfd4e4cb52eb3724aa3 +tree_heads[0] 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345 +tree_heads[-1] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-2] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-3] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-4] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-5] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-6] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-7] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-8] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-9] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-10] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-11] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf ``` We need to pass the `ca-params` file to be able to parse the file, and -check the signature therein. (As this is the first batch, the previous batches -contain a placeholder value.) +check the signature therein. (As not all previous batches exist, they use +the placeholder value for an empty tree.) The `tree` file contains the Merkle tree. ``` -$ mtc inspect tree www/mtc/v1/batches/0/tree +$ mtc inspect tree www/mtc/v04b/batches/0/tree number of leaves 2 number of nodes 3 -root c005dcdb53c4e41befcf3a294b815d8b8aa0a260e9f10bfd4e4cb52eb3724aa3 +tree head 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345 +``` + +The `evidence` file contains the optional evidence that can be provided +with the assertion request. We did not pass any, so they're empty: + ``` +$ mtc inspect evidence www/mtc/v04b/batches/0/evidence +evidence-list (0 entries) -Finally, the `index` file allows a quick lookup in `abridged-assertions` +evidence-list (0 entries) + +Total number of evidence lists: 2 +``` + +Finally, the `index` file allows a quick lookup in `entries` (and `evidence`) by key (hash of the assertion): ``` -$ mtc inspect index www/mtc/v1/batches/0/index +$ mtc inspect index www/mtc/v04b/batches/0/index key seqno offset -28b2216e7905ab48d5444f5b7ebf3d2386bc0444c9721fff77b0b313e734dab4 0 0 -80944a1728bc7b4cd7e583c6b24a5f413ba50b7ef5ba9d214e26c1a1974f0a19 1 69 +0b65c8a5f69e88fd1eb58dff4d317f6173bd31773e14d99ace88a2aa7062fdd9 0 0 0 +78b5ccc905b693659bf6581011f8efb17fd7aedf9ca70a196a22923f560feeca 1 91 3 total number of entries: 2 ``` @@ -300,135 +341,147 @@ $ mtc ca queue --tls-pem p256.pub -d 1.example.com $ mtc ca queue --tls-pem p256.pub -d 2.example.com $ mtc ca queue --tls-pem p256.pub -d 3.example.com $ mtc ca issue -2024/01/19 16:33:57 INFO Starting issuance time=2024-01-19T16:33:57.860+01:00 -2024/01/19 16:33:57 INFO Current state expectedStored=0,…,2 expectedActive=0,…,2 existingBatches=0 -2024/01/19 16:33:57 INFO To issue batches=1,2 +2025/04/23 17:12:45 INFO Starting issuance time=2025-04-23T15:12:45.869Z +2025/04/23 17:12:45 INFO Current state expectedStored=0,…,2 expectedActive=0,…,2 existingBatches=0 +2025/04/23 17:12:45 INFO To issue batches=1,2 $ find . +``` . ./signing.key ./www ./www/mtc -./www/mtc/v1 -./www/mtc/v1/ca-params -./www/mtc/v1/batches -./www/mtc/v1/batches/0 -./www/mtc/v1/batches/0/tree -./www/mtc/v1/batches/0/abridged-assertions -./www/mtc/v1/batches/0/signed-validity-window -./www/mtc/v1/batches/0/index -./www/mtc/v1/batches/latest -./www/mtc/v1/batches/1 -./www/mtc/v1/batches/1/tree -./www/mtc/v1/batches/1/abridged-assertions -./www/mtc/v1/batches/1/signed-validity-window -./www/mtc/v1/batches/1/index -./www/mtc/v1/batches/2 -./www/mtc/v1/batches/2/tree -./www/mtc/v1/batches/2/abridged-assertions -./www/mtc/v1/batches/2/signed-validity-window -./www/mtc/v1/batches/2/index +./www/mtc/v04b +./www/mtc/v04b/ca-params +./www/mtc/v04b/batches +./www/mtc/v04b/batches/0 +./www/mtc/v04b/batches/0/validity-window +./www/mtc/v04b/batches/0/tree +./www/mtc/v04b/batches/0/entries +./www/mtc/v04b/batches/0/evidence +./www/mtc/v04b/batches/0/latest +./www/mtc/v04b/batches/0/index +./www/mtc/v04b/batches/latest +./www/mtc/v04b/batches/1 +./www/mtc/v04b/batches/1/validity-window +./www/mtc/v04b/batches/1/tree +./www/mtc/v04b/batches/1/entries +./www/mtc/v04b/batches/1/evidence +./www/mtc/v04b/batches/1/index +./www/mtc/v04b/batches/2 +./www/mtc/v04b/batches/2/validity-window +./www/mtc/v04b/batches/2/tree +./www/mtc/v04b/batches/2/entries +./www/mtc/v04b/batches/2/evidence +./www/mtc/v04b/batches/2/index ./queue ./tmp ``` As we waited a bit longer, the current batch is `2`, which will contain the queued assertions. The batch `1` in between will be empty. - Now `latest` points to `2`, and its signed validity window is more interesting. ``` -$ mtc inspect -ca-params www/mtc/v1/ca-params signed-validity-window www/mtc/v1/batches/2/signed-validity-window +$ mtc inspect -ca-params www/mtc/v04b/ca-params validity-window www/mtc/v04b/batches/1/validity-window signature ✅ batch_number 2 -tree_heads[-9] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-8] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-7] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-6] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-5] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-4] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-3] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-2] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[-1] f2f65b0486c8cad3876475c9c509afdf3f51dc073b1d2d2d261ff9883d63f98e -tree_heads[0] c005dcdb53c4e41befcf3a294b815d8b8aa0a260e9f10bfd4e4cb52eb3724aa3 -tree_heads[1] 98a421741cf06a19b56d7b52436f686885bd798611426f638ffcdb6b5a65c42c -tree_heads[2] ab3cb1262fc084be0447c2b3d175d63f6ec2782dcc1443888b12f685976093d5 +tree_heads[2] 03a95ba3c354e2b0eb4bea9b111dbc8b97e2c90b85ddcc63d4b635b16f77005d +tree_heads[1] 7ceda88ec6c8e34ecacde47588e2605fb86192b94ca96cb897fa6ff442198c8c +tree_heads[0] 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345 +tree_heads[-1] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-2] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-3] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-4] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-5] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-6] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-7] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-8] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf +tree_heads[-9] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf ``` ### Creating a certificate -In MTC, a **certificate** is an assertion, -together with the TrustAnchorIdentifier (consisting of an OID for the CA and the batch number), -and an authentication path in the Merkle tree. +In MTC, a **certificate** is an assertion together with a trust anchor identifier +(to identify the CA), and an authentication path in the Merkle tree. Let's create one for our initial assertion. ``` -$ mtc ca cert -i my-assertion -o my-cert +$ mtc ca cert -i my-asr -o my-cert ``` -If we inspect the certificate, it can recompute the root from the authentication path and CA parameters: +If we inspect the certificate, it can recompute the root from the +authentication path and CA parameters: ``` -$ mtc inspect -ca-params www/mtc/v1/ca-params cert my-cert +$ mtc inspect -ca-params www/mtc/v04b/ca-params cert my-cert subject_type TLS signature_scheme p256 -public_key_hash a02a1758e4c9d6511dc02f59301b9f29e41762d3d769c87a22333497984a41ef +public_key_hash 20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a dns [example.com] ip4 [198.51.100.60] -proof_type merkle_tree_sha256 -CA OID 62253.12.15 -batch 0 -index 0 -recomputed root c005dcdb53c4e41befcf3a294b815d8b8aa0a260e9f10bfd4e4cb52eb3724aa3 +proof_type merkle_tree_sha256 +CA OID 62253.12.15 +Batch number 0 +index 1 +recomputed tree head 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345 authentication path - 00b17df8d909fd3e77005486a16ca00fdc9af38f92a23351359fd420d9f2ef78 + 8964f010faa9e499b21917f8792b541b7b1ac19f313a5d53094c698c2edc330b ``` -This is indeed the root of the `0`th batch, and so this certificate is valid. +This is indeed the root of batch `0`, and so this certificate is valid. -### Run CA server +### Run CA as server -Run an HTTP server to serve static files, accept queue requests, periodically -issue new batches of certificate, and serve issued certificates. +An Merkle Tree CA can be run just from the commandline, but it's often +more convenient to run it as a server. To start the server, run: -Start the server. ``` -$ mtc ca --ca-path . serve --listen-addr localhost:8080 +$ mtc ca serve -listen-addr localhost:8080 ``` +This will accept HTTP requests on `localhost:8080` and serve the static +files. It will also accept queue requests; periodically issue new batches; +and return issued certificates. + Get and inspect CA parameters. + ``` -$ curl -X GET "http://localhost:8080/static/mtc/v1/ca-params" -o ca-params +$ curl -s "http://localhost:8080/mtc/v04b/ca-params" -o ca-params $ mtc inspect ca-params ca-params -issuer 123.4.5 -start_time 1739593848 2025-02-14 23:30:48 -0500 EST -batch_duration 300 5m0s -life_time 3600 1h0m0s -storage_window_size 24 2h0m0s -validity_window_size 12 -server_prefix ca.example.com/path -public_key fingerprint ml-dsa-87:be1903a366b462b7b4e0010120d4b38279bbf4e350559b95e93671dbc4b821fc +issuer 62253.12.15 +start_time 1745420554 2025-04-23 15:02:34 +0000 UTC +batch_duration 300 5m0s +life_time 3600 1h0m0s +storage_window_size 24 2h0m0s +validity_window_size 12 +server_prefix ca.example.com/path +public_key fingerprint ml-dsa-87:84489bcb42b411a85d163ae95e7deb92b106a75840819a985e44d0e01ae3238e ``` Queue up the assertion created in above. + ``` -$ curl -X POST "http://localhost:8080/ca/queue" --data-binary "@my-assertion" -w "%{http_code}" +$ curl -X POST "http://localhost:8080/ca/queue" --data-binary "@my-asr" -w "%{http_code}" 200 ``` -Wait 5 minutes for the next batch and retrieve the certificate from the CA server. +After it's been issued, we can get the certificate via the `/ca/cert` endpoint: + ``` -$ curl -X POST "http://localhost:8080/ca/cert" --data-binary "@my-assertion" -o my-cert +$ curl -X POST "http://localhost:8080/ca/cert" --data-binary "@my-asr" -o my-cert $ mtc inspect -ca-params ca-params cert my-cert -subject_type TLS +subject_type TLS signature_scheme p256 -public_key_hash f3ee2efd8bc3dd01ab924d12ee0bc5661866a4b147fcdc6881b5455c9084c973 -dns [example.com] -ip4 [198.51.100.60] -proof_type merkle_tree_sha256 -CA OID 123.4.5 -Batch number 991 -index 0 -recomputed root 27a893fa1f864f97b2c2073f784bfd6bbcd1b2deb3d8aafbdd18b09ac10bc430 +public_key_hash 20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a +dns [example.com] +ip4 [198.51.100.60] + +proof_type merkle_tree_sha256 +CA OID 62253.12.15 +Batch number 0 +index 1 +recomputed tree head 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345 authentication path + 8964f010faa9e499b21917f8792b541b7b1ac19f313a5d53094c698c2edc330b ```