metamorphic-log 0.1.2

Tamper-evident, append-only transparency log + verification SDK for the Metamorphic platform: RFC 6962/9162 Merkle proofs, C2SP tlog-tiles substrate, witnessed checkpoints, hybrid post-quantum checkpoint signing, and CONIKS-style index privacy. Single source of truth for primitives is metamorphic-crypto.
Documentation

metamorphic-log

A tamper-evident, append-only transparency log engine and verification SDK. Built by Moss Piglet Corporation to support privacy- first software, including Metamorphic and Mosslet.

It implements the cryptographic verification core over an append-only Merkle log (RFC 6962 / RFC 9162), wraps the C2SP tlog-tiles substrate for storage and serving, supports externally witnessed checkpoints for anti-equivocation, layers in hybrid post-quantum checkpoint signatures, and adds CONIKS-style index privacy via a swappable VRF.

Status: v0.1, building slice-by-slice. Implemented: the conformance core (canonical Layer-0 leaf encoding, RFC 6962 Merkle hashing, RFC 6962 / RFC 9162 inclusion + consistency verification), the C2SP tlog-tiles substrate with checkpoint / signed-note parsing and classical Ed25519 witness verification, additive hybrid post-quantum checkpoint signing, CONIKS index privacy (swappable VRF + SHA3-512 commitments + lookup/absence proofs), the signed per-namespace policy layer with declared == observed enforcement, and the browser verification + monitor SDK (WASM). The leaf layer is application-agnostic — any app defines its own opaque record type under a versioned <namespace>/<record-type>/v<N> context label; the bundled key_history_v1 conformance instance (the format used by Mosslet, the first consumer) is reproduced byte-for-byte by the native crate and the WASM SDK. Ingestion/scale primitives land in a later slice.

Browser verification + monitor SDK (WASM)

The crate ships a wasm-bindgen personality (the wasm module, wasm32-only) so a browser can monitor the log itself instead of trusting a server. It is a thin shell over the rlib core — no parallel logic — published to npm as @f0rest8/metamorphic-log. It exposes the full verification surface: verifyInclusion / verifyConsistency, checkpoint/signed-note verification (Ed25519 + hybrid PQ), CONIKS coniksVerifyLookup / coniksVerifyAbsence, and signedPolicyVerify + the declared == observed policyEnforce* checks. A cross-language byte-parity KAT (tests/cross_language.rs, run under wasm-pack test --node) proves the WASM exports reproduce the native KAT vectors byte-for-byte. The Elixir NIF (metamorphic_log, Rustler + dirty schedulers) is a deferred follow-up in its own sibling Hex package, mirroring the metamorphic_crypto precedent of a thin NIF over the published crate.

Verifying proofs

use metamorphic_log::{verify_inclusion, verify_consistency};

// Prove a leaf is committed at `index` in a tree of `size` whose head is `root`.
verify_inclusion(index, size, leaf_hash, &audit_path, root)?;

// Prove the tree of `size2`/`root2` is an append-only extension of `size1`/`root1`.
verify_consistency(size1, size2, &proof, root1, root2)?;

Defining a leaf (any application)

A log leaf is opaque, app-defined bytes — the Merkle layer never inspects them, so your canonical record drops in with zero reformatting. You choose a versioned context label ("acme/user-keys/v1", "example-app/audit-event/v2", …) as the domain separator for the per-record content hash. The bundled metamorphic_log::leaf::key_history_v1 module is a worked example of such a record type (and the byte-locked conformance fixture); model your own on it.

Index privacy (CONIKS)

A coniks::ConiksDirectory maps identities to committed values at VRF-derived, privacy-preserving tree positions, and answers lookups with presence or absence proofs that a relying party verifies independently — from only the namespace, the VRF public key, the directory root, and the proof.

use metamorphic_log::coniks::{ConiksDirectory, LookupResult, Namespace, verify_lookup};
use metamorphic_log::vrf::Ecvrf;

let mut dir = ConiksDirectory::new(Namespace::parse("acme")?, Box::new(Ecvrf));
dir.insert(b"alice@example.com", b"key-history-head")?;
let root = dir.root();

let LookupResult::Present(proof) = dir.lookup(b"alice@example.com")? else {
    unreachable!()
};
// Independent verification — no access to the directory needed.
let value = verify_lookup(
    &Ecvrf, dir.namespace(), dir.vrf_public_key(), &root, b"alice@example.com", &proof,
)?;
assert_eq!(value, b"key-history-head");

The VRF is swappable behind the vrf::Vrf trait (classical ECVRF today; a hybrid PQ construction slots in later with no format change). Commitments (commitment) are SHA3-512 — post-quantum and binding regardless of the VRF.

Single source of truth for primitives

This crate contains no cryptographic primitives of its own. Every hash (SHA-256 / SHA3-512), signature (composite hybrid PQ), KEM (ML-KEM), and KDF comes from metamorphic-crypto, the audited, RustCrypto-only core. There is no parallel crypto stack here.

What a transparency log does — and does not — provide

A transparency log gives you, after you have a key to anchor on:

  • Continuity — the history you observe is append-only and self-consistent over time (consistency proofs).
  • Anti-equivocation — the operator cannot show different histories to different observers without detection, because independent witnesses co-sign checkpoints.
  • Tamper-evidence — any retroactive edit to the log breaks an inclusion or consistency proof.

It does not solve:

  • First-contact / bootstrap trust. A transparency log cannot tell you whether the first key you ever saw for a peer is the genuine one. That is a Trust-On-First-Use (TOFU) problem your application must handle separately from this library (for example, with out-of-band fingerprint or safety-number verification). The log makes a key accountable over time; it does not vouch for its origin.

Cryptographic posture

  • PQ from day one for integrity, authentication, confidentiality, and commitments. Checkpoints are designed for hybrid post-quantum signing via metamorphic-crypto's composite (ML-DSA + classical) signatures.
  • Index-privacy (the CONIKS VRF) defaults to a classical ECVRF-edwards25519-SHA512-TAI construction (RFC 9381 ciphersuite 0x03), behind a swappable trait. The constant-time ELL2 (0x04) suite and a hybrid (PQ + classical) output path are designed in but not built — the former lands with a curve backend that exposes a conformant hash-to-curve, the latter when an audited lattice VRF exists. This is the only layer with a classical default in v0.1.
  • Primitives are hybrid post-quantum, pure-Rust, and NCC-audited (via metamorphic-crypto). They are not FIPS-validated, and this project does not claim FIPS validation.

Standards spine

  • RFC 6962 / RFC 9162 — Merkle log; inclusion + consistency proofs
  • C2SP tlog-tiles, tlog-witness, checkpoint / signed-note — interoperable substrate enabling reciprocal witnessing
  • RFC 9381 — ECVRF-edwards25519 (CONIKS index privacy)
  • FIPS 203 / 204 + CNSA 2.0 — post-quantum primitives (via metamorphic-crypto)
  • NIST SP 800-56C / 800-108 — KDF roles

Module layout

Module Layer Responsibility
leaf 0 Canonical, length-prefixed leaf encoding
merkle 1 RFC 6962 SHA-256 tree-node hashing (fixed, witness-audited)
proof 1 Inclusion + consistency proof verification
checkpoint 2 Signed-note / witnessed checkpoints; hybrid PQ signing
vrf 3 Swappable VRF trait; classical ECVRF (RFC 9381 TAI) default
commitment 3 SHA3-512 hiding/binding index→value commitments
coniks 3 Per-namespace directory; presence + absence (index privacy)
policy 0 Signed, versioned namespace policy; declared == observed
note 2 C2SP signed-note parse/verify (Ed25519 + hybrid PQ lines)
tile 2 C2SP tlog-tiles coordinates / serving geometry
wasm Browser verification + monitor SDK (wasm32-only)
error Crate-wide error type

Safety & supply chain

  • #![forbid(unsafe_code)] at this layer
  • RustCrypto-only dependencies; primitives delegated to metamorphic-crypto
  • Edition 2024, MSRV 1.85, dual-licensed MIT OR Apache-2.0
  • CI runs fmt --check, clippy -D warnings, tests, a wasm32-unknown-unknown check, a wasm-pack SDK build, the cross-language byte-parity KAT (wasm-pack test --node), cargo audit, and an MSRV-floor build; all action refs are SHA-pinned. The tagged release pipeline adds CycloneDX SBOM, cosign keyless signing, build-provenance attestation, and OIDC trusted publishing to crates.io + npm
  • See SECURITY.md for the disclosure process

License

Licensed under either of

at your option.