####################################Python Bitcoin KeyChain

#This program takes an initial private key (seed) and and increment (customincr), and generates a list of keys (wallet).

#Then it saves a .txt file holding the list of related keys.

#No need for custom libraries, the eliptic equations are built in!

#For good security, roll lots of d10 dice to sequence a key somewhere between 2^50 and 2^253.

#Then pick an increment, the larger the better, but keep it below 2^150.

#This program WILL generate any address, the limits are suggestions, and invalid keys should close program.

#Keep those two numbers safe, they can be used to recreate the entire wallet!

#Also, this is a ready to use application, to enable prompt for seeds, scroll down and uncomment blocks noted.

##### Lawsky Aug, '14 , some of this code is from other sources, and is free for all purposes.

import hashlib , binascii # local imports required for conversions, they are standard with Python

#import urllib2 #for blockchain query that is disabled

#import json #for blockchain query that is disabled

Pcurve = 2 ** 256 - 2 ** 32 - 2 ** 9 - 2 ** 8 - 2 ** 7 - 2 ** 6 - 2 ** 4 - 1 # The proven prime

N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 # Number of points in the field

Acurve = 0 ; Bcurve = 7 # These two defines the elliptic curve. y^2 = x^3 + Acurve * x + Bcurve

Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240 #x coordinate of GPoint

Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 #y coordinate of GPoint

GPoint = ( Gx , Gy ) # This is our generator point. Trillions of dif ones possible

t = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' # Base58 Charset

def Byte2Hex ( b ) : #Convert a byte string to hex number

out = "" ;

for x in b:

y = hex ( ord ( x ) ) [ 2 : ]

if len ( y ) == 1 :

y = "0" +y

out+ = "%2s" %y

return out

def Hex2Byte ( hexStr ) : # convert hex number into a byte string

bytes = [ ]

hexStr = '' . join ( hexStr. split ( " " ) )

for i in range ( 0 , len ( hexStr ) , 2 ) :

bytes . append ( chr ( int ( hexStr [ i:i+ 2 ] , 16 ) ) )

return '' . join ( bytes )

def Int2Byte ( n , b ) : #Convert a integer to a byte string of length b

out = ""

for i in range ( b ) :

( n , m ) = divmod ( n , 256 )

out = chr ( m ) +out

return out

def modinv ( a , n = Pcurve ) : #Extended Euclidean Algorithm/'division' in elliptic curves

lm , hm = 1 , 0

low , high = a%n , n

while low > 1 :

ratio = high/low

nm , new = hm-lm*ratio , high-low*ratio

lm , low , hm , high = nm , new , lm , low

return lm % n

def ECadd ( a , b ) : # Not true addition, invented for EC. Could have been called anything.

LamAdd = ( ( b [ 1 ] -a [ 1 ] ) * modinv ( b [ 0 ] -a [ 0 ] , Pcurve ) ) % Pcurve

x = ( LamAdd*LamAdd-a [ 0 ] -b [ 0 ] ) % Pcurve

y = ( LamAdd* ( a [ 0 ] -x ) -a [ 1 ] ) % Pcurve

return ( x , y )

def ECdouble ( a ) : # This is called point doubling, also invented for EC.

Lam = ( ( 3 *a [ 0 ] *a [ 0 ] +Acurve ) * modinv ( ( 2 *a [ 1 ] ) , Pcurve ) ) % Pcurve

x = ( Lam*Lam- 2 *a [ 0 ] ) % Pcurve

y = ( Lam* ( a [ 0 ] -x ) -a [ 1 ] ) % Pcurve

return ( x , y )

def EccMultiply ( GenPoint , ScalarHex ) : #Double & add. Not true multiplication

if ScalarHex == 0 or ScalarHex >= N: raise Exception ( "Invalid Scalar/Private Key" )

ScalarBin = str ( bin ( ScalarHex ) ) [ 2 : ]

Q = GenPoint

for i in range ( 1 , len ( ScalarBin ) ) : # This is invented EC multiplication.

Q = ECdouble ( Q ) ; # print "DUB", Q[0]; print

if ScalarBin [ i ] == "1" :

Q = ECadd ( Q , GenPoint ) ; # print "ADD", Q[0]; print

return ( Q )

def numtowif ( numpriv ) : #converts numerical private key to WIF

step1 = '80' + hex ( numpriv ) [ 2 : ] . strip ( 'L' ) . zfill ( 64 )

step2 = hashlib. sha256 ( binascii . unhexlify ( step1 ) ) . hexdigest ( )

step3 = hashlib. sha256 ( binascii . unhexlify ( step2 ) ) . hexdigest ( )

step4 = int ( step1 + step3 [ : 8 ] , 16 )

return '' . join ( [ t [ step4/ ( 58 **l ) % 58 ] for l in range ( 100 ) ] ) [ ::- 1 ] . lstrip ( '1' )

def wiftonum ( wifpriv ) : #converts from WIF to numerical private key

return sum ( [ t. index ( wifpriv [ ::- 1 ] [ l ] ) * ( 58 **l ) for l in range ( len ( wifpriv ) ) ] ) / ( 2 ** 32 ) % ( 2 ** 256 )

def validwif ( wifpriv ) : #validates WIF generated by converting it back and forth

return numtowif ( wiftonum ( wifpriv ) ) == wifpriv

def b58encode ( v ) : #Encode a byte string to the Base58

base = len ( t )

