Skip to content

Commit

Permalink
Litecoin support added
Browse files Browse the repository at this point in the history
  • Loading branch information
salfter committed May 1, 2013
1 parent e0adea2 commit dc70fa5
Show file tree
Hide file tree
Showing 3 changed files with 303 additions and 29 deletions.
49 changes: 28 additions & 21 deletions README
@@ -1,21 +1,28 @@
This fork adds a couple of scripts to make fillet.py easier to use:

1) hex2wifaddr.py converts hexadecimal private keys to WIF private keys and
addresses. Unlike pywallet.py, it has minimal dependencies (no bsddb, no
ecdsa, etc.). For the purposes for which we need it, it's also a fair bit
faster. It's mostly cribbed from https://bitcointalk.org/index.php?topic=23241.0.
I've never really done anything much with Python, so odds are good that the
parts I've written may be suboptimal.
2) getkey.sh wraps all this up into an easy-to-use utility to retrieve a
particular deterministic key, given a file and an index (>0). You can
retrieve either the address or the private key. If you have ImageMagick
and qrencode installed (and have an X server running), you can have it
pop up a QR code.

Scott Alfter
scott@alfter.us
8 Jan 13

If you found this useful, you can hit the tipjar at 1TipSAXbE6owdU24bcBDJKmL8JRxQe5Yu.


This fork adds a couple of scripts to make fillet.py easier to use:

1) hex2wifaddr.py converts hexadecimal private keys to WIF private keys and
addresses. Unlike pywallet.py, it has minimal dependencies (no bsddb, no
ecdsa, etc.). For the purposes for which we need it, it's also a fair bit
faster. It's mostly cribbed from https://bitcointalk.org/index.php?topic=23241.0.
I've never really done anything much with Python, so odds are good that the
parts I've written may be suboptimal.

2) getkey.sh wraps all this up into an easy-to-use utility to retrieve a
particular deterministic key, given a file and an index (>0). You can
retrieve either the address or the private key. If you have ImageMagick
and qrencode installed (and have an X server running), you can have it
pop up a QR code.

3) Support for Litecoin addresses has been added. If I knew more about
Python and its implementation of getopt, I would've just fixed
hex2wifaddr.py to support both Bitcoin and Litecoin. Instead, I took the
easy route and created hex2wifaddr_ltc.py. diff will indicate what
little needed changing (hint: the prefix for private hex keys is 0x80 for
Bitcoin and 0xB0 for Litecoin, and the network version is 0x00 for
Bitcoin and 0x30 for Litecoin).

Scott Alfter
scott@alfter.us
1 May 13

If you found this useful, you can hit the tipjar at 1TipSAXbE6owdU24bcBDJKmL8JRxQe5Yu.
23 changes: 15 additions & 8 deletions getkey.sh
Expand Up @@ -10,17 +10,19 @@ options: -p|--private retrieve private key (default: address)
EOF
}

getkey="address"
getprivkey=0
showqrcode=0
OPTS=$(getopt -o pqh --long private,qr,help -- "$@")
decoder=hex2wifaddr.py
OPTS=$(getopt -o pqhl --long private,qr,help,litecoin -- "$@")
eval set -- "$OPTS"
while true; do
case "$1" in
-p|--private) getkey="privkey"; shift;;
-q|--qr) showqrcode=1; shift;;
-h|--help) help; exit 1;;
--) shift; break;;
*) echo Internal error; exit 1;;
-l|--litecoin) decoder=hex2wifaddr_ltc.py; shift;;
-p|--private) getprivkey=1; shift;;
-q|--qr) showqrcode=1; shift;;
-h|--help) help; exit 1;;
--) shift; break;;
*) echo Internal error; exit 1;;
esac
done

Expand All @@ -31,7 +33,12 @@ fi

hexkey=$(python fillet.py --file "$1" --keynumber "$2" --size 1 | sed "s/.*: //")

out=$(python hex2wifaddr.py $hexkey | grep "$getkey" | sed "s/.* //")
if [ $getprivkey == 1 ]
then
out=$(python $decoder $hexkey | grep privkey | sed "s/.* //")
else
out=$(python $decoder $hexkey | grep address | sed "s/.* //")
fi

if [ $showqrcode == 1 ]
then
Expand Down
260 changes: 260 additions & 0 deletions hex2wifaddr_ltc.py
@@ -0,0 +1,260 @@
#! /usr/bin/env python

# convert a hexadecimal private key to WIF and retrieve the associated address
# cribbed heavily from code at https://bitcointalk.org/index.php?topic=23241.0

import random
import hashlib
import sys

class CurveFp( object ):
def __init__( self, p, a, b ):
self.__p = p
self.__a = a
self.__b = b

def p( self ):
return self.__p

def a( self ):
return self.__a

def b( self ):
return self.__b

def contains_point( self, x, y ):
return ( y * y - ( x * x * x + self.__a * x + self.__b ) ) % self.__p == 0

class Point( object ):
def __init__( self, curve, x, y, order = None ):
self.__curve = curve
self.__x = x
self.__y = y
self.__order = order
if self.__curve: assert self.__curve.contains_point( x, y )
if order: assert self * order == INFINITY

def __add__( self, other ):
if other == INFINITY: return self
if self == INFINITY: return other
assert self.__curve == other.__curve
if self.__x == other.__x:
if ( self.__y + other.__y ) % self.__curve.p() == 0:
return INFINITY
else:
return self.double()

p = self.__curve.p()
l = ( ( other.__y - self.__y ) * \
inverse_mod( other.__x - self.__x, p ) ) % p
x3 = ( l * l - self.__x - other.__x ) % p
y3 = ( l * ( self.__x - x3 ) - self.__y ) % p
return Point( self.__curve, x3, y3 )

def __mul__( self, other ):
def leftmost_bit( x ):
assert x > 0
result = 1L
while result <= x: result = 2 * result
return result / 2

e = other
if self.__order: e = e % self.__order
if e == 0: return INFINITY
if self == INFINITY: return INFINITY
assert e > 0
e3 = 3 * e
negative_self = Point( self.__curve, self.__x, -self.__y, self.__order )
i = leftmost_bit( e3 ) / 2
result = self
while i > 1:
result = result.double()
if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self
if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self
i = i / 2
return result

def __rmul__( self, other ):
return self * other

def __str__( self ):
if self == INFINITY: return "infinity"
return "(%d,%d)" % ( self.__x, self.__y )

def double( self ):
if self == INFINITY:
return INFINITY

p = self.__curve.p()
a = self.__curve.a()
l = ( ( 3 * self.__x * self.__x + a ) * \
inverse_mod( 2 * self.__y, p ) ) % p
x3 = ( l * l - 2 * self.__x ) % p
y3 = ( l * ( self.__x - x3 ) - self.__y ) % p
return Point( self.__curve, x3, y3 )

def x( self ):
return self.__x

def y( self ):
return self.__y

def curve( self ):
return self.__curve

def order( self ):
return self.__order

INFINITY = Point( None, None, None )

def inverse_mod( a, m ):
if a < 0 or m <= a: a = a % m
c, d = a, m
uc, vc, ud, vd = 1, 0, 0, 1
while c != 0:
q, c, d = divmod( d, c ) + ( c, )
uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc
assert d == 1
if ud > 0: return ud
else: return ud + m

# secp256k1
_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
_b = 0x0000000000000000000000000000000000000000000000000000000000000007L
_a = 0x0000000000000000000000000000000000000000000000000000000000000000L
_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L

class Signature( object ):
def __init__( self, r, s ):
self.r = r
self.s = s

class Public_key( object ):
def __init__( self, generator, point ):
self.curve = generator.curve()
self.generator = generator
self.point = point
n = generator.order()
if not n:
raise RuntimeError, "Generator point must have order."
if not n * point == INFINITY:
raise RuntimeError, "Generator point order is bad."
if point.x() < 0 or n <= point.x() or point.y() < 0 or n <= point.y():
raise RuntimeError, "Generator point has x or y out of range."

