import type { SignedTreeHead } from './sth.js'; import type { MerkleTree } from './tree.js'; import type { SignatureSuite } from '../sign/types.js'; /** * Constructor options for `SignedLog`. */ export interface SignedLogOpts { /** Underlying Merkle tree, holds the stateful append + proof surface. */ tree: MerkleTree; /** * Signature suite. Must have an entry in the C2SP cosignature * algorithm-byte registry (currently `Ed25519Suite` and * `MlDsa44Suite`); other suites throw `SigningError`. */ suite: S; /** * Log identity, the first line of every checkpoint body. Validated * at construction (non-empty, no whitespace, no plus characters) * per c2sp.org/tlog-checkpoint §Note text. */ origin: string; /** * Signing key, exactly `suite.skSize` bytes. The SignedLog stores * a private copy; `dispose()` zeroes that copy. The caller's view * of the buffer is left untouched. */ signingKey: Uint8Array; /** * Public key, exactly `suite.pkSize` bytes. Used to derive the * 4-byte keyId stamped on every emitted signature line and to * match incoming signature lines during verify. */ pubkey: Uint8Array; } /** * Signed transparency log substrate. Combines a `MerkleTree` with a * registered cosignature `SignatureSuite` and an origin string; * exposes append, proof, and cosignature sign / verify operations. * * Per-call WASM lifecycle is enforced by the suite itself (see the * SignatureSuite factories under `src/ts/sign/suites/`). `SignedLog` * does not wrap additional try/finally around `suite.sign` / * `suite.verify` because the suite already does. Internally the * SignedLog owns a private copy of the signing key wiped by * `dispose()`. */ export declare class SignedLog { readonly tree: MerkleTree; readonly suite: S; readonly origin: string; readonly pubkey: Uint8Array; readonly wasmModules: readonly string[]; private readonly _algoEntry; private readonly _keyId; private _signingKey; private _disposed; constructor(opts: SignedLogOpts); /** * Append a leaf to the underlying tree and return the new leaf's * index, hash, and inclusion proof against the post-append tree size. */ append(leafBytes: Uint8Array): { leafIndex: number; leafHash: Uint8Array; inclusionProof: Uint8Array[]; }; size(): number; rootHash(): Uint8Array; getInclusionProof(leafIndex: number, treeSize?: number): Uint8Array[]; getConsistencyProof(oldSize: number, newSize: number): Uint8Array[]; /** * Issue a cosignature over the current checkpoint and emit the * signed-note envelope per c2sp.org/signed-note §Format. The * signature line carries the `timestamped_signature` payload * from c2sp.org/tlog-cosignature §Format; the bytes the suite * signs are dispatched on the algorithm's * `messageConstruction`: * * - `'cosig'` → `buildCosigSignedMessage(body, ts)` * (Ed25519, §"Ed25519 signed message") * - `'cosigned-message'` → `buildCosignedMessage(...)` * (ML-DSA-44, §"ML-DSA-44 signed message") * * `timestamp` defaults to current wall-clock POSIX seconds. The * c2sp.org/tlog-witness `add-checkpoint` rule mandates a non-zero * timestamp on production cosignatures; `0` is accepted by this * function for test reproducibility but witness verifiers will * reject envelopes that carry it. Tests and vector generators * pass an explicit value to lock byte stability. */ signCheckpoint(opts?: { timestamp?: number; }): Uint8Array; /** * Parse a signed-note envelope into the structured `SignedTreeHead` * form per c2sp.org/signed-note §Format. Surfaces the body's * decoded `Checkpoint`, the signature lines that survived the * permissive signed-note parse, and the primary log cosignature's * POSIX-seconds timestamp (extracted via * `parseCosigSignaturePayload` on the line whose keyId matches * this log's pubkey-derived keyId). * * If no signature line matches, `timestamp` is reported as 0. The * field is informational at parse time; cryptographic verification * lives in `verifyCheckpoint`. Throws `RangeError` on whole-envelope * structural failure (the parseSignedNote / parseCheckpointBody * contract); does not throw on signature line content issues. */ parseCheckpoint(bytes: Uint8Array): SignedTreeHead; /** * Verify a signed-note envelope against this SignedLog's origin, * pubkey, suite, and tree hasher. Returns `true` iff the envelope * parses, carries a signature line whose keyId matches this log's * pubkey-derived keyId, the `timestamped_signature` payload on * that line decodes cleanly, and the signature verifies under * `suite.verify` over the cosignature signed message reconstructed * with the parsed timestamp. * * Returns `false` on every soft-fail mode: wrong origin, wrong * root-hash length, no matching keyId line, malformed payload, * signature failure. Throws only on this log's own disposed * state; never on envelope content (envelope content is public, * so timing distinctions on its content are not security-sensitive). * * The keyId comparison uses `constantTimeEqual` for hygiene around * key-material-adjacent state; the origin and root-hash-length * early returns are intentional non-constant-time exits since * both fields are public per the spec. */ verifyCheckpoint(bytes: Uint8Array): boolean; /** * Zero the stored signing-key copy. Idempotent. Subsequent calls * to any public method throw. */ dispose(): void; private _assertNotDisposed; /** * Dispatch the cosignature signed-message construction on the * algorithm-byte registry entry's `messageConstruction`. The * `body` argument is the canonical checkpoint body from * `serializeCheckpointBody`, ending in 0x0A. * * 'cosig' c2sp.org/tlog-cosignature §"Ed25519 signed * message". The full envelope body is * embedded verbatim after the * cosignature/v1 + time prefix. * * 'cosigned-message' c2sp.org/tlog-cosignature §"ML-DSA-44 * signed message". The body is decomposed * into origin, tree size, and root hash; * cosigner_name == origin (Phase 7 logs sign * their own checkpoints); start == 0; end == * tree size; hash == root hash. */ private _buildSignedMessage; }