Skip to content

Fix Electrum v1 seed/key derivation and unify NFKD normalization - #13

Draft
edtubbs with Copilot wants to merge 14 commits into
0.1.5-dev-electrum-seedfrom
copilot/fix-dogecoin-seed-test
Draft

Fix Electrum v1 seed/key derivation and unify NFKD normalization#13
edtubbs with Copilot wants to merge 14 commits into
0.1.5-dev-electrum-seedfrom
copilot/fix-dogecoin-seed-test

Conversation

Copilot AI commented Feb 16, 2026

Copy link
Copy Markdown

Electrum v1 mnemonic support had several correctness issues: seed function applied stretch_key prematurely (seed should be raw decoded bytes), key derivation used compressed keys (v1 requires uncompressed), and normalization used a custom ASCII-only function instead of the existing utf8proc NFKD pipeline. All outputs verified against bip-utils ElectrumV1 class.

Seed derivation (src/bip39.c)

  • dogecoin_seed_from_electrum_v1_mnemonic: returns raw 16-byte decoded seed, no stretching — e.g. "hardly point goal..."8edad31a95e7d59f8837667510d75a4d
  • dogecoin_mnemonic_is_electrum_seed: returns 0/-1 (was 1/0), matching all other bip39 functions
  • Replaced electrum_prepare_seed_ascii + manual lowercase loops with single utf8proc_map() call using NFKD | CASEFOLD
  • strtokstrtok_r for OP-TEE (aarch64 TrustZone) compatibility
  • #define UTF8PROC_STATIC to fix __imp_utf8proc_map linker errors on Windows/MinGW

Key derivation (src/key.c)

  • electrum_v1_derive_privkey32: performs stretch_key internally (100k SHA256 on hex seed), derives child keys via double-SHA256 tweak on raw 64-byte MPK
  • Uses uncompressed pubkeys throughout (v1 requirement)

CLI (src/cli/such.c)

  • mnemonic_to_key -l: uncompressed WIF (prefix 6, not Q)
  • mnemonic_to_addresses -l: uncompressed pubkey P2PKH addresses

Tests

  • Seed decode vectors in bip39_tests.c, key/address vectors in key_tests.c (no overlap)
  • Both "hardly point goal..." and "like just love..." mnemonics verified against bip-utils
# Reference verification
from bip_utils import ElectrumV1MnemonicDecoder, ElectrumV1SeedGenerator, ElectrumV1
mnemonic = "hardly point goal hallway patience key stone difference ready caught listen fact"
entropy = ElectrumV1MnemonicDecoder().Decode(mnemonic)  # 8edad31a95e7d59f8837667510d75a4d
seed = ElectrumV1SeedGenerator(mnemonic).Generate()
e = ElectrumV1.FromSeed(seed)
e.GetPrivateKey(0, 0).ToWif()    # 6J...
e.GetPrivateKey(0, 0).PublicKey().ToAddress()  # D5H1b4A...

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

- Remove extra SHA256 pre-hash in dogecoin_seed_from_electrum_v1_mnemonic
  (Electrum v1 passes hex string bytes directly to stretch function)
- Fix electrum_v1_derive_privkey32 to use raw binary MPK bytes (64 bytes)
  instead of hex string (128 chars) and double SHA256 instead of single
- Add test vector: seed='8edad31a95e7d59f8837667510d75a4d' mnemonic=
  'hardly point goal hallway patience key stone difference ready caught
  listen fact'
- Update expected values for 'like just love...' test vector
- Add key derivation test for proper v1 12-word mnemonic

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix dogecoin_seed_from_electrum_v1_mnemonic and update test Fix Electrum v1 seed and key derivation to match original algorithm Feb 16, 2026
Copilot AI requested a review from edtubbs February 16, 2026 23:50
The seed IS the raw decoded value (e.g. 8edad31a95e7d59f8837667510d75a4d).
stretch_key() belongs in key derivation, not seed storage, matching
original Electrum v1 Python implementation.

Changes:
- dogecoin_seed_from_electrum_v1_mnemonic: returns raw decoded 16-byte
  seed directly, no stretching, no fallback for non-v1 mnemonics
- electrum_v1_derive_privkey32: now does stretch_key internally (convert
  16-byte seed to hex, 100k SHA256 iterations) before key derivation
- Remove unused electrum_v1_stretch from bip39.c
- Update all tests with correct expected seed values
- Derived keys unchanged (stretching just moved to correct location)

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title Fix Electrum v1 seed and key derivation to match original algorithm Fix Electrum v1: seed is raw decoded value, move stretch_key to key derivation Feb 17, 2026
Copilot AI and others added 2 commits February 17, 2026 03:06
Electrum v1 uses uncompressed public keys for both WIF encoding and
P2PKH address generation, as documented in the bip-utils reference:
  https://bip-utils.readthedocs.io/en/latest/bip_utils/electrum/electrum_v1.html

Changes:
- CLI mnemonic_to_key with -l: uncompressed WIF (starts with '6' not 'Q')
- CLI mnemonic_to_addresses with -l: uncompressed pubkey P2PKH addresses
- Test: add bip-utils reference vectors verifying addresses match exactly
- All output verified against pip install bip-utils ElectrumV1 class

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Address code review feedback: zero-initialize wif_payload to prevent
potential information leakage before setting version byte and privkey.

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title Fix Electrum v1: seed is raw decoded value, move stretch_key to key derivation Fix Electrum v1: raw seed, stretch in key derivation, uncompressed keys Feb 17, 2026
Verified against pip install bip-utils ElectrumV1 class:
  entropy: 00285dfe00285e0100285e0400285e07
  child key[0]: b9a83170bfc2a80219d3d4b789acf145...
  address[0]: DJqkqMLLkio821TrxpZxUDhVFaSDR7VeWz

All values match bip-utils reference exactly.

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
strtok is not available in the OP-TEE (TrustZone) aarch64 environment.
Replace with a simple pointer-walk tokenizer that splits on spaces
without depending on the C standard library's strtok.

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title Fix Electrum v1: raw seed, stretch in key derivation, uncompressed keys Fix Electrum v1 mnemonic: correct seed/key derivation, uncompressed keys, OP-TEE compat Feb 17, 2026
strtok_r is thread-safe and already available in OP-TEE (unlike strtok).
The repo already maps strtok_r to strtok_s for MSVC compatibility.

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title Fix Electrum v1 mnemonic: correct seed/key derivation, uncompressed keys, OP-TEE compat Fix Electrum v1 seed derivation, key encoding, and OP-TEE link error Feb 17, 2026
Verified against bip-utils:
- 12 words is a strict v1 requirement (ElectrumV1MnemonicConst.MNEMONIC_WORD_NUM == [12])
- ASCII lowercase == NFKD + lowercase for the English-only v1 wordlist
  (all 1626 words are pure ASCII, so NFKD is a no-op)

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title Fix Electrum v1 seed derivation, key encoding, and OP-TEE link error Fix Electrum v1: correct seed/key derivation, uncompressed encoding, OP-TEE link fix Feb 17, 2026
Copilot AI and others added 2 commits February 17, 2026 19:14
Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
- key_tests: Remove "alpha bravo" rejection test (covered in bip39_tests)
- key_tests: Remove raw seed hex assertions (covered in bip39_tests)
- key_tests: Keep unique key derivation and address tests
- bip39_tests: Remove duplicate "alpha bravo" with passphrase test

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title Fix Electrum v1: correct seed/key derivation, uncompressed encoding, OP-TEE link fix Fix Electrum v1 seed derivation, key derivation, and normalize API conventions Feb 17, 2026
Replace all utf8proc_NFKD() + manual lowercase loop patterns with a
single utf8proc_map() call using NFKD + CASEFOLD flags. Unicode
case-folding is a superset of lowercasing and handles it correctly.

Also removes the now-unused electrum_prepare_seed_ascii function.

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title Fix Electrum v1 seed derivation, key derivation, and normalize API conventions Fix Electrum v1 seed derivation, key derivation, and unify NFKD normalization Feb 17, 2026
@edtubbs
edtubbs force-pushed the 0.1.5-dev-electrum-seed branch from 9f23b1c to bcd590a Compare February 17, 2026 19:43
On Windows (MinGW), utf8proc.h decorates functions with
__declspec(dllimport) unless UTF8PROC_STATIC or UTF8PROC_EXPORTS
is defined. Since libdogecoin links utf8proc statically, bip39.c
needs UTF8PROC_STATIC to avoid __imp_utf8proc_map linker errors.

Fixes: undefined reference to `__imp_utf8proc_map` on x86_64-win
and i686-win CI builds.

Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com>
Copilot AI changed the title Fix Electrum v1 seed derivation, key derivation, and unify NFKD normalization Fix Electrum v1 seed/key derivation and unify NFKD normalization Feb 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants