reloading01

Reading a CT log entry

What a get-entries response actually contains, and why half of it looks like a certificate that nobody can use.

01c56a0fe72c2 min

A certificate transparency log speaks plain HTTP. Ask a log for a slice of its tree and you get JSON back:

curl -s 'https://ct.googleapis.com/logs/us/argon2026h2/ct/v1/get-entries?start=0&end=0'
{
  "entries": [
    {
      "leaf_input": "AAAAAAGR...",
      "extra_data": "AAWZMIIF..."
    }
  ]
}

Two base64 blobs, no field names, no hostnames. Everything useful is inside them.

leaf_input

leaf_input is a serialised MerkleTreeLeaf — the exact bytes the log hashed into its Merkle tree. Decoded, the header is small and fixed:

Bytes Field Note
1 version 0 for RFC 6962
1 leaf type 0 = timestamped entry
8 timestamp milliseconds since the epoch
2 entry type 0 = X.509, 1 = precertificate

The entry type decides how to read the rest. For an X.509 entry the leaf carries the DER certificate itself. For a precertificate entry it carries the issuer key hash and the TBS certificate with the poison extension stripped out — which means the bytes in the leaf never form a certificate you could serve.

extra_data

extra_data holds what the leaf left out: the issuing chain, and for precertificate entries the full precertificate as submitted. If you want the chain up to the root, it is here rather than in the leaf.

Why so many duplicates

Scan a live feed for a while and the same hostname shows up repeatedly. Three ordinary reasons:

  • the precertificate and the final certificate are separate entries,
  • CAs submit to several logs, and you are reading more than one,
  • renewals are frequent — a 90-day certificate reappears every couple of months.

Deduplicating on the certificate’s SHA-256 fingerprint removes the third case but not the first two, because a precertificate and its certificate are genuinely different DER. Key on the tbsCertificate with the poison and SCT extensions removed if you want those to collapse into one.

Watch the tree, not just the entries

get-entries gives you content. get-sth gives you the signed tree head: size, root hash, timestamp, signature. Two tree heads plus a consistency proof from get-sth-consistency prove the log only appended between them.

Pulling entries without ever checking a tree head means you are trusting the log operator — which is the one thing CT was designed to avoid.

index
0004
written
digest
01c56a0fe72c

The digest is the first twelve hex characters of the SHA-256 hash of the post’s Markdown body. Change the text and the digest changes with it.