A log is a single, append-only Merkle Tree of submitted certificate and precertificate entries.
When it receives and accepts a valid submission, the log
MUST return an SCT that corresponds to the submitted certificate or precertificate. If the log has previously seen this valid submission, it
SHOULD return the same SCT as it returned before, as discussed in
Section 11.3. If different SCTs are produced for the same submission, multiple log entries will have to be created, one for each SCT (as the timestamp is a part of the leaf structure). Note that if a certificate was previously logged as a precertificate, then the precertificate's SCT of type
precert_sct_v2 would not be appropriate; instead, a fresh SCT of type
x509_sct_v2 should be generated.
An SCT is the log's promise to append to its Merkle Tree an entry for the accepted submission. Upon producing an SCT, the log
MUST fulfill this promise by performing the following actions within a fixed amount of time known as the Maximum Merge Delay (MMD), which is one of the log's parameters (see
Section 4.1):
-
Allocate a tree index to the entry representing the accepted submission.
-
Calculate the root of the tree.
-
Sign the root of the tree (see Section 4.10).
The log may append multiple entries before signing the root of the tree.
Log operators
SHOULD NOT impose any conditions on retrieving or sharing data from the log.
A log is defined by a collection of immutable parameters, which are used by clients to communicate with the log and to verify log artifacts. Except for the Final STH, each of these parameters
MUST be established before the log operator begins to operate the log.
-
Base URL:
-
The prefix used to construct URLs [RFC 3986] for client messages (see Section 5). The base URL MUST be an "https" URL, MAY contain a port, and MAY contain a path with any number of path segments but MUST NOT contain a query string, fragment, or trailing "/". Example: https://ct.example.org/blue.
-
Hash Algorithm:
-
The hash algorithm used for the Merkle Tree (see Section 10.2.1).
-
Signature Algorithm:
-
The signature algorithm used (see Section 2.2).
-
Public Key:
-
The public key used to verify signatures generated by the log. A log MUST NOT use the same keypair as any other log.
-
Log ID:
-
The OID that uniquely identifies the log.
-
Maximum Merge Delay:
-
The MMD the log has committed to. This document deliberately does not specify any limits on the value to allow for experimentation.
-
Version:
-
The version of the protocol supported by the log (currently 1 or 2).
-
Maximum Chain Length:
-
The longest certificate chain submission the log is willing to accept, if the log imposes any limit.
-
STH Frequency Count:
-
The maximum number of STHs the log may produce in any period equal to the Maximum Merge Delay (see Section 4.10).
-
Final STH:
-
If a log has been closed down (i.e., no longer accepts new entries), existing entries may still be valid. In this case, the client should know the final valid STH in the log to ensure no new entries can be added without detection. This value MUST be provided in the form of a TransItem of type signed_tree_head_v2. If a log is still accepting entries, this value should not be provided.
[
JSON.Metadata] is an example of a metadata format that includes the above elements.
A log determines whether to accept or reject a submission by evaluating it against the minimum acceptance criteria (see
Section 4.2.1) and against the log's discretionary acceptance criteria (see
Section 4.2.2).
If the acceptance criteria are met, the log
SHOULD accept the submission. (A log may decide, for example, to temporarily reject acceptable submissions to protect itself against denial-of-service attacks.)
The log
SHALL allow retrieval of its list of accepted trust anchors (see
Section 5.7), each of which is a root or intermediate CA certificate. This list might usefully be the union of root certificates trusted by major browser vendors.
To ensure that logged certificates and precertificates are attributable to an accepted trust anchor, to set clear expectations for what monitors would find in the log, and to avoid being overloaded by invalid submissions, the log
MUST reject a submission if any of the following conditions are not met:
-
The submission, type, and chain inputs MUST be set as described in Section 5.1. The log MUST NOT accommodate misordered CA certificates or use any other source of intermediate CA certificates to attempt certification path construction.
-
Each of the zero or more intermediate CA certificates in the chain MUST have one or both of the following features:
-
The Basic Constraints extension with the cA boolean asserted.
-
The Key Usage extension with the keyCertSign bit asserted.
-
Each certificate in the chain MUST fall within the limits imposed by the zero or more Basic Constraints pathLenConstraint values found higher up the chain.
-
Precertificate submissions MUST conform to all of the requirements in Section 3.2.
If the minimum acceptance criteria are met but the submission is not fully valid according to [
RFC 5280] verification rules (e.g., the certificate or precertificate has expired, is not yet valid, has been revoked, exhibits ASN.1 DER encoding errors but the log can still parse it, etc.), then the acceptability of the submission is left to the log's discretion. It is useful for logs to accept such submissions in order to accommodate quirks of CA certificate-issuing software and to facilitate monitoring of CA compliance with applicable policies and technical standards. However, it is impractical for this document to enumerate, and for logs to consider, all of the ways that a submission might fail to comply with [
RFC 5280].
Logs
SHOULD limit the length of chain they will accept. The maximum chain length is one of the log's parameters (see
Section 4.1).
If a submission is accepted and an SCT is issued, the accepting log
MUST store the entire chain used for verification. This chain
MUST include the certificate or precertificate itself, the zero or more intermediate CA certificates provided by the submitter, and the trust anchor used to verify the chain (even if it was omitted from the submission). The log
MUST provide this chain for auditing upon request (see
Section 5.6) so that the CA cannot avoid blame by logging a partial or empty chain. Each log entry is a
TransItem structure of type
x509_entry_v2 or
precert_entry_v2. However, a log may store its entries in any format. If a log does not store this
TransItem in full, it must store the
timestamp and
sct_extensions of the corresponding
TimestampedCertificateEntryDataV2 structure. The
TransItem can be reconstructed from these fields and the entire chain that the log used to verify the submission.
Each log is identified by an OID, which is one of the log's parameters (see
Section 4.1) and which
MUST NOT be used to identify any other log. A log's operator
MUST either allocate the OID themselves or request an OID from the Log ID registry (see
Section 10.2.5). One way to get an OID arc, from which OIDs can be allocated, is to request a Private Enterprise Number from IANA by completing the
registration form. The only advantage of the registry is that the DER encoding can be small. (Recall that OID allocations do not require a central registration, although logs will most likely want to make themselves known to potential clients through out-of-band means.) Various data structures include the DER encoding of this OID, excluding the ASN.1 tag and length bytes, in an opaque vector:
Note that the ASN.1 length and the opaque vector length are identical in size (1 byte) and value, so the full DER encoding (including the tag and length) of the OID can be reproduced simply by prepending an OBJECT IDENTIFIER tag (0x06) to the opaque vector length and contents.
The OID used to identify a log is limited such that the DER encoding of its value, excluding the tag and length,
MUST be no longer than 127 octets.
Various data structures are encapsulated in the
TransItem structure to ensure that the type and version of each one is identified in a common fashion:
enum {
x509_entry_v2(0x0100), precert_entry_v2(0x0101),
x509_sct_v2(0x0102), precert_sct_v2(0x0103),
signed_tree_head_v2(0x0104), consistency_proof_v2(0x0105),
inclusion_proof_v2(0x0106),
/* Reserved Code Points */
reserved_rfc6962(0x0000..0x00FF),
reserved_experimentaluse(0xE000..0xEFFF),
reserved_privateuse(0xF000..0xFFFF),
(0xFFFF)
} VersionedTransType;
struct {
VersionedTransType versioned_type;
select (versioned_type) {
case x509_entry_v2: TimestampedCertificateEntryDataV2;
case precert_entry_v2: TimestampedCertificateEntryDataV2;
case x509_sct_v2: SignedCertificateTimestampDataV2;
case precert_sct_v2: SignedCertificateTimestampDataV2;
case signed_tree_head_v2: SignedTreeHeadDataV2;
case consistency_proof_v2: ConsistencyProofDataV2;
case inclusion_proof_v2: InclusionProofDataV2;
} data;
} TransItem;
versioned_type is a value from the IANA registry in
Section 10.2.3 that identifies the type of the encapsulated data structure and the earliest version of this protocol to which it conforms. This document is v2.
data is the encapsulated data structure. The various structures named with the
DataV2 suffix are defined in later sections of this document.
Note that
VersionedTransType combines the v1 type enumerations
Version,
LogEntryType,
SignatureType, and
MerkleLeafType [
RFC 6962]. Note also that v1 did not define
TransItem, but this document provides guidelines (see
Appendix A) on how v2 implementations can coexist with v1 implementations.
Future versions of this protocol may reuse
VersionedTransType values defined in this document as long as the corresponding data structures are not modified and may add new
VersionedTransType values for new or modified data structures.
enum {
reserved(65535)
} ExtensionType;
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
The
Extension structure provides a generic extensibility for log artifacts, including SCTs (
Section 4.8) and STHs (
Section 4.10). The interpretation of the
extension_data field is determined solely by the value of the
extension_type field.
This document does not define any extensions, but it does establish a registry for future
ExtensionType values (see
Section 10.2.4). Each document that registers a new
ExtensionType must specify the context in which it may be used (e.g., SCT, STH, or both) and describe how to interpret the corresponding
extension_data.
The leaves of a log's Merkle Tree correspond to the log's entries (see
Section 4.3). Each leaf is the leaf hash (
Section 2.1) of a
TransItem structure of type
x509_entry_v2 or
precert_entry_v2, which encapsulates a
TimestampedCertificateEntryDataV2 structure. Note that leaf hashes are calculated as
HASH(0x00 || TransItem), where the hash algorithm is one of the log's parameters.
opaque TBSCertificate<1..2^24-1>;
struct {
uint64 timestamp;
opaque issuer_key_hash<32..2^8-1>;
TBSCertificate tbs_certificate;
Extension sct_extensions<0..2^16-1>;
} TimestampedCertificateEntryDataV2;
timestamp is the date and time at which the certificate or precertificate was accepted by the log, in the form of a 64-bit unsigned number of milliseconds elapsed since the Unix Epoch (1 January 1970 00:00:00 UTC -- see [
UNIXTIME]), ignoring leap seconds, in network byte order. Note that the leaves of a log's Merkle Tree are not required to be in strict chronological order.
issuer_key_hash is the HASH of the public key of the CA that issued the certificate or precertificate, calculated over the DER encoding of the key represented as SubjectPublicKeyInfo [
RFC 5280]. This is needed to bind the CA to the certificate or precertificate, making it impossible for the corresponding SCT to be valid for any other certificate or precertificate whose TBSCertificate matches
tbs_certificate. The length of the
issuer_key_hash MUST match HASH_SIZE.
tbs_certificate is the DER-encoded TBSCertificate from the submission. (Note that a precertificate's TBSCertificate can be reconstructed from the corresponding certificate, as described in
Section 8.1.2).
sct_extensions is byte-for-byte identical to the SCT extensions of the corresponding SCT.
The type of the
TransItem corresponds to the value of the
type parameter supplied in the
Section 5.1 call.
An SCT is a
TransItem structure of type
x509_sct_v2 or
precert_sct_v2, which encapsulates a
SignedCertificateTimestampDataV2 structure:
struct {
LogID log_id;
uint64 timestamp;
Extension sct_extensions<0..2^16-1>;
opaque signature<1..2^16-1>;
} SignedCertificateTimestampDataV2;
log_id is this log's unique ID, encoded in an opaque vector, as described in
Section 4.4.
timestamp is equal to the timestamp from the corresponding
TimestampedCertificateEntryDataV2 structure.
sct_extensions is a vector of 0 or more SCT extensions. This vector
MUST NOT include more than one extension with the same
extension_type. The extensions in the vector
MUST be ordered by the value of the
extension_type field, smallest value first. All SCT extensions are similar to noncritical X.509v3 extensions (i.e., the
mustUnderstand field is not set), and a recipient
SHOULD ignore any extension it does not understand. Furthermore, an implementation
MAY choose to ignore any extension(s) that it does understand.
signature is computed over a
TransItem structure of type
x509_entry_v2 or
precert_entry_v2 (see
Section 4.7) using the signature algorithm declared in the log's parameters (see
Section 4.1).
The log stores information about its Merkle Tree in a
TreeHeadDataV2:
opaque NodeHash<32..2^8-1>;
struct {
uint64 timestamp;
uint64 tree_size;
NodeHash root_hash;
Extension sth_extensions<0..2^16-1>;
} TreeHeadDataV2;
The length of NodeHash
MUST match HASH_SIZE of the log.
timestamp is the current date and time, using the format defined in
Section 4.7.
tree_size is the number of entries currently in the log's Merkle Tree.
root_hash is the root of the Merkle Tree.
sth_extensions is a vector of 0 or more STH extensions. This vector
MUST NOT include more than one extension with the same
extension_type. The extensions in the vector
MUST be ordered by the value of the
extension_type field, smallest value first. If an implementation sees an extension that it does not understand, it
SHOULD ignore that extension. Furthermore, an implementation
MAY choose to ignore any extension(s) that it does understand.
Periodically, each log
SHOULD sign its current tree head information (see
Section 4.9) to produce an STH. When a client requests a log's latest STH (see
Section 5.2), the log
MUST return an STH that is no older than the log's MMD. However, since STHs could be used to mark individual clients (by producing a new STH for each query), a log
MUST NOT produce STHs more frequently than its parameters declare (see
Section 4.1). In general, there is no need to produce a new STH unless there are new entries in the log; however, in the event that a log does not accept any submissions during an MMD period, the log
MUST sign the same Merkle Tree Hash with a fresh timestamp.
An STH is a
TransItem structure of type
signed_tree_head_v2, which encapsulates a
SignedTreeHeadDataV2 structure:
struct {
LogID log_id;
TreeHeadDataV2 tree_head;
opaque signature<1..2^16-1>;
} SignedTreeHeadDataV2;
log_id is this log's unique ID encoded in an opaque vector, as described in
Section 4.4.
The
timestamp in
tree_head MUST be at least as recent as the most recent SCT timestamp in the tree. Each subsequent timestamp
MUST be more recent than the timestamp of the previous update.
tree_head contains the latest tree head information (see
Section 4.9).
signature is computed over the
tree_head field using the signature algorithm declared in the log's parameters (see
Section 4.1).
To prepare a Merkle consistency proof for distribution to clients, the log produces a
TransItem structure of type
consistency_proof_v2, which encapsulates a
ConsistencyProofDataV2 structure:
struct {
LogID log_id;
uint64 tree_size_1;
uint64 tree_size_2;
NodeHash consistency_path<0..2^16-1>;
} ConsistencyProofDataV2;
log_id is this log's unique ID encoded in an opaque vector, as described in
Section 4.4.
tree_size_1 is the size of the older tree.
tree_size_2 is the size of the newer tree.
consistency_path is a vector of Merkle Tree nodes proving the consistency of two STHs, as described in
Section 2.1.4.
To prepare a Merkle inclusion proof for distribution to clients, the log produces a
TransItem structure of type
inclusion_proof_v2, which encapsulates an
InclusionProofDataV2 structure:
struct {
LogID log_id;
uint64 tree_size;
uint64 leaf_index;
NodeHash inclusion_path<0..2^16-1>;
} InclusionProofDataV2;
log_id is this log's unique ID encoded in an opaque vector, as described in
Section 4.4.
tree_size is the size of the tree on which this inclusion proof is based.
leaf_index is the 0-based index of the log entry corresponding to this inclusion proof.
inclusion_path is a vector of Merkle Tree nodes proving the inclusion of the chosen certificate or precertificate, as described in
Section 2.1.3.
Log operators may decide to shut down a log for various reasons, such as deprecation of the signature algorithm. If there are entries in the log for certificates that have not yet expired, simply making TLS clients stop recognizing that log will have the effect of invalidating SCTs from that log. In order to avoid that, the following actions
SHOULD be taken:
-
Make it known to clients and monitors that the log will be frozen. This is not part of the API, so it will have to be done via a relevant out-of-band mechanism.
-
Stop accepting new submissions (the error code "shutdown" should be returned for such requests).
-
Once MMD from the last accepted submission has passed and all pending submissions are incorporated, issue a final STH and publish it as one of the log's parameters. Having an STH with a timestamp that is after the MMD has passed from the last SCT issuance allows clients to audit this log regularly without special handling for the final STH. At this point, the log's private key is no longer needed and can be destroyed.
-
Keep the log running until the certificates in all of its entries have expired or exist in other logs (this can be determined by scanning other logs or connecting to domains mentioned in the certificates and inspecting the SCTs served).