def verifies( self, hash, signature ):
G = self.generator
n = G.order()
r = signature.r
s = signature.s
if r < 1 or r > n-1: return False
if s < 1 or s > n-1: return False
c = inverse_mod( s, n )
u1 = ( hash * c ) % n
u2 = ( r * c ) % n
xy = u1 * G + u2 * self.point
v = xy.x() % n
return v == r

class Private_key( object ):
def __init__( self, public_key, secret_multiplier ):
self.public_key = public_key
self.secret_multiplier = secret_multiplier

def der( self ):
hex_der_key = '06052b8104000a30740201010420' + \
'%064x' % self.secret_multiplier + \
'a00706052b8104000aa14403420004' + \
'%064x' % self.public_key.point.x() + \
'%064x' % self.public_key.point.y()
return hex_der_key.decode('hex')

def sign( self, hash, random_k ):
G = self.public_key.generator
n = G.order()
k = random_k % n
p1 = k * G
r = p1.x()
if r == 0: raise RuntimeError, "amazingly unlucky random number r"
s = ( inverse_mod( k, n ) * \
( hash + ( self.secret_multiplier * r ) % n ) ) % n
if s == 0: raise RuntimeError, "amazingly unlucky random number s"
return Signature( r, s )

curve_256 = CurveFp( _p, _a, _b )
generator_256 = Point( curve_256, _Gx, _Gy, _r )
g = generator_256

b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

def hex_open_key_to_hex_hash160(hex_open_key):
h160 = hashlib.new('ripemd160')
h160.update(hashlib.sha256(('04'+hex_open_key).decode('hex')).hexdigest().decode('hex'))
return h160.hexdigest()

def hex_hash160_to_hex_addr_v0(hex_hash160):
return '30'+hex_hash160+hashlib.sha256(hashlib.sha256(('30'+hex_hash160).decode('hex')).hexdigest().decode('hex')).hexdigest()[0:8]

def hex_addr_v0_to_hex_hash160(hex_addr_v0):
return hex_addr_v0[2:-8]

def hex_to_base58(hex_data):
base58 = ''
int_data = int(hex_data, 16)
while int_data >= len(b58chars):
base58 = b58chars[int_data%len(b58chars)] + base58
int_data = int_data/len(b58chars)
base58 = b58chars[int_data%len(b58chars)] + base58
for i in xrange(len(hex_data)/2):
if hex_data[i*2:i*2+2] == '00':
base58 = '1' + base58
else:
break
return base58

def base58_to_hex(base58):
hex_data = ''
int_data = 0
for i in xrange(-1, -len(base58)-1, -1):
int_data += (b58chars.index(base58[i]))*58**(-i-1)
hex_data = hex(int_data)[2:-1]
for i in xrange(len(base58)):
if base58[i] == '1':
hex_data = '00' + hex_data
else:
break
return hex_data

def main(argv=None):
if argv is None:
argv=sys.argv
if len(argv)<2:
print "Usage: "+argv[0]+" hexkey"
sys.exit(2)

i=1
while (i<len(argv)):
### set privkey
secret=int(argv[i],16)

### echo hexkey
print "hexkey ", ("00"+hex(secret)[:-1][2:])[-64:]

### decode privkey to WIF
secret_txt="B0"+("0000000000000000000000000000000000000000000000000000000000000000"+hex(secret)[2:][:-1])[-64:]
print "privkey", hex_to_base58(secret_txt+hashlib.sha256(hashlib.sha256(secret_txt.decode("hex")).hexdigest().decode("hex")).hexdigest()[:8])

### retrieve pubkey
pubkey = Public_key( g, g * secret )
hex_open_key=("00"+hex(pubkey.point.x())[:-1][2:])[-64:]+("00"+hex(pubkey.point.y())[:-1][2:])[-64:]

### print pubkey
print "address", hex_to_base58(hex_hash160_to_hex_addr_v0(hex_open_key_to_hex_hash160(hex_open_key)))

i=i+1

if __name__ == "__main__":
sys.exit(main())

0 comments on commit dc70fa5

Please sign in to comment.