1.) Is it possible to recover part of private key, when I have valid address?
For example:
let's say one of my paper wallet is partialy destroyed, and I have lost 5 or more characters from private, like:
pub: 1DrqrF3JEYB9TXWymz4zGoEzM5i75JMLFB
priv: L3Wji4b***4Po3vo2gmi42Y2kbbxtnpAfQ5wJx11BgNr****8v3Q
It is possible as long as exact position of lost characters & it's public key/address are known since 58^5 (656.356.768) combination is small number for GPU these days. But AFAIK this software don't support such feature.
This software does support such feature.
(post moved from bitcointalk.org/index.php?topic=5112311.msg51532763#msg51532763)
FAQ, How to recover a bitcoin PaperWallet using BitCrack.We need a program
github.com/brichard19/BitCrack
Our task is to correctly specify the options "--keyspace" and "--stride".
Show how to recover
17GD2bc5b7SvU7bzN7CYUSc6h4zBJ36Rx6
5KVzbojiLkuTHwJQsKr6W9prrHx4XYfzjwsstfqjdembMEUsY42
dddb314fa4915afbcc75edc4a8d37de912dd9de86e9b0ba365eca5c0d676e50c
1) Calculate "--keyspace"
The private key of the paper wallet is in WIF format (base58check encoding).
We need to convert it to HEX format (base16 encoding)
For this we will use an online converter, for example
brainwalletx.github.io/#converter
for
5KVzbojiLku?????sKr6W9prrHx4XYfzjwsstfqjdembMEUsY42
the min range key will be(1)
5KVzbojiLku11111sKr6W9prrHx4XYfzjwsstfqjdembMEUsY42
the min range key +1 will be(2)
5KVzbojiLku11112sKr6W9prrHx4XYfzjwsstfqjdembMEUsY42
the max range key will be(3)
5KVzbojiLkuzzzzzsKr6W9prrHx4XYfzjwsstfqjdembMEUsY42
Request the key(1) into the converter
"Source Text"->"5KVzbojiLku11111sKr6W9prrHx4XYfzjwsstfqjdembMEUsY42"
"Source Encoding"->"Base58"
Response
"Convert To"->"Hex"
"Result"->"80dddb314fa49158b8a43cae43cc017003a9ce0b2c43b407c4cbbb533d532a64a4186b7dab"
Save the number(1) in a notepad
80dddb314fa49158b8a43cae43cc017003a9ce0b2c43b407c4cbbb533d532a64a4186b7dab
Request the key(2) into the converter
"Source Text"->"5KVzbojiLku11112sKr6W9prrHx4XYfzjwsstfqjdembMEUsY42"
"Source Encoding"->"Base58"
Response
"Convert To"->"Hex"
"Result"->"80dddb314fa49158b8a43cceec129f5caa5f74de940f749841d3a1f8959aa342cc186b7dab"
Save the number(2) in a notepad
80dddb314fa49158b8a43cceec129f5caa5f74de940f749841d3a1f8959aa342cc186b7dab
Request the key(3) into the converter
"Source Text"->"5KVzbojiLkuzzzzzsKr6W9prrHx4XYfzjwsstfqjdembMEUsY42"
"Source Encoding"->"Base58"
Response
"Convert To"->"Hex"
"Result"->"80dddb314fa4915db64236699367f258926fcbd827398fc02deccb5d58bc12a77c186b7dab"
Save the number(3) in a notepad
80dddb314fa4915db64236699367f258926fcbd827398fc02deccb5d58bc12a77c186b7dab
We remove the left 2 digits ("80" is a bitcoin network label)
(1')dddb314fa49158b8a43cae43cc017003a9ce0b2c43b407c4cbbb533d532a64a4186b7dab
(2')dddb314fa49158b8a43cceec129f5caa5f74de940f749841d3a1f8959aa342cc186b7dab
(3')dddb314fa4915db64236699367f258926fcbd827398fc02deccb5d58bc12a77c186b7dab
Save the number(1') and the number(2') (they are needed to get "--stride")
We remove the right 8 digits ("186b7dab" - this is the checksum base58check)
(1'')dddb314fa49158b8a43cae43cc017003a9ce0b2c43b407c4cbbb533d532a64a4
(2'') for "--keyspace" number(2'') is not needed, skip
(3'')dddb314fa4915db64236699367f258926fcbd827398fc02deccb5d58bc12a77c
ATTENTION!
If your WIF key starts with "5" - this is an uncompressed key, and you can skip this warning.
If your WIF key starts with "L" or "K" - this is a compressed key, read the warning.
WIF "L"/"K" compressed keys are labeled "01" before the checksum - this also needs to be removed.
Ie, we remove the right 10 digits ("01" + 8 digit checksum base58check)
In fact, the number(1'') and number(3'') is the range of candidates for private keys in HEX.
Then the option "--keyspace (1''):(3'')" like
--keyspace dddb314fa49158b8a43cae43cc017003a9ce0b2c43b407c4cbbb533d532a64a4:dddb314fa4915db64236699367f258926fcbd827398fc02deccb5d58bc12a77c
2) Calculate "--stride".
Convert number(1') and number(2') to DEC (base10 encoding)
Request the key(1') into the converter
"Source Text"->"dddb314fa49158b8a43cae43cc017003a9ce0b2c43b407c4cbbb533d532a64a4186b7dab"
"Source Encoding"->"Hex"
Response
"Convert To"->"Dec"
"Result"->"4309931794287609088053274827537810492913374270149990123844761122944746242931016
15414699"
Save the number(1''') in a notepad
4309931794287609088053274827537810492913374270149990123844761122944746242931016
15414699
Request the key(2') into the converter
"Source Text"->"dddb314fa49158b8a43cceec129f5caa5f74de940f749841d3a1f8959aa342cc186b7dab"
"Source Encoding"->"Hex"
Response
"Convert To"->"Dec"
"Result"->"4309931794287609088053275352320830782839823951879774905331246684460802851879681
94018731"
Save the number(2''') in a notepad
4309931794287609088053275352320830782839823951879774905331246684460802851879681
94018731
Why do we need the (1''') and (2''') in DEC? We need the difference (2')-(1') (not all calculators can count in hex)
For this, we will use an online high precision calculator, for example
www.ttmath.org/online_calculatorsubtract from the larger (2''') the smaller (1'''),
430993179428760908805327535232083078283982395187977490533124668446080285187968194018731
-
430993179428760908805327482753781049291337427014999012384476112294474624293101615414699
=
The result is:
5.2478302028992644968172978478148648556151605660894866578604032e+61
i.e
52478302028992644968172978478148648556151605660894866578604032
Convert to HEX
Request into the converter
"Source Text"->"52478302028992644968172978478148648556151605660894866578604032"
"Source Encoding"->"Dec"
Response
"Convert To"->"Hex"
"Result"->"20a8469deca6b5a6d367cbc0907d07e6a5584778de2800000000"
If your WIF key starts with "5" - we remove 8 digits from the right.
If your WIF key starts with "L" or "K" - we remove 10 numbers from the right.
20a8469deca6b5a6d367cbc0907d07e6a5584778de28
now option "--stride" as
--stride 20a8469deca6b5a6d367cbc0907d07e6a5584778de28
3) Run BitCrack and restore PrivateKey
D:\BitCrack\0.30>cuBitCrack -u --keyspace dddb314fa49158b8a43cae43cc017003a9ce0b2c43b407c4cbbb533d532a64a4:dddb314fa4915db64236699367f258926fcbd827398fc02deccb5d58bc12a77c --stride 20a8469deca6b5a6d367cbc0907d07e6a5584778de28 17GD2bc5b7SvU7bzN7CYUSc6h4zBJ36Rx6
[2019-06-20.09:48:12] [Info] Compression: uncompressed
[2019-06-20.09:48:12] [Info] Starting at: DDDB314FA49158B8A43CAE43CC017003A9CE0B2C43B407C4CBBB533D532A64A4
[2019-06-20.09:48:12] [Info] Ending at: DDDB314FA4915DB64236699367F258926FCBD827398FC02DECCB5D58BC12A77C
[2019-06-20.09:48:12] [Info] Counting by: 0000000000000000000020A8469DECA6B5A6D367CBC0907D07E6A5584778DE28
[2019-06-20.09:48:12] [Info] Initializing GeForce GTX 980
[2019-06-20.09:48:12] [Info] Generating 262,144 starting points (10.0MB)
[2019-06-20.09:48:12] [Info] 10.0%
[2019-06-20.09:48:12] [Info] 20.0%
[2019-06-20.09:48:12] [Info] 30.0%
[2019-06-20.09:48:12] [Info] 40.0%
[2019-06-20.09:48:12] [Info] 50.0%
[2019-06-20.09:48:12] [Info] 60.0%
[2019-06-20.09:48:12] [Info] 70.0%
[2019-06-20.09:48:13] [Info] 80.0%
[2019-06-20.09:48:13] [Info] 90.0%
[2019-06-20.09:48:13] [Info] 100.0%
[2019-06-20.09:48:13] [Info] Done
GeForce GTX 980 492 / 8192MB | 1 target 69.95 MKey/s (253,493,248 total)
[00:00:01][2019-06-20.09:48:17] [Info] Address: 17GD2bc5b7SvU7bzN7CYUSc6h4zBJ36Rx6
Private key: DDDB314FA4915AFBCC75EDC4A8D37DE912DD9DE86E9B0BA365ECA5C0D676E50C
Compressed: no
Public key:
566465A2FA23C1983ED1968029E48C318ACF6FEB6B9A949B33FACA7E7803F03B
6F514ADC66D41F5A31E6187FD8F3419601F22A930B9BE7204E9BCC551C81A836
[2019-06-20.09:48:17] [Info] No targets remaining
4) BitCrack Performance Tips.
BitCrack may not optimally calculate the -t and -p parameters (halving the speed).
run with the option "--continue sess.txt", any address, for example
xxBitCrack --continue sess.txt 17GD2bc5b7SvU7bzN7CYUSc6h4zBJ36Rx6
wait a little longer than a minute until the file of saving the progress will not be created, turn off;
open sess.txt, for example, "threads = 256" (this is the -t option) and "points = 32" (this is the -p option)
try to double the value, the speed should double; try double again..
remember them and always add when running BitCrack, for example
xxBitCrack -t 512 -p 1024 17GD2bc5b7SvU7bzN7CYUSc6h4zBJ36Rx6
5) Repeat the calculations for the compressed key
19BoCbK9SMQ2GeHRo5h5HFMDXsWAe5DJu4
L4eyEdHXUCnX3kfrM8BhHfxF2Zp2Zpo7rZDZd4S7AmhpcFvTBi3o
dddb314fa4915afbcc75edc4a8d37de912dd9de86e9b0ba365eca5c0d676e50c
for
L4eyEd?????X3kfrM8BhHfxF2Zp2Zpo7rZDZd4S7AmhpcFvTBi3o
the min range key will be(1)
L4eyEd11111X3kfrM8BhHfxF2Zp2Zpo7rZDZd4S7AmhpcFvTBi3o
the min range key +1 will be(2)
L4eyEd11112X3kfrM8BhHfxF2Zp2Zpo7rZDZd4S7AmhpcFvTBi3o
the max range key will be(3)
L4eyEdzzzzzX3kfrM8BhHfxF2Zp2Zpo7rZDZd4S7AmhpcFvTBi3o
"Base58"->"Hex"
(1)80dddb31430a12ceca48afa7302db7c16d048111b4490ef5aba1c538656e935576019027ecb6
(2)80dddb31430a12cfebbe7a4306570c1dbb22845e0aa8ec5e2fc70d1c25a48956f0019027ecb6
(3)80dddb316f464e913be304a3096441652fbe6bb3ee73e4b2dd88117af6a472823c019027ecb6
(1')dddb31430a12ceca48afa7302db7c16d048111b4490ef5aba1c538656e935576019027ecb6
(2')dddb31430a12cfebbe7a4306570c1dbb22845e0aa8ec5e2fc70d1c25a48956f0019027ecb6
(3')dddb316f464e913be304a3096441652fbe6bb3ee73e4b2dd88117af6a472823c019027ecb6
(1'')dddb31430a12ceca48afa7302db7c16d048111b4490ef5aba1c538656e935576
(2'') for "--keyspace" number(2'') is not needed, skip
(3'')dddb316f464e913be304a3096441652fbe6bb3ee73e4b2dd88117af6a472823c
--keyspace dddb31430a12ceca48afa7302db7c16d048111b4490ef5aba1c538656e935576:dddb316f464e913be304a3096441652fbe6bb3ee73e4b2dd88117af6a472823c
(1')"Hex"->"Dec"
"Source Text"->"dddb31430a12ceca48afa7302db7c16d048111b4490ef5aba1c538656e935576019027ecb6"
"Result"->"1103342535601602776525285853177912161262361522527439740718572909944271817096236
43576200374"
(2')"Hex"->"Dec"
"Source Text"->"dddb31430a12cfebbe7a4306570c1dbb22845e0aa8ec5e2fc70d1c25a48956f0019027ecb6"
"Result"->"1103342535601602796503089304906835914569619353461138982851022867509637813984017
46502937782"
(2')-(1')
1103342535601602796503089304906835914569619353461138982851022867509637813984017
46502937782
-
1103342535601602776525285853177912161262361522527439740718572909944271817096236
43576200374
=
The result is:
1.997780345172892375330725783093369924213244995756536599688778102926737408e+72
i.e
1997780345172892375330725783093369924213244995756536599688778102926737408
"Source Text"->"1997780345172892375330725783093369924213244995756536599688778102926737408"
"Dec"->"Hex"
"Result"->"012175ca9bd629545c4e1e034c565fdd68842547e3c035f6017a0000000000"
012175ca9bd629545c4e1e034c565fdd68842547e3c035f6017a
--stride 012175ca9bd629545c4e1e034c565fdd68842547e3c035f6017a
D:\BitCrack\0.30>cuBitCrack -c --keyspace dddb31430a12ceca48afa7302db7c16d048111b4490ef5aba1c538656e935576:dddb316f464e913be304a3096441652fbe6bb3ee73e4b2dd88117af6a472823c --stride 012175ca9bd629545c4e1e034c565fdd68842547e3c035f6017a 19BoCbK9SMQ2GeHRo5h5HFMDXsWAe5DJu4
[2019-06-20.10:12:55] [Info] Compression: compressed
[2019-06-20.10:12:55] [Info] Starting at: DDDB31430A12CECA48AFA7302DB7C16D048111B4490EF5ABA1C538656E935576
[2019-06-20.10:12:55] [Info] Ending at: DDDB316F464E913BE304A3096441652FBE6BB3EE73E4B2DD88117AF6A472823C
[2019-06-20.10:12:55] [Info] Counting by: 000000000000012175CA9BD629545C4E1E034C565FDD68842547E3C035F6017A
[2019-06-20.10:12:55] [Info] Initializing GeForce GTX 980
[2019-06-20.10:12:56] [Info] Generating 262,144 starting points (10.0MB)
[2019-06-20.10:12:56] [Info] 10.0%
[2019-06-20.10:12:56] [Info] 20.0%
[2019-06-20.10:12:56] [Info] 30.0%
[2019-06-20.10:12:56] [Info] 40.0%
[2019-06-20.10:12:56] [Info] 50.0%
[2019-06-20.10:12:56] [Info] 60.0%
[2019-06-20.10:12:56] [Info] 70.0%
[2019-06-20.10:12:56] [Info] 80.0%
[2019-06-20.10:12:56] [Info] 90.0%
[2019-06-20.10:12:57] [Info] 100.0%
[2019-06-20.10:12:57] [Info] Done
GeForce GTX 980 492 / 8192MB | 1 target 78.25 MKey/s (141,557,760 total)
[00:00:00][2019-06-20.10:12:59] [Info] Address: 19BoCbK9SMQ2GeHRo5h5HFMDXsWAe5DJu4
Private key: DDDB314FA4915AFBCC75EDC4A8D37DE912DD9DE86E9B0BA365ECA5C0D676E50C
Compressed: yes
Public key:
02566465A2FA23C1983ED1968029E48C318ACF6FEB6B9A949B33FACA7E7803F03B
[2019-06-20.10:12:59] [Info] No targets remaining
#######################
python3 script, automate calc --keyspace and --stride
#python3
import sys
#######################
def int_to_unknown_bytes(num, byteorder='big'):
"""Converts an int to the least number of bytes as possible."""
return num.to_bytes((num.bit_length() + 7) // 8 or 1, byteorder)
BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
BASE58_ALPHABET_LIST = list(BASE58_ALPHABET)
BASE58_ALPHABET_INDEX = {char: index for index, char in enumerate(BASE58_ALPHABET)}
def b58decode(string):
alphabet_index = BASE58_ALPHABET_INDEX
num = 0
try:
for char in string:
num *= 58
num += alphabet_index[char]
except KeyError:
raise ValueError('"{}" is an invalid base58 encoded '
'character.'.format(char))
bytestr = int_to_unknown_bytes(num)
pad = 0
for char in string:
if char == '1':
pad += 1
else:
break
return b'\x00' * pad + bytestr
#######################
# Main
if __name__ == '__main__':
print('########')
if len(sys.argv)<2 :
print('[usage] BitCrackPaperWalletRecovery.py 5KYZdUEo39z*****uX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss')
exit(1)
privkey0 = sys.argv[1];
print('[WIFprivkey] {}'.format(privkey0));
if len(sys.argv[1]) < 50 or len(sys.argv[1]) > 52 :
print('[error] wrong len(50..52)');
exit(1);
if privkey0.find("5",0,1) == 0:
#print("[compress] off")
flag_compress = False
elif privkey0.find("L",0,1) == 0 or privkey0.find("K",0,1) == 0 :
#print("[compress] on")
flag_compress = True
else:
print("[error] wif privkey should start with 5,K,L")
exit(1);
marker = privkey0.rfind("*")
if marker == -1 :
print("[error] marker(*) not found" )
exit(1);
print('########')
privkey1 = privkey0.replace("*","1")
print("(1)[minimal] {}".format(privkey1.upper()));
privkey2 = privkey1[:marker]+"2"+privkey1[marker+1:]
print("(2)[minim+1] {}".format(privkey2.upper()));
privkey3 = privkey0.replace("*","z")
print("(3)[maximal] {}".format(privkey3.upper()));
print('########')
privkey1hex = hex(int.from_bytes(b58decode(privkey1),"big"))[4:]
print("(1') {}".format(privkey1hex.upper()));
privkey2hex = hex(int.from_bytes(b58decode(privkey2),"big"))[4:]
print("(2') {}".format(privkey2hex.upper()));
privkey3hex = hex(int.from_bytes(b58decode(privkey3),"big"))[4:]
print("(3') {}".format(privkey3hex.upper()));
checksum = privkey1hex[-8:]
if checksum == privkey2hex[-8:] and checksum == privkey3hex[-8:] :
print("[checksum] {}".format(checksum.upper()));
else:
print("[error] checksum corrupted")
print(" --stride should be integer, not float! recovery impossible!")
exit(1);
if flag_compress:
if privkey1hex[-10:-8] != "01" or privkey2hex[-10:-8] != "01" or privkey3hex[-10:-8] != "01":
print("[error] byte of compress - corrupted")
print(" --stride should be integer, not float! recovery impossible!")
exit(1);
print('########')
privkey11hex = privkey1hex[:64]
privkey22hex = privkey2hex[:64]
privkey33hex = privkey3hex[:64]
print("(1'') {}".format(privkey11hex.upper()));
print("(2'') {}".format(privkey22hex.upper()));
print("(3'') {}".format(privkey33hex.upper()));
print('########')
print("[ --keyspace {}:{} ]".format(privkey11hex.upper(),privkey33hex.upper()))
print('########')
strideint = int(privkey2hex,16) - int(privkey1hex,16)
strideraw = hex(strideint)[2:]
#print("[strideraw] {}".format(strideraw));
if flag_compress == False:
stridehex = strideraw[:-8]
elif flag_compress == True :
stridehex = strideraw[:-10]
print("[stride] {}".format(stridehex.upper()));
print('########')
print("[ --stride {} ]".format(stridehex.upper()));
print('########')
#######################
if the checksum(or/and flag compress x01) is corrupted, than recovery now is impossible.
(--stride opt becomes float but must be integer!)
Recovery of these keys is possible only if brichard19 implements support float for --stride opt.
if it is easier to explain..
In WIFbase58privkey, each lost symbol(*/?) to the right is a division stride by 58.
Already from the middle, the stride no longer divides completely.
What does checksum have to do with it? In fact, the checksum forms the float part.
While it is equal, then when subtracting it is reset.
As soon as it becomes different for lowerKey and lowerKey+1, this indicates - stride becomes float.