Skip to content

Instantly share code, notes, and snippets.

@HelloZeroNet
Last active November 21, 2015 08:47
Show Gist options
  • Save HelloZeroNet/747ffc1366bfdd47d657 to your computer and use it in GitHub Desktop.
Save HelloZeroNet/747ffc1366bfdd47d657 to your computer and use it in GitHub Desktop.
# via http://pastebin.com/H1XikJFd
# -*- Mode: Python -*-
# This is a combination of http://pastebin.com/bQtdDzHx and
# https://github.com/Bitmessage/PyBitmessage/blob/master/src/pyelliptic/openssl.py
# that doesn't crash on OSX.
import ctypes
import ctypes.util
import hashlib
import base64
addrtype = 0
class _OpenSSL:
"""
Wrapper for OpenSSL using ctypes
"""
def __init__(self, library):
"""
Build the wrapper
"""
self._lib = ctypes.CDLL(library)
self.pointer = ctypes.pointer
self.c_int = ctypes.c_int
self.byref = ctypes.byref
self.create_string_buffer = ctypes.create_string_buffer
self.BN_new = self._lib.BN_new
self.BN_new.restype = ctypes.c_void_p
self.BN_new.argtypes = []
self.BN_copy = self._lib.BN_copy
self.BN_copy.restype = ctypes.c_void_p
self.BN_copy.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
self.BN_mul_word = self._lib.BN_mul_word
self.BN_mul_word.restype = ctypes.c_int
self.BN_mul_word.argtypes = [ctypes.c_void_p, ctypes.c_int]
self.BN_set_word = self._lib.BN_set_word
self.BN_set_word.restype = ctypes.c_int
self.BN_set_word.argtypes = [ctypes.c_void_p, ctypes.c_int]
self.BN_add = self._lib.BN_add
self.BN_add.restype = ctypes.c_void_p
self.BN_add.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p]
self.BN_mod_sub = self._lib.BN_mod_sub
self.BN_mod_sub.restype = ctypes.c_int
self.BN_mod_sub.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.BN_mod_mul = self._lib.BN_mod_mul
self.BN_mod_mul.restype = ctypes.c_int
self.BN_mod_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.BN_mod_inverse = self._lib.BN_mod_inverse
self.BN_mod_inverse.restype = ctypes.c_void_p
self.BN_mod_inverse.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.BN_cmp = self._lib.BN_cmp
self.BN_cmp.restype = ctypes.c_int
self.BN_cmp.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
self.BN_bn2bin = self._lib.BN_bn2bin
self.BN_bn2bin.restype = ctypes.c_int
self.BN_bn2bin.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
self.BN_bin2bn = self._lib.BN_bin2bn
self.BN_bin2bn.restype = ctypes.c_void_p
self.BN_bin2bn.argtypes = [ctypes.c_void_p, ctypes.c_int,
ctypes.c_void_p]
self.EC_KEY_new_by_curve_name = self._lib.EC_KEY_new_by_curve_name
self.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
self.EC_KEY_new_by_curve_name.argtypes = [ctypes.c_int]
self.EC_KEY_get0_group = self._lib.EC_KEY_get0_group
self.EC_KEY_get0_group.restype = ctypes.c_void_p
self.EC_KEY_get0_group.argtypes = [ctypes.c_void_p]
self.EC_KEY_set_private_key = self._lib.EC_KEY_set_private_key
self.EC_KEY_set_private_key.restype = ctypes.c_int
self.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.EC_KEY_set_public_key = self._lib.EC_KEY_set_public_key
self.EC_KEY_set_public_key.restype = ctypes.c_int
self.EC_KEY_set_public_key.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.EC_POINT_set_compressed_coordinates_GFp = self._lib.EC_POINT_set_compressed_coordinates_GFp
self.EC_POINT_set_compressed_coordinates_GFp.restype = ctypes.c_int
self.EC_POINT_set_compressed_coordinates_GFp.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
self.EC_POINT_new = self._lib.EC_POINT_new
self.EC_POINT_new.restype = ctypes.c_void_p
self.EC_POINT_new.argtypes = [ctypes.c_void_p]
self.EC_POINT_free = self._lib.EC_POINT_free
self.EC_POINT_free.restype = None
self.EC_POINT_free.argtypes = [ctypes.c_void_p]
self.EC_GROUP_get_order = self._lib.EC_GROUP_get_order
self.EC_GROUP_get_order.restype = ctypes.c_void_p
self.EC_GROUP_get_order.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
self.EC_GROUP_get_degree = self._lib.EC_GROUP_get_degree
self.EC_GROUP_get_degree.restype = ctypes.c_void_p
self.EC_GROUP_get_degree.argtypes = [ctypes.c_void_p]
self.EC_GROUP_get_curve_GFp = self._lib.EC_GROUP_get_curve_GFp
self.EC_GROUP_get_curve_GFp.restype = ctypes.c_void_p
self.EC_GROUP_get_curve_GFp.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p]
self.EC_POINT_mul = self._lib.EC_POINT_mul
self.EC_POINT_mul.restype = ctypes.c_int
self.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p]
self.EC_KEY_set_private_key = self._lib.EC_KEY_set_private_key
self.EC_KEY_set_private_key.restype = ctypes.c_int
self.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p,
ctypes.c_void_p]
self.EC_KEY_set_conv_form = self._lib.EC_KEY_set_conv_form
self.EC_KEY_set_conv_form.restype = None
self.EC_KEY_set_conv_form.argtypes = [ctypes.c_void_p,
ctypes.c_int]
self.BN_CTX_new = self._lib.BN_CTX_new
self._lib.BN_CTX_new.restype = ctypes.c_void_p
self._lib.BN_CTX_new.argtypes = []
self.BN_CTX_start = self._lib.BN_CTX_start
self._lib.BN_CTX_start.restype = ctypes.c_void_p
self._lib.BN_CTX_start.argtypes = [ctypes.c_void_p]
self.BN_CTX_get = self._lib.BN_CTX_get
self._lib.BN_CTX_get.restype = ctypes.c_void_p
self._lib.BN_CTX_get.argtypes = [ctypes.c_void_p]
self.ECDSA_sign = self._lib.ECDSA_sign
self.ECDSA_sign.restype = ctypes.c_int
self.ECDSA_sign.argtypes = [ctypes.c_int, ctypes.c_void_p,
ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
self.ECDSA_verify = self._lib.ECDSA_verify
self.ECDSA_verify.restype = ctypes.c_int
self.ECDSA_verify.argtypes = [ctypes.c_int, ctypes.c_void_p,
ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
self.i2o_ECPublicKey = self._lib.i2o_ECPublicKey
self.i2o_ECPublicKey.restype = ctypes.c_void_p
self.i2o_ECPublicKey.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
ssl = _OpenSSL(ctypes.util.find_library('ssl') or 'libeay32')
openssl_version = "%.9X" % ssl._lib.SSLeay()
NID_secp256k1 = 714
def check_result (val, func, args):
if val == 0:
raise ValueError
else:
return ctypes.c_void_p (val)
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
ssl.EC_KEY_new_by_curve_name.errcheck = check_result
POINT_CONVERSION_COMPRESSED = 2
POINT_CONVERSION_UNCOMPRESSED = 4
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
__b58base = len(__b58chars)
def b58encode(v):
""" encode v, which is a string of bytes, to base58.
"""
long_value = 0L
for (i, c) in enumerate(v[::-1]):
long_value += (256**i) * ord(c)
result = ''
while long_value >= __b58base:
div, mod = divmod(long_value, __b58base)
result = __b58chars[mod] + result
long_value = div
result = __b58chars[long_value] + result
# Bitcoin does a little leading-zero-compression:
# leading 0-bytes in the input become leading-1s
nPad = 0
for c in v:
if c == '\0': nPad += 1
else: break
return (__b58chars[0]*nPad) + result
def hash_160(public_key):
md = hashlib.new('ripemd160')
md.update(hashlib.sha256(public_key).digest())
return md.digest()
def hash_160_to_bc_address(h160):
vh160 = chr(addrtype) + h160
h = Hash(vh160)
addr = vh160 + h[0:4]
return b58encode(addr)
def public_key_to_bc_address(public_key):
h160 = hash_160(public_key)
return hash_160_to_bc_address(h160)
def msg_magic(message):
return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message
def get_address(eckey):
size = ssl.i2o_ECPublicKey (eckey, 0)
mb = ctypes.create_string_buffer (size)
ssl.i2o_ECPublicKey (eckey, ctypes.byref (ctypes.pointer (mb)))
return public_key_to_bc_address(mb.raw)
def Hash(data):
return hashlib.sha256(hashlib.sha256(data).digest()).digest()
def bx(bn, size=32):
b = ctypes.create_string_buffer(size)
ssl.BN_bn2bin(bn, b);
return b.raw.encode('hex')
def verify_message(address, signature, message):
pkey = ssl.EC_KEY_new_by_curve_name(NID_secp256k1)
eckey = SetCompactSignature(pkey, Hash(msg_magic(message)), signature)
addr = get_address(eckey)
return (address == addr)
def SetCompactSignature(pkey, hash, signature):
sig = base64.b64decode(signature)
if len(sig) != 65:
raise BaseException("Wrong encoding")
nV = ord(sig[0])
if nV < 27 or nV >= 35:
return False
if nV >= 31:
ssl.EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED)
nV -= 4
r = ssl.BN_bin2bn (sig[1:33], 32, None)
s = ssl.BN_bin2bn (sig[33:], 32, None)
eckey = ECDSA_SIG_recover_key_GFp(pkey, r, s, hash, len(hash), nV - 27,
False);
return eckey
def ECDSA_SIG_recover_key_GFp(eckey, r, s, msg, msglen, recid, check):
n = 0
i = recid / 2
group = ssl.EC_KEY_get0_group(eckey)
ctx = ssl.BN_CTX_new()
ssl.BN_CTX_start(ctx)
order = ssl.BN_CTX_get(ctx)
ssl.EC_GROUP_get_order(group, order, ctx)
x = ssl.BN_CTX_get(ctx)
ssl.BN_copy(x, order);
ssl.BN_mul_word(x, i);
ssl.BN_add(x, x, r)
field = ssl.BN_CTX_get(ctx)
ssl.EC_GROUP_get_curve_GFp(group, field, None, None, ctx)
if (ssl.BN_cmp(x, field) >= 0):
return False
R = ssl.EC_POINT_new(group)
ssl.EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)
if check:
O = ssl.EC_POINT_new(group)
ssl.EC_POINT_mul(group, O, None, R, order, ctx)
if ssl.EC_POINT_is_at_infinity(group, O):
return False
Q = ssl.EC_POINT_new(group)
n = ssl.EC_GROUP_get_degree(group)
e = ssl.BN_CTX_get(ctx)
ssl.BN_bin2bn(msg, msglen, e)
if 8 * msglen > n: ssl.BN_rshift(e, e, 8 - (n & 7))
zero = ssl.BN_CTX_get(ctx)
ssl.BN_set_word(zero, 0)
ssl.BN_mod_sub(e, zero, e, order, ctx)
rr = ssl.BN_CTX_get(ctx);
ssl.BN_mod_inverse(rr, r, order, ctx)
sor = ssl.BN_CTX_get(ctx)
ssl.BN_mod_mul(sor, s, rr, order, ctx)
eor = ssl.BN_CTX_get(ctx)
ssl.BN_mod_mul(eor, e, rr, order, ctx)
ssl.EC_POINT_mul(group, Q, eor, R, sor, ctx)
ssl.EC_KEY_set_public_key(eckey, Q)
return eckey
def getMessagePubkey(message, sig):
pkey = ssl.EC_KEY_new_by_curve_name(NID_secp256k1)
eckey = SetCompactSignature(pkey, Hash(msg_magic(message)), sig)
size = ssl.i2o_ECPublicKey (eckey, 0)
mb = ctypes.create_string_buffer (size)
ssl.i2o_ECPublicKey (eckey, ctypes.byref (ctypes.pointer (mb)))
return mb.raw
def test():
sign = "HGbib2kv9gm9IJjDt1FXbXFczZi35u0rZR3iPUIt5GglDDCeIQ7v8eYXVNIaLoJRI4URGZrhwmsYQ9aVtRTnTfQ="
pubkey = "044827c756561b8ef6b28b5e53a000805adbf4938ab82e1c2b7f7ea16a0d6face9a509a0a13e794d742210b00581f3e249ebcc705240af2540ea19591091ac1d41"
assert getMessagePubkey("hello", sign).encode("hex") == pubkey
test() # Make sure it working right
sign = "HGbib2kv9gm9IJjDt1FXbXFczZi35u0rZR3iPUIt5GglDDCeIQ7v8eYXVNIaLoJRI4URGZrhwmsYQ9aVtRTnTfQ="
pubkey = "044827c756561b8ef6b28b5e53a000805adbf4938ab82e1c2b7f7ea16a0d6face9a509a0a13e794d742210b00581f3e249ebcc705240af2540ea19591091ac1d41"
print public_key_to_bc_address(getMessagePubkey("hello", sign)) # 1N2XWu5soeppX2qUjvrf81rpdbShKJrjTr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment