| Internet-Draft | MTCRS | October 2026 |
| Stradling | Expires 11 April 2027 | [Page] |
This document defines a hash chain revocation mechanism for Merkle Tree Certificates (MTC). A Merkle Tree CA includes a hash chain anchor in the certificate at issuance time. For each certificate it has not revoked, once per period (typically hourly), the CA reveals the next value working backwards along that chain, paired with its period as a tick. The authenticating party fetches that tick and embeds it in the certificate's MTCProof as the certificate's non-revocation proof, alongside the inclusion proof that establishes authenticity. The relying party can then cryptographically verify that the certificate has not been revoked, without contacting anyone.¶
This mechanism provides timely revocation without requiring signatures per revocation check, without relying on the relying party to poll for revocation updates, and without introducing new trust relationships beyond the existing CA.¶
This note is to be removed before publishing as an RFC.¶
The latest revision of this draft can be found at https://robstradling.github.io/mtcrs/draft-strad-plants-mtcrs.html. Status information for this document may be found at https://datatracker.ietf.org/doc/draft-strad-plants-mtcrs/.¶
Discussion of this document takes place on the PKI, Logs, And Tree Signatures Working Group mailing list (mailto:plants@ietf.org), which is archived at https://mailarchive.ietf.org/arch/browse/plants. Subscribe at https://www.ietf.org/mailman/listinfo/plants/.¶
Source for this draft and an issue tracker can be found at https://github.com/robstradling/mtcrs.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 11 April 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Merkle Tree Certificates [I-D.ietf-plants-merkle-tree-certs] authenticate TLS connections using compact inclusion proofs into a Merkle Tree maintained by a certification authority (CA). The base MTC specification is designed around short-lived certificates and leaves certificate-level revocation out of scope. It notes that existing mechanisms such as CRLs and OCSP apply unchanged (Section 12.7 of [I-D.ietf-plants-merkle-tree-certs]). Its own serial-range revocation (Section 7.5 of [I-D.ietf-plants-merkle-tree-certs]) is a complementary mitigation for CA misbehavior rather than a per-certificate revocation service. Section 12.7 of [I-D.ietf-plants-merkle-tree-certs] also observes that the sequential serial numbers its issuance logs assign may enable future improvements to revocation, while placing such work out of scope. This document is an attempt at that work.¶
What counts as short-lived, however, is set by root program policy. Deployments such as Chrome's draft Quantum-resistant Root Program policy [CHROME-MTC] permit certificate lifetimes of up to 47 days. That policy recommends a 7-day validity and requires each MTC CA to operate at least one cosigner key limited to it, while permitting up to three further keys that issue at 47 days. Without revocation, exposure to key compromise or certificate misissuance is bounded only by expiry: a week in the recommended case, and a month and a half at the permitted maximum. Relying parties that have them may fall back to out-of-band systems such as [CRLite] or [CRLSets], but these are vendor-controlled and not universal, and no in-band mechanism exists.¶
This document defines such a mechanism based on hash chains [MICALI].
That primitive is thirty years old and never took hold in the Web PKI, for reasons of delivery rather than cryptography that Merkle Tree Certificates remove (Appendix E.6.1).
At issuance, the CA commits a hash chain anchor into the MTC log entry as an X.509 extension.
For each non-revoked certificate, once per tick interval (e.g., every hour), the CA reveals the previous hash chain value, walking the committed hash chain backward.
To revoke a certificate, the CA simply stops revealing values (Section 5).
The authenticating party (server) embeds the current tick, the revealed value paired with its period, in the certificate's MTCProof (the signatureValue), and the relying party (client) verifies it against the anchor committed in the log entry.¶
The MTCProof already carries a proof of inclusion: the evidence that a certificate is authentic, because its entry sits in a cosigned Merkle Tree. This mechanism adds a proof of non-revocation to the same structure, so that together they let a relying party confirm not merely that the certificate was issued, but that it may be relied upon now.¶
This approach achieves the following properties:¶
At any full handshake, revocation takes effect within at most two periods (e.g., two hours) under the default acceptance window (Section 16.10), regardless of when the relying party last updated its trusted subtrees. Session resumption defers the check (Section 13.7).¶
Unlike OCSP [RFC6960], verification requires only hash computations, not signature verification. The CA incurs no signing load for revocation status.¶
The hash chain value is a required component of the certificate presentation. Unlike OCSP stapling, the mechanism cannot be silently omitted by the authenticating party.¶
The hash chain value is verified against the anchor already committed in the Merkle Tree. No new trust relationships or authenticated channels are needed.¶
A single tick (34 bytes for SHA-256: a 2-byte period and a 32-byte hash value) is added per handshake to the certificate's MTCProof, carried as a cosignature whose framing brings it to about 42 bytes (Section 8.1.1). The committed anchor adds about 50 bytes to each log entry (Section 7). Section 3.2 collects the full cost to each party.¶
These properties come with three deliberate trade-offs, treated in full later but noted here so they are visible from the outset. First, enforcement introduces an availability dependency: because an authenticating party must refresh its tick each period, a tick-distribution outage that outlasts the certificate's buffer renders it unusable (Section 13.1). Second, revocation is enforceable but not transparent. Withholding a tick is not a signed, logged artifact, so monitors cannot observe revocation events in the Merkle Tree, and a deployment that needs an auditable revocation record obtains it from a mechanism outside MTC (Section 16.8). Third, the two-period bound is a property of the full handshake. A resumed TLS session carries no certificate and therefore checks no tick, so the latency a given client actually experiences is the longer of that bound and its session-resumption window, which TLS 1.3 caps at seven days (Section 13.7). The first two are intrinsic to fetch-free, hard-fail revocation rather than defects, and both are bounded. The third is shared with every handshake-time revocation mechanism.¶
This mechanism adds a per-certificate, per-period distribution service to a design that deliberately avoids per-certificate online infrastructure, and at scale that service is substantial. A CA serving 109 certificates answers on the order of 105 to 106 tick requests per second (Section 10). What separates it from the OCSP responder MTC was built to do without is that relying parties never contact it (Section 16.5), so it lies outside the handshake path entirely. It holds no key and signs nothing. Every response is a public value that is immutable within its period. The service is therefore a precomputed dataset rather than a computation, which a CA can replicate to, or delegate wholesale to, parties trusted for availability alone (Section 13.5, Appendix E.6.3). That is a shape the ecosystem already operates at population scale, in software update and revocation-list distribution, and one a CA can hand to the parties already running it (Section 13.5).¶
This mechanism is designed to layer onto the base MTC specification [I-D.ietf-plants-merkle-tree-certs] without changing the MTCProof structure or how the base specification parses it. Appendix C collects what this document asks of the base specification, and Appendix D the design choices it leaves open for the working group to settle. The rationale for choosing this approach over the alternatives, and the argument that functional revocation is superior to passive expiry, are developed in Appendix E and Appendix F. Those two appendices are written to support an adoption decision rather than an implementation, and account for about a quarter of the document. Once the working group has settled the questions in Appendix D, they are expected to be cut back to the conclusions reached, or moved to a companion document, leaving a specification closer in size to the one it extends. A reader wanting the shape of the mechanism before its details will find a non-normative walk-through of one certificate's lifecycle in Section 3.¶
This document is published as Experimental to gather implementation and deployment experience with hash chain revocation for Merkle Tree Certificates. The author's intent is that it advance to the Standards Track if the PLANTS working group is willing to adopt it, ideally with the clarification it requests of the base specification (Appendix C) folded into the base MTC specification itself.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
This document uses the TLS presentation language defined in Section 3 of [RFC9846] for the HashChainInput (Section 6) and HashChainTick (Section 8) structures, and ASN.1 [X.690] for the certificate extension it defines (Section 7). The HashValue and TrustAnchorID types are those of [I-D.ietf-plants-merkle-tree-certs].¶
This document uses the hash function HASH and its output length in bytes HASH_SIZE that a Merkle Tree CA defines for its issuance logs (Section 5 of [I-D.ietf-plants-merkle-tree-certs]). For a CA using SHA-256, HASH is SHA-256 and HASH_SIZE is 32. Hash chain values, the anchor, and the tick all use this hash. HASH is a per-CA parameter, uniform across every issuance log that CA operates, so a certificate's hash chain uses the single hash function of its issuing CA. This mechanism therefore never chooses or negotiates a hash, and carries no algorithm identifier in the anchor, the tick, or any other committed or presented data. The one place an identifier appears is the tick base URL, which names the algorithm for the single party that cannot otherwise obtain it (Section 10). The two parties that compute with it obtain it differently, and neither needs a carrier that does not already exist. A relying party is configured with the CA's log hash algorithm as part of the base MTC configuration it needs to accept any certificate from that CA (Section 7.1 of [I-D.ietf-plants-merkle-tree-certs]). The base specification identifies that algorithm by the type of the Merkle Tree CA extension in the CA certificate, which names one hash per extension type, id-pe-mtcCertificationAuthority-SHA256 being the one it defines (Section 5.5 of [I-D.ietf-plants-merkle-tree-certs]). An authenticating party has no such configuration, and reads the algorithm from the final segment of the tick base URL it is given (Section 10). That URL is the one value a CA must convey to it in any case, so the algorithm travels with the locator rather than needing a channel of its own. It needs HASH only to verify a fetched tick against the anchor committed in its own certificate before presenting it (Section 12), and takes HASH_SIZE from the length of that anchor (Section 7).¶
This document uses the roles defined in [I-D.ietf-plants-merkle-tree-certs]. They are the certification authority (CA) that issues certificates, the authenticating party (in TLS, the server) that presents one, the relying party (in TLS, the client) that validates it, and the monitor that watches issuance logs. It adds the following terms, each specified in full in the section cited.¶
The sequence of values h[0] through h[hash_chain_length] that the CA generates for a log entry at issuance, each the hash of its predecessor (Section 4).¶
The first value of a hash chain, h[0].
It is secret, is never revealed, and yields the entire hash chain to anyone who learns it (Section 16.3).¶
The last value of a hash chain, h[hash_chain_length].
It is public, is committed to the Merkle Tree as part of the certificate, and is the value against which every tick is verified (Section 7).¶
One interval of tick_interval seconds.
Periods are numbered from 0, with period 0 beginning at the certificate's notBefore (Section 4).¶
tick_interval:The duration of one period, in seconds. It is a per-certificate value carried in the certificate, and defaults to 3600 (Section 4).¶
hash_chain_length:The number of periods in the certificate's lifetime, ceil(lifetime / tick_interval), which is also the index of the anchor within the hash chain (Section 4).¶
The pair {period, value} that the CA reveals for a period and the authenticating party embeds in the MTCProof (Section 8).
A tick is the certificate's non-revocation proof: the component of the MTCProof attesting that the certificate has not been revoked as of that period, complementing the inclusion proof and cosignatures that attest authenticity.
Whether "tick" is the right name for it is one of the questions this document puts to the working group (Appendix D.5).¶
serial_number:The certificate's serialNumber, which the base specification builds from the entry's log number and its index within that log, and which addresses that entry's tick in the distribution interface unless the CA uses unguessable tick URLs (Section 10, Section 10.3).¶
A party other than the CA that serves ticks over the HTTP interface of Section 10. Because ticks are self-authenticating, a distributor is trusted for availability only, never for integrity (Section 13.5).¶
Verifiable offline by hashing forward to the anchor committed in the certificate (Section 9), needing no signature over the value and no trust beyond the CA's existing trust anchor. Every hash chain value in this mechanism has this property. Because a tampered or forged value cannot reach the committed anchor in the number of steps its claimed period implies, it holds even though the MTCProof that carries the tick is itself unsigned and mutable.¶
This section is a non-normative walk-through of the mechanism's lifecycle, followed by summaries of what each party implements (Section 3.1) and what that costs (Section 3.2).
The normative details follow in Section 4 through Section 10.
It reuses the small example of Appendix B: a hash chain of length hash_chain_length = 5 (a real certificate uses a much longer one, for example 1,128 for a 47-day lifetime with a one-hour period).
Figure 1 shows how the parties interact, and Figure 2 depicts the hash chain lifecycle that the five steps below trace.¶
Issuance.
For each log entry, the CA generates a secret random seed h[0] and hashes it forward hash_chain_length times to obtain h[1], h[2], ..., h[5] (Section 4).
The final value h[5] is the anchor.
The CA commits it into the certificate as the id-pe-hashChainAnchor extension (Section 7).
Because the anchor sits in the log entry, it is covered by the Merkle Tree and the cosignatures.¶
Per-period reveal.
Time after the certificate's notBefore is divided into periods of tick_interval seconds (one hour by default).
At the start of period t, the CA reveals h[hash_chain_length - t] (for period 2, that is h[3]), unless it has revoked the certificate, in which case it reveals nothing further (Section 5.1).
These period boundaries are the certificate's own: because they are counted from its notBefore, each certificate advances through its periods on its own schedule (Section 4).
The hash chain is revealed in reverse of the order it was generated, so revealing the current value gives no help in computing any future one.¶
Server refresh.
Once per period, the authenticating party (server) fetches the certificate's current value as a tick {period, value} with a single plain HTTP GET (Section 10).
For period 2, that tick is {2, h[3]}.
It writes the tick into the MTCProof it presents in TLS handshakes (Section 8).
A server holding several certificates fetches one tick per certificate, each on that certificate's own schedule.
The MTCProof is not committed to the tree, so refreshing the tick each period does not disturb the inclusion proof or cosignatures.¶
Relying-party verification.
A relying party reads the tick and the committed anchor from the certificate, hashes tick.value forward tick.period times, and checks that the result equals the anchor (Section 9).
Hashing h[3] twice yields h[5].
It also checks that tick.period agrees with its own clock to within one period.
Verification is entirely offline.
The relying party fetches nothing (Section 16.5).¶
Revocation = withholding. To revoke a certificate, the CA simply stops revealing its hash chain values (Section 5.1). Once the last revealed tick's period ends, no party can produce a valid tick, because doing so would require inverting the hash. The certificate therefore becomes unusable within at most two periods.¶
The work this mechanism creates falls to three parties, and this table collects what each of them builds, in the order the work happens, together with the two parties it asks nothing new of. Nothing here is a new requirement, and each entry cites the section that states it.¶
| Party | What it implements |
|---|---|
| Certification authority | Chooses a tick_interval and a lifetime satisfying the two bounds on their ratio, generates a seed per entry, and hashes it forward to the anchor (Section 4). Commits the anchor in the certificate (Section 7). Reveals one value per period, and stops revealing to revoke (Section 5). Serves ticks at the published base URL, and conveys that URL to its subscribers (Section 10, Section 10.2). |
| Authenticating party | Derives its tick URL from the base URL and its own serialNumber, or is given the complete URL where the CA uses unguessable ones (Section 10, Section 10.3). Fetches once per period, at a deterministic offset within it (Section 13.3). Verifies each fetched tick against the anchor in its own certificate before installing it, overwrites the tick in the MTCProof's tick cosignature, and withholds the certificate from selection while it holds no tick within the acceptance window (Section 12). |
| Relying party | Reads the anchor from the certificate's extensions and the tick from the tick cosignature in the MTCProof (Section 7, Section 8.1.1). Runs the verification procedure, which is entirely offline (Section 9.2). Fetches nothing at any point (Section 16.5). |
| Issuance log and cosigners | Nothing. The anchor reaches the Merkle Tree as ordinary certificate bytes, so no component that builds or signs subtrees need recognize it (Appendix F.4). |
| Monitor | Nothing, beyond reading log entries as it already does. Where tick URLs are derivable, a monitor MAY additionally watch them for withheld ticks (Section 16.4). |
Each figure below is derived in the section cited, and collected here so that the whole cost can be seen at once. They assume SHA-256, a one-hour period, and a population of 109 certificates. That population is a round figure above the base specification's own estimates, which give 682 million active certificates for a single large CA and 2.9 billion unexpired across the Web PKI (Section 6.5 of [I-D.ietf-plants-merkle-tree-certs]). Each cost below is therefore an upper bound for any one CA.¶
| Party or object | What this mechanism costs |
|---|---|
| Relying party | About 300 microseconds of hashing per full handshake, and no network request at any point (Section 13.6, Section 16.5). |
| Authenticating party | One plain HTTP GET per certificate per period, and a 34-byte overwrite in the certificate it presents (Section 10, Section 12). |
| Certification authority | No signatures at all. About 42 GB per period published to distributors, and either about 340 GB of traversal state or none (Section 13.5, Section 13.2). |
| Monitor | The entry bytes below, downloaded once per entry rather than per period (Section 7). |
| Log entry | About 50 bytes for the committed anchor, a fifth to a quarter of a domain-validated entry (Section 7). |
| Handshake | About 42 bytes for the tick and its cosignature framing, 6 to 11 percent of the inclusion proof it travels beside (Section 8.1.1). |
The bytes are paid on every certificate and every handshake, and the hashing is borne by a party that chose neither the certificate's lifetime nor its period (Section 13.6). That is why the worst case that hashing can reach is referred to the working group (Appendix D.6).¶
The hash chain mechanism introduces the following additional parameter:¶
tick_interval:A duration, in seconds: the length of one period, and so the cadence at which the CA reveals a value and the authenticating party refreshes its tick (Section 10).
It sets the granularity of revocation but not the latency, which is about two intervals under the default acceptance window (Section 16.10).
This MUST be greater than zero.
The RECOMMENDED and default value is 3600 (one hour).
Section 7 specifies how this default is encoded so that a certificate using it carries no tick_interval bytes.
The number of periods in a certificate's lifetime is hash_chain_length = ceil(lifetime / tick_interval), where lifetime is the certificate's validity period in seconds, notAfter - notBefore.¶
Because a tick carries its period in a 16-bit field (Section 8), hash_chain_length MUST NOT exceed 65,535, so that every period number (0 through hash_chain_length - 1) and the verifier's one-period acceptance allowance (Section 9) are representable.
A CA MUST NOT issue a certificate with the id-pe-hashChainAnchor extension whose lifetime and tick_interval imply a longer hash chain.¶
This ceiling is deliberate, and it is the reason the period field is 16 bits rather than wider.
Verification hashes forward up to tick.period times (Section 9), so the relying party's work per full handshake is set by the CA's choice of tick_interval relative to the certificate lifetime.
A wider field would let a CA impose an arbitrarily large number of forward hashes on every relying party that validates its certificates, which is a cost those relying parties cannot decline.
Bounding the field at 16 bits caps that work at 65,535 hash evaluations for any certificate from any CA, and does so in the parser rather than by CA compliance.
A tick.period read from a 16-bit field cannot exceed 65,535 whatever the issuer did.¶
The ceiling is on the number of periods, not on tick_interval directly, so the shortest usable period scales with the certificate's lifetime.
It is about 62 seconds for a 47-day certificate, 10 seconds for a 7-day one, and 2 seconds for a 1-day one.
The RECOMMENDED one-hour period is far inside it: 1,128 periods for a 47-day lifetime (Appendix E.3), some 58 times below the ceiling.
Deployments SHOULD stay well within it rather than treating it as a target, since the verification cost a certificate imposes is borne by every relying party that validates it (Section 13.6).¶
tick_interval need not evenly divide the lifetime.
If it does not, the final period is shorter than tick_interval, ending when the certificate expires.
This is harmless.
The verifier computes the period from not_before and the base MTC validity check bounds the certificate at notAfter, so the truncated final period needs no special handling, and its shorter span only means revocation during it takes effect faster.¶
The certificate's validity period MUST be longer than tick_interval.
A certificate whose validity period is not longer than tick_interval would have hash_chain_length = 1: its only tick is the public period-0 anchor, which enforces nothing, because revocation is only enforceable from period 1 onwards (Appendix E.4).
A CA MUST NOT include the id-pe-hashChainAnchor extension, and MUST NOT use this mechanism, for such a certificate.
Because the period-0 grace defers enforcement of a just-issued certificate to the start of period 2 (Appendix E.4), deployments SHOULD choose a validity period substantially longer than twice tick_interval so that revocation is effective for most of the certificate's life.¶
tick_interval is carried per certificate, in the HashChainAnchorInfo committed to the Merkle Tree (Section 7), rather than being a CA-wide configuration constant.
A relying party verifies offline and has no provisioning relationship with the CA, so reading the value from the certificate is the only way it can obtain it without a new authenticated distribution channel.
Committing it to the tree makes it self-authenticating.
It also lets a CA change the interval for newly issued certificates without redistributing anything, since each existing certificate keeps the value it was issued with.
A CA MAY of course use the same interval throughout.
The point is only that each verifier takes the value from the certificate, not from CA-wide configuration it cannot see.¶
At certificate issuance time, for each log entry, the CA generates a hash chain as follows:¶
Generate a seed of HASH_SIZE bytes (32 bytes for SHA-256) using a cryptographically secure random number generator ([RFC4086]) or an approved deterministic random bit generator. The seed MUST be unpredictable. An adversary who can guess or recover it can compute the whole hash chain and forge ticks for a revoked certificate, so it carries the same unpredictability and confidentiality requirement as a secret key (Section 16.3).¶
Compute the hash_chain_length + 1 values of the hash chain:¶
h[0] = seed h[i] = Hash(HashChainInput(h[i-1])) for i = 1, ..., hash_chain_length¶
The anchor is h[hash_chain_length], the final value in the hash chain.¶
The anchor is included in the certificate as an X.509 extension (see Section 7).¶
Periods are numbered starting from 0.
Period 0 begins at the certificate's notBefore time and each subsequent period begins tick_interval seconds later.
The period number at any given time t is:¶
period = floor((t - not_before) / tick_interval)¶
not_before is the notBefore time of the certificate's validity period, expressed in the same units as t (seconds since the Unix epoch).
It anchors the period schedule to a value that both the CA and every verifier read from the certificate, so they compute identical period boundaries regardless of their wall-clock differences.
It is not necessarily the exact instant of issuance.
The CA MUST number periods from notBefore and MUST NOT begin revealing ticks before period 0 starts at notBefore.¶
Period numbering depends only on notBefore.
It is independent of the MTCProof's start and end fields, which describe the chosen subtree interval used for the inclusion proof and play no part in the period schedule.
The standalone and landmark-relative certificates for an entry can therefore carry different start and end yet number periods identically, because they share the same notBefore (Section 8.4).¶
Backdating notBefore, as CAs commonly do to accommodate relying-party clock skew, shifts the certificate forward in its own schedule.
Backdating by less than one tick_interval is harmless, placing the certificate a little way into period 0 when it is first presented.
Backdating by one tick_interval or more places it in period 1 or later at the moment of issuance, which forfeits the period 0 grace (Appendix E.4) in both its parts.
The CA no longer has until period 1 to serve the first secret tick, and the authenticating party can no longer present the committed anchor while it installs.
A CA that backdates that far MUST therefore be serving the entry's ticks from the moment it issues the certificate, since the authenticating party cannot present it at all until it has fetched one.
Deployments that want the grace preserved keep backdating below one tick_interval.¶
Setting notBefore later than issuance (forward-dating) is different: there is no period earlier than 0, and for any time t earlier than notBefore the quantity (t - not_before) is negative.
Such a certificate is simply not yet valid.
A verifier MUST reject it through the base MTC validity check before computing any period, and MUST NOT evaluate the period expression with unsigned arithmetic, which would underflow for such times and could yield a spuriously large period.¶
The non-revocation proof relies only on the preimage resistance of the hash function, not on collision resistance.
Given h[i], it is computationally infeasible to compute h[i-1] (which would be needed to forge a future validity proof).
The hash chain is revealed in reverse order precisely for this reason (Section 5.1).
Knowledge of the current value does not help compute future values.¶
The label in HashChainInput (Section 6) domain-separates hash chain values from other uses of the hash function in MTC, and issuer_ca_id separates one CA's hash chains from every other CA's.
Within a CA, the independent random seed separates one certificate's chain from another's (Section 6).
The hash chain is the only place this mechanism uses a hash function at all, so there is no second notion of hashing to keep distinct from it.¶
This section defines what a CA does with a hash chain once the certificate carrying its anchor has been issued: reveal one value per period for as long as the certificate is to remain usable, and stop revealing in order to revoke it. Revocation is the absence of a reveal, so the two are one mechanism seen from opposite sides, and they are specified together here.¶
For each non-revoked certificate, at the start of period t, the CA reveals the hash chain value h[hash_chain_length - t].
This value can be verified by hashing it t times and comparing with the anchor.¶
Periods run from 0 to hash_chain_length - 1, so the last value the CA ever reveals is h[1], one step from the seed, in the certificate's final period.
The CA MUST NOT reveal a value for any period at or beyond hash_chain_length, and MUST NOT reveal the seed h[0] under any circumstances.
h[0] is the whole hash chain, and disclosing it would let any party forge ticks for every remaining period (Section 16.3).
A CA that derives the period to serve from a clock MUST therefore bound that period at hash_chain_length - 1 rather than evaluating h[hash_chain_length - t] for an arbitrary t.
Tick publication for a certificate stops when the certificate expires.¶
For period 0, hash_chain_length - t equals hash_chain_length, so the value revealed is the anchor h[hash_chain_length] itself, which is already public, being committed in the certificate (Section 7).
The period 0 tick therefore provides no cryptographic assurance of non-revocation.
Any party holding the certificate can construct it by pairing that anchor with period 0, which takes no hashing at all, and a verifier hashes forward zero times and compares the anchor with itself.
This is an intentional design choice.
Appendix E.4 gives the rationale.¶
To revoke a certificate, the CA stops revealing new values. Once the previous value expires (at the start of the next period), the certificate is effectively revoked. No party can produce a valid value without knowledge of unrevealed hash chain elements, which requires inverting the hash function. Because period 0's value is the public anchor, the earliest period for which the CA can withhold a secret value is period 1. A certificate therefore cannot be revoked during period 0, and one the CA wishes to revoke from the moment of issuance becomes unusable at the start of period 2 (Appendix E.4).¶
Figure 3 traces the two-period bound for a certificate the CA decides to revoke during period 1.¶
Withholding is the entirety of the revocation action, but a CA must discharge it across every channel through which it publishes ticks, and pair it with the base MTC mechanism where this one does not reach. On deciding to revoke an entry, a CA:¶
MUST stop revealing that entry's hash chain values from the start of the next period, and MUST NOT serve them from its own tick distribution service (Section 10) thereafter.¶
MUST stop supplying that entry's values to delegated distributors (Section 13.5), whatever the form of the feed, which this document does not standardize (Section 13.4). A distributor pre-provisioned with a buffer of future values cannot be revoked through that channel until the buffer is exhausted, which is why the buffer SHOULD be short.¶
MUST, when revoking because of key compromise, also revoke the serial ranges of that key's certificates that carry no anchor, since withholding ticks reaches only the certificates that carry one (Section 16.9).¶
MUST fall back to the base MTC revoked-ranges mechanism if the seed or its derivation secret is compromised, because an attacker holding those can forge ticks whatever the CA withholds (Section 16.3).¶
No cache purge is required or useful. A tick cached at an HTTP intermediary is bounded by the same acceptance window as a freshly fetched one (Section 16.10), so the certificate becomes unusable on the same schedule either way. A deployment that needs the revocation to be publicly auditable must record it through a mechanism that produces such an artifact, since neither withholding a tick nor the base revoked ranges does (Section 16.8).¶
The HashChainInput structure provides domain separation for hash chain computations:¶
struct {
uint8 label[8] = "crs/v1\n\0";
TrustAnchorID issuer_ca_id;
HashValue preimage;
} HashChainInput;
¶
A fixed ASCII string providing domain separation from other uses of the hash function in MTC, so that a hash chain value cannot be reinterpreted as, or collide with, another MTC hash computation (for example, a Merkle Tree leaf or node hash).
Its value MUST be the string crs/v1, followed by a newline (U+000A), followed by a zero byte (U+0000).
The form follows the base MTC specification's own label, "subtree/v1\n\0" (Section 5.3.1 of [I-D.ietf-plants-merkle-tree-certs]), which likewise names the construction and versions it without further prefix, the label space being MTC's already.
Versioning matters here because a future revision of HashChainInput would otherwise share a domain with this one.
The NUL terminator keeps the MTC label space prefix-free, so no label can be a prefix of another, and the leading c (0x63) is neither 0x30, the DER SEQUENCE tag, nor the start of any other MTC label.
At 8 bytes it also keeps HashChainInput within a single SHA-256 compression block for any issuer_ca_id of up to 14 bytes (Section 13.6).¶
issuer_ca_id:The issuing CA's ID, which is its trust anchor ID.
Both parties read it from the certificate's issuer field, which the base specification defines as that CA ID encoded as a PKIX distinguished name (Section 5.1 of [I-D.ietf-plants-merkle-tree-certs]).
Taking it from the certificate rather than from the verifier's own trust anchor keeps this input a property of the certificate, so it does not depend on path-building state.
It also gives the authenticating party a source, since it has no trust anchor of its own to consult.
It is the same value the base verification procedure already extracts from issuer to construct the log ID (Section 7.2 of [I-D.ietf-plants-merkle-tree-certs]).¶
The previous hash chain value being hashed.¶
All fields are encoded with the TLS presentation language ([RFC9846]).
issuer_ca_id is a TrustAnchorID, which the base specification declares as opaque TrustAnchorID<1..2^8-1> (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]) and which therefore already carries its own one-byte length prefix.
No further vector wrapping is applied to it.
For TrustAnchorID 32473.1, that is the five bytes 04 81fd5901 (Appendix B).
Figure 4 shows the resulting layout.¶
The chain is salted per CA rather than per certificate.
issuer_ca_id keeps one CA's hash chains in a domain distinct from every other CA's, and within a CA the independent random seed of Section 4 is what separates one certificate's chain from another's.¶
A per-entry salt was considered and rejected. The obvious candidate, the certificate's serial number, would impose an ordering constraint on issuance, because the base specification builds that serial from the entry's index in the issuance log (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]). No hash chain value could then be computed until the index was known, while the anchor ending that chain must be committed in the entry that occupies it. That is satisfiable where a CA's issuance front end and log sequencer are one component and awkward where they are not, and this document prefers not to constrain that boundary. That the serial nonetheless keys the tick URL (Section 10) is not in tension with this. A URL is needed only once the entry has been sequenced, whereas a salt would be needed before the chain that the entry commits to could exist at all. Carrying a salt in the certificate instead would avoid the constraint but enlarge every committed entry, and a value drawn from the same generator as the seed would repeat exactly when the seed did. Deriving one from the certificate or the entry is circular, since the anchor is committed inside both and any salt must be fixed before the chain that produces it.¶
What per-entry salting would have bought is small. It would frustrate a preimage search mounted against a CA's whole certificate population at once, which the per-CA salting permits and which is far out of reach either way (Section 16.2). It would also keep two chains apart if a seed-generation fault repeated a seed across entries, which the per-CA salt does not, so that residue rests on the quality of the seed (Section 16.3).¶
The Hash function is HASH, the hash function of the issuing CA, which is uniform across that CA's issuance logs (Section 5 of [I-D.ietf-plants-merkle-tree-certs]) and which both computing parties read from the CA certificate (Section 2).¶
This document defines a new X.509 certificate extension for carrying the hash chain anchor.
This extension is included in the TBSCertificateLogEntry's extensions field (Section 5.2.1 of [I-D.ietf-plants-merkle-tree-certs]), and thus appears in the TBSCertificate of the resulting Merkle Tree Certificate and in the entry's tbs_cert_entry_data that the base specification commits to the Merkle Tree.
Figure 5 shows how this divides the certificate.¶
id-pe-hashChainAnchor OBJECT IDENTIFIER ::= {
iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) pe(1) TBD }
¶
The extension value contains the DER encoding of the following ASN.1 structure, for which Appendix A gives the complete module:¶
HashChainAnchorInfo ::= SEQUENCE {
tickInterval INTEGER (1..4294967295) DEFAULT 3600,
anchor OCTET STRING
}
¶
tickInterval:The tick interval in seconds for this certificate (Section 4), with a default of 3600 (one hour).
Under DER, a certificate using the default value MUST omit this field (Section 11.5 of [X.690]), so the common one-hour case adds no per-entry bytes.
A relying party that finds the field absent MUST use the default of 3600, and MUST reject a certificate that carries the field with the value 3600 (Section 9).
The upper bound of 4294967295 (232 - 1 seconds, about 136 years) is a parser bound rather than a policy one, in the same spirit as the 16-bit period field (Section 4).
It keeps tickInterval a fixed-width integer, so a relying party never has to allocate or evaluate an arbitrarily large INTEGER before reaching the checks that would reject it.
It sits far above any certificate lifetime, and tick_interval must in any case be shorter than the certificate's validity period (Section 4), which is the constraint that actually binds.
The relying party reads this value (or the default) from here.
It is used to number periods and to compute the expected period during verification (Section 9).¶
The hash chain anchor value h[hash_chain_length].
This OCTET STRING MUST be exactly HASH_SIZE bytes long.
Truncating it, to save bytes in the log entry and the handshake, is considered and rejected in Appendix F.5.
A relying party MUST reject the certificate if it is not (Section 9).¶
The extension MUST appear at most once in a certificate, as Section 4.2 of [RFC5280] requires of any extension.
A relying party rejects a certificate carrying more than one (Section 9).
The extension MUST NOT be present in a certificate whose validity period is not longer than tick_interval (Section 4).
Such a certificate cannot advance beyond period 0, so the mechanism would enforce nothing.¶
Because the anchor is committed to the Merkle Tree, this extension enlarges every log entry that carries it.
With the default tick_interval, the committed data is a HashChainAnchorInfo carrying only the anchor (HASH_SIZE bytes, 32 for SHA-256) plus its DER and extension framing.
That is about 50 bytes per entry for SHA-256, of which 36 are the HashChainAnchorInfo itself (Appendix B) and the rest the X.509 extension's OBJECT IDENTIFIER and wrapper.
A domain-validated entry, whose subject is empty and whose public key appears only as a hash, runs to roughly 200 to 250 bytes, so the anchor enlarges it by something like a fifth to a quarter.
Entries carrying a subject name or many subjectAltName values are larger, and the proportion correspondingly smaller.
Being a fixed-size hash, it does not grow with post-quantum key or signature sizes, unlike much of what MTC was designed to keep out of the log.¶
The cost lands in two bounded places. The first is monitor bandwidth. Monitors download every entry, so the ecosystem-wide cost is those bytes times the number of participating entries: on the order of tens of gigabytes at 109 entries. It is incurred once per entry rather than per period, and is far below the per-certificate signatures (Appendix F.11) this mechanism avoids. The second is the certificate presentation, where the same anchor bytes travel in each handshake as ordinary TBSCertificate content. The inclusion proof is unaffected, since its size depends on tree depth rather than entry size, so the anchor adds no hashes to the proof path. That proof is also the right yardstick for the per-handshake cost, since the anchor and the tick travel beside it. The base specification estimates it at 384 bytes for a standalone certificate and 736 bytes for a landmark-relative one (Section 6.5 of [I-D.ietf-plants-merkle-tree-certs]). Against that, the tick cosignature, about 42 bytes (Section 8.1.1), is some 6 to 11 percent. The same section measures its proof sizes against a single ML-DSA-44 signature at 2,420 bytes, the cost Merkle Tree Certificates exist to avoid paying per certificate. Against that yardstick the tick cosignature is about 1.7 percent.¶
This committed cost is the unavoidable price of self-authentication.
Unlike the tick base URL, which is deliberately kept out of the certificate (Section 10.2), the anchor is the value every tick is verified against and therefore cannot be delivered out of band.
The DEFAULT encoding of tickInterval keeps that field off the wire whenever the default period is used, holding the committed cost to the anchor itself.
The more compact entry-extension encoding (Appendix F.4) trims the framing further.¶
This document carries the anchor as an X.509 extension of the TBSCertificateLogEntry rather than as a committed MTCLogEntryExtension, so that the anchor rides as ordinary certificate bytes and the generic MTC log and cosigner infrastructure need not be MTCRS-aware. The anchor's home is the one design choice this document explicitly refers to the working group, and Appendix F.4 sets out the alternative and its trade-offs in full. Whichever the base specification selects becomes the single anchor home for the ecosystem, and the verification procedure (Section 9) is identical either way.¶
The id-pe-hashChainAnchor extension SHOULD be marked non-critical, so that relying parties that do not implement this mechanism can still process the certificate. Such a relying party ignores the extension, and because the tick travels as a cosignature from a cosigner it does not recognize, it ignores the tick as well (Section 8.1.1). It therefore accepts the certificate exactly as it would one that carried no anchor. However, relying parties that do implement this mechanism MUST enforce hash chain verification as described in Section 9 when the extension is present. An MTC ecosystem in which all relying parties are expected to support hash chain revocation MAY mark the extension critical, causing implementations that do not recognize it to reject the certificate. Marking the extension critical is the transition lever that forces relying parties unaware of this mechanism to hard-fail rather than silently ignore it. The entry-extension encoding (Appendix F.4) lacks this lever. During a transition in which not all relying parties yet implement this mechanism, the base MTC revoked-ranges mechanism and external revocation systems continue to provide coverage, and a root program MAY mandate critical marking once adoption is deemed sufficient. Criticality is the one lever governing how this mechanism enters an ecosystem that does not yet implement it everywhere, and Appendix E.7 explains why no other is needed.¶
When a hash chain anchor extension is present in the certificate, the authenticating party MUST include a hash chain tick in the MTCProof structure (carried in the certificate's signatureValue), as the tick cosignature of Section 8.1.1.
The tick is the certificate's non-revocation proof: where the inclusion proof and cosignatures attest that the certificate is authentic, the tick attests that it has not been revoked as of the current period.
The tick is a HashChainTick:¶
struct {
uint16 period;
HashValue value;
} HashChainTick;
¶
The period number for which this tick is valid.
It tells the relying party two things.
The first is the freshness the tick asserts: that the certificate was not revoked as of this period, checked against the relying party's clock (Section 9).
The second is how many times to hash value forward to reach the committed anchor.
The field is 16 bits, which caps both the period number and the relying party's forward-hash count at 65,535 for every certificate, whatever tick_interval its issuer chose (Section 4).¶
The hash chain value h[hash_chain_length - period].¶
Carrying the period costs two bytes per handshake that could in principle be recovered.
A relying party could omit it and instead try each period its acceptance window admits, accepting the tick if any of them reaches the anchor, since the default window admits only three.
This document carries the period explicitly for two reasons.
The first is cost: trial verification multiplies the forward hashing, which Section 4 shows is the axis that needs bounding rather than relaxing, and it does so in the worst case at exactly the moment the count is already largest.
It also degrades as the window widens, since a relying party accepting k preceding periods would need k + 2 trials (Section 16.10).
The second is diagnosis.
An explicit period distinguishes a stale tick from a forged or misrouted one, both for the relying party's error reporting and for the authenticating party's pre-installation freshness check (Section 10).
That check has no acceptance window to search and no way to detect staleness without the period.¶
The MTCProof is not committed to the Merkle Tree (only the TBSCertificateLogEntry is hashed into the tree), so the tick can be updated each period without affecting the inclusion proof or cosignatures.
The authenticating party reconstructs or replaces the signatureValue with a fresh tick while reusing the same inclusion proof and the other cosignatures.¶
The authenticating party MUST include a HashChainTick whose period falls within the default acceptance window (step 4 of Section 9.2), computed against its own clock.
It is bound to the default, and to its own clock, because neither the window a given relying party applies nor that party's clock is observable to it.
The window's tolerance of one period in each direction is what absorbs the difference between the two clocks (Section 16.10).
The default suffices, since a relying party may only widen the window and therefore accepts a superset of what the default admits.
In practice this is the most recent tick it has fetched and verified (Section 10).
It SHOULD present the current period's tick once it holds one, but is not required to switch at the period boundary.
The deterministic fetch offset means the preceding period's tick is normally presented for the first part of each period (Section 13.3).
An authenticating party that cannot obtain a fresh tick likewise continues to present its most recent still-valid one (Section 13.1).
The relying party checks tick.period against its own clock using the acceptance window, which allows for clock skew and caching and is specified in step 4 of Section 9.2.¶
A relying party that implements this mechanism rejects a certificate whose anchor is not accompanied by a tick (Section 9.2), so a certificate carrying an anchor needs one from the moment it is issued.
The CA MUST therefore include the tick cosignature (Section 8.1.1) in every MTCProof it constructs for such a certificate, carrying the tick for the period the certificate is in at issuance, or the period 0 tick if it has not yet entered period 0.
For a certificate whose notBefore is not backdated by a full tick_interval, which includes one dated in the future, that tick is the committed anchor, which the authenticating party could equally construct for itself (Section 5.1).
For one backdated further it is the tick for the period the certificate is already in, which the CA must in any case be serving by then (Section 4).
An authenticating party MAY present the certificate as delivered for as long as that tick remains within the default acceptance window, and refreshes it thereafter (Section 12).¶
The HashChainTick travels in the MTCProof's existing signatures field, as a Cosignature (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]) attributed to a cosigner ID reserved for the purpose.
The base MTCProof structure, and the way the base specification parses it, are unchanged.¶
The tick cosigner ID of a CA is the trust anchor ID formed by appending the component hashChainTick(TBD) to that CA's ID, {caID hashChainTick(TBD)}, using the "MTC CA Identifier Child Components" registry of the base specification (Section 13.2.3 of [I-D.ietf-plants-merkle-tree-certs]), as requested in Section 17.
For the CA ID 32473.1 of the test vectors, and assuming the value 3, the tick cosigner ID is 32473.1.3, whose binary representation is the five bytes 81fd590103 (Appendix B).
Both the authenticating party and the relying party form it from the certificate's issuer, as they obtain issuer_ca_id (Section 6), so nothing need be configured to distribute it.¶
When a certificate carries a hash chain anchor, the signatures field of every MTCProof presented with it MUST contain a Cosignature whose cosigner_id is the issuing CA's tick cosigner ID and whose signature is the serialized HashChainTick, exactly 2 + HASH_SIZE bytes.
The base specification already requires cosigner_id values to be unique and sorted, so the tick cosignature occupies the position that ordering gives it, and an MTCProof can carry at most one.
When a certificate carries no anchor, its MTCProof SHOULD NOT contain a tick cosignature, and a relying party ignores one as it ignores any unrecognized cosigner.
The authenticating party MUST include the tick cosignature in every presentation of a certificate that carries an anchor, whatever subset of the other cosignatures it selects for a given relying party.¶
A tick cosignature is not a signature. It is computed over no CosignedSubtree, verifies against no public key, and says nothing about the subtree. A relying party MUST NOT count it toward its cosigner requirements, and MUST NOT be configured with a tick cosigner ID as a trusted cosigner (Section 7.3 of [I-D.ietf-plants-merkle-tree-certs]). A CA MUST NOT operate a cosigner, or allocate a GREASE cosigner ID, under its tick cosigner ID. Section 16.6 discusses why.¶
Carrying the tick this way asks nothing new of a relying party that does not implement this mechanism.
Such a relying party ignores cosigners it does not recognize (Section 7.2 of [I-D.ietf-plants-merkle-tree-certs]), and the base specification already admits cosignatures whose signature is not a subtree signature, in the form of GREASE cosignatures (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]).
It therefore accepts the certificate as it would one without an anchor, which is what lets the non-critical anchor extension deploy incrementally (Section 7.2).
For the same reason the tick adds no region of ignored data that the base MTCProof did not already have.
A landmark-relative certificate needs no cosignatures to authenticate its subtree, but the base specification permits it to carry cosignatures for other purposes (Section 6.4.4 of [I-D.ietf-plants-merkle-tree-certs]), so it carries the tick cosignature in the same way.¶
Enforcement does not depend on the cosignature being present, because what triggers it is the committed anchor. An active attacker can strip the tick cosignature, as it can strip anything in the uncommitted MTCProof, but a relying party implementing this mechanism then finds an anchor without a tick and rejects the certificate (step 2 of Section 9.2).¶
The framing costs a few bytes.
A Cosignature prefixes the tick with the cosigner ID and its one-byte length, and with a two-byte length for the signature, so a tick cosignature occupies 38 + n bytes for a CA ID of n bytes, which is 42 bytes in the test vectors.
The tick cosigner ID is itself a trust anchor ID, whose binary representation may not exceed 32 bytes (Section 4 of [I-D.ietf-tls-trust-anchor-ids]), so a CA whose ID is already 32 bytes long has no tick cosigner ID and cannot use this mechanism.¶
Taking the ID from the CA's own arc is what makes it collision-free, since no real or GREASE cosigner can hold an ID that the CA allocates beneath itself and the registry reserves. It also keeps the ID beneath a Private Enterprise Number, as trust anchor IDs require (Section 4 of [I-D.ietf-tls-trust-anchor-ids]). A single tick cosigner ID shared by every CA would be a few bytes shorter, but it would need an arc of its own and could not be recognized from the certificate's issuer. Appendix F.8 records the other ways of carrying the tick in the MTCProof that were considered.¶
Because the authenticating party rewrites the tick once per period, the complete certificate it presents is not stable over the certificate's lifetime.
The TBSCertificate is fixed at issuance and never changes, but the signatureValue that carries the MTCProof changes at least once per tick_interval, for every certificate that uses this mechanism.
This introduces no new property.
The base specification already treats the signatureValue as malleable, and already requires that applications deriving a unique identifier from the Certificate use the TBSCertificate, or some portion of it, instead (Section 12.6 of [I-D.ietf-plants-merkle-tree-certs]).
What changes is the frequency: an allowance a deployment might otherwise never exercise becomes a rewrite at a predictable cadence, on every certificate that carries an anchor.¶
The base requirement is restated here in the form this mechanism gives it. Applications MUST derive any stable certificate identifier from the TBSCertificate, never from the complete certificate and never from the MTCProof. This applies wherever a certificate is used as a cache key, compared for equality, or recorded for later comparison, including:¶
certificate pinning and fingerprint allow-lists;¶
caches, session state, and connection-reuse decisions keyed by certificate, such as HTTP/2 and HTTP/3 connection coalescing;¶
logging, auditing, and monitoring pipelines that deduplicate by certificate fingerprint, which would otherwise record a distinct value for each certificate in each period.¶
The failure modes here are quiet rather than loud: a cache that stops hitting, a pin that no longer matches, an audit log that grows by a factor of the number of periods. None of them produces an error that points at its cause, which is why this requirement is stated normatively rather than left to be inferred from the mutability of the MTCProof.¶
Monitors are unaffected. They consume log entries rather than presented certificates (Section 16.8), and the entry, including the committed anchor, is fixed for the life of the certificate.¶
No new TLS extension type is required.
When the authenticating party presents a Merkle Tree Certificate, the hash chain tick is carried within the certificate's signatureValue as part of the MTCProof, which is already transmitted in the CertificateEntry.¶
The presence of the hash chain anchor signals to the relying party that the MTCProof carries a HashChainTick (Section 8).
That is the id-pe-hashChainAnchor extension in the primary design, or the hash_chain_anchor entry extension in the alternative (Appendix F.4).
If the tick is absent, malformed, or fails verification, the relying party MUST reject the certificate.¶
Two certificate profiles exist for the same log entry: a standalone certificate and a landmark-relative certificate (Sections 6.3 and 6.4 of [I-D.ietf-plants-merkle-tree-certs]). The two differ only in the subtree and signatures carried in their MTCProof. They certify the same TBSCertificateLogEntry. Either may be issued by the CA, and the landmark-relative form may also be constructed by any party holding the standalone certificate, since the procedure is not specific to the CA (Section 6.4.4 of [I-D.ietf-plants-merkle-tree-certs]).¶
Hash chain revocation is keyed by the log entry, not by the certificate profile:¶
Both profiles commit to the same id-pe-hashChainAnchor extension, which is part of the TBSCertificateLogEntry or of the entry's extensions (Appendix F.4), so a single anchor and hash chain per entry serves both.¶
The certificate's serialNumber is fixed by the entry's position in the log (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]), so both profiles carry the same serial and therefore resolve to the same tick URL (Section 10).¶
The HashChainTick for a given period is therefore identical in both certificates.¶
An authenticating party commonly holds both a standalone and a landmark-relative certificate for the same entry. Under ACME the standalone certificate response carries a link to the landmark-relative one, which the CA constructs and serves once a landmark covering the entry has been allocated (Section 9.2 of [I-D.ietf-plants-merkle-tree-certs]). It may also hold both during the renewal overlap described in Section 10.4 of [I-D.ietf-plants-merkle-tree-certs]. It fetches the entry's tick once per period and writes that same value into the MTCProof of whichever certificate it presents. Refreshing the tick is independent of profile selection: the authenticating party selects between the two certificates using the base MTC mechanism (Section 8 of [I-D.ietf-plants-merkle-tree-certs]), and updates the HashChainTick in whichever MTCProof it sends.¶
An authenticating party that constructs its own landmark-relative certificate acquires one obligation under this document, which is to include the tick cosignature in it (Section 8.1.1). Construction recovers the certificate inputs from the standalone certificate and takes the subtree and inclusion proof from the issuance log, none of which touches the log entry. The anchor and the tick URL are therefore unchanged, and the tick the authenticating party already holds applies to the constructed certificate without a further fetch.¶
When a relying party receives a Merkle Tree Certificate with the id-pe-hashChainAnchor extension, it performs hash chain verification in addition to the base MTC verification procedure.
The relying party MUST complete the base MTC validity check, which bounds the certificate between its notBefore and notAfter (Section 7.2 of [I-D.ietf-plants-merkle-tree-certs]), before computing any period or performing any of the forward hashing of step 5.
That ordering is what confines the work of step 5 to a certificate the relying party is otherwise willing to accept.
The steps below name the id-pe-hashChainAnchor X.509 extension of the primary design.
Under the entry-extension alternative (Appendix F.4) the relying party instead reads the same HashChainAnchorInfo from the entry's extensions, and the procedure is otherwise identical.
Figure 6 shows the two checks that bind a tick to the certificate.¶
The verifier first assembles the inputs to HashChainInput (Section 6) and to the period computation (Section 4). All of them are read from the certificate itself, and the verifier additionally needs HASH, which it holds as part of its base MTC configuration for the issuing CA (Section 2). No data from the CA's tick distribution service (Section 10) is needed, and the verifier MUST NOT fetch anything (Section 16.5):¶
issuer_ca_id:The issuing CA's ID, read from the certificate's issuer field (Section 6), which is the same value the base procedure extracts to construct the log ID (Section 7.2 of [I-D.ietf-plants-merkle-tree-certs]).
A relying party MUST take it from the certificate rather than from whichever trust anchor it is chaining to.
For a correctly chaining certificate the two are equal, and a relying party MAY check that they are.
It also determines the tick cosigner ID (Section 8.1.1).¶
tick_interval and anchor:Read from the HashChainAnchorInfo carried in the id-pe-hashChainAnchor extension.
If tickInterval is absent, use its default of 3600 (Section 7).¶
not_before:The notBefore time of the certificate's validity period (Section 4), which is the same value the CA used to number periods.¶
Using these inputs, the verifier performs the following steps:¶
Extract the HashChainAnchorInfo from the certificate's id-pe-hashChainAnchor extension.
If not present, skip hash chain verification (the certificate does not use this mechanism).
If more than one is present, reject the certificate with a bad_certificate error.
Section 4.2 of [RFC5280] forbids a certificate from including more than one instance of an extension, and nothing would select between two anchors.
If the extension value does not DER-decode as a well-formed HashChainAnchorInfo, reject the certificate with a bad_certificate error.
Examples are a malformed SEQUENCE, a tickInterval outside INTEGER (1..4294967295), a tickInterval present carrying the DEFAULT value 3600, which DER requires to be omitted (Section 11.5 of [X.690]), or trailing data after the structure.
A decoder that accepts DEFAULT values permissively, as a BER decoder does, will not catch the third of these, so it MUST be checked explicitly.
The anchor extension is committed to the Merkle Tree, so a non-canonical encoding of it would be a distinct entry certifying the same thing.
If the anchor OCTET STRING is not exactly HASH_SIZE bytes, reject the certificate with a bad_certificate error.
Reject the certificate with a bad_certificate error if hash_chain_length, which is ceil(lifetime / tick_interval), exceeds 65,535.
A CA is forbidden to issue such a certificate (Section 4), and the relying party can compute the quantity from notBefore, notAfter and tick_interval alone, as for the check below.
The tick's 16-bit period field cannot represent that certificate's later periods, so it would otherwise stop verifying partway through its life for a reason the verifier could have detected at the outset.
Finally, reject the certificate with a bad_certificate error if its validity period is not longer than tick_interval, which a CA is forbidden to issue (Section 4) and which the relying party can detect from notBefore, notAfter and tick_interval alone.
Such a certificate never leaves period 0, so its only tick is the public anchor and the mechanism would enforce nothing while appearing to.¶
Find the tick cosignature: the Cosignature in the MTCProof's signatures whose cosigner_id is the issuing CA's tick cosigner ID (Section 8.1.1).
If there is none, reject the certificate with a bad_certificate error.
If its signature is not exactly 2 + HASH_SIZE bytes, reject the certificate with a bad_certificate error.
Otherwise parse that signature as the HashChainTick.
The tick cosignature does not count toward the relying party's cosigner requirements (Section 8.1.1).¶
Compute the expected period from the current time:¶
expected_period = floor((current_time - not_before) / tick_interval)¶
Check that tick.period lies within the acceptance window.
The default acceptance window is expected_period - 1, expected_period, and expected_period + 1.
A relying party MAY widen it in either direction as a matter of policy, with the consequences described in Section 16.10 and collected in Section 16.11.
It MUST NOT narrow it, because the authenticating party targets the default and cannot observe a narrower one (Section 8).
A relying party MUST reject a certificate whose tick.period falls outside the acceptance window it applies, with a certificate_expired error.
That alert covers both directions, because [RFC9846] defines it as "a certificate has expired or is not currently valid".
A tick too far in the future, which a verifier whose clock is well behind the authenticating party's would see, is not currently valid either.
The alert is deliberately not certificate_revoked, which [RFC9846] defines as "a certificate was revoked by its signer".
An out-of-window tick does not establish that.
It means only that non-revocation is unproven as of now, which a distribution outage (Section 13.1), an authenticating party that failed to refresh, or a skewed clock (Section 16.10) all produce just as a genuine revocation does.
Claiming revocation would assert something the relying party cannot know and would misdirect diagnosis of what is usually an availability fault.¶
The two edges of the window are not symmetric.
There is no period below 0, so when expected_period is 0 the lower neighbor is simply absent and the default accepted set is {0, 1}.
The upper neighbor needs no such treatment.
A relying party does not know hash_chain_length (Section 7), so it cannot tell whether expected_period + 1 lies beyond the certificate's last period, and it need not.
No tick exists for a period the CA never reveals, and one cannot be forged (Section 16.1), so admitting the value costs nothing.¶
Two arithmetic hazards attend the comparison.
A verifier MUST NOT compute expected_period - 1 in unsigned arithmetic, which would underflow (the same hazard as the negative period expression of Section 4).
It MUST likewise compute the window in arithmetic wider than the 16-bit period field.
Since hash_chain_length cannot exceed 65,535, the final period of a valid certificate is 65,534 and expected_period + 1 reaches at most 65,535, which fits, but exactly at the top of the range with no headroom.
A verifier computing in 16 bits would wrap to 0, and so admit a period-0 tick, as soon as expected_period exceeded that, which a certificate already past its notAfter produces.
That is why the base validity check MUST precede any period computation, as stated at the head of this section.¶
Starting from tick.value, iteratively hash tick.period times:¶
v = tick.value
for i = 1 to tick.period:
v = Hash(HashChainInput(v))
¶
The count is tick.period, not hash_chain_length - tick.period: the subtraction is applied when the CA chooses which value to reveal, since period t reveals h[hash_chain_length - t], which lies exactly t hashes below the anchor (Section 5.1).
A relying party therefore never needs hash_chain_length, which is not carried in the certificate (Section 7).
As the period counts up, the hash chain index counts down, and the number of forward hashes to the anchor is the period itself.¶
Two independent bounds confine this iteration, so a forged or oversized tick cannot inflate it into a denial-of-service vector.
The first is structural: tick.period is read from a 16-bit field, so the count cannot exceed 65,535 for any certificate from any issuer, without the relying party trusting the CA to have chosen sensible parameters (Section 4).
The second is the certificate's own: step 4 has already constrained tick.period to the acceptance window, whose upper end is bounded by the certificate's lifetime.
The largest legitimate tick.period is hash_chain_length - 1, the certificate's final period, which requires hash_chain_length - 1 forward hashes: 1,127 for the 1,128-period hash chain of a 47-day, one-hour-period certificate (Appendix E.3).
Under the default acceptance window, the expected_period + 1 allowance (Section 16.10) raises the defensive upper bound the verifier must tolerate by one, to hash_chain_length.
A relying party that widens the window raises that bound correspondingly.¶
A relying party MAY additionally impose a local ceiling well below 65,535.
It can compute that same bound as hash_chain_length, which is ceil(lifetime / tick_interval) (Section 4), from the certificate alone, and reject with a bad_certificate error if it exceeds its configured limit.
Implementations for which even the structural bound is too costly, such as constrained devices, SHOULD configure such a limit and SHOULD apply it before beginning the iteration, so that the check costs one division rather than the hashing it avoids (Section 13.6).
The consequences of setting that limit are collected in Section 16.11.¶
Compare the result with anchor from the HashChainAnchorInfo. If they do not match, reject the certificate with a bad_certificate error.¶
If all steps succeed, the hash chain verification passes, confirming that the CA has not revoked this certificate as of the indicated period.¶
The MTCProof is not committed to the Merkle Tree and can be modified in transit, but the tick is nonetheless self-authenticating.
Steps 5 and 6 bind both tick.value and tick.period to the committed anchor, because a given value reaches the anchor only after the exact number of forward hashes that its true period implies.
Tampering with either field therefore fails verification, and producing a tick for a period the CA has not yet revealed requires inverting the hash function (Section 16.1).
The only tick an attacker can present that still verifies is a genuine, already-revealed one, and step 4 bounds how stale that may be.¶
Authenticating parties obtain current hash chain ticks over the HTTP interface defined in this section. It is the interoperable baseline for this mechanism, and support for it is mandatory at both ends.¶
A CA MUST ensure that ticks are served over this interface, at the tick base URL it publishes (Section 10.2), for every period in which it reveals a value (Section 5.1). It need not serve them from its own infrastructure. It MAY delegate that to mirrors, content delivery networks, or other distributors, which changes who answers the request but not the interface or the URL that addresses it (Section 13.5). An authenticating party MUST support this interface.¶
A CA and its authenticating parties MAY additionally agree on any other channel, such as a batch endpoint (Section 13.4) or an entirely different transport (Appendix F). Any such agreement is bilateral, and supplements the baseline rather than replacing it, so an authenticating party that implements only this interface can always obtain its ticks from any conforming CA. Nothing in this section concerns relying parties, which verify the embedded tick offline and never fetch (Section 16.5).¶
At large deployment scale, tick distribution is dominated by aggregate request volume rather than per-request cost.
A CA serving 109 active certificates with a one-hour period sees on the order of 105 to 106 tick requests per second.
That population is above the base specification's own estimate for a single large CA (Section 6.5 of [I-D.ietf-plants-merkle-tree-certs]), so the figure is an upper bound rather than a projection.
Because each certificate's periods run from its own notBefore (Section 4), that load is spread across the interval to the extent issuance times are.
It concentrates only where many certificates share a boundary, as when a CA rounds notBefore or renewals arrive in waves.
At this scale the serving path is fed by replication rather than by cache fill.
Each tick is immutable within its period and so is freely cacheable, but caching collapses requests only where an entry has several fetchers, as in a deployment that terminates TLS on many nodes (Section 12).
A certificate presented by a single server has one fetcher per period, so its request is a cache miss whenever it is made, and edge caching alone therefore does not reduce the number of distinct entries an origin must answer for.
What does is delegated distribution (Section 13.5), in which the CA publishes the currently-revealed values to distributors as a bundle and those distributors answer the requests, so no per-certificate request need reach the CA at all.
Spreading client refresh timing (Section 13.3) then flattens what remains.
Both are operational rather than protocol matters, and are described alongside bulk retrieval in Section 13.¶
The CA (or a mirror) serves ticks over HTTP. Given a tick base URL for the CA (see Section 10.2), the tick for a particular certificate is fetched from:¶
The tick base URL that the CA publishes (Section 10.2) MUST have the form {origin}/.well-known/mtcrs/v1/{hash_name}.¶
origin:A scheme, host, and optional port. A CA MAY point that hostname at a CDN or mirror through ordinary DNS or HTTP routing.¶
hash_name:The name of the CA's log hash algorithm, taken from the "Named Information Hash Algorithm Registry" [RFC6920], which gives lowercase names such as sha-256 that need no percent-encoding in a path segment.
It MUST name the CA's log hash algorithm (Section 2).
The base specification identifies that algorithm by an object identifier and this interface names it from a different registry, so a CA MUST NOT serve this interface for a hash that has no entry in the Named Information Hash Algorithm Registry.
Registering one would be a prerequisite for carrying, over this interface, a hash that a future base specification introduces.
Carrying it here is what gives the authenticating party HASH, and it costs nothing to convey.
The URL is the one value a CA is already obliged to deliver, so any issuance protocol that delivers it delivers the algorithm with it (Section 10.2).
An authenticating party MUST NOT fetch from a base URL naming an algorithm it does not implement, and MUST NOT guess one.
It MUST also check that the named algorithm's output length equals the length of the anchor in its own certificate, and MUST NOT fetch if the two disagree.
That check costs nothing and needs no other input.
Without it the mismatch is not caught until every fetched tick fails, either on the response-length check (Section 10.4) or because forward hashing under the wrong algorithm can never reach an anchor of a different length.
The check detects a disagreement in output length rather than in algorithm, so it does not catch a substitution between two hashes of the same size.
Where the authenticating party also holds the CA certificate, the algorithm named by that certificate's Merkle Tree CA extension type is authoritative, and it SHOULD report a disagreement as a CA misconfiguration.¶
serial_number:The certificate's serialNumber, which the base specification constructs from the entry's log number and its zero-based index within that log as (log_number << 48) | index (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]).
It is encoded in big-endian order as exactly 16 lowercase hexadecimal digits, zero-padded, so that the CA and the authenticating party derive an identical URL and every request has the same shape.¶
The CA and the authenticating party both read the serial straight from the certificate. The authenticating party in particular does not reconstruct the log entry, so deriving its own tick URL costs it no cryptography and needs no per-request metadata from the CA.¶
The serial is the base specification's own identifier for an entry, used to locate it in the log during verification (Section 7.2 of [I-D.ietf-plants-merkle-tree-certs]) and to name it in the revocation record sketched in Section 16.8.1. Keying ticks on it therefore keeps one identifier for one entry throughout. Its uniqueness is structural rather than probabilistic, because indices are assigned sequentially within a log and the log number distinguishes a CA's logs. No two of a CA's entries can therefore share a URL, and there is no collision case to reason about. That sequential assignment is the property the base specification itself identifies as one that may enable improvements to revocation (Section 12.7 of [I-D.ietf-plants-merkle-tree-certs]). It is also identical for a given entry's standalone and landmark-relative certificates, which differ only in the proof they carry (Section 8.4).¶
The URL is therefore trivially derivable by anyone holding the certificate, and that cuts both ways. It lets a third party enumerate a CA's population by counting indices and probe each certificate's status. That is a smaller change than it appears, because MTC issuance logs are published, so any party willing to download one could already derive every address under any keying (Section 16.4). Derivability is also what lets a monitor watch an endpoint for withheld ticks at all, which is the check Section 16.4 offers. A CA that wants its endpoint not to answer to enumeration replaces the path segment with the unguessable token of Section 10.3, which denies derivation outright rather than merely making it laborious. Relying parties MUST NOT fetch under either keying, and gain nothing by doing so, since the authenticating party already presents the current tick (Section 16.5).¶
The tick base URL is not derived from the CA's identifier. A Merkle Tree CA is identified by a TrustAnchorID, which is a relative object identifier (Section 5.1 of [I-D.ietf-plants-merkle-tree-certs]) rather than a hostname, so it cannot be turned into an origin. The base URL is instead conveyed to the authenticating party out of band, as described in Section 10.2.¶
The scheme (http:// or https://) is whatever the CA specifies as part of the base URL.
Because each tick is self-authenticating, the tick fetch does not require transport-layer integrity, and because the tick value is public, it does not require transport-layer confidentiality of the response body.
CAs MAY therefore publish an http:// base URL, which eliminates TLS handshake overhead and permits caching by any HTTP intermediary.
The one property plain HTTP does not provide is confidentiality of the request itself.
An on-path observer can see which {serial_number} is being requested.
CAs whose deployments consider this metadata sensitive SHOULD publish an https:// base URL instead.¶
For example, if a CA using SHA-256 publishes the tick base URL http://mtcrs.example/.well-known/mtcrs/v1/sha-256, ticks are served at:¶
http://mtcrs.example/.well-known/mtcrs/v1/sha-256/tick/a1b2c3...f0¶
The v1 segment versions the MTCRS HTTP interface as a whole: the placement of {hash_name}, the addressing of {serial_number}, and the response format (Section 10.4).
It is a migration lever, not a per-request parameter.
A future revision needing a different addressing scheme or wire format would define a v2 namespace, which a CA MAY serve alongside v1 during a transition, without affecting the Merkle Tree, the non-revocation proof, or already-issued certificates.¶
Because the CA's TrustAnchorID is an identifier and not a locator, the tick base URL MUST be conveyed to the authenticating party out of band. This is not a gap. The authenticating party already obtains its certificate through a provisioning channel that involves a real CA endpoint, for example an ACME directory. That channel is therefore the natural carrier for the base URL, and no locator need be derived from the certificate.¶
A CA MUST make the tick base URL available through at least one of the following mechanisms. An authenticating party MUST support the provisioning-channel mechanism appropriate to how it obtains certificates, and MUST also support reading the base URL from the CA certificate SIA whenever it holds that certificate. Supporting only the former would leave it unable to obtain the base URL from a CA that publishes only the latter, which is the CA's sole option for an issuance protocol with no provisioning binding. A CA and an authenticating party could then each conform and still fail to interoperate.¶
The base specification defines the CA's certificate representation (Section 5.5 of [I-D.ietf-plants-merkle-tree-certs]) but not how a party comes to hold it, and an authenticating party, unlike a relying party, has no trust anchor configuration for it to arrive in (Section 6). In practice it comes from the channel that issued the certificate, where that protocol returns one, or from local configuration. A CA relying on the SIA as its only discovery route MUST therefore ensure its subscribers can obtain its CA certificate, since otherwise the fallback is unavailable to exactly the deployments it exists to serve. Nothing else obliges an authenticating party to hold that certificate, because the base URL names the log hash algorithm in its final segment (Section 10), so a party holding the URL holds everything this mechanism asks of it.¶
The base URL is delivered when the certificate is provisioned. Section 11 defines this binding for ACME. Each other issuance protocol needs an analogous binding, which this document does not define. A CA issuing over such a protocol MUST either specify one (for example, in a short companion document registering the equivalent of the ACME fields of Section 11) or publish the base URL through the CA certificate SIA below. A provisioning binding is moreover required for unguessable tick URLs (Section 10.3), which the SIA cannot carry. A protocol with no provisioning binding can therefore support only the derivable-URL scheme, and only via the SIA.¶
The base URL MAY additionally be published in the CA's certificate representation (Section 5.5 of [I-D.ietf-plants-merkle-tree-certs]) using the id-ad-mtcrsTicks Subject Information Access access method defined in Section 17, whose accessLocation is a uniformResourceIdentifier giving the tick base URL.
Its final segment names the same algorithm as that certificate's own Merkle Tree CA extension type, so a CA publishing both states the algorithm twice and MUST state it consistently (Section 10).
Publishing it is the CA's choice; understanding it is not the authenticating party's, since this is the only carrier available to a CA whose issuance protocol has no provisioning binding.
This carries a single per-CA URL on a single object, adds no per-log-entry bytes, and provides a protocol-independent, published record that an authenticating party, its tooling, or an auditor can read once without access to any provisioning transcript.
Because it is per-CA and distributed out of band rather than presented in the TLS handshake, it avoids the costs that led this document to reject a per-certificate tick URL in Authority Information Access (Appendix F.3).
Its value is therefore as a published fallback and tooling record.
Where a provisioning binding exists (for example ACME, Section 11) it is redundant with that channel, which takes precedence when the two disagree.
Where none exists, it is the only interoperable carrier of the base URL.
It carries only the base URL, so it does not apply when unguessable tick URLs (Section 10.3) are used.
In that case the full per-certificate URL is delivered through the provisioning channel and the SIA SHOULD NOT be published.
A deployment that uses unguessable tick URLs therefore has a single discovery channel, the provisioning channel, with no SIA fallback.
The CA MUST ensure that channel reliably delivers the complete per-certificate URL.¶
Keeping the base URL out of the certificate is not a secrecy measure, since the URL is derivable by anyone holding the certificate (Section 16.5). It is instead a way to avoid standardizing a per-certificate fetch affordance in a field relying parties routinely parse. Only the authenticating party is given the base URL through provisioning, because only it needs to refresh the value it presents. Relying parties verify the embedded tick offline (Section 9) and never fetch (Section 16.5). The base URL (or, for unguessable tick URLs, the full per-certificate URL) is delivered once at provisioning. The authenticating party MUST retain it for as long as it presents the certificate and reuse it to refresh the tick each period (Section 10), rather than rediscovering it per fetch.¶
Merkle Tree Certificates are renewed frequently (Section 10.4 of [I-D.ietf-plants-merkle-tree-certs] recommends renewal at about 75% of lifetime). A CA that migrates its tick infrastructure can therefore update the base URL it hands out and rely on renewals to propagate the change, optionally serving HTTP redirects from the old origin in the meantime.¶
The tick fetch path described above is .well-known/mtcrs/v1/{hash_name}/tick/{serial_number}, and {serial_number} is read directly from the certificate by anyone who holds it, including a relying party (Section 16.5).
Keeping the base URL out of the certificate therefore does not make the tick URL unguessable.
A CA that wishes to make relying-party fetching infeasible by construction, rather than only forbidding it normatively, MAY replace the derivable path component with an unguessable per-certificate capability token:¶
{tick_base_url}/tick/{tick_token}
¶
tick_token:A high-entropy (at least 128-bit) value that does not appear anywhere in the certificate.
Because the token is absent from the certificate, a relying party cannot construct the URL, while the authenticating party is given it at provisioning time (see below).
It is carried as a single URL path segment, so a CA that derives it from binary material, as the deterministic construction below does, MUST encode it in characters that need no percent-encoding in a path segment.
Lowercase hexadecimal, as used for serial_number above, and base64url without padding ([RFC4648]) both qualify.
The encoded segment MUST NOT exceed 255 characters, which leaves ample room for the construction below and keeps the request within the URL lengths intermediaries and origin servers commonly accept.¶
The CA MAY generate the token by either of the following methods:¶
Random, with server-side state. The CA generates a random token per certificate and maps it to the corresponding hash chain. This adds one indexed lookup to the CA's existing per-certificate state.¶
Deterministic, stateless (RECOMMENDED). The CA derives the token by applying a deterministic authenticated encryption scheme (for example, AES-SIV [RFC5297]) keyed by a CA-held secret to the entry identifier:¶
tick_token = key_id || DAE(K_ca, serial_number)¶
The CA recovers serial_number by decrypting the token, so no additional per-certificate state is required.
The token is unguessable without K_ca and is stable for the certificate's lifetime, which preserves caching.
The key_id prefix identifies K_ca so that it can be rotated.
The CA retains superseded keys for decryption during an overlap window, and because Merkle Tree Certificates are renewed frequently, rotated tokens propagate through renewal, as for base-URL migration (Section 10.2).¶
When this hardening is used, discovery is necessarily per-certificate: the CA delivers the complete tick URL (base URL and token together) to the authenticating party through the provisioning channel.
For ACME, the order object carries the full URL in a tickURL field, and the CA does not publish a tickBaseURL in its directory metadata (Section 11).
The CA-certificate SIA (Section 10.2) conveys only the per-CA base URL, which cannot locate a token-addressed tick, and only the provisioning channel carries the token.
The SIA therefore provides no operational value when unguessable tick URLs are used, and a CA that uses them SHOULD NOT publish the id-ad-mtcrsTicks access method.¶
This hardening preserves the properties of the base HTTP interface. Each token addresses a value that is immutable within a period, so per-period caching is unchanged and a cache in front of the origin is indifferent to how the path segment was formed (Section 13.3).¶
Delegated distribution (Section 13.5) needs one adjustment.
A distributor is asked for a token rather than a serial, and cannot resolve one to the entry it names without K_ca.
Giving it K_ca would let it derive every token the CA issues, and is unnecessary.
A CA that uses unguessable tick URLs MUST instead ensure that each of its distributors can answer the token-addressed requests directed to it, whatever the form of the feed, which this document does not standardize (Section 13.4).
Keying the bundle by tick_token is the natural way, since the CA is the party holding K_ca and a distributor then answers from a table keyed by exactly what the request carries, with no mapping of its own.
The records are correspondingly larger, since a token is longer than an 8-byte serial.
Nothing else about the arrangement changes, since the token is an addressing capability rather than a confidentiality secret and the values a distributor serves are public either way.¶
The token is an addressing capability, not a confidentiality secret, and this hardening is defense in depth rather than a hard security boundary. The tick it locates is public and self-authenticating, so the fetch still requires no transport-layer integrity or confidentiality and MAY be served over plain HTTP. The token is correspondingly a bearer capability. An authenticating party could disclose it, and an on-path observer of a plain-HTTP fetch can see it. Possession of it grants only the ability to fetch that public value, or to observe its absence. It does not permit forging ticks, which requires the secret hash chain values (Section 16.3), nor using the certificate, which requires the corresponding private key. Its benefit is that a relying party, or a third party holding a captured certificate, can no longer derive the tick URL from the certificate and probe the CA for its status (Section 16.5).¶
That benefit has a matching cost, because a monitor is also a third party holding the certificate. Unguessable tick URLs therefore trade away the external observability that Section 16.4 offers as a check on a CA withholding ticks, leaving the affected subscriber as the only party positioned to notice. A CA adopting them is to that extent asking to be taken on trust, and a deployment that values the check above the unprobeability should keep its tick URLs derivable.¶
The response body is the serialized HashChainTick structure: a 2-byte big-endian period followed by HASH_SIZE bytes of value (34 bytes total for SHA-256).
The response Content-Type MUST be application/octet-stream.
An authenticating party MUST reject a response body that is not exactly 2 + HASH_SIZE bytes.
It SHOULD NOT reject a response on the basis of its Content-Type, since it verifies the body against the anchor committed in its own certificate before installing it (Section 10) and that check is what establishes the response is the tick it asked for.¶
The CA uses HTTP status codes ([RFC9110]) as follows:¶
The response body is the current HashChainTick for the entry. During period 0 the current tick is the public anchor paired with period 0 (Section 5.1), so a CA whose distribution service is already provisioned for the entry MAY serve it. This is permitted but not required. The period 0 grace exists precisely so that provisioning need not be complete at the instant of issuance (Appendix E.4), and an authenticating party can construct that tick from the anchor committed in its own certificate in any case.¶
The CA is not serving a current tick for this entry. This covers both a revoked certificate, for which the CA has stopped revealing values (Section 5.2), and a tick that is merely not yet available, as during the period 0 grace (Appendix E.4). The status code does not distinguish these cases, so an authenticating party MUST NOT treat a 404 as definitive proof of revocation. It means only that no fresh tick was obtained on this attempt.¶
The server knows that no further tick will ever be published for this entry, because the certificate has been revoked (Section 5.2), or because it has expired and tick publication has stopped (Section 5.1). A CA MAY return 410 in place of 404 where it holds that knowledge, but is never required to. A delegated distributor generally cannot produce it at all: the CA revokes by dropping the entry from subsequent bundles, so a distributor cannot distinguish a revoked entry from one it was never given (Section 13.5). A 410 is a diagnostic hint and nothing more. It is unsigned and MAY be carried over plain HTTP (Section 10), so an on-path observer or a faulty distributor can forge one. An authenticating party MUST NOT let it curtail a tick that is still within the default acceptance window (Section 12). Nor may its absence be read the other way. A plain 404 says nothing about whether the certificate is revoked, so an authenticating party or monitor MUST NOT infer non-revocation from the lack of a 410.¶
Transient overload. The authenticating party retries according to the Retry-After header (Section 13.3).¶
An authenticating party follows redirects, which is what lets a CA migrate its tick infrastructure by redirecting from the old origin while renewals propagate the new base URL (Section 10.2). It SHOULD bound the number it follows for a single fetch, for which five is ample. Following one is safe even to an untrusted target, since whatever is returned is verified against the anchor committed in the authenticating party's own certificate before it is installed (Section 12). A redirect to a hostile origin can therefore deny service but cannot forge a tick.¶
Any other status code carries its ordinary HTTP semantics ([RFC9110]). An authenticating party treats any response that is neither a 200 nor a redirect it follows as "no fresh tick obtained on this attempt", and falls back to its most recent still-valid tick.¶
Requiring the period 0 tick to be served, rather than permitting it, would not make a 404 mean "revoked". Revocation is only one of several causes of a missing tick, alongside an origin outage, a cache miss, and a distributor that has not yet received the current bundle (Section 13.5), and those arise in every period. The ambiguity is therefore neither introduced nor removed by the period 0 case, and enforcement rests throughout on the presence of a fresh tick rather than the absence of one (Section 16.8).¶
The CA SHOULD set HTTP cache headers with a max-age no longer than tick_interval seconds.
For example:¶
When the CA issues certificates via ACME, it SHOULD convey the tick base URL in the meta object of its ACME directory (Section 7.1.1 of [RFC8555]) as a new field:¶
"tickBaseURL": "https://mtcrs.example/.well-known/mtcrs/v1/sha-256"¶
The tickBaseURL field contains the tick base URL from which the authenticating party derives its tick fetch URL, by appending /tick/{serial_number} (Section 10).
Its final segment names the CA's log hash algorithm, so an ACME client that has this field has both the locator and the algorithm, and needs nothing further (Section 2).¶
The directory is the right home for it because the value is per-CA, not per-certificate: a single base URL covers every certificate that CA issues (Section 10.2). Carrying it there means an ACME client fetches it once, from an object it already retrieves and can cache, rather than receiving the same constant on every order. It also lets the CA direct authenticating parties to a CDN, regional mirror, or any other origin, without adding bytes to the certificate or the log entry.¶
If the tickBaseURL field is absent from the directory metadata, the authenticating party obtains the base URL through another mechanism in Section 10.2, that is, the CA certificate SIA.¶
A CA using the unguessable-URL scheme (Section 10.3) does not publish tickBaseURL, because no single per-CA value can address a token-bearing URL.
It instead returns the complete per-certificate URL in a tickURL field of the ACME order object:¶
"tickURL": "https://mtcrs.example/.well-known/mtcrs/v1/sha-256/tick/9f8..."¶
The two fields therefore live in different objects and cannot collide: tickBaseURL is a property of the CA and tickURL a property of one issuance.
A CA MUST NOT publish tickBaseURL while issuing token-addressed ticks, since an authenticating party that used it would derive an address the CA does not serve.
Whether a per-certificate URL is better carried as a link relation on the certificate response, which is the form the base specification's own ACME extension takes, is left open (Appendix D.2).¶
CAs using issuance protocols other than ACME SHOULD provide an equivalent mechanism for communicating the tick base URL, or the complete per-certificate URL, during certificate provisioning.¶
This section defines what an authenticating party does with the interface of Section 10: how it refreshes a tick, what it checks before installing one, and when it may and may not present the certificate. It is the counterpart to Section 9, which defines the relying party's procedure.¶
For each certificate it serves, the authenticating party periodically fetches that certificate's current tick from the CA:¶
At least once per tick_interval, the authenticating party fetches an updated HashChainTick for the certificate.¶
Before installing a fetched tick, the authenticating party MUST verify it against the anchor committed in its own certificate: that hashing tick.value forward tick.period times yields the anchor (Section 9).
It MAY instead verify the tick against one it has already verified for an earlier period, by hashing the new value forward the difference between the two periods and comparing the result with the value it holds.
Consecutive revealed values are adjacent in the hash chain, since period t reveals h[hash_chain_length - t] (Section 5.1), so the ordinary case of a single elapsed period costs one hash computation rather than tick.period of them.
This shortcut applies only when the fetched period is greater than the one it holds.
A response carrying a lower period is stale or misrouted, and an authenticating party MUST NOT compute the difference in unsigned arithmetic, which would underflow into an enormous iteration count driven by a response it has not yet authenticated (the same hazard the verifier faces in Section 9).
It falls back to the full walk to the anchor, or discards the response.
This is transitively a verification against the anchor, because the held tick was itself verified against the anchor, so it is neither weaker nor an approximation.
Only the first tick an authenticating party installs for a certificate requires the full walk.
A tick that fails this check MUST NOT be installed or presented.
The authenticating party discards it and continues to serve its most recent still-valid tick, retrying as under Section 10.4.
This catches a corrupted, truncated, or misrouted (wrong-entry) response before it is ever presented in a handshake, where it would otherwise cause relying parties to reject the certificate.
The authenticating party SHOULD additionally check that tick.period is the period it currently expects, so that an unexpectedly stale or future-dated response is also rejected.
Because a stale-but-genuine tick still hashes to the anchor, only this freshness check detects it.
This part is a SHOULD rather than a MUST because clock skew between the authenticating party and the CA can legitimately shift the served period by one (Section 16.10).
A deployment MAY narrow it to strict equality where both clocks are trusted.¶
The authenticating party updates the HashChainTick carried in its certificate's MTCProof (signatureValue) with the newly fetched value.
That is an in-place overwrite of the tick cosignature's signature, which keeps both its length and its position among the cosignatures (Section 8.1.1).
The inclusion proof and the other cosignatures remain unchanged.¶
During TLS handshakes, the authenticating party presents the certificate with the current tick.¶
An authenticating party MUST NOT present a certificate carrying a hash chain anchor unless it holds a tick for that certificate whose period falls within the default acceptance window, computed against its own clock (Section 8). Every relying party implementing this mechanism rejects such a certificate, so presenting one converts a tick-distribution problem into a failed handshake for no benefit. Such a certificate is therefore ineligible for selection, dropping out of the candidate set exactly as one whose trust anchor the relying party does not support does (Section 4.5.1.2 of [RFC9846], Section 8 of [I-D.ietf-plants-merkle-tree-certs]). Where the authenticating party holds a certificate from another CA, it selects that one instead (see below). If no eligible certificate remains, it has none to present. A server's certificate_list MUST NOT be empty (Section 4.5.1 of [RFC9846]), so it MUST abort the handshake with a fatal alert rather than complete one that cannot verify. The RECOMMENDED alert is handshake_failure, which [RFC9846] defines as indicating that the sender was "unable to negotiate an acceptable set of security parameters given the options available". A certificate-related alert SHOULD NOT be used, because nothing is wrong with any certificate the relying party has sent or would receive: the condition is an availability fault on the authenticating party's side (Section 13.1). The one case in which it holds a usable tick without having fetched anything is period 0, where it can construct the tick from the anchor committed in its own certificate.¶
During period 0 the authenticating party need not fetch at all.
The period 0 tick is the public anchor committed in its own certificate (Section 5.1), which it can construct and present directly.
A 404 during period 0 is therefore expected and harmless, because the CA has until the start of period 1 to begin serving the entry's ticks (Appendix E.4).
A CA already provisioned for the entry may equally return that tick with a 200 (Section 10.4), which the authenticating party has no need of either way.
This does not apply to a certificate whose notBefore was backdated by one tick_interval or more, which is already past period 0 when it is issued.
Its first fetch must succeed before it can be served (Section 4).¶
Unlike a 404 during period 0, repeated failure to obtain a fresh tick is not harmless.
It is the observable signature of either revocation (Section 5.2) or a distribution failure, and a 404 does not tell the authenticating party which (Section 10.4).
An authenticating party SHOULD therefore raise an operational alarm once it has failed to obtain a fresh tick for a full tick_interval, rather than waiting until the certificate stops working.
By then it has at most one period of runway left.¶
Where the server offers the optional 410 (Section 10.4), the authenticating party learns that no further tick is coming. It can then attribute that alarm correctly, stop retrying an entry that will never be served again, and begin provisioning a replacement certificate. Because the signal is unauthenticated, it MUST NOT cause the authenticating party to stop presenting a tick that is still within the default acceptance window. Treating a forged 410 as grounds to withdraw a healthy certificate would hand anyone able to inject a response a remote kill switch, which is a worse position than simply continuing to serve the valid tick it already holds.¶
An authenticating party that holds a certificate from another CA SHOULD fail over to it before its newest tick leaves the default acceptance window (Section 13.1), which restores service whichever of the two causes applies. Continuing to present a certificate whose newest tick has already fallen outside that window achieves nothing: every relying party implementing this mechanism rejects it (step 4 of Section 9.2).¶
The authenticating party may be unable to obtain a fresh tick, for example because the CA is unavailable. It then continues to serve the most recent tick it holds for as long as that tick remains within the default acceptance window (step 4 of Section 9.2). That gives it between one and two periods of runway from its last successful fetch (Section 13.1). Once that runway is exhausted, the certificate becomes unusable until a fresh tick is obtained or a new certificate is provisioned. Section 13.1 discusses this dependency and its mitigations, including widening the acceptance window (Section 16.10) and holding certificates from multiple CAs.¶
A deployment that terminates TLS on many nodes must get each fresh tick to every node that presents the certificate. This document does not prescribe how. Either arrangement works: every node MAY fetch independently, since the per-entry offset already spreads their requests and a cache or CDN collapses them (Section 13.3), or one node MAY fetch and push the result to the rest. The tick is a public, immutable, 34-byte value with no validity window of its own, no signature to check and no per-node state, so nothing is lost by fetching it more than once. A node still holding the preceding period's tick keeps serving correctly while it catches up. This is the point at which OCSP stapling has historically been most difficult to operate. A stapled response is a signed object with its own validity window and responder certificate, and it has to reach every terminator before it goes stale (Appendix E.6.2). A tick has none of those properties, which is why this document leaves the choice to the deployment rather than specifying a distribution mechanism for it.¶
This section covers the operational characteristics of the mechanism that are not security properties in themselves: the availability dependency introduced by periodic tick refresh, what running it costs a CA, how tick distribution is scaled and delegated, and what it costs a relying party to verify. None of it changes the wire protocol, which Section 10 defines in full, and none of it is visible to relying parties, which never fetch (Section 16.5). The subsections run in the order the work does, from the CA that generates hash chains, through the interface that distributes them, to the relying party that verifies a tick in a handshake.¶
An authenticating party must fetch a fresh tick at least once per tick_interval (Section 10).
A tick fetched for period t remains acceptable until the end of period t+1, because a relying party also accepts the immediately preceding period's tick (step 4 of Section 9.2).
A single successful fetch therefore provides between one and two periods of runway, depending on how far into period t it landed.
An outage that outlasts that runway renders the affected certificate unusable until a fresh tick is obtained.
This is an availability dependency that the base MTC short-lived-certificate model does not have, and deployments SHOULD plan for it.
It is intrinsic to enforceable revocation rather than a defect.
A mechanism that let a server keep presenting a usable certificate regardless of CA state would, by construction, fail open, which is the soft-fail behavior this design rejects (Appendix E.6.2).
The goal is therefore to bound the dependency, not to eliminate it.
Several factors and mitigations limit its impact:¶
The tick interval is the outage-tolerance budget.
The runway above is measured in periods, so tick_interval sets its absolute length: hours for a one-hour period, days for a one-day one, at the cost of proportionally delayed revocation enforcement.
Deployments choose tick_interval to balance revocation latency against their realistic availability expectations for tick distribution.¶
The dependency is on a lightweight service. Fetching a tick is a single lightweight HTTP GET with no per-request cryptography. It is far less fragile than ACME issuance or an OCSP responder, and simpler to operate and more resilient than the latter (Appendix E.6.3). Because the authenticating party keeps serving through the runway above, brief outages are invisible to relying parties.¶
The fetch need not leave the deployment. The dependency is on reaching some distributor, not on reaching the CA. This matters where servers have no outbound connectivity at all, since under base MTC such a server can be handed a certificate by an out-of-band process and reach nothing for the rest of that certificate's life, whereas this mechanism needs a tick each period. An operator in that position runs a distributor itself (Section 13.5), or has one connected node fetch and push ticks to the others exactly as it already pushes certificates (Section 12). Either restores the original property, because a tick verifies against the committed anchor wherever it was obtained (Section 9).¶
The acceptance window can be widened, deliberately. A relying party MAY accept ticks from further preceding periods, converting a tick-distribution outage longer than one period into bounded additional revocation latency rather than a hard failure (Section 16.10). This is a relying-party (or root-program) policy, not something a server can switch on, and it applies to every certificate that relying party validates, so it loosens revocation freshness ecosystem-wide. It is therefore a conscious fallback for known-poor availability, not a default. It remains hard-fail once the widened window elapses: a bounded extension of acceptable staleness, not a fail-open. The multi-CA approach below is preferable wherever it is available, because it restores availability without accepting any additional staleness.¶
Multiple independent CAs remove the single point of failure. Authenticating parties SHOULD obtain Merkle Tree Certificates from multiple independent CAs, so that if one CA's tick distribution becomes unavailable they can immediately present a certificate from another whose ticks remain current. Failover needs no new protocol. The tick is embedded in the MTCProof rather than negotiated as a separate stapled response. A server holding certificates from several CAs therefore simply presents, in each handshake, one for which it currently holds a fresh tick and whose trust anchor the relying party supports, using the base MTC certificate-selection mechanism (Section 8 of [I-D.ietf-plants-merkle-tree-certs]). It is driven by a background tick refresh, not by a handshake-time refetch or a new failover exchange. Its preconditions are that the relying party support the alternate CA's trust anchor, and that the two CAs fail independently, which is not automatic (see below). Because Merkle Tree Certificates are lightweight to obtain and maintain, the incremental cost of holding certificates from two or three CAs is modest relative to the resilience gained.¶
The last of these mitigations rests on an assumption. Ticks are safe to delegate because they are self-authenticating, so a CA is encouraged to serve them from mirrors, content delivery networks, or other distributors (Section 13.5). Relatively few operators run distribution infrastructure at that scale, so two CAs may delegate to the same one, and their tick availability is then perfectly correlated. Holding certificates from both buys nothing in that case, and the concentration makes the failure large as well as correlated, since a single distributor's outage renders every affected certificate unusable once its runway expires, whichever CA issued it. A deployment relying on multi-CA failover SHOULD therefore confirm that its CAs do not share a tick distributor, and CAs SHOULD publish enough about their distribution arrangements for that to be checkable. Delegation and multi-CA operation are both worth doing, but they are not independent of one another, and a deployment that treats them as independent overestimates its resilience.¶
Compared with relying on short lifetimes alone, this is a shift in the availability dependency rather than a new one, and the shift is smaller than it first appears. Short-lived certificates do not remove the dependency on CA availability. They relocate it. Such a certificate depends on the CA's issuance pipeline being reachable each time it must renew, and one due to renew during an issuance outage expires just as an MTCRS certificate does when a tick outage outlasts its runway.¶
The difference is cadence and weight.
MTCRS moves the dependency onto a static, cacheable, CDN- and anycast-friendly GET with no cryptography (Appendix E.6.3).
That is far easier to keep at very high availability than the ACME issuance, validation, signing, logging, and CT path a short-lived certificate depends on.
Multi-CA operation removes even that as a single point of failure, so a single CA's tick outage need not break any certificate globally.
A longer tick_interval trades the runway back toward a short-lived certificate's issuance cadence while still permitting the in-life revocation that passive expiry cannot.¶
The alternative, no in-band revocation at all, instead makes the ecosystem depend entirely on external revocation systems whose availability the CA does not control.¶
A CA has two largely independent implementation choices for each certificate's hash chain of length hash_chain_length (denoted L below): how to produce each period's revealed value, and where the per-certificate seed comes from.
Both are CA-side only, and the on-the-wire tick and the relying party's verification procedure (Section 9) are unchanged.¶
Neither naive extreme is attractive at scale.
Storing each hash chain in full costs O(L) per certificate, roughly 35 KiB at L = 1128 (a 47-day lifetime with a one-hour period), or some 36 TB across 109 certificates.
Storing only the seed costs O(1) but recomputes each revealed value from scratch, up to L hash evaluations, which is O(L2) hashing over the certificate's lifetime.¶
A CA MAY instead use fractal hash chain traversal [FRACTAL] [ALMOST-OPTIMAL], which addresses exactly this setting: a hash chain computed forward from a secret seed and revealed in reverse (Section 5.1).
It keeps about log2(L) precomputed values per certificate, parked at self-similar positions along the hash chain, and spends about half that many hash evaluations per period advancing the more distant of them toward where they will next be needed.
At L = 1128 that is some 350 bytes per certificate, about 340 GB across 109, and about 5 hash evaluations per revealed value.
It dominates a square-root checkpoint scheme, which would need about 1.1 TB and up to 34 hashes per value, on both axes, and turns the seed-only extreme's O(L2) lifetime cost into O(L log L).¶
The hashing is not what binds at that scale. Advancing every certificate's traversal once per period is on the order of 105 read-modify-write operations per second against those 340 GB, spread through the interval as certificates reach their own period boundaries (Section 4). Sizing that state store, rather than the hashing, is the CA-side engineering problem, and a hierarchical chain would remove it entirely, since any value becomes recomputable from the seed and no per-certificate state is kept (Appendix F.6). The stored values are unrevealed hash chain values and therefore carry the same confidentiality requirement as the seed (Section 16.3).¶
The per-certificate seed itself can also be eliminated from storage.
Instead of generating and storing an independent random seed per certificate, a CA MAY derive each seed from a single long-term CA secret with a keyed KDF or PRF, for example h[0] = HMAC-SHA256(ca_seed, label || issuer_ca_id || entry_id), where entry_id is whatever value the CA uses to distinguish one entry from another.
That value never appears on the wire and this document does not constrain it, but it MUST differ between entries, since two entries deriving the same seed would hold the same hash chain and could not be revoked independently.
HashChainInput carries no per-entry salt (Section 6), so the seed is the only thing separating one certificate's chain from another's.
A CA choosing entry_id should therefore prefer a value it can fix before the entry is sequenced into the log, such as a hash of the certificate inputs, rather than the entry's index.
The keying is what makes derived seeds computationally indistinguishable from the independent random seeds of Section 4, which is why a raw Hash(ca_seed || ...) is forbidden (Section 16.3).
Any hash chain is then recomputable on demand from ca_seed and the public entry identity, giving O(1) secret storage for the entire CA and stateless, reconstructible issuance, with no change visible to verifiers.¶
The cost is concentration.
Compromise of ca_seed exposes every certificate's hash chain, past, present, and future, so it demands the custody of an issuance signing key (Section 16.3).
Being a single small key, it is nonetheless better suited to that custody than a bulk per-certificate seed store.
Per-log or per-epoch sub-seeds bound the blast radius and can be retired as their logs expire, and they are the finest granularity available, because a certificate's entire hash chain derives from the seed fixed at its issuance rather than from anything per-period.
As with any seed compromise, rotation protects only certificates issued afterward, and already-committed anchors still require the revoked-ranges fallback (Section 16.3).¶
Because a relying party also accepts a tick for the immediately preceding period (Section 9), an authenticating party has up to one full tick_interval of slack in which to fetch each new tick and need not fetch at the period boundary.¶
Period boundaries are each certificate's own, counted from its notBefore (Section 4), so they are staggered to whatever extent issuance times are.
They coincide only where notBefore values coincide, which is common in practice: CAs frequently round notBefore, and automated renewal tends to arrive in waves.
A thundering herd is therefore a consequence of clustered issuance rather than of the period schedule itself, and the two mechanisms below exploit the slack above to blunt it in either case.¶
Rather than fetching at the start of each period, an authenticating party SHOULD fetch at a fixed offset into the first half of the period, derived deterministically from its own certificate's serialNumber:¶
offset = serial_number mod max(1, tick_interval / 2)¶
where the division is integer division.
The authenticating party reads the serial from its own certificate, so the offset is available even when the tick URL is addressed by an unguessable token (Section 10.3) rather than by the serial.
The authenticating party fetches the current period's tick at period_start + offset, where period_start is the start time of that period.
During the first offset seconds of the period it continues to serve the preceding period's tick.¶
The serving delay and a verifier whose clock runs ahead both draw on the same one-period preceding-tick grace (Section 16.10).
A verifier whose clock is ahead by more than (tick_interval - offset) already expects the following period and rejects a tick two periods behind its expectation.
Bounding the offset to half of tick_interval leaves at least half a period of that grace available to absorb verifier clock skew, while still spreading fetches across a wide window.¶
Because the index component of the serial is assigned sequentially, certificates issued in a burst take consecutive offsets and so spread evenly across the window by construction, which is exactly the clustered-issuance case that produces a thundering herd. The offset needs no coordination, shared state, or central scheduler, and is stable from period to period, which aids caching and diagnosis. This is preferable to independent random jitter, which can still cluster and which varies each period.¶
The CA (or an edge cache) SHOULD serve each tick with a Cache-Control max-age no longer than tick_interval seconds (Section 10.4), so that a caching layer can collapse repeated requests for the same entry into a single origin fetch per period.
This helps an entry that has several fetchers, as in a multi-node deployment (Section 12), and does nothing for an entry that has one.
A CA MAY additionally apply a small per-response jitter to max-age so that cache entries for different entries do not all expire simultaneously.¶
Under transient overload, the CA or edge MAY respond with HTTP status code 429 (Too Many Requests) or 503 (Service Unavailable) together with a Retry-After header indicating when the authenticating party should retry. To avoid a synchronized second wave, the CA SHOULD randomize Retry-After values across clients rather than returning a single fixed value. Because the authenticating party retains its previously fetched tick, which remains valid until the end of the current period, backing off in response to Retry-After does not interrupt service, provided a fresh tick is obtained before the previous one expires.¶
The deterministic per-entry offset above and edge caching together flatten period-boundary load. Neither reduces the number of distinct entries an origin must answer for in a period, which is what delegating the serving path addresses (Section 13.5). This document specifies both as SHOULD rather than MUST, because fetch timing is not observable to relying parties and affects neither interoperability nor the security of verification. A specific load-shaping or availability target is left to root-program or CA operational policy.¶
The HTTP interface (Section 10) addresses one tick per request, so an operator fronting many certificates, such as a large hosting provider or CDN, issues on the order of N fetches per period for N certificates. The per-entry offset (Section 13.3) spreads them across the period but does not reduce their number. This stays inexpensive. Each tick is a 34-byte value that is immutable within its period and cacheable (Section 10.4), and HTTP/2 and HTTP/3 multiplex many such small requests over a few persistent connections, so N fetches is not N connections or N round-trip stalls. An operator that prefers fewer requests can front its certificates with its own cache, or act as a delegated distributor (Section 13.5). The CA-to-distributor bundle is exactly a bulk transfer of the currently-revealed ticks, so taking that feed obtains all of them in one exchange.¶
A CA MAY additionally offer a batch endpoint keyed by a list of serial_number values, or of tokens (Section 10.3).
The trade-off is cacheability.
A batch response is specific to the set requested, and so is far less cacheable by generic HTTP intermediaries than the per-entry GETs.
It therefore suits an operator fetching from the CA or a mirror it controls rather than from a shared edge cache.
This document standardizes neither a batch wire format nor the CA-to-distributor bundle (Section 13.5).
Both are bilateral agreements, layered on the single-tick interface rather than replacing it, since that interface is mandatory at both ends and is what guarantees interoperability (Section 10).
An operator large enough to find per-entry fetches unattractive is therefore choosing between a private batch arrangement with each of its CAs and becoming a distributor for each, and neither is interoperable today.
Specifying the bundle would be the more useful of the two, since a single format would serve delegated distribution and bulk retrieval alike, and this document leaves that to the working group.
It does not affect relying parties, who never fetch (Section 16.5).¶
Because a tick is self-authenticating (Section 9), the party that serves ticks need not be trusted for integrity. A distributor cannot forge a tick for a period the CA has not revealed, by preimage resistance (Section 16.1), nor serve a tampered value that verifies. Distribution is therefore safe to delegate to third parties, which serve only public values and hold no seed and no signing key.¶
The CA publishes to its authorized distributors the value currently revealed for each entry, as a bundle keyed by serial_number, refreshing it as certificates advance through their own periods (Section 4).
A CA that uses unguessable tick URLs (Section 10.3) keys the bundle by tick_token instead, so that its key matches what its distributors are asked for.
Each distributor serves those values through the HTTP interface of Section 10.
This is what makes the aggregate request volume tractable, because a distributor answers from the bundle it already holds and no per-certificate request need reach the CA (Section 10).
The bundle is small in relation to that volume.
One serial-keyed record is a serial and a tick, 42 bytes, so a CA with 109 active certificates publishes about 42 GB per period.
Because period boundaries are each certificate's own (Section 4), that need not be sent as a periodic bulk transfer.
A CA can stream records as certificates cross their boundaries, which at hourly periods is a sustained rate of roughly 90 Mbit/s to each distributor.
To revoke a certificate the CA drops its entry from subsequent refreshes, so absence is revocation and no revocation list is exchanged.
Compromising a distributor exposes nothing that is not already public.¶
MTC mirrors are a natural home for this role, since they already replicate and serve MTC log data at high availability. Content delivery networks and relying-party-side operators can serve on the same terms, including browser providers, which already run large-scale revocation-distribution infrastructure. Because none of them is trusted for integrity, a CA MAY spread distribution across anycast, several independent CDNs, or several distributors concurrently with no added trust, removing its own origin as a single point of failure. How much redundancy to provide is a matter for root-program or CA policy rather than an interoperability requirement of this document. Running such a service is distinct from the prohibition in Section 16.5, which forbids a relying party from using the endpoint as its own online responder during validation. A relying-party-side organization may perfectly well operate one that authenticating parties fetch from.¶
What is delegated is distribution, not revocation authority. The CA retains the seed and the unrevealed hash chain values (Section 16.3), so it alone decides what to reveal. A distributor can at most withhold or delay the values it was given (Section 16.4), which is an availability fault mitigated by redundancy, not a way to un-revoke a certificate. The only trust placed in any distributor is for availability.¶
A CA MAY publish only already-revealed values, in which case each distributor depends on it for every refresh and it retains sole, immediate control of revocation. Alternatively, as disaster-recovery planning, a CA MAY pre-provision a distributor with a small buffer of future periods' values so that certificates stay usable through a CA-side outage. That is a delegation of liveness, and it costs exactly what it buys. A certificate cannot be revoked through a distributor that already holds its future values, so the buffer length caps revocation latency through that channel, leaving only the base MTC revoked-ranges fallback (Section 16.7) during the window. Buffered values are as sensitive as the CA's own unrevealed ones (Section 16.3), since compromising the distributor keeps a revoked certificate alive for the remainder of the buffer. A CA SHOULD therefore keep the buffer short, sized to its outage-tolerance against revocation-latency budget, and SHOULD pre-provision only distributors trusted to stop serving on instruction.¶
The buffer is compact and inherently bounded.
For N periods the CA sends one value per certificate, the value that will be revealed N periods ahead, from which the distributor derives every intervening period by hashing forward (Section 5.1).
It confers no power beyond period t+N, since any later period would require inverting the hash.¶
A CA MUST NOT instead share the seed-derivation secret (Section 13.2.2), which would grant the unbounded ability to forge non-revocation for the entire certificate population, and MUST NOT hand that secret or per-certificate seeds to a successor operator even in disaster recovery. It is as sensitive as the issuance signing key, so transferring it is a root-key-custody event that destroys forward security. Sharing it is also unnecessary. The bounded buffer keeps issued certificates usable through the outage, and because Merkle Tree Certificates are short-lived (Section 10.4 of [I-D.ietf-plants-merkle-tree-certs]) the failing CA's population ages out while subscribers migrate to a successor issuing under its own key and seed. If the disaster is itself a seed compromise, the response is the revoked-ranges fallback, not wider custody of a tainted secret.¶
The feed from CA to distributor SHOULD be authenticated and integrity-protected. This is not required for relying-party security, which rests on self-authentication and the authenticating party's pre-installation check (Section 9), but it prevents a distributor being fed corrupt bundles that would cause authenticating parties to reject ticks and refetch.¶
A relying party verifies a tick by hashing tick.value forward tick.period times (Section 9), so the cost grows with the certificate's age: near the end of a 47-day certificate with a one-hour period it computes up to 1,127 hashes.
Two properties of that loop set its cost.
Each step hashes a HashChainInput of 41 bytes plus the length of the issuing CA's ID (Section 6), 45 bytes for the 4-byte ID of the test vectors.
Any ID of up to 14 bytes keeps that within a single SHA-256 compression block, so the work is one block per elapsed period.
The steps cannot be batched or pipelined, because each input is the preceding output, so every step also pays the hash function's initialization and finalization.
Trust anchor IDs may be as long as 32 bytes (Section 4 of [I-D.ietf-tls-trust-anchor-ids]), and one longer than 14 bytes adds a second block to every step.
On a core with SHA-256 instructions that is a small fraction of the per-step cost below, which initialization and finalization dominate, but in software it roughly doubles it.¶
On a current x86-64 core with SHA-256 instructions, 1,127 steps measure approximately 300 microseconds, or about 270 nanoseconds per step. That is the same order as the handshake's asymmetric cryptography rather than negligible beside it. On the same core it is roughly five times the cost of one ECDSA P-256 signature verification. The figure is small in absolute terms and is paid only on full handshakes, but a deployment budgeting for it should treat it as equivalent to a few additional signature checks rather than as free.¶
The cost is proportional to hash_chain_length, which is ceil(lifetime / tick_interval) (Section 4), so a shorter certificate lifetime or a longer tick_interval reduces it directly.
It is also worst at the end of a certificate's life and near zero just after issuance, averaging half the maximum.¶
Constrained relying parties are where this cost is significant.
A software SHA-256 on a 32-bit microcontroller without a hash accelerator costs on the order of a thousand cycles per block, which puts 1,127 steps in the tens of milliseconds at typical clock rates.
A hardware SHA engine lowers the per-block cost but not the per-call overhead, which dominates for single-block messages.
Such a verifier can compute hash_chain_length from the certificate and reject before hashing anything if it exceeds a configured ceiling (step 5 of Section 9.2, Section 16.11), and a deployment that controls its own CA can choose parameters that suit it.
Neither remedy helps a constrained client validating an arbitrary server's certificate, where the issuing CA chooses both the lifetime and tick_interval and the client can only bear the cost or reject the certificate.¶
The worst case any certificate can impose is the 65,535 forward hashes the 16-bit period field permits (Section 4). That measures approximately 18 milliseconds on the general-purpose core above and is of the order of a second on a constrained one, which is why a relying party that cannot afford it rejects such a certificate before hashing (step 5 of Section 9.2).¶
Across the many connections a page load opens, the total stays bounded.
Most connections pay nothing.
A resumed session carries no Certificate message and verifies no tick (Section 13.7), and connection coalescing (HTTP/2 and HTTP/3) collapses many same-origin assets onto one connection.
The cost is incurred per full handshake rather than per request, so it does not grow with page complexity.
A relying party that revalidates the same (entry, period), for example a recurring third-party CDN origin, MAY cache the verified result and skip the forward hashing on repeat.
One that has retained a verified tick for an earlier period of the same entry can do better still, verifying the current tick by hashing it forward only the difference between the two periods.
It rests on the same reason, and offers the same security, as the authenticating party's incremental check (Section 12).
As there, the shortcut applies only when the presented period exceeds the retained one, and a relying party MUST NOT compute the difference in unsigned arithmetic.
The opposite case arises without an attacker, since the acceptance window admits expected_period - 1 while the relying party may hold a tick for expected_period itself.
A presented period below the retained one is therefore ordinary, and the relying party verifies from the anchor instead.
On a battery-powered sensor or wearable one verification costs a fraction of a millijoule, comparable to the asymmetric operations the same handshake performs.¶
Selecting a different hash function does not materially change any of this. The cost is one compression block per elapsed period whatever the primitive, and this mechanism inherits HASH from the issuing CA (Section 4) rather than choosing it. That inheritance is what keeps an algorithm identifier out of the anchor and the tick (Section 2). Primitives faster than SHA-256 in software on 32-bit cores do exist, but they offer a small constant factor, are less likely to be hardware-accelerated, and would diverge from the hash the surrounding ecosystem already uses. The quantity that governs this cost is the number of periods, not the speed of the primitive, and reducing it is a property of the construction (Appendix F.6).¶
A relying party checks the non-revocation proof (Section 9) only when it validates the certificate, which happens during a full TLS handshake.
The following TLS behaviors mean this check does not recur for the life of a connection or a resumed session.
The effective client-side revocation latency is therefore bounded not by tick_interval alone but by how long a client keeps or resumes a connection:¶
Once a full handshake completes, the certificate, and hence the tick, is not re-evaluated for the lifetime of that connection. A long-lived connection (HTTP keep-alive, HTTP/2, or HTTP/3) may continue to use a certificate that has since been revoked until the connection closes.¶
A resumed TLS session carries no Certificate message: the server's authentication is derived from the original full handshake and is not re-validated, so no tick is presented and none is checked. A client may therefore resume without re-checking revocation for as long as its session tickets remain usable. TLS 1.3 caps a ticket's lifetime at seven days (Section 4.7.1 of [RFC9846]), and implementations commonly use shorter, configurable limits, but within that window resumption bypasses tick verification. This covers 0-RTT early data (Section 2.3 of [RFC9846]), which travels on a pre-shared key established by an earlier full handshake and so presents no certificate either. A client sends it before the resumed handshake completes, so it is the case in which application data moves furthest ahead of the last tick that was checked.¶
TLS 1.3 removed renegotiation, and browsers have disabled or restricted TLS 1.2 renegotiation, so renegotiation cannot be relied upon to re-present a fresh tick. There is likewise no mechanism for a server to push an updated certificate or tick mid-connection.¶
The effective latency before a revocation takes effect at a given client is therefore approximately the maximum of tick_interval, the remaining lifetime of any established connection, and the client's session-resumption window.
A deployment that wants revocation to take effect within about one tick_interval SHOULD bound the session-ticket lifetime, and where practical the lifetime of long-lived connections, to a value near tick_interval.
That forces a fresh full handshake, and thus a fresh tick, within that period.
On the relying-party side this is a policy choice (Section 16.11).
A client MAY cap how long it reuses session tickets and force a periodic full handshake so that the tick is re-checked, independently of the server's ticket-lifetime setting.¶
This limitation is not specific to this mechanism.
Every handshake-time revocation mechanism (OCSP [RFC6960], CRLite [CRLite], CRLSets [CRLSets]) is likewise consulted only when the certificate is validated, and the base MTC short-lived-certificate model has the same property.
A revoked-but-unexpired certificate is equally accepted on a resumed session.
Relative to passive expiry, this mechanism still improves matters, because every full handshake re-checks a per-period non-revocation proof rather than trusting a static notAfter.¶
This section records the status of known implementations of the mechanism defined by this specification at the time of posting of this Internet-Draft, following [RFC7942]. It is requested that the RFC Editor remove this section before publication.¶
There are no known implementations at the time of writing. Section 3.1 summarizes what implementing the mechanism involves for each party. The author intends to produce a reference implementation covering hash chain generation (Section 4), tick distribution (Section 10), and relying-party verification (Section 9), and to report interoperability results to the working group. The test vectors of Appendix B are given so that independent implementations can check their HashChainInput encoding and hashing order against a fixed example before any interoperable deployment exists.¶
The Privacy Considerations of the base MTC specification (Section 11 of [I-D.ietf-plants-merkle-tree-certs]) apply to Merkle Tree Certificates that use this mechanism. This mechanism adds one network interaction, the authenticating party's periodic tick fetch (Section 10), and deliberately adds none on the relying-party side.¶
No relying-party activity is exposed. The current tick is embedded in the certificate presentation and verified offline against the committed anchor, and relying parties never fetch ticks (Section 16.5). Consequently the CA learns nothing about which certificates a relying party validates or which sites it visits. This is the central privacy difference from client-driven OCSP [RFC6960], whose status fetches revealed relying-party browsing to the CA. That failure mode is avoided here by construction rather than by policy.¶
The authenticating party's tick fetch, by contrast, exposes request metadata.
An on-path observer of a tick fetch, or the CA (or CDN) serving it, sees which serial_number is being requested, or with unguessable tick URLs which tick_token (Section 10.3).
It can thereby learn which certificate the authenticating party holds, and from the source address of the request, where that certificate is deployed.
The request identifies the authenticating party's own certificate rather than any relying party, so it discloses no relying-party activity.
It is nonetheless a disclosure this mechanism introduces, in two respects.¶
A certificate is public, but its deployment need not be. Publicly issued certificates are routinely used on hosts that are not reachable or enumerable from the public Internet, among them internal services, management interfaces, and development systems. Such a host contacts its CA at issuance and at renewal today. Under this mechanism it contacts the CA, or whichever distributor serves its ticks, once per period for the life of the certificate, and the source address of that request is not derivable from the certificate.¶
The cadence is itself a signal. A per-certificate fetch once per period reports which certificates are in active use, and when a deployment starts, stops, or is interrupted. Issuance alone does not convey that.¶
Both follow from the fetch existing rather than from how it is carried, so transport security does not address them. An https base URL protects the request against on-path observers and leaves it fully visible to the party serving it. The measure that does apply is delegation. Because ticks are self-authenticating, a deployment need not fetch from the CA at all (Section 13.5), and may fetch instead from a distributor it selects or operates, so that no per-fetch record reaches the issuer. A CA or distributor SHOULD NOT retain per-fetch records beyond what operating the service requires, since a record that is not kept can be neither disclosed nor compelled (Appendix E.6.5).¶
Two further points concern the transport and the token:¶
Because a tick is self-authenticating and public, the fetch does not require transport-layer confidentiality for correctness, so a CA MAY serve ticks over plain HTTP (Section 10).
Plain HTTP leaves the requested serial_number or tick_token visible to on-path observers.
A CA whose subscribers deploy certificates that are not otherwise publicly enumerable SHOULD publish an https base URL instead.¶
Unguessable tick URLs (Section 10.3) are an addressing and access-control measure, not a confidentiality one: the tick_token appears in the request URL, so it offers no confidentiality against an observer of the authenticating party's own fetch.
Its privacy benefit is solely that a relying party, or a third party holding only the certificate, cannot derive the URL and probe the CA for the certificate's status (Section 16.5).¶
Pushed revocation lists such as [CRLite] and [CRLSets] are checked by the client entirely offline. This mechanism preserves the same relying-party privacy, since the client fetches nothing either way, and does so universally, for any TLS client rather than only browsers that ship the list. Its one difference is the authenticating party's own fetch, and that metadata concerns the server's own public certificate (whose liveness a public server already exposes by answering connections), not any relying party. It also need not reach the CA at all. Because ticks are self-authenticating, distribution can be delegated to CDNs, mirrors, or other distributors (Section 13.5). The CA then publishes a bundle of currently-revealed values and answers no per-certificate request, so it observes no per-server fetch pattern (Section 10).¶
The security of this mechanism depends on the preimage resistance of the hash function used.
SHA-256 [SHS] provides 256 bits of preimage resistance, which is sufficient for all foreseeable certificate lifetimes.
With a one-hour tick_interval and a 47-day lifetime, the hash chain length is 1,128, which does not meaningfully degrade the security margin.¶
This mechanism is post-quantum robust as specified and needs no migration to a new primitive. Its security rests solely on the preimage resistance of the hash function, for which the best known quantum attack is Grover's algorithm, a quadratic speed-up. Against SHA-256 that leaves work on the order of 2128 for a single target. Because the hash chain is salted per CA rather than per certificate (Section 6), an attacker content to invert any one of a CA's chains can search them together, which at 109 certificates costs about 2226 classically and 2113 quantumly. Both remain ample margins for all foreseeable certificate lifetimes, and they are the figures the rest of this document refers to. The non-revocation proof relies on no collision resistance, because a revealed value is bound to a specific hash chain by the committed anchor rather than by any collision property. The weaker quantum bounds on collision finding therefore do not apply, and no second hash appears anywhere in the mechanism for them to apply to. It also inherits whatever hash the CA's issuance log uses (Section 4), so a CA that moves to a larger or post-quantum-oriented hash carries this mechanism along with no change here.¶
Just as importantly, this mechanism keeps post-quantum signatures off the per-period revocation path. A signed-status approach, using OCSP-like per-certificate signatures (Appendix F.11), would require a post-quantum signature per certificate per period (for example an ML-DSA-65 signature of roughly 3,309 bytes), whereas a tick is 34 bytes of hash output and is never signed. Adopting hash chain revocation is therefore aligned with a post-quantum transition rather than a distraction from it: it removes post-quantum signing from the revocation path instead of adding it.¶
The CA MUST keep the hash chain seed (h[0]) and all not-yet-revealed hash chain values confidential. Compromise of these values would allow an attacker to produce future ticks, defeating revocation.¶
A CA that derives per-certificate seeds from a single long-term secret (Section 13.2.2) concentrates this requirement into that secret.
It MUST then be protected at least as strongly as the issuance signing key, and per-log or per-epoch sub-seed derivation SHOULD be used to bound the impact of a compromise.
Such derivation MUST use a keyed KDF or PRF, for example HKDF or HMAC.
A raw Hash(ca_seed || ...) construction MUST NOT be used, as it invites length-extension and MAC-misuse and can make derived seeds distinguishable from independent random seeds (Section 13.2.2).¶
If the CA's seed storage is compromised, the CA MUST revoke all affected certificates via the base MTC revocation mechanism (revoked ranges of serial numbers) as a fallback, as described in Section 16.7.¶
A compromised or malicious CA could withhold ticks from a legitimate authenticating party, either broadly or targeted at a specific subscriber. In the targeted case it revokes that certificate with no auditable signal. This is analogous to a CA refusing to issue OCSP responses, or refusing to issue or renew certificates at all. It is inherent in the CA trust model rather than novel to this mechanism, and it is mitigated by the same forces that discipline CA behavior today:¶
The authenticating party knows it did not receive a tick, and can raise an alarm, switch to another CA, or fall back to a traditionally-signed certificate.¶
Where tick URLs are derivable (Section 10), the endpoint can be monitored externally (Certificate Transparency-style auditing [RFC9162]), which makes broad withholding observable. Two limits apply. A CA using unguessable tick URLs (Section 10.3) cannot be monitored this way at all, a monitor being exactly the third party that scheme prevents from deriving the URL. And nothing obliges the endpoint to answer every requester alike, so a CA willing to discriminate by source address can serve monitors the current tick while withholding it from the subscriber. External observation therefore disciplines broad or careless withholding rather than a determined and targeted one, against which the subscriber's own detection above is the dependable signal. Supplying an artifact a third party could rely on instead is the subject of Section 16.8.1.¶
The interface is mandatory at both ends and addressed identically at every CA (Section 10), so an authenticating party is not tied to one CA's tick service. Deployments are in any case encouraged to hold certificates from several CAs (Section 13.1), so a party facing withheld ticks fails over to another CA's certificate at once, rather than waiting on a commercial response.¶
Because ticks are small and cacheable, they are readily distributed via CDN, which further reduces the attack surface for withholding.¶
The converse exposure is load rather than withholding. The endpoint is public and unauthenticated, and in the derivable-URL scheme its addresses can be computed by anyone holding a certificate or reading the log (Section 16.5). A third party can therefore fetch any entry's tick, and can do so in volume. This is the ordinary position of a public static HTTP endpoint, and it is met by the ordinary means: caching, rate limiting, and CDN or anycast capacity (Section 13.3). These are effective here because every response is an immutable, precomputed per-period value that costs the origin nothing to produce (Appendix E.6.3). Unguessable tick URLs (Section 10.3) additionally deny an attacker the ability to address entries it has not seen. What such flooding cannot do is defeat revocation: it neither forges a tick nor suppresses one that an authenticating party has already fetched and installed.¶
The current tick is embedded in the MTCProof presented during the handshake and is verified offline against the anchor committed in the Merkle Tree (Section 9). A relying party therefore has no need to contact the CA, and MUST NOT fetch ticks or otherwise use the tick distribution endpoint as an online revocation responder.¶
This is a privacy and availability protection, not a secrecy one.
The tick distribution URL is not secret.
The fetch path is .well-known/mtcrs/v1/{hash_name}/tick/{serial_number} with {serial_number} read straight from the certificate by anyone holding it, and the origin is low-entropy and, when the CA certificate SIA (Section 10.2) is used, available to relying parties as well.
By default the design does not, and cannot, technically prevent a relying party from constructing the URL and fetching.
It declines to standardize or advertise such a fetch as an affordance to relying parties.¶
A CA that wishes to remove this derivability entirely MAY use the optional per-certificate capability-token hardening in Section 10.3, which makes the tick URL unguessable to a party holding only the certificate. A relying-party fetch would gain nothing over the embedded tick, since the authenticating party already presents the current value. It would meanwhile reintroduce the CA-visibility of relying-party activity (the CA learning which sites a relying party visits), the added latency, and the soft-fail behavior that made client-driven OCSP [RFC6960] problematic. That visibility is the exposure CAs cited when they began retiring OCSP altogether (Appendix E.6.5).¶
The CA certificate SIA access method (Section 10.2) exists to convey the base URL to authenticating-party tooling. Relying parties possess the CA certificate but MUST NOT use its tick base URL to fetch tick status.¶
The tick travels in the MTCProof's signatures field (Section 8.1.1), which, like the rest of the MTCProof, is neither committed to the Merkle Tree nor covered by any signature.
Hash chain revocation does not rely on the tick cosignature's authenticity, because the tick is self-authenticating and its presence is mandated by the committed id-pe-hashChainAnchor extension.
Its place in that field nonetheless needs care in two directions.¶
A relying party that counted a tick cosignature toward its cosigner requirements would be treating public hash output as a cosigner's assurance that a subtree is consistent with that cosigner's view of the log, which it is not. If a real cosigner, or a GREASE one, could hold the tick cosigner ID, the same confusion could arise from the other side. This is why Section 8.1.1 forbids both, and why the ID is drawn from an arc the CA alone controls.¶
Conversely, a party that drops cosignatures a relying party does not need, for example to save bytes, removes the tick with them, and a relying party implementing this mechanism then rejects the certificate (Section 9.2). That is why the tick cosignature is outside whatever selection of cosignatures the authenticating party makes.¶
The field already admits cosignatures that relying parties ignore, including GREASE cosignatures whose signature is an arbitrary byte string (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]), so carrying the tick there adds no region of ignored data that the base MTCProof did not already have.¶
Independently of all this, the MTCProof changes once per period, which constrains how an application may identify a certificate (Section 8.2). Deriving an identifier from anything but the TBSCertificate breaks pinning and fingerprint allow-lists.¶
The hash chain mechanism complements rather than replaces the base MTC revoked ranges mechanism.
Revoked ranges are relying-party configuration: each relying party maintains, per CA, a list of revoked serial-number ranges, seeded from the CA certificate's minSerial and maxSerial and extended from out-of-band sources (Section 7.5 of [I-D.ietf-plants-merkle-tree-certs]).
They are therefore closer in kind to CRLite [CRLite] or CRLSets [CRLSets] than to a logged or signed artifact: pushed state, distributed out of band, effective only for relying parties that receive it.
Nothing about a revoked range is committed to the Merkle Tree.
Revoked ranges provide a fallback for scenarios where the hash chain mechanism is insufficient:¶
Compromise of the CA's hash chain seed storage¶
Bulk revocation of many certificates simultaneously¶
Relying parties that have not yet implemented hash chain verification¶
Relying parties that support both mechanisms SHOULD check both. A certificate is considered revoked if either mechanism indicates revocation. Whether to check the base MTC revoked ranges in addition to the hash chain is a relying-party policy choice (Section 16.11).¶
This composition is one-directional. Other revocation signals may only add grounds for revocation: the base MTC revoked ranges, or external systems such as CRLite [CRLite] or CRLSets [CRLSets]. A relying party MAY consult them and MUST reject if any reports the certificate revoked. A relying party MUST NOT, however, treat a "not revoked" result from any such mechanism as license to accept a certificate whose tick is absent, stale, or fails verification. Doing so would reduce this mechanism's hard-fail to the strippable soft-fail it is designed to prevent (Appendix E.6.2). A missing tick is thus a rejection in its own right.¶
Consulting another mechanism can nonetheless serve two diagnostic purposes when a tick is absent. The first is distinguishing an actual revocation from a mere distribution outage, which a 404 does not (Section 16.8). The second is recovering the revocation reason (keyCompromise, cessationOfOperation, and so on) from a mechanism that does convey one, since a tick's absence carries no reason code (Section 16.8). Neither changes the safe action for an unproven tick, which remains rejection, subject only to the availability levers of Section 13.1.¶
Revocation in this mechanism is the absence of a tick. The CA stops revealing hash chain values (Section 5.1) and the certificate becomes unusable within two periods. There is deliberately no positive, signed, non-repudiable artifact asserting "the CA revoked entry X as of period T." This is a genuine difference from CRLs and OCSP, whose signed responses are such artifacts. The base MTC revoked-ranges mechanism does not supply one either. It is relying-party configuration distributed out of band, not anything committed to the log (Section 16.7). Nor is the absence peculiar to this mechanism. The Web PKI has no transparent, immutable record of revocations answering to what Certificate Transparency provides for issuance, and the measurement work that documents that gap argues for one [REVOCATION-STATUSES]. The transparency here is asymmetric. Opting a certificate into the mechanism is transparent, because the anchor is an extension committed to the Merkle Tree and so visible to monitors (Section 7). The per-period revocation state, by contrast, is neither signed nor committed, so a monitor cannot observe it in the log.¶
Four consequences follow, each bounded:¶
Revocation is not provable, and not always observable. Where tick URLs are derivable, a monitor watching a certificate's tick endpoint can detect that ticks have stopped. It cannot by that alone prove the CA revoked it rather than suffered a distribution outage, because a 404 does not distinguish the two (Section 10.4). Even that much is unavailable under unguessable tick URLs, and a CA willing to answer monitors and subscribers differently can evade it (Section 16.4). The dependable signal is therefore the subscriber's own, and what is missing is an artifact on which a third party could rely (Section 16.8.1).¶
No revocation-event transparency in the log. Because revealed ticks are not committed to the tree, the fact and time of a revocation are not recorded there. Nor do base MTC revoked ranges supply such a record, being relying-party configuration rather than log content (Section 16.7), so no in-band, logged revocation record exists anywhere in MTC. A deployment that requires one uses an external system that provides it, either CRLs or OCSP, which apply to Merkle Tree Certificates unchanged (Section 12.7 of [I-D.ietf-plants-merkle-tree-certs]), or a signed revocation feed published by the CA (see below).¶
No revocation reason codes. A tick's absence carries no reason (keyCompromise, cessationOfOperation, and so on). Conveying a reason is out of scope here. A deployment that needs machine-readable reasons uses the base MTC revoked-ranges mechanism or an external revocation system that carries them.¶
No status after expiry.
Tick publication stops when the certificate expires (Section 5.1), so no status can be obtained for an expired certificate, and because revocation is absence, nothing then distinguishes one that was revoked from one that was not.
CRLs and OCSP can in principle answer past notAfter, which profiles for long-term signature validation depend on.
In practice they commonly do not, since a longitudinal study of over a million revoked certificates observed the status still present in the CRL after the certificate had expired in only 26.5 percent of cases.
It was preserved for more than a week beyond expiry in 2.9 percent [REVOCATION-STATUSES].
This is out of scope for the TLS use case that motivates this mechanism, where an expired certificate is rejected on validity grounds before any tick is examined.
A tick retained while the certificate was valid does remain verifiable indefinitely, since verification is offline hashing against the anchor in the certificate (Section 9).
It is therefore durable, self-authenticating evidence of non-revocation as of its own period, in 34 bytes rather than an archived signed response.
Only the negative cannot be reconstructed later.¶
None of this weakens enforcement, which is what the mechanism exists to provide. A revoked certificate stops verifying at every relying party within the two-period bound (Section 16.10) whether or not any auditable artifact exists, because enforcement depends on the presence of a fresh tick, not on a monitor observing its absence. Auditability, where a deployment needs it, therefore comes from outside both this mechanism and the base revoked-ranges mechanism: from CRLs or OCSP, which apply unchanged (Section 12.7 of [I-D.ietf-plants-merkle-tree-certs]), or from a signed revocation feed the CA publishes. This document neither defines nor requires such a feed. The base specification's own extensibility would, however, admit a record built inside the log rather than beside it, and the following subsection sets out what such a record could and could not establish.¶
MTCLogEntryType is an extensible enum, and the base specification states that future documents MAY define new values for it (Section 5.2.1 of [I-D.ietf-plants-merkle-tree-certs]) by adding to its "MTC Log Entry Types" registry (Section 13.2.1 of [I-D.ietf-plants-merkle-tree-certs]). That registry's policy is Specification Required, so a new entry type needs a specification but no change to the base specification. A revocation record is therefore constructible within the log. This document does not define one. The sketch here exists so that the working group can judge whether it wants one, and where it belongs (Appendix D.7).¶
The construction is a new MTCLogEntryType whose data identifies the revoked entry by its serial number, gives the first period for which ticks will be withheld, and optionally carries a reason code.
The CA appends it to its issuance log, so it is covered by the subtree hash and by the cosignatures over that subtree.
Its evidentiary force comes from a property the base specification already establishes, that a subtree signature is a binding assertion by the CA that it has certified every entry in the subtree (Section 12.4 of [I-D.ietf-plants-merkle-tree-certs]).
Any signed checkpoint containing the entry, together with an inclusion proof, therefore suffices to prove the CA made it.
That is the signed, non-repudiable artifact this section otherwise lacks, and unlike a CRL entry it does not expire ([REVOCATION-STATUSES]).
Relying parties are unaffected, since an unrecognized entry type differs in its type field from the entry a relying party reconstructs, so its proof never matches (Section 12.5 of [I-D.ietf-plants-merkle-tree-certs]).
The record is for monitors, and enforcement continues to rest on the presence of a fresh tick.¶
Such a record would bind the logged status to the served status in one direction, and would do so cryptographically.
A logged revocation naming period t, together with a tick for that entry whose period is t or later, is a contradiction anyone can check offline.
The log entry cannot be repudiated and the tick cannot be forged without inverting the hash (Section 16.1), so the pair is evidence that the CA both declared a revocation and continued to supply the material that defeats it.
Neither artifact requires trusting the monitor that presents it.
One interaction needs care: a CA that has pre-provisioned a distributor with a buffer of future values (Section 13.5) can produce that contradiction without acting again, so a deployment doing both would have to reconcile the logged period with the buffer depth.¶
The converse direction cannot be bought at all, and the reason bounds what any revocation transparency mechanism can promise here. Withholding is the absence of a response, and absence is not attributable. A distribution outage, a failed distributor, a network partition and a deliberate unlogged revocation are indistinguishable to an observer, which is the same reason a 404 does not mean "revoked" (Section 10.4). Certificate Transparency closes the corresponding gap for issuance by conscripting the relying party, since a certificate lacking evidence of logging is rejected and logging is thereby compelled. That remedy is unavailable here, because it requires the relying party to check something it does not receive in the handshake, and this mechanism rests on the relying party fetching nothing (Section 16.5). Pushing the log's contents to clients out of band would restore it only for the clients that receive the push, which is the coverage limitation this document declines to depend on (Appendix F.10).¶
Such a mechanism would therefore be sound rather than complete. Nothing inconsistent can be logged and served without producing a proof, but a CA can still withhold silently. The most a specification can add is an obligation to declare, so that ticks stopping with no corresponding entry becomes a detectable failure to comply rather than an unremarkable event. This is the asymmetry that governs enforcement reappearing in the transparency layer. Presence is provable and absence is not, whether the thing present is a tick or a log entry.¶
Hash chain revocation applies per certificate, not per key. More precisely, it applies per log entry carrying the id-pe-hashChainAnchor extension. An attacker who holds a private key (the only party who can use any certificate for that key) could therefore present a different, still-valid certificate for the same key that does not carry the anchor extension, escaping tick enforcement.¶
This bounds what the mechanism claims. Withholding ticks bounds the exposure of the certificates that carry an anchor, and revokes a key only when every unexpired certificate for that key carries one. The limitation bites where the mechanism is most wanted, since key compromise is the case for which a two-period bound is worth having at all.¶
Certificates without an anchor are outside this mechanism's scope and are handled exactly as in base MTC, by the per-serial revoked-ranges mechanism, which is independent of hash chain revocation and bounded by the short lifetimes and trusted-validity windows of the base model. A CA revoking a compromised key MUST therefore revoke all of that key's certificates, withholding ticks for those that use this mechanism (Section 5.1) and revoking the serial ranges of the rest (Section 16.7). Withholding ticks alone would revoke only the certificates that carry an anchor.¶
That fallback is weaker than the mechanism it stands in for. Revoked ranges are relying-party configuration distributed out of band, so they reach only the relying parties that receive them (Section 16.7), which is the same property this document gives as a reason not to depend on external revocation systems (Appendix F.10). For a key whose certificates do not all carry anchors, the effective guarantee is that of the weakest certificate for that key.¶
Unanchored certificates for a key are most likely to exist during a transition. A deployment presenting a Merkle Tree Certificate to relying parties that support it and a traditionally-signed certificate to those that do not may hold both for one key. Holding certificates from several CAs for resilience (Section 13.1) permits the same across issuers. A deployment that wants key-level revocation SHOULD therefore not share a key between certificates that carry an anchor and certificates that do not. Keeping them on separate keys confines each certificate this mechanism does not cover to a key that is not also used with one it does, so that withholding a certificate's ticks is sufficient to retire it.¶
A domain name holder can also narrow how unanchored certificates for its names come to exist, by constraining issuance through CAA [RFC8659].
An issue parameter meaning that certificates for the name must carry a hash chain anchor would compose with the authorization list already published there, rather than needing a critical property that would forbid issuance by every CA that does not implement it.
Its distinctive contribution is reach, since it binds any CA that honors it, including ones the subscriber never chose to use, which neither the issuance channel of Section 11 nor anything carried in the certificate can do.
It earns that place only where anchoring varies within a CA, since a CA that anchors everything it issues is already described by an ordinary CAA authorization list.
This document does not define such a parameter, which is separable from this mechanism and can be specified on its own.
What it cannot become is enforcement.
Section 1 of [RFC8659] states that relying parties MUST NOT use CAA records as part of certificate validation, because a CAA record set grants authority as of now whereas a certificate issued before the record was published remains what it was.
CAA therefore reaches the CA and never the party that checks the tick, which makes it a defense against an accidental downgrade such as a renewal that quietly moves to a product without an anchor.
It is not a defense against an attacker who holds a key and finds a CA willing to issue.¶
A root program reaches further than either measure, because it both admits the CA and configures the relying party. Requiring that every certificate a member CA issues carries a hash chain anchor empties the population of unanchored certificates, and rejecting an MTC certificate that carries none enforces that requirement at the point of use, which is the step CAA cannot take. The two steps need not arrive together. [CHROME-MTC] already requires every CA operator it admits to provide both standalone and landmark-relative certificates (Section 8.4), while leaving the choice between them to the subscriber's ACME client. That mandates a capability across the ecosystem without mandating its use in any particular certificate. Requiring anchored issuance first and enforcing on it once adoption is sufficient follows the same order, which is the order Appendix E.7 describes.¶
The limitation behind these measures is the general property that revocation targets certificates rather than keys, and it is not specific to this mechanism. It is more visible here only because this mechanism makes the certificates it covers substantially harder to keep alive than the ones it does not.¶
The default acceptance window of verification step 4 accepts a tick whose period is the verifier's expected_period, the immediately preceding period (expected_period - 1), or the immediately following period (expected_period + 1).
This tolerates a verifier clock that is behind or ahead of the authenticating party's by up to one full tick_interval in either direction.
It also tolerates an authenticating party that is still serving the previous period's tick for caching or staggered refresh (Section 13.3).
Figure 7 shows the window.¶
The two directions are not equivalent in cost.
Accepting the immediately following period, which is what a verifier whose clock is behind will see, costs nothing in revocation terms.
That tick is a fresher non-revocation proof than the verifier expected, and a tick for a period the CA has not yet reached cannot be forged, by preimage resistance (Section 16.1).
Accepting the immediately preceding period, which arises from a verifier clock that is ahead or from a deliberately stale tick, accepts a non-revocation proof up to one tick_interval old.
That is the intended one-period grace.¶
Deployments with known clock-skew or availability concerns MAY widen the window, as step 4 permits. Accepting further preceding periods tolerates a tick-distribution outage (Section 13.1) at the cost of correspondingly delayed revocation enforcement, while accepting further following periods tolerates a verifier clock that runs further behind and carries no revocation cost.¶
Widening the preceding side relaxes the revocation bound one-for-one.
A relying party that accepts the k immediately preceding periods keeps a withheld certificate usable for up to k + 1 periods after the CA stops revealing values, in place of the two periods the default window gives (Section 5.1).
The worst-case revocation latency stated elsewhere in this document, at most two periods, therefore assumes the default window.
How quickly revocation takes effect at a given relying party is a property of that relying party's policy, not of the hash chain.
This is why widening is a deliberate, ecosystem-wide trade-off rather than a per-server option (Section 16.11).¶
The acceptance window is anchored to the relying party's own clock, so revocation timeliness depends on that clock's integrity.
A clock running behind true time shifts the window toward the past, so a genuine but stale tick, one the CA revealed before it stopped revealing, can fall inside the window and be accepted.
An attacker who moves a relying party's clock backward can thereby keep a revoked certificate acceptable for roughly the induced offset, bounded by the window width and ultimately by notAfter, checked against the same clock.
The forward direction adds no forgery avenue, since a tick for a period the CA has not yet reached cannot be produced without inverting the hash (Section 16.1).
The exposure comes entirely from the clock being wrong, not from accepting a fresher-than-expected proof, and it is the trusted-time dependency shared by every time-based check: notAfter, OCSP thisUpdate and nextUpdate, CRL validity, and the base MTC short-lived-certificate model itself.¶
Two consequences follow.
The acceptance window SHOULD be kept as narrow as clock quality allows, since widening it for outage tolerance enlarges this exposure.
And a deployment whose relying parties cannot trust their clocks gains little revocation timeliness from a short tick_interval, because the clock error rather than the period then bounds how long a revoked certificate remains acceptable.
A relying party that requires tight revocation SHOULD therefore source time from a trusted, integrity-protected clock.
Both this and the window width are relying-party policy choices (Section 16.11).¶
Several behaviors of this mechanism are configurable by relying-party (client) policy, which a root program may also set. This section collects them for convenience. Each is specified in full in the section cited, and nothing here is a new requirement.¶
The default accepts a tick for the current, immediately preceding, or immediately following period. A relying party MAY widen the preceding side to tolerate a tick-distribution outage, at the cost of correspondingly delayed revocation, or the following side to tolerate a clock that runs behind, at no revocation cost. Widening applies to every certificate the relying party validates (Section 16.10, Section 13.1). Widening is the only direction available: a relying party MUST NOT narrow the window, since authenticating parties target the default and cannot observe a narrower one (step 4 of Section 9.2).¶
The acceptance window is anchored to the relying party's clock, so revocation timeliness is bounded by clock integrity. A relying party that requires tight revocation SHOULD source time from a trusted, integrity-protected clock and keep its window narrow (Section 16.10).¶
Verification hashes forward up to tick.period times, which the 16-bit period field caps at 65,535 for any certificate from any CA (Section 4).
A relying party MAY set a lower local ceiling on a certificate's implied maximum period and reject anything above it before hashing, and a constrained implementation SHOULD do so (step 5 of Section 9.2, Section 13.6).
This lever differs from the acceptance window in kind, not just in degree.
A window that is too narrow rejects a certificate until its next tick refresh, whereas a ceiling below what the deployment's CAs actually issue rejects those certificates for their whole lifetime.
It is therefore set against the tick_interval values a relying party expects to encounter, not against its own preferences alone.¶
A relying party that also supports the base MTC revoked-ranges mechanism SHOULD check both. A certificate is revoked if either indicates so (Section 16.7).¶
The tick is checked only at full-handshake certificate validation.
A relying party that wants revocation to take effect within about one tick_interval SHOULD cap session-ticket reuse and force periodic full handshakes, so that a fresh tick is re-checked (Section 13.7).¶
A relying party MAY refuse a certificate that carries no hash chain anchor once its validity period exceeds a configured threshold, since without an anchor its exposure to a detected compromise is bounded only by that period (Appendix E.1).
The threshold is at once the lifetime above which the relying party demands revocation and the worst-case exposure it accepts where revocation is absent, so a certificate that escapes the requirement is by construction one short enough for expiry to bound.
It also narrows the downgrade of Section 16.9, since an unanchored certificate for the same key is then usable against that relying party only if its own validity period is within the threshold.
The check reads notBefore, notAfter, and the presence of the extension, so it costs neither hashing nor network access.
As with the acceptance window it applies to every certificate that relying party validates, and a threshold below what its CAs issue unanchored rejects those certificates outright.¶
A relying party that implements this mechanism MUST enforce hash chain verification whenever the id-pe-hashChainAnchor extension is present. An ecosystem MAY additionally mark the extension critical for hard enforcement (Section 7.2).¶
Relying parties MUST NOT fetch ticks or use the distribution endpoint as an online responder (Section 16.5). This is a constraint, not a configurable choice.¶
The resilience levers that involve holding certificates from multiple CAs, operating redundant tick distribution, and choosing tick_interval are authenticating-party or CA decisions, not relying-party policy (Section 13.1, Section 4).¶
IANA is requested to register the following entry in the "SMI Security for PKIX Module Identifier" registry ([RFC7299]):¶
| Decimal | Description | References |
|---|---|---|
| TBD | id-mod-mtcrs-2026 | This document |
IANA is requested to register the following entry in the "SMI Security for PKIX Certificate Extension" registry ([RFC7299]):¶
| Decimal | Description | References |
|---|---|---|
| TBD | id-pe-hashChainAnchor | This document |
IANA is requested to register the following entry in the "SMI Security for PKIX Access Descriptor" registry ([RFC7299]):¶
| Decimal | Description | References |
|---|---|---|
| TBD | id-ad-mtcrsTicks | This document |
The id-ad-mtcrsTicks access method is used as a Subject Information Access access method ([RFC5280]) in a Merkle Tree CA certificate (Section 5.5 of [I-D.ietf-plants-merkle-tree-certs]).
Its accessLocation is a uniformResourceIdentifier giving the CA's tick base URL (Section 10.2).¶
id-ad-mtcrsTicks OBJECT IDENTIFIER ::= { id-ad TBD }
¶
IANA is requested to register the following entry in the "Well-Known URIs" registry ([RFC8615]):¶
| Field | Value |
|---|---|
| URI Suffix | mtcrs |
| Change Controller | IETF |
| Specification document(s) | This document |
| Status | provisional |
| Related Information | Path prefix for the MTCRS tick distribution HTTP interface: /.well-known/mtcrs/v1/{hash_name}/tick/{serial_number} (Section 10) |
The status is provisional because Section 3.1 of [RFC8615] reserves permanent registration for values defined by Standards Track RFCs and other open standards, or for values the experts find to be in use. This document is Experimental and has no known implementations (Section 14). If it advances to the Standards Track, or the mechanism comes into use, the registration can be made permanent by the process that section describes.¶
IANA is requested to register the following entry in the "ACME Directory Metadata Fields" registry (Section 9.7.6 of [RFC8555]):¶
| Field Name | Field Type | Reference |
|---|---|---|
tickBaseURL
|
string | This document |
This field appears in the meta object of an ACME directory and carries the CA's tick base URL (Section 11).
It is registered here rather than as an order object field because its value is a property of the CA rather than of any one issuance.¶
IANA is requested to register the following entry in the "ACME Order Object Fields" registry (Section 9.7.2 of [RFC8555]):¶
| Field Name | Field Type | Configurable | Reference |
|---|---|---|---|
tickURL
|
string | false | This document |
This field is set by the server in the order object and is not configurable by the client in a newOrder request.
A CA uses it for the unguessable per-certificate URL scheme (Section 10.3), in which the tick URL cannot be derived from a per-CA base URL.
A CA that publishes tickURL does not publish tickBaseURL (Section 11).¶
IANA is requested to register the following entry in the "MTC CA Identifier Child Components" registry established by Section 13.2.3 of [I-D.ietf-plants-merkle-tree-certs]:¶
| Value | Name | Reference |
|---|---|---|
| TBD | hashChainTick | This document |
The value 3, the lowest not already allocated, is suggested. The component forms each CA's tick cosigner ID (Section 8.1.1).¶
This appendix provides an ASN.1 module for the structures this document defines, following the conventions of [RFC5912].¶
MTCRS-2026
{ iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) id-mod(0)
id-mod-mtcrs-2026(TBD) }
DEFINITIONS IMPLICIT TAGS ::= BEGIN
IMPORTS
EXTENSION
FROM PKIX-CommonTypes-2009 -- in [RFC5912]
{ iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) id-mod(0)
id-mod-pkixCommon-02(57) } ;
-- PKIX arcs
id-pkix OBJECT IDENTIFIER ::=
{ iso(1) identified-organization(3) dod(6) internet(1)
security(5) mechanisms(5) pkix(7) }
id-pe OBJECT IDENTIFIER ::= { id-pkix 1 }
id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
-- Hash chain anchor certificate extension
ext-hashChainAnchor EXTENSION ::= {
SYNTAX HashChainAnchorInfo
IDENTIFIED BY id-pe-hashChainAnchor
CRITICALITY { TRUE | FALSE } }
id-pe-hashChainAnchor OBJECT IDENTIFIER ::= { id-pe TBD }
HashChainAnchorInfo ::= SEQUENCE {
tickInterval INTEGER (1..4294967295) DEFAULT 3600,
anchor OCTET STRING }
-- Tick distribution Subject Information Access method
id-ad-mtcrsTicks OBJECT IDENTIFIER ::= { id-ad TBD }
END
¶
The id-mod-mtcrs-2026, id-pe-hashChainAnchor, and id-ad-mtcrsTicks arcs contain TBD values to be replaced with the OIDs assigned by IANA (Section 17). The year in the module name and in id-mod-mtcrs-2026 follows the convention of [RFC5912] and is a placeholder for the year of publication, to be corrected if that differs.¶
This appendix gives a worked example that pins down the exact HashChainInput byte layout (Section 6) and hashing order. All values are hexadecimal, and the hash function is SHA-256 (HASH_SIZE = 32).¶
The example uses:¶
issuer_ca_id: TrustAnchorID 32473.1, whose binary representation is 81fd5901.
Carrying its own one-byte length prefix, it encodes as 04 81fd5901.¶
hash_chain_length = 5¶
seed h[0] = the 32 bytes 00 01 ... 1f (a fixed value for this example, whereas a real CA uses a cryptographically random, secret seed)¶
The fixed fields of HashChainInput therefore encode as:¶
label 6372732f76310a00 ("crs/v1\n\0")
issuer_ca_id 0481fd5901
¶
Each step computes h[i] = SHA-256(HashChainInput(h[i-1])), where HashChainInput(preimage) is the concatenation label || issuer_ca_id || preimage.
For example, HashChainInput(h[0]) is the following 45 bytes, which with SHA-256's 9 bytes of padding occupies a single 64-byte compression block:¶
6372732f76310a000481fd5901000102030405060708090a0b0c0d0e0f101112 131415161718191a1b1c1d1e1f¶
The resulting hash chain is:¶
h[0] 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f h[1] 059d23b611e311ce3ab76ccae5a12d175b6aede9d7d537629a6f2f7ebfbae7ec h[2] 085a8899525433f5323153579a50c2d254863f2cd21dee45e7da53def37fde76 h[3] c6936c83dd5ec7b4e5c5f7c643d04200311e2ad8adb4e912623b37213c08f397 h[4] 169f23ce127f6af4ef400c4de2c92915eaa4652724bbbd96b288a1612b3210fb h[5] 5905f7a677c88b43c7756c40749a557d90c0118de0d5ab50cd71c5713063dc3c¶
The anchor committed in the certificate is h[hash_chain_length] = h[5].¶
For period t = 2, the CA reveals h[hash_chain_length - t] = h[3].
The HashChainTick is { period = 2, value = h[3] }, which serializes as the following 34 bytes, a 2-byte big-endian period followed by the 32-byte value (Section 10.4):¶
0002c6936c83dd5ec7b4e5c5f7c643d04200311e2ad8adb4e912623b37213c08 f397¶
In the certificate's MTCProof this tick travels as the tick cosignature (Section 8.1.1). Assuming the value 3 for the hashChainTick component, the tick cosigner ID is 32473.1.3, whose binary representation is 81fd590103. The Cosignature is that ID with its one-byte length prefix, followed by the tick with its two-byte length prefix, 42 bytes in all:¶
0581fd5901030022 0002c6936c83dd5ec7b4e5c5f7c643d04200311e2ad8adb4e912623b37213c08 f397¶
To verify, a relying party hashes tick.value forward tick.period (2) times (Section 9):¶
v0 = h[3] = c6936c83...3c08f397 v1 = SHA-256(HashChainInput(v0)) = 169f23ce...2b3210fb (= h[4]) v2 = SHA-256(HashChainInput(v1)) = 5905f7a6...3063dc3c (= h[5])¶
v2 equals the anchor h[5], so verification succeeds.¶
The certificate's id-pe-hashChainAnchor extension carries the DER encoding of a HashChainAnchorInfo (Section 7) whose anchor is h[5].
The two encodings below pin the DEFAULT handling of tickInterval.¶
With the default tick_interval of 3600, DER omits the field (Section 11.5 of [X.690]), so the HashChainAnchorInfo is a SEQUENCE carrying only the anchor OCTET STRING (36 bytes):¶
30220420 5905f7a677c88b43c7756c40749a557d90c0118de0d5ab50cd71c5713063dc3c¶
With a non-default tick_interval, for example 86400 (one day), the field is present as an INTEGER preceding the anchor (41 bytes):¶
302702030151800420 5905f7a677c88b43c7756c40749a557d90c0118de0d5ab50cd71c5713063dc3c¶
Here 30 is SEQUENCE, 04 20 the 32-byte anchor OCTET STRING, and 02 03 015180 the INTEGER 86400.
A relying party that finds tickInterval absent MUST use the default of 3600 (Section 7).¶
For convenience, this section collects what this document asks of the base specification. Each item is specified in full in the section cited.¶
No change to the MTCProof structure, or to how the base specification parses and verifies it, is required. The tick travels as a cosignature (Section 8.1.1), which the base parsing rules already accept and which a relying party that does not implement this mechanism already ignores (Section 7.2 of [I-D.ietf-plants-merkle-tree-certs]). The tick cosigner ID needs a component in the base specification's "MTC CA Identifier Child Components" registry (Section 13.2.3 of [I-D.ietf-plants-merkle-tree-certs]), whose Specification Required policy lets this document request it (Section 17).¶
One clarification is requested:¶
Allow cosignatures that are not subtree signatures.
The base specification describes the signatures field as containing subtree signatures, and permits GREASE cosignatures carrying arbitrary bytes as its one exception (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]).
A sentence beside that provision would make the tick cosignature an intended use of the field rather than an unanticipated one.
It would say that other documents may define cosigner IDs whose signature carries other data, and that such cosignatures never count toward a relying party's cosigner requirements.
If the base specification later lets relying parties select which cosignatures they receive, it would also need to leave such cosignatures outside that selection (Section 8.1.1).¶
The following item is optional. It needs no change to the base specification's text, but it is a choice for the base ecosystem rather than for this document alone, so it is listed here:¶
Use a hash_chain_anchor entry extension as the anchor's home, in place of the X.509 extension.
The code point would be registered in the base specification's "MTC Log Entry Extension Types" registry (Section 13.2.2 of [I-D.ietf-plants-merkle-tree-certs]), whose Specification Required policy admits a registration by this document or any other.
What the choice asks of the base ecosystem is that CA cosigners recognize the type before signing any subtree that contains it (Section 5.4 of [I-D.ietf-plants-merkle-tree-certs]), which the X.509 extension does not (Appendix F.4).¶
Everything else this document defines layers on top of an otherwise unmodified base MTC log and cosigner deployment and needs no base-specification change. That covers the id-pe-hashChainAnchor X.509 extension (Section 17), the hash chain construction (Section 4), verification (Section 9), and tick distribution (Section 10).¶
The tick nonetheless belongs in the MTCProof rather than beside it.
Embedding it in the certificate's own proof of validity is what lets a relying party that implements this mechanism reject a certificate whose tick has been removed (Appendix E.5).
The one route that would leave the MTCProof untouched is to carry the tick in TLS instead, either as a new extension or by reusing status_request.
That is rejected because a TLS-carried status can be stripped with no signal that one was expected, which forces the soft-fail this mechanism exists to avoid (Appendix F.7).
Three other ways of carrying the tick within the MTCProof were also considered: a trailing field (Appendix F.8.1), a general proof-extensions field (Appendix F.8.2), and a new signature algorithm (Appendix F.8.3).
Each of them would change what every relying party parses, which is the property the cosignature avoids.
The author considers the choice settled rather than open, which is why it does not appear in Appendix D.¶
Appendix C states what this document asks of the base specification as concrete proposals, so that they are reviewable. This section collects the choices behind those proposals that the author considers genuinely open, each with the alternatives, this document's current preference, and a pointer to the full analysis. A working group that adopts this document should expect to settle them. Nothing in this section is itself a normative requirement.¶
The anchor can be an X.509 extension of the TBSCertificateLogEntry (Section 7) or a committed entry extension (Appendix F.4). Both are committed to the Merkle Tree, so the verification procedure is identical either way. The trade is compactness and committed/uncommitted symmetry against a criticality lever and MTCRS-agnostic log and cosigner software. Preference: the X.509 extension, because it lets the mechanism layer onto an unmodified MTC log and cosigner deployment. Whichever is chosen becomes the single anchor home for the ecosystem.¶
Where a CA uses unguessable tick URLs, this document carries the complete per-certificate URL in a tickURL field of the ACME order object (Section 11).
The base specification's own ACME extension reaches for a different instrument in the same situation, defining an acme-optional-alternate link relation served on the certificate response rather than adding a field to the order object (Section 9.1 of [I-D.ietf-plants-merkle-tree-certs]).
A link relation fits tickURL for the same reason it fits there, since what it names is a resource belonging to the issued certificate rather than a property of the order that produced it, and it would register in the same registry the base specification already uses.
The question is only about this one field.
tickBaseURL is a per-CA constant and belongs in the directory metadata either way (Section 11).
Preference: the order object field, as the simpler of the two and the one for which Section 9.7.2 of [RFC8555] already provides a registry.
The working group may reasonably weigh consistency with the base specification's idiom above that.¶
The two jointly set revocation latency: a withheld tick stops verifying within (k + 1) tick_intervals, where k is the number of preceding periods a relying party accepts (Section 16.10).
This document uses one hour (Appendix E.3) with k = 1, which is where the two-period bound quoted throughout comes from.
They are worth settling separately because they have different owners.
tick_interval is per-certificate and set by the CA, carried in the certificate for every verifier to read (Section 4), whereas the acceptance window is relying-party or root-program policy applying uniformly to every certificate that party validates (Section 16.11).
A one-day interval is also viable and materially shifts the balance between revocation latency and outage tolerance (Section 13.1).
Widening k buys outage tolerance at a one-for-one cost in latency (Section 16.10).
Preference: one hour with k = 1.
Neither is a protocol question.
Both concern recommended defaults and what root programs should require.
The one protocol question adjacent to them is the width of the tick's period field, which caps how short a period can be at all and bounds the forward hashing a CA can impose on relying parties (Section 4, Appendix D.6).¶
The period 0 tick is the public anchor, which gives the CA a one-period grace before it must serve a new certificate's first secret tick but defers enforcement of a just-issued certificate to the start of period 2 (Appendix E.4). Computing the hash chain one element longer removes the grace and enforces from period 1. That construction is given in Appendix E.4. Preference: keep the grace, as the operational headroom is generally worth more than sub-two-period revocation of a brand-new certificate.¶
The name appears throughout this document and in the parameter and component names it proposes (tick_interval, tickInterval, hashChainTick), so it is cheap to change now and expensive later.
It was chosen for its clock connotation, one per period on a fixed cadence, and because it is unclaimed in TLS and PKI, unlike "token", "witness", "checkpoint" and "heartbeat".
"Token" is doubly unavailable, since this document already uses it for the capability that addresses a tick URL (Section 10.3) and its bearer-credential connotation is the opposite of what a tick is, which is public, unsigned, and useless without the certificate.
The weakness of "tick" is that it ordinarily names a time event rather than a value, which is why this document always presents it as the pair {period, value}.
Preference: keep "tick", with the accurate alternative "non-revocation proof" used as the gloss at first mention rather than as the primary name.¶
Verification hashes forward once per elapsed period, so a relying party validating an arbitrary certificate is exposed not to the typical cost but to the worst case the wire format permits, which the 16-bit period field fixes at 65,535 hash computations however the issuing CA sets its parameters (Section 4, Section 13.6).
Two levers reduce that bound, and they are alternatives rather than complements, since either makes the other unnecessary.
A hierarchical construction buys the reduction with bytes, taking the worst case to 511 for a 66-byte tick in place of a 34-byte one, and removing the CA's per-certificate traversal state (Appendix F.6.1, Section 13.2.1).
A narrower period field buys it with parameter freedom, an 8-bit field bounding the worst case at 254 while saving a byte rather than spending 32 (Appendix F.6.3).
A longer default tick_interval reduces the typical cost by more than a two-level chain does and spends no bytes, but it does not touch this bound at all, and it belongs to Appendix D.3 rather than being a third lever here, because what it trades against is revocation latency.
Whichever lever is chosen, the choice is made once for the ecosystem rather than per certificate (Appendix F.6), as the anchor's home is in Appendix D.1.
Preference: neither, keeping the flat chain for its simplicity and smaller per-handshake cost, and the 16-bit field because the configuration an 8-bit one would forbid is a configuration a root program currently permits.
This is the least settled preference in this section.
A working group that treats constrained relying parties as a first-class constituency should adopt one of the two levers, and should not adopt both (Appendix F.6.3).¶
Revocation here is the absence of a tick, so nothing attests that a revocation occurred (Section 16.8). The base specification's MTCLogEntryType is extensible, so a revocation entry is constructible, and Section 16.8.1 sketches one. It would supply a permanent, non-repudiable record, could carry a reason code, and would make a logged revocation contradicted by a later tick provable by anyone offline. It would not make silent withholding detectable, because absence is not attributable and the remedy Certificate Transparency uses is unavailable to a mechanism whose relying parties fetch nothing. The cost is that CA cosigners must recognize the new entry type before they can sign any subtree containing one (Section 5.4 of [I-D.ietf-plants-merkle-tree-certs]). The question is also separable from hash chains, since such a record would serve the base revoked-ranges mechanism equally well. Preference: not in this document, which asks the base specification for no more than a clarification and has no implementations yet. A companion document, or the base specification itself, is the better home. A working group that regards the missing revocation record as the more pressing gap may reasonably decide otherwise.¶
This section provides rationale for the choices made in this document.¶
A common argument holds that sufficiently short certificate lifetimes eliminate the need for revocation: if a certificate expires in one day, the window of exposure after key compromise or misissuance is bounded by that day. This reasoning is incomplete. Three independent time intervals govern the security of a certificate:¶
Certificate lifetime: The maximum duration for which a certificate can be presented. Without revocation, this is the upper bound on exposure after any problem.¶
Revocation latency: Once the CA decides to revoke, how quickly can the certificate become unusable? Without a revocation mechanism, this equals the remaining certificate lifetime. With hash chain revocation, this is at most two periods (e.g., two hours) at any full handshake, and at a client that resumes sessions it is bounded instead by that client's resumption window (Section 13.7).¶
CA validation frequency: How often the CA re-verifies that the subscriber remains authorized (domain control, organization identity, etc.). This determines how quickly the CA learns of problems that are not self-reported by the subscriber.¶
These three intervals interact as follows. The worst-case exposure time after a problem occurs is:¶
exposure = min(remaining_lifetime,
detection_time + revocation_latency)
¶
Without revocation, detection_time is irrelevant.
The certificate remains valid until it expires regardless of what the CA knows.
With revocation, the CA can act as soon as it detects the problem, and the certificate becomes unusable within the revocation latency.¶
This has a counterintuitive consequence. A certificate with a long lifetime but active revocation can provide shorter exposure than a certificate with a short lifetime but no revocation. Taking the two configurations the Chrome policy sets out ([CHROME-MTC]), and assuming in each case that the CA learns of the compromise within minutes, from a subscriber report, a domain validation re-check, or an external notification:¶
A 7-day certificate, which is the recommended validity and the limit on the cosigner key every MTC CA is required to operate, has a worst-case exposure of 7 days without revocation, when the compromise occurs just after issuance.
With a one-hour tick_interval it is about 2 hours.¶
A 47-day certificate, the permitted maximum, has a worst-case exposure of 47 days without revocation.
With a one-hour tick_interval it is also about 2 hours, because the bound no longer depends on the lifetime.¶
The second pairing is the more striking and the first is the more representative, since 7 days is what the policy recommends. Both show the same structural point, which is that an enforceable bound stops being a function of the certificate's lifetime.¶
By substituting expensive asymmetric signatures with incredibly cheap symmetric hashing, this mechanism allows CAs to achieve the security benefit of an hourly expiration window without any of the architectural cost that comes with hourly re-issuance [SHORTLIVED]. Rather than re-signing and re-logging every certificate each hour, the CA reveals a single precomputed hash value per period, and the relying party verifies it with a single hash computation.¶
The critical difference is that passive expiry provides no mechanism for the CA to act on new information. A hash chain tick is a continuous assertion of non-revocation: each tick is an active statement by the CA that, as of this period, it has not revoked the certificate. Absence of the tick is immediately detectable and enforced by the relying party.¶
Even with functional revocation, the CA cannot revoke a certificate for a problem it does not know about. The frequency of CA re-validation therefore determines the effective security bound for non-self-reported problems (e.g., loss of domain control that the subscriber does not notice or report).¶
Revocation acts only on the detected share of that risk, and expiry bounds the rest, so the shorter a certificate's lifetime the smaller the reduction revocation can make. That is an argument for pairing it with frequent re-validation rather than against having it, since the detected share is the only part on which a CA can act at all.¶
Without revocation, validation frequency is largely irrelevant: even if the CA discovers a problem mid-lifetime, it cannot shorten the certificate's validity. The only recourse is to publish the revocation via an external mechanism (CRLite, CRLSets) that may or may not reach all relying parties.¶
With hash chain revocation, frequent CA validation translates directly into security improvement. The CA can act on a detected problem at the very next period boundary, by withholding that certificate's tick, and the certificate stops verifying within at most two periods of the decision (Section 5.1, Section 16.10). This creates an incentive structure where CAs that validate more frequently provide measurably better security, an incentive that does not exist in a pure short-lived-certificate model without revocation.¶
Root program policies can leverage this by requiring both short tick intervals and minimum re-validation frequencies, achieving a defense-in-depth posture that neither mechanism provides alone.¶
This reframes where certificate lifetime sits in the security argument. Much of the pressure to shorten certificate lifetimes is a substitute for revocation: with no reliable in-band revocation, expiry is the only enforceable bound on exposure after key compromise or misissuance. Fine-grained hash chain revocation supplies that bound directly, within about two periods rather than a lifetime. It therefore removes revocation latency as a rationale for reducing lifetimes further. Once revocation is measured in hours, shrinking a lifetime from weeks to days barely changes the worst-case exposure to a detected problem, while still incurring the issuance, logging, and relying-party trusted-subtree costs that shorter lifetimes carry (Appendix F.9).¶
This document does not argue that certificates should therefore be longer. The residual reasons to bound lifetime are real and are not addressed by revocation. The first is exposure to problems the CA never detects, which revocation cannot act on, and which re-validation frequency, above, bounds instead. The second is the agility and hygiene benefits of forced rotation: rapid algorithm migration, retirement of stale certificates, and keeping certificates aligned with current domain control.¶
The narrower point is that, with enforceable revocation in place, lifetime and revocation become independent levers. A root program can then set each on its own merits, rather than using lifetime as a proxy for a revocation mechanism it did not have.¶
Several alternative revocation mechanisms were considered and rejected, and Appendix F analyzes each. Hash chains [MICALI] were selected because they are the only known mechanism that provides all of the properties listed in the Introduction at once. Those are timely revocation, zero per-period CA signing, self-authentication against the committed anchor, mandatory hard-fail enforcement, and a per-handshake cost of about 42 bytes. They achieve this using nothing but a hash function and basic arithmetic, with no new cryptographic primitive. Each alternative in Appendix F secures some of these but sacrifices at least one. Signed per-certificate status reintroduces per-period signing (Appendix E.6.3), a separate TLS or stapled channel reintroduces the strippable soft-fail (Appendix E.6.2), and pushed external lists give up universal, CA-anchored enforcement (Appendix E.6.4). It is this simultaneity, not any single property, that distinguishes hash chains. Appendix E.6 examines the three prior approaches this one is most often compared with.¶
A one-hour tick_interval provides a good balance:¶
A certificate stops being usable within at most two hours of the CA's decision to revoke it, the current period plus the one preceding period the acceptance window admits (Section 16.10). That bound is per certificate, so retiring a compromised key means revoking every unexpired certificate for it (Section 16.9).¶
Authenticating parties must fetch a new tick once per hour. This is a trivial HTTP request for a small response.¶
For 47-day certificates, the hash chain length is 1,128, well inside the 65,535 the 16-bit period field permits (Section 4). Verification requires at most 1,127 hash computations, a few hundred microseconds on a general-purpose core and tens of milliseconds on a constrained one (Section 13.6).¶
A CA MAY store one seed per active certificate (32 bytes each, or 32 GB for 1 billion), but need not. Deriving seeds from a single long-term CA secret (Section 13.2.2) reduces per-certificate secret storage to nothing, since any hash chain is recomputed on demand from that one secret and the public entry identity. Traversal or checkpoint schemes (Section 13.2.1) bound the recomputation cost, so "store millions of secret seeds" is a choice, not a requirement.¶
A one-day period is also viable, reducing operational frequency at the cost of up to 48-hour revocation latency.
At day-scale periods the hash chain is short enough that a CA can store each hash chain in full and skip the fractal traversal of Section 13.2.1 entirely, since hash_chain_length is then on the order of the lifetime in days, e.g. 47 for a 47-day certificate.
A day-scale period cuts relying-party verification cost by that same factor, from at most 1,127 hash computations to at most 46, which makes it the cheapest accommodation available for constrained verifiers, since it spends no additional bytes (Section 13.6, Appendix F.6).
The once-per-day fetch cadence also gives a far more forgiving outage-tolerance budget (Section 13.1), and the price is coarser revocation.¶
A day-scale period should be compared against a same-lifetime certificate with no in-band revocation, whose worst-case exposure is the full remaining lifetime, rather than against a one-day short-lived certificate. Its worst-case revocation latency is about two periods (up to ~48 hours), which is longer than a one-day certificate's 24-hour exposure but far shorter than the tens of days a long-lived certificate without revocation would allow. The fine-grained-revocation advantage over short lifetimes (Appendix E.1) is precisely what a short period buys, so deployments SHOULD choose the shortest period operationally feasible.¶
The period 0 tick is the public anchor, so the mechanism does not enforce revocation during period 0. The earliest period for which the CA can withhold a secret value is period 1. Because the acceptance window keeps the anchor acceptable throughout period 1 (Section 9), a certificate the CA wishes to revoke from issuance becomes unusable only at the start of period 2. That is the same worst-case bound of at most two periods that applies to any revocation decision (Appendix E.1). The only capability given up is revoking a certificate faster than that bound in the first moments after issuance, and this trade-off is deliberate and has two advantages.¶
The CA's tick distribution service (Section 10) does not need to have computed and begun serving a certificate's first secret tick at the exact instant of issuance.
It has until the start of period 1, one full tick_interval, to begin doing so.
This assumes notBefore is not backdated by a full tick_interval or more, which would place the certificate past period 0 at issuance and remove the grace (Section 4).
This mirrors established practice for OCSP [RFC6960], where a newly issued certificate's first status response is permitted to be briefly unavailable after issuance (the CA/Browser Forum Baseline Requirements, for example, allow up to 15 minutes).
Revocation infrastructure need not be instantaneously ready for brand-new certificates.¶
The only capability lost is revoking a certificate within the first tick_interval after it was issued.
A certificate that must be revoked within, for example, one hour of issuance is an unusual case, and the CA directly controls the relevant decision.
If it already knows at issuance time that a certificate should not be trusted, it simply does not issue it.
That an arbitrary party can present the public anchor as a period 0 tick confers no additional capability on an attacker, because presenting the certificate in a TLS handshake still requires possession of the corresponding private key.
It means only that the CA's revocation signal does not take effect until period 1.¶
A deployment that instead requires revocation enforcement from the moment of issuance, with no period 0 grace, can obtain it by treating the anchor as an ordinary secret hash chain element.
It computes the hash chain one element longer, uses h[hash_chain_length + 1] as the anchor, and hashes the revealed value period + 1 times (rather than period times) during verification.
The period 0 tick is then the secret value h[hash_chain_length], which the CA can withhold.
This document uses the shorter construction because the operational grace period is generally more valuable than sub-two-period revocation of a just-issued certificate, and refers the choice to the working group (Appendix D.4).¶
The MTCProof is the bundle of evidence a relying party checks to accept a certificate.
The inclusion proof and cosignatures establish authenticity, and the tick completes it with non-revocation, so embedding the tick extends the certificate's proof of validity rather than attaching unrelated data.
It is embedded directly in the MTCProof (the certificate's signatureValue) rather than delivered via a separate channel because:¶
Inseparable from acceptance: The tick is part of the certificate presentation, not a separate signal. When the committed id-pe-hashChainAnchor extension is present, a relying party that implements this mechanism requires the MTCProof to carry the tick (Section 8.1.1), and rejects the certificate if it is absent. The tick is not itself covered by a CA signature. Like the rest of the MTCProof it is mutable, which is precisely what lets it be refreshed each period, so an active attacker can remove the bytes. What it cannot do is remove them and leave a certificate that such a relying party still accepts. Stripping the tick therefore forces a hard failure rather than the silent soft-fail that let a stripped OCSP staple pass (Appendix E.6.2): the guarantee is that revocation status cannot be dropped undetectably, not that the bytes are physically immovable.¶
No new protocol machinery: No TLS CertificateEntry extension, no negotiation, and no other signaling mechanism is needed, so the handshake itself is unchanged (Section 8.3).
The tick travels inside the existing certificate structure.
The authenticating party does need a way to replace the signatureValue of a certificate it is currently serving, once per period and without interrupting connections, which is a real implementation requirement and carries the identifier-stability consequences of Section 8.2.
What it does not need is what a stapled response requires, namely a second object with its own validity window, a responder certificate to validate, and a per-connection negotiation to carry it.¶
Safe to update dynamically: The MTCProof is not committed to the Merkle Tree, and only the TBSCertificateLogEntry is.
The authenticating party can freely replace the signatureValue each period without invalidating the inclusion proof or cosignatures.
The cost is that the complete certificate then differs from period to period, so identifiers must be taken from the TBSCertificate (Section 8.2).¶
No additional round-trips: The tick travels with the certificate in the same TLS message. No DNS lookups or side-channel fetches are needed by the relying party.¶
Server must participate: The authenticating party is already responsible for maintaining its Merkle Tree Certificate and refreshing it before expiry. Adding a lightweight hourly tick refresh is an incremental burden, not a new class of operational requirement.¶
Four earlier developments bear directly on this design. Micali's own certificate revocation system introduced the primitive, and OCSP stapling is the closest existing analogue. The other two are the ecosystem's own verdict on the alternatives, delivered from opposite ends: the browsers' migration to pushed revocation lists, and the CAs' retirement of OCSP responders. The first two fell short for reasons with a common root, that status could not be delivered to the relying party both cheaply and enforceably, and the second two followed from that failure. They are treated together here because the answer in each case turns on the same property of Merkle Tree Certificates, that the certificate presentation is itself the delivery channel.¶
Micali's certificate revocation system [MICALI] introduced the hash chain primitive this document builds on, yet it never saw wide deployment in the Web PKI. The reason was not the cryptography, which is sound, but delivery. The scheme made proving non-revocation cheap to verify, but it did not change how the per-period value reached the relying party. A verifier still had to obtain a fresh per-certificate value each period, and classic X.509 offered no low-cost, enforceable place to carry it, so the relying party had to fetch it. That reintroduced the distribution, latency, privacy, and soft-fail problems of OCSP [RFC6960], and the ecosystem settled on OCSP and CRLs instead.¶
Merkle Tree Certificates remove that barrier, which is what makes the same primitive deployable now:¶
Delivery is free. The tick rides inside the MTCProof the authenticating party already presents in the handshake (Section 8), so the relying party receives it with the certificate and fetches nothing (Section 16.5). The only fetch is the server's own once-per-period refresh, a static cacheable GET that can be delegated (Section 13.5).¶
The commitment is free. MTC already commits the anchor in the Merkle Tree and covers it with cosignatures (Section 7), so the tick is self-authenticating with no new signature, responder, or trust relationship. Classic CRS, by contrast, needed the CA to sign each hash chain's anchor into the certificate itself.¶
The remaining costs are tractable at scale. Zero per-period signing keeps post-quantum signatures off the revocation path (Section 16.2), precomputation is bounded by fractal traversal (Section 13.2.1), and distribution is delegable, which addresses the generation and distribution load that also burdened CRS.¶
CRS's limitation was therefore delivery and enforcement, not the hash chain. MTC supplies the missing channel, and the enforcement that follows from it is the subject of the next subsection.¶
OCSP stapling delivers a CA-signed status response inside the TLS handshake, which makes it the closest existing analogue to this mechanism. It has nonetheless failed to become an enforceable revocation channel.¶
Stapling ([RFC6960], carried via the TLS status_request extension) is optional and strippable.
The client requests it, and the server, or a network attacker, can omit the stapled response with no signal that one was expected.
A relying party cannot distinguish a deliberately stripped response from a temporarily unavailable responder, so it must soft-fail, treating missing status as "not revoked", to avoid breaking legitimate connections.
Soft-fail in turn provides almost no protection against an active attacker, who simply suppresses the response.
The result is a self-reinforcing loop.
Enforcement is impossible, so the security benefit is small; the benefit is small, so servers have little incentive to deploy; deployment stays incomplete, so relying parties can never move to hard-fail.
OCSP Must-Staple ([RFC7633]) was introduced to break that loop by letting a certificate commit to requiring a stapled response, and saw little adoption, because enabling it risks self-inflicted outages if the responder or the stapling path fails, and the ecosystem never reached the coverage that would let relying parties depend on it.¶
The hash chain tick is not a separate, optional signal. It is carried inside the MTCProof, which is part of the certificate presentation itself (Section 8). A relying party that parses the certificate necessarily encounters the tick. There is no request step to omit, and nothing a middlebox or misconfigured server can strip while leaving a certificate that a relying party implementing this mechanism will accept. Relying parties can therefore hard-fail from the outset, which is the property Must-Staple aimed at, obtained by construction rather than by a policy flag.¶
Several further differences lower the deployment barrier.
There is no responder fleet, responder certificate, or per-check signing operation.
No status_request negotiation or CertificateStatus handling is needed in TLS implementations beyond MTC support itself (Section 8.3).
The server refreshes a small value once per period with no cryptographic operations, rather than fetching, validating and stapling signed responses that carry their own validity windows.
And MTC is greenfield, so there is no legacy soft-fail install base: enforcement can be mandatory from day one, or made so by marking the anchor extension critical (Section 7.2).¶
The counterweight is that a tick, unlike a soft-failed OCSP response, is a hard dependency. A server that cannot refresh within a period becomes unusable until it does. This is Must-Staple's failure mode, but a smaller version of it with an escape. The refresh is a static unsigned fetch rather than a time-bounded signed response with a responder certificate, and where Must-Staple binds one certificate to one responder with no fallback, a server can hold certificates from several CAs and present one whose tick is current (Section 13.1).¶
Adoption is the other half of Must-Staple's failure, and it turns on incentives rather than on fragility. Must-Staple asked a site operator to accept an availability risk in exchange for a security property that protects its visitors, and to do so one certificate at a time while coverage was too low for any relying party to enforce anything. Almost no operator took that trade, and the reasoning was sound, since the risk was immediate while the benefit was both deferred and largely external. Three things differ here.¶
The decision is not the individual operator's, taken one certificate at a time. Whether a certificate carries an anchor is a property of the certificate, chosen by the issuing CA and constrained by root-program policy (Section 7.2), so the mechanism does not require subscribers to elect into a stricter regime one by one. That removes the adoption problem Must-Staple died of, and replaces it with the question of who sets that policy, which is a working group and root-program matter rather than a protocol one.¶
MTC being greenfield, noted above for enforcement, bears equally on incentives. Must-Staple's early adopters carried the whole risk through a long interval in which coverage was too sparse for the benefit to exist at all. A mechanism folded into MTC before deployment has no such interval.¶
And the operator is not purely subsidizing others. Revocation protects relying parties, but the party impersonated by a compromised key is the subscriber itself, and without enforceable revocation that impersonation can continue for as long as the certificate remains valid. An operator able to have that certificate made unusable within two periods, rather than waiting out its remaining lifetime, is buying something for itself. The risk taken in exchange is warned rather than sudden, since a failure to refresh raises an operational alarm while the certificate is still working (Section 12), and it is recoverable by ordinary certificate selection rather than by any new failover machinery (Section 13.1).¶
What remains true is that an ecosystem requiring this mechanism makes tick distribution part of its availability floor, which is the substance of the trade (Section 13.1).¶
Tick distribution is also simpler to operate than an OCSP responder. The serving path holds no online signing key and no responder certificate. It returns precomputed values that are immutable within a period, so it is a static, cacheable, CDN-offloadable, plain-HTTP key-value service (Section 10) with no per-request cryptography and nothing security-critical in the request path. A compromised or overloaded tick service can only serve public precomputed values. It cannot mint a false non-revocation statement, unlike a responder whose signing key is a high-value online target. Relying parties impose no load at all, because they never fetch (Section 16.5), which removes the responder-in-the-hot-path that made online OCSP a latency, availability, and privacy problem.¶
Two qualifications apply. The new cost is on the generation side, since the CA must produce the current tick for every non-revoked certificate each period, a precompute workload (Section 13.2) rather than OCSP's sign-on-demand. And the failure mode differs by design: an OCSP responder outage fails open, whereas a tick-distribution outage lasting longer than the buffer fails closed for the affected certificate (Section 13.1).¶
The major browsers disabled live OCSP checking and never pushed stapling to ubiquity, moving instead to client-side pushed revocation: Chrome's CRLSets [CRLSets], Mozilla's OneCRL and CRLite [CRLite], and Apple's on-device revocation data. A handshake-carried status mechanism might therefore look like a return to an approach the ecosystem has already rejected. It is not, because the specific reasons for that move do not apply here. Online OCSP had to soft-fail, whereas this mechanism hard-fails by construction. Client-driven OCSP leaked relying-party browsing to CAs, whereas relying parties here never contact the CA at all (Section 16.5). Must-Staple's stapling pipeline was fragile, whereas the refresh here is a trivial cacheable GET with no signing, buffered for a period and backed by multi-CA fallback (Section 16.4).¶
The pushed-list systems remain valuable and complementary, being comprehensive and fail-closed, but they are vendor-controlled and effective only for relying parties that ship the feed (Appendix F.10). This mechanism provides a universal, CA-operated baseline enforced by every relying party, including non-browser TLS clients and IoT devices with no external feed. The browser move in fact validates the design adopted here, which is fail-closed enforcement with no handshake-time relying-party network dependency.¶
The most recent movement is on the CA side rather than the relying-party side. Let's Encrypt announced in 2024 its intent to end OCSP service in favor of CRLs [LE-OCSP], following a CA/Browser Forum ballot that had already made providing OCSP optional for publicly trusted CAs. A mechanism that asks CAs to operate a new per-certificate, per-period status service therefore owes an answer to the charge that it asks the ecosystem to rebuild what it has just finished dismantling. The two reasons given divide cleanly, and only one of them applies here.¶
The first, given as the primary reason, was privacy. When a relying party checks status online, "the Certificate Authority operating the OCSP responder immediately becomes aware of which website is being visited from that visitor's particular IP address" [LE-OCSP], and a CA that chooses not to retain that information may still be compelled to collect it. This mechanism does not reduce that exposure. It removes the interaction that creates it. Relying parties never contact the CA or any distributor (Section 16.5), because the tick is already embedded in the certificate presentation and verified offline (Section 9). There is no relying-party request to observe, to log, or to be compelled to retain. The objection that ended OCSP is therefore answered here by construction, and in the strongest available form, which is that the data never comes into existence rather than that it is discarded.¶
The second reason was operational. Running OCSP had "taken up considerable resources", and keeping CA infrastructure as simple as possible was described as critical to the continuity of compliance, reliability, and efficiency [LE-OCSP]. This mechanism does not escape that objection by being smaller, and its request volume is in fact higher, because a tick is per certificate and per period whereas an OCSP response was long-lived and widely cached (Section 10). What differs is the kind of service rather than its size. An OCSP responder is an online signing service. It holds a private key, carries a responder certificate, performs cryptography in the request path, and sits inside the CA's audited perimeter, which is what makes it a continuity-of-compliance problem rather than merely a traffic problem. A tick service holds no key, signs nothing, and returns precomputed public values that are immutable within their period (Appendix E.6.3). It can therefore be run as ordinary static content distribution and delegated in whole to parties trusted for availability alone (Section 13.5), which is exactly what an online signing service cannot be.¶
One consequence of the retirement cuts the other way. CAs retiring OCSP moved to CRLs, which reach browsers through the pushed-list systems described above and reach other relying parties only if those parties fetch and process CRLs for themselves, which few TLS clients do (Appendix F.10). The retirement removed a mechanism that did not work well. It did not supply one that does, and it leaves the majority of TLS clients with no in-band per-certificate revocation status at all.¶
Two aspects of this design are shaped by the need to deploy into an ecosystem where not every relying party will support hash chain revocation at once: the criticality of the anchor extension, and where the tick is carried.¶
Marking id-pe-hashChainAnchor non-critical (Section 7.2) lets CAs begin issuing certificates with hash chain anchors before every relying party enforces them. Aware relying parties act on the extension and unaware ones proceed without it, and during the transition the base MTC revoked-ranges mechanism and external revocation systems continue to provide coverage. This mirrors how many X.509 extensions are specified as non-critical (Authority Information Access, Authority Key Identifier, CRL Distribution Points per [RFC5280]). Because the marking is SHOULD rather than MUST, it MAY be marked critical for hard enforcement from day one. That applies to an ecosystem in which all relying parties are known to support the mechanism, or to a root program once adoption is sufficient.¶
Because the tick travels as a cosignature (Section 8.1.1), a relying party that does not implement this mechanism parses the MTCProof exactly as the base specification defines it. It ignores the tick cosignature as it ignores any unrecognized cosigner, and it ignores the non-critical anchor extension. It therefore accepts an anchored certificate as it would one without an anchor, and one certificate serves relying parties that implement this mechanism and those that do not. The criticality lever is consequently the only one this mechanism needs.¶
That is the main reason this document carries the tick as a cosignature rather than in a new MTCProof field (Appendix F.8). Any new field, whether a trailing one or a general extensions field, makes a relying party predating it reject the certificate on unexpected trailing bytes, whatever the anchor extension's criticality. Deploying it would then require amending the base specification before issuance could begin, deploying concurrently while MTC is still greenfield, marking the extension critical regardless, or negotiating the extended parse so that the tick reaches only relying parties that can read it. Carrying the tick in a field every relying party already parses removes the need for all four.¶
A CA could publish current ticks via DNS in addition to the mandatory HTTP interface (Section 10), for example a TXT record at a name derived from serial_number, which the authenticating party fetches and embeds in the MTCProof.
Because the tick is embedded regardless of transport, the relying party's verification is unchanged and the choice is purely between the CA and the authenticating party.
It does not affect interoperability.
DNS's hierarchical caching suits small, frequently-updated values, letting recursive resolvers serve ticks without CA-operated CDN infrastructure.¶
Its apparent costs are addressable: large zones with one record per certificate, and TTL-bounded propagation.
A programmable authoritative server can synthesize the response for {serial_number}._tick.<zone> on demand from the same hash chain state (Section 10), so no per-certificate records are stored.
A TTL no longer than tick_interval bounds staleness: a briefly stale record stays acceptable under the one-period grace (Section 16.10), and the authenticating party's pre-installation check (Section 9) refetches an unexpectedly stale one.
Because ticks are self-authenticating, the delegated-distribution model (Section 13.5) applies unchanged, with edge DNS nodes fed the same bundle.¶
DNSSEC is neither required nor worth adding here. Each tick is self-authenticating (Section 9), so an attacker cannot forge one in transit without inverting the hash, and suppressing a tick is only a denial of service against any distribution channel. DNSSEC would add key management and signing for frequently-changing records, and larger responses, without improving the mechanism's security.¶
Another alternative is to distribute ticks over OCSP [RFC6960]. This would mean defining a new OCSP response type (or response extension) that carries the HashChainTick, so an authenticating party fetches its tick from an OCSP responder instead of the HTTP interface (Section 10). As with DNS-based distribution, this would be only a transport choice for the authenticating party's fetch: the tick is still embedded in the MTCProof and verified offline, so the relying party's procedure is unchanged. The surface appeal is that CAs already operate OCSP infrastructure.¶
This approach was rejected because:¶
The tick is self-authenticating, so an OCSP signature is pointless or harmful. An OCSP response is a responder-signed status. Signing each tick reintroduces exactly the per-response signing load this mechanism is designed to avoid, as with the OCSP-like per-certificate signatures alternative below (Appendix E.6.3). Leaving it unsigned is not really an OCSP response.¶
It is heavier and less cacheable than a plain GET. The HTTP interface is a static, plain-HTTP, CDN-cacheable key-value fetch with no per-request cryptography. OCSP adds DER request construction, a responder, nonce handling, and signature validation, contradicting the operational-simplicity argument (Appendix E.6.3).¶
Addressing and semantics do not fit.
OCSP is a relying-party-to-responder status query located via AIA and keyed by CertID (issuer name and key hashes plus serial).
Here, by contrast, the authenticating party fetches a value about its own certificate, addressed by serial_number and the CA's TrustAnchorID (Section 10).
Mapping this onto OCSP reintroduces the AIA-inflation problem of a per-certificate URL in the certificate (Appendix F.3).¶
It invites relying-party fetching. OCSP is strongly associated with client-side status checking, so an OCSP-shaped interface risks reintroducing the CA-visible relying-party fetch, latency, and soft-fail that Section 16.5 forbids and this design avoids.¶
Because the relying party only ever sees the embedded tick, an additional CA-to-authenticating-party transport is a bilateral choice that does not affect interoperability. A CA MAY tunnel ticks over any transport it and its authenticating parties support, including OCSP, alongside the mandatory HTTP interface (Section 10), but this document neither standardizes nor recommends an OCSP encoding for it.¶
Another alternative is to convey the tick distribution URL via a new Authority Information Access (AIA) access method in the certificate, following the established pattern used for OCSP responder URLs in [RFC5280].¶
This approach was rejected because:¶
AIA is part of the TBSCertificate, which is committed to the Merkle Tree. A ~50-80 byte URL in every log entry increases tree size and inclusion proof transmission costs, conflicting with MTC's goal of compactness.¶
If the CA migrates its tick distribution infrastructure, all existing certificates still contain the old URL. Delivering the URL out of band (Section 10.2) lets the CA migrate its tick infrastructure without certificate reissuance.¶
Relying parties verify the embedded tick offline against the committed anchor and never fetch ticks (Section 16.5). Putting a per-certificate URL in the certificate would not make fetching infeasible, since the URL is derivable regardless (Section 16.5). Placing it in a field relying parties routinely parse would, however, standardize and encourage client-side tick fetching, reintroducing the OCSP-style privacy leak, latency, and soft-fail problems this mechanism avoids.¶
The authenticating party obtained the certificate from the CA (e.g., via ACME) and can receive the tick base URL through that same channel at zero per-certificate cost (Section 10.2).¶
Out-of-band delivery of the base URL at provisioning (Section 10.2) conveys it at zero per-certificate cost. CAs MAY additionally publish the base URL in the CA certificate SIA (Section 10.2) for a protocol-independent record.¶
This document carries the anchor in an X.509 certificate extension (id-pe-hashChainAnchor) placed in the extensions field of the TBSCertificateLogEntry, and thus also in the TBSCertificate. An alternative is to carry it as an MTCLogEntryExtension, the entry-level extension point defined in Section 5.2.1 of [I-D.ietf-plants-merkle-tree-certs], rather than as an X.509 extension. Both are committed to the Merkle Tree, so either home makes the anchor self-authenticating. The choice is between two extension mechanisms, not between committed and uncommitted storage.¶
In this alternative, a new MTCLogEntryExtensionType (for example, hash_chain_anchor) is registered in the base specification's "MTC Log Entry Extension Types" registry (Section 13.2.2 of [I-D.ietf-plants-merkle-tree-certs]), and its extension_data carries the HashChainAnchorInfo (DER-encoded, or an equivalent TLS-encoded structure).
The verifier reads the anchor and tick_interval from the entry's extensions, which it already reconstructs from the MTCProof's extensions field during base MTC verification (Section 7.2 of [I-D.ietf-plants-merkle-tree-certs]), rather than from an X.509 extension.¶
Obtaining the anchor costs neither party any meaningful extra work.
Both the anchor and the tick then travel in the MTCProof: the entry extensions in its leading field, the tick among its cosignatures.
A relying party therefore finds the anchor by scanning a short type-length-value list it has already parsed to reconstruct the entry, in place of the scan of the certificate's X.509 extensions that the primary design performs.
Each is a lookup in a list the party decodes regardless.
An authenticating party likewise reads it from the certificate it already holds, and preserves the entry extensions verbatim when it rewrites the signatureValue to install a fresh tick (Section 10).
Disturbing them would break the inclusion proof, so the error is self-detecting.¶
Tick addressing is unaffected.
Ticks are keyed by the certificate's serialNumber (Section 10), which the entry's position in the log fixes rather than its contents, so it neither changes when the anchor moves between extension points nor depends on the anchor for its uniqueness.¶
This has a natural symmetry with the tick's encoding: the immutable, committed anchor lives in the committed entry extensions, while the mutable, per-period tick lives in the uncommitted cosignatures (Section 8.1.1). It is also more compact, because an MTCLogEntryExtension uses a short TLS type-and-length framing rather than an X.509 extension's OBJECT IDENTIFIER and DER wrapper.¶
It has three costs, however:¶
No criticality lever. Entry extensions have no critical marking, and relying parties ignore unrecognized ones (Section 12.5 of [I-D.ietf-plants-merkle-tree-certs]). The transition strategy of marking the X.509 extension critical to force unaware relying parties to hard-fail (Section 7.2) is therefore unavailable. Enforcement rests entirely on relying parties that implement this mechanism.¶
Not visible to generic X.509 tooling. The anchor no longer appears in the TBSCertificate, so only MTC-aware software can observe that a certificate uses this mechanism.¶
The log-signing infrastructure must be MTCRS-aware.
Section 5.4 of [I-D.ietf-plants-merkle-tree-certs] forbids a CA cosigner from signing a subtree containing an entry with an extension_type it does not recognize.
A hash_chain_anchor entry extension therefore forces the CA's log and cosigner components to recognize it before they can sign any subtree containing an MTCRS certificate.
With the X.509 extension, the anchor rides inside the entry's tbs_cert_entry_data as ordinary certificate bytes, so that recognition gate never fires, because the gate concerns the entry type and extension_type, not X.509 extensions.
The generic log and cosigner infrastructure therefore remain MTCRS-agnostic, and only the issuance front-end and the tick distribution service (Section 10) need be MTCRS-aware.¶
Because of the last point in particular, this document uses the X.509 extension as the primary design. It lets this mechanism be layered onto an otherwise unmodified MTC log and cosigner deployment. Registering the code point would be straightforward, since that registry's policy is Specification Required. If the working group is also willing to have CA cosigner software recognize it, the entry-extension encoding can be adopted instead, gaining the compactness and the committed/uncommitted symmetry described above.¶
The anchor is HASH_SIZE bytes (Section 7) and the tick carries a value of the same size (Section 8). Truncating both to 16 bytes would save 16 bytes in every log entry, cutting the committed overhead from about 50 bytes to about 34, and 16 bytes in every handshake, cutting the tick from 34 bytes to 18. It would also halve the CA's traversal state (Section 13.2.1). Given how much of MTC's design is spent on compactness, this looks attractive.¶
It is nonetheless rejected, because the saving comes directly out of the security margin this mechanism depends on, and out of the one property MTC exists to provide. The non-revocation guarantee rests on preimage resistance (Section 16.1): forging a tick for a period the CA has not revealed means inverting one hash step. An n-bit hash offers 2n preimage resistance classically but only about 2n/2 against Grover's algorithm (Section 16.2). At the full 256 bits that leaves about 2128, an ample quantum margin. Truncated to 128 bits it leaves about 264, which is not a margin at all for a mechanism whose entire premise is post-quantum readiness. Multi-target search makes it worse still. An attacker needs only one forgery to keep one revoked certificate alive, and searching a CA's 109 anchors together brings the quantum cost of a truncated anchor to about 249 (Section 16.2).¶
The economics are also poor even setting security aside. The 16 bytes come off a TBSCertificateLogEntry of a few hundred bytes, so the entry shrinks by a few percent, and off a handshake already carrying an inclusion proof and cosignatures, where the tick is not the dominant term (Section 7). A truncation length would additionally have to be either fixed, creating a second notion of hash size alongside the CA's HASH, or carried per certificate as a further parameter, adding a knob and a negotiation surface to a structure whose simplicity is the point (Section 4).¶
A deployment that wants smaller anchors should reduce them by choosing a CA whose tree hash is smaller, since this mechanism inherits HASH from the issuing CA (Section 16.2), rather than by truncating a stronger hash beneath the security level the rest of the ecosystem assumes.¶
Sharing one anchor across many entries, rather than shortening it, is the other way to reduce the committed cost, and it does not work either. Each certificate must be independently revocable, which requires its own withholdable chain, so a genuinely shared value would revoke every certificate using it or none of them. Committing a Merkle root over a batch of per-entry anchors preserves that independence, but a verifier still needs the anchor for the certificate in front of it, so the certificate must carry that anchor and an inclusion path to the root, which is strictly more than carrying the anchor alone.¶
This document uses a single flat hash chain, so a relying party hashes forward once per elapsed period and verification costs up to hash_chain_length - 1 hash computations (Section 13.6).
Two levers reduce that, and they belong to different parties.¶
The first is tick_interval, since hash_chain_length is ceil(lifetime / tick_interval) (Section 4).
Moving a 47-day certificate from one-hour to one-day periods takes the worst case from 1,127 hash computations to 46, a larger reduction than the two-level construction below achieves, and it costs no additional bytes.
What it costs is revocation latency, about two days in place of about two hours (Appendix E.3).
That trade is already before the working group as the choice of default interval (Appendix D.3), and the lever is the issuing CA's rather than the relying party's.¶
The second is the construction, and it is the only lever a relying party has.
A relying party validating an arbitrary server's certificate chooses neither that certificate's lifetime nor its tick_interval, so what concerns it is the worst case any certificate can impose, which the 16-bit period field fixes at 65,535 hash computations however the interval is set (Section 4).
Changing the hash function does not move that either, since each step is a single compression block whatever primitive is used, and this mechanism inherits HASH from the issuing CA rather than choosing it.
Shortening the verification path is what moves it.
Two constructions do so: a hierarchical hash chain (Appendix F.6.1) and, as the limiting case, a Merkle tree over the certificate's periods (Appendix F.6.2).
Both buy hash computations with bytes, and they differ only in where on that curve they sit.
The columns below give the worst-case forward hash computations for a 1,128-period certificate (47 days at one-hour periods), the worst case for the longest chain the 16-bit period field admits, and the resulting tick size:¶
| Construction | Steps, 1,128 | Steps, worst | Tick bytes |
|---|---|---|---|
| Flat chain (current) | 1,127 | 65,535 | 34 |
| Two levels | 67 | 511 | 66 |
| Three levels | 32 | 122 | 98 |
| Period Merkle tree | 11 | 16 | 386 |
This document keeps the flat chain because it is the simplest construction that works, because the added bytes are charged to every handshake whereas the verification cost is paid only on full handshakes, and because the interval lever above already addresses the typical case without spending any bytes at all. What it leaves unaddressed is the worst case an arbitrary certificate can impose on a relying party that did not choose its parameters. Narrowing the period field would bound that worst case as well, by restricting the parameters a CA may choose rather than by changing the construction, and the two are alternatives rather than complements (Appendix F.6.3). The balance between them is a working group judgment rather than an authorial one.¶
Whichever construction is chosen is chosen once for the ecosystem rather than per certificate. Were a second one ever added, it would be recorded as a committed field of the HashChainAnchorInfo, DEFAULT-encoded so that the flat case costs no bytes (Section 7). Making it instead a per-certificate freedom that relying-party policy may insist on is a different proposal, and the construction is the wrong variable for such a policy. What a relying party bears is a number of hash computations, which it can already bound directly from committed fields (Section 16.11), and the two do not track each other. A flat chain over 48 daily periods costs 47 computations, whereas a two-level chain over the longest chain the period field admits costs 511, so a relying party insisting on the hierarchy would reject the cheaper certificate and accept the one ten times dearer. Acting on such a policy would mean expressing the construction as a trust anchor the relying party advertises, since the trust anchors it supports are the only thing it signals about a certificate it has not yet received (Section 4.5.1.2 of [RFC9846]), and this mechanism adds nothing to the handshake (Section 8.3). That much is expressible, since the base specification reserves the OID arc beneath each CA ID, allocates sub-arcs of it through its "MTC CA Identifier Child Components" registry (Section 13.2.3 of [I-D.ietf-plants-merkle-tree-certs]), and already uses two of them to advertise landmark state (Section 5.1 of [I-D.ietf-plants-merkle-tree-certs]). What makes it the wrong instrument here is that landmark state ages out whereas a construction would not, so it would sit in trust-anchor identity for as long as both constructions existed and double relying-party configuration. The certificate states its construction in a committed field, but a relying party must choose what to advertise before it sees one. If any substantial population of relying parties may insist on the hierarchy, every CA seeking universal acceptance must issue it, so the ecosystem pays the additional bytes in every handshake and carries both code paths as well. A variable-size tick would additionally cost the constant response-length check the distribution interface relies on (Section 10.4).¶
A hierarchical chain gives the relying party a few short chains to walk in place of one long one [ONE-WAY-CHAINS].
In the two-level form, the period is decomposed into a coarse index and a fine index using a branching factor b = ceil(sqrt(hash_chain_length)), which both parties derive from the certificate so that it occupies no wire bytes.
A coarse chain advances once every b periods.
Each coarse value seeds, through a domain-separated derivation, a fine chain of b values covering the periods within that coarse step.
The tick carries the fine value and the coarse value, because the verifier cannot invert the derivation to recover the latter.
Verification hashes the fine value forward to its chain's head, checks that head against the derivation of the coarse value, and hashes the coarse value forward to the anchor, for at most 2b - 1 hash computations in place of hash_chain_length - 1.¶
Two levels is the interesting point on the curve above, and it would buy two things.¶
The first is verification cost.
It falls from about 300 microseconds to about 18 microseconds on a general-purpose core, and from tens of milliseconds to one or two milliseconds on a constrained one (Section 13.6).
The worst case any certificate can impose matters more than the typical one, and it falls from 65,535 hash computations to 511, which is about 140 microseconds on a general-purpose core and around ten milliseconds on a constrained one.
That would make the worst case affordable for every verifier, so the local ceiling of Section 16.11 and the defensive bound of verification step 5 would no longer be needed.
As specified in this document, a constrained relying party validating an arbitrary server's certificate can only bear the cost or reject the certificate, because the issuing CA chooses both the lifetime and tick_interval.¶
The second is that it would remove the CA's per-certificate mutable state.
Any tick becomes recomputable from the seed in about 2b hash computations, so fractal traversal (Section 13.2.1) would be unnecessary, and a CA deriving seeds from a long-term secret (Section 13.2.2) would hold no per-certificate state at all.
The CA's hashing rises in exchange, from about 5 hash computations per revealed value to about 2b, which at 109 certificates and hourly periods is a few cores rather than a storage system.¶
The cost is that the tick doubles, from 34 bytes to 66. Against the base specification's estimate of 384 bytes for a standalone inclusion proof (Section 6.5 of [I-D.ietf-plants-merkle-tree-certs]), and counting the cosignature framing (Section 8.1.1), that is a rise from about 11 percent to about 19 percent of the proof it travels beside. In a specification whose surrounding design is built on compactness, that is the material objection. The construction is also less simple than a single chain, and the period decomposition would have to be reconciled with the period 0 grace (Appendix E.4), with backdating (Section 4), and with the acceptance window (Section 16.10).¶
The limiting case of the same trade replaces the chain with a tree.
The CA commits a Merkle root in place of the anchor, and reveals for each period the leaf authenticating that period together with its path to the root.
The relying party verifies that path in about log2(hash_chain_length) hash computations rather than by walking a chain, so the cost is logarithmic in the worst case as well as the typical one and no longer grows with the certificate's age.
Revocation is unchanged in kind, since the CA simply stops publishing leaves and withholding remains the whole of the revocation action (Section 5.2).
A plain balanced tree suffices, because the leaves are exactly the certificate's hash_chain_length periods and that count is fixed at issuance.
The append-only accumulators built for logs that grow without a known bound, such as a Merkle mountain range, would add structure without shortening any path.¶
This tree is per certificate, which distinguishes it from the certificate revocation tree [CRT], with which it is easily confused.
A CRT is one tree per CA whose leaves are the revoked serial-number ranges, so a path proves a queried certificate's status by bracketing it between two of them, and the proof grows with the number of certificates the CA has revoked.
The tree considered here has one certificate's periods as its leaves, proves non-revocation by the presence of that period's leaf rather than by bracketing, and is bounded by hash_chain_length instead.
The decisive difference is the root.
A CRT's is signed afresh on each update, which reintroduces the per-period CA signature this mechanism exists to avoid (Appendix F.11), whereas this one is committed at issuance exactly as the anchor it replaces is, and so is signed never.
Its closer relative is therefore the tree-shaped sibling of the hash chain, a per-certificate tree over validity periods as in [QUASIMODO].¶
It is rejected here on size. At 11 hashes of path the tick reaches 386 bytes, about the size of the entire inclusion proof it accompanies (Section 6.5 of [I-D.ietf-plants-merkle-tree-certs]), so it would roughly double the certificate's proof material to save work that two levels already reduce to a few hundred microseconds. It would also cost the property that a tick is a single opaque value of fixed size, which is what keeps the distribution interface a fixed-length key-value fetch and its response-length check a constant (Section 10.4). A tree is the wrong shape for the CA as well, which would have to hold or recompute a certificate's whole period tree to answer a request, where a chain position is reachable by hashing forward from the seed.¶
Both constructions above buy hash computations with bytes.
The period field buys them with parameter freedom instead, and it is the only lever here that spends nothing.
An 8-bit period would cap hash_chain_length at 255 and so bound the worst case at 254 hash computations, which is affordable on any verifier, including the constrained ones for which even a two-level chain's 511 is a visible cost (Section 13.6).
It would also save a byte in every handshake rather than spending 32.¶
What it costs is the range of parameters a CA may choose, because the ceiling is on periods and the shortest usable period therefore scales with the certificate's lifetime (Section 4). A 47-day certificate could use no period shorter than about 4.4 hours, and a 7-day one none shorter than about 40 minutes. Put the other way, one-hour periods would be confined to certificates of about ten days or less. That accommodates the 7-day validity the Chrome policy recommends, which is 168 periods, but forbids the one-hour, 47-day combination the same policy permits ([CHROME-MTC]). Widening the field instead would permit sub-minute periods on multi-week certificates, at a correspondingly larger worst case for every relying party.¶
This document keeps 16 bits, because the configuration an 8-bit field would forbid is one a root program currently permits, and because the worst case 16 bits admit is bounded rather than unbounded.
Like the choice of construction, the width is referred to the working group (Appendix D.6), and the two are substitutes rather than complements.
With hash_chain_length capped at 255 a flat chain's worst case is already affordable, so a hierarchy adopted alongside an 8-bit field would spend 32 bytes to save work that is no longer expensive.¶
Another option is to carry the tick in TLS rather than in the MTCProof.
That means either a new TLS or CertificateEntry extension, or the existing status_request extension (defined for OCSP stapling), whose CertificateStatusType enum is extensible beyond OCSP.
In TLS 1.3 it would ride in the CertificateEntry extensions.¶
All such approaches share a disqualifying property.
A TLS-carried status is opt-in and strippable.
A separate extension can be omitted by a middlebox or misconfigured server, and status_request is requested by the client and may be omitted by the server.
In either case there is no signal that one was expected, forcing relying parties to soft-fail, exactly the failure mode analyzed in Appendix E.6.2.
Embedding the tick in the MTCProof instead makes it inseparable from the certificate's acceptance, so stripping forces a hard failure rather than a silent soft-fail (Appendix E.5).¶
Two further points weigh against a TLS encoding.
A new TLS extension type requires IANA registration and TLS-stack changes, whereas embedding in the MTCProof needs no TLS-layer change beyond MTC support itself.
And status_request specifically carries OCSPResponse semantics, a signed responder assertion, so repurposing it for a bare, self-authenticating hash value would be a poor semantic fit.¶
This document carries the tick as a cosignature (Section 8.1.1). Three other ways of carrying it within the MTCProof were considered. All three change what a relying party must parse, which is the property the cosignature avoids (Appendix E.7).¶
The base MTCProof could be amended to end with a status_tick field, a variant selected by whether the entry carries an anchor and holding either nothing or a HashChainTick.
It is the smallest structural change, and it adds no region of ignored data, since the field is fixed-size and fully verified.
Its costs are those of any new field.
A relying party predating the amendment rejects the certificate on the trailing bytes whatever the anchor extension's criticality, so issuance would have to wait for the amendment or be gated on negotiation (Appendix E.7).
And because the selector is not encoded in the MTCProof, every implementation that parses an MTCProof, whether or not it implements this mechanism, would have to recognize id-pe-hashChainAnchor in order to know where the structure ends.
Both costs fall on parties that gain nothing from the mechanism.
What it saves is the cosignature framing, 8 bytes per handshake for a 4-byte CA ID.¶
The base MTCProof could instead gain a general proof_extensions field, a trailing length-prefixed list of type-and-value pairs that is not committed to the Merkle Tree, with the tick carried as one of its entries.
That would also give future proof-level mechanisms somewhere to go without a further structural change.
It has the trailing field's deployment cost, and in addition it adds a second region of data that relying parties ignore when unrecognized, beside the one the signatures field already provides (Section 16.6).
A base specification adopting it would need to bound its size, constrain which entries a certificate may carry, and require that any security-relevant entry be mandated by something committed to the tree, as the tick is by the anchor.
The signatures field already gives the tick a place to go, so this document does not propose the general field.¶
A certificate using this mechanism could carry a distinct signature algorithm identifier in place of id-alg-mtcProof (Section 6.2 of [I-D.ietf-plants-merkle-tree-certs]), whose signatureValue format includes the tick.
That leaves the base MTCProof untouched by defining a second one beside it, and it does not supply what enforcement needs.
The TBSCertificateLogEntry has no signature field (Section 5.2.1 of [I-D.ietf-plants-merkle-tree-certs]), so the algorithm identifier is not committed to the Merkle Tree, and an attacker can substitute id-alg-mtcProof and drop the tick.
Enforcement would therefore still be keyed to the committed anchor, and the algorithm identifier would add nothing to it.
A relying party that does not recognize the algorithm rejects the certificate, so it behaves as a mandatory critical marking with no non-critical option.
Each further proof-level mechanism would need another algorithm, and each combination of mechanisms another again.¶
The simplest revocation strategy is to make certificates short-lived enough that revocation is unnecessary. The general case for functional revocation over passive expiry is made once, in Appendix E.1. This section only catalogs the specific operational costs that nonetheless motivate longer lifetimes:¶
Shorter lifetimes require more frequent issuance. With millions of subscribers, daily certificate issuance produces proportionally larger logs and more frequent Merkle Tree constructions.¶
An authenticating party that cannot reach the CA for one day loses its certificate entirely. Longer lifetimes provide more buffer against CA outages.¶
The number of landmark subtrees relying parties must maintain grows with shorter lifetimes and more frequent landmark allocation, at the cost of increased CA operational complexity.¶
Root program policies such as [CHROME-MTC] have set maximum lifetimes (e.g., 47 days) based on ecosystem-wide operational feasibility assessments, and permit them alongside a shorter recommended validity precisely because not every deployment can renew on the shorter cadence. Not all deployments can support arbitrarily short lifetimes.¶
Matching a one-hour period's revocation latency with lifetime alone would mean certificates expiring about every two hours. That attains comparable worst-case exposure but does not remove the cost. It moves the cost to the heavier places cataloged above. For the CA that means a roughly hundredfold increase in issuance, tree cosigning, and log growth, and for relying parties the same increase in trusted-subtree sync, discarding the batched compactness MTC exists to provide. It also imposes a stricter availability dependency, since re-issuance (key generation, CSR, challenge, log inclusion) is far heavier to keep continuously live than a lightweight tick fetch. Hash chain revocation buys the same fine-grained revocation for a few hundred microseconds of hashing per full handshake instead (Section 13.6), which is the cheap side of the trade by a wide margin.¶
Hash chain revocation provides the revocation latency benefits of short-lived certificates while retaining the operational advantages of longer lifetimes.¶
External revocation systems like [CRLite] and [CRLSets] compress revocation information into compact data structures pushed to relying parties. The base MTC specification suggests using these as a complement.¶
This approach has limitations when used as the sole revocation mechanism for MTC:¶
These systems are updated on the order of hours to days, depending on the deployment. They do not provide a guaranteed upper bound on revocation latency.¶
Not all relying parties subscribe to external revocation feeds. A mechanism that depends on the relying party having an up-to-date feed cannot provide universal revocation enforcement.¶
External revocation requires the relying party to trust the feed provider (e.g., the browser vendor) in addition to the CA. Hash chain revocation uses only the existing CA trust relationship.¶
These mechanisms remain valuable as defense in depth and as a fallback for the hash chain mechanism, as discussed in Section 16.7.¶
A CA could sign per-certificate non-revocation statements each period, analogous to OCSP responses.¶
This approach was rejected because:¶
A CA with a billion active certificates would need to produce a billion signatures per period. With post-quantum signature algorithms, this is computationally expensive.¶
OCSP responses include a full signature (e.g., 3,309 bytes for ML-DSA-65). Hash chain ticks are 34 bytes.¶
OCSP requires its own responder infrastructure, certificate chain, and protocol. Hash chains require only a hash function.¶
The hash chain revocation concept is based on Silvio Micali's Certificate Revocation System (CRS) [MICALI], later known as NOVOMODO [NOVOMODO]. The name "MTCRS" is a nod to it. Coincidentally, "RS" also happens to be the initials of the author of this document.¶
The approach of carrying the tick as a cosignature was suggested by David Benjamin.¶
RFC Editor's Note: Please remove this section prior to publication of a final version of this document.¶
This is the initial revision, so there are no changes to record yet.¶