1#! /usr/bin/env python 2# 3# setup.py : Distutils setup script 4# 5# =================================================================== 6# The contents of this file are dedicated to the public domain. To 7# the extent that dedication to the public domain is not available, 8# everyone is granted a worldwide, perpetual, royalty-free, 9# non-exclusive license to exercise all rights associated with the 10# contents of this file for any purpose whatsoever. 11# No rights are reserved. 12# 13# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 17# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 18# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20# SOFTWARE. 21# =================================================================== 22 23from __future__ import print_function 24 25try: 26 from setuptools import Extension, Command, setup 27 from setuptools.command.build_ext import build_ext 28except ImportError: 29 from distutils.core import Extension, Command, setup 30 from distutils.command.build_ext import build_ext 31 32from distutils.command.build_py import build_py 33import re 34import os 35import sys 36import shutil 37import struct 38 39from compiler_opt import set_compiler_options 40 41 42use_separate_namespace = os.path.isfile(".separate_namespace") 43 44project_name = "pycryptodome" 45package_root = "Crypto" 46other_project = "pycryptodomex" 47other_root = "Cryptodome" 48 49if use_separate_namespace: 50 project_name, other_project = other_project, project_name 51 package_root, other_root = other_root, package_root 52 53longdesc = """ 54PyCryptodome 55============ 56 57PyCryptodome is a self-contained Python package of low-level 58cryptographic primitives. 59 60It supports Python 2.7, Python 3.5 and newer, and PyPy. 61 62You can install it with:: 63 64 pip install THIS_PROJECT 65 66All modules are installed under the ``THIS_ROOT`` package. 67 68Check the OTHER_PROJECT_ project for the equivalent library that 69works under the ``OTHER_ROOT`` package. 70 71PyCryptodome is a fork of PyCrypto. It brings several enhancements 72with respect to the last official version of PyCrypto (2.6.1), 73for instance: 74 75* Authenticated encryption modes (GCM, CCM, EAX, SIV, OCB) 76* Accelerated AES on Intel platforms via AES-NI 77* First class support for PyPy 78* Elliptic curves cryptography (NIST P-256, P-384 and P-521 curves only) 79* Better and more compact API (`nonce` and `iv` attributes for ciphers, 80 automatic generation of random nonces and IVs, simplified CTR cipher mode, 81 and more) 82* SHA-3 (including SHAKE XOFs) and BLAKE2 hash algorithms 83* Salsa20 and ChaCha20 stream ciphers 84* scrypt and HKDF 85* Deterministic (EC)DSA 86* Password-protected PKCS#8 key containers 87* Shamir's Secret Sharing scheme 88* Random numbers get sourced directly from the OS (and not from a CSPRNG in userspace) 89* Simplified install process, including better support for Windows 90* Cleaner RSA and DSA key generation (largely based on FIPS 186-4) 91* Major clean ups and simplification of the code base 92 93PyCryptodome is not a wrapper to a separate C library like *OpenSSL*. 94To the largest possible extent, algorithms are implemented in pure Python. 95Only the pieces that are extremely critical to performance (e.g. block ciphers) 96are implemented as C extensions. 97 98For more information, see the `homepage`_. 99 100All the code can be downloaded from `GitHub`_. 101 102.. _OTHER_PROJECT: https://pypi.python.org/pypi/OTHER_PROJECT 103.. _`homepage`: http://www.pycryptodome.org 104.. _GitHub: https://github.com/Legrandin/pycryptodome 105""".replace("THIS_PROJECT", project_name).\ 106 replace("THIS_ROOT", package_root).\ 107 replace("OTHER_PROJECT", other_project).\ 108 replace("OTHER_ROOT", other_root) 109 110 111class PCTBuildExt (build_ext): 112 113 # Avoid linking Python's dynamic library 114 def get_libraries(self, ext): 115 return [] 116 117 118class PCTBuildPy(build_py): 119 def find_package_modules(self, package, package_dir, *args, **kwargs): 120 modules = build_py.find_package_modules(self, package, package_dir, 121 *args, **kwargs) 122 123 # Exclude certain modules 124 retval = [] 125 for item in modules: 126 pkg, module = item[:2] 127 retval.append(item) 128 return retval 129 130 131class TestCommand(Command): 132 "Run self-test" 133 134 # Long option name, short option name, description 135 user_options = [ 136 ('skip-slow-tests', None, 'Skip slow tests'), 137 ('wycheproof-warnings', None, 'Show warnings from wycheproof tests'), 138 ('module=', 'm', 'Test a single module (e.g. Cipher, PublicKey)'), 139 ] 140 141 def initialize_options(self): 142 self.build_dir = None 143 self.skip_slow_tests = None 144 self.wycheproof_warnings = None 145 self.module = None 146 147 def finalize_options(self): 148 self.set_undefined_options('install', ('build_lib', 'build_dir')) 149 self.config = {'slow_tests': not self.skip_slow_tests, 150 'wycheproof_warnings': self.wycheproof_warnings} 151 152 def run(self): 153 # Run sub commands 154 for cmd_name in self.get_sub_commands(): 155 self.run_command(cmd_name) 156 157 # Run SelfTest 158 old_path = sys.path[:] 159 self.announce("running self-tests on " + package_root) 160 try: 161 sys.path.insert(0, self.build_dir) 162 163 if use_separate_namespace: 164 from Cryptodome import SelfTest 165 from Cryptodome.Math import Numbers 166 else: 167 from Crypto import SelfTest 168 from Crypto.Math import Numbers 169 170 moduleObj = None 171 if self.module: 172 if self.module.count('.') == 0: 173 # Test a whole a sub-package 174 full_module = package_root + ".SelfTest." + self.module 175 module_name = self.module 176 else: 177 # Test only a module 178 # Assume only one dot is present 179 comps = self.module.split('.') 180 module_name = "test_" + comps[1] 181 full_module = package_root + ".SelfTest." + comps[0] + "." + module_name 182 # Import sub-package or module 183 moduleObj = __import__(full_module, globals(), locals(), module_name) 184 185 print(package_root + ".Math implementation:", 186 str(Numbers._implementation)) 187 188 SelfTest.run(module=moduleObj, verbosity=self.verbose, stream=sys.stdout, config=self.config) 189 finally: 190 # Restore sys.path 191 sys.path[:] = old_path 192 193 # Run slower self-tests 194 self.announce("running extended self-tests") 195 196 sub_commands = [('build', None)] 197 198 199def create_cryptodome_lib(): 200 assert os.path.isdir("lib/Crypto") 201 202 try: 203 shutil.rmtree("lib/Cryptodome") 204 except OSError: 205 pass 206 for root_src, dirs, files in os.walk("lib/Crypto"): 207 208 root_dst, nr_repl = re.subn('Crypto', 'Cryptodome', root_src) 209 assert nr_repl == 1 210 211 for dir_name in dirs: 212 full_dir_name_dst = os.path.join(root_dst, dir_name) 213 if not os.path.exists(full_dir_name_dst): 214 os.makedirs(full_dir_name_dst) 215 216 for file_name in files: 217 full_file_name_src = os.path.join(root_src, file_name) 218 full_file_name_dst = os.path.join(root_dst, file_name) 219 220 print("Copying file %s to %s" % (full_file_name_src, full_file_name_dst)) 221 shutil.copy2(full_file_name_src, full_file_name_dst) 222 223 if full_file_name_src.split(".")[-1] not in ("py", "pyi"): 224 if full_file_name_src != "py.typed": 225 continue 226 227 if sys.version_info[0] > 2: 228 extra_param = { "encoding": "utf-8" } 229 else: 230 extra_param = {} 231 with open(full_file_name_dst, "rt", **extra_param) as fd: 232 content = (fd.read(). 233 replace("Crypto.", "Cryptodome."). 234 replace("Crypto ", "Cryptodome "). 235 replace("'Crypto'", "'Cryptodome'"). 236 replace('"Crypto"', '"Cryptodome"')) 237 os.remove(full_file_name_dst) 238 with open(full_file_name_dst, "wt", **extra_param) as fd: 239 fd.write(content) 240 241 242# Parameters for setup 243packages = [ 244 "Crypto", 245 "Crypto.Cipher", 246 "Crypto.Hash", 247 "Crypto.IO", 248 "Crypto.PublicKey", 249 "Crypto.Protocol", 250 "Crypto.Random", 251 "Crypto.Signature", 252 "Crypto.Util", 253 "Crypto.Math", 254 "Crypto.SelfTest", 255 "Crypto.SelfTest.Cipher", 256 "Crypto.SelfTest.Hash", 257 "Crypto.SelfTest.IO", 258 "Crypto.SelfTest.Protocol", 259 "Crypto.SelfTest.PublicKey", 260 "Crypto.SelfTest.Random", 261 "Crypto.SelfTest.Signature", 262 "Crypto.SelfTest.Util", 263 "Crypto.SelfTest.Math", 264] 265package_data = { 266 "Crypto" : [ "py.typed", "*.pyi" ], 267 "Crypto.Cipher" : [ "*.pyi" ], 268 "Crypto.Hash" : [ "*.pyi" ], 269 "Crypto.Math" : [ "*.pyi" ], 270 "Crypto.Protocol" : [ "*.pyi" ], 271 "Crypto.PublicKey" : [ "*.pyi" ], 272 "Crypto.Random" : [ "*.pyi" ], 273 "Crypto.Signature" : [ "*.pyi" ], 274 "Crypto.IO" : [ "*.pyi" ], 275 "Crypto.Util" : [ "*.pyi" ], 276} 277 278ext_modules = [ 279 # Hash functions 280 Extension("Crypto.Hash._MD2", 281 include_dirs=['src/'], 282 sources=["src/MD2.c"], 283 py_limited_api=True), 284 Extension("Crypto.Hash._MD4", 285 include_dirs=['src/'], 286 sources=["src/MD4.c"], 287 py_limited_api=True), 288 Extension("Crypto.Hash._MD5", 289 include_dirs=['src/'], 290 sources=["src/MD5.c"], 291 py_limited_api=True), 292 Extension("Crypto.Hash._SHA1", 293 include_dirs=['src/'], 294 sources=["src/SHA1.c"], 295 py_limited_api=True), 296 Extension("Crypto.Hash._SHA256", 297 include_dirs=['src/'], 298 sources=["src/SHA256.c"], 299 py_limited_api=True), 300 Extension("Crypto.Hash._SHA224", 301 include_dirs=['src/'], 302 sources=["src/SHA224.c"], 303 py_limited_api=True), 304 Extension("Crypto.Hash._SHA384", 305 include_dirs=['src/'], 306 sources=["src/SHA384.c"], 307 py_limited_api=True), 308 Extension("Crypto.Hash._SHA512", 309 include_dirs=['src/'], 310 sources=["src/SHA512.c"], 311 py_limited_api=True), 312 Extension("Crypto.Hash._RIPEMD160", 313 include_dirs=['src/'], 314 sources=["src/RIPEMD160.c"], 315 py_limited_api=True), 316 Extension("Crypto.Hash._keccak", 317 include_dirs=['src/'], 318 sources=["src/keccak.c"], 319 py_limited_api=True), 320 Extension("Crypto.Hash._BLAKE2b", 321 include_dirs=['src/'], 322 sources=["src/blake2b.c"], 323 py_limited_api=True), 324 Extension("Crypto.Hash._BLAKE2s", 325 include_dirs=['src/'], 326 sources=["src/blake2s.c"], 327 py_limited_api=True), 328 Extension("Crypto.Hash._ghash_portable", 329 include_dirs=['src/'], 330 sources=['src/ghash_portable.c'], 331 py_limited_api=True), 332 Extension("Crypto.Hash._ghash_clmul", 333 include_dirs=['src/'], 334 sources=['src/ghash_clmul.c'], 335 py_limited_api=True), 336 337 # MACs 338 Extension("Crypto.Hash._poly1305", 339 include_dirs=['src/'], 340 sources=["src/poly1305.c"], 341 py_limited_api=True), 342 343 # Block encryption algorithms 344 Extension("Crypto.Cipher._raw_aes", 345 include_dirs=['src/'], 346 sources=["src/AES.c"], 347 py_limited_api=True), 348 Extension("Crypto.Cipher._raw_aesni", 349 include_dirs=['src/'], 350 sources=["src/AESNI.c"], 351 py_limited_api=True), 352 Extension("Crypto.Cipher._raw_arc2", 353 include_dirs=['src/'], 354 sources=["src/ARC2.c"], 355 py_limited_api=True), 356 Extension("Crypto.Cipher._raw_blowfish", 357 include_dirs=['src/'], 358 sources=["src/blowfish.c"], 359 py_limited_api=True), 360 Extension("Crypto.Cipher._raw_eksblowfish", 361 include_dirs=['src/'], 362 define_macros=[('EKS',None),], 363 sources=["src/blowfish.c"], 364 py_limited_api=True), 365 Extension("Crypto.Cipher._raw_cast", 366 include_dirs=['src/'], 367 sources=["src/CAST.c"], 368 py_limited_api=True), 369 Extension("Crypto.Cipher._raw_des", 370 include_dirs=['src/', 'src/libtom/'], 371 sources=["src/DES.c"], 372 py_limited_api=True), 373 Extension("Crypto.Cipher._raw_des3", 374 include_dirs=['src/', 'src/libtom/'], 375 sources=["src/DES3.c"], 376 py_limited_api=True), 377 Extension("Crypto.Util._cpuid_c", 378 include_dirs=['src/'], 379 sources=['src/cpuid.c'], 380 py_limited_api=True), 381 382 # Chaining modes 383 Extension("Crypto.Cipher._raw_ecb", 384 include_dirs=['src/'], 385 sources=["src/raw_ecb.c"], 386 py_limited_api=True), 387 Extension("Crypto.Cipher._raw_cbc", 388 include_dirs=['src/'], 389 sources=["src/raw_cbc.c"], 390 py_limited_api=True), 391 Extension("Crypto.Cipher._raw_cfb", 392 include_dirs=['src/'], 393 sources=["src/raw_cfb.c"], 394 py_limited_api=True), 395 Extension("Crypto.Cipher._raw_ofb", 396 include_dirs=['src/'], 397 sources=["src/raw_ofb.c"], 398 py_limited_api=True), 399 Extension("Crypto.Cipher._raw_ctr", 400 include_dirs=['src/'], 401 sources=["src/raw_ctr.c"], 402 py_limited_api=True), 403 Extension("Crypto.Cipher._raw_ocb", 404 sources=["src/raw_ocb.c"], 405 py_limited_api=True), 406 407 # Stream ciphers 408 Extension("Crypto.Cipher._ARC4", 409 include_dirs=['src/'], 410 sources=["src/ARC4.c"], 411 py_limited_api=True), 412 Extension("Crypto.Cipher._Salsa20", 413 include_dirs=['src/', 'src/libtom/'], 414 sources=["src/Salsa20.c"], 415 py_limited_api=True), 416 Extension("Crypto.Cipher._chacha20", 417 include_dirs=['src/'], 418 sources=["src/chacha20.c"], 419 py_limited_api=True), 420 421 # Others 422 Extension("Crypto.Protocol._scrypt", 423 include_dirs=['src/'], 424 sources=["src/scrypt.c"], 425 py_limited_api=True), 426 427 # Utility modules 428 Extension("Crypto.Util._strxor", 429 include_dirs=['src/'], 430 sources=['src/strxor.c'], 431 py_limited_api=True), 432 433 # ECC 434 Extension("Crypto.PublicKey._ec_ws", 435 include_dirs=['src/'], 436 sources=['src/modexp_utils.c', 'src/siphash.c', 'src/ec_ws.c', 437 'src/mont.c', 'src/p256_table.c', 'src/p384_table.c', 438 'src/p521_table.c'], 439 py_limited_api=True, 440 ), 441 442 # Math 443 Extension("Crypto.Math._modexp", 444 include_dirs=['src/'], 445 sources=['src/modexp.c', 'src/siphash.c', 'src/modexp_utils.c', 'src/mont.c'], 446 py_limited_api=True, 447 ), 448] 449 450if use_separate_namespace: 451 452 # Fix-up setup information 453 for i in range(len(packages)): 454 packages[i] = packages[i].replace("Crypto", "Cryptodome") 455 new_package_data = {} 456 for k, v in package_data.items(): 457 new_package_data[k.replace("Crypto", "Cryptodome")] = v 458 package_data = new_package_data 459 for ext in ext_modules: 460 ext.name = ext.name.replace("Crypto", "Cryptodome") 461 462 # Recreate lib/Cryptodome from scratch, unless it is the only 463 # directory available 464 if os.path.isdir("lib/Crypto"): 465 create_cryptodome_lib() 466 467# Add compiler specific options. 468set_compiler_options(package_root, ext_modules) 469 470# By doing this we need to change version information in a single file 471with open(os.path.join("lib", package_root, "__init__.py")) as init_root: 472 for line in init_root: 473 if line.startswith("version_info"): 474 version_tuple = eval(line.split("=")[1]) 475 476version_string = ".".join([str(x) for x in version_tuple]) 477 478setup( 479 name=project_name, 480 version=version_string, 481 description="Cryptographic library for Python", 482 long_description=longdesc, 483 author="Helder Eijs", 484 author_email="helderijs@gmail.com", 485 url="https://www.pycryptodome.org", 486 platforms='Posix; MacOS X; Windows', 487 zip_safe=False, 488 python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', 489 classifiers=[ 490 'Development Status :: 5 - Production/Stable', 491 'License :: OSI Approved :: BSD License', 492 'License :: OSI Approved :: Apache Software License', 493 'License :: Public Domain', 494 'Intended Audience :: Developers', 495 'Operating System :: Unix', 496 'Operating System :: Microsoft :: Windows', 497 'Operating System :: MacOS :: MacOS X', 498 'Topic :: Security :: Cryptography', 499 'Programming Language :: Python :: 2', 500 'Programming Language :: Python :: 2.7', 501 'Programming Language :: Python :: 3', 502 'Programming Language :: Python :: 3.5', 503 'Programming Language :: Python :: 3.6', 504 'Programming Language :: Python :: 3.7', 505 'Programming Language :: Python :: 3.8', 506 'Programming Language :: Python :: 3.9', 507 ], 508 license="BSD, Public Domain", 509 packages=packages, 510 package_dir={"": "lib"}, 511 package_data=package_data, 512 cmdclass={ 513 'build_ext': PCTBuildExt, 514 'build_py': PCTBuildPy, 515 'test': TestCommand, 516 }, 517 ext_modules=ext_modules, 518) 519