Skip to content

Commit b962428

Browse files
committed
Compressed key support
Test vectors taken from <http://sourceforge.net/mailarchive/message.php?msg_id=28648286>.
1 parent 74434e8 commit b962428

File tree

3 files changed

+44
-12
lines changed

3 files changed

+44
-12
lines changed

addrgen.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,27 @@
66
import base58
77
import ctssl
88

9-
def wif (priv, testnet=False):
9+
def wif (priv, compressed, testnet=False):
1010
'''
1111
Convert raw private key to base58-encoded wallet import format.
1212
13-
>>> wif (binascii.unhexlify (b'338b7f9c13b44747fdef077898f688411693df40d5f6943ebba30917125934c9'))
14-
b'5JCzE8aEchKzGQThaYqKn5bcHvisWThwC2tU3eUB6VVAx1WV5fQ'
13+
>>> wif (binascii.unhexlify (b'1111111111111111111111111111111111111111111111111111111111111111'), False)
14+
b'5HwoXVkHoRM8sL2KmNRS217n1g8mPPBomrY7yehCuXC1115WWsh'
15+
>>> wif (binascii.unhexlify (b'1111111111111111111111111111111111111111111111111111111111111111'), True)
16+
b'KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp'
17+
>>> wif (binascii.unhexlify (b'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'), False)
18+
b'5KVzsHJiUxgvBBgtVS7qBTbbYZpwWM4WQNCCyNSiuFCJzYMxg8H'
19+
>>> wif (binascii.unhexlify (b'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'), True)
20+
b'L4ezQvyC6QoBhxB4GVs9fAPhUKtbaXYUn8YTqoeXwbevQq4U92vN'
21+
>>> wif (binascii.unhexlify (b'47f7616ea6f9b923076625b4488115de1ef1187f760e65f89eb6f4f7ff04b012'), False)
22+
b'5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y'
23+
>>> wif (binascii.unhexlify (b'47f7616ea6f9b923076625b4488115de1ef1187f760e65f89eb6f4f7ff04b012'), True)
24+
b'KydbzBtk6uc7M6dXwEgTEH2sphZxSPbmDSz6kUUHi4eUpSQuhEbq'
1525
'''
1626
result = (b'\x80' if not testnet else b'\xef') + (b'\x00' * (32 - len (priv)) + priv)
1727
assert len (result) == 33
28+
if compressed:
29+
result += b'\x01'
1830
h1 = hashlib.sha256 (result)
1931
h2 = hashlib.sha256 (h1.digest ())
2032
result += h2.digest ()[:4]
@@ -24,11 +36,25 @@ def addr (pub, testnet=False):
2436
'''
2537
Convert raw public key to base58check-encoded address.
2638
27-
>>> addr (binascii.unhexlify (b'04bbed292cf660fcd6fd29590ea53bbad38603a8b55d93806d12af56c994bae8d1df64d4b52607543c44031b26c401648928f748dd447c736b3c47f61f38477c28'))
28-
b'1Br8AVNn7XtvEBHc5hyaXsnPZFVAbLpNiL'
39+
>>> addr (binascii.unhexlify (b'044f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa385b6b1b8ead809ca67454d9683fcf2ba03456d6fe2c4abe2b07f0fbdbb2f1c1'))
40+
b'1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a'
41+
>>> addr (binascii.unhexlify (b'034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa'))
42+
b'1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9'
43+
>>> addr (binascii.unhexlify (b'04ed83704c95d829046f1ac27806211132102c34e9ac7ffa1b71110658e5b9d1bdedc416f5cefc1db0625cd0c75de8192d2b592d7e3b00bcfb4a0e860d880fd1fc'))
44+
b'1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE'
45+
>>> addr (binascii.unhexlify (b'02ed83704c95d829046f1ac27806211132102c34e9ac7ffa1b71110658e5b9d1bd'))
46+
b'1NKRhS7iYUGTaAfaR5z8BueAJesqaTyc4a'
47+
>>> addr (binascii.unhexlify (b'042596957532fc37e40486b910802ff45eeaa924548c0e1c080ef804e523ec3ed3ed0a9004acf927666eee18b7f5e8ad72ff100a3bb710a577256fd7ec81eb1cb3'))
48+
b'1PM35qz2uwCDzcUJtiqDSudAaaLrWRw41L'
49+
>>> addr (binascii.unhexlify (b'032596957532fc37e40486b910802ff45eeaa924548c0e1c080ef804e523ec3ed3'))
50+
b'19ck9VKC6KjGxR9LJg4DNMRc45qFrJguvV'
2951
'''
30-
assert len (pub) == 65 # pad?
31-
assert pub[0] == 4
52+
if pub[0] == 4:
53+
assert len (pub) == 65
54+
elif pub[0] == 2 or pub[0] == 3:
55+
assert len (pub) == 33
56+
else:
57+
assert False, 'Unknown public key format: {}'.format (pub[0])
3258
h3 = hashlib.sha256 (pub)
3359
h4 = hashlib.new ('ripemd160', h3.digest ())
3460
result = (b'\x00' if not testnet else b'\x6f') + h4.digest ()
@@ -38,23 +64,24 @@ def addr (pub, testnet=False):
3864
return base58.encode (result)
3965

4066
def generate (a):
41-
with ctssl.EC_KEY () as key:
67+
with ctssl.EC_KEY (compressed=not a.uncompressed) as key:
4268
priv = key.priv ()
4369
pub = key.pub ()
4470
with print_lock:
45-
print (wif (priv, testnet=a.testnet).decode ('ascii'), addr (pub, testnet=a.testnet).decode ('ascii'))
71+
print (wif (priv, key.compressed, testnet=a.testnet).decode ('ascii'), addr (pub, testnet=a.testnet).decode ('ascii'))
4672
if a.raw:
4773
print ('', binascii.hexlify (priv).decode ('ascii'), binascii.hexlify (pub).decode ('ascii'))
4874

4975
def main ():
5076
parser = argparse.ArgumentParser (description = 'Generate Bitcoin addresses.')
51-
parser.add_argument ('--count', '-c', help='Number of addresses to generate', action='store', default=1)
77+
parser.add_argument ('--number', '-n', help='Number of addresses to generate', action='store', default=1)
78+
parser.add_argument ('--uncompressed', '-u', help='Use uncompressed format', action='store_true')
5279
parser.add_argument ('--raw', '-r', help='Display raw private/public key', action='store_true')
5380
parser.add_argument ('--testnet', '-t', help='Generate testnet address', action='store_true')
5481
a = parser.parse_args ()
5582

5683
p = multiprocessing.Pool (processes=multiprocessing.cpu_count ())
57-
p.map (generate, [a] * int (a.count))
84+
p.map (generate, [a] * int (a.number))
5885

5986
if __name__ == '__main__':
6087
print_lock = multiprocessing.Lock ()

ctssl/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import ctssl.detail as detail
55

66
class EC_KEY:
7-
def __init__ (self):
7+
def __init__ (self, compressed=True):
8+
self.compressed = compressed
89
pass
910

1011
def __enter__ (self):
1112
self.k = detail.ssl.EC_KEY_new_by_curve_name (detail.NID_secp256k1)
1213
detail.ssl.EC_KEY_generate_key (self.k)
14+
detail.ssl.EC_KEY_set_conv_form (self.k, detail.POINT_CONVERSION_COMPRESSED if self.compressed else detail.POINT_CONVERSION_UNCOMPRESSED)
1315
return self
1416

1517
def __exit__ (self, *exc_info):

ctssl/detail.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import ctypes
22
import ctypes.util
33

4+
POINT_CONVERSION_COMPRESSED = 2
5+
POINT_CONVERSION_UNCOMPRESSED = 4
6+
47
NID_secp256k1 = 714
58

69
ssl = ctypes.cdll.LoadLibrary (ctypes.util.find_library ('ssl'))

0 commit comments

Comments
 (0)