setup.py 17 KB

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