Author

Topic: Pbwhatkey -- deterministic private key generator (PBKDF2 & pywallet.py based) (Read 5865 times)

jr. member
Activity: 42
Merit: 1000

You don't have to be snarky about the idea... it's simply a suggestion and you guys are a very short way from having this enabled using pywallet.py.  If you don't like it, let's have a discussion about what problems it might have and how they could be resolved.
I like your idea, but personally have no time to try to implement it right now.
Devil always in the details. Only after we will have reference implementation we will see HIM's ears.
Or not will see Smiley
Quote
Also, I don't know why you would question the security of safe-deposit boxes, but that wasn't the point at all.
 
I only mean that if you loose somehow (bank went bankrupt or get robbed,riots or some other event)your backup placed in safe box you will be in huge trouble.
jr. member
Activity: 42
Merit: 1000
So I'm reviving an old thread here, but I'm interested in a slightly different application of deterministic key generation.  It seems like something that could integrated with pywallet very easily.  I'm sure I'm not the first person to suggest this, but I'm not finding other threads about it.

Rather than using passwords to deterministically generate your key, I'd like to use a random number generator to create a 256-bit Private-Key-Generator once.  This generator would be the first private key, GenKey, and then you get a semi-infinite sequence of new keys by simply following:

Code:
PrivKey[i+1] = hash256( GenKey XOR PrivKey[i] );

You wouldn't need the key stretching (at least that's what I'm assuming the iterations are for in the PBKDF2 module), because you're using full entropy in your original key.  Using this technique, you only need to backup your wallet once.  Sure, it links all your addresses together, but 99% of the time with the current wallet, if the attacker gets one key, he gets all of them, anyway.  And by using GenKey in each iteration, even if attacker gets PrivKey(i), he cannot determine any of the other keys.  My primary motivation is that I want to be able to put my GenKey into a QR code and store it in a safe-deposit box, and then I never have to worry about losing my private keys.  

With the current wallet, I only get a pool of 100 keys, and have to re-backup my wallet every time I run out.

Your idea in my view is closer to deterministic wallets.
Go ahead, be the first to produce some REAL code that way.
Show it.People will criticize your approach.
Maybe something interesting will be born.

Also i am sure that safe-deposit boxes are not really so SAFE nowadays.
Keeping your secrets there you simply exchange one risk to another.
jr. member
Activity: 42
Merit: 1000
My bad ! You are right !
 Those elliptic curves create chaos in my tired head )



jr. member
Activity: 42
Merit: 1000
version 4 available for testing :
Code:
#!/usr/bin/python

# Copyright (c) 2011, Ukigo

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


usage = """             Usage : ./pb4 your_passphrase your_salt number_of_iterations

                        Example :  ./pb4 +WindFall-Money*. Candy+] 777777 """


from pbkdf2_rmd import PBKDF2_RMD
import os, sys, hashlib
from hashlib import sha256
import pywallet as pyw
#from optparse import OptionParser

b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def base58_encode (n):
    l = []
    while n > 0:
        n, r = divmod (n, 58)
        l.insert (0, (b58_digits[r]))
    return ''.join (l)

def base58_decode (s):
    n = 0
    for ch in s:
        n *= 58
        digit = b58_digits.index (ch)
        n += digit
    return n

def dhash (s):
    return sha256(sha256(s).digest()).digest()

def convert_privkey_to_base58 (s):
    checksum = dhash ('\x80' + s)[:4]
    return base58_encode (
        int ('0x80' + (s + checksum).encode ('hex_codec'), 16)
        )

def main():

    try:
        if sys.argv[1] in ("help", "-h", "-help","--help"):
            print " "
            print usage
            exit(0)
        if int(sys.argv[3]) < 60001:
            print " "
            print "Number of iterations must be > 60000 ( better use > 1'000'000 )"
            exit(0)

    except IndexError:
        print " "
        print usage
        exit(0)

    print " "
    print "    Please wait. Generating keypair ..."
    salt = hashlib.sha512(sys.argv[2]).hexdigest()
    print " "
    print "Password was : ", sys.argv[1]

    topsec = PBKDF2_RMD(hashlib.sha512(sys.argv[1]).hexdigest(), salt, int(sys.argv[3])).read(32)
    #print "                .............................................................."
    print "Secret  multiplier : ", topsec.encode("hex")

    
    
    print " "
    

    secret = pyw.str_to_long(topsec)
    eckey = pyw.EC_KEY(secret)
    pub = pyw.GetPubKey(eckey)
    priv = pyw.GetPrivKey(eckey)
    priv_base58 = pyw.SecretToASecret(pyw.PrivKeyToSecret(priv))


    print "Private key for import : ", priv_base58
    

    print " "
    print "Bitcoin address :  "+pyw.public_key_to_bc_address(pub)
    print " "
    exit(0)

if __name__ == '__main__':
    main()
jr. member
Activity: 42
Merit: 1000
As i understand this :
"topsec" is NOT a private key itself, but a "secret multiplier" using to construct  private key.
 See pywallet source code.
 
 Previous unpublished version of "pb" (working with pywallet v1.0 that have diffrent API)
 simply creates 32-digit hexadecimal and then uses it as private key.

 With pywallet v 1.1 it can not be done, so i decide to create secret multiplier from input params.
 But maybe you are right and your proposed changes will enlarge keyspace a lot.

I'm not sure how many digits secret multiplier must have ?!
 
legendary
Activity: 1428
Merit: 1093
Core Armory Developer
Yes, it is a deterministic wallet.  In hindsight I realize is not precisely the purpose of this original post, but it is related.  Pywallet is the perfect tool for enabling this technique.  The command line interface would look like:

   
Code:
./pywallet.py --create-deterministic-wallet --generator-key=random256bit.bin --numkeys 10000 -o wallet.dat

This would calculate the first 10,000 keys based on the generator, and add them to key pool in wallet.dat.  If you run out of keys, you can re-run with a higher number, and it will add the new keys to it.  Perhaps it could eventually be included in the client so you never have to run anything:  just create your generator-key once, back it up, and the client will create endless keys from it.

You don't have to be snarky about the idea... it's simply a suggestion and you guys are a very short way from having this enabled using pywallet.py.  If you don't like it, let's have a discussion about what problems it might have and how they could be resolved.

Also, I don't know why you would question the security of safe-deposit boxes, but that wasn't the point at all.  People want to be able to backup their wallet once and know that they always have a backup somewhere they consider safe in case their hard-drive fails.  With the current wallets, they have to backup every 100 transactions.  Additionally, there is no warning when their key pool is exhausted, so there's a risk of using non-backed-up keys without realizing it.  This deterministic wallet solves a lot of problems, and I don't see where the reduced security is.



legendary
Activity: 1428
Merit: 1093
Core Armory Developer
So I'm reviving an old thread here, but I'm interested in a slightly different application of deterministic key generation.  It seems like something that could integrated with pywallet very easily.  I'm sure I'm not the first person to suggest this, but I'm not finding other threads about it.

Rather than using passwords to deterministically generate your key, I'd like to use a random number generator to create a 256-bit Private-Key-Generator once.  This generator would be the first private key, GenKey, and then you get a semi-infinite sequence of new keys by simply following:

Code:
PrivKey[i+1] = hash256( GenKey XOR PrivKey[i] );

You wouldn't need the key stretching (at least that's what I'm assuming the iterations are for in the PBKDF2 module), because you're using full entropy in your original key.  Using this technique, you only need to backup your wallet once.  Sure, it links all your addresses together, but 99% of the time with the current wallet, if the attacker gets one key, he gets all of them, anyway.  And by using GenKey in each iteration, even if attacker gets PrivKey(i), he cannot determine any of the other keys.  My primary motivation is that I want to be able to put my GenKey into a QR code and store it in a safe-deposit box, and then I never have to worry about losing my private keys.  

With the current wallet, I only get a pool of 100 keys, and have to re-backup my wallet every time I run out.
full member
Activity: 140
Merit: 430
Firstbits: 1samr7
As i understand this :
"topsec" is NOT a private key itself, but a "secret multiplier" using to construct  private key.
 See pywallet source code.

It's true that the topsec isn't the same thing as a pywallet Private_key.  However, an EC private key is just a large integer between 0 and the group order, and one would assume your intention is to use topsec as this value.

Indeed the secret and secret_multiplier from the pywallet code is exactly the EC private key.  The act of multiplying the generator (point) by secret (integer) produces the EC public key (point).

Quote
I'm not sure how many digits secret multiplier must have ?!

It's 32 bytes long.  The largest useful value is one less than: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

BTW, new version looks a lot better!
full member
Activity: 140
Merit: 430
Firstbits: 1samr7
On second thought, there is one detail about this program that worries me.

It sets up the PBKDF2 function and reads out 16 bytes as a hexadecimal-encoded string.

Code:
    topsec = PBKDF2_RMD(hashlib.sha512(sys.argv[1]).hexdigest(), salt, int(sys.argv[3])).hexread(16)

Then it passes the string to the pywallet integer converter.  I think what you meant to do with this is read 32 bytes of unencoded data from the PBKDF2 function:

Code:
    topsec = PBKDF2_RMD(hashlib.sha512(sys.argv[1]).hexdigest(), salt, int(sys.argv[3])).read(32)

and while the next line will no longer be able to print the raw private key without a str.encode('hex'), the str_to_long will at least get the full key data.  As it stands now, it looks like it's using the ASCII hexadecimal string as the raw private key, which would provide about half the expected level of security.
full member
Activity: 140
Merit: 430
Firstbits: 1samr7
This program would appear to work exactly as advertised.  It produces good, repeatable public/private key pairs out of passwords using a standard, well-regarded algorithm.  Kudos on moving to Python, it's so easy to read the code, and the base58 functions are elegant and understandable!

In any case, memorizing a 51-character private key is unwieldy, but I'll argue that a strong password of comparable security can be a little over half as long and much easier to memorize.  Which, besides backup, would be really great in low-tech situations where wallet files and all physical representations of keys can't be retained.
jr. member
Activity: 42
Merit: 1000
Jump to: