diff --git a/README.md b/README.md
index c325b23e..fe2239b8 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,8 @@ Decentralized websites using Bitcoin crypto and the BitTorrent network
After the peers have verified the `content.json` integrity (using the
signature), they download the modified files and publish the new content to
other peers.
-* More details: [Slideshow about how ZeroNet works](https://docs.google.com/presentation/d/1_2qK1IuOKJ51pgBvllZ9Yu7Au2l551t3XBgyTSvilew/pub?start=false&loop=false&delayms=3000)
+
+#### [Slideshow about ZeroNet cryptography, site updates, multi-user sites ยป](https://docs.google.com/presentation/d/1_2qK1IuOKJ51pgBvllZ9Yu7Au2l551t3XBgyTSvilew/pub?start=false&loop=false&delayms=3000)
diff --git a/plugins/Stats/StatsPlugin.py b/plugins/Stats/StatsPlugin.py
index 1e36936c..e6fa9afa 100644
--- a/plugins/Stats/StatsPlugin.py
+++ b/plugins/Stats/StatsPlugin.py
@@ -155,6 +155,11 @@ class UiRequestPlugin(object):
for obj in objs:
yield " - %.1fkb: %s
" % (self.getObjSize(obj, hpy), cgi.escape(repr(obj)))
+ from socket import socket
+ objs = [obj for obj in gc.get_objects() if isinstance(obj, socket)]
+ yield "
Sockets (%s):
" % len(objs)
+ for obj in objs:
+ yield " - %.1fkb: %s
" % (self.getObjSize(obj, hpy), cgi.escape(repr(obj)))
from msgpack import Unpacker
objs = [obj for obj in gc.get_objects() if isinstance(obj, Unpacker)]
@@ -305,11 +310,23 @@ class UiRequestPlugin(object):
address = CryptBitcoin.privatekeyToAddress(privatekey)
- with benchmark("verify x 10", 1.6):
+ if CryptBitcoin.opensslVerify: # Openssl avalible
+ with benchmark("openssl verify x 100", 0.37):
+ for i in range(100):
+ if i%10==0: yield "."
+ ok = CryptBitcoin.verify(data, address, sign)
+ assert ok, "does not verify from %s" % address
+ else:
+ yield " - openssl verify x 100...not avalible :(
"
+
+ opensslVerify_bk = CryptBitcoin.opensslVerify # Emulate openssl not found in any way
+ CryptBitcoin.opensslVerify = None
+ with benchmark("pure-python verify x 10", 1.6):
for i in range(10):
yield "."
ok = CryptBitcoin.verify(data, address, sign)
assert ok, "does not verify from %s" % address
+ CryptBitcoin.opensslVerify = opensslVerify_bk
yield "
CryptHash:
"
diff --git a/plugins/Zeroname/UiRequestPlugin.py b/plugins/Zeroname/UiRequestPlugin.py
index 371ab2d0..3e54c765 100644
--- a/plugins/Zeroname/UiRequestPlugin.py
+++ b/plugins/Zeroname/UiRequestPlugin.py
@@ -27,7 +27,6 @@ class UiRequestPlugin(object):
if self.isProxyRequest(): # Match to site domain
referer = re.sub("^http://zero[/]+", "http://", referer) # Allow /zero access
- print referer
referer_site_address = re.match("http[s]{0,1}://(.*?)(/|$)", referer).group(1)
else: # Match to request path
referer_site_address = re.match("/(?P
[A-Za-z0-9\.]+)(?P/.*|$)", referer_path).group("address")
diff --git a/src/Config.py b/src/Config.py
index 05f3478b..2e872935 100644
--- a/src/Config.py
+++ b/src/Config.py
@@ -4,7 +4,7 @@ import ConfigParser
class Config(object):
def __init__(self):
self.version = "0.2.9"
- self.rev = 120
+ self.rev = 122
self.parser = self.createArguments()
argv = sys.argv[:] # Copy command line arguments
argv = self.parseConfig(argv) # Add arguments from config file
diff --git a/src/Crypt/CryptBitcoin.py b/src/Crypt/CryptBitcoin.py
index 7735c1f7..9adfaf9e 100644
--- a/src/Crypt/CryptBitcoin.py
+++ b/src/Crypt/CryptBitcoin.py
@@ -1,5 +1,13 @@
from lib.BitcoinECC import BitcoinECC
from lib.pybitcointools import bitcoin as btctools
+import logging
+# Try to load openssl
+try:
+ from lib.opensslVerify import opensslVerify
+ logging.info("OpenSSL loaded, version: %s" % opensslVerify.openssl_version)
+except Exception, err:
+ logging.info("OpenSSL load failed: %s, falling back to slow bitcoin verify" % err)
+ opensslVerify = None
def newPrivatekey(uncompressed=True): # Return new private key
@@ -45,8 +53,12 @@ def signOld(data, privatekey): # Return sign to data using private key (backward
def verify(data, address, sign): # Verify data using address and sign
if hasattr(sign, "endswith"):
- pub = btctools.ecdsa_recover(data, sign)
- sign_address = btctools.pubtoaddr(pub)
+ if opensslVerify: # Use the faster method if avalible
+ pub = opensslVerify.getMessagePubkey(data, sign)
+ sign_address = btctools.pubtoaddr(pub)
+ else: # Use pure-python
+ pub = btctools.ecdsa_recover(data, sign)
+ sign_address = btctools.pubtoaddr(pub)
return sign_address == address
else: # Backward compatible old style
bitcoin = BitcoinECC.Bitcoin()
diff --git a/src/Debug/Debug.py b/src/Debug/Debug.py
index 3ab4c48b..f7070963 100644
--- a/src/Debug/Debug.py
+++ b/src/Debug/Debug.py
@@ -10,6 +10,7 @@ class Notify(Exception):
def formatException(err=None):
+ if type(err) == Notify: return err
exc_type, exc_obj, exc_tb = sys.exc_info()
if not err: err = exc_obj.message
tb = []
@@ -21,6 +22,7 @@ def formatException(err=None):
if __name__ == "__main__":
+
try:
print 1/0
except Exception, err:
@@ -36,4 +38,10 @@ if __name__ == "__main__":
except Exception, err:
print err
print "Json load error: %s" % formatException(err)
+
+ try:
+ raise Notify("nothing...")
+ except Exception, err:
+ print "Notify: %s" % formatException(err)
+
loadJson()
diff --git a/src/Ui/UiServer.py b/src/Ui/UiServer.py
index b6848e55..6eec62c9 100644
--- a/src/Ui/UiServer.py
+++ b/src/Ui/UiServer.py
@@ -20,17 +20,25 @@ class UiWSGIHandler(WSGIHandler):
def run_application(self):
self.server.sockets[self.client_address] = self.socket
if "HTTP_UPGRADE" in self.environ: # Websocket request
- ws_handler = WebSocketHandler(*self.args, **self.kwargs)
- ws_handler.__dict__ = self.__dict__ # Match class variables
- ws_handler.run_application()
+ try:
+ ws_handler = WebSocketHandler(*self.args, **self.kwargs)
+ ws_handler.__dict__ = self.__dict__ # Match class variables
+ ws_handler.run_application()
+ except Exception, err:
+ logging.error("UiWSGIHandler websocket error: %s" % Debug.formatException(err))
+ if config.debug: # Allow websocket errors to appear on /Debug
+ import sys
+ del self.server.sockets[self.client_address]
+ sys.modules["main"].DebugHook.handleError()
else: # Standard HTTP request
#print self.application.__class__.__name__
try:
- return super(UiWSGIHandler, self).run_application()
+ super(UiWSGIHandler, self).run_application()
except Exception, err:
logging.error("UiWSGIHandler error: %s" % Debug.formatException(err))
if config.debug: # Allow websocket errors to appear on /Debug
import sys
+ del self.server.sockets[self.client_address]
sys.modules["main"].DebugHook.handleError()
del self.server.sockets[self.client_address]
diff --git a/src/lib/opensslVerify/HashInfo.txt b/src/lib/opensslVerify/HashInfo.txt
new file mode 100644
index 00000000..f5308e27
Binary files /dev/null and b/src/lib/opensslVerify/HashInfo.txt differ
diff --git a/src/lib/opensslVerify/OpenSSL License.txt b/src/lib/opensslVerify/OpenSSL License.txt
new file mode 100644
index 00000000..97234459
--- /dev/null
+++ b/src/lib/opensslVerify/OpenSSL License.txt
@@ -0,0 +1,126 @@
+
+ LICENSE ISSUES
+ ==============
+
+ The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
+ the OpenSSL License and the original SSLeay license apply to the toolkit.
+ See below for the actual license texts. Actually both licenses are BSD-style
+ Open Source licenses. In case of any license issues related to OpenSSL
+ please contact openssl-core@openssl.org.
+
+ OpenSSL License
+ ---------------
+
+/* ====================================================================
+ * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+ Original SSLeay License
+ -----------------------
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
diff --git a/src/lib/opensslVerify/ReadMe.txt b/src/lib/opensslVerify/ReadMe.txt
new file mode 100644
index 00000000..352ccef6
--- /dev/null
+++ b/src/lib/opensslVerify/ReadMe.txt
@@ -0,0 +1,59 @@
+=============================================================================
+OpenSSL v1.0.2a Precompiled Binaries for Win32
+-----------------------------------------------------------------------------
+
+ *** Release Information ***
+
+Release Date: Mrz 20, 2015
+
+Author: Frederik A. Winkelsdorf (opendec.wordpress.com)
+ for the Indy Project (www.indyproject.org)
+
+Requirements: Indy 10.5.5+ (SVN Version or Delphi 2009 and newer)
+
+Dependencies: The libraries have no noteworthy dependencies
+
+Installation: Copy both DLL files into your application directory
+
+Supported OS: Windows 2000 up to Windows 8
+
+-----------------------------------------------------------------------------
+
+ *** Legal Disclaimer ***
+
+THIS SOFTWARE IS PROVIDED BY ITS AUTHOR AND THE INDY PROJECT "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 REGENTS 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.
+
+OpenSSL license terms are provided in the file "OpenSSL License.txt".
+
+PLEASE CHECK IF YOU NEED TO COMPLY WITH EXPORT RESTRICTIONS FOR CRYPTOGRAPHIC
+SOFTWARE AND/OR PATENTS.
+
+-----------------------------------------------------------------------------
+
+ *** Build Information Win32 ***
+
+Built with: Microsoft Visual C++ 2008 Express Edition
+ The Netwide Assembler (NASM) v2.11.05 Win32
+ Strawberry Perl v5.20.0.1 Win32 Portable
+ Windows PowerShell
+ FinalBuilder 7 Embarcadero Edition
+
+Commands: perl configure VC-WIN32
+ ms\do_nasm
+ adjusted ms\ntdll.mak (replaced "/MD" with "/MT")
+ adjusted ms\version32.rc (Indy Information inserted)
+ nmake -f ms\ntdll.mak
+ nmake -f ms\ntdll.mak test
+ editbin.exe /rebase:base=0x11000000 libeay32.dll
+ editbin.exe /rebase:base=0x12000000 ssleay32.dll
+
+=============================================================================
\ No newline at end of file
diff --git a/src/lib/opensslVerify/__init__.py b/src/lib/opensslVerify/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/lib/opensslVerify/libeay32.dll b/src/lib/opensslVerify/libeay32.dll
new file mode 100644
index 00000000..6359cc5a
Binary files /dev/null and b/src/lib/opensslVerify/libeay32.dll differ
diff --git a/src/lib/opensslVerify/opensslVerify.py b/src/lib/opensslVerify/opensslVerify.py
new file mode 100644
index 00000000..84e3a8b7
--- /dev/null
+++ b/src/lib/opensslVerify/opensslVerify.py
@@ -0,0 +1,392 @@
+# Code is borrowed from https://github.com/blocktrail/python-bitcoinlib
+# Thanks!
+
+import base64, hashlib
+
+import ctypes
+import ctypes.util
+_bchr = chr
+_bord = ord
+try:
+ _ssl = ctypes.CDLL("src/lib/opensslVerify/libeay32.dll")
+except:
+ _ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library('ssl') or ctypes.util.find_library('crypto') or 'libeay32')
+
+import sys
+
+openssl_version = "%.9X" % _ssl.SSLeay()
+
+
+# this specifies the curve used with ECDSA.
+_NID_secp256k1 = 714 # from openssl/obj_mac.h
+
+# Thx to Sam Devlin for the ctypes magic 64-bit fix.
+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
+
+# From openssl/ecdsa.h
+class ECDSA_SIG_st(ctypes.Structure):
+ _fields_ = [("r", ctypes.c_void_p),
+ ("s", ctypes.c_void_p)]
+
+class CECKey:
+ """Wrapper around OpenSSL's EC_KEY"""
+
+ POINT_CONVERSION_COMPRESSED = 2
+ POINT_CONVERSION_UNCOMPRESSED = 4
+
+ def __init__(self):
+ self.k = _ssl.EC_KEY_new_by_curve_name(_NID_secp256k1)
+
+ def __del__(self):
+ if _ssl:
+ _ssl.EC_KEY_free(self.k)
+ self.k = None
+
+ def set_secretbytes(self, secret):
+ priv_key = _ssl.BN_bin2bn(secret, 32, _ssl.BN_new())
+ group = _ssl.EC_KEY_get0_group(self.k)
+ pub_key = _ssl.EC_POINT_new(group)
+ ctx = _ssl.BN_CTX_new()
+ if not _ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx):
+ raise ValueError("Could not derive public key from the supplied secret.")
+ _ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx)
+ _ssl.EC_KEY_set_private_key(self.k, priv_key)
+ _ssl.EC_KEY_set_public_key(self.k, pub_key)
+ _ssl.EC_POINT_free(pub_key)
+ _ssl.BN_CTX_free(ctx)
+ return self.k
+
+ def set_privkey(self, key):
+ self.mb = ctypes.create_string_buffer(key)
+ return _ssl.d2i_ECPrivateKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key))
+
+ def set_pubkey(self, key):
+ self.mb = ctypes.create_string_buffer(key)
+ return _ssl.o2i_ECPublicKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key))
+
+ def get_privkey(self):
+ size = _ssl.i2d_ECPrivateKey(self.k, 0)
+ mb_pri = ctypes.create_string_buffer(size)
+ _ssl.i2d_ECPrivateKey(self.k, ctypes.byref(ctypes.pointer(mb_pri)))
+ return mb_pri.raw
+
+ def get_pubkey(self):
+ size = _ssl.i2o_ECPublicKey(self.k, 0)
+ mb = ctypes.create_string_buffer(size)
+ _ssl.i2o_ECPublicKey(self.k, ctypes.byref(ctypes.pointer(mb)))
+ return mb.raw
+
+ def get_raw_ecdh_key(self, other_pubkey):
+ ecdh_keybuffer = ctypes.create_string_buffer(32)
+ r = _ssl.ECDH_compute_key(ctypes.pointer(ecdh_keybuffer), 32,
+ _ssl.EC_KEY_get0_public_key(other_pubkey.k),
+ self.k, 0)
+ if r != 32:
+ raise Exception('CKey.get_ecdh_key(): ECDH_compute_key() failed')
+ return ecdh_keybuffer.raw
+
+ def get_ecdh_key(self, other_pubkey, kdf=lambda k: hashlib.sha256(k).digest()):
+ # FIXME: be warned it's not clear what the kdf should be as a default
+ r = self.get_raw_ecdh_key(other_pubkey)
+ return kdf(r)
+
+ def sign(self, hash):
+ if not isinstance(hash, bytes):
+ raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
+ if len(hash) != 32:
+ raise ValueError('Hash must be exactly 32 bytes long')
+
+ sig_size0 = ctypes.c_uint32()
+ sig_size0.value = _ssl.ECDSA_size(self.k)
+ mb_sig = ctypes.create_string_buffer(sig_size0.value)
+ result = _ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
+ assert 1 == result
+ if bitcoin.core.script.IsLowDERSignature(mb_sig.raw[:sig_size0.value]):
+ return mb_sig.raw[:sig_size0.value]
+ else:
+ return self.signature_to_low_s(mb_sig.raw[:sig_size0.value])
+
+ def sign_compact(self, hash):
+ if not isinstance(hash, bytes):
+ raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
+ if len(hash) != 32:
+ raise ValueError('Hash must be exactly 32 bytes long')
+
+ sig_size0 = ctypes.c_uint32()
+ sig_size0.value = _ssl.ECDSA_size(self.k)
+ mb_sig = ctypes.create_string_buffer(sig_size0.value)
+ result = _ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
+ assert 1 == result
+
+ if bitcoin.core.script.IsLowDERSignature(mb_sig.raw[:sig_size0.value]):
+ sig = mb_sig.raw[:sig_size0.value]
+ else:
+ sig = self.signature_to_low_s(mb_sig.raw[:sig_size0.value])
+
+ sig = bitcoin.core.DERSignature.deserialize(sig)
+
+ r_val = sig.r
+ s_val = sig.s
+
+ # assert that the r and s are less than 32 long, excluding leading 0s
+ assert len(r_val) <= 32 or r_val[0:-32] == b'\x00'
+ assert len(s_val) <= 32 or s_val[0:-32] == b'\x00'
+
+ # ensure r and s are always 32 chars long by 0padding
+ r_val = ((b'\x00' * 32) + r_val)[-32:]
+ s_val = ((b'\x00' * 32) + s_val)[-32:]
+
+ # tmp pubkey of self, but always compressed
+ pubkey = CECKey()
+ pubkey.set_pubkey(self.get_pubkey())
+ pubkey.set_compressed(True)
+
+ # bitcoin core does <4, but I've seen other places do <2 and I've never seen a i > 1 so far
+ for i in range(0, 4):
+ cec_key = CECKey()
+ cec_key.set_compressed(True)
+
+ result = cec_key.recover(r_val, s_val, hash, len(hash), i, 1)
+ if result == 1:
+ if cec_key.get_pubkey() == pubkey.get_pubkey():
+ return r_val + s_val, i
+
+ raise ValueError
+
+ def signature_to_low_s(self, sig):
+ der_sig = ECDSA_SIG_st()
+ _ssl.d2i_ECDSA_SIG(ctypes.byref(ctypes.pointer(der_sig)), ctypes.byref(ctypes.c_char_p(sig)), len(sig))
+ group = _ssl.EC_KEY_get0_group(self.k)
+ order = _ssl.BN_new()
+ halforder = _ssl.BN_new()
+ ctx = _ssl.BN_CTX_new()
+ _ssl.EC_GROUP_get_order(group, order, ctx)
+ _ssl.BN_rshift1(halforder, order)
+
+ # Verify that s is over half the order of the curve before we actually subtract anything from it
+ if _ssl.BN_cmp(der_sig.s, halforder) > 0:
+ _ssl.BN_sub(der_sig.s, order, der_sig.s)
+
+ _ssl.BN_free(halforder)
+ _ssl.BN_free(order)
+ _ssl.BN_CTX_free(ctx)
+
+ derlen = _ssl.i2d_ECDSA_SIG(ctypes.pointer(der_sig), 0)
+ if derlen == 0:
+ _ssl.ECDSA_SIG_free(der_sig)
+ return None
+ new_sig = ctypes.create_string_buffer(derlen)
+ _ssl.i2d_ECDSA_SIG(ctypes.pointer(der_sig), ctypes.byref(ctypes.pointer(new_sig)))
+ _ssl.BN_free(der_sig.r)
+ _ssl.BN_free(der_sig.s)
+
+ return new_sig.raw
+
+ def verify(self, hash, sig):
+ """Verify a DER signature"""
+ if not sig:
+ return false
+
+ # New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first.
+ norm_sig = ctypes.c_void_p(0)
+ _ssl.d2i_ECDSA_SIG(ctypes.byref(norm_sig), ctypes.byref(ctypes.c_char_p(sig)), len(sig))
+
+ derlen = _ssl.i2d_ECDSA_SIG(norm_sig, 0)
+ if derlen == 0:
+ _ssl.ECDSA_SIG_free(norm_sig)
+ return false
+
+ norm_der = ctypes.create_string_buffer(derlen)
+ _ssl.i2d_ECDSA_SIG(norm_sig, ctypes.byref(ctypes.pointer(norm_der)))
+ _ssl.ECDSA_SIG_free(norm_sig)
+
+ # -1 = error, 0 = bad sig, 1 = good
+ return _ssl.ECDSA_verify(0, hash, len(hash), norm_der, derlen, self.k) == 1
+
+ def set_compressed(self, compressed):
+ if compressed:
+ form = self.POINT_CONVERSION_COMPRESSED
+ else:
+ form = self.POINT_CONVERSION_UNCOMPRESSED
+ _ssl.EC_KEY_set_conv_form(self.k, form)
+
+ def recover(self, sigR, sigS, msg, msglen, recid, check):
+ """
+ Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
+ recid selects which key is recovered
+ if check is non-zero, additional checks are performed
+ """
+ i = int(recid / 2)
+
+ r = None
+ s = None
+ ctx = None
+ R = None
+ O = None
+ Q = None
+
+ assert len(sigR) == 32, len(sigR)
+ assert len(sigS) == 32, len(sigS)
+
+ try:
+ r = _ssl.BN_bin2bn(bytes(sigR), len(sigR), _ssl.BN_new())
+ s = _ssl.BN_bin2bn(bytes(sigS), len(sigS), _ssl.BN_new())
+
+ group = _ssl.EC_KEY_get0_group(self.k)
+ ctx = _ssl.BN_CTX_new()
+ order = _ssl.BN_CTX_get(ctx)
+ ctx = _ssl.BN_CTX_new()
+
+ if not _ssl.EC_GROUP_get_order(group, order, ctx):
+ return -2
+
+ x = _ssl.BN_CTX_get(ctx)
+ if not _ssl.BN_copy(x, order):
+ return -1
+ if not _ssl.BN_mul_word(x, i):
+ return -1
+ if not _ssl.BN_add(x, x, r):
+ return -1
+
+ field = _ssl.BN_CTX_get(ctx)
+ if not _ssl.EC_GROUP_get_curve_GFp(group, field, None, None, ctx):
+ return -2
+
+ if _ssl.BN_cmp(x, field) >= 0:
+ return 0
+
+ R = _ssl.EC_POINT_new(group)
+ if R is None:
+ return -2
+ if not _ssl.EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx):
+ return 0
+
+ if check:
+ O = _ssl.EC_POINT_new(group)
+ if O is None:
+ return -2
+ if not _ssl.EC_POINT_mul(group, O, None, R, order, ctx):
+ return -2
+ if not _ssl.EC_POINT_is_at_infinity(group, O):
+ return 0
+
+ Q = _ssl.EC_POINT_new(group)
+ if Q is None:
+ return -2
+
+ n = _ssl.EC_GROUP_get_degree(group)
+ e = _ssl.BN_CTX_get(ctx)
+ if not _ssl.BN_bin2bn(msg, msglen, e):
+ return -1
+
+ if 8 * msglen > n:
+ _ssl.BN_rshift(e, e, 8 - (n & 7))
+
+ zero = _ssl.BN_CTX_get(ctx)
+ # if not _ssl.BN_zero(zero):
+ # return -1
+ if not _ssl.BN_mod_sub(e, zero, e, order, ctx):
+ return -1
+ rr = _ssl.BN_CTX_get(ctx)
+ if not _ssl.BN_mod_inverse(rr, r, order, ctx):
+ return -1
+ sor = _ssl.BN_CTX_get(ctx)
+ if not _ssl.BN_mod_mul(sor, s, rr, order, ctx):
+ return -1
+ eor = _ssl.BN_CTX_get(ctx)
+ if not _ssl.BN_mod_mul(eor, e, rr, order, ctx):
+ return -1
+ if not _ssl.EC_POINT_mul(group, Q, eor, R, sor, ctx):
+ return -2
+
+ if not _ssl.EC_KEY_set_public_key(self.k, Q):
+ return -2
+
+ return 1
+ finally:
+ if r: _ssl.BN_free(r)
+ if s: _ssl.BN_free(s)
+ if ctx: _ssl.BN_CTX_free(ctx)
+ if R: _ssl.EC_POINT_free(R)
+ if O: _ssl.EC_POINT_free(O)
+ if Q: _ssl.EC_POINT_free(Q)
+
+
+def recover_compact(hash, sig):
+ """Recover a public key from a compact signature."""
+ if len(sig) != 65:
+ raise ValueError("Signature should be 65 characters, not [%d]" % (len(sig), ))
+
+ recid = (_bord(sig[0]) - 27) & 3
+ compressed = (_bord(sig[0]) - 27) & 4 != 0
+
+ cec_key = CECKey()
+ cec_key.set_compressed(compressed)
+
+ sigR = sig[1:33]
+ sigS = sig[33:65]
+
+ result = cec_key.recover(sigR, sigS, hash, len(hash), recid, 0)
+
+ if result < 1:
+ return False
+
+ pubkey = cec_key.get_pubkey()
+
+ return pubkey
+
+def encode(val, base, minlen=0):
+ base, minlen = int(base), int(minlen)
+ code_string = ''.join([chr(x) for x in range(256)])
+ result = ""
+ while val > 0:
+ result = code_string[val % base] + result
+ val //= base
+ return code_string[0] * max(minlen - len(result), 0) + result
+
+def num_to_var_int(x):
+ x = int(x)
+ if x < 253: return chr(x)
+ elif x < 65536: return chr(253)+encode(x, 256, 2)[::-1]
+ elif x < 4294967296: return chr(254) + encode(x, 256, 4)[::-1]
+ else: return chr(255) + encode(x, 256, 8)[::-1]
+
+
+def msg_magic(message):
+ return "\x18Bitcoin Signed Message:\n" + num_to_var_int( len(message) ) + message
+
+
+def getMessagePubkey(message, sig):
+ message = msg_magic(message)
+ hash = hashlib.sha256(hashlib.sha256(message).digest()).digest()
+ sig = base64.b64decode(sig)
+
+ pubkey = recover_compact(hash, sig)
+ return pubkey
+
+def test():
+ sign = "HGbib2kv9gm9IJjDt1FXbXFczZi35u0rZR3iPUIt5GglDDCeIQ7v8eYXVNIaLoJRI4URGZrhwmsYQ9aVtRTnTfQ="
+ pubkey = "044827c756561b8ef6b28b5e53a000805adbf4938ab82e1c2b7f7ea16a0d6face9a509a0a13e794d742210b00581f3e249ebcc705240af2540ea19591091ac1d41"
+ assert getMessagePubkey("hello", sign).encode("hex") == pubkey
+
+test() # Make sure it working right
+
+if __name__ == "__main__":
+ import time
+ from pybitcointools import bitcoin as btctools
+ priv = "5JsunC55XGVqFQj5kPGK4MWgTL26jKbnPhjnmchSNPo75XXCwtk"
+ address = "1N2XWu5soeppX2qUjvrf81rpdbShKJrjTr"
+ sign = btctools.ecdsa_sign("hello", priv) # HGbib2kv9gm9IJjDt1FXbXFczZi35u0rZR3iPUIt5GglDDCeIQ7v8eYXVNIaLoJRI4URGZrhwmsYQ9aVtRTnTfQ=
+
+ s = time.time()
+ for i in range(100):
+ pubkey = getMessagePubkey("hello", sign)
+ verified = btctools.pubkey_to_address(pubkey) == address
+ print "100x Verified", verified, time.time()-s
diff --git a/src/main.py b/src/main.py
index 411fb120..6bdc7484 100644
--- a/src/main.py
+++ b/src/main.py
@@ -124,7 +124,9 @@ class Actions:
def siteVerify(self, address):
+ import time
from Site import Site
+ s = time.time()
logging.info("Verifing site: %s..." % address)
site = Site(address)
@@ -138,7 +140,7 @@ class Actions:
logging.info("Verifying site files...")
bad_files = site.storage.verifyFiles()
if not bad_files:
- logging.info("[OK] All file sha512sum matches!")
+ logging.info("[OK] All file sha512sum matches! (%.3fs)" % (time.time()-s))
else:
logging.error("[ERROR] Error during verifying site files!")
diff --git a/zeronet.py b/zeronet.py
index 5fce3ec1..a0c4d373 100644
--- a/zeronet.py
+++ b/zeronet.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
def main():
- print " - Starting ZeroNet..."
+ print "- Starting ZeroNet..."
import sys, os
try:
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) # Imports relative to src