0

I lost 3 words out 12 and not sure of the order of the rest besides the first 3, and the last one. I wrote a Python script with mnemonic and the crypto.com wallet library, it works, to my knowledge atleast i still havent found my seed yet, im up to about 70mil combinations the code has processed, but the issue is I wanna see if anyone can make it faster, or just help me in general please.

Code:

import time
import itertools
from itertools import chain,repeat,count,islice
from collections import Counter
from mnemonic import Mnemonic 
import json
from chainlibpy import Wallet
import sqlite3

Words2 = ["2048 mnemonic words"]
count = 0
found = False
mnemo = Mnemonic("english")

#Words I know, just not sure of position
Words2 = ['']

#Words I know
comos = ['word1', 'word2', 'word3']        
def listToString(s):
    listToStr = ' '.join([str(element) for element in s])
    return listToStr

def createdb(address=None, mn=None):

    con = sqlite3.connect("Combos.db")
    cur = con.cursor()
    cur.execute("create table if not exists Addresses (address INT PRIMARY KEY, mnemonic NTEXT)")
    cur.execute("INSERT INTO Addresses (address, mnemonic) VALUES ('{}', '{}')".format(address, mn))
    con.commit()
    con.close()

def checkAddy(phrase):
    global found
    add = "address"
   # testadd = "testaddressnolongerinuse"
    wallet = Wallet(phrase)
    if wallet.address == add:
        createdb(wallet.address, phrase)
        found = True
        print("found")
    
def randomizeWord():
    global count
    global testing
    global comos 
    
    randomw = random.sample(Words, 3)
    words2 = random.sample(Words2, len(Words2))
    comos.extend(words2)
    comos.extend(randomw)  
    comis = ["lastword"]
    comos.extend(comis)
    isValid = mnemo.check(listToString(comos))
    
    if isValid:
        checkAddy(listToString(comos))
    else:
        comos = ['word1', 'word2', 'word3']
    comos = ['word1', 'word2' 'word3']
    count = count +1
    print(str(count))
    
        
while found != True:
    randomizeWord()
    
1
  • How does such a scenario even happen?
    – HansBKK
    Mar 28 at 1:03

3 Answers 3

1

Since you have the checksum, and a majority of the words, I believe working with the binary or hex would be faster.

I don't know how much you know, or don't, so I'm going to start from the beginning, but I'm going to keep it simple by not including information that is not needed for this task.

A seed phrase is created by using a random number of some length, between 128 bits to 256 bits. This number is then hashed, and a portion of it is taken from the begining and extends the size of the original number.

These extra bits are the checksum.

For a seed phrase that is 12 words long, this checksum is 4 bits in length, and the original number is 128 bits long. For a total of 132 bits.

Eleven bits are needed to select a word.

Having the last word means, not only do you have the checksum, you have 7 other bits that precede it.

Your first three words being known, you also have the first 33 bits in order.

I do question if you mean you are missing the words 9, 10, and 11; or if these missing words are intended to be mixed in the assorted known words. In your code, your order your words as such:

[first 3 words] [5 words assorted order] [random 2 words] [last word]

If this is the case, I would

  1. convert my words into their numbers
  2. do a permutation on the known words (permutation will eliminate redundant checks)
  3. increment a number and append it for the missing section (include padding)
  4. append the first 7 bits from the last word
  5. hash for checksum and compare returned checksum to original checksum

If I have a match, I might log this seed. But I would prefer do a check for a balance at that time.

The reason I would log them (note below on why I wouldn't), and this is homework for you. I don't know how you went about going from seed to address when you created your address initially. I am not confident that you will get an address match if you stumbled upon your correct seed without this information. This is only because you might have used a different tool than you are now, which may have used a different derivation path. The tool you have in your code uses:

DEFAULT_DERIVATION_PATH = "m/44'/394'/0'/0/0"
DEFAULT_BECH32_HRP = "cro"

Source: https://github.com/crypto-org-chain/chainlibpy/blob/master/chainlibpy/wallet.py

Note: A permutation of 5 words from a list of 5 words will yield 120 iterations To get through 33 missing bits, you'll go through 8,589,934,592 rounds to finish that search space. For a grand total of 1,030,792,151,040 attempts at max, if it happened to be the last one, my computer should be able to finish that entire space in a night. This would be the reason why you should figure out how your seed words where converted to your address, on a bad day with long words, it could push into several terrabytes and take much longer for all those reads and writes to disk.

from hashlib import sha256
from binascii import unhexlify
from itertools import permutations

#Wordlist, ideally you'd save as a file and import
#wordlist = [2048 words]

#Settings
FIRST_THREE = ["word", "word", "word"]
KNOWN_FIVE = ["word", "word", "word", "word", "word"]
# I kept as list so I don't have to write a dynamic function to operate on lists or strings
LAST_WORD = ["word"]

#Returns a list of same length with strings of binary without the "0b" binary prefix
bin_formatter = lambda my_words: [format(wordlist.index(word), "011b") for word in my_words]

def main():
    #Initialize setup
    # 1. convert words into their numbers
    main_section = {}
    # First section can be joined because it will not change
    main_section["first_section"] = "".join(bin_formatter(FIRST_THREE))
    # 2. Handling the permutation now, will join on each iteration
    main_section["second_section"] = permutations(bin_formatter(KNOWN_FIVE), 5)
    # 1.3 Last section is split into the 7 bits and checksum bits
    main_section["last_section"] = {}
    _temp_last_word = bin_formatter(LAST_WORD)[0]
    main_section["last_section"]["primary_bits"], main_section["last_section"]["checksum"] = _temp_last_word[:7], _temp_last_word[7:]

    #Main loop over permutations
    for ss in main_section["second_section"]:    
        # 3. increment a number and append it for the missing section (include padding)
        for missing in range(8589934592):
            #Using format to exclude "0b" prefix; specifying to add enough 0's to make it 33 bits long, zeros are added on left side
            missing_binary = format(missing, "033b")
            # 4. append the first 7 bits from the last word
            binary_representation = main_section["first_section"] + "".join(ss) + missing_binary + main_section["last_section"]["primary_bits"]

            #Hash prep
            hex_representation = format(int(binary_representation, 2), "032x")
            computer_ready_bytes = unhexlify(hex_representation)

            # 5. hash for checksum and compare returned checksum to original checksum
            hash_result = sha256(computer_ready_bytes).hexdigest()
            #One hex symbol is equivalent to 4 bits, convert it to int then to binary with padding
            checksum_result = format(int(hash_result[0], 16), '04b')
            if main_section["last_section"]["checksum"] == checksum_result:
                #append to file
                #perform confirmation steps
                #whichever suits you

main()

Working Example, nearly identical code above, but with test case built-in. Wordlist is imported, I put a gist with the english wordlist with "\n" character stripped, place this file in same folder and execute. https://gist.github.com/Gerschel/030f703deab4d47500748b7958f5c17c

from hashlib import sha256
from binascii import unhexlify
from itertools import permutations

#Test words
#['vague', 'salute', 'start', 'title', 'opinion', 'fancy', 'skate', 'arrange', 'meadow', 'absent', 'segment', 'blush']
#Missing target
target = ["meadow", "absent", "segment"]
#Test bits
#'11110000101101111101101101010010111100010111100110110110101001011111001010000000011000101000100110100000000101110000110010001100'
#Test checksum
#'0100'
#Test full bits
#'111100001011011111011011010100101111000101111001101101101010010111110010100000000110001010001001101000000001011100001100100011000100'
#Wordlist, ideally you'd save as a file and import
from english import wordlist

#Settings
FIRST_THREE = ["vague", "salute", "start"]
KNOWN_FIVE = ["title", "opinion", "fancy", "skate", "arrange"]
# I kept as list so I don't have to write a dynamic function to operate on lists or strings
LAST_WORD = ["blush"]

#Returns a list of same length with strings of binary without the "0b" binary prefix
bin_formatter = lambda my_words: [format(wordlist.index(word), "011b") for word in my_words]

def main():
    #Initialize setup
    # 1. convert words into their numbers
    main_section = {}
    # First section can be joined because it will not change
    main_section["first_section"] = "".join(bin_formatter(FIRST_THREE))
    # 2. Handling the permutation now, will join on each iteration
    main_section["second_section"] = permutations(bin_formatter(KNOWN_FIVE), 5)
    # 1.3 Last section is split into the 7 bits and checksum bits
    main_section["last_section"] = {}
    _temp_last_word = bin_formatter(LAST_WORD)[0]
    main_section["last_section"]["primary_bits"], main_section["last_section"]["checksum"] = _temp_last_word[:7], _temp_last_word[7:]

    #Main loop over permutations
    found = False
    for ss in main_section["second_section"]:

        # 3. increment a number and append it for the missing section (include padding)
        for missing in range(4615000000,4619000000):
            #Using format to exclude "0b" prefix; specifying to add enough 0's to make it 33 bits long, zeros are added on left side
            missing_binary = format(missing, "033b")
            # 4. append the first 7 bits from the last word
            binary_representation = main_section["first_section"] + "".join(ss) + missing_binary + main_section["last_section"]["primary_bits"]

            #Hash prep
            hex_representation = format(int(binary_representation, 2), "032x")
            computer_ready_bytes = unhexlify(hex_representation)

            # 5. hash for checksum and compare returned checksum to original checksum
            hash_result = sha256(computer_ready_bytes).hexdigest()
            #One hex symbol is equivalent to 4 bits, convert it to int then to binary with padding
            checksum_result = format(int(hash_result[0], 16), '04b')
            if main_section["last_section"]["checksum"] == checksum_result:
                res_words = []
                bits = binary_representation + checksum_result
                for e in range(0, len(bits), 11):
                    res_words.append(wordlist[int(bits[e: e+11], 2)])
                if res_words[8:11] == target:
                    found = True
                    break
                print(f"Match: {res_words[8:11] == target}\t\t {res_words[8:11]}", end="\r")
        if found:
            print(res_words)
            break

main()
0

I wanna see if anyone can make it faster

I suggest one of

  • Ask about optimising your Python code on a programming Q&A website such as stackoverflow.com or codereview.stackexchange.com. Optimising python is more their thing and doesn't necessarily require any Bitcoin knowledge. First read their tour/help pages though to make sure your question is composed to meet the remit of those websites.
  • Compare your code with well-established code that does exactly the same but which has likely been far better optimised over many years of widespread use. For example btcrecover. That seems to also be written in Python.
0

I wrote a similar script in python3 to help with seed recovery. You can check it out here. It has an interactive prompt to allow you to input words, however, it requires you know the placement of the words you do know. If you have 9/12 words and zero knowledge of the 9 words' placement (order), the odds of brute forcing the seed are very slim. Best of luck!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.