val = 0

for c in v:

val* = 256

val+ = ord ( c )

result = ""

while val:

( val , mod ) = divmod ( val , base )

result = t [ mod ] +result

pad = 0

for c in v:

if c == " \0 " :

pad+ = 1

else :

break

return ( t [ 0 ] *pad ) +result

def pubtoaddy ( puk ) : #converts uncompressed public key to public address

version = chr ( 0 )

h = hashlib. new ( "SHA256" )

h. update ( puk )

s = h. digest ( )

h = hashlib. new ( "RIPEMD160" )

h. update ( s )

kh = version+h. digest ( )

h = hashlib. new ( "SHA256" )

h. update ( kh )

cs = h. digest ( )

h = hashlib. new ( "SHA256" )

h. update ( cs )

cs = h. digest ( ) [ : 4 ]

adr = b58encode ( kh+cs )

return adr

###Enable to allow blockchain query:

#def GetBCIbalance(query):

# url = 'https://blockchain.info/q/addressbalance/' + query

# json_obj = urllib2.urlopen(url)

# data = json.load(json_obj)

# return data

def genwallet ( ) : # the main generator

totalbalance = 0

walletname = "DEFAULTWALLET.txt"

#################Change these############ GENERATOR SEEDS: Initial numerical private key and numerical increment between keys, thus: key chain

seedbtc = 13787023450987234098623465093474584872345908273452360908908071097932145987679 # default seed = first numerical private key to use in generation

customincr = 10873345756478567985678956481319876234879623450 # default increment between private keys

###################################################################################################################

customcount = 50 # default address count

#######DO NOT MOVE###########Enable the following to have Prompt for custom filename,seed and customincr -- these two numbers are all you need with this scrip to recover all keys, so pick smart.

#customseed = input('Enter a seed number between 2^50 and 2^253: ') #Overrides default seed with prompt

#seedbtc = customseed

#customcount = input('How many wallets, 144 max: ')

#if customcount >> 144:

# customcount = 144

#customincr = input('Enter an increment number between 2^3 and 2^116: ')

#walletname = raw_input('Pick a filename: ')

########DO NOT MOVE##########You can also recover wallets made with this software with just the first two numerical private keys, prk2-prk1=customincr

text_file = open ( walletname , "w" ) #creates new file to store all keys, change the name of this file after/during creation to NOT LOSE KEYS!!!

multn = 0 #don't change this or you'll have to remember it too!!! this is the multipier that spaces your private keys

for looper in range ( 0 , customcount ) : # for each address

balance = 0

privKey = seedbtc+customincr*multn # equals seedbtc on first run, then counts up by customincr. These are your numerical private keys.

multn = multn+ 1 #counter for customincr multiplier

hexprkstr = '%x' % privKey #removes 0x and 'L' for printing purposes

PublicKey = EccMultiply ( GPoint , privKey ) #gets public key from Eliptic function

WIFKEY = numtowif ( privKey ) #gets WIF

wifvalid = validwif ( numtowif ( privKey ) ) #checks if WIF validates by double conversion

ucpuk = "04" + "%064x" % PublicKey [ 0 ] + "%064x" % PublicKey [ 1 ] ; #uncompressed public key

if PublicKey [ 1 ] % 2 == 1 : #odd compressed public key

cpuk = "03" + str ( hex ( PublicKey [ 0 ] ) [ 2 :- 1 ] ) . zfill ( 64 )

else : #even compressed public key

cpuk = "02" + str ( hex ( PublicKey [ 0 ] ) [ 2 :- 1 ] ) . zfill ( 64 )

PubAd = pubtoaddy ( Hex2Byte ( ucpuk ) ) #gets public address

#balance = float(GetBCIbalance(PubAd))/100000000; balchen = true #Enable to do api balance check on blockchain.info

#totalbalance = totalbalance + balance #tracks total balance for disabled query

newhex = hexprkstr

for padhexpr in range ( 0 , ( 64 - len ( hexprkstr ) ) ) : #makes hex private key ready to copy

newhex = "0" + newhex # just padding left side with zeros until 64 characters

hexprkstr = newhex

print >> text_file

print >> text_file , "PublicAddress" , looper+ 1 , ": " , PubAd #, " ",balance #enable ', " ",balance' to print

print >> text_file , "NumericalPrivteKey: "

print >> text_file , " " , privKey

print >> text_file , "HexPrivateKey: "

print >> text_file , " " , hexprkstr

print >> text_file , "WalletImportFormat: "

print >> text_file , " " , WIFKEY , " : Use this to validate"

if wifvalid:

donothing = 0 #does not print if WIF is valid

else :

print >> text_file , " " , "WIFValid: " , wifvalid #prints if WIF is invalid

print >> text_file , "UncompressedPublicKey: "

print >> text_file , " " , ucpuk

print >> text_file , "CompressedPublicKey: "

print >> text_file , " " , cpuk

print PubAd #, " ",balance #enable ', " ",balance' to print

text_file. close ( )

print "File has been created with" , multn , "key sets." # + " Total balance of: ", totalbalance #enable ' + " Total balance of: ", totalbalance' to print balance

genwallet ( ) # Start Generator

########################################This section enables restart

#newwalletstart = "y"

#yesido = "y"

#while newwalletstart is yesido:

# newwalletstart = raw_input('Create another wallet? y, else close:')

# if newwalletstart is yesido:

# genwallet()