KEYTRANS combined-tree directory verification (Layer 3, experimental).
KEYTRANS is the IETF key-transparency protocol
(draft-ietf-keytrans-protocol-04): a single combined log-and-prefix tree
that lets a relying party verify the value bound to a label — and monitor it
for silent changes — without the operator being able to equivocate. Like
CONIKS, verification is stateless: it recomputes everything from public inputs
(the VRF public key, the published combined-tree root, the label, and the
proof blob) and holds no directory state.
These are the verifier functions, mirroring the browser WASM SDK's
keytransVerify*Suite surface. There are three proof kinds:
verify_search/6— greatest-version search (§6): the value at a label's most recent version, or that the label is absent.verify_fixed_version/7— fixed-version search (§7): the value at a specific version.verify_monitor/7— monitoring (§8): that a known(label, version)is still consistently included (a downgrade is rejected).
Suites
The cipher suite is always explicit — there is no default. Pass one of:
:kt128_sha256_p256(0x0001) — the on-spec IETF standard suite: ECVRF-P256-SHA256-TAI labels + HMAC-SHA256 commitment.:kt128_sha256_ed25519(0x0002) — the on-spec IETF standard suite: ECVRF-Ed25519 (truncated) labels + HMAC-SHA256 commitment.:metamorphic_hybrid_exp(0xF000) — the private experimental hybrid-PQ suite (SHA3-512 commitment), in the §15.1 private-use range.
The suite selects the VRF construction, commitment width, and opening length;
it must match the suite the directory published under. The :coniks and
:keytrans directory routes a namespace may declare are carried on the
namespace policy (see MetamorphicLog.Policy).
Experimental / movable posture
Everything here is tagged KEYTRANS_EXP_04 and movable: the proof wire
format tracks the IETF draft and is deliberately not byte-frozen the way the
CONIKS and checkpoint layers are. Pin the metamorphic_log version if you
depend on a specific KEYTRANS wire.
Arguments
All binary arguments are base64-encoded:
context— the commitment domain-separation string (UTF-8), e.g."acme/keytrans-commitment/v1".vrf_public— the operator's VRF public key.root— the published combined-tree root.label— the queried label (e.g. an account identifier).proof— the movable KEYTRANS proof blob returned by the operator.
Summary
Types
An opaque, stateful KEYTRANS directory resource owned by the runtime.
A recomputed search outcome: the bound value (base64) or absence.
A KEYTRANS cipher suite (§15.1).
Functions
The current combined-tree root (the published directory root), base64-encoded.
Open a per-namespace directory on suite from an existing VRF secret key,
committing values under context, returning an opaque, empty directory/0
resource.
The VRF public key (base64) relying parties use to verify this directory's
proofs. Returns {:ok, vrf_public_b64}.
Generate a fresh VRF keypair for suite.
Produce a greatest-version search proof for label against the current log
head.
The §15.1 suite_id (u16) for a suite atom.
Append a new version of label with value, published at timestamp
(milliseconds since the Unix epoch) and blinded by opening — the suite's
Nc-byte commitment opening, which the operator supplies from a CSPRNG.
Serialized against other appends.
Verify a fixed-version search proof (§7): the value bound to label at a
specific version, or that that version is absent.
Verify a monitoring proof (§8): that a known (label, version) is still
consistently included in the directory committed by root. A downgrade (a
ladder rung that no longer proves inclusion) is rejected.
Verify a greatest-version search proof (§6): the value bound to label at
its most recent version under the directory committed by root, or that the
label is absent.
Types
@opaque directory()
An opaque, stateful KEYTRANS directory resource owned by the runtime.
Held as a reference to a Rust-side RwLock<KeytransDirectory>: reads
(combined_root/1, prove_search/2, directory_vrf_public/1) run
concurrently, appends (update/5) are serialized. It maintains the single
logical prefix tree and the chronological combined tree — built once with
directory_open/3 and grown incrementally with update/5, rather than
rebuilt per request. Not serializable; do not persist it. Persist the VRF
secret (see generate_vrf_key/1) and replay versions to rebuild.
Experimental: the KEYTRANS wire is movable and not byte-frozen. #45
serving launches CONIKS-only; this surface backs the follow-up path.
@type outcome() :: {:present, value_b64 :: String.t()} | :absent
A recomputed search outcome: the bound value (base64) or absence.
@type suite() :: :kt128_sha256_p256 | :kt128_sha256_ed25519 | :metamorphic_hybrid_exp
A KEYTRANS cipher suite (§15.1).
:kt128_sha256_p256—0x0001:kt128_sha256_ed25519—0x0002:metamorphic_hybrid_exp—0xF000
Functions
The current combined-tree root (the published directory root), base64-encoded.
Returns {:ok, root_b64} or {:error, reason} (an empty directory has no
root).
@spec directory_open(suite(), context :: String.t(), vrf_secret_b64 :: String.t()) :: {:ok, directory()} | {:error, String.t()}
Open a per-namespace directory on suite from an existing VRF secret key,
committing values under context, returning an opaque, empty directory/0
resource.
Replay the namespace's versions into it with update/5 to reconstruct the
current directory. Returns {:ok, directory} or {:error, reason} (an unknown
suite or a structurally invalid secret key).
The VRF public key (base64) relying parties use to verify this directory's
proofs. Returns {:ok, vrf_public_b64}.
Generate a fresh VRF keypair for suite.
Returns {:ok, {secret_b64, public_b64}}. The secret_b64 is per-namespace
operator infrastructure — persist it securely and pass it to
directory_open/3; it is not user key material and not a signing key. The
public_b64 is published so relying parties can verify proofs.
@spec prove_search(directory(), label_b64 :: String.t()) :: {:ok, {:present, String.t(), String.t()}} | {:ok, {:absent, String.t()}} | {:error, String.t()}
Produce a greatest-version search proof for label against the current log
head.
Returns {:ok, {:present, value_b64, proof_b64}} when the label has a value,
{:ok, {:absent, proof_b64}} when it does not, or {:error, reason} (an empty
directory or VRF failure). The returned proof_b64 verifies via
verify_search/6 against the published directory_vrf_public/1 key and
combined_root/1.
@spec suite_id(suite()) :: 0..65535
The §15.1 suite_id (u16) for a suite atom.
Examples
iex> MetamorphicLog.Keytrans.suite_id(:kt128_sha256_p256)
1
iex> MetamorphicLog.Keytrans.suite_id(:metamorphic_hybrid_exp)
61_440
@spec update( directory(), label_b64 :: String.t(), value_b64 :: String.t(), timestamp :: non_neg_integer(), opening_b64 :: String.t() ) :: {:ok, non_neg_integer()} | {:error, String.t()}
Append a new version of label with value, published at timestamp
(milliseconds since the Unix epoch) and blinded by opening — the suite's
Nc-byte commitment opening, which the operator supplies from a CSPRNG.
Serialized against other appends.
Returns {:ok, version} with the new zero-based version number, or
{:error, reason} (a wrong opening length, VRF failure, or oversized
commitment inputs).
@spec verify_fixed_version( suite(), String.t(), String.t(), String.t(), String.t(), String.t() ) :: {:ok, outcome()} | {:error, String.t()}
Verify a fixed-version search proof (§7): the value bound to label at a
specific version, or that that version is absent.
Returns {:ok, {:present, value_b64}}, {:ok, :absent}, or {:error, reason}.
@spec verify_monitor( suite(), String.t(), String.t(), String.t(), String.t(), String.t() ) :: {:ok, boolean()} | {:error, String.t()}
Verify a monitoring proof (§8): that a known (label, version) is still
consistently included in the directory committed by root. A downgrade (a
ladder rung that no longer proves inclusion) is rejected.
Returns {:ok, true} on success, or {:error, reason}.
@spec verify_search( suite(), String.t(), String.t(), String.t(), String.t(), String.t() ) :: {:ok, outcome()} | {:error, String.t()}
Verify a greatest-version search proof (§6): the value bound to label at
its most recent version under the directory committed by root, or that the
label is absent.
Returns {:ok, {:present, value_b64}}, {:ok, :absent}, or {:error, reason}.
Example
{:ok, {:present, value}} =
MetamorphicLog.Keytrans.verify_search(
:kt128_sha256_p256, context, vrf_public, root, label, proof
)