Skip to content

Add ERC: Hash-Based Key Derivation from BIP-39 - #2172

Open
johba37 wants to merge 14 commits into
ethereum:masterfrom
johba37:hash-based-key-derivation
Open

johba37 wants to merge 14 commits into
ethereum:masterfrom
johba37:hash-based-key-derivation

Conversation

@johba37

@johba37 johba37 commented Nov 13, 2026

Copy link
Copy Markdown

johba and others added 11 commits November 9, 2026 09:17
…rk, phrase length

- Link BIP-32, BIP-39 and BIP-44 at a pinned bitcoin/bips commit (the only form eipw allows).
- Motivation: a third implementation runs PBKDF2 over the phrase with its own salt.
- Rationale: why not a second PBKDF2; ERC-2333, BIP-85 and SLIP-0010; why R in the stateful seed.
- Test Cases: the example key type's parameters inline, and the public key marked informative.
- Security: Grover search over the phrase's entropy, and 24 words for higher levels.
- Blank lines before lists (markdownlint MD032).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
…ounts

- Application secrets: allow other key material, such as an ECDSA gas account's
  private key, and say that breaking it reveals that secret only, not R.
- Security: a shared phrase is only as safe as the weakest wallet that held it;
  wallets SHOULD create a new phrase and SHOULD warn before deriving from an
  existing one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
Findings from implementing the draft in Python and no_std Rust:
- Define M as the wordlist's words joined by single spaces; wallets MUST
  canonicalise input and MUST reject unknown words or a failed checksum.
- Key types are bytes 0x21 to 0x7e (no space).
- Application labels are UTF-8 without normalisation, and SHOULD be ASCII.
- Drop the example public key: its pruned key generation isn't specified
  anywhere public, so the vectors now treat the key type as a string.
- New vectors: a random root at account 2^32 - 1 (stateless, stateful, app),
  and inputs an implementation must reject.
- Reference: ValueError instead of assert (which python -O strips), bool
  accounts refused, label named as in the text, BIP-39 checksum via
  python-mnemonic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
- M uses the wordlist the phrase was made with; wallets MAY support only
  the English one.
- Reference: each list element must be one word of the wordlist, so an
  element holding two words is refused, not split by check().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
- R = HKDF-Extract(salt "HBKD/v1", IKM), 64 bytes; IKM is the BIP-39 seed or
  at least 32 random bytes.
- Every seed and secret is HKDF-Expand(R, info, L) with
  info = purpose || be32(account) || be16(L) || len8(name) || name || tail.
- The key type defines its seed length L (32 to 16320 bytes); key types and
  labels are 1 to 255 bytes and go into info unhashed.
- Rationale for HKDF-SHA512, the salt and each choice in the info layout.
- All test vectors regenerated; a vector with L = 48 and the info bytes of
  the first seed added. The reference needs only the standard library and
  python-mnemonic, and agrees with pycryptodome's HKDF.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
- Key type = the parameter set's name in FIPS 205; L = 3n; the seed is cut
  into SK.seed, SK.prf and PK.seed, the inputs of slh_keygen_internal.
- Test vectors from phrase to public key for SLH-DSA-SHA2-128s,
  SLH-DSA-SHA2-192s and SLH-DSA-SHAKE-256s; the public keys agree in two
  independent libraries.
- Rationale for the rule; Security Considerations on FIPS 205's own key
  generation requirement and on signing variants.
- Reference: slh_dsa_seeds; an SLH-DSA key type needs L = 3n and is
  stateless.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
…store; copied and automated signers

- Conformance: the core is the root of a phrase, accounts, key types,
  stateless seeds and the SLH-DSA key types. Stateful keys, application
  secrets and random roots are optional. No derived value changes.
- Core rule: a key type whose secret key changes with use never takes the
  stateless derivation.
- Restoring: accounts are used in order, and a restoring wallet scans them
  in order; out-of-order use has to be recorded.
- Security: signers that can be copied must not hold a stateful instance;
  automated signers hold a random root, not the phrase.
- Rationale for the split and for the account order.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
…version

- Test Cases no longer use a key type against its own definition: the
  stateful seeds use a made-up one-time key type, and the 48-byte output
  that shows L in info is an application secret, whose length the
  application chooses.
- A key type says whether its keys are stateless or stateful; SLH-DSA key
  types MUST NOT be derived as stateful.
- "Every secret is derived with HMAC-SHA512" replaces "one primitive": the
  BIP-39 checksum needs SHA-256.
- The rejection list now covers everything the text and reference refuse;
  the SLH-DSA vectors state L = 3n.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
- Add a derivation that runs HMAC-SHA512 over the BIP-39 seed under its
  own label.
- Drop the derivation through BIP-32 paths, which is not traced to an
  implementation. The argument against BIP-32 stays.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
@eip-review-bot

eip-review-bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

File ERCS/erc-8499.md

Requires 1 more review from Editors: @g11tech, @jochem-brouwer, @samwilsn, @xinbenlv

@github-actions

Copy link
Copy Markdown

The commit cb52b4b (as a parent of b439530) contains errors.
Please inspect the Run Summary for details.

@github-actions github-actions Bot added the w-ci label Nov 13, 2026
@@ -0,0 +1,450 @@
---
eip: XXXX

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
eip: XXXX
eip: 8499

@johba37
Assigning next sequential EIP/ERC/RIP number.
Numbers are assigned by editors & associates.

Please also update the filename.

title: Hash-Based Key Derivation from BIP-39
description: Derives hash-based post-quantum keys from a BIP-39 phrase using hash functions only.
author: Johann Barbie (@johba37)
discussions-to: https://ethereum-magicians.org/t/hash-based-key-derivation-from-bip-39/30375

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
discussions-to: https://ethereum-magicians.org/t/hash-based-key-derivation-from-bip-39/30375
discussions-to: https://ethereum-magicians.org/t/erc-8499-hash-based-key-derivation-from-bip-39/30375

Updated Eth Magicians title with assigned number

johba and others added 2 commits November 13, 2026 19:47
…l keys

Restoring: a key its account has replaced still counts as in use, and a
wallet keeps trying key types it has stopped using for new keys, since
the first key can be how the account is found. The Rationale's note on
finding accounts says the same.

Stateful keys: the Rationale and Security Considerations point to
EIP-8310, which keeps a validator's XMSS key and gates signing on the
latest state, where this ERC makes new instances after a restore.

Text only. No derived value and no test vector changes.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe
- Key types: a parameter that differs from key to key, such as a tree's
  height, gives one key type string per value.
- Stateful keys: say what the key pair is, and that a key type may give
  the context to its key generation as a public input.
- Sharing a phrase: a practical rule for when to warn about an existing
  phrase.

Text only. No derived value or test vector changes.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BL33xKbhHJyeBeUd14yFe

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants