From 6778d6046f941c0123a309df7cd06a2f2714a23c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Fri, 13 Jan 2017 12:02:34 +0100 Subject: [PATCH] Add OpenSSL 1.1.0 compatibility - thanks to Wolfgang Frisch --- src/bitmessageqt/support.py | 6 +- src/depends.py | 21 +++--- src/highlevelcrypto.py | 4 +- src/pyelliptic/cipher.py | 5 +- src/pyelliptic/ecc.py | 42 ++++++++--- src/pyelliptic/openssl.py | 134 ++++++++++++++++++++++++++++-------- 6 files changed, 153 insertions(+), 59 deletions(-) diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py index 07d0ab3a..83d34e74 100644 --- a/src/bitmessageqt/support.py +++ b/src/bitmessageqt/support.py @@ -100,11 +100,7 @@ def createSupportMessage(myapp): architecture = "32" if ctypes.sizeof(ctypes.c_voidp) == 4 else "64" pythonversion = sys.version - SSLEAY_VERSION = 0 - OpenSSL._lib.SSLeay.restype = ctypes.c_long - OpenSSL._lib.SSLeay_version.restype = ctypes.c_char_p - OpenSSL._lib.SSLeay_version.argtypes = [ctypes.c_int] - opensslversion = "%s (Python internal), %s (external for PyElliptic)" % (ssl.OPENSSL_VERSION, OpenSSL._lib.SSLeay_version(SSLEAY_VERSION)) + opensslversion = "%s (Python internal), %s (external for PyElliptic)" % (ssl.OPENSSL_VERSION, OpenSSL()._version) frozen = "N/A" if paths.frozen: diff --git a/src/depends.py b/src/depends.py index 294c6a22..3e207f61 100755 --- a/src/depends.py +++ b/src/depends.py @@ -1,6 +1,7 @@ #! python import sys +import pyelliptic.openssl #Only really old versions of Python don't have sys.hexversion. We don't support #them. The logging module was introduced in Python 2.3 @@ -106,8 +107,9 @@ def check_openssl(): except: pass - SSLEAY_VERSION = 0 - SSLEAY_CFLAGS = 2 + openssl_version = None + openssl_hexversion = None + openssl_cflags = None cflags_regex = re.compile(r'(?:OPENSSL_NO_)(AES|EC|ECDH|ECDSA)(?!\w)') @@ -118,23 +120,18 @@ def check_openssl(): except OSError: continue logger.info('OpenSSL Name: ' + library._name) - try: - library.SSLeay.restype = ctypes.c_long - library.SSLeay_version.restype = ctypes.c_char_p - library.SSLeay_version.argtypes = [ctypes.c_int] - except AttributeError: + openssl_version, openssl_hexversion, openssl_cflags = pyelliptic.openssl.get_version(library) + if not openssl_version: logger.error('Cannot determine version of this OpenSSL library.') return False - logger.info('OpenSSL Version: ' + library.SSLeay_version(SSLEAY_VERSION)) - compile_options = library.SSLeay_version(SSLEAY_CFLAGS) - logger.info('OpenSSL Compile Options: ' + compile_options) - openssl_hexversion = library.SSLeay() + logger.info('OpenSSL Version: ' + openssl_version) + logger.info('OpenSSL Compile Options: ' + openssl_cflags) #PyElliptic uses EVP_CIPHER_CTX_new and EVP_CIPHER_CTX_free which were #introduced in 0.9.8b. if openssl_hexversion < 0x90802F: logger.error('This OpenSSL library is too old. PyBitmessage requires OpenSSL 0.9.8b or later with AES, Elliptic Curves (EC), ECDH, and ECDSA enabled.') return False - matches = cflags_regex.findall(compile_options) + matches = cflags_regex.findall(openssl_cflags) if len(matches) > 0: logger.error('This OpenSSL library is missing the following required features: ' + ', '.join(matches) + '. PyBitmessage requires OpenSSL 0.9.8b or later with AES, Elliptic Curves (EC), ECDH, and ECDSA enabled.') return False diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index c8b67e77..50f13cce 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -35,7 +35,7 @@ def sign(msg,hexPrivkey): # upgrade PyBitmessage gracefully. # https://github.com/yann2192/pyelliptic/pull/33 # More discussion: https://github.com/yann2192/pyelliptic/issues/32 - return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_ecdsa) # SHA1 + return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.digest_ecdsa_sha1) # SHA1 #return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256) # SHA256. We should switch to this eventually. # Verifies with hex public key def verify(msg,sig,hexPubkey): @@ -44,7 +44,7 @@ def verify(msg,sig,hexPubkey): # of them passes then we will be satisfied. Eventually this can # be simplified and we'll only check with SHA256. try: - sigVerifyPassed = makePubCryptor(hexPubkey).verify(sig,msg,digest_alg=OpenSSL.EVP_ecdsa) # old SHA1 algorithm. + sigVerifyPassed = makePubCryptor(hexPubkey).verify(sig,msg,digest_alg=OpenSSL.digest_ecdsa_sha1) # old SHA1 algorithm. except: sigVerifyPassed = False if sigVerifyPassed: diff --git a/src/pyelliptic/cipher.py b/src/pyelliptic/cipher.py index fb8d0d46..4d932210 100644 --- a/src/pyelliptic/cipher.py +++ b/src/pyelliptic/cipher.py @@ -77,5 +77,8 @@ class Cipher: return buff + self.final() def __del__(self): - OpenSSL.EVP_CIPHER_CTX_cleanup(self.ctx) + if OpenSSL._hexversion > 0x10100000: + OpenSSL.EVP_CIPHER_CTX_reset(self.ctx) + else: + OpenSSL.EVP_CIPHER_CTX_cleanup(self.ctx) OpenSSL.EVP_CIPHER_CTX_free(self.ctx) diff --git a/src/pyelliptic/ecc.py b/src/pyelliptic/ecc.py index 269db952..7b5a07d2 100644 --- a/src/pyelliptic/ecc.py +++ b/src/pyelliptic/ecc.py @@ -223,7 +223,10 @@ class ECC: if (OpenSSL.EC_KEY_set_private_key(own_key, own_priv_key)) == 0: raise Exception("[OpenSSL] EC_KEY_set_private_key FAIL ...") - OpenSSL.ECDH_set_method(own_key, OpenSSL.ECDH_OpenSSL()) + if OpenSSL._hexversion > 0x10100000: + OpenSSL.EC_KEY_set_method(own_key, OpenSSL.EC_KEY_OpenSSL()) + else: + OpenSSL.ECDH_set_method(own_key, OpenSSL.ECDH_OpenSSL()) ecdh_keylen = OpenSSL.ECDH_compute_key( ecdh_keybuffer, 32, other_pub_key, own_key, 0) @@ -299,7 +302,7 @@ class ECC: if privkey is not None: OpenSSL.BN_free(priv_key) - def sign(self, inputb, digest_alg=OpenSSL.EVP_ecdsa): + def sign(self, inputb, digest_alg=OpenSSL.digest_ecdsa_sha1): """ Sign the input with ECDSA method and returns the signature """ @@ -307,7 +310,10 @@ class ECC: size = len(inputb) buff = OpenSSL.malloc(inputb, size) digest = OpenSSL.malloc(0, 64) - md_ctx = OpenSSL.EVP_MD_CTX_create() + if OpenSSL._hexversion > 0x10100000: + md_ctx = OpenSSL.EVP_MD_CTX_new() + else: + md_ctx = OpenSSL.EVP_MD_CTX_create() dgst_len = OpenSSL.pointer(OpenSSL.c_int(0)) siglen = OpenSSL.pointer(OpenSSL.c_int(0)) sig = OpenSSL.malloc(0, 151) @@ -337,7 +343,10 @@ class ECC: if (OpenSSL.EC_KEY_check_key(key)) == 0: raise Exception("[OpenSSL] EC_KEY_check_key FAIL ...") - OpenSSL.EVP_MD_CTX_init(md_ctx) + if OpenSSL._hexversion > 0x10100000: + OpenSSL.EVP_MD_CTX_new(md_ctx) + else: + OpenSSL.EVP_MD_CTX_init(md_ctx) OpenSSL.EVP_DigestInit_ex(md_ctx, digest_alg(), None) if (OpenSSL.EVP_DigestUpdate(md_ctx, buff, size)) == 0: @@ -356,9 +365,13 @@ class ECC: OpenSSL.BN_free(pub_key_y) OpenSSL.BN_free(priv_key) OpenSSL.EC_POINT_free(pub_key) - OpenSSL.EVP_MD_CTX_destroy(md_ctx) + if OpenSSL._hexversion > 0x10100000: + OpenSSL.EVP_MD_CTX_free(md_ctx) + else: + OpenSSL.EVP_MD_CTX_destroy(md_ctx) + pass - def verify(self, sig, inputb, digest_alg=OpenSSL.EVP_ecdsa): + def verify(self, sig, inputb, digest_alg=OpenSSL.digest_ecdsa_sha1): """ Verify the signature with the input and the local public key. Returns a boolean @@ -368,8 +381,10 @@ class ECC: binputb = OpenSSL.malloc(inputb, len(inputb)) digest = OpenSSL.malloc(0, 64) dgst_len = OpenSSL.pointer(OpenSSL.c_int(0)) - md_ctx = OpenSSL.EVP_MD_CTX_create() - + if OpenSSL._hexversion > 0x10100000: + md_ctx = OpenSSL.EVP_MD_CTX_new() + else: + md_ctx = OpenSSL.EVP_MD_CTX_create() key = OpenSSL.EC_KEY_new_by_curve_name(self.curve) if key == 0: @@ -390,8 +405,10 @@ class ECC: raise Exception("[OpenSSL] EC_KEY_set_public_key FAIL ...") if (OpenSSL.EC_KEY_check_key(key)) == 0: raise Exception("[OpenSSL] EC_KEY_check_key FAIL ...") - - OpenSSL.EVP_MD_CTX_init(md_ctx) + if OpenSSL._hexversion > 0x10100000: + OpenSSL.EVP_MD_CTX_new(md_ctx) + else: + OpenSSL.EVP_MD_CTX_init(md_ctx) OpenSSL.EVP_DigestInit_ex(md_ctx, digest_alg(), None) if (OpenSSL.EVP_DigestUpdate(md_ctx, binputb, len(inputb))) == 0: raise Exception("[OpenSSL] EVP_DigestUpdate FAIL ...") @@ -414,7 +431,10 @@ class ECC: OpenSSL.BN_free(pub_key_x) OpenSSL.BN_free(pub_key_y) OpenSSL.EC_POINT_free(pub_key) - OpenSSL.EVP_MD_CTX_destroy(md_ctx) + if OpenSSL._hexversion > 0x10100000: + OpenSSL.EVP_MD_CTX_free(md_ctx) + else: + OpenSSL.EVP_MD_CTX_destroy(md_ctx) @staticmethod def encrypt(data, pubkey, ephemcurve=None, ciphername='aes-256-cbc'): diff --git a/src/pyelliptic/openssl.py b/src/pyelliptic/openssl.py index be2f2afc..db9e7d24 100644 --- a/src/pyelliptic/openssl.py +++ b/src/pyelliptic/openssl.py @@ -31,6 +31,37 @@ class CipherName: return self._blocksize +def get_version(library): + version = None + hexversion = None + cflags = None + try: + #OpenSSL 1.1 + OPENSSL_VERSION = 0 + OPENSSL_CFLAGS = 1 + library.OpenSSL_version.argtypes = [ctypes.c_int] + library.OpenSSL_version.restype = ctypes.c_char_p + version = library.OpenSSL_version(OPENSSL_VERSION) + cflags = library.OpenSSL_version(OPENSSL_CFLAGS) + library.OpenSSL_version_num.restype = ctypes.c_long + hexversion = library.OpenSSL_version_num() + except AttributeError: + try: + #OpenSSL 1.0 + SSLEAY_VERSION = 0 + SSLEAY_CFLAGS = 2 + library.SSLeay.restype = ctypes.c_long + library.SSLeay_version.restype = ctypes.c_char_p + library.SSLeay_version.argtypes = [ctypes.c_int] + version = library.SSLeay_version(SSLEAY_VERSION) + cflags = library.SSLeay_version(SSLEAY_CFLAGS) + hexversion = library.SSLeay() + except AttributeError: + #raise NotImplementedError('Cannot determine version of this OpenSSL library.') + pass + return (version, hexversion, cflags) + + class _OpenSSL: """ Wrapper for OpenSSL using ctypes @@ -40,6 +71,7 @@ class _OpenSSL: Build the wrapper """ self._lib = ctypes.CDLL(library) + self._version, self._hexversion, self._cflags = get_version(self._lib) self.pointer = ctypes.pointer self.c_int = ctypes.c_int @@ -138,18 +170,27 @@ class _OpenSSL: self.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p] - self.ECDH_OpenSSL = self._lib.ECDH_OpenSSL - self._lib.ECDH_OpenSSL.restype = ctypes.c_void_p - self._lib.ECDH_OpenSSL.argtypes = [] + if self._hexversion > 0x10100000: + self.EC_KEY_OpenSSL = self._lib.EC_KEY_OpenSSL + self._lib.EC_KEY_OpenSSL.restype = ctypes.c_void_p + self._lib.EC_KEY_OpenSSL.argtypes = [] + + self.EC_KEY_set_method = self._lib.EC_KEY_set_method + self._lib.EC_KEY_set_method.restype = ctypes.c_int + self._lib.EC_KEY_set_method.argtypes = [ctypes.c_void_p, ctypes.c_void_p] + else: + self.ECDH_OpenSSL = self._lib.ECDH_OpenSSL + self._lib.ECDH_OpenSSL.restype = ctypes.c_void_p + self._lib.ECDH_OpenSSL.argtypes = [] + + self.ECDH_set_method = self._lib.ECDH_set_method + self._lib.ECDH_set_method.restype = ctypes.c_int + self._lib.ECDH_set_method.argtypes = [ctypes.c_void_p, ctypes.c_void_p] 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.ECDH_set_method = self._lib.ECDH_set_method - self._lib.ECDH_set_method.restype = ctypes.c_int - self._lib.ECDH_set_method.argtypes = [ctypes.c_void_p, ctypes.c_void_p] - self.ECDH_compute_key = self._lib.ECDH_compute_key self.ECDH_compute_key.restype = ctypes.c_int self.ECDH_compute_key.argtypes = [ctypes.c_void_p, @@ -208,10 +249,15 @@ class _OpenSSL: self.EVP_rc4 = self._lib.EVP_rc4 self.EVP_rc4.restype = ctypes.c_void_p self.EVP_rc4.argtypes = [] - - self.EVP_CIPHER_CTX_cleanup = self._lib.EVP_CIPHER_CTX_cleanup - self.EVP_CIPHER_CTX_cleanup.restype = ctypes.c_int - self.EVP_CIPHER_CTX_cleanup.argtypes = [ctypes.c_void_p] + + if self._hexversion > 0x10100000: + self.EVP_CIPHER_CTX_reset = self._lib.EVP_CIPHER_CTX_reset + self.EVP_CIPHER_CTX_reset.restype = ctypes.c_int + self.EVP_CIPHER_CTX_reset.argtypes = [ctypes.c_void_p] + else: + self.EVP_CIPHER_CTX_cleanup = self._lib.EVP_CIPHER_CTX_cleanup + self.EVP_CIPHER_CTX_cleanup.restype = ctypes.c_int + self.EVP_CIPHER_CTX_cleanup.argtypes = [ctypes.c_void_p] self.EVP_CIPHER_CTX_free = self._lib.EVP_CIPHER_CTX_free self.EVP_CIPHER_CTX_free.restype = None @@ -250,10 +296,6 @@ class _OpenSSL: self.EVP_DigestFinal_ex.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] - self.EVP_ecdsa = self._lib.EVP_ecdsa - self._lib.EVP_ecdsa.restype = ctypes.c_void_p - self._lib.EVP_ecdsa.argtypes = [] - 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, @@ -264,23 +306,47 @@ class _OpenSSL: 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.EVP_MD_CTX_create = self._lib.EVP_MD_CTX_create - self.EVP_MD_CTX_create.restype = ctypes.c_void_p - self.EVP_MD_CTX_create.argtypes = [] + if self._hexversion > 0x10100000: + self.EVP_MD_CTX_new = self._lib.EVP_MD_CTX_new + self.EVP_MD_CTX_new.restype = ctypes.c_void_p + self.EVP_MD_CTX_new.argtypes = [] + + self.EVP_MD_CTX_reset = self._lib.EVP_MD_CTX_reset + self.EVP_MD_CTX_reset.restype = None + self.EVP_MD_CTX_reset.argtypes = [ctypes.c_void_p] - self.EVP_MD_CTX_init = self._lib.EVP_MD_CTX_init - self.EVP_MD_CTX_init.restype = None - self.EVP_MD_CTX_init.argtypes = [ctypes.c_void_p] + self.EVP_MD_CTX_free = self._lib.EVP_MD_CTX_free + self.EVP_MD_CTX_free.restype = None + self.EVP_MD_CTX_free.argtypes = [ctypes.c_void_p] - self.EVP_MD_CTX_destroy = self._lib.EVP_MD_CTX_destroy - self.EVP_MD_CTX_destroy.restype = None - self.EVP_MD_CTX_destroy.argtypes = [ctypes.c_void_p] + self.EVP_sha1 = self._lib.EVP_sha1 + self.EVP_sha1.restype = ctypes.c_void_p + self.EVP_sha1.argtypes = [] + + self.digest_ecdsa_sha1 = self.EVP_sha1 + else: + self.EVP_MD_CTX_create = self._lib.EVP_MD_CTX_create + self.EVP_MD_CTX_create.restype = ctypes.c_void_p + self.EVP_MD_CTX_create.argtypes = [] + + self.EVP_MD_CTX_init = self._lib.EVP_MD_CTX_init + self.EVP_MD_CTX_init.restype = None + self.EVP_MD_CTX_init.argtypes = [ctypes.c_void_p] + + self.EVP_MD_CTX_destroy = self._lib.EVP_MD_CTX_destroy + self.EVP_MD_CTX_destroy.restype = None + self.EVP_MD_CTX_destroy.argtypes = [ctypes.c_void_p] + + self.EVP_ecdsa = self._lib.EVP_ecdsa + self._lib.EVP_ecdsa.restype = ctypes.c_void_p + self._lib.EVP_ecdsa.argtypes = [] + + self.digest_ecdsa_sha1 = self.EVP_ecdsa self.RAND_bytes = self._lib.RAND_bytes self.RAND_bytes.restype = ctypes.c_int self.RAND_bytes.argtypes = [ctypes.c_void_p, ctypes.c_int] - self.EVP_sha256 = self._lib.EVP_sha256 self.EVP_sha256.restype = ctypes.c_void_p self.EVP_sha256.argtypes = [] @@ -437,7 +503,11 @@ def loadOpenSSL(): if 'darwin' in sys.platform: libdir.extend([ path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.dylib'), - path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.0.0.dylib') + path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.1.0.dylib'), + path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.0.2.dylib'), + path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.0.1.dylib'), + path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.1.0.0.dylib'), + path.join(environ['RESOURCEPATH'], '..', 'Frameworks','libcrypto.0.9.8.dylib'), ]) elif 'win32' in sys.platform or 'win64' in sys.platform: libdir.append(path.join(sys._MEIPASS, 'libeay32.dll')) @@ -445,8 +515,16 @@ def loadOpenSSL(): libdir.extend([ path.join(sys._MEIPASS, 'libcrypto.so'), path.join(sys._MEIPASS, 'libssl.so'), + path.join(sys._MEIPASS, 'libcrypto.so.1.1.0'), + path.join(sys._MEIPASS, 'libssl.so.1.1.0'), + path.join(sys._MEIPASS, 'libcrypto.so.1.0.2'), + path.join(sys._MEIPASS, 'libssl.so.1.0.2'), + path.join(sys._MEIPASS, 'libcrypto.so.1.0.1'), + path.join(sys._MEIPASS, 'libssl.so.1.0.1'), path.join(sys._MEIPASS, 'libcrypto.so.1.0.0'), path.join(sys._MEIPASS, 'libssl.so.1.0.0'), + path.join(sys._MEIPASS, 'libcrypto.so.0.9.8'), + path.join(sys._MEIPASS, 'libssl.so.0.9.8'), ]) if 'darwin' in sys.platform: libdir.extend(['libcrypto.dylib', '/usr/local/opt/openssl/lib/libcrypto.dylib']) @@ -455,7 +533,7 @@ def loadOpenSSL(): else: libdir.append('libcrypto.so') libdir.append('libssl.so') - if 'linux' in sys.platform or 'darwin' in sys.platform or 'freebsd' in sys.platform: + if 'linux' in sys.platform or 'darwin' in sys.platform or 'bsd' in sys.platform: libdir.append(find_library('ssl')) elif 'win32' in sys.platform or 'win64' in sys.platform: libdir.append(find_library('libeay32')) @@ -467,4 +545,4 @@ def loadOpenSSL(): pass raise Exception("Couldn't find and load the OpenSSL library. You must install it.") -loadOpenSSL() \ No newline at end of file +loadOpenSSL()