From aeba28d9e55b29c67d9104b351d8e20428ff82fa Mon Sep 17 00:00:00 2001 From: Jason Tudisco Date: Fri, 5 Jun 2026 22:53:59 -0600 Subject: [PATCH] docs(rust,nodejs): expand TUTORIAL.md recovery-phrase section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reworks the "Pick your primary key" → Option B block in both tutorials into a proper "Recovery phrases" mini-chapter: • Table comparing 24-word (256 bits, bijection) vs 12-word (128 bits, one-way SHA-256 derivation). • Decision guide — why someone would actually pick 12 over 24 (and vice versa). Explicitly: "save the phrase, not just the seed" for the 12-word case. • Wallet-incompatibility callout — KEZ phrases don't produce the same key as the same phrase in Ledger / MetaMask / Bitcoin wallets. Explains the two deliberate reasons (no BIP-39 PBKDF2, no BIP-32 derivation tree), and the inverse — KEZ phrases can't be used to extract funds from a hardware-wallet recovery so a malicious importer can't phish that direction either. • Concrete backup advice — pencil on paper, numbered words, fireproof storage, don't photograph it, don't cloud-sync it, don't split it, don't permute it. Calls out which password-manager patterns are OK vs not. • "Working with phrases later" — clean examples of `identity mnemonic` (no key derived) and `identity from-mnemonic` (recover an existing key), with the note that the recovered output is byte-for-byte identical to what `identity new` originally printed. Same content in both the Rust and Node tutorials, command examples adapted to each CLI invocation style. Co-Authored-By: Claude Opus 4.7 --- nodejs/TUTORIAL.md | 114 ++++++++++++++++++++++++++++++++++++++------- rust/TUTORIAL.md | 114 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 196 insertions(+), 32 deletions(-) diff --git a/nodejs/TUTORIAL.md b/nodejs/TUTORIAL.md index b66df3b..b00fcd2 100644 --- a/nodejs/TUTORIAL.md +++ b/nodejs/TUTORIAL.md @@ -97,8 +97,8 @@ A new nostr keypair: npm run cli -- identity new ``` -Or a new Ed25519 keypair, which comes with a 24-word BIP-39 phrase -alongside the hex seed (both are equivalent backups): +Or a new Ed25519 keypair, which comes with a BIP-39 phrase alongside +the hex seed (both are equivalent backups): ```sh npm run cli -- identity new --key-type ed25519 # 24-word @@ -114,25 +114,107 @@ Secret: 9e3f51… (32-byte seed) Mnemonic (24 words): "abandon ability able about above absent academy accident…" ``` -> **12 vs 24.** 24 words is fully round-trippable: phrase ↔ seed are -> bijective. 12 words is shorter to memorize, but the seed is derived -> from the phrase one-way (KEZ-specific SHA-256 step), so you cannot -> derive a 12-word phrase from a hex seed. Pick whichever you'll -> actually back up. - -You can also get just a phrase, or restore an existing one: - -```sh -npm run cli -- identity mnemonic # fresh 24 words -npm run cli -- identity mnemonic --words 12 # fresh 12 words -npm run cli -- identity from-mnemonic "abandon ability able …" # recover the key -``` - > **Save the backup.** Seed *or* phrase — at least one. Lose them both > and the identity is gone. There's no recovery flow. +### Recovery phrases — what's actually going on + +A KEZ recovery phrase is a [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) +mnemonic — the same 2048-word English wordlist that Bitcoin, Ethereum, +and most hardware wallets use. The words encode random bits: + +| Phrase length | Random bits | Resulting Ed25519 seed | +|---|---|---| +| **24 words** | 256 bits of entropy | The 32-byte seed *is* those 256 bits (1:1). Phrase ↔ seed round-trips. | +| **12 words** | 128 bits of entropy | 16 bytes → 32-byte seed via `SHA-256("kez-bip39-12-v1" \|\| entropy)`. Phrase → seed only (one-way). | + +#### Picking 12 vs 24 + +- **Pick 24 words** when you want full round-trip-ability — i.e. you'd + like to be able to *recover the phrase from the hex seed* at any time + in the future. Anyone's 32-byte Ed25519 secret can be re-encoded into + the unique 24-word phrase that produced it. Bigger security margin + (256 bits of entropy vs 128). +- **Pick 12 words** when you want a shorter thing to write down on + paper or remember. 128 bits of entropy is still enormously beyond + brute-forcing. The trade-off: the path is *one-way only* — you can + always derive the seed from the phrase, but you cannot derive the + phrase from the seed. So if you only ever have the seed, you'll + never know what 12-word phrase produced it. **Save the phrase + itself**, not just the resulting seed. + +Either way the resulting Ed25519 identity is exactly the same shape; +peers can't tell which word count you used. The choice is purely about +your backup ergonomics. + +#### ⚠ Not compatible with hardware-wallet derivations + +A KEZ 12-word phrase **does not** produce the same Bitcoin or Ethereum +key as the same 12 words typed into a Ledger or MetaMask, and vice +versa. The reasons are deliberate: + +1. Other wallets feed the phrase through BIP-39's PBKDF2 to get a + 64-byte "seed", then run that through BIP-32 hierarchical + derivation at a coin-specific path. KEZ doesn't — it takes the + raw entropy and uses it directly (24-word case) or hashes it with + a domain tag (12-word case). +2. KEZ identities aren't part of a derivation tree. There's one + identity per phrase; there's no path component. + +That means: **don't paste your existing hardware-wallet recovery +phrase into KEZ** expecting to get a key you've already seen. It'll +produce a *new* KEZ identity uncorrelated with anything else. + +Conversely: a KEZ phrase you saved is *only* useful for KEZ. A +malicious wallet that says "import this phrase" can't extract your +existing Bitcoin / Ethereum funds from a KEZ phrase, because the +phrase wasn't derived through the same path. + +#### Backing up — concrete advice + +The phrase is the master key to your identity. Practical guidance: + +- **Write it on paper, with a pencil. Number each word (1–12 or 1–24) + so you can later verify the order.** A photograph or cloud document + is one breach away from compromise. +- **Store the paper somewhere fireproof.** Safe-deposit boxes, lockable + desk drawers, etched-stainless-steel cards if you're paranoid. +- **Never type the phrase into a website, chat app, or password + manager that auto-syncs.** Local-only password managers (KeePassXC, + 1Password locked vault) are OK; cloud-synced managers are a softer + target. +- **Don't split it across two locations "for safety".** Half a BIP-39 + phrase weakens the entropy more than it protects against loss. If you + need redundancy, make two complete paper copies in different physical + locations. +- **Don't be cute.** Don't permute the words "because they're easy to + remember in this order." The wordlist position matters; reorder and + you change the key (and the BIP-39 checksum will reject it on + restore anyway). + +### Working with phrases later + +You can generate a fresh phrase without producing a key, or recover +the key from a phrase you wrote down earlier: + +```sh +# Print a fresh 24-word phrase (or 12, with --words 12). No key derived. +npm run cli -- identity mnemonic +npm run cli -- identity mnemonic --words 12 + +# Recover the Ed25519 key from a phrase. Word count auto-detected. +npm run cli -- identity from-mnemonic "abandon ability able about above absent +academy accident account accuse achieve acid acoustic acquire across act +action actor actress actual adapt add addict address" +``` + +The recovered output is identical, byte-for-byte, to what was printed +when you first ran `identity new` — same `Primary:`, same `Public:`, +same `Secret:`. + Throughout the rest of this tutorial you can substitute `--mnemonic "your phrase here"` anywhere `--ed25519-seed ` appears. +Both are accepted on every command that takes a signing key. For the rest of this tutorial we'll use a nostr key for examples and write the secret as `nsec1FAKE...` — substitute your real one. diff --git a/rust/TUTORIAL.md b/rust/TUTORIAL.md index b2cb1dc..1f481eb 100644 --- a/rust/TUTORIAL.md +++ b/rust/TUTORIAL.md @@ -81,7 +81,8 @@ kez identity new --key-type nostr # only if you want a NEW key ### Option B: generate a fresh Ed25519 primary -If you'd rather start clean, generate a new Ed25519 key: +If you'd rather start clean, generate a new Ed25519 key with a BIP-39 +recovery phrase you can write down on paper: ```sh kez identity new --key-type ed25519 # 24-word phrase (default) @@ -101,26 +102,107 @@ You now have **two equivalent backups** — the hex seed *and* the 24-word BIP-39 phrase. Either restores the same identity. Most people back up the phrase (easier to write down, easier to verify by hand). -> **12 vs 24.** 24 words is fully round-trippable: phrase ↔ seed are -> bijective. 12 words is shorter to memorize, but the seed is derived -> from the phrase one-way (KEZ-specific SHA-256 step), so you cannot -> derive a 12-word phrase from a hex seed. Pick whichever you'll -> actually remember to back up. - -You can also get just a phrase without a key, or restore from a phrase -you wrote down earlier: - -```sh -kez identity mnemonic # print a fresh 24-word phrase -kez identity mnemonic --words 12 # 12-word -kez identity from-mnemonic "abandon ability able …" # recover the key -``` - > **Save the backup.** Seed *or* phrase — at least one. Lose them both > and the identity is gone. There's no recovery flow. +### Recovery phrases — what's actually going on + +A KEZ recovery phrase is a [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) +mnemonic — the same 2048-word English wordlist that Bitcoin, Ethereum, +and most hardware wallets use. The words encode random bits: + +| Phrase length | Random bits | Resulting Ed25519 seed | +|---|---|---| +| **24 words** | 256 bits of entropy | The 32-byte seed *is* those 256 bits (1:1). Phrase ↔ seed round-trips. | +| **12 words** | 128 bits of entropy | 16 bytes → 32-byte seed via `SHA-256("kez-bip39-12-v1" \|\| entropy)`. Phrase → seed only (one-way). | + +#### Picking 12 vs 24 + +- **Pick 24 words** when you want full round-trip-ability — i.e. you'd + like to be able to *recover the phrase from the hex seed* at any time + in the future. Anyone's 32-byte ed25519 secret can be re-encoded into + the unique 24-word phrase that produced it. Bigger security margin + (256 bits of entropy vs 128). +- **Pick 12 words** when you want a shorter thing to write down on + paper or remember. 128 bits of entropy is still enormously beyond + brute-forcing. The trade-off: the path is *one-way only* — you can + always derive the seed from the phrase, but you cannot derive the + phrase from the seed. So if you only ever have the seed, you'll + never know what 12-word phrase produced it. **Save the phrase + itself**, not just the resulting seed. + +Either way the resulting Ed25519 identity is exactly the same shape; +peers can't tell which word count you used. The choice is purely about +your backup ergonomics. + +#### ⚠ Not compatible with hardware-wallet derivations + +A KEZ 12-word phrase **does not** produce the same Bitcoin or Ethereum +key as the same 12 words typed into a Ledger or MetaMask, and vice +versa. The reasons are deliberate: + +1. Other wallets feed the phrase through BIP-39's PBKDF2 to get a + 64-byte "seed", then run that through BIP-32 hierarchical + derivation at a coin-specific path. KEZ doesn't — it takes the + raw entropy and uses it directly (24-word case) or hashes it with + a domain tag (12-word case). +2. KEZ identities aren't part of a derivation tree. There's one + identity per phrase; there's no path component. + +That means: **don't paste your existing hardware-wallet recovery +phrase into KEZ** expecting to get a key that you've already seen. +It'll produce a *new* KEZ identity uncorrelated with anything else. + +Conversely: a KEZ phrase you saved is *only* useful for KEZ. A +malicious wallet that says "import this phrase" can't extract your +existing Bitcoin / Ethereum funds from a KEZ phrase, because the +phrase wasn't derived through the same path. + +#### Backing up — concrete advice + +The phrase is the master key to your identity. Practical guidance: + +- **Write it on paper, with a pencil. Number each word (1–12 or 1–24) + so you can later verify the order.** A photograph or cloud document + is one breach away from compromise. +- **Store the paper somewhere fireproof.** Safe-deposit boxes, lockable + desk drawers, etched-stainless-steel cards if you're paranoid. +- **Never type the phrase into a website, chat app, or password + manager that auto-syncs.** Local-only password managers (KeePassXC, + 1Password locked vault) are OK; cloud-synced managers are a softer + target. +- **Don't split it across two locations "for safety".** Half a BIP-39 + phrase weakens the entropy more than it protects against loss. If you + need redundancy, make two complete paper copies in different physical + locations. +- **Don't be cute.** Don't permute the words "because they're easy to + remember in this order." The wordlist position matters; reorder and + you change the key (and the BIP-39 checksum will reject it on + restore anyway). + +### Working with phrases later + +You can generate a fresh phrase without producing a key, or recover +the key from a phrase you wrote down earlier: + +```sh +# Print a fresh 24-word phrase (or 12, with --words 12). No key derived. +kez identity mnemonic +kez identity mnemonic --words 12 + +# Recover the Ed25519 key from a phrase. Word count auto-detected. +kez identity from-mnemonic "abandon ability able about above absent academy +accident account accuse achieve acid acoustic acquire across act action +actor actress actual adapt add addict address" +``` + +The recovered output is identical, byte-for-byte, to what was printed +when you first ran `identity new` — same `Primary:`, same `Public:`, +same `Secret:`. + Throughout the rest of this tutorial you can substitute `--mnemonic "your phrase here"` anywhere `--ed25519-seed ` appears. +Both are accepted on every command that takes a signing key. For the rest of this tutorial we'll use a nostr key for examples and write the secret as `nsec1FAKE...` — substitute your real one.