diff --git a/setup.py b/setup.py index 468dc5f..3275ba9 100755 --- a/setup.py +++ b/setup.py @@ -26,8 +26,21 @@ import sys from setuptools import setup from setuptools.command.build_ext import build_ext -import wolfcrypt -from wolfcrypt._build_wolfssl import build_wolfssl +import re +VERSIONFILE = "wolfcrypt/_version.py" +verstrline = open(VERSIONFILE, "rt").read() +VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" +mo = re.search(VSRE, verstrline, re.M) +if mo: + verstr = mo.group(1) +else: + raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) +VSRE = r"^__wolfssl_version__ = ['\"]([^'\"]*)['\"]" +mo = re.search(VSRE, verstrline, re.M) +if mo: + wolfverstr = mo.group(1) +else: + raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) # long_description @@ -38,30 +51,15 @@ with open("LICENSING.rst") as licensing_file: long_description = long_description.replace(".. include:: LICENSING.rst\n", licensing_file.read()) - -class cffiBuilder(build_ext, object): - - def build_extension(self, ext): - """ Compile manually the wolfcrypt-py extension, bypass setuptools - """ - - # if USE_LOCAL_WOLFSSL environment variable has been defined, - # do not clone and compile wolfSSL from GitHub - if os.environ.get("USE_LOCAL_WOLFSSL") is None: - build_wolfssl(wolfcrypt.__wolfssl_version__) - - super(cffiBuilder, self).build_extension(ext) - - setup( - name=wolfcrypt.__title__, - version=wolfcrypt.__version__, - description=wolfcrypt.__summary__, + name="wolfcrypt", + version=verstr, + description="Python module that encapsulates wolfSSL's crypto engine API.", long_description=long_description, - author=wolfcrypt.__author__, - author_email=wolfcrypt.__email__, - url=wolfcrypt.__uri__, - license=wolfcrypt.__license__, + author="wolfSSL Inc.", + author_email="info@wolfssl.com", + url="https://github.com/wolfssl/wolfcrypt-py", + license="GPLv2 or Commercial License", packages=["wolfcrypt"], @@ -83,6 +81,5 @@ setup( ], setup_requires=["cffi"], - install_requires=["cffi"], - cmdclass={"build_ext" : cffiBuilder} + install_requires=["cffi"] ) diff --git a/tox.ini b/tox.ini index 65680b3..9dab8ee 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,7 @@ envlist = py3 [testenv] +wheel = true setenv = PYTHONPATH = {toxinidir}:{toxinidir}/wolfcrypt-py diff --git a/wolfcrypt/__init__.py b/wolfcrypt/__init__.py index 868a154..f739ee8 100644 --- a/wolfcrypt/__init__.py +++ b/wolfcrypt/__init__.py @@ -18,22 +18,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +from wolfcrypt._version import __version__, __wolfssl_version__ + __title__ = "wolfcrypt" __summary__ = "Python module that encapsulates wolfSSL's crypto engine API." __uri__ = "https://github.com/wolfssl/wolfcrypt-py" -# When bumping the C library version, reset the POST count to 0 - -__wolfssl_version__ = "v5.1.1-stable" - -# We're using implicit post releases [PEP 440] to bump package version -# while maintaining the C library version intact for better reference. -# https://www.python.org/dev/peps/pep-0440/#implicit-post-releases -# -# MAJOR.MINOR.BUILD-POST - -__version__ = "5.1.1-0" - __author__ = "wolfSSL Inc." __email__ = "info@wolfssl.com" diff --git a/wolfcrypt/_build_ffi.py b/wolfcrypt/_build_ffi.py index be65bc1..fbf9b83 100644 --- a/wolfcrypt/_build_ffi.py +++ b/wolfcrypt/_build_ffi.py @@ -21,13 +21,276 @@ import os import sys import re +import subprocess +from contextlib import contextmanager from distutils.util import get_platform from cffi import FFI -from wolfcrypt import __wolfssl_version__ as version -from wolfcrypt._build_wolfssl import wolfssl_inc_path, wolfssl_lib_path, ensure_wolfssl_src, make, make_flags, local_path +from wolfcrypt._version import __wolfssl_version__ as version libwolfssl_path = "" + +def local_path(path): + """ Return path relative to the root of this project + """ + current = os.path.abspath(os.getcwd()) + return os.path.abspath(os.path.join(current, path)) + + +WOLFSSL_SRC_PATH = local_path("lib/wolfssl") + + +def wolfssl_inc_path(): + if sys.platform == "win32": + return os.path.join(WOLFSSL_SRC_PATH) + else: + wolfssl_path = os.environ.get("USE_LOCAL_WOLFSSL") + if wolfssl_path is None: + return local_path("lib/wolfssl") + else: + if os.path.isdir(wolfssl_path) and os.path.exists(wolfssl_path): + return wolfssl_path + "/include" + else: + return "/usr/local/include" + + +def wolfssl_lib_path(): + if sys.platform == "win32": + return os.path.join(WOLFSSL_SRC_PATH, "build", "Release") + else: + wolfssl_path = os.environ.get("USE_LOCAL_WOLFSSL") + if wolfssl_path is None: + return local_path("lib/wolfssl/{}/{}/lib".format( + get_platform(), version)) + else: + if os.path.isdir(wolfssl_path) and os.path.exists(wolfssl_path): + return wolfssl_path + "/lib" + else: + return "/usr/local/lib" + + +def call(cmd): + print("Calling: '{}' from working directory {}".format(cmd, os.getcwd())) + + old_env = os.environ["PATH"] + os.environ["PATH"] = "{}:{}".format(WOLFSSL_SRC_PATH, old_env) + subprocess.check_call(cmd, shell=True, env=os.environ) + os.environ["PATH"] = old_env + + +@contextmanager +def chdir(new_path, mkdir=False): + old_path = os.getcwd() + + if mkdir: + try: + os.mkdir(new_path) + except OSError: + pass + + try: + yield os.chdir(new_path) + finally: + os.chdir(old_path) + + +def checkout_version(version): + """ Ensure that we have the right version + """ + with chdir(WOLFSSL_SRC_PATH): + current = "" + try: + current = subprocess.check_output( + ["git", "describe", "--all", "--exact-match"] + ).strip().decode().split('/')[-1] + except: + pass + + if current != version: + tags = subprocess.check_output( + ["git", "tag"] + ).strip().decode().split("\n") + + if version != "master" and version not in tags: + call("git fetch --depth=1 origin tag {}".format(version)) + + call("git checkout --force {}".format(version)) + + return True # rebuild needed + + return False + + +def ensure_wolfssl_src(ref): + """ Ensure that wolfssl sources are presents and up-to-date + """ + if not os.path.isdir("lib"): + os.mkdir("lib") + with chdir("lib"): + subprocess.run(["git", "clone", "--depth=1", "https://github.com/wolfssl/wolfssl"]) + + if not os.path.isdir(os.path.join(WOLFSSL_SRC_PATH, "wolfssl")): + subprocess.run(["git", "submodule", "update", "--init", "--depth=1"]) + + return checkout_version(version) + + +def make_flags(prefix): + """ Returns compilation flags + """ + if sys.platform == "win32": + flags = [] + flags.append("-DWOLFSSL_CRYPT_TESTS=no") + flags.append("-DWOLFSSL_EXAMPLES=no") + flags.append("-DBUILD_SHARED_LIBS=off") + flags.append("-DWOLFSSL_CRYPT_ONLY=yes") + flags.append("-DWOLFSSL_AES=yes") + flags.append("-DWOLFSSL_DES3=yes") + flags.append("-DWOLFSSL_CHACHA=yes") + flags.append("-DWOLFSSL_AESGCM=yes") + flags.append("-DWOLFSSL_SHA=yes") + flags.append("-DWOLFSSL_SHA384=yes") + flags.append("-DWOLFSSL_SHA512=yes") + flags.append("-DWOLFSSL_SHA3=yes") + flags.append("-DWOLFSSL_HKDF=yes") + flags.append("-DWOLFSSL_MD5=no") + flags.append("-DWOLFSSL_SHA224=no") + flags.append("-DWOLFSSL_POLY1305=no") + flags.append("-DWOLFSSL_RSA=yes") + flags.append("-DWOLFSSL_RSA_PSS=yes") + flags.append("-DWOLFSSL_ECC=yes") + flags.append("-DWOLFSSL_ED25519=yes") + flags.append("-DWOLFSSL_ED448=yes") + flags.append("-DWOLFSSL_CURVE25519=yes") + flags.append("-DWOLFSSL_DH=no") + flags.append("-DWOLFSSL_PWDBASED=yes") + flags.append("-DWOLFSSL_PKCS7=yes") + flags.append("-DWOLFSSL_OLD_TLS=no") + flags.append("-DWOLFSSL_OLD_NAMES=no") + flags.append("-DWOLFSSL_EXTENDED_MASTER=no") + flags.append("-DWOLFSSL_ERROR_STRINGS=no") + # Part of hack for missing CMake option + flags.append("-DCMAKE_C_FLAGS=\"/DWOLFSSL_KEY_GEN=1 /DWOLFCRYPT_ONLY=1 /DWOLFSSL_AESGCM_STREAM=1 /DWOLFSSL_AES_COUNTER=1\"") + + return " ".join(flags) + else: + flags = [] + + if get_platform() in ["linux-x86_64", "linux-i686"]: + flags.append("CFLAGS=-fPIC") + + # install location + flags.append("--prefix={}".format(prefix)) + + # crypt only, lib only + flags.append("--enable-cryptonly") + flags.append("--disable-crypttests") + flags.append("--disable-shared") + + # symmetric ciphers + flags.append("--enable-aes") + flags.append("--enable-aesctr") + flags.append("--enable-des3") + flags.append("--enable-chacha") + + flags.append("--enable-aesgcm-stream") + + flags.append("--enable-aesgcm") + + # hashes and MACs + flags.append("--enable-sha") + flags.append("--enable-sha384") + flags.append("--enable-sha512") + flags.append("--enable-sha3") + flags.append("--enable-hkdf") + + flags.append("--disable-md5") + flags.append("--disable-sha224") + flags.append("--disable-poly1305") + + # asymmetric ciphers + flags.append("--enable-rsa") + flags.append("--enable-rsapss") + flags.append("--enable-ecc") + flags.append("--enable-ed25519") + flags.append("--enable-ed448") + flags.append("--enable-curve25519") + flags.append("--enable-keygen") + + flags.append("--disable-dh") + + # pwdbased + flags.append("--enable-pwdbased") + flags.append("--enable-pkcs7") + + # disabling other configs enabled by default + flags.append("--disable-oldtls") + flags.append("--disable-oldnames") + flags.append("--disable-extended-master") + flags.append("--disable-errorstrings") + + return " ".join(flags) + + +# Horrid hack because we have no CMake option in 5.1.1 for this +def cmake_hack(): + options_file = os.path.join(WOLFSSL_SRC_PATH, "wolfssl", "options.h") + with open(options_file, "r") as f: + contents = f.readlines() + + contents.insert(26, "#undef WOLFSSL_KEY_GEN\n") + contents.insert(27, "#define WOLFSSL_KEY_GEN\n") + contents.insert(28, "#undef WOLFCRYPT_ONLY\n") + contents.insert(29, "#define WOLFCRYPT_ONLY\n") + contents.insert(30, "#undef WOLFSSL_AESGCM_STREAM\n") + contents.insert(31, "#define WOLFSSL_AESGCM_STREAM\n") + contents.insert(32, "#undef WOLFSSL_AES_COUNTER\n") + contents.insert(33, "#define WOLFSSL_AES_COUNTER\n") + + with open(options_file, "w") as f: + contents = "".join(contents) + f.write(contents) + + +def make(configure_flags): + """ Create a release of wolfSSL C library + """ + if sys.platform == 'win32': + build_path = os.path.join(WOLFSSL_SRC_PATH, "build") + if not os.path.isdir(build_path): + os.mkdir(build_path) + with chdir(build_path): + call("cmake .. {}".format(configure_flags)) + cmake_hack() + call("cmake --build . --config Release") + else: + with chdir(WOLFSSL_SRC_PATH): + call("git clean -fdX") + + try: + call("./autogen.sh") + except subprocess.CalledProcessError: + call("libtoolize") + call("./autogen.sh") + + call("./configure {}".format(configure_flags)) + call("make") + call("make install-exec") + + +def build_wolfssl(version="master"): + prefix = local_path("lib/wolfssl/{}/{}".format( + get_platform(), version)) + if sys.platform == 'win32': + libfile = os.path.join(WOLFSSL_SRC_PATH, "build", "Release", "wolfssl.lib") + else: + libfile = os.path.join(prefix, 'lib/libwolfssl.la') + + ensure_wolfssl_src(version) + + if not os.path.isfile(libfile): + make(make_flags(prefix)) + def get_libwolfssl(): global libwolfssl_path if sys.platform == "win32": @@ -53,12 +316,13 @@ def generate_libwolfssl(): get_platform(), version)) make(make_flags(prefix)) + if get_libwolfssl() == 0: generate_libwolfssl() get_libwolfssl() # detect features if user has built against local wolfSSL library -# if they are not, we are controlling build options in _build_wolfssl.py +# if they are not, we are controlling build options above local_wolfssl = os.environ.get("USE_LOCAL_WOLFSSL") if local_wolfssl is not None: # Try to do native wolfSSL/wolfCrypt feature detection. @@ -626,4 +890,6 @@ if FIPS_ENABLED and (FIPS_VERSION > 5 or (FIPS_VERSION == 5 and FIPS_VERSION >= ffibuilder.cdef(_cdef) -ffibuilder.compile(verbose=True) + +if __name__ == "__main__": + ffibuilder.compile(verbose=True) diff --git a/wolfcrypt/_build_wolfssl.py b/wolfcrypt/_build_wolfssl.py deleted file mode 100644 index 2b28c29..0000000 --- a/wolfcrypt/_build_wolfssl.py +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2006-2022 wolfSSL Inc. -# -# This file is part of wolfSSL. (formerly known as CyaSSL) -# -# wolfSSL is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# wolfSSL is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -import os -import sys -import subprocess -from contextlib import contextmanager -from distutils.util import get_platform -from wolfcrypt.__init__ import __wolfssl_version__ as version - - -def local_path(path): - """ Return path relative to the root of this project - """ - current = os.path.abspath(os.getcwd()) - return os.path.abspath(os.path.join(current, path)) - -WOLFSSL_SRC_PATH = local_path("lib/wolfssl") - - -def wolfssl_inc_path(): - if sys.platform == "win32": - return os.path.join(WOLFSSL_SRC_PATH) - else: - wolfssl_path = os.environ.get("USE_LOCAL_WOLFSSL") - if wolfssl_path is None: - return local_path("lib/wolfssl") - else: - if os.path.isdir(wolfssl_path) and os.path.exists(wolfssl_path): - return wolfssl_path + "/include" - else: - return "/usr/local/include" - - -def wolfssl_lib_path(): - if sys.platform == "win32": - return os.path.join(WOLFSSL_SRC_PATH, "build", "Release") - else: - wolfssl_path = os.environ.get("USE_LOCAL_WOLFSSL") - if wolfssl_path is None: - return local_path("lib/wolfssl/{}/{}/lib".format( - get_platform(), version)) - else: - if os.path.isdir(wolfssl_path) and os.path.exists(wolfssl_path): - return wolfssl_path + "/lib" - else: - return "/usr/local/lib" - - -def call(cmd): - print("Calling: '{}' from working directory {}".format(cmd, os.getcwd())) - - old_env = os.environ["PATH"] - os.environ["PATH"] = "{}:{}".format(WOLFSSL_SRC_PATH, old_env) - subprocess.check_call(cmd, shell=True, env=os.environ) - os.environ["PATH"] = old_env - - -@contextmanager -def chdir(new_path, mkdir=False): - old_path = os.getcwd() - - if mkdir: - try: - os.mkdir(new_path) - except OSError: - pass - - try: - yield os.chdir(new_path) - finally: - os.chdir(old_path) - - -def checkout_version(version): - """ Ensure that we have the right version - """ - with chdir(WOLFSSL_SRC_PATH): - current = "" - try: - current = subprocess.check_output( - ["git", "describe", "--all", "--exact-match"] - ).strip().decode().split('/')[-1] - except: - pass - - if current != version: - tags = subprocess.check_output( - ["git", "tag"] - ).strip().decode().split("\n") - - if version != "master" and version not in tags: - call("git fetch --depth=1 origin tag {}".format(version)) - - call("git checkout --force {}".format(version)) - - return True # rebuild needed - - return False - - -def ensure_wolfssl_src(ref): - """ Ensure that wolfssl sources are presents and up-to-date - """ - if not os.path.isdir("lib"): - os.mkdir("lib") - with chdir("lib"): - subprocess.run(["git", "clone", "--depth=1", "https://github.com/wolfssl/wolfssl"]) - - if not os.path.isdir(os.path.join(WOLFSSL_SRC_PATH, "wolfssl")): - subprocess.run(["git", "submodule", "update", "--init", "--depth=1"]) - - return checkout_version(version) - - -def make_flags(prefix): - """ Returns compilation flags - """ - if sys.platform == "win32": - flags = [] - flags.append("-DWOLFSSL_CRYPT_TESTS=no") - flags.append("-DWOLFSSL_EXAMPLES=no") - flags.append("-DBUILD_SHARED_LIBS=off") - flags.append("-DWOLFSSL_CRYPT_ONLY=yes") - flags.append("-DWOLFSSL_AES=yes") - flags.append("-DWOLFSSL_DES3=yes") - flags.append("-DWOLFSSL_CHACHA=yes") - flags.append("-DWOLFSSL_AESGCM=yes") - flags.append("-DWOLFSSL_SHA=yes") - flags.append("-DWOLFSSL_SHA384=yes") - flags.append("-DWOLFSSL_SHA512=yes") - flags.append("-DWOLFSSL_SHA3=yes") - flags.append("-DWOLFSSL_HKDF=yes") - flags.append("-DWOLFSSL_MD5=no") - flags.append("-DWOLFSSL_SHA224=no") - flags.append("-DWOLFSSL_POLY1305=no") - flags.append("-DWOLFSSL_RSA=yes") - flags.append("-DWOLFSSL_RSA_PSS=yes") - flags.append("-DWOLFSSL_ECC=yes") - flags.append("-DWOLFSSL_ED25519=yes") - flags.append("-DWOLFSSL_ED448=yes") - flags.append("-DWOLFSSL_CURVE25519=yes") - flags.append("-DWOLFSSL_DH=no") - flags.append("-DWOLFSSL_PWDBASED=yes") - flags.append("-DWOLFSSL_PKCS7=yes") - flags.append("-DWOLFSSL_OLD_TLS=no") - flags.append("-DWOLFSSL_OLD_NAMES=no") - flags.append("-DWOLFSSL_EXTENDED_MASTER=no") - flags.append("-DWOLFSSL_ERROR_STRINGS=no") - # Part of hack for missing CMake option - flags.append("-DCMAKE_C_FLAGS=\"/DWOLFSSL_KEY_GEN=1 /DWOLFCRYPT_ONLY=1 /DWOLFSSL_AESGCM_STREAM=1 /DWOLFSSL_AES_COUNTER=1\"") - - return " ".join(flags) - else: - flags = [] - - if get_platform() in ["linux-x86_64", "linux-i686"]: - flags.append("CFLAGS=-fPIC") - - # install location - flags.append("--prefix={}".format(prefix)) - - # crypt only, lib only - flags.append("--enable-cryptonly") - flags.append("--disable-crypttests") - flags.append("--disable-shared") - - # symmetric ciphers - flags.append("--enable-aes") - flags.append("--enable-aesctr") - flags.append("--enable-des3") - flags.append("--enable-chacha") - - flags.append("--enable-aesgcm-stream") - - flags.append("--enable-aesgcm") - - # hashes and MACs - flags.append("--enable-sha") - flags.append("--enable-sha384") - flags.append("--enable-sha512") - flags.append("--enable-sha3") - flags.append("--enable-hkdf") - - flags.append("--disable-md5") - flags.append("--disable-sha224") - flags.append("--disable-poly1305") - - # asymmetric ciphers - flags.append("--enable-rsa") - flags.append("--enable-rsapss") - flags.append("--enable-ecc") - flags.append("--enable-ed25519") - flags.append("--enable-ed448") - flags.append("--enable-curve25519") - flags.append("--enable-keygen") - - flags.append("--disable-dh") - - # pwdbased - flags.append("--enable-pwdbased") - flags.append("--enable-pkcs7") - - # disabling other configs enabled by default - flags.append("--disable-oldtls") - flags.append("--disable-oldnames") - flags.append("--disable-extended-master") - flags.append("--disable-errorstrings") - - return " ".join(flags) - - -# Horrid hack because we have no CMake option in 5.1.1 for this -def cmake_hack(): - options_file = os.path.join(WOLFSSL_SRC_PATH, "wolfssl", "options.h") - with open(options_file, "r") as f: - contents = f.readlines() - - contents.insert(26, "#undef WOLFSSL_KEY_GEN\n") - contents.insert(27, "#define WOLFSSL_KEY_GEN\n") - contents.insert(28, "#undef WOLFCRYPT_ONLY\n") - contents.insert(29, "#define WOLFCRYPT_ONLY\n") - contents.insert(30, "#undef WOLFSSL_AESGCM_STREAM\n") - contents.insert(31, "#define WOLFSSL_AESGCM_STREAM\n") - contents.insert(32, "#undef WOLFSSL_AES_COUNTER\n") - contents.insert(33, "#define WOLFSSL_AES_COUNTER\n") - - with open(options_file, "w") as f: - contents = "".join(contents) - f.write(contents) - - -def make(configure_flags): - """ Create a release of wolfSSL C library - """ - if sys.platform == 'win32': - build_path = os.path.join(WOLFSSL_SRC_PATH, "build") - if not os.path.isdir(build_path): - os.mkdir(build_path) - with chdir(build_path): - call("cmake .. {}".format(configure_flags)) - cmake_hack() - call("cmake --build . --config Release") - else: - with chdir(WOLFSSL_SRC_PATH): - call("git clean -fdX") - - try: - call("./autogen.sh") - except subprocess.CalledProcessError: - call("libtoolize") - call("./autogen.sh") - - call("./configure {}".format(configure_flags)) - call("make") - call("make install-exec") - - -def build_wolfssl(version="master"): - prefix = local_path("lib/wolfssl/{}/{}".format( - get_platform(), version)) - if sys.platform == 'win32': - libfile = os.path.join(WOLFSSL_SRC_PATH, "build", "Release", "wolfssl.lib") - else: - libfile = os.path.join(prefix, 'lib/libwolfssl.la') - - ensure_wolfssl_src(version) - - if not os.path.isfile(libfile): - make(make_flags(prefix)) - -if __name__ == "__main__": - build_wolfssl() diff --git a/wolfcrypt/_version.py b/wolfcrypt/_version.py new file mode 100644 index 0000000..134861a --- /dev/null +++ b/wolfcrypt/_version.py @@ -0,0 +1,12 @@ +# When bumping the C library version, reset the POST count to 0 + +__wolfssl_version__ = "v5.1.1-stable" + +# We're using implicit post releases [PEP 440] to bump package version +# while maintaining the C library version intact for better reference. +# https://www.python.org/dev/peps/pep-0440/#implicit-post-releases +# +# MAJOR.MINOR.BUILD-POST + +__version__ = "5.1.1-0" +