<p>Wallets created by Electrum 1.x have seeds containing 12 words (24 words is also possible for <a href="https://github.com/spesmilo/electrum/pull/1087" rel="nofollow">custom-created seeds</a>). Given a zero-based array of seed_words of that length, this pseudocode calculates the master_private_key:</p>

<pre><code>i = 0
while i &lt; length(seed_words):
    # convert each word into an int in the range [0,1625]
    # based on the word's position in the sorted word list
    seed_ints[i] = lookup_seed_word(seed_words[i])
    i = i + 1

num_words  = 1626
num_words2 = num_words * num_words
seed_hex_str = ""
i = 0
while i &lt; length(seed_words):
    # (hex8 converts an int into an ASCII string of
    # exactly 8 zero-padded lowercase hex digits;
    # % is the integer remainder operator
    seed_hex_str = seed_hex_str + hex8( seed_ints[i    ]
                    + num_words  * (   (seed_ints[i + 1] - seed_ints[i    ]) % num_words )
                    + num_words2 * (   (seed_ints[i + 2] - seed_ints[i + 1]) % num_words ))
    i = i + 3

unstretched_seed = ascii_string_to_byte_array(seed_hex_str)
seed = byte_array()  # an empty byte array
i = 0
while i &lt; 100000:
    # sha256 operates on and produces byte arrays
    seed = sha256(seed + unstretched_seed)
    i = i + 1

master_private_key = byte_array_to_int(seed, order=big_endian)
</code></pre>

<p>In case you're wondering, it seems that the reason the seed_ints calculation looks overly complex may be to avoid <a href="https://github.com/spesmilo/electrum/blob/1b6abf6e028cbabd5e125784cff6d4ada665e722/lib/old_mnemonic.py#L1657-L1658" rel="nofollow">infringing on a patent</a>.</p>

<p>For comparison, wallets created by Electrum 2.x typically have seeds containing 13 words, however they will on occasion have fewer. (It's technically possible to construct seeds of nearly any length which will be accepted when restoring an Electrum 2.x wallet.) Here's the pseudocode which calculates a BIP-32 extended master private key:</p>

<pre><code># Electrum 2.x doesn't separate mnemonic words with spaces in sentences for any CJK
# scripts when calculating the checksum or deriving a binary seed (even though this
# seems inappropriate for some CJK scripts such as Hiragana as used by the ja wordlist)
if language is CJK:
    space = ""
else:
    space = " "

seed_phrase = ""
i = 0
do:
    word = seed_words[i]
    normalize_unicode(word, normalization=nfkd)
    remove_unicode_combining_marks(word)  # e.g. accent marks
    seed_phrase = seed_phrase + word
    i = i + 1
    if i ≥ length(seed_words):
        exit-loop
    seed_phrase = seed_phrase + space

seed_utf8 = unicode_to_byte_array(seed_phrase, format=utf8)

if hmac_sha512(key="Seed version", message=seed_utf8)[0] ≠ 1:
    fail("invalid checksum")

stretched_seed = pbkdf2_hmac_sha512(password=seed_utf8, salt="electrum", iterations=2048, output_length=64)
seed_bytes = hmac_sha512(key="Bitcoin seed", message=stretched_seed)

private_key        = byte_array_to_int(seed_bytes[0..31], order=big_endian)
chain_code_bytes   = seed_bytes[32..63]
master_private_key = create_bip32_extended_private_key(private_key, chain_code_bytes)
</code></pre>

<p><a href="https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki" rel="nofollow">BIP-39</a>, an alternative derivation technique, is similar to Electrum 2.x but not identical.</p>

CC BY-SA 3.0
2015-04-30T22:52:00.840Z
"seed = sha256(seed + unstretched_seed)" or seed = sha256(seed) + sha256(unstretched_seed))" ? Im unaware of a SHA256 method that takes two byte arrays.
Mr Jones
CC BY-SA 3.0
2015-04-30T23:05:18.600Z
You're right, it doesn't. The '+' was intended to convey concatenation. IOW, the original unstretched_seed is appended to the result of the last sha256 before its hash is taken in every iteration. For a more concrete example, see here: https://github.com/gurnec/btcrecover/blob/5d5432182f95def45929f91f8b9a5daa2ad21ba9/seedrecover.py#L391-L392
Christopher Gurnee
CC BY-SA 3.0
2015-05-01T13:14:41.737Z
@MrJones FYI I fixed a mistake in the Electrum 1.x pseudocode: num_words was wrong. Sorry about that.
Christopher Gurnee
CC BY-SA 3.0
2015-05-02T10:47:45.060Z
I confirm the patent-avoidance thing
ThomasV
CC BY-SA 3.0
2015-04-30T13:52:56.673Z
2015-05-01T13:13:20.617Z
2015-05-01T13:13:20.617Z
Christopher Gurnee
<p>Electrum uses a seed of 12 words to generate a keypair and then hierarchically generates addresses out of that keypair.</p>

<p>I know how to generate hierarchic addresses from a keypair, but what I don't understand is how Electrum generates a keypair from the seed. I understand the seed consists of words coming from a 2048 length wordlist, but after that it is unclear to me how it starts to generate the sequence of addresses.</p>

<hr>

<p>I'd like to do the HD generation in either Java / C++ / Python or C#.
If I can understand the process, I might be able to reproduce it myself and generate hierarchical addresses from a seed.</p>

<hr>

<p>EDIT:</p>

<p>A day later I find this Java implementation... Thanks for the answers!</p>

<p><a href="https://github.com/harningt/atomun-mnemonic" rel="noreferrer">https://github.com/harningt/atomun-mnemonic</a></p>

<electrum><seed><bip32-hd-wallets>
How does Electrum make a keypair out of a seed?
Mr Jones
# How does Electrum make a keypair out of a seed?

(Asked by: username_0 on 2015-04-30)

Electrum uses a seed of 12 words to generate a keypair and then hierarchically generates addresses out of that keypair.

I know how to generate hierarchic addresses from a keypair, but what I don't understand is how Electrum generates a keypair from the seed. I understand the seed consists of words coming from a 2048 length wordlist, but after that it is unclear to me how it starts to generate the sequence of addresses.

---

I'd like to do the HD generation in either Java / C++ / Python or C#.
If I can understand the process, I might be able to reproduce it myself and generate hierarchical addresses from a seed.

---

EDIT:

A day later I find this Java implementation... Thanks for the answers!

<https://github.com/harningt/atomun-mnemonic>

**Tags:** electrum, seed, bip32-hd-wallets


---

## 1 answers

### Answer by username_1 on 2015-04-30. Score: 5 | Accepted answer

Wallets created by Electrum 1.x have seeds containing 12 words (24 words is also possible for [custom-created seeds](https://github.com/spesmilo/electrum/pull/1087)). Given a zero-based array of seed\_words of that length, this pseudocode calculates the master\_private\_key:

```
i = 0
while i < length(seed_words):
    # convert each word into an int in the range [0,1625]
    # based on the word's position in the sorted word list
    seed_ints[i] = lookup_seed_word(seed_words[i])
    i = i + 1

num_words  = 1626
num_words2 = num_words * num_words
seed_hex_str = ""
i = 0
while i < length(seed_words):
    # (hex8 converts an int into an ASCII string of
    # exactly 8 zero-padded lowercase hex digits;
    # % is the integer remainder operator
    seed_hex_str = seed_hex_str + hex8( seed_ints[i    ]
                    + num_words  * (   (seed_ints[i + 1] - seed_ints[i    ]) % num_words )
                    + num_words2 * (   (seed_ints[i + 2] - seed_ints[i + 1]) % num_words ))
    i = i + 3

unstretched_seed = ascii_string_to_byte_array(seed_hex_str)
seed = byte_array()  # an empty byte array
i = 0
while i < 100000:
    # sha256 operates on and produces byte arrays
    seed = sha256(seed + unstretched_seed)
    i = i + 1

master_private_key = byte_array_to_int(seed, order=big_endian)

```

In case you're wondering, it seems that the reason the seed\_ints calculation looks overly complex may be to avoid [infringing on a patent](https://github.com/spesmilo/electrum/blob/1b6abf6e028cbabd5e125784cff6d4ada665e722/lib/old_mnemonic.py#L1657-L1658).

For comparison, wallets created by Electrum 2.x typically have seeds containing 13 words, however they will on occasion have fewer. (It's technically possible to construct seeds of nearly any length which will be accepted when restoring an Electrum 2.x wallet.) Here's the pseudocode which calculates a BIP-32 extended master private key:

```
# Electrum 2.x doesn't separate mnemonic words with spaces in sentences for any CJK
# scripts when calculating the checksum or deriving a binary seed (even though this
# seems inappropriate for some CJK scripts such as Hiragana as used by the ja wordlist)
if language is CJK:
    space = ""
else:
    space = " "

seed_phrase = ""
i = 0
do:
    word = seed_words[i]
    normalize_unicode(word, normalization=nfkd)
    remove_unicode_combining_marks(word)  # e.g. accent marks
    seed_phrase = seed_phrase + word
    i = i + 1
    if i ≥ length(seed_words):
        exit-loop
    seed_phrase = seed_phrase + space

seed_utf8 = unicode_to_byte_array(seed_phrase, format=utf8)

if hmac_sha512(key="Seed version", message=seed_utf8)[0] ≠ 1:
    fail("invalid checksum")

stretched_seed = pbkdf2_hmac_sha512(password=seed_utf8, salt="electrum", iterations=2048, output_length=64)
seed_bytes = hmac_sha512(key="Bitcoin seed", message=stretched_seed)

private_key        = byte_array_to_int(seed_bytes[0..31], order=big_endian)
chain_code_bytes   = seed_bytes[32..63]
master_private_key = create_bip32_extended_private_key(private_key, chain_code_bytes)

```

[BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki), an alternative derivation technique, is similar to Electrum 2.x but not identical.

> **Comments:**
> **username_0**: "seed = sha256(seed + unstretched_seed)" or seed = sha256(seed) + sha256(unstretched_seed))" ? Im unaware of a SHA256 method that takes two byte arrays.  
> **username_1**: You're right, it doesn't. The '+' was intended to convey concatenation. IOW, the original unstretched_seed is appended to the result of the last sha256 before its hash is taken in every iteration. For a more concrete example, see here: https://github.com/gurnec/btcrecover/blob/5d5432182f95def45929f91f8b9a5daa2ad21ba9/seedrecover.py#L391-L392  
> **username_1**: @username_0 FYI I fixed a mistake in the Electrum 1.x pseudocode: num_words was wrong. Sorry about that.  
> **username_2**: I confirm the patent-avoidance thing  

---

