This standard defines a keystore format for stateful hash-based signing keys, specifically eXtended Merkle Signature Scheme (XMSS) as used by the Lean Ethereum consensus layer (hereafter leanxmss). It extends ERC-2335 so that the encrypted secret may be an XMSS seed and adds the machinery a consumable key requires: an explicit scheme-parameter block, a non-authoritative capacity snapshot, normative rules for where the authoritative signing state lives, a commit-before-sign durability requirement, reserved leaf ranges for concurrent signers, and import/export semantics that forbid silently resetting a key's signing position.
The encryption upgrades (Advanced Encryption Standard with 256-bit keys (AES-256) and Authenticated Encryption with Associated Data (AEAD)) are minor. The substance of this EIP is state management, because an XMSS key is destroyed by leaf reuse and the existing keystore model assumes keys are immutable, freely copyable, and restore-safe, three assumptions that are unsafe for consumable keys.
ERC-2335 stores a 32-byte BLS scalar. Lean Ethereum replaces BLS validator keys with XMSS for post-quantum security. An XMSS secret can also be reduced to a small seed, so the container needs only modest change. The danger is elsewhere.
Each XMSS signature consumes a one-time Winternitz One-Time Signature Plus (WOTS+) leaf identified by an index that must not ever be reused. Reuse of a leaf across two distinct messages enables forgery and is therefore key-destroying. This single property breaks three behaviors that are safe today and that operators, wallets, and clients currently take for granted:
Validators already solve an isomorphic problem for BLS: EIP-3076 slashing protection enforces "never sign two different messages for the same slot." For the synchronized XMSS variant, where one leaf is bound to one epoch/slot, "never reuse a leaf" and "never double-sign a slot" are the same invariant. This EIP makes that coupling normative rather than coincidental, and specifies the remaining cases (off-protocol signers such as builder-bid signing) that fall outside the slashing-protection database.
The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", and "MAY" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
leaf = f(epoch) or f(slot)); state authority is the slashing-protection database.This EIP defines keystore version 5. A v5 keystore is a v4 keystore with the changes below.
crypto.cipher.function MUST be an AEAD or 256-bit construction. aes-256-gcm is RECOMMENDED. aes-128-ctr MUST NOT be used in v5.crypto.kdf.function MAY be argon2id in addition to the scrypt and pbkdf2 of v4. Parameters MUST target current memory-hardness guidance.These are precautionary against Grover and do not affect the consumable-key semantics below.
crypto.cipher.message is the encrypted XMSS seed, not an expanded private key. The seed length is defined by the scheme parameters. The keystore stores only material required to recover the key (see §6); it does not store live signing state.
scheme object"scheme": {
"name": "leanxmss",
"params": {
"hash": "<hash identifier>",
"tree_height": 32,
"hypertree_layers": 1,
"encoding": "target_sum",
"winternitz_w": 8,
"dimension": 46,
"target_sum": 200,
"index_mode": "synchronized",
"activation_epoch": 0,
"lifetime_leaves": 4294967296
}
}
scheme.name MUST identify the signature scheme. A reader that does not recognize scheme.name MUST refuse to use the keystore.scheme.params MUST contain every parameter required to deterministically regenerate the public key and to verify a signature. These fields are immutable; a writer MUST NOT alter them after creation.hash identifies the tweakable hash function and its underlying field. This specification is hash-agnostic: any hash meeting the security requirements of the signature scheme MAY be used, and hash records the concrete choice so that a verifier can reproduce it. The Poseidon1 hash (for example over the KoalaBear field) is still being evaluated for consideration as the recommended instantiation.tree_height is the height of the (single) Merkle tree; with hypertree_layers it determines lifetime_leaves = 2^(tree_height × hypertree_layers).hypertree_layers is the number of stacked Merkle trees. leanxmss is a single-tree Generalized XMSS, so this MUST be 1.encoding identifies the incomparable encoding. leanxmss uses target_sum.winternitz_w is the base of each hash chain (BASE in leanSig).dimension is the number of hash chains (the hypercube dimension). Together with winternitz_w and target_sum it fully specifies the target-sum encoding and is REQUIRED to regenerate the public key and verify a signature.target_sum is the target sum of the target-sum encoding.index_mode MUST be synchronized or counter per §1.The non-hash values above are the recommended production parameter set for a 2^32 key lifetime, matching leanSig's SIGAbortingTargetSumLifetime32Dim46Base8 instantiation (LOG_LIFETIME = 32, DIMENSION = 46, BASE = 8, TARGET_SUM = 200). The format is hash-agnostic; the concrete hash function (for example Poseidon1 over KoalaBear) is still being evaluated for consideration and is recorded in the hash field.
state snapshot object"state": {
"authoritative": false,
"authority": "slashing-protection",
"reference": "<opaque locator, optional>",
"capacity": { "total": 4294967296, "consumed": 12345, "remaining": 4294954951 },
"high_water": 12345,
"reserved_ranges": []
}
state.authoritative MUST be false in any keystore file. The file is a snapshot for operator visibility only and MUST NOT be consulted as the high-water mark at signing time.state.authority MUST be one of slashing-protection, external, or embedded and MUST be consistent with scheme.params.index_mode:synchronized ⇒ slashing-protection.counter ⇒ external (a dedicated state store).embedded is RESERVED and SHOULD NOT be used for production validator keys; it exists only for self-contained single-signer tooling and, if used, the file ceases to be immutable.capacity and high_water are informational. A consumer MUST treat the authoritative store, not these fields, as binding.A signer MUST consult and advance a single authoritative state when producing a signature.
f(epoch) (or f(slot)); refusing to double-sign a slot is exactly refusing to reuse a leaf. Implementations MUST bind the XMSS key and its slashing-protection history as an inseparable unit: any operation that moves, imports, or exports the key MUST move, import, or export the corresponding history atomically with it.uuid, holding the current counter. It MUST provide the same durability and atomicity guarantees as §4.For every signature, an implementation MUST, in order:
fsync or platform equivalent), and committed via atomic rename or an equivalently atomic transaction, before step 5.Releasing a signature before the state advance is durable is a violation: a crash between release and persistence causes index reuse on restart. Step 3 MUST complete before step 5; steps 3 and 4 MAY be reordered relative to each other, but neither may precede step 2.
To support concurrent or high-throughput signers (e.g. builder-bid signing) without serializing every signature on one lock, a keystore MAY partition its leaf space into disjoint reserved ranges:
"reserved_ranges": [
{ "id": "signer-a", "start": 0, "end": 524287, "high_water": 410022 },
{ "id": "signer-b", "start": 524288, "end": 1048575, "high_water": 524288 }
]
[start, end] and MUST maintain its own authoritative high-water mark for that range under the §4 ordering.This is the recommended mechanism for the builder-bid exhaustion case, where a single global counter is too contended.
This EIP distinguishes two operations that legacy tooling conflates:
Therefore:
Why extend 2335 rather than define a new format. The encryption envelope, KDF abstraction, and modular design of 2335 are sound and widely implemented; only the cipher strength and the assumption of a fixed-size reusable secret needed changing. Reusing the envelope preserves tooling and review surface.
Why couple to 3076 instead of a bespoke state mechanism. In synchronized mode the slashing-protection invariant and the leaf-uniqueness invariant are identical. Defining a second, parallel state mechanism would create two sources of truth that can disagree, the worst outcome for a consumable key. Making the slashing DB authoritative gives the key a single, already-battle-tested guardrail.
Why a non-authoritative snapshot in the file. Operators need to see capacity and remaining leaves without a separate tool, but a mutable field in a portable file is a reuse trap. Marking it authoritative: false keeps visibility while denying it any role at signing time.
Why commit-before-sign is normative, not advisory. It is the single ordering rule that prevents crash-induced reuse. Everything else in the spec is bookkeeping around it.
Synchronized vs counter. Synchronized keys map cleanly onto consensus duties and the slashing DB. Off-protocol duties (builder-bid signing being the motivating example) have no slot to key on and need a free counter and its own durable store. Both are specified so a single deployment can hold keys of each kind. Open issue for discussion: whether the lean profile mandates synchronized-only for protocol keys and confines counter mode to clearly-separated signing services.
v5 is not backwards compatible with v4 consumers, which assume a reusable 32-byte secret and have no notion of state authority. v4 and v5 keystores are distinguished by the version field and the presence of the scheme object. A v5-aware client must refuse to operate on a consumable key as if it were a v4 reusable key. v4 BLS keystores remain valid under their own standard and are out of scope.
To be added. At minimum: (1) round-trip encrypt/decrypt of a leanxmss seed; (2) refusal to sign on high-water regression; (3) crash-injection between signature computation and state persistence demonstrating no reuse on restart; (4) disjointness and exclusive-assignment checks for reserved ranges.
To be added.
lifetime_leaves selection are operational requirements, not optional.Copyright and related rights waived via CC0.