├── .github ├── workflows │ ├── LICENSE.md │ ├── update.yml │ ├── archive.yml │ ├── ghpages.yml │ └── publish.yml └── CODEOWNERS ├── package.json ├── go.mod ├── LICENSE.md ├── go.sum ├── .editorconfig ├── Makefile ├── .gitignore ├── README.md ├── CONTRIBUTING.md ├── charter-ietf-plants.md ├── estimate_tile_size.go └── draft-davidben-tls-merkle-tree-certs.md /.github/workflows/LICENSE.md: -------------------------------------------------------------------------------- 1 | This project is in the public domain. 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "aasvg": "^0.4.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/davidben/merkle-tree-certs 2 | 3 | go 1.24.6 4 | 5 | require golang.org/x/crypto v0.41.0 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | See the 4 | [guidelines for contributions](https://github.com/davidben/merkle-tree-certs/blob/main/CONTRIBUTING.md). 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Automatically generated CODEOWNERS 2 | # Regenerate with `make update-codeowners` 3 | draft-davidben-tls-merkle-tree-certs.md davidben@google.com 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= 2 | golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # See http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*.{md,xml,org}] 6 | charset = utf-8 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBDIR := lib 2 | include $(LIBDIR)/main.mk 3 | 4 | $(LIBDIR)/main.mk: 5 | ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null)) 6 | git submodule sync 7 | git submodule update $(CLONE_ARGS) --init 8 | else 9 | git clone -q --depth 10 $(CLONE_ARGS) \ 10 | -b main https://github.com/martinthomson/i-d-template $(LIBDIR) 11 | endif 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.pdf 3 | *.redxml 4 | *.swp 5 | *.txt 6 | *.upload 7 | *~ 8 | .tags 9 | /*-[0-9][0-9].xml 10 | /.*.mk 11 | /.gems/ 12 | /.refcache 13 | /.venv/ 14 | /.vscode/ 15 | /lib 16 | /node_modules/ 17 | /versioned/ 18 | Gemfile.lock 19 | archive.json 20 | draft-davidben-tls-merkle-tree-certs.xml 21 | package-lock.json 22 | report.xml 23 | !requirements.txt 24 | -------------------------------------------------------------------------------- /.github/workflows/update.yml: -------------------------------------------------------------------------------- 1 | name: "Update Generated Files" 2 | # This rule is not run automatically. 3 | # It can be run manually to update all of the files that are part 4 | # of the template, specifically: 5 | # - README.md 6 | # - CONTRIBUTING.md 7 | # - .note.xml 8 | # - .github/CODEOWNERS 9 | # - Makefile 10 | # 11 | # 12 | # This might be useful if you have: 13 | # - added, removed, or renamed drafts (including after adoption) 14 | # - added, removed, or changed draft editors 15 | # - changed the title of drafts 16 | # 17 | # Note that this removes any customizations you have made to 18 | # the affected files. 19 | on: workflow_dispatch 20 | 21 | jobs: 22 | build: 23 | name: "Update Files" 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: "Checkout" 27 | uses: actions/checkout@v4 28 | 29 | - name: "Update Generated Files" 30 | uses: martinthomson/i-d-template@v1 31 | with: 32 | make: update-files 33 | token: ${{ github.token }} 34 | 35 | - name: "Push Update" 36 | run: git push 37 | -------------------------------------------------------------------------------- /.github/workflows/archive.yml: -------------------------------------------------------------------------------- 1 | name: "Archive Issues and Pull Requests" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 0,2,4' 6 | repository_dispatch: 7 | types: [archive] 8 | workflow_dispatch: 9 | inputs: 10 | archive_full: 11 | description: 'Recreate the archive from scratch' 12 | default: false 13 | type: boolean 14 | 15 | jobs: 16 | build: 17 | name: "Archive Issues and Pull Requests" 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: write 21 | steps: 22 | - name: "Checkout" 23 | uses: actions/checkout@v4 24 | 25 | # Note: No caching for this build! 26 | 27 | - name: "Update Archive" 28 | uses: martinthomson/i-d-template@v1 29 | env: 30 | ARCHIVE_FULL: ${{ inputs.archive_full }} 31 | with: 32 | make: archive 33 | token: ${{ github.token }} 34 | 35 | - name: "Update GitHub Pages" 36 | uses: martinthomson/i-d-template@v1 37 | with: 38 | make: gh-archive 39 | token: ${{ github.token }} 40 | 41 | - name: "Save Archive" 42 | uses: actions/upload-artifact@v4 43 | with: 44 | path: archive.json 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Merkle Tree Certificates 2 | 3 | This is the working area for the individual Internet-Draft, "Merkle Tree Certificates". 4 | 5 | * [Editor's Copy](https://davidben.github.io/merkle-tree-certs/#go.draft-davidben-tls-merkle-tree-certs.html) 6 | * [Datatracker Page](https://datatracker.ietf.org/doc/draft-davidben-tls-merkle-tree-certs) 7 | * [Individual Draft](https://datatracker.ietf.org/doc/html/draft-davidben-tls-merkle-tree-certs) 8 | * [Compare Editor's Copy to Individual Draft](https://davidben.github.io/merkle-tree-certs/#go.draft-davidben-tls-merkle-tree-certs.diff) 9 | 10 | 11 | ## Contributing 12 | 13 | See the 14 | [guidelines for contributions](https://github.com/davidben/merkle-tree-certs/blob/main/CONTRIBUTING.md). 15 | 16 | Contributions can be made by creating pull requests. 17 | The GitHub interface supports creating pull requests using the Edit (✏) button. 18 | 19 | 20 | ## Command Line Usage 21 | 22 | Formatted text and HTML versions of the draft can be built using `make`. 23 | 24 | ```sh 25 | $ make 26 | ``` 27 | 28 | Command line usage requires that you have the necessary software installed. See 29 | [the instructions](https://github.com/martinthomson/i-d-template/blob/main/doc/SETUP.md). 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: "Update Editor's Copy" 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - README.md 7 | - CONTRIBUTING.md 8 | - LICENSE.md 9 | - .gitignore 10 | pull_request: 11 | paths-ignore: 12 | - README.md 13 | - CONTRIBUTING.md 14 | - LICENSE.md 15 | - .gitignore 16 | 17 | jobs: 18 | build: 19 | name: "Update Editor's Copy" 20 | runs-on: ubuntu-latest 21 | permissions: 22 | contents: write 23 | steps: 24 | - name: "Checkout" 25 | uses: actions/checkout@v4 26 | 27 | - name: "Setup" 28 | id: setup 29 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" 30 | 31 | - name: "Caching" 32 | uses: actions/cache@v4 33 | with: 34 | path: | 35 | .refcache 36 | .venv 37 | .gems 38 | node_modules 39 | .targets.mk 40 | key: i-d-${{ steps.setup.outputs.date }} 41 | restore-keys: i-d- 42 | 43 | - name: "Build Drafts" 44 | uses: martinthomson/i-d-template@v1 45 | with: 46 | token: ${{ github.token }} 47 | 48 | - name: "Update GitHub Pages" 49 | uses: martinthomson/i-d-template@v1 50 | if: ${{ github.event_name == 'push' }} 51 | with: 52 | make: gh-pages 53 | token: ${{ github.token }} 54 | 55 | - name: "Archive Built Drafts" 56 | uses: actions/upload-artifact@v4 57 | with: 58 | path: | 59 | draft-*.html 60 | draft-*.txt 61 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Publish New Draft Version" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "draft-*" 7 | workflow_dispatch: 8 | inputs: 9 | email: 10 | description: "Submitter email" 11 | default: "" 12 | type: string 13 | 14 | jobs: 15 | build: 16 | name: "Publish New Draft Version" 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: "Checkout" 20 | uses: actions/checkout@v4 21 | 22 | # See https://github.com/actions/checkout/issues/290 23 | - name: "Get Tag Annotations" 24 | run: git fetch -f origin ${{ github.ref }}:${{ github.ref }} 25 | 26 | - name: "Setup" 27 | id: setup 28 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" 29 | 30 | - name: "Caching" 31 | uses: actions/cache@v4 32 | with: 33 | path: | 34 | .refcache 35 | .venv 36 | .gems 37 | node_modules 38 | .targets.mk 39 | key: i-d-${{ steps.setup.outputs.date }} 40 | restore-keys: i-d- 41 | 42 | - name: "Build Drafts" 43 | uses: martinthomson/i-d-template@v1 44 | with: 45 | token: ${{ github.token }} 46 | 47 | - name: "Upload to Datatracker" 48 | uses: martinthomson/i-d-template@v1 49 | with: 50 | make: upload 51 | env: 52 | UPLOAD_EMAIL: ${{ inputs.email }} 53 | 54 | - name: "Archive Submitted Drafts" 55 | uses: actions/upload-artifact@v4 56 | with: 57 | path: "versioned/draft-*-[0-9][0-9].*" 58 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repository relates to activities in the Internet Engineering Task Force 4 | ([IETF](https://www.ietf.org/)). All material in this repository is considered 5 | Contributions to the IETF Standards Process, as defined in the intellectual 6 | property policies of IETF currently designated as 7 | [BCP 78](https://www.rfc-editor.org/info/bcp78), 8 | [BCP 79](https://www.rfc-editor.org/info/bcp79) and the 9 | [IETF Trust Legal Provisions (TLP) Relating to IETF Documents](http://trustee.ietf.org/trust-legal-provisions.html). 10 | 11 | Any edit, commit, pull request, issue, comment or other change made to this 12 | repository constitutes Contributions to the IETF Standards Process 13 | (https://www.ietf.org/). 14 | 15 | You agree to comply with all applicable IETF policies and procedures, including, 16 | BCP 78, 79, the TLP, and the TLP rules regarding code components (e.g. being 17 | subject to a Simplified BSD License) in Contributions. 18 | ## Working Group Information 19 | 20 | Discussion of this work occurs on the [PKI, Logs, And Tree Signatures mailing list](mailto:plants@ietf.org) 21 | ([archive](https://mailarchive.ietf.org/arch/browse/plants/), 22 | [subscribe](https://www.ietf.org/mailman/listinfo/plants)). 23 | In addition to contributions in GitHub, you are encouraged to participate in 24 | discussions there. 25 | 26 | **Note**: Some working groups adopt a policy whereby substantive discussion of 27 | technical issues needs to occur on the mailing list. 28 | 29 | You might also like to familiarize yourself with other 30 | [Working Group documents](https://datatracker.ietf.org/wg/plants/documents/). 31 | -------------------------------------------------------------------------------- /charter-ietf-plants.md: -------------------------------------------------------------------------------- 1 | # Draft PLANTS Charter 2 | 3 | This is a draft charter for a new IETF working group. The working group does not yet exist, so references to the "PLANTS Working Group" are aspirational. Discussion at the [IETF PLANTS mailing list](https://mailman3.ietf.org/mailman3/lists/plants.ietf.org/). 4 | 5 | ## PKI, Logs, And Tree Signatures (PLANTS) 6 | 7 | The goal of the PLANTS Working Group is to trim the increasing costs of operating PKIs with Certificate Transparency (CT; RFC 6962 and RFC 9162), when used in interactive protocols like TLS (RFC 8446). 8 | 9 | Today, such applications apply two separate systems: a certification authority (CA) signs individual bindings between public keys and application identifiers (e.g., a DNS name), returning an X.509 certificate. CT logs then log entire certificates, returning signed certificate timestamps. The outputs of these two steps are presented to the relying party. 10 | 11 | Overhead from post-quantum signatures and keys will add significant costs in two ways: 12 | 13 | * Each log entry contains an entire certificate, with public key and signature. Post-quantum overhead is multiplied across every entry, increasing the costs to log operators and the rest of the transparency ecosystem. 14 | 15 | * Relying parties are presented with signatures from the CA and CT logs. Post-quantum overhead is multiplied per signature, increasing the size and latency of the TLS handshake. 16 | 17 | Additionally, more entities are requesting certificates, and certificate validity windows are narrowing, both of which increase the overall size of a CT log, with corresponding costs for log operators. 18 | 19 | The PLANTS Working Group will define a mechanism that integrates log construction into certificate issuance, and reduces the amount of data in individual log entries and the TLS handshake. Integrating the log into certificate issuance enables techniques where one signature can cover multiple key/identifier bindings, e.g., by signing Merkle Tree hashes. 20 | 21 | The Working Group will initially put down roots and define the mechanisms needed to interoperably construct and consume certificates: 22 | 23 | 1. An extensible and externally monitorable transparency log structure, maintained by a CA, containing the key/identifier bindings that the CA has certified. 24 | 25 | 2. Certificate constructions to prove to relying parties that a binding is both in the CA's view of the log and externally monitorable. 26 | 27 | 3. How the certificate constructions may be provisioned (with mechanisms including ACME (RFC 8555)) and used in TLS. 28 | 29 | As part of this work, the Working Group may extend PKIX (RFC 5280), e.g., with new extensions or signature algorithms. As appropriate, the PLANTS Working Group will liaise with the LAMPS Working Group to ensure adequate lighting for this work and help it grow. As needed, the Working Group may also define extensions to ACME and TLS to integrate its certificate constructions. In doing so, it is expected to liaise with the TLS and ACME Working Groups for cross-pollination. 30 | 31 | Though not the initial focus, the PLANTS Working Group may consider other properties of transparent PKIs to improve upon the status quo, such as auditing, monitoring, or revocation. If feasible concrete improvements are identified, the Working Group may recharter to seed secondary deliverables that build on its initial work. 32 | 33 | In evaluating decisions and design tradeoffs, the Working Group will consider security, privacy, transparency, performance, and deployment properties, aiming to comparably meet the needs of today's applications that use CT-based PKIs with TLS. In particular the WG will deliver a design that can ensure that a misbehavior by a single party cannot compromise transparency for relying parties. The Working Group may consider and explain how these mechanisms could be adapted for deployment in a private or limited scope PKI (potentially without transparency) as a secondary use case. 34 | 35 | The PLANTS Working Group's scope is to explore mechanisms for CAs and transparency ecosystems to certify key/identifier bindings in a publicly monitorable way. Alternate trust models and changes to how TLS uses the end-entity key are not in scope for the Working Group. 36 | 37 | The Working Group will not submit specifications for publication to the IESG before demonstrating two interoperable implementations. 38 | -------------------------------------------------------------------------------- /estimate_tile_size.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "crypto/sha256" 7 | "crypto/x509" 8 | "crypto/x509/pkix" 9 | "encoding/asn1" 10 | "errors" 11 | "flag" 12 | "fmt" 13 | "io" 14 | "math/rand/v2" 15 | "net/http" 16 | "net/url" 17 | "os" 18 | "slices" 19 | "strconv" 20 | "strings" 21 | "time" 22 | 23 | "golang.org/x/crypto/cryptobyte" 24 | cbasn1 "golang.org/x/crypto/cryptobyte/asn1" 25 | ) 26 | 27 | const ( 28 | logEntryTypeX509 = 0 29 | logEntryTypePrecert = 1 30 | 31 | fullTileSize = 256 32 | 33 | tbsCertEntry = 1 34 | ) 35 | 36 | var ( 37 | flagURL = flag.String("url", "", "the URL of the log to sample") 38 | flagSamples = flag.Int("samples", 5, "number of samples to run") 39 | flagFilterKeyIDs = flag.Bool("filter-key-id", false, "filter out SKID and AKID extensions") 40 | flagFilterAIA = flag.Bool("filter-aia", false, "filter out AIA extension") 41 | flagPQEmbeddedSCTs = flag.Bool("pq-embedded-scts", false, "simulate embedded SCTs getting upgraded to post-quantum") 42 | flagPQAlg = flag.String("pq-alg", "ML-DSA-44", "the PQ algorithm to simulate") 43 | 44 | // Put together some placeholder value based on https://www.ietf.org/archive/id/draft-davidben-tls-merkle-tree-certs-06.html#name-log-ids 45 | placeholderIssuer = []byte{0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x00, 0x00, 0x0d, 0x04, 0xd6, 0x79, 0x09, 0x01} 46 | ) 47 | 48 | type pqAlgorithm struct { 49 | publicKeySize int 50 | signatureSize int 51 | } 52 | 53 | func getPQAlgorithm(alg string) (pqAlgorithm, bool) { 54 | switch alg { 55 | case "ML-DSA-44": 56 | return pqAlgorithm{publicKeySize: 1312, signatureSize: 2420}, true 57 | case "ML-DSA-65": 58 | return pqAlgorithm{publicKeySize: 1952, signatureSize: 3309}, true 59 | case "ML-DSA-87": 60 | return pqAlgorithm{publicKeySize: 2592, signatureSize: 4627}, true 61 | } 62 | return pqAlgorithm{}, false 63 | } 64 | 65 | // Extracts leaf certificates from a data tile, as defined in https://c2sp.org/static-ct-api 66 | func parseDataTile(tile []byte, n int) ([]*x509.Certificate, error) { 67 | certs := make([]*x509.Certificate, n) 68 | in := cryptobyte.String(tile) 69 | for i := range n { 70 | var logEntryType uint16 71 | if !in.Skip(8) || 72 | !in.ReadUint16(&logEntryType) { 73 | return nil, errors.New("could not parse tile leaf") 74 | } 75 | 76 | var skip, cert cryptobyte.String 77 | switch logEntryType { 78 | case logEntryTypeX509: 79 | if !in.ReadUint24LengthPrefixed(&cert) || 80 | !in.ReadUint16LengthPrefixed(&skip) /* ext */ || 81 | !in.ReadUint16LengthPrefixed(&skip) /* cert chain */ { 82 | return nil, errors.New("could not parse tile leaf") 83 | } 84 | case logEntryTypePrecert: 85 | if !in.Skip(32) /* hash */ || 86 | !in.ReadUint24LengthPrefixed(&skip) /* tbs */ || 87 | !in.ReadUint16LengthPrefixed(&skip) /* ext */ || 88 | !in.ReadUint24LengthPrefixed(&cert) || 89 | !in.ReadUint16LengthPrefixed(&skip) /* cert chain */ { 90 | return nil, errors.New("could not parse tile leaf") 91 | } 92 | default: 93 | return nil, errors.New("unknown log entry type") 94 | } 95 | 96 | c, err := x509.ParseCertificate(cert) 97 | if err != nil { 98 | return nil, err 99 | } 100 | certs[i] = c 101 | } 102 | if !in.Empty() { 103 | return nil, errors.New("excess data in data tile") 104 | } 105 | return certs, nil 106 | } 107 | 108 | func addX509Time(bb *cryptobyte.Builder, t time.Time) { 109 | t = t.UTC() 110 | if y := t.Year(); 1950 <= y && y <= 2049 { 111 | bb.AddASN1UTCTime(t) 112 | } else { 113 | bb.AddASN1GeneralizedTime(t) 114 | } 115 | } 116 | 117 | var ( 118 | oidSubjectKeyId = asn1.ObjectIdentifier{2, 5, 29, 14} 119 | oidAuthorityKeyId = asn1.ObjectIdentifier{2, 5, 29, 35} 120 | oidAuthorityInformationAccess = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 1} 121 | oidSCTExtension = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2} 122 | ) 123 | 124 | // Encodes a representative TBSCertificateLogEntry from an X.509 Certificate, as defined in draft-davidben-tls-merkle-tree-certs-06. 125 | func tbsCertLogEntryFromCert(cert *x509.Certificate) []byte { 126 | b := cryptobyte.NewBuilder(nil) 127 | b.AddUint16(tbsCertEntry) 128 | b.AddASN1(cbasn1.SEQUENCE, func(tbs *cryptobyte.Builder) { 129 | tbs.AddASN1(cbasn1.Tag(0).Constructed().ContextSpecific(), func(vers *cryptobyte.Builder) { 130 | vers.AddASN1Int64(2) 131 | }) 132 | tbs.AddBytes(placeholderIssuer) 133 | tbs.AddASN1(cbasn1.SEQUENCE, func(val *cryptobyte.Builder) { 134 | addX509Time(val, cert.NotBefore) 135 | addX509Time(val, cert.NotAfter) 136 | }) 137 | tbs.AddBytes(cert.RawSubject) 138 | hash := sha256.Sum256(cert.RawSubjectPublicKeyInfo) 139 | tbs.AddASN1OctetString(hash[:]) 140 | tbs.AddASN1(cbasn1.Tag(3).Constructed().ContextSpecific(), func(exts *cryptobyte.Builder) { 141 | certExts := slices.DeleteFunc(slices.Clone(cert.Extensions), func(ext pkix.Extension) bool { 142 | // Today's CT logs often log certificates with embedded SCTs due to an awkward parts 143 | // of how CT was designed. These would not exist at all in MTCs. 144 | if ext.Id.Equal(oidSCTExtension) { 145 | return true 146 | } 147 | // SKIDs and AKIDs only exist to aid path-building when one CA name corresponds to 148 | // multiple keys. MTC fixes this at the source and requires a distinct CA name, so 149 | // PKIs can reasonably say to omit them. 150 | if *flagFilterKeyIDs && (ext.Id.Equal(oidSubjectKeyId) || ext.Id.Equal(oidAuthorityKeyId)) { 151 | return true 152 | } 153 | // AIA is only needed to deal with a host of issues around path-building, which we 154 | // can fix at the source with negotiation. 155 | if *flagFilterAIA && ext.Id.Equal(oidAuthorityInformationAccess) { 156 | return true 157 | } 158 | return false 159 | }) 160 | der, err := asn1.Marshal(certExts) 161 | if err != nil { 162 | panic(err) 163 | } 164 | exts.AddBytes(der) 165 | }) 166 | }) 167 | return b.BytesOrPanic() 168 | } 169 | 170 | type embeddedSCTInfo struct { 171 | numSCTs int 172 | totSig int 173 | } 174 | 175 | func parseEmbeddedSCTs(cert *x509.Certificate) (embeddedSCTInfo, error) { 176 | var info embeddedSCTInfo 177 | var ext *pkix.Extension 178 | for i := range cert.Extensions { 179 | if cert.Extensions[i].Id.Equal(oidSCTExtension) { 180 | ext = &cert.Extensions[i] 181 | break 182 | } 183 | } 184 | if ext == nil { 185 | return info, nil 186 | } 187 | 188 | value := cryptobyte.String(ext.Value) 189 | var sctList, scts cryptobyte.String 190 | if !value.ReadASN1(&sctList, cbasn1.OCTET_STRING) || !value.Empty() || 191 | !sctList.ReadUint16LengthPrefixed(&scts) || !sctList.Empty() || 192 | scts.Empty() { 193 | return embeddedSCTInfo{}, fmt.Errorf("error parsing SCT extension") 194 | } 195 | for !scts.Empty() { 196 | var sct, ctExts, sig cryptobyte.String 197 | if !scts.ReadUint16LengthPrefixed(&sct) || 198 | !sct.Skip(1+32+8) || // version, id, timestamp 199 | !sct.ReadUint16LengthPrefixed(&ctExts) || 200 | !sct.Skip(2) || // sigalg 201 | !sct.ReadUint16LengthPrefixed(&sig) || 202 | !sct.Empty() { 203 | return embeddedSCTInfo{}, fmt.Errorf("error parsing SCT extension") 204 | } 205 | info.numSCTs++ 206 | info.totSig += len(sig) 207 | } 208 | return info, nil 209 | } 210 | 211 | func fetchTreeSize(baseURL *url.URL) (int, error) { 212 | resp, err := http.Get(baseURL.JoinPath("checkpoint").String()) 213 | if err != nil { 214 | return 0, err 215 | } 216 | if resp.StatusCode != 200 { 217 | return 0, fmt.Errorf("got non-200 status %d from checkpoint", resp.StatusCode) 218 | } 219 | defer resp.Body.Close() 220 | body, err := io.ReadAll(resp.Body) 221 | if err != nil { 222 | return 0, err 223 | } 224 | lines := strings.Split(string(body), "\n") 225 | if len(lines) < 2 { 226 | return 0, fmt.Errorf("bad checkpoint") 227 | } 228 | return strconv.Atoi(lines[1]) 229 | } 230 | 231 | func encodeIndex(idx int) string { 232 | ret := fmt.Sprintf("%03d", idx%1000) 233 | idx /= 1000 234 | for idx != 0 { 235 | ret = fmt.Sprintf("x%03d/", idx%1000) + ret 236 | idx /= 1000 237 | } 238 | return ret 239 | } 240 | 241 | func fetchDataTile(baseURL *url.URL, idx, size int) ([]byte, error) { 242 | baseURL = baseURL.JoinPath("tile", "data") 243 | if size == fullTileSize { 244 | baseURL = baseURL.JoinPath(encodeIndex(idx)) 245 | } else { 246 | baseURL = baseURL.JoinPath(encodeIndex(idx)+".p", strconv.Itoa(size)) 247 | } 248 | resp, err := http.Get(baseURL.String()) 249 | if err != nil { 250 | return nil, err 251 | } 252 | if resp.StatusCode != 200 { 253 | return nil, fmt.Errorf("got non-200 status %d from data tile fetch", resp.StatusCode) 254 | } 255 | defer resp.Body.Close() 256 | return io.ReadAll(resp.Body) 257 | } 258 | 259 | func gzipSize(in []byte) int { 260 | var b bytes.Buffer 261 | w := gzip.NewWriter(&b) 262 | if _, err := w.Write(in); err != nil { 263 | panic(err) 264 | } 265 | if err := w.Flush(); err != nil { 266 | panic(err) 267 | } 268 | return b.Len() 269 | } 270 | 271 | func percent(a, b int) float64 { 272 | return 100.0 * float64(a) / float64(b) 273 | } 274 | 275 | type tileStats struct { 276 | name string 277 | tot, gzipTot int 278 | } 279 | 280 | func printAvg(s tileStats) { 281 | fmt.Printf("Average %s tile: %.2f bytes (%.2f bytes gzipped)\n", s.name, float64(s.tot)/float64(*flagSamples), float64(s.gzipTot)/float64(*flagSamples)) 282 | } 283 | 284 | func compareStats(s1, s2 tileStats) { 285 | fmt.Printf("%s / %s: %.2f%% uncompressed, %.2f%% gzipped\n", s1.name, s2.name, percent(s1.tot, s2.tot), percent(s1.gzipTot, s2.gzipTot)) 286 | } 287 | 288 | func ifElse[T any](b bool, ifTrue, ifFalse T) T { 289 | if b { 290 | return ifTrue 291 | } else { 292 | return ifFalse 293 | } 294 | } 295 | 296 | func do() error { 297 | if len(*flagURL) == 0 { 298 | return errors.New("no log supplied, see https://googlechrome.github.io/CertificateTransparency/log_lists.html for available logs (must be tiled)") 299 | } 300 | baseURL, err := url.Parse(*flagURL) 301 | if err != nil { 302 | return err 303 | } 304 | if !baseURL.IsAbs() { 305 | return errors.New("not a valid URL") 306 | } 307 | pqAlg, ok := getPQAlgorithm(*flagPQAlg) 308 | if !ok { 309 | return fmt.Errorf("unknown post-quantum algorithm: %s", *flagPQAlg) 310 | } 311 | 312 | fmt.Printf("Sampling %d tile%s from log %s\n", *flagSamples, ifElse(*flagSamples == 1, "", "s"), *flagURL) 313 | 314 | treeSize, err := fetchTreeSize(baseURL) 315 | if err != nil { 316 | return err 317 | } 318 | fmt.Printf("Tree size: %d\n", treeSize) 319 | 320 | ctStats := tileStats{name: "CT"} 321 | mtcStats := tileStats{name: "MTC"} 322 | pqCTStats := tileStats{name: "PQ CT"} 323 | 324 | for range *flagSamples { 325 | numFullTiles := treeSize / fullTileSize 326 | 327 | sample := rand.IntN(numFullTiles) 328 | tileSize := fullTileSize 329 | fmt.Printf("Sampling tile %d\n", sample) 330 | 331 | tile, err := fetchDataTile(baseURL, sample, tileSize) 332 | if err != nil { 333 | return err 334 | } 335 | // For a fair comparison, redo the gzip rather than reuse the original 336 | // log's gzip, so that CT and MTC tiles have the same gzip settings. 337 | ctGzipSize := gzipSize(tile) 338 | 339 | certs, err := parseDataTile(tile, tileSize) 340 | if err != nil { 341 | return err 342 | } 343 | 344 | pqIncrease := 0 345 | b := cryptobyte.NewBuilder(nil) 346 | for _, cert := range certs { 347 | scts, err := parseEmbeddedSCTs(cert) 348 | if err != nil { 349 | return err 350 | } 351 | 352 | // As a very, very rough estimate of the status quo with PQ, 353 | // simulate replacing the leaf SPKI, leaf signature, and embedded 354 | // SCT signatures with the chosen PQ algorithm. 355 | pqIncrease += pqAlg.publicKeySize - len(cert.RawSubjectPublicKeyInfo) 356 | pqIncrease += pqAlg.signatureSize - len(cert.Signature) 357 | if *flagPQEmbeddedSCTs { 358 | pqIncrease += scts.numSCTs*pqAlg.signatureSize - scts.totSig 359 | } 360 | 361 | // Construct the MTC tiles. 362 | entry := tbsCertLogEntryFromCert(cert) 363 | b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { 364 | child.AddBytes(entry) 365 | }) 366 | } 367 | mtcTile := b.BytesOrPanic() 368 | mtcGzipSize := gzipSize(mtcTile) 369 | 370 | ctStats.tot += len(tile) 371 | ctStats.gzipTot += ctGzipSize 372 | // Assume that keys and signatures are incompressible, so just add it to 373 | // the gzip sizes. 374 | pqCTStats.tot += len(tile) + pqIncrease 375 | pqCTStats.gzipTot += ctGzipSize + pqIncrease 376 | mtcStats.tot += len(mtcTile) 377 | mtcStats.gzipTot += mtcGzipSize 378 | } 379 | 380 | fmt.Printf("\n") 381 | printAvg(ctStats) 382 | printAvg(pqCTStats) 383 | fmt.Printf(" Simulated by replacing public keys and signatures with %s\n", *flagPQAlg) 384 | fmt.Printf(" Embedded SCT signatures %s replaced\n", ifElse(*flagPQEmbeddedSCTs, "also", "not")) 385 | printAvg(mtcStats) 386 | fmt.Printf(" Simulated by converting CT tiles to MTC tiles\n") 387 | fmt.Printf(" AIA extensions %sremoved (AIA is unnecessary without path-building)\n", ifElse(*flagFilterAIA, "", "not ")) 388 | fmt.Printf(" SKID/AKID extensions %sremoved (SKID/AKID are unnecessary without path-building)\n", ifElse(*flagFilterKeyIDs, "", "not ")) 389 | fmt.Printf("(PQ MTC and MTC tiles have the same size.)\n") 390 | fmt.Printf("\n") 391 | 392 | compareStats(pqCTStats, ctStats) 393 | compareStats(mtcStats, ctStats) 394 | compareStats(mtcStats, pqCTStats) 395 | return nil 396 | } 397 | 398 | func main() { 399 | flag.Parse() 400 | if err := do(); err != nil { 401 | fmt.Fprintf(os.Stderr, "Failed: %s\n", err) 402 | os.Exit(1) 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /draft-davidben-tls-merkle-tree-certs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Merkle Tree Certificates 3 | docname: draft-davidben-tls-merkle-tree-certs-latest 4 | submissiontype: IETF 5 | category: std 6 | number: 7 | date: 8 | consensus: true 9 | v: 3 10 | area: "Security" 11 | venue: 12 | github: "davidben/merkle-tree-certs" 13 | latest: "https://davidben.github.io/merkle-tree-certs/draft-davidben-tls-merkle-tree-certs.html" 14 | 15 | author: 16 | - 17 | ins: "D. Benjamin" 18 | name: "David Benjamin" 19 | organization: "Google LLC" 20 | email: davidben@google.com 21 | 22 | - 23 | ins: "D. O'Brien" 24 | name: "Devon O'Brien" 25 | email: devon.obrien@gmail.com 26 | 27 | - 28 | ins: "B.E. Westerbaan" 29 | name: "Bas Westerbaan" 30 | organization: "Cloudflare" 31 | email: bas@cloudflare.com 32 | 33 | - 34 | ins: "L. Valenta" 35 | name: "Luke Valenta" 36 | organization: "Cloudflare" 37 | email: lvalenta@cloudflare.com 38 | 39 | - 40 | ins: "F. Valsorda" 41 | name: "Filippo Valsorda" 42 | organization: "Geomys" 43 | email: ietf@filippo.io 44 | 45 | normative: 46 | X.690: 47 | title: "Information technology - ASN.1 encoding Rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)" 48 | date: February 2021 49 | author: 50 | org: ITU-T 51 | seriesinfo: 52 | ISO/IEC 8824-1:2021 53 | 54 | # For the ASN.1 module 55 | RFC5912: 56 | 57 | informative: 58 | CHROME-CT: 59 | title: Chrome Certificate Transparency Policy 60 | target: https://googlechrome.github.io/CertificateTransparency/ct_policy.html 61 | date: 2022-03-17 62 | author: 63 | - org: Google Chrome 64 | 65 | APPLE-CT: 66 | title: Apple's Certificate Transparency policy 67 | target: https://support.apple.com/en-us/HT205280 68 | date: 2021-03-05 69 | author: 70 | - org: Apple 71 | 72 | CHROMIUM: 73 | title: Component Updater 74 | target: https://chromium.googlesource.com/chromium/src/+/main/components/component_updater/README.md 75 | date: 2022-03-03 76 | author: 77 | - org: Chromium 78 | 79 | FIREFOX: 80 | title: Firefox Remote Settings 81 | target: https://wiki.mozilla.org/Firefox/RemoteSettings 82 | date: 2022-08-20 83 | author: 84 | - org: Mozilla 85 | 86 | LetsEncrypt: 87 | title: Let's Encrypt Stats 88 | target: https://letsencrypt.org/stats/ 89 | date: 2023-03-07 90 | author: 91 | - org: Let's Encrypt 92 | 93 | MerkleTown: 94 | title: Merkle Town 95 | target: https://ct.cloudflare.com/ 96 | date: 2023-03-07 97 | author: 98 | - org: Cloudflare, Inc. 99 | 100 | SharedFactors: 101 | title: Finding shared RSA factors in the Certificate Transparency logs 102 | target: https://bora.uib.no/bora-xmlui/bitstream/handle/11250/3001128/Masters_thesis__for_University_of_Bergen.pdf 103 | date: 2022-05-13 104 | author: 105 | - name: Henry Faltin Våge 106 | - org: University of Bergen 107 | 108 | STH-Discipline: 109 | title: STH Discipline & Security Considerations 110 | target: https://mailarchive.ietf.org/arch/msg/trans/Zm4NqyRc7LDsOtV56EchBIT9r4c/ 111 | date: 2017-03-03 112 | author: 113 | - name: Richard Barnes 114 | 115 | CABF-153: 116 | title: Ballot 153 – Short-Lived Certificates 117 | target: https://cabforum.org/2015/11/11/ballot-153-short-lived-certificates/ 118 | author: 119 | - org: CA/Browser Forum 120 | date: 2015-11-11 121 | 122 | CABF-SC081: 123 | title: "Ballot SC081v3: Introduce Schedule of Reducing Validity and Data Reuse Periods" 124 | target: https://cabforum.org/2025/04/11/ballot-sc081v3-introduce-schedule-of-reducing-validity-and-data-reuse-periods/ 125 | author: 126 | - org: CA/Browser Forum 127 | date: 2025-04-11 128 | 129 | SCTNotAfter: 130 | title: How to distrust a CA without any certificate errors 131 | target: https://dadrian.io/blog/posts/sct-not-after/ 132 | date: March 6, 2025 133 | author: 134 | - name: David Adrian 135 | 136 | AuditingRevisited: 137 | title: Private SCT Auditing, Revisited 138 | target: https://eprint.iacr.org/2025/556.pdf 139 | date: 2025-04-25 140 | author: 141 | - name: Lena Heimberger 142 | - name: Christopher Patton 143 | - name: Bas Westerbaan 144 | 145 | TLOG-TILES: 146 | title: Tiled Transparency Logs 147 | target: https://c2sp.org/tlog-tiles 148 | date: June 2025 149 | author: 150 | org: C2SP 151 | 152 | TLOG-WITNESS: 153 | title: Transparency Log Witness Protocol 154 | target: https://c2sp.org/tlog-witness 155 | date: June 2025 156 | author: 157 | org: C2SP 158 | 159 | TLOG-MIRROR: 160 | title: Transparency Log Mirrors 161 | target: https://c2sp.org/tlog-mirror 162 | date: July 2025 163 | author: 164 | org: C2SP 165 | 166 | # TODO: Remove these when the "Extensions to Tiled Transparency Logs" section is removed. 167 | TLOG-CHECKPOINT: 168 | title: Transparency Log Checkpoints 169 | target: https://c2sp.org/tlog-checkpoint 170 | date: March 2024 171 | author: 172 | org: C2SP 173 | SIGNED-NOTE: 174 | title: Note 175 | target: https://c2sp.org/signed-note 176 | date: April 2025 177 | author: 178 | org: C2SP 179 | 180 | ... 181 | 182 | --- abstract 183 | 184 | This document describes Merkle Tree certificates, a new form of X.509 certificates which integrate public logging of the certificate, in the style of Certificate Transparency. The integrated design reduces logging overhead in the face of both shorter-lived certificates and large post-quantum signature algorithms, while still achieving comparable security properties to traditional X.509 and Certificate Transparency. Merkle Tree certificates additionally admit an optional signatureless optimization, which decreases the message size by avoiding signatures altogether, at the cost of only applying to up-to-date relying parties and older certificates. 185 | 186 | --- middle 187 | 188 | # Introduction 189 | 190 | Authors' Note: This is an early draft of a proposal with many parts. We expect most details will change as the proposal evolves. This document has a concrete specification of these details, but this is only intended as a starting point, and to help convey the overall idea. The name of the draft says "tls" to keep continuity with earlier iterations of this work, but the protocol itself is not TLS-specific. 191 | 192 | In Public Key Infrastructures (PKIs) that use Certificate Transparency (CT) {{?RFC6962}} for a public logging requirement, an authenticating party must present Signed Certificate Timestamps (SCTs) alongside certificates. CT policies often require two or more SCTs per certificate {{APPLE-CT}} {{CHROME-CT}}, each of which carries a signature. These signatures are in addition to those in the certificate chain itself. 193 | 194 | Current signature schemes can use as few as 32 bytes per key and 64 bytes per signature {{?RFC8032}}, but post-quantum replacements are much larger. For example, ML-DSA-44 {{?FIPS204=DOI.10.6028/NIST.FIPS.204}} uses 1,312 bytes per public key and 2,420 bytes per signature. ML-DSA-65 uses 1,952 bytes per public key and 3,309 bytes per signature. Even with a directly-trusted intermediate ({{Section 7.5 of ?I-D.ietf-tls-trust-anchor-ids}}), two SCTs and a leaf certificate signature adds 7,260 bytes of authentication overhead with ML-DSA-44 and 9,927 bytes with ML-DSA-65. 195 | 196 | This increased overhead additionally impacts CT logs themselves. Most of a log's costs scale with the total storage size of the log. Each log entry contains both a public key, and a signature from the CA. With larger public keys and signatures, the size of each log entry will grow. 197 | 198 | Additionally, as PKIs transition to shorter-lived certificates {{CABF-153}} {{CABF-SC081}}, the number of entries in the log will grow. 199 | 200 | This document introduces Merkle Tree certificates, a new form of X.509 certificate that integrates logging with certificate issuance. Each CA maintains a log of everything it issues, signing views of the log to assert it has issued the contents. The CA signature is combined with cosignatures from other parties who verify correct operation and optionally mirror the log. These signatures, together with an inclusion proof for an individual entry, constitute a certificate. 201 | 202 | This achieves the following: 203 | 204 | * Log entries do not scale with public key and signature sizes. Entries replace public keys with hashes and do not contain signatures, while preserving non-repudiability ({{non-repudiation}}). 205 | 206 | * To bound growth, long-expired entries can be pruned from logs and mirrors without interrupting existing clients. This allows log sizes to scale by retention policies, not the lifetime of the log, even as certificate lifetimes decrease. 207 | 208 | * After a processing delay, authenticating parties can obtain a second "signatureless" certificate for the same log entry. This second certificate is an optional size optimization that avoids the need for any signatures, assuming an up-to-date client that has some predistributed log information. 209 | 210 | {{overview}} gives an overview of the system. {{subtrees}} describes a Merkle Tree primitive used by this system. {{issuance-logs}} describes the log structure. Finally, {{certificates}} and {{relying-parties}} describe how to construct and consume a Merkle Tree certificate. 211 | 212 | # Conventions and Definitions 213 | 214 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", 215 | "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this 216 | document are to be interpreted as described in BCP 14 {{!RFC2119}} {{!RFC8174}} 217 | when, and only when, they appear in all capitals, as shown here. 218 | 219 | This document additionally uses the TLS presentation language defined in {{Section 3 of !RFC8446}}, as well as the notation defined in {{Section 2.1.1 of !RFC9162}}. 220 | 221 | `U+` followed by four hexadecimal characters denotes a Unicode codepoint, to be encoded in UTF-8 {{!RFC3629}}. `0x` followed by two hexadecimal characters denotes a byte value in the 0-255 range. 222 | 223 | `[start, end)`, where `start <= end`, denotes the half-open interval containing integers `x` such that `start <= x < end`. 224 | 225 | Given a non-negative integer `n`, 226 | 227 | * `LSB(n)` refers to the least-significant bit of `n`'s binary representation. Equivalently, it is the remainder when `n` is divided by 2. 228 | 229 | * `BIT_WIDTH(n)` refers to the smallest number of bits needed to represent `n`. `BIT_WIDTH(0)` is zero. 230 | 231 | * `POPCOUNT(n)` refers to the number of set bits in `n`'s binary representation. 232 | 233 | * `BIT_CEIL(n)` refers to the smallest power of 2 that is greater or equal to `n`. 234 | 235 | To *left-shift* a non-negative integer `n` is to shift each bit in its binary representation to one upper position. Equivalently, it is `n` times 2. Given non-negative integers `a` and `b`, `a << b` refers to `a` left-shifted `b` times. 236 | 237 | To *right-shift* a non-negative integer `n` is to shift each bit in its binary representation to one lower position, discarding the least-significant bit. Equivalently, it is the floor of `n` divided by 2. Given non-negative integers `a` and `b`, `a >> b` refers to `a` right-shifted `b` times. 238 | 239 | Given two non-negative integers `a` and `b`, `a & b` refers to the non-negative integer such that each bit position is set if the corresponding bit is set in both `a` and `b`, and unset otherwise. This is commonly referred to as the bitwise AND operator. 240 | 241 | ## Terminology and Roles 242 | 243 | This document discusses the following roles: 244 | 245 | Authenticating party: 246 | : The party that authenticates itself in the protocol. In TLS, this is the side sending the Certificate and CertificateVerify message. 247 | 248 | Certification authority (CA): 249 | : The service that issues certificates to the authenticating party, after performing some validation process on the certificate contents. 250 | 251 | Relying party: 252 | : The party to whom the authenticating party presents its identity. In TLS, this is the side receiving the Certificate and CertificateVerify message. 253 | 254 | Monitor: 255 | : Parties who watch logs for certificates of interest, analogous to the role in {{Section 8.2 of ?RFC9162}}. 256 | 257 | Issuance log: 258 | : A log, maintained by the CA, of everything issued by that CA. 259 | 260 | Cosigner: 261 | : A service that signs views of an issuance log, to assert correct operation and other properties about the entries. 262 | 263 | Additionally, there are several terms used throughout this document to describe this proposal. This section provides an overview. They will be further defined and discussed in detail throughout the document. 264 | 265 | Checkpoint: 266 | : A description of the complete state of the log at some time. 267 | 268 | Entry: 269 | : An individual element of the log, describing information which the CA has validated and certified. 270 | 271 | Subtree: 272 | : A smaller Merkle Tree over a portion of the log, defined by an interior node of some snapshot of the log. Subtrees can be efficiently shown to be consistent with the whole log. 273 | 274 | Inclusion proof: 275 | : A sequence of hashes that efficiently proves some entry is contained in some checkpoint or subtree. 276 | 277 | Consistency proof: 278 | : A sequence of hashes that efficiently proves a checkpoint or subtree is contained within another checkpoint. 279 | 280 | Cosignature: 281 | : A signature from either the CA or other cosigner, over some checkpoint or subtree. 282 | 283 | Landmark: 284 | : One of an infrequent subset of tree sizes that can be used to predistribute trusted subtrees to relying parties for signatureless certificates. 285 | 286 | Landmark subtree: 287 | : A subtree determined by a landmark. Landmark subtrees are common points of reference between relying parties and signatureless certificates. 288 | 289 | Full certificate: 290 | : A certificate containing an inclusion proof to some subtree, and several cosignatures over that subtree. 291 | 292 | Signatureless certificate: 293 | : An optimized certificate containing an inclusion proof to a landmark subtree, and no signatures. 294 | 295 | # Overview 296 | 297 | In Certificate Transparency, a CA first certifies information by signing it, then submits the resulting certificate (or precertificate) to logs for logging. Merkle Tree Certificates invert this process: the CA certifies information by logging it, then submits the log to cosigners to verify log operation. A certificate is assembled from the result and proves the information is in the CA's log. 298 | 299 | ~~~aasvg 300 | +-- Certificate Authority -----+ +-- Authenticating Party ----+ 301 | | | | | 302 | | 2. Validate request <---+----+-- 1. Request certificate | 303 | | | | | | 304 | | | | | | 305 | | V | | | 306 | | | | | 307 | | 3. Add to issuance log | | | 308 | | +---[ CA cosign ] | | | 309 | | / \ ----+----+-> 5. Download certificates | 310 | | / \ | | | 311 | | / \ | | * tbscert | 312 | | +-------+ | | = = = inclusion proof | 313 | | * * * * tbscert entries | | [ CA ] cosignatures | 314 | | | | [ mirror ] | 315 | +------------------------------+ +-----------------------------+ 316 | / | \ 317 | / | \ 4. Submit log to cosigners 318 | V V V for cosignatures 319 | 320 | +-- Mirrors, other cosigners --+ +-- Monitors -----------------+ 321 | | | | | 322 | | +---[ CA cosign ] +-+ | | 323 | | / \ [ mirror cosign ] | | | | 324 | | / \ | | | | 325 | | / \ <-+-+--+-- 6. Monitor CA operation | 326 | | +-------+ | | | | 327 | | * * * * | | +-----------------------------+ 328 | +-+----------------------------+ | 329 | | ...quorum of cosigners... | 330 | +------------------------------+ 331 | ~~~ 332 | {: #fig-issuance-overview title="A diagram of the issuance architecture, detailed below"} 333 | 334 | Merkle Tree Certificates are issued as follows. {{fig-issuance-overview}} depicts this process. 335 | 336 | 1. The authenticating party requests a certificate, e.g. over ACME {{?RFC8555}} 337 | 338 | 2. The CA validates each incoming issuance request, e.g. with ACME challenges. From there, the process differs. 339 | 340 | 3. The CA operates an append-only *issuance log* ({{issuance-logs}}). Unlike a CT log, this issuance log only contains entries added by the CA: 341 | 342 | 1. The CA adds a TBSCertificateLogEntry ({{log-entries}}) to its log, describing the information it is certifying. 343 | 344 | 2. The CA signs a *checkpoint*, which describes the current state of the log. A signed checkpoint certifies that the CA issued *every* entry in the Merkle Tree ({{certification-authority-cosigners}}). 345 | 346 | 3. The CA additionally signs *subtrees* ({{subtrees}}) that together contain certificates added since the last checkpoint ({{arbitrary-intervals}}). This is an optimization to reduce inclusion proof sizes. A signed subtree certifies that the CA has issued *every* entry in the subtree. 347 | 348 | 4. The CA submits the new log state to *cosigners*. Cosigners validate the log is append-only and optionally provide additional services, such as mirroring its contents. They cosign the CA's checkpoints and subtrees. 349 | 350 | 5. The CA now has enough information to construct a certificate and give it to the authenticating party. A certificate contains: 351 | 352 | * The TBSCertificate being certified 353 | * An inclusion proof from the TBSCertificate to some subtree 354 | * Cosignatures from the CA and cosigners on the subtree 355 | 356 | 6. As in Certificate Transparency, monitors observe the issuance log to ensure the CA is operated correctly. 357 | 358 | A certificate with cosignatures is known as a *full certificate*. Analogous to X.509 trust anchors and trusted CT logs, relying parties are configured with trusted cosigners ({{trusted-cosigners}}) that allow them to accept Merkle Tree certificates. The inclusion proof proves the TBSCertificate is part of some subtree, and cosignatures from trusted cosigners prove the subtree was certified by the CA and available to monitors. Where CT logs entire certificates, the issuance log's entries are smaller TBSCertificateLogEntry ({{log-entries}}) structures, which do not scale with public key or signature size. 359 | 360 | This same issuance process also produces a *signatureless certificate*. This is an optional, optimized certificate that avoids all cosignatures, including the CA signature. Signatureless certificates are available after a short period of time and usable with up-to-date relying parties. 361 | 362 | ~~~aasvg 363 | +-- Certificate Authority -------+ 364 | | | +-- Update Channel --+ 365 | | /\ | | | 366 | | / \ 1. Allocate landmarks -+--+----------------+ | 367 | | +----+ | | | | | 368 | +--------------------------+-----+ +----------------+---+ 369 | | | 370 | 2. Make signatureless | 3. Distribute | 371 | cert from landmark | landmarks | 372 | V | 373 | +-- Authenticating Party --------+ | 374 | | | | 375 | | signatureless cert | V 376 | | tbscert | +-- Up-to-date RP -----+ 377 | | inclusion proof to landmark -+->| landmark hashes | 378 | | | | trusted cosigners | 379 | | | +----------------------+ 380 | | full cert | 381 | | tbscert | +-- Unupdated RP ------+ 382 | | inclusion proof | | (stale or no hashes) | 383 | | cosignatures ------------+->| trusted cosigners | 384 | | | +----------------------+ 385 | +--------------------------------+ 386 | 4. Select certificate by RP 387 | ~~~ 388 | {: #fig-signatureless-overview title="A diagram of signatureless certificate construction and usage, detailed below"} 389 | 390 | Signatureless certificates are constructed and used as follows. {{fig-signatureless-overview}} depicts this process. 391 | 392 | 1. Periodically, the tree size of the CA's most recent checkpoint is designated as a *landmark*. This determines *landmark subtrees*, which are common points of reference between relying parties and signatureless certificates. 393 | 394 | 2. Once some landmark includes the TBSCertificate, the signatureless certificate is constructed with: 395 | 396 | * The TBSCertificate being certified 397 | * An inclusion proof from the TBSCertificate to a landmark subtree 398 | 399 | 3. In the background, landmark subtrees are predistributed to relying parties, with cosignatures checked against relying party requirements. This occurs periodically in the background, separate from the application protocol. 400 | 401 | 4. During the application protocol, such as TLS {{?RFC8446}}, if the relying party already supports the landmark subtree, the authenticating party can present the signatureless certificate. Otherwise, it presents a full certificate. The authenticating party may also select between several signatureless certificates, as described in {{certificate-renewal}}. 402 | 403 | # Subtrees 404 | 405 | This section extends the Merkle Tree definition in {{Section 2.1 of !RFC9162}} by defining a *subtree* of a Merkle Tree. A subtree is an interior node of a Merkle Tree, which can be efficiently shown consistent with the original Merkle Tree and any Merkle Tree with additional elements appended. This specification uses subtrees to reduce the size of inclusion proofs. 406 | 407 | ## Definition of a Subtree 408 | 409 | Given an ordered list of `n` inputs, `D_n = {d[0], d[1], ..., d[n-1]}`, {{Section 2.1.1 of !RFC9162}} defines the Merkle Tree via the Merkle Tree Hash `MTH(D_n)`. 410 | 411 | A *subtree* of this Merkle Tree is itself a Merkle Tree, defined by `MTH(D[start:end])`. `start` and `end` are integers such that: 412 | 413 | * `0 <= start < end <= n` 414 | * `start` is a multiple of `BIT_CEIL(end - start)` 415 | 416 | Note that, if `start` is zero, the second condition is always true. 417 | 418 | In the context of a single Merkle Tree, the subtree defined by `start` and `end` is denoted by half-open interval `[start, end)`. It contains the entries whose indices are in that half-open interval. 419 | 420 | The *size* of the subtree is `end - start`. If the subtree's size is a power of two, it is said to be *full*, otherwise it is said to be *partial*. 421 | 422 | If a subtree is full, then it is directly contained in the tree of hash operations in `MTH(D_n)` for `n >= end`. 423 | 424 | If a subtree is partial, it is directly contained in `MTH(D_n)` only if `n = end`. 425 | 426 | ## Example Subtrees 427 | 428 | {{fig-subtree-example}} shows the subtrees `[4, 8)` and `[8, 13)`: 429 | 430 | ~~~aasvg 431 | +--------+ 432 | | [4, 8) | 433 | +--------+ 434 | / \ 435 | +-----+ +-----+ 436 | |[4,6)| |[6,8)| 437 | +-----+ +-----+ 438 | / \ / \ 439 | +-+ +-+ +-+ +-+ 440 | |4| |5| |6| |7| 441 | +-+ +-+ +-+ +-+ 442 | 443 | +----------------+ 444 | | [8, 13) | 445 | +----------------+ 446 | / | 447 | +---------+ | 448 | | [8, 12) | | 449 | +---------+ | 450 | / \ | 451 | +------+ +-------+ | 452 | |[8,10)| |[10,12)| | 453 | +------+ +-------+ | 454 | / \ / \ | 455 | +-+ +-+ +--+ +--+ +--+ 456 | |8| |9| |10| |11| |12| 457 | +-+ +-+ +--+ +--+ +--+ 458 | ~~~ 459 | {: #fig-subtree-example title="Two example subtrees, one full and one partial"} 460 | 461 | Both subtrees are directly contained in a Merkle Tree of size 13, depicted in {{fig-subtree-containment-example}}. `[4, 8)` is contained because, although `n` (13) is not `end` (8), the subtree is full. `[8, 13)` is contained because `n` (13) is `end` (13). 462 | 463 | ~~~aasvg 464 | +-----------------------------+ 465 | | [0, 13) | 466 | +-----------------------------+ 467 | / \ 468 | +----------------+ +~~~~~~~~~~~~~~~~+ 469 | | [0, 8) | | [8, 13) | 470 | +----------------+ +~~~~~~~~~~~~~~~~+ 471 | / \ / | 472 | +--------+ +========+ +~~~~~~~~~+ | 473 | | [0, 4) | | [4, 8) | | [8, 12) | | 474 | +--------+ +========+ +~~~~~~~~~+ | 475 | / \ / \ / \ | 476 | +-----+ +-----+ +=====+ +=====+ +~~~~~~+ +~~~~~~~+ | 477 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| |[8,10)| |[10,12)| | 478 | +-----+ +-----+ +=====+ +=====+ +~~~~~~+ +~~~~~~~+ | 479 | / \ / \ / \ / \ / \ / \ | 480 | +-+ +-+ +-+ +-+ +=+ +=+ +=+ +=+ +~+ +~+ +~~+ +~~+ +~~+ 481 | |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12| 482 | +-+ +-+ +-+ +-+ +=+ +=+ +=+ +=+ +~+ +~+ +~~+ +~~+ +~~+ 483 | ~~~ 484 | {: #fig-subtree-containment-example title="A Merkle Tree of size 13"} 485 | 486 | In contrast, `[8, 13)` is not directly contained in a Merkle Tree of size 14, depicted in {{fig-subtree-containment-example-2}}. However, the subtree is still computed over consistent elements. 487 | 488 | ~~~aasvg 489 | +-----------------------------+ 490 | | [0, 14) | 491 | +-----------------------------+ 492 | / \ 493 | +----------------+ +----------------+ 494 | | [0, 8) | | [8, 14) | 495 | +----------------+ +----------------+ 496 | / \ / | 497 | +--------+ +--------+ +---------+ | 498 | | [0, 4) | | [4, 8) | | [8, 12) | | 499 | +--------+ +--------+ +---------+ | 500 | / \ / \ / \ | 501 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ +-------+ 502 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| |[8,10)| |[10,12)| |[12,14)| 503 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ +-------+ 504 | / \ / \ / \ / \ / \ / \ / \ 505 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +~+ +~+ +~~+ +~~+ +~~+ +--+ 506 | |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12| |13| 507 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +~+ +~+ +~~+ +~~+ +~~+ +--+ 508 | ~~~ 509 | {: #fig-subtree-containment-example-2 title="A Merkle Tree of size 14"} 510 | 511 | ## Subtree Inclusion Proofs 512 | 513 | Subtrees are Merkle Trees, so entries can be proven to be contained in the subtree. A subtree inclusion proof for entry `index` of the subtree `[start, end)` is a Merkle inclusion proof, as defined in {{Section 2.1.3.1 of !RFC9162}}, where `m` is `index - start` and the tree inputs are `D[start:end]`. 514 | 515 | Subtree inclusion proofs contain a sequence of nodes that are sufficient to reconstruct the subtree hash, `MTH(D[start:end])`, out of the hash for entry `index`, `MTH({d[index]})`, thus demonstrating that the subtree hash contains the entry's hash. 516 | 517 | ### Example Subtree Inclusion Proofs 518 | 519 | The inclusion proof for entry 10 of subtree `[8, 13)` contains the hashes `MTH({d[11]})`, `MTH(D[8:10])`, and `MTH({d[12]})`, depicted in {{fig-subtree-inclusion-proof}}. `MTH({d[10]})` is not part of the proof because the verifier is assumed to already know its value. 520 | 521 | ~~~aasvg 522 | +----------------+ 523 | | [8, 13) | 524 | +----------------+ 525 | / | 526 | +---------+ | 527 | | [8, 12) | | 528 | +---------+ | 529 | / \ | 530 | +======+ +-------+ | 531 | |[8,10)| |[10,12)| | 532 | +======+ +-------+ | 533 | / \ / \ | 534 | +-+ +-+ +~~+ +==+ +==+ 535 | |8| |9| |10| |11| |12| 536 | +-+ +-+ +~~+ +==+ +==+ 537 | ~~~ 538 | {: #fig-subtree-inclusion-proof title="An example subtree inclusion proof"} 539 | 540 | ### Evaluating a Subtree Inclusion Proof 541 | 542 | Given a subtree inclusion proof, `inclusion_proof`, for entry `index`, with hash `entry_hash`, of a subtree `[start, end)`, the subtree inclusion proof can be *evaluated* to compute the expected subtree hash: 543 | 544 | 545 | 546 | 1. Check that `[start, end)` is a valid subtree ({{definition-of-a-subtree}}), and that `start <= index < end`. If either do not hold, fail proof evaluation. 547 | 548 | 1. Set `fn` to `index - start` and `sn` to `end - start - 1`. 549 | 550 | 1. Set `r` to `entry_hash`. 551 | 552 | 1. For each value `p` in the `inclusion_proof` array: 553 | 554 | 1. If `sn` is 0, then stop the iteration and fail proof evaluation. 555 | 556 | 1. If `LSB(fn)` is set, or if `fn` is equal to `sn`, then: 557 | 558 | 1. Set `r` to `HASH(0x01 || p || r)`. 559 | 560 | 1. Until `LSB(fn)` is set, right-shift `fn` and `sn` equally. 561 | 562 | Otherwise: 563 | 564 | 1. Set `r` to `HASH(0x01 || r || p)`. 565 | 566 | 1. Finally, right-shift both `fn` and `sn` one time. 567 | 568 | 1. If `sn` is not zero, fail proof evaluation. 569 | 570 | 1. Return `r` as the expected subtree hash. 571 | 572 | This is the same as the procedure in {{Section 2.1.3.2 of !RFC9162}}, where `leaf_index` is `index - start`, `tree_size` is `end - start`, and `r` is returned instead of compared with `root_hash`. 573 | 574 | {{inclusion-proof-evaluation-explain}} explains this procedure in more detail. 575 | 576 | ### Verifying a Subtree Inclusion Proof 577 | 578 | Given a subtree inclusion proof, `inclusion_proof`, for entry `index`, with hash `entry_hash`, of a subtree `[start, end)` with hash `subtree_hash`, the subtree inclusion proof can be *verified* to verify the described entry is contained in the subtree: 579 | 580 | 1. Let `expected_subtree_hash` be the result of evaluating the inclusion proof as described {{evaluating-a-subtree-inclusion-proof}}. If evaluation fails, fail the proof verification. 581 | 582 | 1. If `subtree_hash` is equal to `expected_subtree_hash`, the entry is contained in the subtree. Otherwise, fail the proof verification. 583 | 584 | ## Subtree Consistency Proofs 585 | 586 | A subtree `[start, end)` can be efficiently proven to be consistent with the full Merkle Tree. That is, given `MTH(D[start:end])` and `MTH(D_n)`, the proof demonstrates that the input `D[start:end]` to the subtree hash was equal to the corresponding elements of the input `D_n` to the Merkle Tree hash. 587 | 588 | Subtree consistency proofs contain sufficient nodes to reconstruct both the subtree hash, `MTH(D[start:end])`, and the full tree hash, `MTH(D_n)`, in such a way that every input to the subtree hash was also incorporated into the full tree hash. 589 | 590 | ### Generating a Subtree Consistency Proof 591 | 592 | The subtree consistency proof, `SUBTREE_PROOF(start, end, D_n)` is defined similarly to {{Section 2.1.4.1 of !RFC9162}}, in terms of a helper function that tracks whether the subtree hash is known: 593 | 594 | ~~~pseudocode 595 | SUBTREE_PROOF(start, end, D_n) = 596 | SUBTREE_SUBPROOF(start, end, D_n, true) 597 | ~~~ 598 | 599 | If `start = 0` and `end = n`, the subtree is the root: 600 | 601 | ~~~pseudocode 602 | SUBTREE_SUBPROOF(0, n, D_n, true) = {} 603 | SUBTREE_SUBPROOF(0, n, D_n, false) = {MTH(D_n)} 604 | ~~~ 605 | 606 | Otherwise, `n > 1`. Let `k` be the largest power of two smaller than `n`. The consistency proof is defined recursively as: 607 | 608 | * If `end <= k`, the subtree is on the left of `k`. The proof proves consistency with the left child and includes the right child: 609 | 610 | ~~~pseudocode 611 | SUBTREE_SUBPROOF(start, end, D_n, b) = 612 | SUBTREE_SUBPROOF(start, end, D[0:k], b) : MTH(D[k:n]) 613 | ~~~ 614 | 615 | * If `k <= start`, the subtree is on the right of `k`. The proof proves consistency with the right child and includes the left child. 616 | 617 | ~~~pseudocode 618 | SUBTREE_SUBPROOF(start, end, D_n, b) = 619 | SUBTREE_SUBPROOF(start - k, end - k, D[k:n], b) : MTH(D[0:k]) 620 | ~~~ 621 | 622 | * Otherwise, `start < k < end`, which implies `start = 0`. The proof proves consistency with the right child and includes the left child. 623 | 624 | ~~~pseudocode 625 | SUBTREE_SUBPROOF(0, end, D_n, b) = 626 | SUBTREE_SUBPROOF(0, end - k, D[k:n], false) : MTH(D[0:k]) 627 | ~~~ 628 | 629 | When `start` is zero, this computes a Merkle consistency proof: 630 | 631 | ~~~pseudocode 632 | SUBTREE_PROOF(0, end, D_n) = PROOF(end, D_n) 633 | ~~~ 634 | 635 | When `end = start + 1`, this computes a Merkle inclusion proof: 636 | 637 | ~~~pseudocode 638 | SUBTREE_PROOF(start, start + 1, D_n) = PATH(start, D_n) 639 | ~~~ 640 | 641 | {{consistency-proof-structure}} explains the structure of a subtree consistency proof in more detail. 642 | 643 | ### Example Subtree Consistency Proofs 644 | 645 | The subtree consistency proof for `[4, 8)` and a tree of size 14 contains `MTH(D[0:4])` and `MTH(D[8:14])`, depicted in {{fig-subtree-consistency-example-1}}. The verifier is assumed to know the subtree hash, so there is no need to include `MTH(D[4:8])` itself in the consistency proof. 646 | 647 | ~~~aasvg 648 | +~~~~~~~~+ 649 | | [4, 8) | 650 | +~~~~~~~~+ 651 | / \ 652 | +-----+ +-----+ 653 | |[4,6)| |[6,8)| 654 | +-----+ +-----+ 655 | / \ / \ 656 | +-+ +-+ +-+ +-+ 657 | |4| |5| |6| |7| 658 | +-+ +-+ +-+ +-+ 659 | 660 | +-----------------------------+ 661 | | [0, 14) | 662 | +-----------------------------+ 663 | / \ 664 | +----------------+ +================+ 665 | | [0, 8) | | [8, 14) | 666 | +----------------+ +================+ 667 | / \ / | 668 | +========+ +~~~~~~~~+ +---------+ | 669 | | [0, 4) | | [4, 8) | | [8, 12) | | 670 | +========+ +~~~~~~~~+ +---------+ | 671 | / \ / \ / \ | 672 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ +-------+ 673 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| |[8,10)| |[10,12)| |[12,14)| 674 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ +-------+ 675 | / \ / \ / \ / \ / \ / \ / \ 676 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+ +--+ 677 | |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12| |13| 678 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+ +--+ 679 | ~~~ 680 | {: #fig-subtree-consistency-example-1 title="An example subtree consistency proof for a subtree that is directly contained in the full tree"} 681 | 682 | The subtree consistency proof for `[8, 13)` and a tree of size 14 contains `MTH({d[12]})`, `MTH({d[13]})`, `MTH(D[8:12])`, and `MTH(D[0:8])`, depicted in {{fig-subtree-consistency-example-2}}. `[8, 13)` is not directly contained in the tree, so the proof must include sufficient nodes to reconstruct both hashes. 683 | 684 | ~~~aasvg 685 | +----------------+ 686 | | [8, 13) | 687 | +----------------+ 688 | / | 689 | +=========+ | 690 | | [8, 12) | | 691 | +=========+ | 692 | / \ | 693 | +------+ +-------+ | 694 | |[8,10)| |[10,12)| | 695 | +------+ +-------+ | 696 | / \ / \ | 697 | +-+ +-+ +--+ +--+ +==+ 698 | |8| |9| |10| |11| |12| 699 | +-+ +-+ +--+ +--+ +==+ 700 | 701 | +-----------------------------+ 702 | | [0, 14) | 703 | +-----------------------------+ 704 | / \ 705 | +================+ +----------------+ 706 | | [0, 8) | | [8, 14) | 707 | +================+ +----------------+ 708 | / \ / | 709 | +--------+ +--------+ +=========+ | 710 | | [0, 4) | | [4, 8) | | [8, 12) | | 711 | +--------+ +--------+ +=========+ | 712 | / \ / \ / \ | 713 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ +-------+ 714 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| |[8,10)| |[10,12)| |[12,14)| 715 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ +-------+ 716 | / \ / \ / \ / \ / \ / \ / \ 717 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +==+ +==+ 718 | |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12| |13| 719 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +==+ +==+ 720 | ~~~ 721 | {: #fig-subtree-consistency-example-2 title="An example subtree consistency proof for a subtree that is not directly contained in the full tree"} 722 | 723 | ### Verifying a Subtree Consistency Proof 724 | 725 | The following procedure can be used to verify a subtree consistency proof. 726 | 727 | Given a Merkle Tree over `n` elements, a subtree defined by `[start, end)`, a consistency proof `proof`, a subtree hash `node_hash`, and a root hash `root_hash`: 728 | 729 | 730 | 731 | 1. Check that `[start, end)` is a valid subtree ({{definition-of-a-subtree}}), and that `end <= n`. If either do not hold, fail proof verification. These checks imply `0 <= start < end <= n`. 732 | 1. Set `fn` to `start`, `sn` to `end - 1`, and `tn` to `n - 1`. 733 | 1. If `sn` is `tn`, then: 734 | 1. Until `fn` is `sn`, right-shift `fn`, `sn`, and `tn` equally. 735 | 1. Otherwise: 736 | 1. Until `fn` is `sn` or `LSB(sn)` is not set, right-shift `fn`, `sn`, and `tn` equally. 737 | 1. If `fn` is `sn`, set `fr` and `sr` to `node_hash`. 738 | 1. Otherwise: 739 | 1. If `proof` is an empty array, stop and fail verification. 740 | 1. Remove the first value of the `proof` array and set `fr` and `sr` to the removed value. 741 | 1. For each value `c` in the `proof` array: 742 | 1. If `tn` is `0`, then stop the iteration and fail the proof verification. 743 | 1. If `LSB(sn)` is set, or if `sn` is equal to `tn`, then: 744 | 1. If `fn < sn`, set `fr` to `HASH(0x01 || c || fr)`. 745 | 1. Set `sr` to `HASH(0x01 || c || sr)`. 746 | 1. Until `LSB(sn)` is set, right-shift `fn`, `sn`, and `tn` equally. 747 | 1. Otherwise: 748 | 1. Set `sr` to `HASH(0x01 || sr || c)`. 749 | 1. Right-shift `fn`, `sn`, and `tn` once more. 750 | 1. Compare `tn` to `0`, `fr` to `node_hash`, and `sr` to `root_hash`. If any are not equal, fail the proof verification. If all are equal, accept the proof. 751 | 752 | {{consistency-proof-verification-explain}} explains this procedure in more detail. 753 | 754 | ## Arbitrary Intervals 755 | 756 | Not all `[start, end)` intervals of a Merkle Tree are valid subtrees. This section describes how, for any `start < end`, to determine up to two subtrees that efficiently cover the interval. The subtrees are determined by the following procedure: 757 | 758 | 1. If `end - start` is one, return a single subtree, `[start, end)`. 759 | 760 | 2. Otherwise, run the following to return a pair of subtrees: 761 | 762 | 1. Let `last` be `end - 1`, the last index in `[start, end)`. 763 | 764 | 2. Let `split` be the bit index of the most significant bit where `start` and `last` differ. Bits are numbered from the least significant bit, starting at zero. `split` is the height at which `start` and `last`'s paths in the tree diverge. 765 | 766 | 3. Let `mid` be `last` with the least significant `split` bits set to zero. `mid` is the leftmost leaf node in the above divergence point's right branch. 767 | 768 | 4. Within the least significant `split` bits of `left`, let `b` be the bit index of the most significant bit with value zero, if any: 769 | 770 | 1. If there is such a bit, let `left_split` be `b + 1`. 771 | 2. Otherwise, let `left_split` be zero. 772 | 773 | `left_split` is the height of the lowest common ancestor of the nodes in `[start, mid)`. 774 | 775 | 5. Let `left_start` be `start` with the least significant `left_split` bits set to zero. `left_start` is the above lowest common ancestor's leftmost leaf node. 776 | 777 | 6. Return the subtrees `[left_start, mid)` and `[mid, end)`. 778 | 779 | When the procedure returns a single subtree, the subtree is `[start, start+1)`. When it returns two subtrees, `left` and `right`, the subtrees satisfy the following properties: 780 | 781 | * `left.end = right.start`. That is, the two subtrees cover adjacent intervals. 782 | * `left.start <= start` and `end = right.end`. That is, the two subtrees together cover the entire target interval, possibly with some extra entries before `start` left, but not after `end`. 783 | * `left.end - left.start < 2 * (end - start)` and `right.end - right.start <= end - start`. That is, the two subtrees efficiently cover the interval. 784 | * `left` is full, while `right` may be partial. 785 | 786 | The following Python code implements this procedure: 787 | 788 | ~~~python 789 | def find_subtrees(start, end): 790 | """ Returns a list of one or two subtrees that efficiently 791 | cover [start, end). """ 792 | assert start < end 793 | if end - start == 1: 794 | return [(start, end),] 795 | last = end - 1 796 | # Find where start and last's tree paths diverge. The two 797 | # subtrees will be on either side of the split. 798 | split = (start ^ last).bit_length() - 1 799 | mask = (1 << split) - 1 800 | mid = last & ~mask 801 | # Maximize the left endpoint. This is just before start's 802 | # path leaves the right edge of its new subtree. 803 | left_split = (~start & mask).bit_length() 804 | left_start = start & ~((1 << left_split) - 1) 805 | return [(left_start, mid), (mid, end)] 806 | ~~~ 807 | 808 | {{fig-subtree-pair-example}} shows the subtrees which cover `[5, 13)` in a Merkle Tree of 13 elements. The two subtrees selected are `[4, 8)` and `[8, 13)`. Note that the subtrees cover a slightly larger interval than `[5, 13)`. 809 | 810 | 811 | 812 | ~~~aasvg 813 | +-----------------------------+ 814 | | [0, 13) | 815 | +-----------------------------+ 816 | / \ 817 | +----------------+ +================+ 818 | | [0, 8) | | [8, 13) | 819 | +----------------+ +================+ 820 | / \ / | 821 | +--------+ +========+ +---------+ | 822 | | [0, 4) | | [4, 8) | | [8, 12) | | 823 | +--------+ +========+ +---------+ | 824 | / \ / \ / \ | 825 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ | 826 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| |[8,10)| |[10,12)| | 827 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ | 828 | / \ / \ / \ / \ / \ / \ | 829 | +-+ +-+ +-+ +-+ +-+ +=+ +=+ +=+ +=+ +=+ +==+ +==+ +==+ 830 | |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12| 831 | +-+ +-+ +-+ +-+ +-+ +=+ +=+ +=+ +=+ +=+ +==+ +==+ +==+ 832 | ~~~ 833 | {: #fig-subtree-pair-example title="An example selection of subtrees to cover an interval"} 834 | 835 | Two subtrees are needed because a single subtree may not be able to efficiently cover an interval. {{fig-subtree-counterexample}} shows the smallest subtree that contains `[7, 9)` in a 9-element tree. The smallest single subtree that contains the interval is `[0, 9)` but this is the entire tree. Using two subtrees, the interval can be described by `[7, 8)` and `[8, 9)`. 836 | 837 | ~~~aasvg 838 | +===================+ 839 | | [0, 9) | 840 | +===================+ 841 | / | 842 | +----------------+ | 843 | | [0, 8) | | 844 | +----------------+ | 845 | / \ | 846 | +--------+ +--------+ | 847 | | [0, 4) | | [4, 8) | | 848 | +--------+ +--------+ | 849 | / \ / \ | 850 | +-----+ +-----+ +-----+ +-----+ | 851 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| | 852 | +-----+ +-----+ +-----+ +-----+ | 853 | / \ / \ / \ / \ | 854 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +=+ +=+ 855 | |0| |1| |2| |3| |4| |4| |6| |7| |8| 856 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +=+ +=+ 857 | ~~~ 858 | {: #fig-subtree-counterexample title="An example showing an inefficient choice of a single subtree"} 859 | 860 | # Issuance Logs 861 | 862 | This section defines the structure of an *issuance log*. 863 | 864 | An issuance log describes an append-only sequence of *entries* ({{log-entries}}), identified consecutively by an index value, starting from zero. Each entry is an assertion that the CA has certified. The entries in the issuance log are represented as a Merkle Tree, described in {{Section 2.1 of !RFC9162}}. 865 | 866 | Unlike {{?RFC6962}} and {{?RFC9162}}, an issuance log does not have a public submission interface. The log only contains entries which the log operator, i.e. the CA, chose to add. As entries are added, the Merkle Tree is updated to be computed over the new sequence. 867 | 868 | A snapshot of the log is known as a *checkpoint*. A checkpoint is identified by its *tree size*, that is the number of elements comitted to the log at the time. Its contents can be described by the Merkle Tree Hash ({{Section 2.1.1 of !RFC9162}}) of entries zero through `tree_size - 1`. 869 | 870 | Cosigners ({{cosigners}}) sign assertions about the state of the issuance log. A Merkle Tree CA operates a combination of an issuance log and one or more CA cosigners ({{certification-authority-cosigners}}) that authenticate the log state and certifies the contents. External cosigners may also be deployed to assert correct log operation or provide other services to relying parties ({{trusted-cosigners}}). 871 | 872 | ## Log Parameters 873 | 874 | An issuance log has the following parameters: 875 | 876 | * A log ID, which uniquely identifies the log. See {{log-ids}}. 877 | * A collision-resistant cryptographic hash function. SHA-256 {{!SHS=DOI.10.6028/NIST.FIPS.180-4}} is RECOMMENDED. 878 | * A minimum index, which is the index of the first log entry which is available. See {{log-pruning}}. This value changes over the lifetime of the log. 879 | 880 | Throughout this document, the hash algorithm in use is referred to as HASH, and the size of its output in bytes is referred to as HASH_SIZE. 881 | 882 | ## Log IDs 883 | 884 | Each issuance log is identified by a *log ID*, which is a trust anchor ID {{!I-D.ietf-tls-trust-anchor-ids}}. 885 | 886 | An issuance log's log ID determines an X.509 distinguished name ({{Section 4.1.2.4 of !RFC5280}}). The distinguished name has a single relative distinguished name, which has a single attribute. The attribute has type `id-rdna-trustAnchorID`, defined below: 887 | 888 | ~~~asn.1 889 | id-rdna-trustAnchorID OBJECT IDENTIFIER ::= { 890 | iso(1) identified-organization(3) dod(6) internet(1) security(5) 891 | mechanisms(5) pkix(7) rdna(25) TBD} 892 | ~~~ 893 | 894 | The attribute's value is a RELATIVE-OID containing the trust anchor ID's ASN.1 representation. For example, the distinguished name for a log named `32473.1` would be represented in syntax of {{?RFC4514}} as: 895 | 896 | ~~~ 897 | 1.3.6.1.5.5.7.25.TBD=#0d0481fd5901 898 | ~~~ 899 | 900 | For initial experimentation, early implementations of this design will: 901 | 902 | 1. Use UTF8String to represent the attribute's value rather than RELATIVE-OID. The UTF8String contains trust anchor ID's ASCII representation, e.g. `324731.1`. 903 | 904 | 1. Use the OID 1.3.6.1.4.1.44363.47.1 instead of `id-rdna-trustAnchorID`. 905 | 906 | For example, the distinguished name for a log named `32473.1` would be represented in syntax of {{?RFC4514}} as: 907 | 908 | ~~~ 909 | 1.3.6.1.4.1.44363.47.1=#0c0733323437332e31 910 | ~~~ 911 | 912 | ## Log Entries 913 | 914 | Each entry in the log is a MerkleTreeCertEntry, defined with the TLS presentation syntax below. A MerkleTreeCertEntry describes certificate information that the CA has validated and certified. 915 | 916 | ~~~tls-presentation 917 | struct {} Empty; 918 | 919 | enum { 920 | null_entry(0), tbs_cert_entry(1), (2^16-1) 921 | } MerkleTreeCertEntryType; 922 | 923 | struct { 924 | MerkleTreeCertEntryType type; 925 | select (type) { 926 | case null_entry: Empty; 927 | case tbs_cert_entry: opaque tbs_cert_entry_data[N]; 928 | /* May be extended with future types. */ 929 | } 930 | } MerkleTreeCertEntry; 931 | ~~~ 932 | 933 | When `type` is `tbs_cert_entry`, `N` is the number of bytes needed to consume the rest of the input. A MerkleTreeCertEntry is expected to be decoded in contexts where the total length of the entry is known. 934 | 935 | `tbs_cert_entry_data` contains the DER {{X.690}} encoding of a TBSCertificateLogEntry, defined below: 936 | 937 | ~~~asn.1 938 | TBSCertificateLogEntry ::= SEQUENCE { 939 | version [0] EXPLICIT Version DEFAULT v1, 940 | issuer Name, 941 | validity Validity, 942 | subject Name, 943 | subjectPublicKeyInfoHash OCTET STRING, 944 | issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 945 | subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 946 | extensions [3] EXPLICIT Extensions OPTIONAL } 947 | ~~~ 948 | 949 | The `version`, `issuer`, `validity`, `subject`, `issuerUniqueID`, `subjectUniqueID`, and `extensions` fields have the corresponding semantics as in {{Section 4.1.2 of !RFC5280}}, with the exception of `subjectPublicKeyInfoHash`. `subjectPublicKeyInfoHash` contains the hash of subject's public key as a SubjectPublicKeyInfo ({{Section 4.1.2.7 of !RFC5280}}). The hash uses the log's hash function ({{log-parameters}}) and is computed over the SubjectPublicKeyInfo's DER {{X.690}} encoding. The `issuer` field MUST be the issuance log's log ID as an X.509 distinguished name, as described in {{log-ids}}. 950 | 951 | When `type` is `null_entry`, the entry does not represent any information. The entry at index zero of every issuance log MUST be of type `null_entry`. Other entries MUST NOT use `null_entry`. `null_entry` exists to avoid zero serial numbers in the certificate format ({{certificate-format}}). 952 | 953 | MerkleTreeCertEntry is an extensible structure. Future documents may define new values for MerkleTreeCertEntryType, with corresponding semantics. See {{certification-authority-cosigners}} and {{new-log-entry-types}} for additional discussion. 954 | 955 | ## Cosigners 956 | 957 | This section defines a log *cosigner*. A cosigner follows some append-only view of the log and signs subtrees ({{subtrees}}) consistent with that view. The signatures generated by a cosigner are known as *cosignatures*. All subtrees signed by a cosigner MUST be consistent with each other. The cosigner may be external to the log, in which case it might ensure consistency by checking consistency proofs. The cosigner may be operated together with the log, in which case it can trust its log state. 958 | 959 | A cosignature MAY implicitly make additional statements about a subtree, determined by the cosigner's role. This document defines one concrete cosigner role, a CA cosigner ({{certification-authority-cosigners}}), to authenticate the log and certify entries. Other documents and specific deployments may define other cosigner roles, to perform different functions in a PKI. For example, {{TLOG-WITNESS}} defines a cosigner that only checks the log is append-only, and {{TLOG-MIRROR}} defines a cosigner that mirrors a log. 960 | 961 | Each cosigner has a public key and a *cosigner ID*, which uniquely identifies the cosigner. The cosigner ID is a trust anchor ID {{!I-D.ietf-tls-trust-anchor-ids}}. By identifying the cosigner, the cosigner ID specifies both the public key and the additional statements made by the cosigner's signatures. If a single operator performs multiple cosigner roles in an ecosystem, each role MUST use a distinct cosigner ID and SHOULD use a distinct key. 962 | 963 | A single cosigner, with a single cosigner ID and public key, MAY generate cosignatures for multiple logs. In this case, signed subtrees only need to be consistent with others for the same log. 964 | 965 | ### Signature Format 966 | 967 | A cosigner computes a cosignature for a subtree in some log by signing a MTCSubtreeSignatureInput, defined below using the TLS presentation language ({{Section 3 of !RFC8446}}): 968 | 969 | ~~~tls-presentation 970 | opaque HashValue[HASH_SIZE]; 971 | 972 | /* From Section 4.1 of draft-ietf-tls-trust-anchor-ids */ 973 | opaque TrustAnchorID<1..2^8-1>; 974 | 975 | struct { 976 | TrustAnchorID log_id; 977 | uint64 start; 978 | uint64 end; 979 | HashValue hash; 980 | } MTCSubtree; 981 | 982 | struct { 983 | uint8 label[16] = "mtc-subtree/v1\n\0"; 984 | TrustAnchorID cosigner_id; 985 | MTCSubtree subtree; 986 | } MTCSubtreeSignatureInput; 987 | ~~~ 988 | 989 | `log_id` MUST be the issuance log's ID ({{log-ids}}), in its binary representation ({{Section 3 of !I-D.ietf-tls-trust-anchor-ids}}). `start` and `end` MUST define a valid subtree of the log, and `hash` MUST be the subtree's hash value in the cosigner's view of the log. The `label` is a fixed prefix for domain separation. Its value MUST be the string `mtc-subtree/v1`, followed by a newline (U+000A), followed by a zero byte (U+0000). `cosigner_id` MUST be the cosigner ID, in its binary representation. 990 | 991 | The resulting signature is known as a *subtree signature*. When `start` is zero, the resulting signature describes the checkpoint with tree size `end` and is also known as a *checkpoint signature*. 992 | 993 | For each supported log, a cosigner retains its checkpoint signature with the largest `end`. This is known as the cosigner's *current* checkpoint. If the cosigner's current checkpoint has tree size `tree_size`, it MUST NOT generate a signature for a subtree `[start, end)` if `start > 0` and `end > tree_size`. That is, a cosigner can only sign a non-checkpoint subtree if it is contained in its current checkpoint. In a correctly-operated cosigner, every signature made by the cosigner can be proven consistent with its current checkpoint with a subtree consistency proof ({{subtree-consistency-proofs}}). As a consequence, a cosigner that signs a subtree is held responsible for all the entries in the tree of size matching the subtree end, even if the corresponding checkpoint is erroneously unavailable. 994 | 995 | Before signing a subtree, the cosigner MUST ensure that `hash` is consistent with its log state. Different cosigner roles may obtain this assurance differently. For example, a cosigner may compute the hash from its saved log state (e.g. if it is the log operator or maintains a copy of the log) or by verifying a subtree consistency proof ({{subtree-consistency-proofs}}) from its current checkpoint. When a cosigner signs a subtree, it is held responsible *both* for the subtree being consistent with its other signatures, *and* for the cosigner-specific additional statements. 996 | 997 | Cosigners SHOULD publish their current checkpoint, along with the checkpoint signature. 998 | 999 | [[TODO: CT and tlog put timestamps in checkpoint signatures. Do we want them here? In CT and tlog, the timestamps are monotonically increasing as the log progresses, but we also sign subtrees. We can separate subtree and checkpoint signatures, with timestamps only in the latter, but it's unclear if there is any benefit to this.]] 1000 | 1001 | ### Signature Algorithms 1002 | 1003 | The cosigner's public key specifies both the key material and the signature algorithm to use with the key material. In order to change key or signature parameters, a cosigner operator MUST deploy a new cosigner, with a new cosigner ID. Signature algorithms MUST fully specify the algorithm parameters, such as hash functions used. This document defines the following signature algorithms: 1004 | 1005 | * ECDSA with P-256 and SHA-256 {{!FIPS186-5=DOI.10.6028/NIST.FIPS.186-5}} 1006 | * ECDSA with P-384 and SHA-384 {{!FIPS186-5}} 1007 | * Ed25519 {{!RFC8032}} 1008 | * ML-DSA-44 {{!FIPS204}} 1009 | * ML-DSA-65 {{!FIPS204}} 1010 | * ML-DSA-87 {{!FIPS204}} 1011 | 1012 | Other documents or deployments MAY define other signature schemes and formats. Log clients that accept cosignatures from some cosigner are assumed to be configured with all parameters necessary to verify that cosigner's signatures, including the signature algorithm and version of the signature format. 1013 | 1014 | ## Certification Authority Cosigners 1015 | 1016 | A *CA cosigner* is a cosigner ({{cosigners}}) that certifies the contents of a log. 1017 | 1018 | When a CA cosigner signs a subtree, it makes the additional statement that it has certified each entry in the subtree. For example, a domain-validating CA states that it has performed domain validation for each entry, at some time consistent with the entry's validity dates. CAs are held responsible for every entry in every subtree they sign. Proving an entry is included ({{subtree-inclusion-proofs}}) in a CA-signed subtree is sufficient to prove the CA certified it. 1019 | 1020 | What it means to certify an entry depends on the entry type: 1021 | 1022 | * To certify an entry of type `null_entry` is a no-op. A CA MAY freely certify `null_entry` without being held responsible for any validation. 1023 | * To certify an entry of type `tbs_cert_entry` is to certify the TBSCertificateLogEntry, as defined in {{log-entries}}. 1024 | 1025 | Entries are extensible. Future documents MAY define `type` values and what it means to certify them. A CA MUST NOT sign a subtree if it contains an entry with `type` that it does not recognize. Doing so would certify that the CA has validated the information in some not-yet-defined entry format. {{new-log-entry-types}} further discusses security implications of new formats. 1026 | 1027 | A CA operator MAY operate multiple CA cosigners that all certify the same log in parallel. This may be useful when, e.g., rotating CA keys. In this case, each CA instance MUST have a distinct name. The CA operator's ACME server can return all CA cosignatures together in a single certificate, with the application protocol selecting the cosignatures to use. {{use-in-tls}} describes how this is done in TLS {{!RFC8446}}. 1028 | 1029 | If the CA operator additionally operates a traditional X.509 CA, that CA key MUST be distinct from any Merkle Tree CA cosigner keys. 1030 | 1031 | ## Publishing Logs 1032 | 1033 | *[[NOTE: This section is written to avoid depending on a specific serving protocol. The current expectation is that a Web PKI deployment would derive from {{TLOG-TILES}}, to match the direction of Certificate Transparency and pick up improvements made there.* 1034 | 1035 | *For now, we avoid a normative reference on {{TLOG-TILES}} and also capture the fact that the certificate construction is independent of the choice of protocol. Similar to how the CT ecosystem is migrating to a tiled interface, were someone to improve on {{TLOG-TILES}}, a PKI could migrate to that new protocol without impacting certificate verification.* 1036 | 1037 | *That said, this is purely a starting point for describing the design. We expect the scope of this document, and other related documents to adapt as the work evolves across the IETF, C2SP, Certificate Transparency, and other communities.]]* 1038 | 1039 | Issuance logs are intended to be publicly accessible in some form, to allow monitors to detect misissued certificates. 1040 | 1041 | The access method does not affect certificate interoperability, so this document does not prescribe a specific protocol. An individual issuance log MAY be published in any form, provided other parties in the PKI are able to consume it. Relying parties SHOULD define log serving requirements, including the allowed protocols and expected availability, as part of their policies on which CAs to support. See also {{log-availability}}. 1042 | 1043 | For example, a log ecosystem could use {{TLOG-TILES}} to serve logs. {{TLOG-TILES}} improves on {{?RFC6962}} and {{?RFC9162}} by exposing the log as a collection of cacheable, immutable "tiles". This works well with a variety of common HTTP {{?RFC9110}} serving architectures. It also allows log clients to request arbitrary tree nodes, so log clients can fetch the structures described in {{subtrees}}. 1044 | 1045 | ### Log Pruning 1046 | 1047 | Over time, an issuance log's entries will expire and likely be replaced with certificate renewals. As this happens, the total size of the log grows, even if the unexpired subset remains fixed. To mitigate this, issuance logs MAY be *pruned*, as described in this section. 1048 | 1049 | Pruning makes some prefix of the log unavailable, without changing the tree structure. It may be used to reduce the serving cost of long-lived logs, where any entries have long expired. {{log-availability}} discusses policies on when pruning may be permitted. This section discusses how it is done and the impact on log structure. 1050 | 1051 | An issuance log is pruned by updating its *minimum index* parameter ({{log-parameters}}). The minimum index is the index of the first log entry that the log publishes. (See {{publishing-logs}}.) It MUST be less than or equal to the tree size of the log's current checkpoint, and also satisfy any availability policies set by relying parties who trust the CA. 1052 | 1053 | An entry is said to be *available* if its index is greater than or equal to the minimum index. A checkpoint is said to be available if its tree size is greater than the minimum index. A subtree `[start, end)` is said to be available if `end` is greater than the minimum index. 1054 | 1055 | Log protocols MUST serve enough information to allow a log client to efficiently obtain the following: 1056 | 1057 | * Signatures over the latest checkpoint by the CA's cosigners ({{certification-authority-cosigners}}) 1058 | * Any individual available log entry ({{log-entries}}) 1059 | * The hash value of any available checkpoint 1060 | * An inclusion proof ({{Section 2.1.3 of !RFC9162}}) for any available entry to any containing checkpoint 1061 | * A consistency proof ({{Section 2.1.4 of !RFC9162}}) between any two available checkpoints 1062 | * The hash value of any available subtree ({{subtrees}}) 1063 | * A subtree inclusion proof ({{subtree-inclusion-proofs}}) for any available entry in any containing subtree 1064 | * A subtree consistency proof ({{subtree-consistency-proofs}}) between any available subtree to any containing checkpoint 1065 | 1066 | Meeting these requirements requires a log to retain some information about pruned entries. Given a node `[start, end)` in the Merkle Tree, if `end` is less than or equal to the minimum index, the node's children MAY be discarded in favor of the node's hash. 1067 | 1068 | {{fig-prune-tree}} shows an example pruned tree with 13 elements, where the minimum index is 7. It shows the original tree, followed by the pruned tree. The pruned tree depicts the nodes that MUST be available or computable. Note that entry 6 MAY be discarded, only the hash of entry 6 must be available. 1069 | 1070 | ~~~aasvg 1071 | +-----------------------------+ 1072 | | [0, 13) | 1073 | +-----------------------------+ 1074 | / \ 1075 | +----------------+ +----------------+ 1076 | | [0, 8) | | [8, 13) | 1077 | +----------------+ +----------------+ 1078 | / \ / | 1079 | +--------+ +--------+ +---------+ | 1080 | | [0, 4) | | [4, 8) | | [8, 12) | | 1081 | +--------+ +--------+ +---------+ | 1082 | / \ / \ / \ | 1083 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ | 1084 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| |[8,10)| |[10,12)| | 1085 | +-----+ +-----+ +-----+ +-----+ +------+ +-------+ | 1086 | / \ / \ / \ / \ / \ / \ | 1087 | +=+ +=+ +=+ +=+ +=+ +=+ +=+ +=+ +=+ +=+ +==+ +==+ +==+ 1088 | |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12| 1089 | +=+ +=+ +=+ +=+ +=+ +=+ +=+ +=+ +=+ +=+ +==+ +==+ +==+ 1090 | 1091 | 1092 | +-----------------------------+ 1093 | | [0, 13) | 1094 | +-----------------------------+ 1095 | / \ 1096 | +----------------+ +----------------+ 1097 | | [0, 8) | | [8, 13) | 1098 | +----------------+ +----------------+ 1099 | / \ / | 1100 | +--------+ +--------+ +---------+ | 1101 | | [0, 4) | | [4, 8) | | [8, 12) | | 1102 | +--------+ +--------+ +---------+ | 1103 | / \ / \ | 1104 | +-----+ +-----+ +------+ +-------+ | 1105 | |[4,6)| |[6,8)| |[8,10)| |[10,12)| | 1106 | +-----+ +-----+ +------+ +-------+ | 1107 | / \ / \ / \ | 1108 | +-+ +=+ +=+ +=+ +==+ +==+ +==+ 1109 | |6| |7| |8| |9| |10| |11| |12| 1110 | +-+ +=+ +=+ +=+ +==+ +==+ +==+ 1111 | ~~~ 1112 | {: #fig-prune-tree title="An example showing the minimum nodes that must be available after pruning"} 1113 | 1114 | Logs MAY retain additional nodes, or expect log clients to compute required nodes from other nodes. For example, in {{fig-prune-tree}}, the log's serving protocol MAY instead serve `[0, 2)` and `[2, 4)`, with the log client computing `[0, 4)` from those values. 1115 | 1116 | # Certificates 1117 | 1118 | This section defines how to construct Merkle Tree Certificates, which are X.509 Certificates {{!RFC5280}} that assert the information in an issuance log entry. A Merkle Tree Certificate is constructed from the following: 1119 | 1120 | * A TBSCertificateLogEntry ({{log-entries}}) contained in the issuance log ({{issuance-logs}}) 1121 | * A subject public key whose hash matches the TBSCertificateLogEntry 1122 | * A subtree ({{subtrees}}) that contains the log entry 1123 | * Zero or more signatures ({{cosigners}}) over the subtree, which together satisfy relying party requirements ({{trusted-cosigners}}) 1124 | 1125 | For any given TBSCertificateLogEntry, there are multiple possible certificates that may prove the entry is certified by the CA and publicly logged, varying by choice of subtree and signatures. {{certificate-format}} defines how the certificate is constructed based on those choices. {{full-certificates}} and {{signatureless-certificates}} define two profiles of Merkle Tree Certificates, full certificates and signatureless certificates, and how to select the subtree and signatures for them. 1126 | 1127 | ## Certificate Format 1128 | 1129 | The information is encoded in an X.509 Certificate {{!RFC5280}} as follows: 1130 | 1131 | The TBSCertificate's `version`, `issuer`, `validity`, `subject`, `issuerUniqueID`, `subjectUniqueID`, and `extensions` MUST match the corresponding fields of the TBSCertificateLogEntry. Per {{log-entries}}, this means `issuer` MUST be the issuance log's log ID as an X.509 distinguished name, as described in {{log-ids}}. 1132 | 1133 | The TBSCertificate's `serialNumber` MUST contain the zero-based index of the TBSCertificateLogEntry in the log. {{Section 4.1.2.2 of !RFC5280}} forbids zero as a serial number, but {{log-entries}} defines a `null_entry` type for use in entry zero, so the index will be positive. This encoding is intended to avoid implementation errors by having the serial numbers and indices off by one. 1134 | 1135 | The TBSCertificate's `subjectPublicKeyInfo` contains the specified public key. Its hash MUST match the TBSCertificateLogEntry's `subjectPublicKeyInfoHash`. 1136 | 1137 | The TBSCertificate's `signature` and the Certificate's `signatureAlgorithm` MUST contain an AlgorithmIdentifier whose `algorithm` is id-alg-mtcProof, defined below, and whose `parameters` is omitted. 1138 | 1139 | ~~~asn.1 1140 | id-alg-mtcProof OBJECT IDENTIFIER ::= { 1141 | iso(1) identified-organization(3) dod(6) internet(1) security(5) 1142 | mechanisms(5) pkix(7) algorithms(6) TBD} 1143 | ~~~ 1144 | 1145 | For initial experimentation, early implementations of this design will use the OID 1.3.6.1.4.1.44363.47.0 instead of `id-alg-mtcProof`. 1146 | 1147 | The `signatureValue` contains an MTCProof structure, defined below using the TLS presentation language ({{Section 3 of !RFC8446}}): 1148 | 1149 | ~~~tls-presentation 1150 | opaque HashValue[HASH_SIZE]; 1151 | 1152 | struct { 1153 | TrustAnchorID cosigner_id; 1154 | opaque signature<0..2^16-1>; 1155 | } MTCSignature; 1156 | 1157 | struct { 1158 | uint64 start; 1159 | uint64 end; 1160 | HashValue inclusion_proof<0..2^16-1>; 1161 | MTCSignature signatures<0..2^16-1>; 1162 | } MTCProof; 1163 | ~~~ 1164 | 1165 | `start` and `end` MUST contain the corresponding parameters of the chosen subtree. `inclusion_proof` MUST contain a subtree inclusion proof ({{subtree-inclusion-proofs}}) for the log entry and the subtree. `signatures` contains the chosen subtree signatures. In each signature, `cosigner_id` contains the cosigner ID ({{cosigners}}) in its binary representation ({{Section 3 of !I-D.ietf-tls-trust-anchor-ids}}), and `signature` contains the signature value as described in {{signature-format}}. 1166 | 1167 | The MTCProof is encoded into the `signatureValue` with no additional ASN.1 wrapping. The most significant bit of the first octet of the signature value SHALL become the first bit of the bit string, and so on through the least significant bit of the last octet of the signature value, which SHALL become the last bit of the bit string. 1168 | 1169 | ## Full Certificates 1170 | 1171 | A *full certificate* is a Merkle Tree certificate which contains sufficient signatures to allow a relying party to trust the choice of subtree, without any predistributed information beyond the cosigner(s) parameters. Full certificates can be issued without significant processing delay. 1172 | 1173 | When issuing a certificate, the CA first adds the TBSCertificateLogEntry to its issuance log. It then schedules a job to construct a checkpoint and collect cosignatures. The job proceeds as follows: 1174 | 1175 | 1. The CA signs the checkpoint with its key(s) ({{certification-authority-cosigners}}). 1176 | 2. Using the procedure in {{arbitrary-intervals}}, the CA determines the two subtrees that cover the entries added between this checkpoint and the most recent checkpoint. 1177 | 3. The CA signs each subtree with its key(s) ({{cosigners}}). 1178 | 4. The CA requests sufficient checkpoint cosignatures ({{cosigners}}) from external cosigners to meet relying party requirements ({{trusted-cosigners}}). 1179 | 5. The CA requests subtree cosignatures ({{requesting-subtree-signatures}}) from the cosigners above. 1180 | 6. For each certificate in the interval, the CA constructs certificates ({{certificate-format}}) using the covering subtree. 1181 | 1182 | Steps 4 and 5 are analogous to requesting SCTs from CT logs in Certificate Transparency, except that a single run of this job collects signatures for many certificates at once. The CA MAY request signatures from a redundant set of cosigners and select the ones that complete first. 1183 | 1184 | This document does not prescribe the specific cosigner roles, or a particular protocol for requesting cosignatures. Protocols for cosigners MAY vary depending on the needs for that cosigner. A consistency-only cosigner, such as {{TLOG-WITNESS}}, might only require a checkpoint signature and consistency proof, while a mirroring cosigner, such as {{TLOG-MIRROR}} might require the full log contents. 1185 | 1186 | A cosigner MAY expose a private interface for the CA, to reduce denial-of-service risk, or a cosigner MAY expose a public interface for other parties to request additional cosignatures. The latter may be useful if a relying party requires a cosigner that the CA does not communicate with. In this case, an authenticating party MAY request cosignatures and add them to the certificate. However, it is RECOMMENDED that the CA collect cosignatures for the authenticating party. This simplifies deployment, as relying party policies change over time. 1187 | 1188 | This document does not place any requirements on how frequently this job runs. More frequent runs results in lower issuance delay, but higher signing overhead. It is RECOMMENDED that CAs run at most one instance of this job at a time, starting the next instance after the previous one completes. A single run collects signatures for all entries since the most recent checkpoint, so there is little benefit to overlapping them. Less frequent runs may also aid relying parties that wish to directly audit signatures, as described in Section 5.2 of {{AuditingRevisited}}, though this document does not define such a system. 1189 | 1190 | ## Signatureless Certificates 1191 | 1192 | A *signatureless certificate* is a Merkle Tree certificate which contains no signatures and instead assumes the relying party had predistributed information about which subtrees were trusted. Signatureless certificates are an optional size optimization. They require a processing delay to construct, and only work in a sufficiently up-to-date relying party. Authenticating parties thus SHOULD deploy a corresponding full certificate alongside any signatureless certificate, and use some application-protocol-specific mechanism to select between the two. {{use-in-tls}} discusses such a mechanism for TLS {{!RFC8446}}. 1193 | 1194 | ### Landmarks 1195 | 1196 | A signatureless certificate is constructed based on a *landmark sequence*, which is a sequence of *landmarks*. Landmarks are agreed-upon tree sizes across the ecosystem for optimizing certificates. Landmarks SHOULD be allocated by the CA, but they can also be allocated by some other coordinating party. It is possible, but NOT RECOMMENDED, for multiple landmark sequences to exist per CA. Landmarks are allocated to balance minimizing the delay in obtaining a signatureless certificate with minimizing the size of the relying party's predistributed state. 1197 | 1198 | A landmark sequence has the following fixed parameters: 1199 | 1200 | * `base_id`: An OID arc for trust anchor IDs of individual landmarks 1201 | * `max_landmarks`: A positive integer, describing the maximum number of landmarks that may contain unexpired certificates at any time 1202 | * `landmark_url`: Some URL to fetch the current list of landmarks 1203 | 1204 | Landmarks are numbered consecutively from zero. Each landmark has a trust anchor ID, determined by appending the landmark number to `base_id`. For example, the trust anchor ID for landmark 42 of a sequence with `base_id` of `32473.1` would be `32473.1.42`. 1205 | 1206 | Each landmark specifies a tree size. The first landmark, numbered zero, is always a tree size of zero. The sequence of tree sizes MUST be append-only and strictly monotonically increasing. 1207 | 1208 | Landmarks determine *landmark subtrees*: for each landmark, other than number zero, let `tree_size` be the landmark's tree size and `prev_tree_size` be that of the previous landmark. As described in {{arbitrary-intervals}}, select the one or two subtrees that cover `[prev_tree_size, tree_size)`. Each of those subtrees is a landmark subtree. Landmark zero has no landmark subtrees. 1209 | 1210 | The most recent `max_landmarks` landmarks are said to be *active*. Landmarks MUST be allocated such that, at any given time, only active landmarks contain unexpired certificates. The active landmark subtrees are those determined by the active landmarks. There are at most `2 * max_landmarks` active landmark subtrees at any time. Every unexpired entry will be contained in one or more landmark subtree, or between the last landmark subtree and the latest checkpoint. Active landmark subtrees are predistributed to the relying party as trusted subtrees, as described in {{trusted-subtrees}}. 1211 | 1212 | It is RECOMMENDED that landmarks be allocated following the procedure described in {{allocating-landmarks}}. If landmarks are allocated incorrectly (e.g. past landmarks change, or `max_landmarks` is inaccurate), there are no security consequences, but some older certificates may fail to validate. 1213 | 1214 | Relying parties will locally retain up to `2 * max_landmarks` hashes ({{trusted-subtrees}}) per CA, so `max_landmarks` should be set to balance the delay between landmarks and the amount of state the relying party must maintain. Using the recommended procedure above, a CA with a maximum certificate lifetime of 7 days, allocating a landmark every hour, will have a `max_landmarks` of 168. The client state is then 336 hashes, or 10,752 bytes with SHA-256. 1215 | 1216 | `landmark_url` MUST serve a resource with `Content-Type: text/plain; charset=utf-8` and the following lines. Each line MUST be terminated by a newline character (U+000A): 1217 | 1218 | * Two space-separated non-negative decimal integers: ` `. 1219 | This line MUST satisfy the following, otherwise it is invalid: 1220 | * `num_active_landmarks <= max_landmarks` 1221 | * `num_active_landmarks <= last_landmark` 1222 | * `num_active_landmarks + 1` lines each containing a single non-negative decimal integer, containing a tree size. Numbered from zero to `num_active_landmarks`, line `i` contains the tree size for landmark `last_landmark - i`. The integers MUST be strictly monotonically decreasing and lower or equal to the log's latest tree size. 1223 | 1224 | ### Allocating Landmarks 1225 | 1226 | It is RECOMMENDED that landmarks be allocated using the following procedure: 1227 | 1228 | 1. Select some `time_between_landmarks` duration. Define a series of consecutive, non-overlapping time intervals, each of duration `time_between_landmarks`. 1229 | 2. At most once per time interval, append the latest checkpoint tree size to the landmark sequence if it is greater than the last landmark's tree size. 1230 | 1231 | To ensure that only active landmarks contain unexpired certificates, set `max_landmarks` to `ceil(max_cert_lifetime / time_between_landmarks) + 1`, where `max_cert_lifetime` is the CA's maximum certificate lifetime. 1232 | 1233 | ### Constructing Signatureless Certificates 1234 | 1235 | Given a TBSCertificateLogEntry in the issuance log and a landmark sequence, a signatureless certificate is constructed as follows: 1236 | 1237 | 1. Wait for the first landmark to be allocated that contains the entry. 1238 | 2. Determine the landmark's subtrees and select the one that contains the entry. 1239 | 3. Construct a certificate ({{certificate-format}}) using the selected subtree and no signatures. 1240 | 1241 | Before sending this certificate, the authenticating party SHOULD obtain some application-protocol-specific signal that implies the relying party has been configured with the corresponding landmark. ({{trusted-subtrees}} defines how relying parties are configured.) The trust anchor ID of the landmark may be used as an efficient identifier in the application protocol. {{use-in-tls}} discusses how to do this in TLS {{!RFC8446}}. 1242 | 1243 | ## Size Estimates 1244 | 1245 | The inclusion proofs in full and signatureless certificates scale logarithmically with the size of the subtree. These sizes can be estimated with the CA's issuance rate. The byte counts below assume the issuance log's hash function is SHA-256. 1246 | 1247 | Some organizations have published statistics which can be used to estimate this rate for the Web PKI. As of June 9th, 2025: 1248 | 1249 | * {{LetsEncrypt}} reported around 558,000,000 active certificates for a single CA 1250 | * {{MerkleTown}} reported around 2,100,000,000 unexpired certificates in CT logs, across all CAs 1251 | * {{MerkleTown}} reported an issuance rate of around 444,000 certificates per hour, across all CAs 1252 | 1253 | The current issuance rate across the Web PKI may not necessarily be representative of the Web PKI after a transition to short-lived certificates. Assuming a certificate lifetime of 7 days, and that subscribers will update their certificates 75% of the way through their lifetime (see {{certificate-renewal}}), every certificate will be reissued every 126 hours. This gives issuance rate estimates of around 4,400,000 certificates per hour and 17,000,000 certificates per hour, for the first two values above. Note the larger estimate is across all CAs, while subtrees would only span one CA. 1254 | 1255 | Using the per-CA short lifetime estimate, if the CA mints a checkpoint every 2 seconds, full certificate subtrees will span around 2,500 certificates, leading to 12 hashes in the inclusion proof, or 384 bytes. Full certificates additionally must carry a sufficient set of signatures to meet relying party requirements. 1256 | 1257 | If a new landmark is allocated every hour, signatureless certificate subtrees will span around 4,400,000 certificates, leading to 23 hashes in the inclusion proof, giving an inclusion proof size of 736 bytes, with no signatures. This is significantly smaller than a single ML-DSA-44 signature, 2,420 bytes, and almost ten times smaller than the three ML-DSA-44 signatures necessary to include post-quantum SCTs. 1258 | 1259 | The proof sizes grow logarithmically, so 32 hashes, or 1024 bytes, is sufficient for subtrees of up to 232 (4,294,967,296) certificates. 1260 | 1261 | # Relying Parties 1262 | 1263 | This section discusses how relying parties verify Merkle Tree Certificates. 1264 | 1265 | ## Trust Anchors 1266 | 1267 | In order to accept certificates from a Merkle Tree CA, a relying party MUST be configured with: 1268 | 1269 | * The log ID ({{log-ids}}) 1270 | * A set of supported cosigners, as pairs of cosigner ID and public key 1271 | * A policy on which combinations of cosigners to accept in a certificate ({{trusted-cosigners}}) 1272 | * An optional list of trusted subtrees, with their hashes, that are known to be consistent with the relying party's cosigner requirements ({{trusted-subtrees}}) 1273 | * A list of revoked ranges of indices ({{revocation-by-index}}) 1274 | 1275 | [[TODO: Define some representation for this. In a trust anchor, there's a lot of room for flexibility in what the client stores. In principle, we could even encode some of this information in an X.509 intermediate certificate, if an application wishes to use this with a delegation model with intermediates, though the security story becomes more complex. Decide how/whether to do that.]] 1276 | 1277 | ## Verifying Certificate Signatures 1278 | 1279 | When verifying the signature on an X.509 certificate (Step (a)(1) of {{Section 6.1.3 of !RFC5280}}) whose issuer is a Merkle Tree CA, the relying party performs the following procedure: 1280 | 1281 | 1. Check that the TBSCertificate's `signature` field is `id-alg-mtcProof` with omitted parameters. If either check fails, abort this process and fail verification. 1282 | 1283 | 1. Decode the `signatureValue` as an MTCProof, as described in {{certificate-format}}. 1284 | 1285 | 1. Let `index` be the certificate's serial number. If `index` is contained in one of the relying party's revoked ranges ({{revocation-by-index}}), abort this process and fail verification. 1286 | 1287 | 1. Construct a TBSCertificateLogEntry as follows: 1288 | 1. Copy the `version`, `issuer`, `validity`, `subject`, `issuerUniqueID`, `subjectUniqueID`, and `extensions` fields from the TBSCertificate. 1289 | 1. Set `subjectPublicKeyInfoHash` to the hash of the DER encoding of `subjectPublicKeyInfo`. 1290 | 1291 | 1. Construct a MerkleTreeCertEntry of type `tbs_cert_entry` with contents the TBSCertificateLogEntry. Let `entry_hash` be the hash of the entry, `MTH({entry}) = HASH(0x00 || entry)`, as defined in {{Section 2.1.1 of !RFC9162}}. 1292 | 1293 | 1. Let `expected_subtree_hash` be the result of evaluating the MTCProof's `inclusion_proof` for entry `index`, with hash `entry_hash`, of the subtree described by the MTCProof's `start` and `end`, following the procedure in {{evaluating-a-subtree-inclusion-proof}}. If evaluation fails, abort this process and fail verification. 1294 | 1295 | 1. If `[start, end)` matches a trusted subtree ({{trusted-subtrees}}), check that `expected_subtree_hash` is equal to the trusted subtree's hash. Return success if it matches and failure if it does not. 1296 | 1297 | 1. Otherwise, check that the MTCProof's `signatures` contain a sufficient set of valid signatures from cosigners to satisfy the relying party's cosigner requirements ({{trusted-cosigners}}). Unrecognized cosigners MUST be ignored. Signatures are verified as described in {{signature-format}}. The `hash` field of the MTCSubtree is set to `expected_subtree_hash`. 1298 | 1299 | This procedure only replaces the signature verification portion of X.509 path validation. The relying party MUST continue to perform other checks, such as checking expiry. 1300 | 1301 | ## Trusted Cosigners 1302 | 1303 | A relying party's cosigner policy determines the sets of cosigners that must sign a view of the issuance log before it is trusted. 1304 | 1305 | This document does not prescribe a particular policy, but gives general guidance. Relying parties MAY implement policies other than those described below, and MAY incorporate cosigners acting in roles not described in this document. 1306 | 1307 | In picking trusted cosigners, the relying party SHOULD ensure the following security properties: 1308 | 1309 | Authenticity: 1310 | : The relying party only accepts entries certified by the CA 1311 | 1312 | Transparency: 1313 | : The relying party only accepts entries that are publicly accessible, so that monitors, particularly the subject of the certificate, can notice any unauthorized certificates 1314 | 1315 | Relying parties SHOULD ensure authenticity by requiring a signature from the most recent CA cosigner key. If the CA is transitioning from an old to new key, the relying party SHOULD accept both until certificates that predate the new key expire. This is analogous to the signature in a traditional X.509 certificate. 1316 | 1317 | While a CA signature is sufficient to prove a subtree came from the CA, this is not enough to ensure the certificate is visible to monitors. A misbehaving CA might not operate the log correctly, either presenting inconsistent versions of the log to relying parties and monitors, or refuse to publish some entries. 1318 | 1319 | To mitigate this, relying parties SHOULD ensure transparency by requiring a quorum of signatures from additional cosigners. At minimum, these cosigners SHOULD enforce a consistent view of the log. For example, {{TLOG-WITNESS}} describes a lightweight "witness" cosigner role that checks this with consistency proofs. This is not sufficient to ensure durable logging. {{revocation-by-index}} discusses mitigations for this. Alternatively, a relying party MAY require cosigners that serve a copy of the log, in addition to enforcing a consistent view. For example, {{TLOG-MIRROR}} describes a "mirror" cosigner role. 1320 | 1321 | Relying parties MAY accept the same set of additional cosigners across issuance logs. 1322 | 1323 | Cosigner roles are extensible without changes to certificate verification itself. Future specifications and individual deployments MAY define other cosigner roles to incorporate into relying party policies. 1324 | 1325 | {{choosing-cosigners}} discusses additional deployment considerations in cosigner selection. 1326 | 1327 | ## Trusted Subtrees 1328 | 1329 | As an optional optimization, a relying party MAY incorporate a periodically updated, predistributed list of active landmark subtrees, determined as described in {{landmarks}}. The relying party configures these as trusted subtrees, allowing it to accept signatureless certificates ({{signatureless-certificates}}) constructed against those subtrees. 1330 | 1331 | Before configuring the subtrees as trusted, the relying party MUST obtain assurance that each subtree is consistent with checkpoints observed by a sufficient set of cosigners (see {{cosigners}}) to meet its cosigner requirements. It is not necessary that the cosigners have generated signatures over the specific subtrees, only that they are consistent. 1332 | 1333 | This criteria can be checked given: 1334 | 1335 | * Some *reference checkpoint* that contains the latest landmark 1336 | * For each cosigner, either: 1337 | * A cosignature on the reference checkpoint 1338 | * A cosigned checkpoint containing the referenced checkpoint and a valid Merkle consistency proof ({{Section 2.1.4 of !RFC9162}}) between the two 1339 | * For each subtree, a valid subtree consistency proof ({{subtree-consistency-proofs}}) between the subtree and the reference checkpoint 1340 | 1341 | [[TODO: The subtree consistency proofs have many nodes in common. It is possible to define a single "bulk consistency proof" that verifies all the hashes at once, but it's a lot more complex.]] 1342 | 1343 | This document does not prescribe how relying parties obtain this information. A relying party MAY, for example, use an application-specific update service, such as the services described in {{CHROMIUM}} and {{FIREFOX}}. If the relying party considers the service sufficiently trusted (e.g. if the service provides the trust anchor list or certificate validation software), it MAY trust the update service to perform these checks. 1344 | 1345 | The relying party SHOULD incorporate its trusted subtree configuration in application-protocol-specific certificate selection mechanisms, to allow an authenticating party to select a signatureless certificate. The trust anchor IDs of the landmarks may be used as efficient identifiers in the application protocol. {{use-in-tls}} discusses how to do this in TLS {{!RFC8446}}. 1346 | 1347 | ## Revocation by Index 1348 | 1349 | For each supported Merkle Tree CA, the relying party maintains a list of revoked ranges of indices. This allows a relying party to efficiently revoke entries of an issuance log, even if the contents are not necessarily known. This may be used to mitigate the security consequences of misbehavior by a CA, or other parties in the ecosystem. 1350 | 1351 | When a relying party is first configured to trust a CA, it SHOULD be configured to revoke all entries from zero up to but not including the first available unexpired certificate at the time. This revocation SHOULD be periodically updated as entries expire and logs are pruned ({{log-pruning}}). In particular, when CAs prune entries, relying parties SHOULD be updated to revoke all newly unavailable entries. This gives assurance that, even if some unavailable entry had not yet expired, the relying party will not trust it. It also allows monitors to start monitoring a log without processing expired entries. 1352 | 1353 | A misbehaving CA might correctly construct a globally consistent log, but refuse to make some entries or intermediate nodes available. Consistency proofs between checkpoints and subtrees would pass, but monitors cannot observe the entries themselves. Relying parties whose cosigner policies ({{trusted-cosigners}}) do not require durable logging (e.g. via {{TLOG-MIRROR}}) are particularly vulnerable to this. In this case, the indices of the missing entries will still be known, so relying parties can use this mechanism to revoke the unknown entries, possibly as an initial, targeted mitigation before a complete CA removal. 1354 | 1355 | When a CA is found to be untrustworthy, relying parties SHOULD remove trust in that CA. To minimize the compatibility impact of this mitigation, index-based revocation can be used to only distrust entries after some index, while leaving existing entries accepted. This is analogous to the {{SCTNotAfter}} mechanism used in some PKIs. 1356 | 1357 | # Use in TLS 1358 | 1359 | Most X.509 fields such as subjectPublicKeyInfo and X.509 extensions such as subjectAltName are unmodified in Merkle Tree certificates. They apply to TLS-based applications as in a traditional X.509 certificate. The primary new considerations for use in TLS are: 1360 | 1361 | * Whether the authenticating party should send a certificate from one Merkle Tree CA, another Merkle Tree CA, or a traditional X.509 CA 1362 | * Whether the authenticating party should send a full or signatureless certificate 1363 | * What the relying party should communicate to the authenticating party to help it make this decision 1364 | 1365 | Certificate selection in TLS, described in {{Section 4.4.2.2 and Section 4.4.2.3 of !RFC8446}}, incorporates both explicit relying-party-provided information in the ClientHello and CertificateRequest messages and implicit deployment-specific assumptions. This section describes a RECOMMENDED integration of Merkle Tree certificates into TLS trust anchor IDs ({{!I-D.ietf-tls-trust-anchor-ids}}), but applications MAY use application-specific criteria in addition to, or instead of, this recommendation. 1366 | 1367 | ## Extensions to Trust Anchor IDs 1368 | 1369 | [[TODO: Move this into draft-ietf-tls-trust-anchor-ids once the PLANTS WG is further along. See https://github.com/tlswg/tls-trust-anchor-ids/issues/62]] 1370 | 1371 | A TLS deployment may know that all relying parties that accept one trust anchor must additionally accept another trust anchor, or desire identifiers for groups of related trust anchors. For example, in this document, the relying party will recognize up to `max_landmark` consecutive landmarks, so the latest landmark can be used to represent the range. 1372 | 1373 | Incorporating this knowledge into certificate selection can optimize the ClientHello or CertificateRequest extension. It is RECOMMENDED that this information be provisioned alongside the certificate, e.g. provided by the CA. This section extends the CertificatePropertyList structure ({{Section 6 of !I-D.ietf-tls-trust-anchor-ids}}) with the `additional_trust_anchor_ranges` certificate property to do this: 1374 | 1375 | ~~~ tls-presentation 1376 | enum { 1377 | additional_trust_anchor_ranges(1), (2^16-1) 1378 | } CertificatePropertyType; 1379 | 1380 | struct { 1381 | TrustAnchorID base; 1382 | uint64 min; 1383 | uint64 max; 1384 | } TrustAnchorRange; 1385 | 1386 | TrustAnchorRange TrustAnchorRangeList<1..2^16-1>; 1387 | ~~~ 1388 | 1389 | A trust anchor range `r` is said to *contain* a trust anchor ID `id`, if `id`, as a relative OID, is the concatenation of `r.base` and some integer component between `min` and `max`, inclusive. 1390 | 1391 | The following procedure can be used to perform this check. It succeeds if `r` contains `id` and fails otherwise: 1392 | 1393 | 1. Check that `r.base` does not end in the middle of an OID component. That is, check that the most-significant bit of the last byte of `r.base` is unset. If it is set, fail the procedure. 1394 | 2. Check that `r.base` is a prefix of `id`. If not, fail the procedure. Let `rest` be `id` with the `r.base` prefix removed. 1395 | 3. Decode `rest` as a minimally-encoded, big-endian, base-128 OID component as follows: 1396 | 1. If `rest` is empty, fail the procedure. 1397 | 2. If the most-significant bit of the last byte of `rest` is set, fail the procedure. 1398 | 3. If the most-significant bit of any other byte of `rest` is unset, fail the procedure. 1399 | 4. If the first byte of `rest` is 0x80, fail the procedure. 1400 | 5. Set `v` to zero. Throughout this procedure, `v` will be less than 264. 1401 | 6. For each byte `b` of `rest`: 1402 | 1. If `v` is greater than or equal to 257, fail the procedure. 1403 | 2. Set `v` to `(v << 7) + (b & 127)`. 1404 | 4. Check if `min <= v <= max`. If this is not true, fail the procedure. Otherwise, the procedure succeeds. 1405 | 1406 | {{Section 4.2 of !I-D.ietf-tls-trust-anchor-ids}} is updated as follows. If the ClientHello or CertificateRequest contains a `trust_anchors extension`, the authenticating party SHOULD send a certification path such that one of the following is true: 1407 | 1408 | * The certification path's trust anchor ID appears in the relying party's `trust_anchors` extension, or 1409 | * One of the certification path's additional trust anchor ranges contains some ID in the relying party's `trust_anchors` extension 1410 | 1411 | Trust anchor ranges do not impact an authenticating party's list of available trust anchors in EncryptedExtensions (see {{Section 4.3 of !I-D.ietf-tls-trust-anchor-ids}}) or the HTTPS/SVCB record (see {{Section 5 of !I-D.ietf-tls-trust-anchor-ids}}). Those continue to reference the single trust anchor ID that corresponds to each certificate. 1412 | 1413 | In applications that use additional trust anchor ranges, relying parties MAY send a single trust anchor ID to represent all certificates whose trust anchor ranges contain that trust anchor ID. This includes: 1414 | 1415 | * Trust anchors that are sent in response to an EncryptedExtensions or HTTPS/SVCB message from the authenticating party 1416 | * Trust anchors that are sent in `trust_anchors`, independently of the authenticating party 1417 | 1418 | ## Using Trust Anchor IDs 1419 | 1420 | A full certificate will generally be accepted by relying parties that trust the issuing CA. To determine this, a full certificate has a trust anchor ID of the corresponding log ID ({{log-ids}}). The authenticating party can obtain this information either by parsing the certificate's issuer field or via out-of-band information as described in {{Section 3.2 of !I-D.ietf-tls-trust-anchor-ids}}. Authenticating and relying parties SHOULD use the `trust_anchors` extension to determine whether the full certificate would be acceptable. 1421 | 1422 | [[TODO: Ideally we would negotiate cosigners. https://github.com/tlswg/tls-trust-anchor-ids/issues/54 has a sketch of how one might do this, though other designs are possible. Negotiating cosigners allows the ecosystem to manage cosigners efficiently, without needing to collect every possible cosignature and send them all at once. This is wasteful, particularly with post-quantum algorithms.]] 1423 | 1424 | A full certificate MAY also be sent without explicit relying party trust signals, however doing so means the authenticating party implicitly assumes the relying party trusts the issuing CA. This may be viable if, for example, the CA is relatively ubiquitous among supported relying parties. 1425 | 1426 | A signatureless certificate, defined against landmark number `L`, has a trust anchor ID of `base_id`, concatenated with `L`, as described in {{landmarks}}, and SHOULD be provisioned with this value. Additionally, relying parties that trust later landmarks may also be assumed to trust landmark `L`, so a signatureless certificate SHOULD additionally provisioned with an additional trust anchor range whose `base` is `base_id`, `min` is `L`, and `max` is `L + max_landmarks - 1`. 1427 | 1428 | A relying party that has been configured with trusted subtrees ({{trusted-subtrees}}) derived from a set of landmarks SHOULD configure the `trust_anchors` extension to advertise the highest supported landmark in the set. The selection procedures defined in {{!I-D.ietf-tls-trust-anchor-ids}} and {{!extensions-to-trust-anchor-ids}} will then correctly determine whether a signatureless certificate is compatible with the relying party. 1429 | 1430 | When both a signatureless and full certificate are supported by a relying party, an authenticating party SHOULD preferentially use the signatureless certificate. A signatureless certificate asserts the same information as its full counterpart, but is expected to be smaller. An authenticating party SHOULD NOT send a signatureless certificate without a signal that the relying party trusts the corresponding landmark subtree. Even if the relying party is assumed to trust the issuing CA, the relying party may not have sufficiently up-to-date trusted subtrees. 1431 | 1432 | # ACME Extensions 1433 | 1434 | This section describes how to issue Merkle Tree certificates using ACME {{!RFC8555}}. 1435 | 1436 | When downloading the certificate ({{Section 7.4.2 of !RFC8555}}), ACME clients supporting Merkle Tree certificates SHOULD send "application/pem-certificate-chain-with-properties" in their Accept header ({{Section 12.5.1 of !RFC9110}}). ACME servers issuing Merkle Tree certificates SHOULD then respond with that content type and include trust anchor ID information as described in {{Section 6 of !I-D.ietf-tls-trust-anchor-ids}}. {{use-in-tls}} decribes the trust anchor ID assignments for full and signatureless certificates. 1437 | 1438 | When processing an order for a Merkle Tree certificate, the ACME server moves the order to the "valid" state once the corresponding entry is sequenced in the issuance log. The order's certificate URL then serves the full certificate, constructed as described in {{full-certificates}}. 1439 | 1440 | The full certificate response SHOULD additionally carry a alternate URL for the signatureless certificate, as described {{Section 7.4.2 of !RFC8555}}. Before the signatureless certificate is available, the alternate URL SHOULD return a HTTP 503 (Service Unavailable) response, with a Retry-After header ({{Section 10.2.3 of !RFC9110}}) estimating when the certificate will become available. Once the next landmark is allocated, the ACME server constructs a signatureless certificate, as described in {{signatureless-certificates}} and serves it from the alternate URL. 1441 | 1442 | ACME clients supporting Merkle Tree certificates SHOULD support fetching alternate chains. If an alternate chain returns an HTTP 503 with a Retry-After header, as described above, the client SHOULD retry the request at the specified time. 1443 | 1444 | # Deployment Considerations 1445 | 1446 | ## Operational Costs 1447 | 1448 | ### Certification Authority Costs 1449 | 1450 | While Merkle Tree certificates expects CAs to operate logs, the costs of these logs are expected to be much lower than a CT log from {{?RFC6962}} or {{?RFC9162}}: 1451 | 1452 | {{publishing-logs}} does not constrain the API to the one defined in {{?RFC6962}} or {{?RFC9162}}. If the PKI uses a tile-based protocol, such as {{TLOG-TILES}}, the issuance log benefits from the improved caching properties of such designs. 1453 | 1454 | Unlike a CT log, an issuance log does not have public submission APIs. Log entries are only added by the CA directly. The costs are thus expected to scale with the CA's own operations. 1455 | 1456 | A CA only needs to produce a digital signature for every checkpoint, rather than for every certificate. The lower signature rate requirements could allow more secure and/or economical key storage choices. 1457 | 1458 | Individual entries are kept small and do not scale with public key or signature sizes. This mitigates growth from post-quantum algorithms. Public keys in entries are replaced with fixed-sized hashes. There are no signatures in entries themselves, and only signatures on the very latest checkpoint are retained. Every new checkpoint completely subsumes the old checkpoint, so there is no need to retain older signatures. Likewise, a subtree is only signed if contained in another signed checkpoint. 1459 | 1460 | Log pruning ({{log-pruning}}) allows a long-lived log to serve only the more recent entries, scaling with the size of the retention window, rather than the log's total lifetime. 1461 | 1462 | Mirrors of the log can also reduce CA bandwidth costs, because monitors can fetch data from mirrors instead of CAs directly. In PKIs that deploy mirrors as part of cosigner policies, relying parties could set few availability requirements on CAs, as described in {{log-availability}}. 1463 | 1464 | ### Cosigner Costs 1465 | 1466 | The costs of cosigners vary by cosigner role. A consistency-checking cosigner, such as {{TLOG-WITNESS}}, requires very little state and can be run with low cost. 1467 | 1468 | A mirroring cosigner, such as {{TLOG-MIRROR}}, performs comparable roles as CT logs, but several of the cost-saving properties in {{certification-authority-costs}} also apply: improved protocols, smaller entries, less frequent signatures, and log pruning. While a mirror does need to accommodate another party's (the CA's) growth rate, it grows only from new issuances from that one CA. If one CA's issuance rate exceeds the mirror's capacity, that does not impact the mirror's copies of other CAs. Mirrors also do not need to defend against a client uploading a large number of existing certificates all at once. Submissions are also naturally batched and serialized. 1469 | 1470 | ### Monitor Costs 1471 | 1472 | In a CT-based PKI, every log carries a potentially distinct subset of active certificates, so monitors must check the contents of every CT log. At the same time, certificates are commonly synchronized between CT logs. As a result, a monitor will typically download each certificate multiple times, once for every log. In Merkle Tree Certificates, each entry appears in exactly one log. A relying party might require a log to be covered by a quorum of mirrors, but each mirror is cryptographically verified to serve the same contents. Once a monitor has obtained some entry from one mirror, it does not need to download it from the others. 1473 | 1474 | In addition to downloading each entry only once, the entries themselves are smaller, as discussed in {{certification-authority-costs}}. 1475 | 1476 | ## Choosing Cosigners 1477 | 1478 | In selecting trusted cosigners and cosigner requirements ({{trusted-cosigners}}), relying parties navigate a number of trade-offs: 1479 | 1480 | A consistency-checking cosigner, such as {{TLOG-WITNESS}}, is very cheap to run, but does not guarantee durable logging, while a mirroring cosigner is more expensive and may take longer to cosign structures. Requiring a mirror signature provides stronger guarantees to the relying party, which in turn can reduce the requirements on CAs (see {{log-availability}}), however it may cause certificate issuance to take longer. That said, mirrors are comparable to CT logs, if not cheaper (see {{operational-costs}}), so they may be appropriate in PKIs where running CT logs is already viable. 1481 | 1482 | Relying parties that require larger quorums of trusted cosigners can reduce the trust placed in any individual cosigner. However, these larger quorums result in larger, more expensive full certificates. The cost of this will depend on how frequently the signatureless optimization occurs in a given PKI. Conversely, relying parties that require smaller quorums have smaller full certificates, but place more trust in their cosigners. 1483 | 1484 | Relying party policies also impact monitor operation. If a relying party accepts any one of three cosigners, monitors SHOULD check the checkpoints of all three. Otherwise, a malicious CA may send different split views to different cosigners. More generally, monitors SHOULD check the checkpoints in the union of all cosigners trusted by all supported relying parties. This is an efficient check because, if the CA is operating correctly, all cosigners will observe the same tree. Thus the monitor only needs to check consistency proofs between the checkpoints, and check the log contents themselves once. Monitors MAY also rely on other parties in the transparency ecosystem to perform this check. 1485 | 1486 | ## Log Availability 1487 | 1488 | CAs and mirrors are expected to serve their log contents over HTTP. It is possible for the contents to be unavailable, either due to temporary service outage or because the log has been pruned ({{log-pruning}}). If some resources are unavailable, they may not be visible to monitors. 1489 | 1490 | As in CT, PKIs which deploy Merkle Tree certificates SHOULD establish availability policies, adhered to by trusted CAs and mirrors, and enforced by relying party vendors as a condition of trust. Exact availability policies for these services are out of scope for this document, but this section provides some general guidance. 1491 | 1492 | Availability policies SHOULD specify how long an entry must be made available, before a CA or mirror is permitted to prune the entry. It is RECOMMENDED to define this using a *retention period*, which is some time after the entry has expired. In such a policy, an entry could only be pruned if it, and all preceding entries, have already expired for the retention period. Policies MAY opt to set different retention periods between CAs and mirrors. Permitting limited log retention is analogous to the CT practice of temporal sharding {{CHROME-CT}}, except that a pruned issuance log remains compatible with older, unupdated relying parties. 1493 | 1494 | Such policies impact monitors. If the retention period is, e.g. 6 months, this means that monitors are expected to check entries of interest within 6 months. It also means that a new monitor may only be aware of a 6 month history of entries issued for a particular domain. 1495 | 1496 | If historical data is not available to verify the retention period, such as information in another mirror or a trusted summary of expiration dates of entries, it may not be possible to confirm correct behavior. This is mitigated by the revocation process described in {{revocation-by-index}}: if a CA were to prune a forward-dated entry and, in the 6 months when the entry was available, no monitor noticed the unusual expiry, an updated relying party would not accept it anyway. 1497 | 1498 | The log pruning process simply makes some resources unavailable, so availability policies SHOULD constrain log pruning in the same way as general resource availability. That is, if it would be a policy violation for the log to fail to serve a resource, it should also be a policy violation for the log to prune such that the resource is removed, and vice versa. 1499 | 1500 | PKIs that require mirror cosignatures ({{trusted-cosigners}}) can impose minimal to no availability requirements on CAs, all without compromising transparency goals. If a CA never makes some entry available, mirrors will be unable to update. This will prevent relying parties from accepting the undisclosed entries. However, a CA which is persistently unavailable may not offer sufficient benefit to be used by authenticating parties or trusted by relying parties. 1501 | 1502 | However, if a mirror's interface becomes unavailable, monitors may be unable to check for unauthorized issuance, if the entries are not available in another mirror. This does compromise transparency goals. As such, availability policies SHOULD set availability expectations on mirrors. This can also be mitigated by using multiple mirrors, either directly enforced in cosigner requirements, or by keeping mirrors up-to-date with each other. 1503 | 1504 | In PKIs that do not require mirroring cosigners, the CA's serving endpoint is more crucial for monitors. Such PKIs thus SHOULD set availability requirements on CAs. 1505 | 1506 | In each of these cases, availability failures can be mitigated by revoking the unavailable entries by index, as described in {{revocation-by-index}}, likely as a first step in a broader distrust. 1507 | 1508 | ## Certificate Renewal 1509 | 1510 | When an authenticating party requests a certificate, the signatureless certificate will not be available until the next landmark is ready. From there, the signatureless certificate will not be available until relying parties receive new trusted subtrees. 1511 | 1512 | To maximize coverage of the signatureless certificate optimization, authenticating parties performing routine renewal SHOULD request a new Merkle Tree certificate some time before the previous Merkle Tree certificate expires. Renewing around 75% into the previous certificate's lifetime is RECOMMENDED. Authenticating parties additionally SHOULD retain both the new and old certificates in the certificate set until the old certificate expires. As the new subtrees are delivered to relying parties, certificate negotiation will transition relying parties to the new certificate, while retaining the old certificate for relying parties that are not yet updated. 1513 | 1514 | The above also applies if the authenticating party is performing a routine key rotation alongside the routine renewal. In this case, certificate negotiation would pick the key as part of the certificate selection. This slightly increases the lifetime of the old key but maintains the size optimization continuously. 1515 | 1516 | If the service is rotating keys in response to a key compromise, this option is not appropriate. Instead, the service SHOULD immediately discard the old key and request a full certificate and the revocation of the previous certificate. This will interrupt the size optimization until the new signatureless certificate is available and relying parties are updated. 1517 | 1518 | ## Multiple CA Keys 1519 | 1520 | The separation between issuance logs and CA cosigners gives CAs additional flexibility in managing keys. A CA operator wishing to rotate keys, e.g. to guard against compromise of older key material, or upgrade to newer algorithms, could retain the same issuance log and sign its checkpoints and subtrees with both keys in parallel, until relying parties are all updated. Older relying parties would verify the older signatures, while newer relying parties would verify the newer signatures. A cosignature negotiation mechanism in the application protocol (see {{use-in-tls}}) would avoid using extra bandwidth for the two signatures. 1521 | 1522 | # Privacy Considerations 1523 | 1524 | The Privacy Considerations described in {{Section 9 of !I-D.ietf-tls-trust-anchor-ids}} apply to its use with Merkle Tree Certificates. 1525 | 1526 | In particular, relying parties that share an update process for trusted subtrees ({{trusted-subtrees}}) will fetch the same stream of updates. However, updates may reach different users at different times, resulting in some variation across users. This variation may contribute to a fingerprinting attack {{?RFC6973}}. If the Merkle Tree CA trust anchors are sent unconditionally in `trust_anchors`, this variation will be passively observable. If they are sent conditionally, e.g. with the DNS mechanism, the trust anchor list will require active probing. 1527 | 1528 | # Security Considerations 1529 | 1530 | ## Authenticity 1531 | 1532 | A key security requirement of any PKI scheme is that relying parties only accept assertions that were certified by a trusted certification authority. Merkle Tree certificates achieve this by ensuring the relying party only accepts authentic subtree hashes: 1533 | 1534 | * In full certificates, the relying party's cosigner requirements ({{trusted-cosigners}}) are expected to include some signature by the CA's cosigner. The CA's cosigner ({{certification-authority-cosigners}}) is defined to certify the contents of every checkpoint and subtree that it signs. 1535 | 1536 | * In signatureless certificates, the cosigner requirements are checked ahead of time, when the trusted subtrees are predistributed ({{trusted-subtrees}}). 1537 | 1538 | Given such a subtree hash, computed over entries that the CA certified, it then must be computationally infeasible to construct an entry not on this list, and some inclusion proof, such that inclusion proof verification succeeds. This requires using a collision-resistant hash in the Merkle Tree construction. 1539 | 1540 | Log entries contain public key hashes, so it must additionally be computationally infeasible to compute a public key whose hash matches the entry, other than the intended public key. This also requires a collision-resistant hash. 1541 | 1542 | ## Transparency 1543 | 1544 | The transparency mechanisms in this document do not prevent a CA from issuing an unauthorized certificate. Rather, they provide comparable security properties as Certificate Transparency {{?RFC9162}} in ensuring that all certificates are either rejected by relying parties, or visible to monitors and, in particular, the subject of the certificate. 1545 | 1546 | Compared to Certificate Transparency, some of the responsibilities of a log have moved to the CA. All signatures generated by the CA in this system are assertions about some view of the CA's issuance log. However, a CA does not need to function correctly to ensure transparency properties. Relying parties are expected to require a quorum of additional cosigners, which together enforce properties of the log ({{trusted-cosigners}}) and prevent or detect CA misbehavior: 1547 | 1548 | A CA might violate the append-only property of its log and present different views to different parties. However, each individual cosigner will only follow a single append-only view of the log history. Provided the cosigners are correctly operated, relying parties and monitors will observe consistent views between each other. Views that were not cosigned at all may not be detected, but they also will not be accepted by relying parties. 1549 | 1550 | If the CA sends one view to some cosigners and another view to other cosigners, it is possible that multiple views will be accepted by relying parties. However, in that case monitors will observe that cosigners do not match each other. Relying parties can then react by revoking the inconsistent indices ({{revocation-by-index}}), and likely removing the CA. If the cosigners are mirrors, the underlying entries in both views will also be visible. 1551 | 1552 | A CA might correctly construct its log, but refuse to serve some unauthorized entry, e.g. by feigning an outage or pruning the log outside the retention policy ({{log-availability}}). If the relying party requires cosignatures from trusted mirrors, the entry will either be visible to monitors in the mirrors, or have never reached a mirror. In the latter case, the entry will not have been cosigned, so the relying party would not accept it. If the relying party accepts log views without a trusted mirror, the unauthorized entry may not be available. However, the existence of _some_ entry at that index will be visible, so monitors will know the CA is failing to present an entry. Relying parties can then react by revoking the undisclosed entries by index ({{revocation-by-index}}), and likely removing the CA. 1553 | 1554 | ## Public Key Hashes 1555 | 1556 | Unlike Certificate Transparency, the mechanisms in this document do not provide the subject public keys, only the hashed values. This is intended to reduce log serving costs, particularly with large post-quantum keys. As a result, monitors look for unrecognized hashes instead of unrecognized keys. Any unrecognized hash, even if the preimage is unknown, indicates an unauthorized certificate. 1557 | 1558 | This optimization complicates studies of weak public keys, e.g. {{SharedFactors}}. Such studies will have to retrieve the public keys separately, such as by connecting to the TLS servers, or fetching from the CA if it retains the unhashed key. This document does not define a mechanism for doing this, or require that CAs or mirrors retain unhashed keys. The transparency mechanisms in this protocol are primarily intended to allow monitors to observe certificate issuance. 1559 | 1560 | ## Non-Repudiation 1561 | 1562 | When a monitor finds an unauthorized certificate issuance in a log or mirror, it must be possible to prove the CA indeed certified the information in the entry. However, only the latest checkpoint signature is retained by the transparency ecosystem, so it may not be possible to reconstruct the exact certificate seen by relying parties. 1563 | 1564 | However, per {{certification-authority-cosigners}}, any checkpoint signature is a binding assertion by the CA that it has certified every entry in the checkpoint. Thus, given *any* signed checkpoint that contains the unauthorized entry, a Merkle inclusion proof ({{Section 2.1.3 of ?RFC9162}}) is sufficient to prove the CA issued the entry. This is analogous to how, in {{Section 3.2.1 of ?RFC9162}}, CAs are held accountable for signed CT precertificates. 1565 | 1566 | The transparency ecosystem does not retain unhashed public keys, so it also may not be possible to construct a complete certificate from the checkpoint signature and inclusion proof. However, if the log entry's `subjectPublicKeyInfoHash` does not correspond to an authorized key for the subject of the certificate, the entry is still unauthorized. A Merkle Tree CA is held responsible for all log entries it certifies, whether or not the preimage of the hash is known. 1567 | 1568 | ## New Log Entry Types 1569 | 1570 | MerkleTreeCertEntry ({{log-entries}}) is extensible and permits protocol extensions to define new formats for the CA to certify. This means older CAs, cosigners, relying parties, and monitors might interact with new entries: 1571 | 1572 | {{log-entries}} and {{certification-authority-cosigners}} forbid a CA from logging or signing entries that it does not recognize. A CA cannot faithfully claim to certify information if it does not understand it. This is analogous to how a correctly-operated X.509 can never sign an unrecognized X.509 extension. 1573 | 1574 | External cosigners may or may not interact with the unrecognized entries. {{TLOG-MIRROR}} and {{TLOG-WITNESS}} describe cosigners whose roles do not interpret the contents of log entries. New entry types MAY be added without updating them. If a cosigner role does interpret a log entry, it MUST define how it interacts with unknown ones. 1575 | 1576 | If a relying party trusts an issuance log, but the issuance log contains an unrecognized entry, the entry will not cause it to accept an unexpected certificate. In {{verifying-certificate-signatures}}, the relying party constructs the MerkleTreeCertEntry that it expects. The unrecognized entry will have a different `type` value, so the proof will never succeed, assuming the underlying hash function remains collision-resistant. 1577 | 1578 | If a monitor observes an entry with unknown type, it may not be able to determine if it is of interest. For example, it may be unable to tell whether it covers some relevant DNS name. Until the monitor is updated to reflect the current state of the PKI, the monitor may be unable to detect all misissued certificates. 1579 | 1580 | This situation is analogous to the addition of a new X.509 extension. When relying parties add support for log entry types or new X.509 extensions, they SHOULD coordinate with monitors to ensure the transparency ecosystem is able to monitor the new formats. 1581 | 1582 | ## Certificate Malleability 1583 | 1584 | An ASN.1 structure like X.509’s Certificate is an abstract data type that is independent of its serialization. There are multiple encoding rules for ASN.1. Commonly, protocols use DER {{X.690}}, such as {{Section 4.4.2 of ?RFC8446}}. This aligns with {{Section 4.1.1.3 of ?RFC5280}}, which says X.509 signatures are computed over the DER-encoded TBSCertificate. After signature verification, applications can assume the DER-encoded TBSCertificate is not malleable. 1585 | 1586 | While the signature verification process in {{verifying-certificate-signatures}} first transforms the TBSCertificate into a TBSCertificateLogEntry, it preserves this non-malleability. There is a unique valid DER encoding for every abstract TBSCertificate structure, so malleability of the DER-encoded TBSCertificate reduces to malleability of the TBSCertificate value: 1587 | 1588 | * The `version`, `issuer`, `validity`, `subject`, `issuerUniqueID`, `subjectUniqueID`, and `extensions` fields are copied from the TBSCertificate to the TBSCertificateLogEntry unmodified, so they are directly authenticated by the inclusion proof. 1589 | 1590 | * `serialNumber` is omitted from TBSCertificateLogEntry, but its value determines the inclusion proof index, which authenticates it. 1591 | 1592 | * The redundant `signature` field in TBSCertificate is omitted from TBSCertificateLogEntry, but {{verifying-certificate-signatures}} checks for an exact value, so no other values are possible. 1593 | 1594 | * `subjectPublicKeyInfo` is hashed as `subjectPublicKeyInfoHash` in TBSCertificateLogEntry. Provided the underlying hash function is collision-resistant, no other values are possible for a given log entry. 1595 | 1596 | X.509 implementations often implement {{Section 4.1.1.3 of ?RFC5280}} by equivalently retaining the original received DER encoding, rather than recomputing the canonical DER encoding TBSCertificate. This optimization is compatible with the assumptions above. 1597 | 1598 | Some non-conforming X.509 implementations use a BER {{X.690}} parser instead of DER, and then apply this optimization to the received BER encoding. BER encoding is not unique, so this does not produce the same result. In such implementations, the BER-encoded TBSCertificate becomes also non-malleable, and applications may rely on this. To preserve this property in Merkle Tree Certificates, such non-conforming implementations MUST do the following when implementing {{verifying-certificate-signatures}}: 1599 | 1600 | * Reparse the initial identifier (the SEQUENCE tag) and length octets of the TBSCertificate structure with a conforming DER parser and fail verification if invalid. 1601 | 1602 | * When copying the `version`, `issuer`, `validity`, `subject`, `issuerUniqueID`, `subjectUniqueID`, and `extensions` fields, either copy over the observed BER encodings, or reparse each field with a conforming DER parser and fail verification if invalid. 1603 | 1604 | * Reparse the `serialNumber` field with a conforming DER parser and fail verification if invalid. 1605 | 1606 | * Reparse the `signature` field with a conforming DER parser and fail verification if invalid. Equivalently, check for an exact equality with for the expected, DER-encoded value. 1607 | 1608 | * When hashing `subjectPublicKeyInfo`, either hash the observed BER encoding, or reparse the structure with a conforming DER parser and fail verification if invalid. 1609 | 1610 | These additional checks are redundant in X.509 implementations that use a conforming DER parser. 1611 | 1612 | {{log-entries}} requires that the TBSCertificateLogEntry in a MerkleTreeCertEntry be DER-encoded, so applying a stricter parser will be compatible with conforming CAs. While these existing non-conforming implementations may be unable to switch to a DER parser due to compatibility concerns, Merkle Tree Certificates is new, so there is no existing deployment of malformed BER-encoded TBSCertificateLogEntry structures. 1613 | 1614 | The above only ensures the TBSCertificate portion is non-malleable. In Merkle Tree Certificates, similar to ECDSA X.509 signature, the signature value is malleable. Multiple MTCProof structures may prove a single TBSCertificate structure. Additionally, in all X.509-based protocols, a BER-based parser for the outer, unsigned Certificate structure will admit malleability in those portions of the encoding. Applications that derive a unique identifier from the Certificate MUST instead use the TBSCertificate, or some portion of it, for Merkle Tree Certificates. 1615 | 1616 | # IANA Considerations 1617 | 1618 | ## Module Identifier 1619 | 1620 | IANA is requested to add the following entry in the "SMI Security for PKIX Module Identifier" registry {{?RFC7299}}: 1621 | 1622 | | Decimal | Description | References | 1623 | |---------|-----------------|------------| 1624 | | TBD | id-mod-mtc-2025 | [this-RFC] | 1625 | 1626 | ## Algorithm 1627 | 1628 | IANA is requested to add the following entry to the "SMI Security for PKIX Algorithms" registry {{?RFC7299}}: 1629 | 1630 | | Decimal | Description | References | 1631 | |---------|-----------------|------------| 1632 | | TBD | id-alg-mtcProof | [this-RFC] | 1633 | 1634 | ## Relative Distinguished Name Attribute 1635 | 1636 | IANA is requested to add the following entry to the "SMI Security for PKIX Relative Distinguished Name Attribute" registry {{?I-D.ietf-lamps-x509-alg-none}}: 1637 | 1638 | | Decimal | Description | References | 1639 | |---------|-----------------------|------------| 1640 | | TBD | id-rdna-trustAnchorID | [this-RFC] | 1641 | 1642 | --- back 1643 | 1644 | # ASN.1 Module 1645 | 1646 | ~~~asn.1 1647 | MerkleTreeCertificates 1648 | { iso(1) identified-organization(3) dod(6) internet(1) 1649 | security(5) mechanisms(5) pkix(7) id-mod(0) 1650 | id-mod-mtc-2025(TBD) } 1651 | 1652 | DEFINITIONS IMPLICIT TAGS ::= 1653 | BEGIN 1654 | 1655 | IMPORTS 1656 | SIGNATURE-ALGORITHM 1657 | FROM AlgorithmInformation-2009 -- in [RFC5912] 1658 | { iso(1) identified-organization(3) dod(6) internet(1) 1659 | security(5) mechanisms(5) pkix(7) id-mod(0) 1660 | id-mod-algorithmInformation-02(58) } 1661 | ATTRIBUTE 1662 | FROM PKIX-CommonTypes-2009 -- in [RFC5912] 1663 | { iso(1) identified-organization(3) dod(6) internet(1) 1664 | security(5) mechanisms(5) pkix(7) id-mod(0) 1665 | id-mod-pkixCommon-02(57) } ; 1666 | TrustAnchorID 1667 | FROM TrustAnchorIDs-2025 -- in [I-D.ietf-tls-trust-ancohor-ids] 1668 | { iso(1) identified-organization(3) dod(6) internet(1) 1669 | security(5) mechanisms(5) pkix(7) id-mod(0) 1670 | id-mod-trustAnchorIDs-2025(TBD) } 1671 | 1672 | TBSCertificateLogEntry ::= SEQUENCE { 1673 | version [0] EXPLICIT Version DEFAULT v1, 1674 | issuer Name, 1675 | validity Validity, 1676 | subject Name, 1677 | subjectPublicKeyInfoHash OCTET STRING, 1678 | issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 1679 | subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 1680 | extensions [3] EXPLICIT Extensions OPTIONAL } 1681 | 1682 | id-alg-mtcProof OBJECT IDENTIFIER ::= { 1683 | iso(1) identified-organization(3) dod(6) internet(1) security(5) 1684 | mechanisms(5) pkix(7) algorithms(6) TBD} 1685 | 1686 | sa-mtcProof SIGNATURE-ALGORITHM ::= { 1687 | IDENTIFIER id-alg-mtcProof 1688 | PARAMS ARE absent 1689 | } 1690 | 1691 | id-rdna-trustAnchorID OBJECT IDENTIFIER ::= { 1692 | iso(1) identified-organization(3) dod(6) internet(1) security(5) 1693 | mechanisms(5) pkix(7) rdna(25) TBD} 1694 | 1695 | at-trustAnchorID ATTRIBUTE ::= { 1696 | TYPE TrustAnchorID 1697 | IDENTIFIED BY id-rdna-trustAnchorID 1698 | } 1699 | 1700 | END 1701 | ~~~ 1702 | 1703 | # Merkle Tree Structure 1704 | 1705 | This non-normative section describes how the Merkle Tree structure relates to the binary representations of indices. It is included to help implementors understand the procedures described in {{subtrees}}. 1706 | 1707 | ## Binary Representations 1708 | 1709 | Within a Merkle Tree whose size is a power of two, the binary representation of an leaf's index gives the path to that leaf. The leaf is a left child if the least-significant bit is unset and a right child if it is set. The next bit indicates the direction of the parent node, and so on. {{fig-merkle-tree-bits-full}} demonstrates this in a Merkle Tree of size 8: 1710 | 1711 | ~~~aasvg 1712 | +----------------+ 1713 | | [0, 8) | level 3 1714 | +----------------+ 1715 | / \ 1716 | +--------+ +--------+ 1717 | | [0, 4) | | [4, 8) | level 2 1718 | +--------+ +--------+ 1719 | / \ / \ 1720 | +-----+ +-----+ +-----+ +-----+ 1721 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| level 1 1722 | +-----+ +-----+ +-----+ +-----+ 1723 | / \ / \ / \ / \ 1724 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ 1725 | |0| |1| |2| |3| |4| |5| |6| |7| level 0 1726 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ 1727 | ~~~ 1728 | {: #fig-merkle-tree-bits-full title="An example Merkle Tree of size 8"} 1729 | 1730 | The binary representation of `4` is `0b100`. It is the left (0) child of `[4, 6)`, which is the left (0) child of `[4, 8)`, which is the right (1) child of `[0, 8)`. 1731 | 1732 | Each level in the tree corresponds to a bit position and can be correspondingly numbered, with 0 indicating the least-significant bit and the leaf level, and so on. In this numbering, a node's level can be determined as follows: if the node is a root of subtree `[start, end)`, the node's level is `BIT_WIDTH(end - start - 1)`. 1733 | 1734 | Comparing two indices determines the relationship between two paths. The highest differing bit gives the level at which paths from root to leaf diverge. For example, the bit representations of 4 and 6 are `0b100` and `0b110`, respectively. The highest differing bit is bit 1. Bits 2 and up are the same between the two indices. This indicates that the paths from the root to leaves 4 and 6 diverge when going to level 2 to level 1. 1735 | 1736 | This can be generalized to arbitrary-sized Merkle Trees. {{fig-merkle-tree-bits-partial}} depicts a Merkle Tree of size 6: 1737 | 1738 | ~~~aasvg 1739 | +--------------+ 1740 | | [0, 6) | level 3 1741 | +--------------+ 1742 | / | 1743 | +--------+ | 1744 | | [0, 4) | * level 2 1745 | +--------+ | 1746 | / \ | 1747 | +-----+ +-----+ +-----+ 1748 | |[0,2)| |[2,4)| |[4,6)| level 1 1749 | +-----+ +-----+ +-----+ 1750 | / \ / \ / \ 1751 | +-+ +-+ +-+ +-+ +-+ +-+ 1752 | |0| |1| |2| |3| |4| |5| level 0 1753 | +-+ +-+ +-+ +-+ +-+ +-+ 1754 | ~~~ 1755 | {: #fig-merkle-tree-bits-partial title="An example Merkle Tree of size 6"} 1756 | 1757 | When the size of a Merkle Tree is not a power of two, some levels on the rightmost edge of the tree are skipped. The rightmost edge is the path to the last element. The skipped levels can be seen in its binary representation. Here, the last element is 5, which has binary representation `0b101`. When a bit is set, the corresponding node is a right child. When it is unset, the corresponding node is skipped. 1758 | 1759 | In a tree of the next power of two size, the skipped nodes in this path are where there *would* have been a right child, had there been enough elements to construct one. Without a right child, the hash operation is skipped and a skipped node has the same value as its singular child. {{fig-merkle-tree-bits-partial-comparison}} depicts this for a tree of size 6. 1760 | 1761 | ~~~aasvg 1762 | +----------------+ 1763 | | [0, 6) | level 3 1764 | +----------------+ 1765 | / \ 1766 | +--------+ +--------+ 1767 | | [0, 4) | | [4, 6) | level 2 1768 | +--------+ +--------+ 1769 | / \ / \ 1770 | +-----+ +-----+ +-----+ +-----+ 1771 | |[0,2)| |[2,4)| |[4,6)| | | level 1 1772 | +-----+ +-----+ +-----+ +-----+ 1773 | / \ / \ / \ / \ 1774 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ 1775 | |0| |1| |2| |3| |4| |5| | | | | level 0 1776 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ 1777 | ~~~ 1778 | {: #fig-merkle-tree-bits-partial-comparison title="An example Merkle Tree of size 6, viewed as a subset of a tree of size 8"} 1779 | 1780 | Zero bits also indicate skipped nodes in paths that have not yet diverged from the rightmost edge (i.e. the path to the last element), when viewed from root to leaf. In the example, the binary representation of 4 is `0b100`. While bit 0 and bit 1 are both unset, they manifest in the tree differently. Bit 0 indicates that 4 is a right child. However, at bit 1, `0b100` has not yet diverged from the last element, `0b101`. That instead indicates a skipped node, not a left child. 1781 | 1782 | ## Inclusion Proof Evaluation {#inclusion-proof-evaluation-explain} 1783 | 1784 | The procedure in {{evaluating-a-subtree-inclusion-proof}} builds up a subtree hash in `r` by staring from `entry_hash` and iteratively hashing elements of `inclusion_proof` on the left or right. That means this procedure, when successful, must return *some* hash that contains `entry_hash`. 1785 | 1786 | Treating `[start, end)` as a Merkle Tree of size `end - start`, the procedure hashes by based on the path to `index`. Within this smaller Merkle Tree, it has index `fn = index - start` (first number), and the last element has index `sn = end - start - 1` (second number). 1787 | 1788 | Step 4 iterates through `inclusion_proof` and the paths to `fn` and `sn` in parallel. As the procedure right-shifts `fn` and `sn` and looks at the least-significant bit, it moves up the two paths, towards the root. When `sn` is zero, the procedure has reached the top of the tree. The procedure checks that the two iterations complete together. 1789 | 1790 | Iterating from level 0 up, `fn` and `sn` will initially be different. While they are different, step 4.2 hashes on the left or right based on the binary representation, as discussed in {{binary-representations}}. 1791 | 1792 | Once `fn = sn`, the remainder of the path is on the right edge. At that point, the condition in step 4.2 is always true. It only incorporates proof entries on the left, once per set bit. Unset bits are skipped. 1793 | 1794 | Inclusion proofs can also be evaluated by considering these two stages separately. The first stage consumes `l1 = BIT_WIDTH(fn XOR sn)` proof entries. The second stage consumes `l2 = POPCOUNT(fn >> l1)` proof entries. A valid inclusion proof must then have `l1 + l2` entries. The first `l1` entries are hashed based on `fn`'s least significant bits, and the remaining `l2` entries are hashed on the left. 1795 | 1796 | ## Consistency Proof Structure 1797 | 1798 | A subtree consistency proof for `[start, end)` and the tree of `n` elements is similar to an inclusion proof for element `end - 1`. If one starts from `end - 1`'s hash, incorporating the whole inclusion proof should reconstruct `root_hash` and incorporating a subset of the inclusion proof should reconstruct `node_hash`. Thus `end - 1`'s hash and this inclusion proof can prove consistency. A subtree consistency proof in this document applies two optimizations over this construction: 1799 | 1800 | 1. Instead of starting at level 0 with `end - 1`, the proof can start at a higher level. Any ancestor of `end - 1` shared by both the subtree and the overall tree is a valid starting node to reconstruct `node_hash` and `root_hash`. Use the highest level with a commmon ancestor. This truncates the inclusion proof portion of the consistency proof. 1801 | 1802 | 2. If this starting node is the entire subtree, omit its hash from the consistency proof. The verifier is assumed to already know `node_hash`. 1803 | 1804 | A Merkle consistency proof, defined in {{Section 2.1.4 of ?RFC9162}}, applies these same optimizations. 1805 | 1806 | {{fig-truncate-consistency-proof}} depicts a subtree consistency proof between the subtree `[0, 6)` and the Merkle Tree of size 8. The consistency proof begins at level 1, or node `[4, 6)`. The inclusion proof portion is similarly truncated to start at level 1: `[6, 8)` and `[0, 4)`. If the consistency proof began at level 0, the starting node would be leaf 5, and the consistency proof would additionally include leaf 4. 1807 | 1808 | ~~~aasvg 1809 | +----------------+ 1810 | | [0, 6) | level 3 1811 | +----------------+ 1812 | / | 1813 | +========+ +--------+ 1814 | | [0, 4) | | [4, 6) | level 2 1815 | +========+ +--------+ 1816 | / \ | 1817 | +-----+ +-----+ +~~~~~+ 1818 | |[0,2)| |[2,4)| |[4,6)| level 1 1819 | +-----+ +-----+ +~~~~~+ 1820 | / \ / \ / \ 1821 | +-+ +-+ +-+ +-+ +-+ +-+ 1822 | |0| |1| |2| |3| |4| |5| level 0 1823 | +-+ +-+ +-+ +-+ +-+ +-+ 1824 | 1825 | 1826 | +----------------+ 1827 | | [0, 8) | level 3 1828 | +----------------+ 1829 | / \ 1830 | +========+ +--------+ 1831 | | [0, 4) | | [4, 8) | level 2 1832 | +========+ +--------+ 1833 | / \ / \ 1834 | +-----+ +-----+ +~~~~~+ +=====+ 1835 | |[0,2)| |[2,4)| |[4,6)| |[6,8)| level 1 1836 | +-----+ +-----+ +~~~~~+ +=====+ 1837 | / \ / \ / \ / \ 1838 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ 1839 | |0| |1| |2| |3| |4| |5| |6| |7| level 0 1840 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ 1841 | ~~~ 1842 | {: #fig-truncate-consistency-proof title="A subtree consistency proof that starts at level 1 instead of level 0"} 1843 | 1844 | Note that the truncated inclusion proof may include nodes from lower levels, if the corresponding level was skipped on the right edge. {{fig-truncate-consistency-proof-2}} depicts a subtree consistency proof between the subtree `[0, 6)` and the Merkle Tree of size 7. As above, the starting node is `[4, 6)` at level 1. The inclusion proof portion includes leaf 6 at level 0. This is because leaf 6 is taking the place of its skipped parent at level 1. (A skipped node can be thought of as a duplicate of its singular child.) 1845 | 1846 | ~~~aasvg 1847 | +----------------+ 1848 | | [0, 6) | level 3 1849 | +----------------+ 1850 | / | 1851 | +========+ +--------+ 1852 | | [0, 4) | | [4, 6) | level 2 1853 | +========+ +--------+ 1854 | / \ | 1855 | +-----+ +-----+ +~~~~~+ 1856 | |[0,2)| |[2,4)| |[4,6)| level 1 1857 | +-----+ +-----+ +~~~~~+ 1858 | / \ / \ / \ 1859 | +-+ +-+ +-+ +-+ +-+ +-+ 1860 | |0| |1| |2| |3| |4| |5| level 0 1861 | +-+ +-+ +-+ +-+ +-+ +-+ 1862 | 1863 | 1864 | +----------------+ 1865 | | [0, 7) | level 3 1866 | +----------------+ 1867 | / \ 1868 | +========+ +--------+ 1869 | | [0, 4) | | [4, 7) | level 2 1870 | +========+ +--------+ 1871 | / \ / | 1872 | +-----+ +-----+ +~~~~~+ +=+ 1873 | |[0,2)| |[2,4)| |[4,6)| |6| level 1 1874 | +-----+ +-----+ +~~~~~+ +=+ 1875 | / \ / \ / \ | 1876 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ 1877 | |0| |1| |2| |3| |4| |5| |6| level 0 1878 | +-+ +-+ +-+ +-+ +-+ +-+ +-+ 1879 | ~~~ 1880 | {: #fig-truncate-consistency-proof-2 title="The interaction between inclusion proof truncation and skipped levels"} 1881 | 1882 | ## Consistency Proof Verification {#consistency-proof-verification-explain} 1883 | 1884 | The procedure in {{verifying-a-subtree-consistency-proof}} is structured similarly to inclusion proof evaluation ({{inclusion-proof-evaluation-explain}}). It iteratively builds two hashes, `fr` and `sr`, which are expected to equal `node_hash` and `root_hash`, respectively. Everything hashed into `fr` is also hashed into `sr`, so success demonstrates that `root_hash` contains `node_hash`. 1885 | 1886 | Step 2 initializes `fn` (first number), `sn` (second number), and `tn` (third number) to follow, respectively, the paths to `start`, `end - 1` (the last element of the subtree), and `n - 1` (the last element of the tree). 1887 | 1888 | Steps 3 and 4 then skip to the starting node, described in {{consistency-proof-structure}}. The starting node may be: 1889 | 1890 | * The entire subtree `[start, end)` if `[start, end)` is directly contained in the tree. This will occur if `end` is `n` (step 3), or if `[start, end)` is full (exiting step 4 because `fn` is `sn`). 1891 | 1892 | * Otherwise, the highest full subtree along the right edge of `[start, end)`. This corresponds to the process exiting step 4 because `LSB(sn)` is not set. 1893 | 1894 | Steps 5 and 6 initialize the hashes `fr` and `sr`: 1895 | 1896 | * In the first case above, `fn` will equal `sn` after truncation. Step 5 will then initialize the hashes to `node_hash` because consistency proof does not need to include the starting node. 1897 | 1898 | * In the second case above, `fn` is less than `sn`. Step 6 will then initialize the hashes to the first value in the consistency proof. 1899 | 1900 | Step 7 incorporates the remainder of the consistency proof into `fr` and `sr`: 1901 | 1902 | * All hashes are incorporated into `sr`, with hashing on the left or right determined the same as in inclusion proof evaluation. 1903 | 1904 | * A subset of the hashes are incorporated into `fr`. It skips any hash on the right because those contain elements greater than `end - 1`. It also stops incorporating when `fn` and `sn` have converged. 1905 | 1906 | This reconstructs the hashes of the subtree and full tree, which are then compared to expected values in step 8. 1907 | 1908 | In the case when `fn` is `sn` in step 5, the condition in step 7.2.1 is always false, and `fr` is always equal to `node_hash` in step 8. In this case, steps 6 through 8 are equivalent to verifying an inclusion proof for the truncated subtree `[fn, sn + 1)` and truncated tree `tn + 1`. 1909 | 1910 | # Extensions to Tiled Transparency Logs (To Be Removed) 1911 | 1912 | [[TODO: This section is expected to be removed. It is sketched here purely for illustrative purposes, until the features are defined somewhere else, e.g. in the upstream tlog documents.]] 1913 | 1914 | ## Subtree Signed Note Format 1915 | 1916 | A subtree, with signatures, can be represented as a signed note {{SIGNED-NOTE}}. Trust anchor IDs can be converted into log origins and cosigner names by concatenating the ASCII string `oid/1.3.6.1.4.1.` and the ASCII representation of the trust anchor ID. For example, the checkpoint origin for a log named `32473.1` would be `oid/1.3.6.1.4.1.32473.1`. 1917 | 1918 | The note body is a sequence of the following lines, each terminated by a newline character (U+000A): 1919 | 1920 | * The log origin 1921 | * Two space-separated, non-negative decimal integers, ` ` 1922 | * The subtree hash, as single hash encoded in base64 1923 | 1924 | Each note signature has a key name of the cosigner name. The signature's key ID is computed using the reserved signature type in {{SIGNED-NOTE}}, and a fixed string, as follows: 1925 | 1926 | ~~~pseudocode 1927 | key ID = SHA-256(key name || 0x0A || 0xFF || "mtc-subtree/v1")[:4] 1928 | ~~~ 1929 | 1930 | A subtree whose `start` is zero can also be represented as a checkpoint {{TLOG-CHECKPOINT}}. A corresponding subtree signature can be represented as a note signature using a key ID computed as follows: 1931 | 1932 | ~~~pseudocode 1933 | key ID = SHA-256(key name || 0x0A || 0xFF || "mtc-checkpoint/v1")[:4] 1934 | ~~~ 1935 | 1936 | The only difference between the two forms is the implicit transformation from the signed note text to the MTCSubtree structure. 1937 | 1938 | ## Requesting Subtree Signatures 1939 | 1940 | This section defines the `sign-subtree` cosigner HTTP endpoint for clients to obtain subtree signatures from non-CA cosigners, such as mirrors and witnesses. It may be used by the CA when assembling a certificate, or by an authenticating party to add a cosignature to a certificate that the CA did not themselves obtain. 1941 | 1942 | The cosigner MAY expose this endpoint publicly to general authenticating parties, or privately to the CA. The latter is sufficient if the CA is known to automatically request cosignatures from this cosigner when constructing certificates. If private, authenticating the CA is out of scope for this document. 1943 | 1944 | Clients call this endpoint as `POST /sign-subtree`, where `prefix` is some URL prefix. For a mirror or witness, the URL prefix is the submission prefix. The client's request body MUST be a sequence of: 1945 | 1946 | * The requested subtree as a signed note ({{subtree-signed-note-format}}), with zero or more signatures. The endpoint MAY require signatures from the CA as a DoS mitigation, as described below. 1947 | * A blank line 1948 | * A checkpoint, signed by the requested cosigner. The checkpoint's tree size must be at least `end`. 1949 | * A blank line 1950 | * Zero or more subtree consistency proof ({{subtree-consistency-proofs}}) lines. Each line MUST encode a single hash in base64 {{!RFC4648}}. The client MUST NOT send more than 63 consistency proof lines. 1951 | 1952 | Each line MUST terminate in a newline character (U+000A). 1953 | 1954 | The cosigner performs the following steps: 1955 | 1956 | 1. Check that the checkpoint contains signatures from itself 1957 | 2. Check that the subtree consistency proof proves consistency between the subtree hash and the checkpoint 1958 | 3. If all checks pass, cosign the subtree, as described in {{cosigners}} 1959 | 1960 | On success, the response body MUST be a sequence of one or more note signature lines {{SIGNED-NOTE}}, each starting with an em dash character (U+2014) and ending with a newline character (U+000A). The signatures MUST be cosignatures from the cosigner key(s) on the subtree. 1961 | 1962 | Instead of statelessly validating checkpoints by signature, the cosigner MAY statefully check the requested checkpoint against internal witness or mirror state. In this case, if the cosigner needs a newer checkpoint, it responds with a "409 Conflict" with its latest signed checkpoint. In this case, the subtree cosigning SHOULD remember and accept the last few signed checkpoints, to minimize conflicts. 1963 | 1964 | If operating statefully, the subtree cosigner process only needs read access to the mirror or witness state and can freely operate on stale state without violating any invariants. 1965 | 1966 | Mirrors MAY choose to check subtree hashes by querying their log state, instead of evaluating proofs. 1967 | 1968 | Publicly-exposed subtree cosigning endpoints MAY mitigate DoS in a variety of techniques: 1969 | 1970 | * Only cosigning recent subtrees, as old subtrees do not need to be co-signed 1971 | * Caching subtree signatures 1972 | * Requiring a CA signature on the subtree; CAs are only expected to sign two subtrees ({{arbitrary-intervals}}) for each checkpoint 1973 | * Rate-limiting requests 1974 | 1975 | # Acknowledgements 1976 | {:numbered="false"} 1977 | 1978 | This document stands on the shoulders of giants and builds upon decades of work in TLS authentication, X.509, and Certificate Transparency. The authors would like to thank all those who have contributed over the history of these protocols. 1979 | 1980 | The authors additionally thank Bob Beck, Ryan Dickson, Aaron Gable, Nick Harper, Dennis Jackson, Matt Mueller, Chris Patton, Ryan Sleevi, and Emily Stark for many valuable discussions and insights which led to this document. We wish to thank Mia Celeste in particular, whose implementation of an earlier draft revealed several pitfalls. 1981 | 1982 | The idea to mint tree heads infrequently was originally described by Richard Barnes in {{STH-Discipline}}. The size optimization in Merkle Tree Certificates is an application of this idea to the certificate itself. 1983 | 1984 | # Change log 1985 | {:numbered="false"} 1986 | 1987 | > **RFC Editor's Note:** Please remove this section prior to publication of a 1988 | > final version of this document. 1989 | 1990 | ## Since draft-davidben-tls-merkle-tree-certs-00 1991 | {:numbered="false"} 1992 | 1993 | - Simplify hashing by removing the internal padding to align with block size. #72 1994 | 1995 | - Avoid the temptation of floating points. #66 1996 | 1997 | - Require `lifetime` to be a multiple of `batch_duration`. #65 1998 | 1999 | - Rename window to validity window. #21 2000 | 2001 | - Split Assertion into Assertion and AbridgedAssertion. The latter is used in the Merkle Tree and HTTP interface. It replaces `subject_info` by a hash, to save space by not serving large post-quantum public keys. The original Assertion is used everywhere else, including BikeshedCertificate. #6 2002 | 2003 | - Add proper context to every node in the Merkle Tree. #32 2004 | 2005 | - Clarify we use a single `CertificateEntry`. #11 2006 | 2007 | - Clarify we use POSIX time. #1 2008 | 2009 | - Elaborate on CA public key and signature format. #27 2010 | 2011 | - Miscellaneous changes. 2012 | 2013 | ## Since draft-davidben-tls-merkle-tree-certs-01 2014 | {:numbered="false"} 2015 | 2016 | - Minor editorial changes 2017 | 2018 | ## Since draft-davidben-tls-merkle-tree-certs-02 2019 | {:numbered="false"} 2020 | 2021 | - Replace the negotiation mechanism with TLS Trust Anchor Identifiers. 2022 | 2023 | ## Since draft-davidben-tls-merkle-tree-certs-03 2024 | {:numbered="false"} 2025 | 2026 | - Switch terminology from "subscriber" to "authenticating party". 2027 | 2028 | - Use <1..2^24-1> encoding for all certificate types in the CertificateEntry TLS message 2029 | 2030 | - Clarify discussion and roles in transparency ecosystem 2031 | 2032 | - Update references 2033 | 2034 | ## Since draft-davidben-tls-merkle-tree-certs-04 2035 | {:numbered="false"} 2036 | 2037 | Substantially reworked the design. The old design was essentially the landmark checkpoint and CA-built logs ideas, but targeting only the optimized and slow issuance path, and with a more bespoke tree structure: 2038 | 2039 | In both draft-04 and draft-05, a CA looks like today’s CAs except that they run some software to publish what they issue and sign tree heads to certify certificates in bulk. 2040 | 2041 | In draft-04, the CA software publishes certificates in a bunch of independent Merkle Trees. This is very easy to do as a collection of highly cacheable, immutable static files because each tree is constructed independently, and never appended to after being built. In draft-05, the certificates are published in a single Merkle Tree. The {{TLOG-TILES}} interface allows such trees to also use highly cacheable, immutable static files. 2042 | 2043 | In draft-04, there only are hourly tree heads. Clients are provisioned with tree heads ahead of time so we can make small, inclusion-proof-only certificates. In draft-05, the ecosystem must coordinate on defining "landmark" checkpoints. Clients are provisioned with subtrees describing landmark checkpoints ahead of time so we can make small, inclusion-proof-only certificates. 2044 | 2045 | In draft-04, each tree head is independent. In draft-05, each landmark checkpoint contains all the previous checkpoints. 2046 | 2047 | In draft-04, the independent tree heads were easily prunable. In draft-05, we define how to prune a Merkle Tree. 2048 | 2049 | In draft-04, there is no fast issuance mode. In draft-05, frequent, non-landmark checkpoints can be combined with inclusion proofs and witness signatures for fast issuance. This is essentially an STH and inclusion proof in CT. 2050 | 2051 | ## Since draft-davidben-tls-merkle-tree-certs-05 2052 | {:numbered="false"} 2053 | 2054 | - Add some discussion on malleability 2055 | 2056 | - Discuss the monitoring impacts of the responsibility shift from CA with log quorum to CA+log with mirror quorum 2057 | 2058 | - Sketch out a more concrete initial ACME extension 2059 | 2060 | ## Since draft-davidben-tls-merkle-tree-certs-06 2061 | {:numbered="false"} 2062 | 2063 | - Fix mistyped reference 2064 | 2065 | - Removed now unnecessary placeholder text 2066 | 2067 | - First draft at IANA registration and ASN.1 module 2068 | 2069 | - Added a prose version of the procedure to select subtrees 2070 | 2071 | - Rename 'landmarks checkpoint' to 'landmarks' 2072 | 2073 | - Clarify and fix an off-by-one error in recommended landmark allocation scheme 2074 | 2075 | - Add some diagrams to the Overview section 2076 | 2077 | ## Since draft-davidben-tls-merkle-tree-certs-07 2078 | {:numbered="false"} 2079 | 2080 | - Clarify landmark zero 2081 | 2082 | - Clarify signature verification process 2083 | 2084 | - Improve subtree consistency proof verification algorithm 2085 | 2086 | - Add an appendix that explains the Merkle Tree proof procedures 2087 | 2088 | ## Since draft-davidben-tls-merkle-tree-certs-08 2089 | {:numbered="false"} 2090 | 2091 | - Improvements to malleability discussion 2092 | 2093 | - Improvements to subtree definition 2094 | 2095 | - Improvements to `trust_anchors` integration 2096 | 2097 | ## Since draft-davidben-tls-merkle-tree-certs-09 2098 | {:numbered="false"} 2099 | 2100 | - Editorial fixes 2101 | 2102 | - Set a more accurate intended status 2103 | --------------------------------------------------------------------------------