Merkle

RFC 6962 (Certificate Transparency) / RFC 9162 (CT 2.0) Merkle-tree hash and proof-verification core -- the load-bearing primitive a static-CT client, a Merkle-Tree-Certificates relying party, and a sigstore / Rekor inclusion check all compose. It is ALL strict verification over SHA-256: zero new crypto.

leafHash / nodeHash / emptyRootHash build the tree hashes with the two domain-separation prefixes fixed by the spec -- a leaf is SHA-256(0x00 || entry), an interior node is SHA-256(0x01 || left || right), the empty tree is SHA-256(""). Those 0x00 / 0x01 prefixes are the second-preimage defense: without them a leaf whose bytes equal a valid interior node's preimage could be smuggled in as present.

verifyInclusion folds an audit path back to a root and constant-time- compares it to a trusted checkpoint root; verifyConsistency reconstructs BOTH the old and the new root from a consistency proof (the append-only guarantee lives in the old-root leg). Both are fail-closed: a malformed coordinate, an out-of-range index, an inverted window, a wrong hash length, or a proof whose node count does not match the tree geometry throws a typed merkle/* error; the ONLY boolean-false result is the final root comparison ("root matched" vs "did not"). A false from verifyInclusion means "not proven present against this root", never "validly absent" -- an inclusion proof cannot express absence. Tree coordinates are uint64, carried as BigInt so a large index is never Number-narrowed. This is NOT a DER format: like pki.ct it is a companion module reached explicitly, never routed by the detect-and-parse orchestrator.

pki.merkle.leafHash

since 0.1.28 experimental
pki.merkle.leafHash(entry) -> Buffer

The Merkle leaf hash MTH({d}) = SHA-256(0x00 || entry). The 0x00 prefix is the leaf-domain second-preimage separation and is applied unconditionally. Throws merkle/bad-input if entry is not a Buffer / Uint8Array.

Example

pki.merkle.leafHash(Buffer.from("leaf data")); // -> <Buffer 32-byte leaf hash>

References

pki.merkle.nodeHash

since 0.1.28 experimental
pki.merkle.nodeHash(left, right) -> Buffer

The Merkle interior-node hash SHA-256(0x01 || left || right). Both operands must be 32-byte hashes; the 0x01 prefix is applied unconditionally. Throws merkle/bad-input on a non-buffer operand, merkle/bad-hash-length on an operand that is not exactly 32 bytes.

Example

var l = pki.merkle.leafHash(Buffer.from([0]));
var r = pki.merkle.leafHash(Buffer.from([1]));
pki.merkle.nodeHash(l, r); // -> <Buffer 32-byte node hash>

References

pki.merkle.emptyRootHash

since 0.1.28 experimental
pki.merkle.emptyRootHash() -> Buffer

The Merkle tree head of the empty tree, MTH({}) = SHA-256("") (e3b0c442...b855). A fresh Buffer each call.

Example

pki.merkle.emptyRootHash(); // -> <Buffer e3 b0 c4 42 ...>

References

pki.merkle.verifyInclusion

since 0.1.28 experimental
pki.merkle.verifyInclusion(opts) -> boolean

Verify an RFC 6962 / RFC 9162 audit (inclusion) proof: fold leafHash up the audit path and constant-time-compare the reconstructed root to rootHash. Returns true iff the proof binds the leaf to the root; a well-formed proof that does not match returns false ("not proven present against this root", NEVER "validly absent"). A malformed input -- a coordinate that is not a non-negative integer (or a Number >= 2^53), treeSize 0, leafIndex >= treeSize, a non-32-byte hash, or a proof whose node count does not match the tree geometry -- throws a typed merkle/* error.

Options

leafIndex:  number | bigint,  // 0-based leaf position (uint64; pass BigInt above 2^53)
treeSize:   number | bigint,  // total leaf count of the tree the root commits to
leafHash:   Buffer,           // 32-byte leaf hash (e.g. from pki.merkle.leafHash)
proof:      Buffer[],         // the audit path, each node a 32-byte hash
rootHash:   Buffer,           // 32-byte trusted checkpoint root

Example

var lh = pki.merkle.leafHash(Buffer.from([0]));
pki.merkle.verifyInclusion({ leafIndex: 0, treeSize: 1, leafHash: lh, proof: [], rootHash: lh }); // -> true

References

pki.merkle.verifyConsistency

since 0.1.28 experimental
pki.merkle.verifyConsistency(opts) -> boolean

Verify an RFC 6962 / RFC 9162 consistency proof between an older tree of oldSize leaves (root oldRoot) and a newer tree of newSize leaves (root newRoot). Reconstructs BOTH roots from the proof and constant-time-compares each; returns true iff both match (the append-only guarantee lives in the old-root leg -- a proof that yields a valid newRoot but the wrong oldRoot is a rewritten history and returns false). The empty tree is a prefix of every tree, so oldSize 0 checks oldRoot == emptyRootHash() -- and, when newSize is also 0, newRoot == emptyRootHash() too (both trees empty); for newSize > 0 an empty-old proof places no binding on newRoot (an empty prior tree carries no append-only guarantee -- authenticate newRoot separately, e.g. via its checkpoint signature). Equal non-zero sizes require an empty proof and oldRoot == newRoot. A malformed input -- oldSize > newSize, a non-empty proof where the geometry requires none (or empty where it requires one), a non-32-byte hash, or a wrong node count -- throws a typed merkle/* error.

Options

oldSize:  number | bigint,  // leaf count of the older tree (uint64)
newSize:  number | bigint,  // leaf count of the newer tree (>= oldSize)
oldRoot:  Buffer,           // 32-byte root of the older tree
newRoot:  Buffer,           // 32-byte root of the newer tree
proof:    Buffer[],         // the consistency proof, each node a 32-byte hash

Example

var r = pki.merkle.leafHash(Buffer.from([0]));
pki.merkle.verifyConsistency({ oldSize: 1, newSize: 1, oldRoot: r, newRoot: r, proof: [] }); // -> true

References