Skip to content
This repository has been archived by the owner on Feb 14, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from LedgerHQ/cleanup
Browse files Browse the repository at this point in the history
Cleanup project
  • Loading branch information
jibeee authored Mar 23, 2022
2 parents 0da233f + 6b8b5b4 commit 2dd8a94
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 110 deletions.
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
FROM python:alpine3.7
FROM python:3.10-slim

COPY . /app
WORKDIR /app
RUN apt update && apt upgrade && apt install -y \
build-essential
RUN pip install -r requirements.txt
CMD python ./recoverMasterSeed.py
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
# Vault Seed Recovery

## Build

`docker build -t vault_seed_recovery .`

## Execute

`docker run -ti --rm vault_seed_recovery`

## Description

Master Seed Root Key is provided in xprv format.
It can be used as input in tools such as: https://iancoleman.io/bip39/
It can be used as input in tools such as: <https://iancoleman.io/bip39/>

Bitcoin account #0 is also provided for convenience, it can be used in tools such as https://electrum.org/ (Standard Wallet > Use a master key).
Bitcoin account #0 is also provided for convenience, it can be used in tools such as <https://electrum.org/> (Standard Wallet > Use a master key).

Note: Ledger vault derives a 512bits seed from shared owners seed fragments. Due to how bip39 is designed it is not possible to go from a seed to its mnemonic representation. As a consequence it is not possible to import this seed in a Ledger Nano S/X.

![Bip39 Bip32 Seed](Bip39-Bip32-seed.png)

![Vault Master Seed Derivation](Vault-Master-Seed.png)


## Example
```

```shell
$ docker run -ti --rm vault_seed_recovery
Input each Shared Owner recovery mnemonic (24 words) when requested. End with an empty line.
Shared Owner 1 recovery mnemonic : current mesh depth easily predict gloom find neither come cash useful chief lesson picnic purse hollow pave arena oven quality sight final master bright
Expand All @@ -32,5 +35,4 @@ Shared Owner 4 recovery mnemonic :
Master Seed: 606ef87de7809139edbb179de349f0ad31de1aae3fcaea95a1621c48861a64651e1429d0efd862254b014c3941bc67867bd9bf69a07f308a649774552381536b
Master Seed Root Key: xprv9s21ZrQH143K4CPHGVWv5aK15ugeH3KX6SEYDE8fELboo6GXvjuYzXecq794aGxbqBUXevGzZxzqsLS9Kirsk4Y3PABhXD3XiSoqJdBz4EX
Bitcoin account #0 xprv: xprv9xshLXhiFuKV1JYQp6qwPLY1mVPBB4ZQ5dqYw7tcdsDCv5u3Gfqjr7LbHgXhUnTPxkUNW1gXM6C6irDeC9PtHa87dhz12Q1rU6C36nStv8W
```
68 changes: 0 additions & 68 deletions bip32ecpy.py

This file was deleted.

76 changes: 43 additions & 33 deletions recoverMasterSeed.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,54 @@
from mnemonic.mnemonic import Mnemonic
from functools import reduce
from itertools import count
from bip32utils import BIP32Key, BIP32_HARDEN
from binascii import hexlify, unhexlify

# parameters
COMPONENTS = []
m = Mnemonic("english")
from bip_utils import Bip39MnemonicValidator, Bip32Secp256k1, Bip39SeedGenerator

VAULT_BIP32_PATH = "m/5655636'/4932953'"


# util
def xor(*argv):
return bytes(map((lambda x: reduce((lambda a, b: a ^ b), x)), zip(*argv)))

# get list of seeds
print("Input each Shared Owner recovery mnemonic (24 words) when requested. End with an empty line.")
for i in count():

def ask_seeds() -> list[bytes]:
components = []
print(
"Input each Shared Owner recovery mnemonic (24 words) when requested. End with an empty line."
)
for i in count():
while True:
words = input(f"Shared Owner {i+1} recovery mnemonic : ")
if len(words) == 0 or m.check(words):
break
print("Invalid 24 words")
if len(words) == 0:
words = input(f"Shared Owner {i + 1} recovery mnemonic: ")
if len(words) == 0 or Bip39MnemonicValidator().IsValid(words):
break
bip32Key = BIP32Key.fromEntropy(m.to_seed(words)).ChildKey(0x80564C54).ChildKey(0x804B4559)
COMPONENTS.append(bip32Key.PrivateKey() + bip32Key.ChainCode())
print("Invalid 24 words")
if len(words) == 0:
break

seed = Bip39SeedGenerator(words).Generate()
bip32_key = Bip32Secp256k1.FromSeedAndPath(seed, VAULT_BIP32_PATH)
private_key = bip32_key.PrivateKey().Raw().ToBytes()
chain_code = bip32_key.ChainCode().ToBytes()
components.append(private_key + chain_code)
return components

if len(COMPONENTS) == 0:

def main():
components = ask_seeds()
if len(components) == 0:
print("No shared owner recovery mnemonic provided.")
exit(-1)

# xor list of seeds
masterSeed = xor(*COMPONENTS)
print(f"Master Seed: {masterSeed.hex()}")

# Print master seed in xprv format
masterKey = BIP32Key.fromEntropy(masterSeed)
print(f"Master Seed Root Key: {masterKey.ExtendedKey()}")
btc0xprv = masterKey\
.ChildKey(44+BIP32_HARDEN)\
.ChildKey(0+BIP32_HARDEN)\
.ChildKey(0+BIP32_HARDEN)\
.ExtendedKey()
print(f"Bitcoin account #0 xprv: {btc0xprv}")
return

# xor list of seeds
master_seed = xor(*components)
print(f"Master Seed: {master_seed.hex()}")

# Print master seed in xprv format
master_key = Bip32Secp256k1.FromSeed(master_seed)
print(f"Master Seed Root Key: {master_key.PrivateKey().ToExtended()}")

btc0xprv = master_key.DerivePath("m/44'/0'/0'").PrivateKey().ToExtended()
print(f"Bitcoin account #0 xprv: {btc0xprv}")


if __name__ == "__main__":
main()
4 changes: 1 addition & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
pbkdf2==1.3
mnemonic==0.18
bip32utils==0.3.post4
bip-utils==2.3.0

0 comments on commit 2dd8a94

Please sign in to comment.