From 31817314047d3ac059a676cc9f6aa82efa812194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moise=CC=81s=20Guimara=CC=83es?= Date: Tue, 3 May 2016 00:41:54 -0300 Subject: [PATCH] adds docs --- wrapper/python/.gitignore | 4 + wrapper/python/README.rst | 24 +- wrapper/python/docs/Makefile | 8 +- wrapper/python/docs/asymmetric.rst | 74 ++++++ wrapper/python/docs/conf.py | 36 +-- wrapper/python/docs/digest.rst | 71 ++++++ wrapper/python/docs/index.rst | 50 +++- wrapper/python/docs/mac.rst | 74 ++++++ wrapper/python/docs/random.rst | 30 +++ wrapper/python/docs/symmetric.rst | 44 ++++ wrapper/python/requirements-testing.txt | 3 +- wrapper/python/setup.py | 14 +- wrapper/python/test/test_ciphers.py | 41 ++- wrapper/python/test/test_hashes.py | 317 +++--------------------- wrapper/python/tox.ini | 2 +- wrapper/python/wolfcrypt/__init__.py | 2 +- wrapper/python/wolfcrypt/about.py | 58 +++++ wrapper/python/wolfcrypt/build_ffi.py | 2 +- wrapper/python/wolfcrypt/ciphers.py | 199 +++++++++++---- wrapper/python/wolfcrypt/exceptions.py | 23 ++ wrapper/python/wolfcrypt/hashes.py | 184 ++++++++++---- wrapper/python/wolfcrypt/random.py | 37 ++- wrapper/python/wolfcrypt/utils.py | 38 +++ 23 files changed, 882 insertions(+), 453 deletions(-) create mode 100644 wrapper/python/docs/asymmetric.rst create mode 100644 wrapper/python/docs/digest.rst create mode 100644 wrapper/python/docs/mac.rst create mode 100644 wrapper/python/docs/random.rst create mode 100644 wrapper/python/docs/symmetric.rst create mode 100644 wrapper/python/wolfcrypt/about.py create mode 100644 wrapper/python/wolfcrypt/exceptions.py create mode 100644 wrapper/python/wolfcrypt/utils.py diff --git a/wrapper/python/.gitignore b/wrapper/python/.gitignore index 8cb79fe5b..421703396 100644 --- a/wrapper/python/.gitignore +++ b/wrapper/python/.gitignore @@ -5,7 +5,11 @@ __pycache__/ # Distribution build/ +dist/ +.eggs/ *.egg-info/ # Unit test .tox/ +# Sphinx documentation +docs/_build/ diff --git a/wrapper/python/README.rst b/wrapper/python/README.rst index 82bf1352f..23e9d1f78 100644 --- a/wrapper/python/README.rst +++ b/wrapper/python/README.rst @@ -1,8 +1,10 @@ + + wolfcrypt: the wolfSSL Crypto Engine ==================================== -A Python wrapper which encapsulates the wolfCrypt API from wolfSSL library +A Python library that encapsulates wolfSSL's wolfCrypt API 1. Clone the repository and install wolfssl:: @@ -23,12 +25,12 @@ A Python wrapper which encapsulates the wolfCrypt API from wolfSSL library $ pip install -r requirements-testing.txt -3. Run ``python setup.py install`` to build and install wolfcrypt-py:: +3. Run ``python setup.py install`` to build and install wolfcrypt:: $ python setup.py install ... - Finished processing dependencies for wolfcrypt==0.1 + Finished processing dependencies for wolfcrypt==0.1.0 4. Test locally with ``tox``:: @@ -37,5 +39,21 @@ A Python wrapper which encapsulates the wolfCrypt API from wolfSSL library $ tox ... _________________________________ summary _________________________________ + py26: commands succeeded py27: commands succeeded + py35: commands succeeded congratulations :) + +Licensing +========= + + +wolfSSL (formerly known as CyaSSL) and wolfCrypt are either licensed for use +under the GPLv2 or a standard commercial license. For our users who cannot use +wolfSSL under GPLv2, a commercial license to wolfSSL and wolfCrypt is available. +Please contact wolfSSL Inc. directly at: + +Email: licensing@wolfssl.com +Phone: +1 425 245-8247 + +More information can be found on the `wolfSSL website `_. diff --git a/wrapper/python/docs/Makefile b/wrapper/python/docs/Makefile index 805fe1fdc..c552bc9b3 100644 --- a/wrapper/python/docs/Makefile +++ b/wrapper/python/docs/Makefile @@ -96,9 +96,9 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/wolfCrypt.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/wolfcrypt.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/wolfCrypt.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/wolfcrypt.qhc" .PHONY: applehelp applehelp: @@ -115,8 +115,8 @@ devhelp: @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/wolfCrypt" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/wolfCrypt" + @echo "# mkdir -p $$HOME/.local/share/devhelp/wolfcrypt" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/wolfcrypt" @echo "# devhelp" .PHONY: epub diff --git a/wrapper/python/docs/asymmetric.rst b/wrapper/python/docs/asymmetric.rst new file mode 100644 index 000000000..970078dee --- /dev/null +++ b/wrapper/python/docs/asymmetric.rst @@ -0,0 +1,74 @@ +Asymmetric Key Algorithms +========================= + +.. module:: wolfcrypt.ciphers + +**Asymmetric key algorithms** are encryption algorithms that use **a pair +of cryptographic keys**, one for data encryption and signing and the other +one for data decryption and signature verification. + +``wolfcrypt`` provides access to the following **Asymmetric Key Ciphers**: + +Asymmetric Key Encryption Classes +--------------------------------- + +.. autoclass:: RsaPublic + :members: + :inherited-members: + +.. autoclass:: RsaPrivate + :members: + :inherited-members: + + +Example +------- + + >>> from wolfcrypt.ciphers import RsaPrivate, RsaPublic + >>> from wolfcrypt.utils import h2b + >>> + >>> private = "3082025C02010002818100BC730EA849F374A2A9EF18A5DA559921F9C8ECB36D" \ + ... + "48E53535757737ECD161905F3ED9E4D5DF94CAC1A9D719DA86C9E84DC4613682" \ + ... + "FEABAD7E7725BB8D11A5BC623AA838CC39A20466B4F7F7F3AADA4D020EBB5E8D" \ + ... + "6948DC77C9280E22E96BA426BA4CE8C1FD4A6F2B1FEF8AAEF69062E5641EEB2B" \ + ... + "3C67C8DC2700F6916865A902030100010281801397EAE8387825A25C04CE0D40" \ + ... + "7C31E5C470CD9B823B5809863B665FDC3190F14FD5DB15DDDED73B9593311831" \ + ... + "0E5EA3D6A21A716E81481C4BCFDB8E7A866132DCFB55C1166D279224458BF1B8" \ + ... + "48B14B1DACDEDADD8E2FC291FBA5A96EF83A6AF1FD5018EF9FE7C3CA78EA56D3" \ + ... + "D3725B96DD4E064E3AC3D9BE72B66507074C01024100FA47D47A7C923C55EF81" \ + ... + "F041302DA3CF8F1CE6872705700DDF9835D6F18B382F24B5D084B6794F712994" \ + ... + "5AF0646AACE772C6ED4D59983E673AF3742CF9611769024100C0C1820D0CEBC6" \ + ... + "2FDC92F99D821A31E9E9F74BF282871CEE166AD11D188270F3C0B62FF6F3F71D" \ + ... + "F18623C84EEB8F568E8FF5BFF1F72BB5CC3DC657390C1B54410241009D7E05DE" \ + ... + "EDF4B7B2FBFC304B551DE32F0147966905CD0E2E2CBD8363B6AB7CB76DCA5B64" \ + ... + "A7CEBE86DF3B53DE61D21EEBA5F637EDACAB78D94CE755FBD71199C102401898" \ + ... + "1829E61E2739702168AC0A2FA172C121869538C65890A0579CBAE3A7B115C8DE" \ + ... + "F61BC2612376EFB09D1C44BE1343396717C89DCAFBF545648B38822CF2810240" \ + ... + "3989E59C195530BAB7488C48140EF49F7E779743E1B419353123759C3B44AD69" \ + ... + "1256EE0061641666D37C742B15B4A2FEBF086B1A5D3F9012B105863129DBD9E2" + >>> + >>> prv = RsaPrivate(h2b(private)) + >>> + >>> public = "30819F300D06092A864886F70D010101050003818D0030818902818100BC730E" \ + ... + "A849F374A2A9EF18A5DA559921F9C8ECB36D48E53535757737ECD161905F3ED9" \ + ... + "E4D5DF94CAC1A9D719DA86C9E84DC4613682FEABAD7E7725BB8D11A5BC623AA8" \ + ... + "38CC39A20466B4F7F7F3AADA4D020EBB5E8D6948DC77C9280E22E96BA426BA4C" \ + ... + "E8C1FD4A6F2B1FEF8AAEF69062E5641EEB2B3C67C8DC2700F6916865A90203010001" + >>> + >>> pub = RsaPublic(h2b(public)) + >>> + >>> plaintext = b"Everyone gets Friday off." + >>> + >>> ciphertext = pub.encrypt(plaintext) + >>> ciphertext # doctest: +SKIP + b'e\xb7\xc2\xad\x0c\x04.\xefU8\x17QB\x852\x03\x01\xef\xbe=\xb4\xaf\xaf\x97\x9e4\x96\x9f\xc3\x8e\x87\x9a8o$.|_e\x1d\xa2yi?\x83\x18\xf9Yr|\x1fQ\x1a\x18\x1e\xab\xd17\xc5\x8c\xae\x08c)\xbc\nIr\x8d\xc3\x88\x7f\xde\x1f\x1a^lB\r\xf1\xc0\xfd0\xdeA\xf3\xd2\xe5q\x9a0\xee\xb4,\x97\x80\xa4|U;\xe6\x11\xf0\xc2Q\x987\xe1>F\xf5\x14\x186@G~(Q\xf2;\xcb\x05\xee\x88\x0b\xd8\xa7' + >>> + >>> prv.decrypt(ciphertext) + b'Everyone gets Friday off.' + >>> + >>> signature = prv.sign(plaintext) + >>> signature # doctest: +SKIP + b'~\xc4\xe65\x15\xb17\x7fX\xaf,\xc2lw\xbd\x8f\t\x9d\xbf\xac\xdez\x90\xb4\x9f\x1aM\x88#Z\xea\xcb\xa6\xdb\x99\xf55\xd0\xfe|Mu\xb6\xb79(t\x81+h\xf2\xcd\x88v\xa8\xbaM\x86\xcfk\xe8\xf3\x0b\xb8\x8ew\xda>\xf8\xd5[H\xeaAh\xc6\xdaQlo]\xdd\xf8w\xe7#M-\x12f\xae,\xdd\xa6d FP<;R\xa2\x96hJ\xee_\x1fh\xaa\xc8\xdfAJ\xa5\xdd\x05\xc4\x89\x0c\xd7\xa0C\xb7u"U\x03' + >>> + >>> pub.verify(signature) + b'Everyone gets Friday off.' diff --git a/wrapper/python/docs/conf.py b/wrapper/python/docs/conf.py index fe64bbb29..b9d4b4a8c 100644 --- a/wrapper/python/docs/conf.py +++ b/wrapper/python/docs/conf.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # -# wolfCrypt documentation build configuration file, created by -# sphinx-quickstart on Fri Apr 29 16:04:23 2016. +# wolfcrypt documentation build configuration file, created by +# sphinx-quickstart on Fri Apr 29 16:47:53 2016. # # This file is execfile()d with the current directory set to its # containing dir. @@ -31,8 +31,10 @@ import sphinx_rtd_theme # ones. extensions = [ 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', + 'sphinx.ext.githubpages', ] # Add any paths that contain templates here, relative to this directory. @@ -50,18 +52,21 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'wolfCrypt' -copyright = u'2016, wolfSSL' +project = u'wolfcrypt' +copyright = u'2016, wolfSSL Inc. All rights reserved' author = u'wolfSSL' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # -# The short X.Y version. -version = u'0.0' -# The full version, including alpha/beta/rc tags. -release = u'0.0.1' + +base_dir = os.path.join(os.path.dirname(__file__), os.pardir) +about = {} +with open(os.path.join(base_dir, "wolfcrypt", "about.py")) as f: + exec(f.read(), about) + +version = release = about["__version__"] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -125,7 +130,7 @@ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # The name for this set of Sphinx documents. # " v documentation" by default. -#html_title = u'wolfCrypt v0.0.1' +#html_title = u'%s v%s' % (project, release) # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None @@ -207,7 +212,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'wolfCryptdoc' +htmlhelp_basename = 'wolfcrypt-pydoc' # -- Options for LaTeX output --------------------------------------------- @@ -229,7 +234,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'wolfCrypt.tex', u'wolfCrypt Documentation', + (master_doc, 'wolfcrypt.tex', u'wolfcrypt Python Documentation', u'wolfSSL', 'manual'), ] @@ -259,7 +264,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'wolfcrypt', u'wolfCrypt Documentation', + (master_doc, 'wolfcrypt', u'wolfcrypt Python Documentation', [author], 1) ] @@ -273,8 +278,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'wolfCrypt', u'wolfCrypt Documentation', - author, 'wolfCrypt', 'One line description of project.', + (master_doc, 'wolfcrypt', u'wolfcrypt Python Documentation', + author, 'wolfcrypt', 'One line description of project.', 'Miscellaneous'), ] @@ -289,3 +294,6 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False + +# Preserves the order of the members, doesn't sorts them alphabetically. +autodoc_member_order = 'bysource' diff --git a/wrapper/python/docs/digest.rst b/wrapper/python/docs/digest.rst new file mode 100644 index 000000000..0d79f8ef0 --- /dev/null +++ b/wrapper/python/docs/digest.rst @@ -0,0 +1,71 @@ +Message Digests +=============== + +.. module:: wolfcrypt.hashes + +A **message digest** is the output of a **cryptographic hash function** +containing a string of bytes created by a **one-way formula** using the +original message as input. + +Message digests are designed to protect the integrity of a piece of data or +media to detect changes and alterations to any part of a message. + + +Hashing Classes +--------------- + +Interface +~~~~~~~~~ + +All Hashing Functions available in this module implements the following +interface: + +.. autoclass:: _Hash + :members: + +SHA-1 +~~~~~ + +.. attention:: + + NIST has deprecated SHA-1 in favor of the SHA-2 variants. New applications + are strongly suggested to use SHA-2 over SHA-1. + +.. autoclass:: Sha + +SHA-2 family +~~~~~~~~~~~~ + +.. autoclass:: Sha256 + + +.. autoclass:: Sha384 + + +.. autoclass:: Sha512 + + +Example +------- + +.. doctest:: + + >>> from wolfcrypt.hashes import Sha256 + >>> + >>> s = Sha256() + >>> s.update(b'wolf') + >>> s.update(b'crypt') + >>> s.digest() + b'\x96\xe0.{\x1c\xbc\xd6\xf1\x04\xfe\x1f\xdbFR\x02zU\x05\xb6\x86R\xb7\x00\x95\xc61\x8f\x9d\xce\r\x18D' + >>> s.hexdigest() + b'96e02e7b1cbcd6f104fe1fdb4652027a5505b68652b70095c6318f9dce0d1844' + >>> + >>> s.update(b'rocks') + >>> s.hexdigest() + b'e1a50df419d65715c48316bdc6a6f7f0485f4b26c1b107228faf17988b61c83f' + >>> + >>> Sha256(b'wolfcryptrocks').hexdigest() + b'e1a50df419d65715c48316bdc6a6f7f0485f4b26c1b107228faf17988b61c83f' + >>> + >>> Sha256.new(b'wolfcryptrocks').hexdigest() + b'e1a50df419d65715c48316bdc6a6f7f0485f4b26c1b107228faf17988b61c83f' diff --git a/wrapper/python/docs/index.rst b/wrapper/python/docs/index.rst index 35b4525b2..9b796b186 100644 --- a/wrapper/python/docs/index.rst +++ b/wrapper/python/docs/index.rst @@ -1,22 +1,46 @@ -.. wolfCrypt documentation master file, created by - sphinx-quickstart on Fri Apr 29 16:04:23 2016. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +wolfCrypt Python Documentation +================================== -Welcome to wolfCrypt's documentation! -===================================== +**wolfCrypt Python**, a.k.a. ``wolfcrypt`` is a Python library that encapsulates +**wolfSSL's wolfCrypt API**. -Contents: +**wolfCrypt** is a lightweight, portable, C-language-based crypto library +targeted at IoT, embedded, and RTOS environments primarily because of its size, +speed, and feature set. It works seamlessly in desktop, enterprise, and cloud +environments as well. + +Summary +------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + symmetric + asymmetric + digest + mac + random +Licensing +--------- -Indices and tables -================== +wolfSSL’s software is available under two distinct licensing models: +open source and standard commercial licensing. Please see the relevant +section below for information on each type of license. -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +Open Source +~~~~~~~~~~~ +wolfCrypt and wolfSSL software are free software downloads and may be modified +to the needs of the user as long as the user adheres to version two of the GPL +License. The GPLv2 license can be found on the `gnu.org website +`_. + +Commercial Licensing +~~~~~~~~~~~~~~~~~~~~ + +Businesses and enterprises who wish to incorporate wolfSSL products into +proprietary appliances or other commercial software products for +re-distribution must license commercial versions. Licenses are generally issued +for one product and include unlimited royalty-free distribution. Custom +licensing terms are also available at licensing@wolfssl.com. diff --git a/wrapper/python/docs/mac.rst b/wrapper/python/docs/mac.rst new file mode 100644 index 000000000..14b39cd78 --- /dev/null +++ b/wrapper/python/docs/mac.rst @@ -0,0 +1,74 @@ +Message Authentication Codes +============================ + +.. module:: wolfcrypt.hashes + +A **message authentication code** (MAC) is a short piece of information used +to authenticate a message — in other words, to confirm that the message came +from the stated sender (its authenticity) and has not been changed in transit +(its integrity). + +``wolfcrypt`` implements the **Hash-based message authentication code** (HMAC), +which uses a cryptographic hash function coupled with a secret key to produce +**message authentication codes**. + + +Hmac Classes +------------ + +Interface +~~~~~~~~~ + +All Hmac classes available in this module implements the following +interface: + +.. autoclass:: _Hmac + :members: + :inherited-members: + +SHA-1 +~~~~~ + +.. attention:: + + NIST has deprecated SHA-1 in favor of the SHA-2 variants. New applications + are strongly suggested to use SHA-2 over SHA-1. + +.. autoclass:: HmacSha + +SHA-2 family +~~~~~~~~~~~~ + +.. autoclass:: HmacSha256 + + +.. autoclass:: HmacSha384 + + +.. autoclass:: HmacSha512 + + +Example +------- + +.. doctest:: + + >>> from wolfcrypt.hashes import HmacSha256 + >>> + >>> h = HmacSha256('secret') + >>> h.update("wolf") + >>> h.update("crypt") + >>> h.digest() + b'\x18\xbf*\t9\xa2o\xdf\\\xc8\xe0\xc2U\x94,\x8dY\x02;\x1c>> h.hexdigest() + b'18bf2a0939a26fdf5cc8e0c255942c8d59023b1c3c51df8ddb8633fbc166236f' + >>> + >>> h.update("rocks") + >>> h.hexdigest() + b'85dc8c1995d20b17942d52773d8a597d028ad958e5736beafb59a4742f63889e' + >>> + >>> HmacSha256('secret', 'wolfcryptrocks').hexdigest() + b'85dc8c1995d20b17942d52773d8a597d028ad958e5736beafb59a4742f63889e' + >>> + >>> HmacSha256.new('secret', 'wolfcryptrocks').hexdigest() + b'85dc8c1995d20b17942d52773d8a597d028ad958e5736beafb59a4742f63889e' diff --git a/wrapper/python/docs/random.rst b/wrapper/python/docs/random.rst new file mode 100644 index 000000000..ba8e33bda --- /dev/null +++ b/wrapper/python/docs/random.rst @@ -0,0 +1,30 @@ +Random Number Generation +======================== + +A **cryptographically secure pseudo-random number generator** (CSPRNG) is a +**pseudo-random number generator** (PRNG) with properties that make it suitable +for use in cryptography. + +Using the standard random module APIs for cryptographic keys or initialization +vectors can result in major security issues depending on the algorithms in use. + +``wolfcrypt`` provides the following CSPRNG implementation: + +.. module:: wolfcrypt.random + +.. autoclass:: Random + :members: + + +Example +------- + + >>> from wolfcrypt.random import Random + >>> + >>> r = Random() + >>> b = r.byte() + >>> b # doctest: +SKIP + b'\x8c' + >>> b16 = r.bytes(16) + >>> b16 # doctest: +SKIP + b']\x93nk\x95\xbc@\xffX\xab\xdcB\xda\x11\xf7\x03' diff --git a/wrapper/python/docs/symmetric.rst b/wrapper/python/docs/symmetric.rst new file mode 100644 index 000000000..0535080b0 --- /dev/null +++ b/wrapper/python/docs/symmetric.rst @@ -0,0 +1,44 @@ +Symmetric Key Algorithms +======================== + +.. module:: wolfcrypt.ciphers + +**Symmetric key algorithms** are encryption algorithms that use the **same +cryptographic keys** for both encryption and decryption of data. +This operation is also known as **Symmetric Key Encryption**. + +``wolfcrypt`` provides access to the following **Symmetric Key Ciphers**: + +Symmetric Key Encryption Classes +-------------------------------- + +Interface +~~~~~~~~~ + +All **Symmetric Key Ciphers** available in this module implements the following +interface: + +.. autoclass:: _Cipher + :members: + +Classes +~~~~~~~ + +.. autoclass:: Aes + +.. autoclass:: Des3 + + +Example +------- + +.. doctest:: + + >>> from wolfcrypt.ciphers import Aes, MODE_CBC + >>> + >>> cipher = Aes(b'0123456789abcdef', MODE_CBC, b'1234567890abcdef') + >>> ciphertext = cipher.encrypt('now is the time ') + >>> ciphertext + b'\x95\x94\x92W_B\x81S,\xcc\x9dFw\xa23\xcb' + >>> cipher.decrypt(ciphertext) + b'now is the time ' diff --git a/wrapper/python/requirements-testing.txt b/wrapper/python/requirements-testing.txt index 0d3256e5b..61def5278 100644 --- a/wrapper/python/requirements-testing.txt +++ b/wrapper/python/requirements-testing.txt @@ -1,4 +1,3 @@ pytest>=2.9.1 -cffi>=1.5.2 +cffi>=1.6.0 tox>=2.3.1 -sphinx_rtd_theme>=0.1.9 diff --git a/wrapper/python/setup.py b/wrapper/python/setup.py index 9c6b3f64a..35260d529 100755 --- a/wrapper/python/setup.py +++ b/wrapper/python/setup.py @@ -10,12 +10,12 @@ os.chdir(os.path.dirname(sys.argv[0]) or ".") setup( name="wolfcrypt", - version="0.0.1", - description="A python wrapper for the wolfCrypt API", + version="0.1.0", + description="A Python wrapper that encapsulates wolfSSL's wolfCrypt API", long_description=open("README.rst", "rt").read(), - url="https://github.com/wolfssl/wolfcrypt-py", - author="Moisés Guimarães", - author_email="moises@wolfssl.com", + url="https://wolfssl.github.io/wolfcrypt-py", + author="wolfSSL", + author_email="info@wolfssl.com", classifiers=[ "Development Status :: 0 - Alpha", "Programming Language :: Python :: 2", @@ -24,7 +24,7 @@ setup( "License :: GPLv2 License :: Commercial License", ], packages=find_packages(), - setup_requires=["cffi>=1.5.2"], - install_requires=["cffi>=1.5.2"], + setup_requires=["cffi>=1.6.0"], + install_requires=["cffi>=1.6.0"], cffi_modules=["./wolfcrypt/build_ffi.py:ffi"] ) diff --git a/wrapper/python/test/test_ciphers.py b/wrapper/python/test/test_ciphers.py index 57dd2c6f2..c1929c89e 100644 --- a/wrapper/python/test/test_ciphers.py +++ b/wrapper/python/test/test_ciphers.py @@ -19,12 +19,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import unittest from wolfcrypt.ciphers import * +from wolfcrypt.utils import t2b, h2b class TestDes3(unittest.TestCase): - key = "0123456789abcdeffedeba987654321089abcdef01234567".decode("hex") - IV = "1234567890abcdef".decode("hex") - plain = "Now is the time for all " - cipher = "43a0297ed184f80e8964843212d508981894157487127db0".decode("hex") + key = h2b("0123456789abcdeffedeba987654321089abcdef01234567") + IV = h2b("1234567890abcdef") + plain = t2b("Now is the time for all ") + cipher = h2b("43a0297ed184f80e8964843212d508981894157487127db0") def setUp(self): @@ -32,9 +33,6 @@ class TestDes3(unittest.TestCase): def test_raises(self): - # invalid construction - self.assertRaises(ValueError, Des3) - # invalid key length self.assertRaises(ValueError, Des3.new, "key", MODE_CBC, self.IV) @@ -54,7 +52,7 @@ class TestDes3(unittest.TestCase): def test_multi_encryption(self): - result = "" + result = t2b("") segments = tuple(self.plain[i:i + Des3.block_size] \ for i in range(0, len(self.plain), Des3.block_size)) @@ -69,7 +67,7 @@ class TestDes3(unittest.TestCase): def test_multi_decryption(self): - result = "" + result = t2b("") segments = tuple(self.cipher[i:i + Des3.block_size] \ for i in range(0, len(self.cipher), Des3.block_size)) @@ -82,8 +80,8 @@ class TestDes3(unittest.TestCase): class TestAes(unittest.TestCase): key = "0123456789abcdef" IV = "1234567890abcdef" - plain = "now is the time " - cipher = "959492575f4281532ccc9d4677a233cb".decode("hex") + plain = t2b("now is the time ") + cipher = h2b("959492575f4281532ccc9d4677a233cb") def setUp(self): @@ -91,9 +89,6 @@ class TestAes(unittest.TestCase): def test_raises(self): - # invalid construction - self.assertRaises(ValueError, Aes) - # invalid key length self.assertRaises(ValueError, Aes.new, "key", MODE_CBC, self.IV) @@ -113,7 +108,7 @@ class TestAes(unittest.TestCase): def test_multi_encryption(self): - result = "" + result = t2b("") segments = tuple(self.plain[i:i + self.aes.block_size] \ for i in range(0, len(self.plain), self.aes.block_size)) @@ -128,7 +123,7 @@ class TestAes(unittest.TestCase): def test_multi_decryption(self): - result = "" + result = t2b("") segments = tuple(self.cipher[i:i + self.aes.block_size] \ for i in range(0, len(self.cipher), self.aes.block_size)) @@ -159,16 +154,16 @@ class TestRsaPrivate(unittest.TestCase): + "3989E59C195530BAB7488C48140EF49F7E779743E1B419353123759C3B44AD69" \ + "1256EE0061641666D37C742B15B4A2FEBF086B1A5D3F9012B105863129DBD9E2" - plain = "Everyone gets Friday off." + plain = t2b("Everyone gets Friday off.") def setUp(self): - self.rsa = RsaPrivate(self.key.decode("hex")) + self.rsa = RsaPrivate(h2b(self.key)) def test_raises(self): # invalid key - self.assertRaises(KeyError, RsaPrivate, 'key') + self.assertRaises(WolfCryptError, RsaPrivate, 'key') def test_output_size(self): @@ -218,17 +213,17 @@ class TestRsaPublic(unittest.TestCase): + "38CC39A20466B4F7F7F3AADA4D020EBB5E8D6948DC77C9280E22E96BA426BA4C" \ + "E8C1FD4A6F2B1FEF8AAEF69062E5641EEB2B3C67C8DC2700F6916865A90203010001" - plain = "Everyone gets Friday off." + plain = t2b("Everyone gets Friday off.") def setUp(self): - self.private = RsaPrivate(self.prv.decode("hex")) - self.public = RsaPublic(self.pub.decode("hex")) + self.private = RsaPrivate(h2b(self.prv)) + self.public = RsaPublic(h2b(self.pub)) def test_raises(self): # invalid key - self.assertRaises(KeyError, RsaPublic, 'key') + self.assertRaises(WolfCryptError, RsaPublic, 'key') def test_output_size(self): diff --git a/wrapper/python/test/test_hashes.py b/wrapper/python/test/test_hashes.py index 5ed245462..79e953e9c 100644 --- a/wrapper/python/test/test_hashes.py +++ b/wrapper/python/test/test_hashes.py @@ -19,29 +19,28 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import unittest from wolfcrypt.hashes import * +from wolfcrypt.utils import t2b, h2b class TestSha(unittest.TestCase): - digest = "1b6182d68ae91ce0853bd9c6b6edfedd4b6a510d" + _class = Sha + digest = t2b("1b6182d68ae91ce0853bd9c6b6edfedd4b6a510d") def setUp(self): - self.hash = Sha.new() + self.hash = self._class() def test_new(self): - # invalid construction - self.assertRaises(ValueError, Sha) - # update inside constructor - assert Sha.new("wolfcrypt").hexdigest() == self.digest + assert self._class.new("wolfcrypt").hexdigest() == self.digest def test_hash_update_001(self): self.hash.update("wolfcrypt") assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") + assert self.hash.digest() == h2b(self.digest) def test_hash_update_002(self): @@ -49,7 +48,7 @@ class TestSha(unittest.TestCase): self.hash.update("crypt") assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") + assert self.hash.digest() == h2b(self.digest) def test_hash_copy(self): @@ -66,167 +65,47 @@ class TestSha(unittest.TestCase): assert self.hash.hexdigest() == copy.hexdigest() == self.digest -class TestSha256(unittest.TestCase): - digest = "96e02e7b1cbcd6f104fe1fdb4652027a5505b68652b70095c6318f9dce0d1844" +class TestSha256(TestSha): + _class = Sha256 + digest = t2b("96e02e7b1cbcd6f104fe1fdb4652027a" \ + + "5505b68652b70095c6318f9dce0d1844") - def setUp(self): - self.hash = Sha256.new() +class TestSha384(TestSha): + _class = Sha384 + digest = t2b("4c79d80531203a16f91bee325f18c6aada47f9382fe44fc1" \ + + "1f92917837e9b7902f5dccb7d3656f667a1dce3460bc884b") - def test_new(self): - # invalid construction - self.assertRaises(ValueError, Sha256) - - # update inside constructor - assert Sha256.new("wolfcrypt").hexdigest() == self.digest - - - def test_hash_update_001(self): - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_update_002(self): - self.hash.update("wolf") - self.hash.update("crypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_copy(self): - copy = self.hash.copy() - - assert self.hash.hexdigest() == copy.hexdigest() - - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() != copy.hexdigest() - - copy.update("wolfcrypt") - - assert self.hash.hexdigest() == copy.hexdigest() == self.digest - - -class TestSha384(unittest.TestCase): - digest = "4c79d80531203a16f91bee325f18c6aada47f9382fe44fc1" \ - + "1f92917837e9b7902f5dccb7d3656f667a1dce3460bc884b" - - - def setUp(self): - self.hash = Sha384.new() - - - def test_new(self): - # invalid construction - self.assertRaises(ValueError, Sha384) - - # update inside constructor - assert Sha384.new("wolfcrypt").hexdigest() == self.digest - - - def test_hash_update_001(self): - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_update_002(self): - self.hash.update("wolf") - self.hash.update("crypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_copy(self): - copy = self.hash.copy() - - assert self.hash.hexdigest() == copy.hexdigest() - - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() != copy.hexdigest() - - copy.update("wolfcrypt") - - assert self.hash.hexdigest() == copy.hexdigest() == self.digest - - -class TestSha512(unittest.TestCase): - digest = "88fcf67ffd8558d713f9cedcd852db479e6573f0bd9955610a993f609637553c"\ - + "e8fff55e644ee8a106aae19c07f91b3f2a2a6d40dfa7302c0fa6a1a9a5bfa03f" - - - def setUp(self): - self.hash = Sha512.new() - - - def test_new(self): - # invalid construction - self.assertRaises(ValueError, Sha512) - - # update inside constructor - assert Sha512.new("wolfcrypt").hexdigest() == self.digest - - - def test_hash_update_001(self): - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_update_002(self): - self.hash.update("wolf") - self.hash.update("crypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_copy(self): - copy = self.hash.copy() - - assert self.hash.hexdigest() == copy.hexdigest() - - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() != copy.hexdigest() - - copy.update("wolfcrypt") - - assert self.hash.hexdigest() == copy.hexdigest() == self.digest +class TestSha512(TestSha): + _class = Sha512 + digest = t2b("88fcf67ffd8558d713f9cedcd852db47" \ + + "9e6573f0bd9955610a993f609637553c" \ + + "e8fff55e644ee8a106aae19c07f91b3f" \ + + "2a2a6d40dfa7302c0fa6a1a9a5bfa03f") _HMAC_KEY = "python" class TestHmacSha(unittest.TestCase): - digest = "5dfabcfb3a25540824867cd21f065f52f73491e0" + _class = HmacSha + digest = t2b("5dfabcfb3a25540824867cd21f065f52f73491e0") def setUp(self): - self.hash = HmacSha.new(_HMAC_KEY) + self.hash = self._class(_HMAC_KEY) def test_new(self): - # invalid construction - self.assertRaises(ValueError, HmacSha) - # update inside constructor - assert HmacSha.new(_HMAC_KEY, "wolfcrypt").hexdigest() == self.digest + assert self._class.new(_HMAC_KEY,"wolfcrypt").hexdigest() == self.digest def test_hash_update_001(self): self.hash.update("wolfcrypt") assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") def test_hash_update_002(self): @@ -234,7 +113,6 @@ class TestHmacSha(unittest.TestCase): self.hash.update("crypt") assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") def test_hash_copy(self): @@ -251,138 +129,21 @@ class TestHmacSha(unittest.TestCase): assert self.hash.hexdigest() == copy.hexdigest() == self.digest -class TestHmacSha256(unittest.TestCase): - digest = "4b641d721493d80f019d9447830ebfee89234a7d594378b89f8bb73873576bf6" +class TestHmacSha256(TestHmacSha): + _class = HmacSha256 + digest = t2b("4b641d721493d80f019d9447830ebfee" \ + + "89234a7d594378b89f8bb73873576bf6") - def setUp(self): - self.hash = HmacSha256.new(_HMAC_KEY) +class TestHmacSha384(TestHmacSha): + _class = HmacSha384 + digest = t2b("e72c72070c9c5c78e3286593068a510c1740cdf9dc34b512" \ + + "ccec97320295db1fe673216b46fe72e81f399a9ec04780ab") - def test_new(self): - # invalid construction - self.assertRaises(ValueError, HmacSha256) - - # update inside constructor - assert HmacSha256.new(_HMAC_KEY, "wolfcrypt").hexdigest() == self.digest - - - def test_hash_update_001(self): - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_update_002(self): - self.hash.update("wolf") - self.hash.update("crypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_copy(self): - copy = self.hash.copy() - - assert self.hash.hexdigest() == copy.hexdigest() - - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() != copy.hexdigest() - - copy.update("wolfcrypt") - - assert self.hash.hexdigest() == copy.hexdigest() == self.digest - - -class TestHmacSha384(unittest.TestCase): - digest = "e72c72070c9c5c78e3286593068a510c1740cdf9dc34b512" \ - + "ccec97320295db1fe673216b46fe72e81f399a9ec04780ab" - - - def setUp(self): - self.hash = HmacSha384.new(_HMAC_KEY) - - - def test_new(self): - # invalid construction - self.assertRaises(ValueError, HmacSha384) - - # update inside constructor - assert HmacSha384.new(_HMAC_KEY, "wolfcrypt").hexdigest() == self.digest - - - def test_hash_update_001(self): - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_update_002(self): - self.hash.update("wolf") - self.hash.update("crypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_copy(self): - copy = self.hash.copy() - - assert self.hash.hexdigest() == copy.hexdigest() - - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() != copy.hexdigest() - - copy.update("wolfcrypt") - - assert self.hash.hexdigest() == copy.hexdigest() == self.digest - - -class TestHmacSha512(unittest.TestCase): - digest = "c7f48db79314fc2b5be9a93fd58601a1bf42f397ec7f66dba034d44503890e6b"\ - + "5708242dcd71a248a78162d815c685f6038a4ac8cb34b8bf18986dbd300c9b41" - - - def setUp(self): - self.hash = HmacSha512.new(_HMAC_KEY) - - - def test_new(self): - # invalid construction - self.assertRaises(ValueError, HmacSha512) - - # update inside constructor - assert HmacSha512.new(_HMAC_KEY, "wolfcrypt").hexdigest() == self.digest - - - def test_hash_update_001(self): - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_update_002(self): - self.hash.update("wolf") - self.hash.update("crypt") - - assert self.hash.hexdigest() == self.digest - assert self.hash.digest() == self.digest.decode("hex") - - - def test_hash_copy(self): - copy = self.hash.copy() - - assert self.hash.hexdigest() == copy.hexdigest() - - self.hash.update("wolfcrypt") - - assert self.hash.hexdigest() != copy.hexdigest() - - copy.update("wolfcrypt") - - assert self.hash.hexdigest() == copy.hexdigest() == self.digest +class TestHmacSha512(TestHmacSha): + _class = HmacSha512 + digest = t2b("c7f48db79314fc2b5be9a93fd58601a1" \ + + "bf42f397ec7f66dba034d44503890e6b" \ + + "5708242dcd71a248a78162d815c685f6" \ + + "038a4ac8cb34b8bf18986dbd300c9b41") diff --git a/wrapper/python/tox.ini b/wrapper/python/tox.ini index c8778e414..20d93e22f 100644 --- a/wrapper/python/tox.ini +++ b/wrapper/python/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27 +envlist=py26,py27,py35 [testenv] deps=-rrequirements-testing.txt diff --git a/wrapper/python/wolfcrypt/__init__.py b/wrapper/python/wolfcrypt/__init__.py index 498cd8902..907791ff7 100644 --- a/wrapper/python/wolfcrypt/__init__.py +++ b/wrapper/python/wolfcrypt/__init__.py @@ -1,4 +1,4 @@ -# WolfCrypt +# __init__.py # # Copyright (C) 2006-2016 wolfSSL Inc. # diff --git a/wrapper/python/wolfcrypt/about.py b/wrapper/python/wolfcrypt/about.py new file mode 100644 index 000000000..2d8017e91 --- /dev/null +++ b/wrapper/python/wolfcrypt/about.py @@ -0,0 +1,58 @@ +# __about__.py +# +# Copyright (C) 2006-2016 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 + +metadata = dict( + __name__ = "wolfcrypt", + __version__ = "0.1.0", + __author__ = "wolfSSL Inc.", + __email__ = "info@wolfssl.com", + __url__ = "https://wolfssl.github.io/wolfcrypt-py", + __summary__ = "A Python library that encapsulates wolfSSL's wolfCrypt API", + __keywords__ = """ + cryptography, aes, des3, rsa, sha, sha256, sha384, sha512, hmac, random + """, + __license__ = """ + wolfSSL’s software is available under two distinct licensing models: + open source and standard commercial licensing. Please see the relevant + section below for information on each type of license. + + Open Source + + wolfSSL (formerly CyaSSL), yaSSL, wolfCrypt, yaSSH and TaoCrypt software + are free software downloads and may be modified to the needs of the user + as long as the user adheres to version two of the GPL License. The GPLv2 + license can be found on the gnu.org website: + http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + + Commercial Licensing + + Businesses and enterprises who wish to incorporate wolfSSL products into + proprietary appliances or other commercial software products for + re-distribution must license commercial versions. Commercial licenses for + wolfSSL, yaSSL, and wolfCrypt are available for $5,000 USD per end product + or SKU. Licenses are generally issued for one product and include unlimited + royalty-free distribution. Custom licensing terms are also available. + """, + __copyright__ = "Copyright 2016 wolfSSL Inc. All rights reserved" +) + +globals().update(metadata) + +__all__ = metadata.keys() diff --git a/wrapper/python/wolfcrypt/build_ffi.py b/wrapper/python/wolfcrypt/build_ffi.py index 7d876751d..720e96cfc 100644 --- a/wrapper/python/wolfcrypt/build_ffi.py +++ b/wrapper/python/wolfcrypt/build_ffi.py @@ -1,4 +1,4 @@ -# build_random.py +# build_ffi.py # # Copyright (C) 2006-2016 wolfSSL Inc. # diff --git a/wrapper/python/wolfcrypt/ciphers.py b/wrapper/python/wolfcrypt/ciphers.py index 2aa69728c..4084e6bf7 100644 --- a/wrapper/python/wolfcrypt/ciphers.py +++ b/wrapper/python/wolfcrypt/ciphers.py @@ -17,10 +17,13 @@ # 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 -from wolfcrypt._ffi import ffi as _ffi -from wolfcrypt._ffi import lib as _lib +from wolfcrypt._ffi import ffi as _ffi +from wolfcrypt._ffi import lib as _lib +from wolfcrypt.utils import t2b from wolfcrypt.random import Random +from wolfcrypt.exceptions import * + # key direction flags _ENCRYPTION = 0 @@ -38,24 +41,19 @@ _FEEDBACK_MODES = [MODE_ECB, MODE_CBC, MODE_CFB, MODE_OFB, MODE_CTR] class _Cipher(object): - # Magic object that protects against constructors. - _JAPANESE_CYBER_SWORD = object() - - - def __init__(self, token=""): - if token is not self._JAPANESE_CYBER_SWORD: - # PEP 272 -- API for Block Encryption Algorithms v1.0 - raise ValueError("don't construct directly, use new([string])") - - - @classmethod - def new(cls, key, mode, IV=None, **kwargs): + """ + A **PEP 272: Block Encryption Algorithms** compliant + **Symmetric Key Cipher**. + """ + def __init__(self, key, mode, IV=None): if mode not in _FEEDBACK_MODES: raise ValueError("this mode is not supported") - if mode != MODE_CBC: - raise ValueError("this mode is not supported by this cipher") - self = cls(_Cipher._JAPANESE_CYBER_SWORD) + if mode == MODE_CBC: + if IV is None: + raise ValueError("this mode requires an 'IV' string") + else: + raise ValueError("this mode is not supported by this cipher") if self.key_size: if self.key_size != len(key): @@ -71,46 +69,92 @@ class _Cipher(object): raise ValueError("IV must be %d in length" % self.block_size) self._native_object = _ffi.new(self._native_type) - self._enc = None self._dec = None - self._key = key - self._IV = IV if IV else "\0" * self.block_size + self._key = t2b(key) - return self + if IV: + self._IV = t2b(IV) + else: + self._IV = t2b("\0" * self.block_size) + + + @classmethod + def new(cls, key, mode, IV=None, **kwargs): + """ + Returns a ciphering object, using the secret key contained in + the string **key**, and using the feedback mode **mode**, which + must be one of MODE_* defined in this module. + + If **mode** is MODE_CBC or MODE_CFB, **IV** must be provided and + must be a string of the same length as the block size. Not + providing a value of **IV** will result in a ValueError exception + being raised. + """ + return cls(key, mode, IV) def encrypt(self, string): + """ + Encrypts a non-empty string, using the key-dependent data in + the object, and with the appropriate feedback mode. The + string's length must be an exact multiple of the algorithm's + block size or, in CFB mode, of the segment size. Returns a + string containing the ciphertext. + """ + string = t2b(string) + if not string or len(string) % self.block_size: raise ValueError( "string must be a multiple of %d in length" % self.block_size) if self._enc is None: self._enc = _ffi.new(self._native_type) - self._set_key(_ENCRYPTION) + ret = self._set_key(_ENCRYPTION) + if ret < 0: + raise WolfCryptError("Invalid key error (%d)" % ret) - result = "\0" * len(string) - self._encrypt(result, string) + result = t2b("\0" * len(string)) + ret = self._encrypt(result, string) + if ret < 0: + raise WolfCryptError("Encryption error (%d)" % ret) return result def decrypt(self, string): + """ + Decrypts **string**, using the key-dependent data in the + object and with the appropriate feedback mode. The string's + length must be an exact multiple of the algorithm's block + size or, in CFB mode, of the segment size. Returns a string + containing the plaintext. + """ + string = t2b(string) + if not string or len(string) % self.block_size: raise ValueError( "string must be a multiple of %d in length" % self.block_size) if self._dec is None: self._dec = _ffi.new(self._native_type) - self._set_key(_DECRYPTION) + ret = self._set_key(_DECRYPTION) + if ret < 0: + raise WolfCryptError("Invalid key error (%d)" % ret) - result = "\0" * len(string) - self._decrypt(result, string) + result = t2b("\0" * len(string)) + ret = self._decrypt(result, string) + if ret < 0: + raise WolfCryptError("Decryption error (%d)" % ret) return result class Aes(_Cipher): + """ + The **Advanced Encryption Standard** (AES), a.k.a. Rijndael, is + a symmetric-key cipher standardized by **NIST**. + """ block_size = 16 key_size = None # 16, 24, 32 _key_sizes = [16, 24, 32] @@ -119,22 +163,28 @@ class Aes(_Cipher): def _set_key(self, direction): if direction == _ENCRYPTION: - _lib.wc_AesSetKey( + return _lib.wc_AesSetKey( self._enc, self._key, len(self._key), self._IV, _ENCRYPTION) else: - _lib.wc_AesSetKey( + return _lib.wc_AesSetKey( self._dec, self._key, len(self._key), self._IV, _DECRYPTION) def _encrypt(self, destination, source): - _lib.wc_AesCbcEncrypt(self._enc, destination, source, len(source)) + return _lib.wc_AesCbcEncrypt(self._enc, destination, source,len(source)) def _decrypt(self, destination, source): - _lib.wc_AesCbcDecrypt(self._dec, destination, source, len(source)) + return _lib.wc_AesCbcDecrypt(self._dec, destination, source,len(source)) class Des3(_Cipher): + """ + **Triple DES** (3DES) is the common name for the **Triple Data + Encryption Algorithm** (TDEA or Triple DEA) symmetric-key block + cipher, which applies the **Data Encryption Standard** (DES) + cipher algorithm three times to each data block. + """ block_size = 8 key_size = 24 _native_type = "Des3 *" @@ -142,24 +192,27 @@ class Des3(_Cipher): def _set_key(self, direction): if direction == _ENCRYPTION: - _lib.wc_Des3_SetKey(self._enc, self._key, self._IV, _ENCRYPTION) + return _lib.wc_Des3_SetKey(self._enc,self._key,self._IV,_ENCRYPTION) else: - _lib.wc_Des3_SetKey(self._dec, self._key, self._IV, _DECRYPTION) + return _lib.wc_Des3_SetKey(self._dec,self._key,self._IV,_DECRYPTION) def _encrypt(self, destination, source): - _lib.wc_Des3_CbcEncrypt(self._enc, destination, source, len(source)) + return _lib.wc_Des3_CbcEncrypt(self._enc,destination,source,len(source)) def _decrypt(self, destination, source): - _lib.wc_Des3_CbcDecrypt(self._dec, destination, source, len(source)) + return _lib.wc_Des3_CbcDecrypt(self._dec,destination,source,len(source)) class _Rsa(object): + RSA_MIN_PAD_SIZE = 11 + def __init__(self): self.native_object = _ffi.new("RsaKey *") - if _lib.wc_InitRsaKey(self.native_object, _ffi.NULL) != 0: - raise KeyError + ret = _lib.wc_InitRsaKey(self.native_object, _ffi.NULL) + if ret < 0: + raise WolfCryptError("Invalid key error (%d)" % ret) self._random = Random() @@ -171,22 +224,34 @@ class _Rsa(object): class RsaPublic(_Rsa): def __init__(self, key): + key = t2b(key) + _Rsa.__init__(self) idx = _ffi.new("word32*") idx[0] = 0 - if _lib.wc_RsaPublicKeyDecode(key, idx, self.native_object, len(key)): - raise KeyError + ret = _lib.wc_RsaPublicKeyDecode(key, idx, self.native_object, len(key)) + if ret < 0: + raise WolfCryptError("Invalid key error (%d)" % ret) self.output_size = _lib.wc_RsaEncryptSize(self.native_object) - if self.output_size <= 0: - raise KeyError + raise WolfCryptError("Invalid key error (%d)" % self.output_size) def encrypt(self, plaintext): - ciphertext = "\0" * self.output_size + """ + Encrypts **plaintext**, using the public key data in the + object. The plaintext's length must not be greater than: + + **self.output_size - self.RSA_MIN_PAD_SIZE** + + Returns a string containing the ciphertext. + """ + + plaintext = t2b(plaintext) + ciphertext = t2b("\0" * self.output_size) ret = _lib.wc_RsaPublicEncrypt(plaintext, len(plaintext), ciphertext, len(ciphertext), @@ -194,52 +259,84 @@ class RsaPublic(_Rsa): self._random.native_object) if ret != self.output_size: - raise KeyError + raise WolfCryptError("Encryption error (%d)" % ret) return ciphertext def verify(self, signature): - plaintext = "\0" * self.output_size + """ + Verifies **signature**, using the public key data in the + object. The signature's length must be equal to: + + **self.output_size** + + Returns a string containing the plaintext. + """ + signature = t2b(signature) + plaintext = t2b("\0" * self.output_size) ret = _lib.wc_RsaSSL_Verify(signature, len(signature), plaintext, len(plaintext), self.native_object) if ret < 0: - raise KeyError + raise WolfCryptError("Verify error (%d)" % ret) return plaintext[:ret] class RsaPrivate(RsaPublic): def __init__(self, key): + key = t2b(key) + _Rsa.__init__(self) idx = _ffi.new("word32*") idx[0] = 0 - if _lib.wc_RsaPrivateKeyDecode(key, idx, self.native_object, len(key)): - raise KeyError + ret = _lib.wc_RsaPrivateKeyDecode(key, idx, self.native_object,len(key)) + if ret < 0: + raise WolfCryptError("Invalid key error (%d)" % ret) self.output_size = _lib.wc_RsaEncryptSize(self.native_object) + if self.output_size <= 0: + raise WolfCryptError("Invalid key error (%d)" % self.output_size) def decrypt(self, ciphertext): - plaintext = "\0" * self.output_size + """ + Decrypts **ciphertext**, using the private key data in the + object. The ciphertext's length must be equal to: + + **self.output_size** + + Returns a string containing the plaintext. + """ + ciphertext = t2b(ciphertext) + plaintext = t2b("\0" * self.output_size) ret = _lib.wc_RsaPrivateDecrypt(ciphertext, len(ciphertext), plaintext, len(plaintext), self.native_object) if ret < 0: - raise KeyError + raise WolfCryptError("Decryption error (%d)" % ret) return plaintext[:ret] def sign(self, plaintext): - signature = "\0" * self.output_size + """ + Signs **plaintext**, using the private key data in the object. + The plaintext's length must not be greater than: + + **self.output_size - self.RSA_MIN_PAD_SIZE** + + Returns a string containing the signature. + """ + plaintext = t2b(plaintext) + signature = t2b("\0" * self.output_size) ret = _lib.wc_RsaSSL_Sign(plaintext, len(plaintext), signature, len(signature), @@ -247,6 +344,6 @@ class RsaPrivate(RsaPublic): self._random.native_object) if ret != self.output_size: - raise KeyError + raise WolfCryptError("Signature error (%d)" % ret) return signature diff --git a/wrapper/python/wolfcrypt/exceptions.py b/wrapper/python/wolfcrypt/exceptions.py new file mode 100644 index 000000000..838a2b2b0 --- /dev/null +++ b/wrapper/python/wolfcrypt/exceptions.py @@ -0,0 +1,23 @@ +# exceptions.py +# +# Copyright (C) 2006-2016 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 + + +class WolfCryptError(Exception): + pass diff --git a/wrapper/python/wolfcrypt/hashes.py b/wrapper/python/wolfcrypt/hashes.py index 18c0b54b3..816f205cc 100644 --- a/wrapper/python/wolfcrypt/hashes.py +++ b/wrapper/python/wolfcrypt/hashes.py @@ -17,36 +17,43 @@ # 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 -from wolfcrypt._ffi import ffi as _ffi -from wolfcrypt._ffi import lib as _lib +from wolfcrypt._ffi import ffi as _ffi +from wolfcrypt._ffi import lib as _lib +from wolfcrypt.utils import t2b, b2h +from wolfcrypt.exceptions import * class _Hash(object): - # Magic object that protects against constructors. - _JAPANESE_CYBER_SWORD = object() + """ + A **PEP 247: Cryptographic Hash Functions** compliant + **Hash Function Interface**. + """ + def __init__(self, string=None): + self._native_object = _ffi.new(self._native_type) + ret = self._init() + if ret < 0: + raise WolfCryptError("Hash init error (%d)" % ret) - - def __init__(self, token=""): - if token is not self._JAPANESE_CYBER_SWORD: - # PEP 247 -- API for Cryptographic Hash Functions - raise ValueError("don't construct directly, use new([string])") + if (string): + self.update(string) @classmethod def new(cls, string=None): - self = cls(cls._JAPANESE_CYBER_SWORD) - - self._native_object = _ffi.new(self._native_type) - - self._init() - - if (string): - self._update(string) - - return self + """ + Creates a new hashing object and returns it. The optional + **string** parameter, if supplied, will be immediately + hashed into the object's starting state, as if + obj.update(string) was called. + """ + return cls(string) def copy(self): + """ + Returns a separate copy of this hashing object. An update + to this copy won't affect the original object. + """ copy = self.new("") _ffi.memmove(copy._native_object, @@ -57,96 +64,142 @@ class _Hash(object): def update(self, string): - self._update(string) + """ + Hashes **string** into the current state of the hashing + object. update() can be called any number of times during + a hashing object's lifetime. + """ + string = t2b(string) + + ret = self._update(string) + if ret < 0: + raise WolfCryptError("Hash update error (%d)" % ret) def digest(self): - ret = "\0" * self.digest_size + """ + Returns the hash value of this hashing object as a string + containing 8-bit data. The object is not altered in any + way by this function; you can continue updating the object + after calling this function. + """ + result = t2b("\0" * self.digest_size) if self._native_object: obj = _ffi.new(self._native_type) _ffi.memmove(obj, self._native_object, self._native_size) - self._final(obj, ret) + ret = self._final(obj, result) + if ret < 0: + raise WolfCryptError("Hash finalize error (%d)" % ret) - return ret + return result def hexdigest(self): - return self.digest().encode("hex") + """ + Returns the hash value of this hashing object as a string + containing hexadecimal digits. Lowercase letters are used + for the digits 'a' through 'f'. Like the .digest() method, + this method doesn't alter the object. + """ + return b2h(self.digest()) class Sha(_Hash): + """ + **SHA-1** is a cryptographic hash function standardized by **NIST**. + + It produces an [ **160-bit | 20 bytes** ] message digest. + """ digest_size = 20 _native_type = "Sha *" _native_size = _ffi.sizeof("Sha") def _init(self): - _lib.wc_InitSha(self._native_object) + return _lib.wc_InitSha(self._native_object) def _update(self, data): - _lib.wc_ShaUpdate(self._native_object, data, len(data)) + return _lib.wc_ShaUpdate(self._native_object, data, len(data)) def _final(self, obj, ret): - _lib.wc_ShaFinal(obj, ret) + return _lib.wc_ShaFinal(obj, ret) class Sha256(_Hash): + """ + **SHA-256** is a cryptographic hash function from the + **SHA-2 family** and is standardized by **NIST**. + + It produces a [ **256-bit | 32 bytes** ] message digest. + """ digest_size = 32 _native_type = "Sha256 *" _native_size = _ffi.sizeof("Sha256") def _init(self): - _lib.wc_InitSha256(self._native_object) + return _lib.wc_InitSha256(self._native_object) def _update(self, data): - _lib.wc_Sha256Update(self._native_object, data, len(data)) + return _lib.wc_Sha256Update(self._native_object, data, len(data)) def _final(self, obj, ret): - _lib.wc_Sha256Final(obj, ret) + return _lib.wc_Sha256Final(obj, ret) class Sha384(_Hash): + """ + **SHA-384** is a cryptographic hash function from the + **SHA-2 family** and is standardized by **NIST**. + + It produces a [ **384-bit | 48 bytes** ] message digest. + """ digest_size = 48 _native_type = "Sha384 *" _native_size = _ffi.sizeof("Sha384") def _init(self): - _lib.wc_InitSha384(self._native_object) + return _lib.wc_InitSha384(self._native_object) def _update(self, data): - _lib.wc_Sha384Update(self._native_object, data, len(data)) + return _lib.wc_Sha384Update(self._native_object, data, len(data)) def _final(self, obj, ret): - _lib.wc_Sha384Final(obj, ret) + return _lib.wc_Sha384Final(obj, ret) class Sha512(_Hash): + """ + **SHA-512** is a cryptographic hash function from the + **SHA-2 family** and is standardized by **NIST**. + + It produces a [ **512-bit | 64 bytes** ] message digest. + """ digest_size = 64 _native_type = "Sha512 *" _native_size = _ffi.sizeof("Sha512") def _init(self): - _lib.wc_InitSha512(self._native_object) + return _lib.wc_InitSha512(self._native_object) def _update(self, data): - _lib.wc_Sha512Update(self._native_object, data, len(data)) + return _lib.wc_Sha512Update(self._native_object, data, len(data)) def _final(self, obj, ret): - _lib.wc_Sha512Final(obj, ret) + return _lib.wc_Sha512Final(obj, ret) # Hmac types @@ -159,52 +212,91 @@ _HMAC_TYPES = [_TYPE_SHA, _TYPE_SHA256, _TYPE_SHA384, _TYPE_SHA512] class _Hmac(_Hash): + """ + A **PEP 247: Cryptographic Hash Functions** compliant + **Keyed Hash Function Interface**. + """ digest_size = None _native_type = "Hmac *" _native_size = _ffi.sizeof("Hmac") - @classmethod - def new(cls, key, string=None): - self = cls(cls._JAPANESE_CYBER_SWORD) + def __init__(self, key, string=None): + key = t2b(key) self._native_object = _ffi.new(self._native_type) - - self._init(self._type, key) + ret = self._init(self._type, key) + if ret < 0: + raise WolfCryptError("Hmac init error (%d)" % ret) if (string): - self._update(string) + self.update(string) - return self + + + @classmethod + def new(cls, key, string=None): + """ + Creates a new hashing object and returns it. **key** is + a required parameter containing a string giving the key + to use. The optional **string** parameter, if supplied, + will be immediately hashed into the object's starting + state, as if obj.update(string) was called. + """ + return cls(key, string) def _init(self, type, key): - _lib.wc_HmacSetKey(self._native_object, type, key, len(key)) + return _lib.wc_HmacSetKey(self._native_object, type, key, len(key)) def _update(self, data): - _lib.wc_HmacUpdate(self._native_object, data, len(data)) + return _lib.wc_HmacUpdate(self._native_object, data, len(data)) def _final(self, obj, ret): - _lib.wc_HmacFinal(obj, ret) + return _lib.wc_HmacFinal(obj, ret) class HmacSha(_Hmac): + """ + A HMAC function using **SHA-1** as it's cryptographic + hash function. + + It produces a [ **512-bit | 64 bytes** ] message digest. + """ _type = _TYPE_SHA digest_size = Sha.digest_size class HmacSha256(_Hmac): + """ + A HMAC function using **SHA-256** as it's cryptographic + hash function. + + It produces a [ **512-bit | 64 bytes** ] message digest. + """ _type = _TYPE_SHA256 digest_size = Sha256.digest_size class HmacSha384(_Hmac): + """ + A HMAC function using **SHA-384** as it's cryptographic + hash function. + + It produces a [ **512-bit | 64 bytes** ] message digest. + """ _type = _TYPE_SHA384 digest_size = Sha384.digest_size class HmacSha512(_Hmac): + """ + A HMAC function using **SHA-512** as it's cryptographic + hash function. + + It produces a [ **512-bit | 64 bytes** ] message digest. + """ _type = _TYPE_SHA512 digest_size = Sha512.digest_size diff --git a/wrapper/python/wolfcrypt/random.py b/wrapper/python/wolfcrypt/random.py index 047b75856..f819f51d8 100644 --- a/wrapper/python/wolfcrypt/random.py +++ b/wrapper/python/wolfcrypt/random.py @@ -17,15 +17,24 @@ # 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 -from wolfcrypt._ffi import ffi as _ffi -from wolfcrypt._ffi import lib as _lib +from wolfcrypt._ffi import ffi as _ffi +from wolfcrypt._ffi import lib as _lib +from wolfcrypt.utils import t2b + +from wolfcrypt.exceptions import * class Random(object): + """ + A Cryptographically Secure Pseudo Random Number Generator - CSPRNG + """ def __init__(self): self.native_object = _ffi.new("WC_RNG *") - if _lib.wc_InitRng(self.native_object) != 0: + + ret = _lib.wc_InitRng(self.native_object) + if ret < 0: self.native_object = None + raise WolfCryptError("RNG init error (%d)" % ret) def __del__(self): @@ -34,16 +43,26 @@ class Random(object): def byte(self): - ret = "\0" + """ + Generate and return a random byte. + """ + result = t2b("\0") - _lib.wc_RNG_GenerateByte(self.native_object, ret) + ret = _lib.wc_RNG_GenerateByte(self.native_object, result) + if ret < 0: + raise WolfCryptError("RNG generate byte error (%d)" % ret) - return ret + return result def bytes(self, length): - ret = "\0" * length + """ + Generate and return a random sequence of length bytes. + """ + result = t2b("\0" * length) - _lib.wc_RNG_GenerateBlock(self.native_object, ret, length) + ret = _lib.wc_RNG_GenerateBlock(self.native_object, result, length) + if ret < 0: + raise WolfCryptError("RNG generate block error (%d)" % ret) - return ret + return result diff --git a/wrapper/python/wolfcrypt/utils.py b/wrapper/python/wolfcrypt/utils.py new file mode 100644 index 000000000..34646ff8a --- /dev/null +++ b/wrapper/python/wolfcrypt/utils.py @@ -0,0 +1,38 @@ +# utils.py +# +# Copyright (C) 2006-2016 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 sys +from binascii import hexlify as b2h, unhexlify as h2b + + +if sys.version_info[0] == 3: + _text_type = str + _binary_type = bytes +else: + _text_type = unicode + _binary_type = str + + +def t2b(s): + """ + Converts text to bynary. + """ + if isinstance(s, _binary_type): + return s + return _text_type(s).encode("utf-8")