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