summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2021-08-16 00:07:32 +0100
committerColin Watson <cjwatson@debian.org>2021-08-16 00:07:32 +0100
commit7ef4f5a87346ec1362c58904e7e0e686ac25da1e (patch)
treeb2a13b970526abbd5048c2c8412c906dc968a260
Import python-libnacl_1.8.0.orig.tar.gz
[dgit import orig python-libnacl_1.8.0.orig.tar.gz]
-rw-r--r--AUTHORS3
-rw-r--r--LICENSE201
-rw-r--r--MANIFEST.in6
-rw-r--r--PKG-INFO25
-rw-r--r--README.rst61
-rw-r--r--doc/Makefile177
-rw-r--r--doc/conf.py331
-rw-r--r--doc/index.rst29
-rw-r--r--doc/topics/dual.rst87
-rw-r--r--doc/topics/public.rst104
-rw-r--r--doc/topics/raw_generichash.rst31
-rw-r--r--doc/topics/raw_hash.rst23
-rw-r--r--doc/topics/raw_public.rst74
-rw-r--r--doc/topics/raw_sealed.rst41
-rw-r--r--doc/topics/raw_secret.rst61
-rw-r--r--doc/topics/raw_sign.rst57
-rw-r--r--doc/topics/releases/1.0.0.rst13
-rw-r--r--doc/topics/releases/1.1.0.rst21
-rw-r--r--doc/topics/releases/1.2.0.rst22
-rw-r--r--doc/topics/releases/1.3.0.rst6
-rw-r--r--doc/topics/releases/1.3.1.rst5
-rw-r--r--doc/topics/releases/1.3.2.rst5
-rw-r--r--doc/topics/releases/1.3.3.rst6
-rw-r--r--doc/topics/releases/1.3.4.rst7
-rw-r--r--doc/topics/releases/1.4.0.rst14
-rw-r--r--doc/topics/releases/1.4.1.rst9
-rw-r--r--doc/topics/releases/1.4.2.rst8
-rw-r--r--doc/topics/releases/1.4.3.rst17
-rw-r--r--doc/topics/releases/1.4.4.rst30
-rw-r--r--doc/topics/releases/1.4.5.rst14
-rw-r--r--doc/topics/releases/1.5.0.rst19
-rw-r--r--doc/topics/releases/1.5.1.rst17
-rw-r--r--doc/topics/releases/1.5.2.rst11
-rw-r--r--doc/topics/releases/1.6.0.rst21
-rw-r--r--doc/topics/releases/1.6.1.rst8
-rw-r--r--doc/topics/releases/1.7.1.rst21
-rw-r--r--doc/topics/releases/1.7.rst18
-rw-r--r--doc/topics/releases/index.rst9
-rw-r--r--doc/topics/sealed.rst38
-rw-r--r--doc/topics/secret.rst34
-rw-r--r--doc/topics/sign.rst43
-rw-r--r--doc/topics/utils.rst50
-rw-r--r--libnacl.egg-info/PKG-INFO25
-rw-r--r--libnacl.egg-info/SOURCES.txt87
-rw-r--r--libnacl.egg-info/dependency_links.txt1
-rw-r--r--libnacl.egg-info/top_level.txt1
-rw-r--r--libnacl/__init__.py1295
-rw-r--r--libnacl/aead.py70
-rw-r--r--libnacl/base.py73
-rw-r--r--libnacl/blake.py44
-rw-r--r--libnacl/dual.py34
-rw-r--r--libnacl/encode.py63
-rw-r--r--libnacl/public.py108
-rw-r--r--libnacl/sealed.py33
-rw-r--r--libnacl/secret.py47
-rw-r--r--libnacl/secret_easy.py47
-rw-r--r--libnacl/sign.py55
-rw-r--r--libnacl/utils.py101
-rw-r--r--libnacl/version.py2
-rw-r--r--pkg/rpm/python-libnacl.spec153
-rw-r--r--pkg/suse/python-libnacl.changes15
-rw-r--r--pkg/suse/python-libnacl.spec59
-rw-r--r--setup.cfg7
-rw-r--r--setup.py36
-rw-r--r--tests/runtests.py26
-rw-r--r--tests/unit/__init__.py1
-rw-r--r--tests/unit/test_aead.py41
-rw-r--r--tests/unit/test_auth_verify.py102
-rw-r--r--tests/unit/test_blake.py39
-rw-r--r--tests/unit/test_dual.py54
-rw-r--r--tests/unit/test_public.py41
-rw-r--r--tests/unit/test_raw_auth_sym.py20
-rw-r--r--tests/unit/test_raw_auth_sym_easy.py20
-rw-r--r--tests/unit/test_raw_generichash.py39
-rw-r--r--tests/unit/test_raw_hash.py29
-rw-r--r--tests/unit/test_raw_public.py91
-rw-r--r--tests/unit/test_raw_random.py104
-rw-r--r--tests/unit/test_raw_secret.py33
-rw-r--r--tests/unit/test_raw_secret_easy.py33
-rw-r--r--tests/unit/test_raw_sign.py38
-rw-r--r--tests/unit/test_save.py88
-rw-r--r--tests/unit/test_seal.py36
-rw-r--r--tests/unit/test_secret.py22
-rw-r--r--tests/unit/test_secret_easy.py22
-rw-r--r--tests/unit/test_sign.py93
-rw-r--r--tests/unit/test_stream.py43
-rw-r--r--tests/unit/test_verify.py76
-rw-r--r--tests/unit/test_version.py22
88 files changed, 5246 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..c43beb7
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,3 @@
+Thomas S Hatch
+Sam Smith
+Pedro Algarvio
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dd3877
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {2014} Thomas S Hatch
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..c5c3bf6
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+include LICENSE
+include AUTHORS
+include README.rst
+recursive-include tests *.py
+recursive-include doc *
+recursive-include pkg *
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..5b4fd06
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,25 @@
+Metadata-Version: 2.1
+Name: libnacl
+Version: 1.8.0
+Summary: Python bindings for libsodium based on ctypes
+Home-page: https://libnacl.readthedocs.org/
+Author: Thomas S Hatch
+Author-email: thatch@saltstack.com
+License: UNKNOWN
+Platform: UNKNOWN
+Classifier: Operating System :: OS Independent
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Topic :: Security :: Cryptography
+License-File: LICENSE
+License-File: AUTHORS
+
+UNKNOWN
+
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..bbf32de
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,61 @@
+==============
+Python libnacl
+==============
+
+This library is used to gain direct access to the functions exposed by
+Daniel J. Bernstein's nacl library via libsodium. It has
+been constructed to maintain extensive documentation on how to use nacl
+as well as being completely portable. The file in libnacl/__init__.py
+can be pulled out and placed directly in any project to give a single file
+binding to all of nacl.
+
+Higher Level Classes
+====================
+
+The libnacl code also ships with many high level classes which make nacl
+cryptography easy and safe, for documentation please see:
+http://libnacl.readthedocs.org/
+
+Why libnacl
+===========
+
+There are a number of libraries out there binding to libsodium, so why make
+libnacl?
+
+1. libnacl does not have any non-python hard deps outside of libsodium
+2. libnacl does not need to be compiled
+3. libnacl is easy to package and very portable
+4. Inclusion of high level pythonic encryption classes
+5. Ability to have a single embeddable and transferable bindings file
+ that can be added directly to python applications without needing
+ to dep libnacl
+
+This makes libnacl very portable, very easy to use and easy to distribute.
+
+Install
+=======
+
+The libnacl code is easiy installed via a setup.py from the source or via pip.
+
+From Source:
+
+.. code-block:: bash
+
+ tar xvf libnacl-1.5.2.tar.gz
+ cd libnacl-1.5.2
+ python setup.py install
+
+Via Pip:
+
+.. code-block:: bash
+
+ pip install libnacl
+
+Remember that libnacl can be installed for python 2 and 3.
+
+Linux distributions
+-------------------
+
+Libnacl is shiped with many linux distributions, check your distribution
+package manager for the package ``python-libnacl``, ``python2-libnacl``
+and/or ``python3-libnacl``.
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..ab5262e
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,177 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Python-NaCl.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Python-NaCl.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Python-NaCl"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Python-NaCl"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..26dd69f
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,331 @@
+# -*- coding: utf-8 -*-
+#
+# libnacl documentation build configuration file, created by
+# sphinx-quickstart on Thu May 29 10:29:25 2014.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+sys.path.insert(0, os.path.abspath('..'))
+from libnacl import version
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'libnacl'
+copyright = u'2020, Thomas S Hatch'
+
+# 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.
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+#htmlhelp_basename = 'libnacl'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'libnacl.tex', u'libnacl Documentation',
+ u'Thomas S Hatch', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'libnacl', u'libnacl Documentation',
+ [u'Thomas S Hatch'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'libnacl', u'libnacl Documentation',
+ u'Thomas S Hatch', 'libnacl', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
+
+
+# -- Options for Epub output ----------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = u'libnacl'
+epub_author = u'Thomas S Hatch'
+epub_publisher = u'Thomas S Hatch'
+epub_copyright = u'2020, Thomas S Hatch'
+
+# The basename for the epub file. It defaults to the project name.
+#epub_basename = u'libnacl'
+
+# The HTML theme for the epub output. Since the default themes are not optimized
+# for small screen space, using the same theme for HTML and epub output is
+# usually not wise. This defaults to 'epub', a theme designed to save visual
+# space.
+#epub_theme = 'epub'
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+#epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+#epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#epub_identifier = ''
+
+# A unique identification for the text.
+#epub_uid = ''
+
+# A tuple containing the cover image and cover page html template filenames.
+#epub_cover = ()
+
+# A sequence of (type, uri, title) tuples for the guide element of content.opf.
+#epub_guide = ()
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_pre_files = []
+
+# HTML files shat should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+#epub_exclude_files = []
+
+# The depth of the table of contents in toc.ncx.
+#epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#epub_tocdup = True
+
+# Choose between 'default' and 'includehidden'.
+#epub_tocscope = 'default'
+
+# Fix unsupported image types using the PIL.
+#epub_fix_images = False
+
+# Scale large images.
+#epub_max_image_width = 0
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#epub_show_urls = 'inline'
+
+# If false, no index is generated.
+#epub_use_index = True
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..4638f71
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,29 @@
+libnacl: Python bindings to NaCl
+================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ topics/public
+ topics/sealed
+ topics/secret
+ topics/sign
+ topics/dual
+ topics/utils
+ topics/raw_public
+ topics/raw_sealed
+ topics/raw_secret
+ topics/raw_sign
+ topics/raw_hash
+ topics/raw_generichash
+ topics/releases/index
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/doc/topics/dual.rst b/doc/topics/dual.rst
new file mode 100644
index 0000000..59cd3dd
--- /dev/null
+++ b/doc/topics/dual.rst
@@ -0,0 +1,87 @@
+===================
+Dual Key Management
+===================
+
+The libnacl library abstracts a "Dual Key" model. The Dual Key creates a single
+key management object that can be used for both signing and encrypting, it
+generates and maintains a Curve25519 encryption key pair and an ED25519 signing
+keypair. All methods for encryption and signing work with and from Dual Keys.
+
+To encrypt messages using Dual Keys:
+
+.. code-block:: python
+
+ import libnacl.dual
+
+ # Define a message to send
+ msg = b"You've got two empty halves of coconut and you're bangin' 'em together."
+
+ # Generate the key pairs for Alice and bob, if secret keys already exist
+ # they can be passed in, otherwise new keys will be automatically generated
+ bob = libnacl.dual.DualSecret()
+ alice = libnacl.dual.DualSecret()
+
+ # Create the boxes, this is an object which represents the combination of the
+ # sender's secret key and the receiver's public key
+ bob_box = libnacl.public.Box(bob.sk, alice.pk)
+ alice_box = libnacl.public.Box(alice.sk, bob.pk)
+
+ # Bob's box encrypts messages for Alice
+ bob_ctxt = bob_box.encrypt(msg)
+ # Alice's box decrypts messages from Bob
+ bclear = alice_box.decrypt(bob_ctxt)
+ # Alice can send encrypted messages which only Bob can decrypt
+ alice_ctxt = alice_box.encrypt(msg)
+ aclear = alice_box.decrypt(alice_ctxt)
+
+.. note::
+
+ Every encryption routine requires a nonce. The nonce is a 24 char string
+ that must never be used twice with the same keypair. If no nonce is passed
+ in then a nonce is generated based on random data.
+ If it is desired to generate a nonce manually this can be done by passing
+ it into the encrypt method.
+
+DualKey Object
+==============
+
+The DualKey object is used to manage both public and secret keys, this object
+contains a number of methods for both convenience and utility. The key data is
+also available.
+
+Keys
+----
+
+The raw public key is available as DualKey.pk, to generate a hex encoded
+version of the key the pk_hex method is available:
+
+.. code-block:: python
+
+ import libnacl.dual
+
+ fred = libnacl.dual.DualSecret()
+
+ raw_sk = fred.sk
+ hex_sk = fred.hex_sk()
+
+ raw_pk = fred.pk
+ hex_pk = fred.hex_pk()
+
+By saving only the binary keys in memory libnacl ensures that the minimal
+memory footprint is needed.
+
+Saving Keys to Disk
+===================
+
+All libnacl key objects can be safely saved to disk via the save method. This
+method changes the umask before saving the key file to ensure that the saved
+file can only be read by the user creating it and cannot be written to.
+When using dual keys the encrypting and signing keys will be saved togather in
+a single file.
+
+.. code-block:: python
+
+ import libnacl.dual
+
+ fred = libnacl.dual.DualSecret()
+ fred.save('/etc/nacl/fred.key')
diff --git a/doc/topics/public.rst b/doc/topics/public.rst
new file mode 100644
index 0000000..eb2f1bb
--- /dev/null
+++ b/doc/topics/public.rst
@@ -0,0 +1,104 @@
+=====================
+Public Key Encryption
+=====================
+
+Unlike traditional means for public key asymmetric encryption, the nacl
+encryption systems are very high speed. The CurveCP network protocol for
+instance only uses public key encryption for all transport.
+
+Public key encryption is very simple, as is evidenced with this communication
+between Alice and Bob:
+
+.. code-block:: python
+
+ import libnacl.public
+
+ # Define a message to send
+ msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.'
+
+ # Generate the key pairs for Alice and bob, if secret keys already exist
+ # they can be passed in, otherwise new keys will be automatically generated
+ bob = libnacl.public.SecretKey()
+ alice = libnacl.public.SecretKey()
+
+ # Create the boxes, this is an object which represents the combination of the
+ # sender's secret key and the receiver's public key
+ bob_box = libnacl.public.Box(bob.sk, alice.pk)
+ alice_box = libnacl.public.Box(alice.sk, bob.pk)
+
+ # Bob's box encrypts messages for Alice
+ bob_ctxt = bob_box.encrypt(msg)
+ # Alice's box decrypts messages from Bob
+ bclear = alice_box.decrypt(bob_ctxt)
+ # Alice can send encrypted messages which only Bob can decrypt
+ alice_ctxt = alice_box.encrypt(msg)
+ aclear = bob_box.decrypt(alice_ctxt)
+
+.. note::
+
+ Every encryption routine requires a nonce. The nonce is a 24 char string
+ that must never be used twice with the same keypair. If no nonce is passed
+ in then a nonce is generated based on random data.
+ If it is desired to generate a nonce manually this can be done by passing
+ it into the encrypt method.
+
+.. _secretkey-object:
+
+SecretKey Object
+================
+
+The SecretKey object is used to manage both public and secret keys, this object
+contains a number of methods for both convenience and utility. The key data is
+also available.
+
+Keys
+----
+
+The raw public key is available as SecretKey.sk, to generate a hex encoded
+version of the key the sk_hex method is available. The same items are
+available for the public keys:
+
+.. code-block:: python
+
+ import libnacl.public
+
+ fred = libnacl.public.SecretKey()
+
+ raw_sk = fred.sk
+ hex_sk = fred.hex_sk()
+
+ raw_pk = fred.pk
+ hex_pk = fred.hex_pk()
+
+By saving only the binary keys in memory libnacl ensures that the minimal
+memory footprint is needed.
+
+.. _publickey-object:
+
+PublicKey Object
+================
+
+To manage only the public key end, a public key object exists:
+
+.. code-block:: python
+
+ import libnacl.public
+
+ tom = libnacl.public.PublicKey(tom_public_key_hex)
+
+ raw_pk = tom.pk
+ hex_pk = tom.hex_pk()
+
+Saving Keys to Disk
+===================
+
+All libnacl key objects can be safely saved to disk via the save method. This
+method changes the umask before saving the key file to ensure that the saved
+file can only be read by the user creating it and cannot be written to.
+
+.. code-block:: python
+
+ import libnacl.public
+
+ fred = libnacl.public.SecretKey()
+ fred.save('/etc/nacl/fred.key')
diff --git a/doc/topics/raw_generichash.rst b/doc/topics/raw_generichash.rst
new file mode 100644
index 0000000..ba42c6e
--- /dev/null
+++ b/doc/topics/raw_generichash.rst
@@ -0,0 +1,31 @@
+====================================
+Raw Generic Hash (Blake2b) Functions
+====================================
+
+The nacl library comes with blake hashing libraries.
+
+More information on Blake can be found here:
+https://blake2.net
+
+The blake2b hashing algorithm is a keyed hashing algorithm, which allows
+for a key to be associated with a hash. Blake can be executed with or without
+a key.
+
+With a key (they key can should be between 16 and 64 bytes):
+
+.. code-block:: python
+
+ import libnacl
+
+ msg = 'Is there someone else up there we could talk to?'
+ key = libnacl.randombytes(32)
+ h_msg = libnacl.crypto_generichash(msg, key)
+
+Without a key:
+
+.. code-block:: python
+
+ import libnacl
+
+ msg = 'Is there someone else up there we could talk to?'
+ h_msg = libnacl.crypto_generichash(msg)
diff --git a/doc/topics/raw_hash.rst b/doc/topics/raw_hash.rst
new file mode 100644
index 0000000..e161098
--- /dev/null
+++ b/doc/topics/raw_hash.rst
@@ -0,0 +1,23 @@
+==================
+Raw Hash Functions
+==================
+
+The nacl library comes with sha256 and sha512 hashing libraries. They do not
+seem to offer any benefit over python's hashlib, but for completeness they are
+included. Creating a hash of a message is very simple:
+
+.. code-block:: python
+
+ import libnacl
+
+ msg = 'Is there someone else up there we could talk to?'
+ h_msg = libnacl.crypto_hash(msg)
+
+crypto_hash defaults to sha256, sha512 is also available:
+
+.. code-block:: python
+
+ import libnacl
+
+ msg = 'Is there someone else up there we could talk to?'
+ h_msg = libnacl.crypto_hash_sha512(msg)
diff --git a/doc/topics/raw_public.rst b/doc/topics/raw_public.rst
new file mode 100644
index 0000000..9464dc1
--- /dev/null
+++ b/doc/topics/raw_public.rst
@@ -0,0 +1,74 @@
+=========================
+Raw Public Key Encryption
+=========================
+
+.. note::
+
+ While these routines are perfectly safe, higher level convenience
+ wrappers are under development to make these routines easier.
+
+Public key encryption inside the nacl library has been constructed to ensure
+that all cryptographic routines are executed correctly and safely.
+
+The public key encryption is executed via the functions which begin with
+`crypto_box` and can be easily executed.
+
+First generate a public key and secret key keypair for the two communicating
+parties, who for tradition's sake, will be referred to as Alice and Bob:
+
+.. code-block:: python
+
+ import libnacl
+
+ alice_pk, alice_sk = libnacl.crypto_box_keypair()
+ bob_pk, bob_sk = libnacl.crypto_box_keypair()
+
+Once the keys have been generated a cryptographic box needs to be created. The
+cryptographic box takes the party's secret key and the receiving party's public
+key. These are used to create a message which is both signed and encrypted.
+
+Before creating the box a nonce is required. The nonce is a 24 character
+string which should only be used for this message, the nonce should never be
+reused. This means that the nonce needs to be generated in such a way that
+the probability of reusing the nonce string with the same keypair is very
+low. The libnacl wrapper ships with a convenience function which generates a
+nonce from random bytes:
+
+.. code-block:: python
+
+ import libnacl.utils
+ nonce = libnacl.utils.rand_nonce()
+
+Now, with a nonce a cryptographic box can be created, Alice will send a
+message:
+
+.. code-block:: python
+
+ msg = 'Quiet, quiet. Quiet! There are ways of telling whether she is a witch.'
+ box = libnacl.crypto_box(msg, nonce, bob_pk, alice_sk)
+
+Now with a box in hand it can be decrypted by Bob:
+
+.. code-block:: python
+
+ clear_msg = libnacl.crypto_box_open(box, nonce, alice_pk, bob_sk)
+
+The trick here is that the box AND the nonce need to be sent to Bob, so he can
+decrypt the message. The nonce can be safely sent to Bob in the clear.
+
+To bring it all together:
+
+.. code-block:: python
+
+ import libnacl
+ import libnacl.utils
+
+ alice_pk, alice_sk = libnacl.crypto_box_keypair()
+ bob_pk, bob_sk = libnacl.crypto_box_keypair()
+
+ nonce = libnacl.utils.rand_nonce()
+
+ msg = 'Quiet, quiet. Quiet! There are ways of telling whether she is a witch.'
+ box = libnacl.crypto_box(msg, nonce, bob_pk, alice_sk)
+
+ clear_msg = libnacl.crypto_box_open(box, nonce, alice_pk, bob_sk)
diff --git a/doc/topics/raw_sealed.rst b/doc/topics/raw_sealed.rst
new file mode 100644
index 0000000..b2c890e
--- /dev/null
+++ b/doc/topics/raw_sealed.rst
@@ -0,0 +1,41 @@
+=========================
+Raw Sealed Box Encryption
+=========================
+
+Sealed box is a variant of :doc:`public key encryption scheme </raw_public.rst>`
+where the sender is not authenticated. This is done by generating an
+ephemeral key pair, which the public key is prefixed to the cipher text.
+
+First, generate a keypair for the receiver. The sender doesn't need a keypair.
+
+.. code-block:: python
+
+ import libnacl
+
+ pk, sk = libnacl.crypto_box_keypair()
+
+Then a sealed box is created by the sender, using the receiver's public key
+
+.. code-block:: python
+
+ msg = 'Quiet, quiet. Quiet! There are ways of telling whether she is a witch.'
+ box = libnacl.crypto_box_seal(msg, pk)
+
+The receiver then can decrypt the box using their keypair.
+
+.. code-block:: python
+
+ clear_msg = libnacl.crypto_box_seal_open(box, pk, sk)
+
+To bring it all together:
+
+.. code-block:: python
+
+ import libnacl
+
+ pk, sk = libnacl.crypto_box_keypair()
+
+ msg = 'Quiet, quiet. Quiet! There are ways of telling whether she is a witch.'
+ box = libnacl.crypto_box_seal(msg, pk)
+
+ clear_msg = libnacl.crypto_box_seal_open(box, pk, sk)
diff --git a/doc/topics/raw_secret.rst b/doc/topics/raw_secret.rst
new file mode 100644
index 0000000..40f7ae8
--- /dev/null
+++ b/doc/topics/raw_secret.rst
@@ -0,0 +1,61 @@
+=========================
+Raw Secret Key Encryption
+=========================
+
+.. note::
+
+ While these routines are perfectly safe, higher level convenience
+ wrappers are under development to make these routines easier.
+
+Secret key encryption is high speed encryption based on a shared secret key.
+
+.. note::
+
+ The nacl library uses the salsa20 stream encryption cipher for secret key
+ encryption, more information about the salsa20 cipher can be found here:
+ http://cr.yp.to/salsa20.html
+
+The means of encryption assumes that the two sides of the conversation both
+have access to the same shared secret key. First generate a secret key, libnacl
+provides a convenience function for the generation of this key called
+libnacl.utils.salsa_key, then generate a nonce, a new nonce should be used
+every time a new message is encrypted. A convenience function to create a unique
+nonce based on random bytes:
+
+.. code-block:: python
+
+ import libnacl
+ import libnacl.utils
+
+ key = libnacl.utils.salsa_key()
+ nonce = libnacl.utils.rand_nonce()
+
+With the key and nonce in hand, the cryptographic secret box can now be
+generated:
+
+.. code-block:: python
+
+ msg = 'Who are you who are so wise in the ways of science?'
+ box = libnacl.crypto_secretbox(msg, nonce, key)
+
+Now the message can be decrypted on the other end. The nonce and the key are
+both required to decrypt:
+
+.. code-block:: python
+
+ clear_msg = libnacl.crypto_secretbox_open(box, nonce, key)
+
+When placed all together the sequence looks like this:
+
+.. code-block:: python
+
+ import libnacl
+ import libnacl.utils
+
+ key = libnacl.utils.salsa_key()
+ nonce = libnacl.utils.rand_nonce()
+
+ msg = 'Who are you who are so wise in the ways of science?'
+ box = libnacl.crypto_secretbox(msg, nonce, key)
+
+ clear_msg = libnacl.crypto_secretbox_open(box, nonce, key)
diff --git a/doc/topics/raw_sign.rst b/doc/topics/raw_sign.rst
new file mode 100644
index 0000000..732b74b
--- /dev/null
+++ b/doc/topics/raw_sign.rst
@@ -0,0 +1,57 @@
+======================
+Raw Message Signatures
+======================
+
+.. note::
+
+ While these routines are perfectly safe, higher level convenience
+ wrappers are under development to make these routines easier.
+
+Signing messages ensures that the message itself has not been tampered with.
+The application of a signature to a message is something that is is
+automatically applied when using the public key encryption and is not a
+required step when sending encrypted messages. This document however is
+intended to illustrate how to sign plain text messages.
+
+The nacl libs use a separate keypair for signing then is used for
+public key encryption, it is a high performance key signing algorithm
+called ed25519, more information on ed25519 can be found here:
+http://ed25519.cr.yp.to/
+
+The sign messages first generate a signing keypair, this constitutes the
+signing key which needs to be kept secret, and the verify key which is
+made available to message recipients.
+
+.. code-block:: python
+
+ import libnacl
+
+ vk, sk = libnacl.crypto_sign_keypair()
+
+With the signing keypair in hand a message can be signed:
+
+.. code-block:: python
+
+ msg = 'And that, my liege, is how we know the Earth to be banana-shaped.'
+ signed = libnacl.crypto_sign(msg, sk)
+
+The signed message is really just the plain text of the message prepended with
+the signature. The crypto_sign_open function will read the signed message
+and return the original message without the signature:
+
+.. code-block:: python
+
+ orig = libnacl.crypto_sign_open(signed, vk)
+
+Put all together:
+
+.. code-block:: python
+
+ import libnacl
+
+ vk, sk = libnacl.crypto_sign_keypair()
+
+ msg = 'And that, my liege, is how we know the Earth to be banana-shaped.'
+ signed = libnacl.crypto_sign(msg, sk)
+
+ orig = libnacl.crypto_sign_open(signed, vk)
diff --git a/doc/topics/releases/1.0.0.rst b/doc/topics/releases/1.0.0.rst
new file mode 100644
index 0000000..78b9013
--- /dev/null
+++ b/doc/topics/releases/1.0.0.rst
@@ -0,0 +1,13 @@
+===========================
+libnacl 1.0.0 Release Notes
+===========================
+
+This is the first stable release of libnacl, the python bindings for Daniel J.
+Bernstein's nacl library via libsodium.
+
+NaCl Base Functions
+===================
+
+This release features direct access to the underlying functions from nacl
+exposed via importing libnacl. These functions are fully documented and can
+be safely used directly.
diff --git a/doc/topics/releases/1.1.0.rst b/doc/topics/releases/1.1.0.rst
new file mode 100644
index 0000000..507a2b9
--- /dev/null
+++ b/doc/topics/releases/1.1.0.rst
@@ -0,0 +1,21 @@
+===========================
+libnacl 1.1.0 Release Notes
+===========================
+
+This release introduces the addition of high level classes that make using
+NaCl even easier.
+
+High level NaCl
+===============
+
+The addition of the high level classes give a more pythonic abstraction to
+using the underlying NaCl cryptography.
+
+These classes can be found in libnacl.public, libnacl.sign and libnacl.secret.
+
+Easy Nonce Generation
+=====================
+
+The new classes will automatically generate a nonce value per encrypted
+message. The default nonce which is generated can be found in
+`libnacl.utils.time_nonce`.
diff --git a/doc/topics/releases/1.2.0.rst b/doc/topics/releases/1.2.0.rst
new file mode 100644
index 0000000..d868dee
--- /dev/null
+++ b/doc/topics/releases/1.2.0.rst
@@ -0,0 +1,22 @@
+===========================
+libnacl 1.2.0 Release Notes
+===========================
+
+This release introduces the DualKey class, secure key saving and loading, as
+well as enhancements to the time_nonce function.
+
+Dual Key Class
+==============
+
+Dual Keys are classes which can encrypt and sign data. These classes generate
+and maintain both Curve25519 and Ed25519 keys, as well as all methods for both
+encryption and signing.
+
+Time Nonce Improvements
+=======================
+
+The original time nonce routine used the first 20 chars of the 24 char nonce
+for the microsecond timestamp (based on salt's jid), leaving 4 chars for random
+data. This new nonce uses far fewer chars for the timestamp by hex encoding the
+float of microseconds into just 13 chars, leaving 11 chars of random data. This
+makes the default nonce safer and more secure.
diff --git a/doc/topics/releases/1.3.0.rst b/doc/topics/releases/1.3.0.rst
new file mode 100644
index 0000000..e20f181
--- /dev/null
+++ b/doc/topics/releases/1.3.0.rst
@@ -0,0 +1,6 @@
+===========================
+libnacl 1.3.0 Release Notes
+===========================
+
+This release removes the time_nonce function and replaces it with the
+rand_nonce function.
diff --git a/doc/topics/releases/1.3.1.rst b/doc/topics/releases/1.3.1.rst
new file mode 100644
index 0000000..1c131ea
--- /dev/null
+++ b/doc/topics/releases/1.3.1.rst
@@ -0,0 +1,5 @@
+===========================
+libnacl 1.3.1 Release Notes
+===========================
+
+Bring back a safe time_nonce function.
diff --git a/doc/topics/releases/1.3.2.rst b/doc/topics/releases/1.3.2.rst
new file mode 100644
index 0000000..a15f087
--- /dev/null
+++ b/doc/topics/releases/1.3.2.rst
@@ -0,0 +1,5 @@
+===========================
+libnacl 1.3.2 Release Notes
+===========================
+
+Add detection of the libsodium.so.10 lib created by libsodium 0.6
diff --git a/doc/topics/releases/1.3.3.rst b/doc/topics/releases/1.3.3.rst
new file mode 100644
index 0000000..80a38c6
--- /dev/null
+++ b/doc/topics/releases/1.3.3.rst
@@ -0,0 +1,6 @@
+===========================
+libnacl 1.3.3 Release Notes
+===========================
+
+Fix issue and add tests for bug where saving and loading a signing key caused
+a stack trace, se issue #18
diff --git a/doc/topics/releases/1.3.4.rst b/doc/topics/releases/1.3.4.rst
new file mode 100644
index 0000000..b51b932
--- /dev/null
+++ b/doc/topics/releases/1.3.4.rst
@@ -0,0 +1,7 @@
+===========================
+libnacl 1.3.4 Release Notes
+===========================
+
+* Change the default ctype values to be more accurate and efficient
+* Update soname detection on Linux for libsodium 0.7.0
+* Make soname detection a little more future proof
diff --git a/doc/topics/releases/1.4.0.rst b/doc/topics/releases/1.4.0.rst
new file mode 100644
index 0000000..64ac4a2
--- /dev/null
+++ b/doc/topics/releases/1.4.0.rst
@@ -0,0 +1,14 @@
+===========================
+libnacl 1.4.0 Release Notes
+===========================
+
+Blake Hash Support
+==================
+
+Initial support has been added for the blake2b hash algorithm
+
+Misc Fixes
+==========
+
+* Fix issue with keyfile saves on windows
+* Fix libsodium detection for Ubuntu manual installs and Windows dll detection
diff --git a/doc/topics/releases/1.4.1.rst b/doc/topics/releases/1.4.1.rst
new file mode 100644
index 0000000..dd7c8c3
--- /dev/null
+++ b/doc/topics/releases/1.4.1.rst
@@ -0,0 +1,9 @@
+===========================
+libnacl 1.4.1 Release Notes
+===========================
+
+Misc Fixes
+==========
+
+* Fix for crypto_auth_verify and crypto_auth_onetimeverify
+* Lint fixes and updates
diff --git a/doc/topics/releases/1.4.2.rst b/doc/topics/releases/1.4.2.rst
new file mode 100644
index 0000000..31d6856
--- /dev/null
+++ b/doc/topics/releases/1.4.2.rst
@@ -0,0 +1,8 @@
+===========================
+libnacl 1.4.2 Release Notes
+===========================
+
+SecretBox key save and load
+===========================
+
+* Add support to save and load SecretBox keys
diff --git a/doc/topics/releases/1.4.3.rst b/doc/topics/releases/1.4.3.rst
new file mode 100644
index 0000000..72b47c6
--- /dev/null
+++ b/doc/topics/releases/1.4.3.rst
@@ -0,0 +1,17 @@
+===========================
+libnacl 1.4.3 Release Notes
+===========================
+
+crypto_onetimeauth_verify fixes
+===============================
+
+* Fix a call to the crypto_onetimeauth_verify routine into the right libsodium system
+* Add tests for crypto_onetimeauth_verify
+
+Improved support for MacOSX
+===========================
+
+* Improved the lookup procedure for finding libsodium on MacOSX
+
+Add support for reading file streams for key loading
+====================================================
diff --git a/doc/topics/releases/1.4.4.rst b/doc/topics/releases/1.4.4.rst
new file mode 100644
index 0000000..5917d5a
--- /dev/null
+++ b/doc/topics/releases/1.4.4.rst
@@ -0,0 +1,30 @@
+===========================
+libnacl 1.4.4 Release Notes
+===========================
+
+Add pack_nonce options to secretbox
+===================================
+
+* libnacl secretbox has been packing the nonce in each message, the new pack_nonce
+ option allows for the nonce to be omitted which allows for more flexible options
+
+Add soversion 17 detection
+==========================
+
+* Added explicit soversion support for libsodium 17
+
+Fix crypto_onetimeauth tests
+============================
+
+* The crypto onetimeauth test issues have been resolved
+
+Remove tweetnacl Support
+========================
+
+* The tweetnacl support was never really tested, and since the tweetnacl api
+ is not complete we have removed support for it
+
+Add sodium_init calls
+=====================
+
+* Added calls to sodium_init when the lib is loaded
diff --git a/doc/topics/releases/1.4.5.rst b/doc/topics/releases/1.4.5.rst
new file mode 100644
index 0000000..2ad540f
--- /dev/null
+++ b/doc/topics/releases/1.4.5.rst
@@ -0,0 +1,14 @@
+===========================
+libnacl 1.4.5 Release Notes
+===========================
+
+Set low end libsodium version to 0.5
+====================================
+
+* libnacl will only function with libsodium 0.5 and above
+
+Add soversion 18 detection
+==========================
+
+* Added explicit soversion support for libsodium 18
+
diff --git a/doc/topics/releases/1.5.0.rst b/doc/topics/releases/1.5.0.rst
new file mode 100644
index 0000000..301837a
--- /dev/null
+++ b/doc/topics/releases/1.5.0.rst
@@ -0,0 +1,19 @@
+===========================
+libnacl 1.5.0 Release Notes
+===========================
+
+Add Built In libsodium.so Support
+=================================
+
+Added the ability to place a `libsodium.so` file in the libnacl
+python directory as a last resort fallback. To use this feature
+just copy your `libsodium.so` file to the same directory as the libnacl
+`__init__.py` file.
+
+This was added to make total portability of the library easier.
+
+Add `bytes_eq`
+==============
+
+Added the `bytes_eq` function to allow better byte comparison
+
diff --git a/doc/topics/releases/1.5.1.rst b/doc/topics/releases/1.5.1.rst
new file mode 100644
index 0000000..454fbe3
--- /dev/null
+++ b/doc/topics/releases/1.5.1.rst
@@ -0,0 +1,17 @@
+===========================
+libnacl 1.5.1 Release Notes
+===========================
+
+Add Sealed Box Support
+======================
+
+A big thanks to Manatsawin Hanmongkolchai for adding in support for libsodium
+`Sealed Boxes`!
+
+Change Exception on Encrypt/Decrypt Failure
+===========================================
+
+If encryption or decryption fails, `CryptError` is raised instead of
+`ValueError`. This might be a breaking change for your application. See
+`#91 <https://github.com/saltstack/libnacl/issues/91>`__ and
+`#74 <https://github.com/saltstack/libnacl/issues/74>`__.
diff --git a/doc/topics/releases/1.5.2.rst b/doc/topics/releases/1.5.2.rst
new file mode 100644
index 0000000..6e2c142
--- /dev/null
+++ b/doc/topics/releases/1.5.2.rst
@@ -0,0 +1,11 @@
+===========================
+libnacl 1.5.2 Release Notes
+===========================
+
+Add Support for AEAD AES and chacha20poly1305
+=============================================
+
+Big thanks to Nicholas O'Brien for adding support for libsodium's AEAD
+encryption systems. The raw functions are all available to access
+libsodium directly along with the high level AEAD class that cleanly
+follows libnacl's key management model.
diff --git a/doc/topics/releases/1.6.0.rst b/doc/topics/releases/1.6.0.rst
new file mode 100644
index 0000000..e6f35cd
--- /dev/null
+++ b/doc/topics/releases/1.6.0.rst
@@ -0,0 +1,21 @@
+===========================
+libnacl 1.6.0 Release Notes
+===========================
+
+Add Bindings for More Libsodium Function
+========================================
+
+Add bindings for crypto_box_seed_keypair() and crypto_scalarmult_base()
+Add bindings for crypto_box_easy() and crypto_box_open_easy()
+Add bindings for crypto_box_easy_afternm() and crypto_box_open_easy_afternm()
+Add bindings for crypto_sign_ed25519_keypair(), crypto_sign_ed25519_sk_to_pk() and crypto_sign_ed25519_sk_to_seed()
+Add bindings for crypto_sign_detached() and crypto_sign_verify_detached()
+Add bindings for crypto_sign_ed25519_pk_to_curve25519() and crypto_sign_ed25519_sk_to_curve25519()
+
+Please Note The Exception Change From the 1.5.1 Release
+=======================================================
+
+If encryption or decryption fails, `CryptError` is raised instead of
+`ValueError`. This might be a breaking change for your application. See
+`#91 <https://github.com/saltstack/libnacl/issues/91>`__ and
+`#74 <https://github.com/saltstack/libnacl/issues/74>`__.
diff --git a/doc/topics/releases/1.6.1.rst b/doc/topics/releases/1.6.1.rst
new file mode 100644
index 0000000..9ae5a43
--- /dev/null
+++ b/doc/topics/releases/1.6.1.rst
@@ -0,0 +1,8 @@
+===========================
+libnacl 1.6.1 Release Notes
+===========================
+
+Add support for libsodium 1.0.15
+================================
+
+Make sure that libnacl runs correctly on the 1.0.15 release of libsodium
diff --git a/doc/topics/releases/1.7.1.rst b/doc/topics/releases/1.7.1.rst
new file mode 100644
index 0000000..2941198
--- /dev/null
+++ b/doc/topics/releases/1.7.1.rst
@@ -0,0 +1,21 @@
+===========================
+libnacl 1.7.1 Release Notes
+===========================
+
+This release fixes a few minor bugs, primarily in tests, and restores
+functionality with older versions of libsodium.
+
+Compatibility With Older libsodium
+==================================
+
+PR #118 fixes compatibility with Debian 8.
+
+Test Fixes
+==========
+
+Some unreliability in tests were found by the Debian team. These
+issues were fixed in PRs #115 and #116.
+
+Travis no longer supports the same pypy tests on all platforms,
+these were removed in PR #119.
+
diff --git a/doc/topics/releases/1.7.rst b/doc/topics/releases/1.7.rst
new file mode 100644
index 0000000..5ee5549
--- /dev/null
+++ b/doc/topics/releases/1.7.rst
@@ -0,0 +1,18 @@
+=========================
+libnacl 1.7 Release Notes
+=========================
+
+Bindings for kdf in libsodium
+=============================
+
+Thanks to Michael Mendoza, PR #109
+
+Added extra key validation
+==========================
+
+Thanks to Kent Ross, PR #106
+
+Add Crypto_box_easy
+===================
+
+Thanks to jheling PR #114
diff --git a/doc/topics/releases/index.rst b/doc/topics/releases/index.rst
new file mode 100644
index 0000000..d664a47
--- /dev/null
+++ b/doc/topics/releases/index.rst
@@ -0,0 +1,9 @@
+=============
+Release notes
+=============
+
+.. toctree::
+ :maxdepth: 1
+ :glob:
+
+ [0-9]*
diff --git a/doc/topics/sealed.rst b/doc/topics/sealed.rst
new file mode 100644
index 0000000..23d28c6
--- /dev/null
+++ b/doc/topics/sealed.rst
@@ -0,0 +1,38 @@
+=====================
+Sealed Box
+=====================
+
+Sealed box is a variant of :doc:`public key encryption scheme </topics/public>`
+which only the receiver's public key is required. As such, the sender of the
+message cannot be cryptographically authenticated.
+
+.. code-block:: python
+
+ import libnacl.sealed
+ import libnacl.public
+
+ # Define a message to send
+ msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.'
+
+ # Generate the key pair
+ keypair = libnacl.public.SecretKey()
+
+ # Create the box
+ box = libnacl.sealed.SealedBox(keypair)
+
+ # Encrypt messages
+ ctxt = box.encrypt(msg)
+ # Decrypt messages
+ bclear = box.decrypt(ctxt)
+
+Creating Box
+======================
+SealedBox instances can be created by supplying a public and private key. The
+private key is only required when decrypting.
+
+The public key can be supplied as:
+
+* Instance of :ref:`SecretKey <secretkey-object>`, which supply both the public
+ and private key.
+* Instance of :ref:`PublicKey <publickey-object>`
+* Raw binary representation
diff --git a/doc/topics/secret.rst b/doc/topics/secret.rst
new file mode 100644
index 0000000..3690704
--- /dev/null
+++ b/doc/topics/secret.rst
@@ -0,0 +1,34 @@
+=====================
+Secret Key Encryption
+=====================
+
+Secret key encryption is the method of using a single key for both encryption
+and decryption of messages. One of the classic examples from history of secret
+key, or symmetric, encryption is the Enigma machine.
+
+The SecretBox class in libnacl.secret makes this type of encryption very easy
+to execute:
+
+.. code-block:: python
+
+ msg = b'But then of course African swallows are not migratory.'
+ # Create a SecretBox object, if not passed in the secret key is
+ # Generated purely from random data
+ box = libnacl.secret.SecretBox()
+ # Messages can now be safely encrypted
+ ctxt = box.encrypt(msg)
+ # An additional box can be created from the original box secret key
+ box2 = libnacl.secret.SecretBox(box.sk)
+ # Messages can now be easily encrypted and decrypted
+ clear1 = box.decrypt(ctxt)
+ clear2 = box2.decrypt(ctxt)
+ ctxt2 = box2.encrypt(msg)
+ clear3 = box.decrypt(ctxt2)
+
+.. note::
+
+ Every encryption routine requires a nonce. The nonce is a 24 char string
+ that must never be used twice with the same keypair. If no nonce is passed
+ in then a nonce is generated based on random data.
+ If it is desired to generate a nonce manually this can be done by passing
+ it into the encrypt method.
diff --git a/doc/topics/sign.rst b/doc/topics/sign.rst
new file mode 100644
index 0000000..3b38407
--- /dev/null
+++ b/doc/topics/sign.rst
@@ -0,0 +1,43 @@
+==============================
+Signing and Verifying Messages
+==============================
+
+The nacl libs have the capability to sign and verify messages. Please be
+advised that public key encrypted messages do not need to be signed, the
+nacl box construct verifies the validity of the sender.
+
+To sign and verify messages use the Signer and Verifier classes:
+
+.. code-block:: python
+
+ import libnacl.sign
+
+ msg = (b'Well, that\'s no ordinary rabbit. That\'s the most foul, '
+ b'cruel, and bad-tempered rodent you ever set eyes on.')
+ # Create a Signer Object, if the key seed value is not passed in the
+ # signing keys will be automatically generated
+ signer = libnacl.sign.Signer()
+ # Sign the message, the signed string is the message itself plus the
+ # signature
+ signed = signer.sign(msg)
+ # If only the signature is desired without the message:
+ signature = signer.signature(msg)
+ # To create a verifier pass in the verify key:
+ veri = libnacl.sign.Verifier(signer.hex_vk())
+ # Verify the message!
+ verified = veri.verify(signed)
+ verified2 = veri.verify(signature + msg)
+
+Saving Keys to Disk
+===================
+
+All libnacl key objects can be safely saved to disk via the save method. This
+method changes the umask before saving the key file to ensure that the saved
+file can only be read by the user creating it and cannot be written to.
+
+.. code-block:: python
+
+ import libnacl.sign
+
+ signer = libnacl.sign.Signer()
+ signer.save('/etc/nacl/signer.key')
diff --git a/doc/topics/utils.rst b/doc/topics/utils.rst
new file mode 100644
index 0000000..a54eb07
--- /dev/null
+++ b/doc/topics/utils.rst
@@ -0,0 +1,50 @@
+=================
+Utility Functions
+=================
+
+The libnacl system comes with a number of utility functions, these functions
+are made available to make some of the aspects of encryption and key management
+easier. These range from nonce generation to loading saved keys.
+
+Loading Saved Keys
+==================
+
+After keys are saved using the key save method reloading the keys is easy. The
+`libnacl.utils.load_key` function will detect what type of key object saved
+said key and then create the object from the key and return it.
+
+.. code-block:: python
+
+ import libnacl.utils
+
+ key_obj = libnacl.utils.load_key('/etc/keys/bob.key')
+
+The load_key and save routines also support inline key serialization. The
+default is json but msgpack is also supported.
+
+Salsa Key
+=========
+
+A simple function that will return a random byte string suitable for use in
+SecretKey encryption.
+
+.. code-block:: python
+
+ import libnacl.utils
+
+ key = libnacl.utils.salsa_key()
+
+This routine is only required with the raw encryption functions, as the
+`libnacl.secret.SecretBox` will generate the key automatically.
+
+Nonce Routines
+==============
+
+A few functions are available to help with creating nonce values, these
+routines are available because there is some debate about what the best approach
+is.
+
+We recommend a pure random string for the nonce which is returned from
+`rand_nonce`, but some have expressed a desire to create nonces which are
+designed to avoid re-use by more than simply random data and therefore
+the `time_nonce` function is also available.
diff --git a/libnacl.egg-info/PKG-INFO b/libnacl.egg-info/PKG-INFO
new file mode 100644
index 0000000..5b4fd06
--- /dev/null
+++ b/libnacl.egg-info/PKG-INFO
@@ -0,0 +1,25 @@
+Metadata-Version: 2.1
+Name: libnacl
+Version: 1.8.0
+Summary: Python bindings for libsodium based on ctypes
+Home-page: https://libnacl.readthedocs.org/
+Author: Thomas S Hatch
+Author-email: thatch@saltstack.com
+License: UNKNOWN
+Platform: UNKNOWN
+Classifier: Operating System :: OS Independent
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Topic :: Security :: Cryptography
+License-File: LICENSE
+License-File: AUTHORS
+
+UNKNOWN
+
diff --git a/libnacl.egg-info/SOURCES.txt b/libnacl.egg-info/SOURCES.txt
new file mode 100644
index 0000000..2b20d14
--- /dev/null
+++ b/libnacl.egg-info/SOURCES.txt
@@ -0,0 +1,87 @@
+AUTHORS
+LICENSE
+MANIFEST.in
+README.rst
+setup.cfg
+setup.py
+doc/Makefile
+doc/conf.py
+doc/index.rst
+doc/topics/dual.rst
+doc/topics/public.rst
+doc/topics/raw_generichash.rst
+doc/topics/raw_hash.rst
+doc/topics/raw_public.rst
+doc/topics/raw_sealed.rst
+doc/topics/raw_secret.rst
+doc/topics/raw_sign.rst
+doc/topics/sealed.rst
+doc/topics/secret.rst
+doc/topics/sign.rst
+doc/topics/utils.rst
+doc/topics/releases/1.0.0.rst
+doc/topics/releases/1.1.0.rst
+doc/topics/releases/1.2.0.rst
+doc/topics/releases/1.3.0.rst
+doc/topics/releases/1.3.1.rst
+doc/topics/releases/1.3.2.rst
+doc/topics/releases/1.3.3.rst
+doc/topics/releases/1.3.4.rst
+doc/topics/releases/1.4.0.rst
+doc/topics/releases/1.4.1.rst
+doc/topics/releases/1.4.2.rst
+doc/topics/releases/1.4.3.rst
+doc/topics/releases/1.4.4.rst
+doc/topics/releases/1.4.5.rst
+doc/topics/releases/1.5.0.rst
+doc/topics/releases/1.5.1.rst
+doc/topics/releases/1.5.2.rst
+doc/topics/releases/1.6.0.rst
+doc/topics/releases/1.6.1.rst
+doc/topics/releases/1.7.1.rst
+doc/topics/releases/1.7.rst
+doc/topics/releases/index.rst
+libnacl/__init__.py
+libnacl/aead.py
+libnacl/base.py
+libnacl/blake.py
+libnacl/dual.py
+libnacl/encode.py
+libnacl/public.py
+libnacl/sealed.py
+libnacl/secret.py
+libnacl/secret_easy.py
+libnacl/sign.py
+libnacl/utils.py
+libnacl/version.py
+libnacl.egg-info/PKG-INFO
+libnacl.egg-info/SOURCES.txt
+libnacl.egg-info/dependency_links.txt
+libnacl.egg-info/top_level.txt
+pkg/rpm/python-libnacl.spec
+pkg/suse/python-libnacl.changes
+pkg/suse/python-libnacl.spec
+tests/runtests.py
+tests/unit/__init__.py
+tests/unit/test_aead.py
+tests/unit/test_auth_verify.py
+tests/unit/test_blake.py
+tests/unit/test_dual.py
+tests/unit/test_public.py
+tests/unit/test_raw_auth_sym.py
+tests/unit/test_raw_auth_sym_easy.py
+tests/unit/test_raw_generichash.py
+tests/unit/test_raw_hash.py
+tests/unit/test_raw_public.py
+tests/unit/test_raw_random.py
+tests/unit/test_raw_secret.py
+tests/unit/test_raw_secret_easy.py
+tests/unit/test_raw_sign.py
+tests/unit/test_save.py
+tests/unit/test_seal.py
+tests/unit/test_secret.py
+tests/unit/test_secret_easy.py
+tests/unit/test_sign.py
+tests/unit/test_stream.py
+tests/unit/test_verify.py
+tests/unit/test_version.py \ No newline at end of file
diff --git a/libnacl.egg-info/dependency_links.txt b/libnacl.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/libnacl.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/libnacl.egg-info/top_level.txt b/libnacl.egg-info/top_level.txt
new file mode 100644
index 0000000..d238ddf
--- /dev/null
+++ b/libnacl.egg-info/top_level.txt
@@ -0,0 +1 @@
+libnacl
diff --git a/libnacl/__init__.py b/libnacl/__init__.py
new file mode 100644
index 0000000..7649013
--- /dev/null
+++ b/libnacl/__init__.py
@@ -0,0 +1,1295 @@
+# -*- coding: utf-8 -*-
+'''
+Wrap libsodium routines
+'''
+# pylint: disable=C0103
+# Import python libs
+import ctypes
+import sys
+import os
+
+__SONAMES = (23, 18, 17, 13, 10, 5, 4)
+
+
+def _get_nacl():
+ '''
+ Locate the nacl c libs to use
+ '''
+ # Import libsodium
+ if sys.platform.startswith('win'):
+ try:
+ return ctypes.cdll.LoadLibrary('libsodium')
+ except OSError:
+ pass
+ for soname_ver in __SONAMES:
+ try:
+ return ctypes.cdll.LoadLibrary(
+ 'libsodium-{0}'.format(soname_ver)
+ )
+ except OSError:
+ pass
+ msg = 'Could not locate nacl lib, searched for libsodium'
+ raise OSError(msg)
+ elif sys.platform.startswith('darwin'):
+ try:
+ return ctypes.cdll.LoadLibrary('libsodium.dylib')
+ except OSError:
+ pass
+ try:
+ libidx = __file__.find('lib')
+ if libidx > 0:
+ libpath = __file__[0:libidx+3] + '/libsodium.dylib'
+ return ctypes.cdll.LoadLibrary(libpath)
+ except OSError:
+ msg = 'Could not locate nacl lib, searched for libsodium'
+ raise OSError(msg)
+ else:
+ try:
+ return ctypes.cdll.LoadLibrary('libsodium.so')
+ except OSError:
+ pass
+ try:
+ return ctypes.cdll.LoadLibrary('/usr/local/lib/libsodium.so')
+ except OSError:
+ pass
+ try:
+ libidx = __file__.find('lib')
+ if libidx > 0:
+ libpath = __file__[0:libidx+3] + '/libsodium.so'
+ return ctypes.cdll.LoadLibrary(libpath)
+ except OSError:
+ pass
+
+ for soname_ver in __SONAMES:
+ try:
+ return ctypes.cdll.LoadLibrary(
+ 'libsodium.so.{0}'.format(soname_ver)
+ )
+ except OSError:
+ pass
+ try:
+ # fall back to shipped libsodium, trust os version first
+ libpath = os.path.join(os.path.dirname(__file__), 'libsodium.so')
+ return ctypes.cdll.LoadLibrary(libpath)
+ except OSError:
+ pass
+ msg = 'Could not locate nacl lib, searched for libsodium.so, '
+ for soname_ver in __SONAMES:
+ msg += 'libsodium.so.{0}, '.format(soname_ver)
+ raise OSError(msg)
+
+# Don't load libnacl if we are in sphinx
+if not 'sphinx' in sys.argv[0]:
+ nacl = _get_nacl()
+ DOC_RUN = False
+else:
+ nacl = None
+ DOC_RUN = True
+
+
+# Define exceptions
+class CryptError(Exception):
+ """
+ Base Exception for cryptographic errors
+ """
+
+if not DOC_RUN:
+ sodium_init = nacl.sodium_init
+ sodium_init.res_type = ctypes.c_int
+ if sodium_init() < 0:
+ raise RuntimeError('sodium_init() call failed!')
+
+ # Define constants
+ try:
+ crypto_box_SEALBYTES = nacl.crypto_box_sealbytes()
+ HAS_SEAL = True
+ except AttributeError:
+ HAS_SEAL = False
+ try:
+ crypto_aead_aes256gcm_KEYBYTES = nacl.crypto_aead_aes256gcm_keybytes()
+ crypto_aead_aes256gcm_NPUBBYTES = nacl.crypto_aead_aes256gcm_npubbytes()
+ crypto_aead_aes256gcm_ABYTES = nacl.crypto_aead_aes256gcm_abytes()
+ HAS_AEAD_AES256GCM = bool(nacl.crypto_aead_aes256gcm_is_available())
+ crypto_aead_chacha20poly1305_ietf_KEYBYTES = nacl.crypto_aead_chacha20poly1305_ietf_keybytes()
+ crypto_aead_chacha20poly1305_ietf_NPUBBYTES = nacl.crypto_aead_chacha20poly1305_ietf_npubbytes()
+ crypto_aead_chacha20poly1305_ietf_ABYTES = nacl.crypto_aead_chacha20poly1305_ietf_abytes()
+ HAS_AEAD_CHACHA20POLY1305_IETF = True
+ HAS_AEAD = True
+ except AttributeError:
+ HAS_AEAD_AES256GCM = False
+ HAS_AEAD_CHACHA20POLY1305_IETF = False
+ HAS_AEAD = False
+
+ crypto_box_SECRETKEYBYTES = nacl.crypto_box_secretkeybytes()
+ crypto_box_SEEDBYTES = nacl.crypto_box_seedbytes()
+ crypto_box_PUBLICKEYBYTES = nacl.crypto_box_publickeybytes()
+ crypto_box_NONCEBYTES = nacl.crypto_box_noncebytes()
+ crypto_box_ZEROBYTES = nacl.crypto_box_zerobytes()
+ crypto_box_BOXZEROBYTES = nacl.crypto_box_boxzerobytes()
+ crypto_box_BEFORENMBYTES = nacl.crypto_box_beforenmbytes()
+ crypto_scalarmult_BYTES = nacl.crypto_scalarmult_bytes()
+ crypto_scalarmult_SCALARBYTES = nacl.crypto_scalarmult_scalarbytes()
+ crypto_sign_BYTES = nacl.crypto_sign_bytes()
+ crypto_sign_SEEDBYTES = nacl.crypto_sign_secretkeybytes() // 2
+ crypto_sign_PUBLICKEYBYTES = nacl.crypto_sign_publickeybytes()
+ crypto_sign_SECRETKEYBYTES = nacl.crypto_sign_secretkeybytes()
+ crypto_sign_ed25519_PUBLICKEYBYTES = nacl.crypto_sign_ed25519_publickeybytes()
+ crypto_sign_ed25519_SECRETKEYBYTES = nacl.crypto_sign_ed25519_secretkeybytes()
+ crypto_box_MACBYTES = crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES
+ crypto_secretbox_KEYBYTES = nacl.crypto_secretbox_keybytes()
+ crypto_secretbox_NONCEBYTES = nacl.crypto_secretbox_noncebytes()
+ crypto_secretbox_ZEROBYTES = nacl.crypto_secretbox_zerobytes()
+ crypto_secretbox_BOXZEROBYTES = nacl.crypto_secretbox_boxzerobytes()
+ crypto_secretbox_MACBYTES = crypto_secretbox_ZEROBYTES - crypto_secretbox_BOXZEROBYTES
+ crypto_stream_KEYBYTES = nacl.crypto_stream_keybytes()
+ crypto_stream_NONCEBYTES = nacl.crypto_stream_noncebytes()
+ crypto_auth_BYTES = nacl.crypto_auth_bytes()
+ crypto_auth_KEYBYTES = nacl.crypto_auth_keybytes()
+ crypto_onetimeauth_BYTES = nacl.crypto_onetimeauth_bytes()
+ crypto_onetimeauth_KEYBYTES = nacl.crypto_onetimeauth_keybytes()
+ crypto_generichash_BYTES = nacl.crypto_generichash_bytes()
+ crypto_generichash_BYTES_MIN = nacl.crypto_generichash_bytes_min()
+ crypto_generichash_BYTES_MAX = nacl.crypto_generichash_bytes_max()
+ crypto_generichash_KEYBYTES = nacl.crypto_generichash_keybytes()
+ crypto_generichash_KEYBYTES_MIN = nacl.crypto_generichash_keybytes_min()
+ crypto_generichash_KEYBYTES_MAX = nacl.crypto_generichash_keybytes_max()
+ crypto_scalarmult_curve25519_BYTES = nacl.crypto_scalarmult_curve25519_bytes()
+ crypto_hash_BYTES = nacl.crypto_hash_sha512_bytes()
+ crypto_hash_sha256_BYTES = nacl.crypto_hash_sha256_bytes()
+ crypto_hash_sha512_BYTES = nacl.crypto_hash_sha512_bytes()
+ crypto_verify_16_BYTES = nacl.crypto_verify_16_bytes()
+ crypto_verify_32_BYTES = nacl.crypto_verify_32_bytes()
+ crypto_verify_64_BYTES = nacl.crypto_verify_64_bytes()
+
+ try:
+ randombytes_SEEDBYTES = nacl.randombytes_seedbytes()
+ HAS_RAND_SEED = True
+ except AttributeError:
+ HAS_RAND_SEED = False
+
+ try:
+ crypto_kdf_PRIMITIVE = nacl.crypto_kdf_primitive()
+ crypto_kdf_BYTES_MIN = nacl.crypto_kdf_bytes_min()
+ crypto_kdf_BYTES_MAX = nacl.crypto_kdf_bytes_max()
+ crypto_kdf_CONTEXTBYTES = nacl.crypto_kdf_contextbytes()
+ crypto_kdf_KEYBYTES = nacl.crypto_kdf_keybytes()
+ HAS_CRYPT_KDF = True
+ except AttributeError:
+ HAS_CRYPT_KDF = False
+
+ try:
+ crypto_kx_PUBLICKEYBYTES = nacl.crypto_kx_publickeybytes()
+ crypto_kx_SECRETKEYBYTES = nacl.crypto_kx_secretkeybytes()
+ crypto_kx_SEEDBYTES = nacl.crypto_kx_seedbytes()
+ crypto_kx_SESSIONKEYBYTES = nacl.crypto_kx_sessionkeybytes()
+ crypto_kx_PRIMITIVE = nacl.crypto_kx_primitive()
+ HAS_CRYPT_KX = True
+ except AttributeError:
+ HAS_CRYPT_KX = False
+
+ # pylint: enable=C0103
+
+# Pubkey defs
+
+
+def crypto_box_keypair():
+ '''
+ Generate and return a new keypair
+
+ pk, sk = nacl.crypto_box_keypair()
+ '''
+ pk = ctypes.create_string_buffer(crypto_box_PUBLICKEYBYTES)
+ sk = ctypes.create_string_buffer(crypto_box_SECRETKEYBYTES)
+ nacl.crypto_box_keypair(pk, sk)
+ return pk.raw, sk.raw
+
+
+def crypto_box_seed_keypair(seed):
+ '''
+ Generate and return a keypair from a key seed
+ '''
+ if len(seed) != crypto_box_SEEDBYTES:
+ raise ValueError('Invalid key seed')
+ pk = ctypes.create_string_buffer(crypto_box_PUBLICKEYBYTES)
+ sk = ctypes.create_string_buffer(crypto_box_SECRETKEYBYTES)
+ nacl.crypto_box_seed_keypair(pk, sk, seed)
+ return pk.raw, sk.raw
+
+
+def crypto_scalarmult_base(sk):
+ '''
+ Compute and return the scalar product of a standard group element and the given integer.
+
+ This can be used to derive a Curve25519 public key from a Curve25519 secret key,
+ such as for usage with crypto_box and crypto_box_seal.
+ '''
+ if len(sk) != crypto_box_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+ pk = ctypes.create_string_buffer(crypto_box_PUBLICKEYBYTES)
+ if nacl.crypto_scalarmult_base(pk, sk):
+ raise CryptError('Failed to compute scalar product')
+ return pk.raw
+
+
+def crypto_box(msg, nonce, pk, sk):
+ '''
+ Using a public key and a secret key encrypt the given message. A nonce
+ must also be passed in, never reuse the nonce
+
+ enc_msg = nacl.crypto_box('secret message', <unique nonce>, <public key string>, <secret key string>)
+ '''
+ if len(pk) != crypto_box_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+ if len(sk) != crypto_box_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+ if len(nonce) != crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ pad = b'\x00' * crypto_box_ZEROBYTES + msg
+ c = ctypes.create_string_buffer(len(pad))
+ ret = nacl.crypto_box(c, pad, ctypes.c_ulonglong(len(pad)), nonce, pk, sk)
+ if ret:
+ raise CryptError('Unable to encrypt message')
+ return c.raw[crypto_box_BOXZEROBYTES:]
+
+
+def crypto_box_open(ctxt, nonce, pk, sk):
+ '''
+ Decrypts a message given the receiver's private key, and sender's public key
+ '''
+ if len(pk) != crypto_box_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+ if len(sk) != crypto_box_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+ if len(nonce) != crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ pad = b'\x00' * crypto_box_BOXZEROBYTES + ctxt
+ msg = ctypes.create_string_buffer(len(pad))
+ ret = nacl.crypto_box_open(
+ msg,
+ pad,
+ ctypes.c_ulonglong(len(pad)),
+ nonce,
+ pk,
+ sk)
+ if ret:
+ raise CryptError('Unable to decrypt ciphertext')
+ return msg.raw[crypto_box_ZEROBYTES:]
+
+
+def crypto_box_easy(msg, nonce, pk, sk):
+ '''
+ Using a public key and a secret key encrypt the given message. A nonce
+ must also be passed in, never reuse the nonce
+
+ enc_msg = nacl.crypto_box_easy('secret message', <unique nonce>, <public key string>, <secret key string>)
+ '''
+ if len(pk) != crypto_box_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+ if len(sk) != crypto_box_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+ if len(nonce) != crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ c = ctypes.create_string_buffer(len(msg) + crypto_box_MACBYTES)
+ ret = nacl.crypto_box(c, msg, ctypes.c_ulonglong(len(msg)), nonce, pk, sk)
+ if ret:
+ raise CryptError('Unable to encrypt message')
+ return c.raw
+
+
+def crypto_box_open_easy(ctxt, nonce, pk, sk):
+ '''
+ Decrypts a message given the receiver's private key, and sender's public key
+ '''
+ if len(pk) != crypto_box_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+ if len(sk) != crypto_box_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+ if len(nonce) != crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ msg = ctypes.create_string_buffer(len(ctxt) - crypto_box_MACBYTES)
+ ret = nacl.crypto_box_open(
+ msg,
+ ctxt,
+ ctypes.c_ulonglong(len(ctxt)),
+ nonce,
+ pk,
+ sk)
+ if ret:
+ raise CryptError('Unable to decrypt ciphertext')
+ return msg.raw[crypto_box_ZEROBYTES:]
+
+
+def crypto_box_beforenm(pk, sk):
+ '''
+ Partially performs the computation required for both encryption and decryption of data
+ '''
+ if len(pk) != crypto_box_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+ if len(sk) != crypto_box_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+ k = ctypes.create_string_buffer(crypto_box_BEFORENMBYTES)
+ ret = nacl.crypto_box_beforenm(k, pk, sk)
+ if ret:
+ raise CryptError('Unable to compute shared key')
+ return k.raw
+
+
+def crypto_box_afternm(msg, nonce, k):
+ '''
+ Encrypts a given a message, using partial computed data
+ '''
+ if len(k) != crypto_box_BEFORENMBYTES:
+ raise ValueError('Invalid shared key')
+ if len(nonce) != crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ pad = b'\x00' * crypto_box_ZEROBYTES + msg
+ ctxt = ctypes.create_string_buffer(len(pad))
+ ret = nacl.crypto_box_afternm(ctxt, pad, ctypes.c_ulonglong(len(pad)), nonce, k)
+ if ret:
+ raise CryptError('Unable to encrypt messsage')
+ return ctxt.raw[crypto_box_BOXZEROBYTES:]
+
+
+def crypto_box_open_afternm(ctxt, nonce, k):
+ '''
+ Decrypts a ciphertext ctxt given k
+ '''
+ if len(k) != crypto_box_BEFORENMBYTES:
+ raise ValueError('Invalid shared key')
+ if len(nonce) != crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ pad = b'\x00' * crypto_box_BOXZEROBYTES + ctxt
+ msg = ctypes.create_string_buffer(len(pad))
+ ret = nacl.crypto_box_open_afternm(
+ msg,
+ pad,
+ ctypes.c_ulonglong(len(pad)),
+ nonce,
+ k)
+ if ret:
+ raise CryptError('unable to decrypt message')
+ return msg.raw[crypto_box_ZEROBYTES:]
+
+
+def crypto_box_easy_afternm(msg, nonce, k):
+ '''
+ Using a precalculated shared key, encrypt the given message. A nonce
+ must also be passed in, never reuse the nonce
+
+ enc_msg = nacl.crypto_box_easy_afternm('secret message', <unique nonce>, <shared key string>)
+ '''
+ if len(k) != crypto_box_BEFORENMBYTES:
+ raise ValueError('Invalid shared key')
+ if len(nonce) != crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ ctxt = ctypes.create_string_buffer(len(msg) + crypto_box_MACBYTES)
+ ret = nacl.crypto_box_easy_afternm(ctxt, msg, ctypes.c_ulonglong(len(msg)), nonce, k)
+ if ret:
+ raise CryptError('Unable to encrypt messsage')
+ return ctxt.raw
+
+
+def crypto_box_open_easy_afternm(ctxt, nonce, k):
+ '''
+ Decrypts a ciphertext ctxt given k
+ '''
+ if len(k) != crypto_box_BEFORENMBYTES:
+ raise ValueError('Invalid shared key')
+ if len(nonce) != crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ msg = ctypes.create_string_buffer(len(ctxt) - crypto_box_MACBYTES)
+ ret = nacl.crypto_box_open_easy_afternm(
+ msg,
+ ctxt,
+ ctypes.c_ulonglong(len(ctxt)),
+ nonce,
+ k)
+ if ret:
+ raise CryptError('unable to decrypt message')
+ return msg.raw
+
+
+def crypto_box_seal(msg, pk):
+ '''
+ Using a public key to encrypt the given message. The identity of the sender cannot be verified.
+
+ enc_msg = nacl.crypto_box_seal('secret message', <public key string>)
+ '''
+ if not HAS_SEAL:
+ raise ValueError('Underlying Sodium library does not support sealed boxes')
+ if len(pk) != crypto_box_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+ if not isinstance(msg, bytes):
+ raise TypeError('Message must be bytes')
+
+ c = ctypes.create_string_buffer(len(msg) + crypto_box_SEALBYTES)
+ ret = nacl.crypto_box_seal(c, msg, ctypes.c_ulonglong(len(msg)), pk)
+ if ret:
+ raise CryptError('Unable to encrypt message')
+ return c.raw
+
+
+def crypto_box_seal_open(ctxt, pk, sk):
+ '''
+ Decrypts a message given the receiver's public and private key.
+ '''
+ if not HAS_SEAL:
+ raise ValueError('Underlying Sodium library does not support sealed boxes')
+ if len(pk) != crypto_box_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+ if len(sk) != crypto_box_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+ if not isinstance(ctxt, bytes):
+ raise TypeError('Message must be bytes')
+
+ c = ctypes.create_string_buffer(len(ctxt) - crypto_box_SEALBYTES)
+ ret = nacl.crypto_box_seal_open(c, ctxt, ctypes.c_ulonglong(len(ctxt)), pk, sk)
+ if ret:
+ raise CryptError('Unable to decrypt message')
+ return c.raw
+
+# Signing functions
+
+
+def crypto_sign_keypair():
+ '''
+ Generates a signing/verification key pair
+ '''
+ vk = ctypes.create_string_buffer(crypto_sign_PUBLICKEYBYTES)
+ sk = ctypes.create_string_buffer(crypto_sign_SECRETKEYBYTES)
+ ret = nacl.crypto_sign_keypair(vk, sk)
+ if ret:
+ raise ValueError('Failed to generate keypair')
+ return vk.raw, sk.raw
+
+
+def crypto_sign_ed25519_keypair():
+ '''
+ Generates a signing/verification Ed25519 key pair
+ '''
+ vk = ctypes.create_string_buffer(crypto_sign_ed25519_PUBLICKEYBYTES)
+ sk = ctypes.create_string_buffer(crypto_sign_ed25519_SECRETKEYBYTES)
+ ret = nacl.crypto_sign_ed25519_keypair(vk, sk)
+ if ret:
+ raise ValueError('Failed to generate keypair')
+ return vk.raw, sk.raw
+
+
+def crypto_sign_ed25519_sk_to_pk(sk):
+ '''
+ Extract the public key from the secret key
+ '''
+ if len(sk) != crypto_sign_ed25519_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+
+ pk = ctypes.create_string_buffer(crypto_sign_PUBLICKEYBYTES)
+ ret = nacl.crypto_sign_ed25519_sk_to_pk(pk, sk)
+ if ret:
+ raise ValueError('Failed to generate public key')
+ return pk.raw
+
+
+def crypto_sign_ed25519_sk_to_seed(sk):
+ '''
+ Extract the seed from the secret key
+ '''
+ if len(sk) != crypto_sign_ed25519_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+
+ seed = ctypes.create_string_buffer(crypto_sign_SEEDBYTES)
+ ret = nacl.crypto_sign_ed25519_sk_to_seed(seed, sk)
+ if ret:
+ raise ValueError('Failed to generate seed')
+ return seed.raw
+
+
+def crypto_sign(msg, sk):
+ '''
+ Sign the given message with the given signing key
+ '''
+ if len(sk) != crypto_sign_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+
+ sig = ctypes.create_string_buffer(len(msg) + crypto_sign_BYTES)
+ slen = ctypes.pointer(ctypes.c_ulonglong())
+ ret = nacl.crypto_sign(
+ sig,
+ slen,
+ msg,
+ ctypes.c_ulonglong(len(msg)),
+ sk)
+ if ret:
+ raise ValueError('Failed to sign message')
+ return sig.raw
+
+
+def crypto_sign_detached(msg, sk):
+ '''
+ Return signature for the given message with the given signing key
+ '''
+ if len(sk) != crypto_sign_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+
+ sig = ctypes.create_string_buffer(crypto_sign_BYTES)
+ slen = ctypes.pointer(ctypes.c_ulonglong())
+ ret = nacl.crypto_sign_detached(
+ sig,
+ slen,
+ msg,
+ ctypes.c_ulonglong(len(msg)),
+ sk)
+ if ret:
+ raise ValueError('Failed to sign message')
+ return sig.raw[:slen.contents.value]
+
+
+def crypto_sign_seed_keypair(seed):
+ '''
+ Computes and returns the secret and verify keys from the given seed
+ '''
+ if len(seed) != crypto_sign_SEEDBYTES:
+ raise ValueError('Invalid Seed')
+
+ sk = ctypes.create_string_buffer(crypto_sign_SECRETKEYBYTES)
+ vk = ctypes.create_string_buffer(crypto_sign_PUBLICKEYBYTES)
+
+ ret = nacl.crypto_sign_seed_keypair(vk, sk, seed)
+ if ret:
+ raise CryptError('Failed to generate keypair from seed')
+ return (vk.raw, sk.raw)
+
+
+def crypto_sign_open(sig, vk):
+ '''
+ Verifies the signed message sig using the signer's verification key
+ '''
+ if len(vk) != crypto_sign_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+
+ msg = ctypes.create_string_buffer(len(sig))
+ msglen = ctypes.c_ulonglong()
+ msglenp = ctypes.pointer(msglen)
+ ret = nacl.crypto_sign_open(
+ msg,
+ msglenp,
+ sig,
+ ctypes.c_ulonglong(len(sig)),
+ vk)
+ if ret:
+ raise ValueError('Failed to validate message')
+ return msg.raw[:msglen.value] # pylint: disable=invalid-slice-index
+
+
+def crypto_sign_verify_detached(sig, msg, vk):
+ '''
+ Verifies that sig is a valid signature for the message msg using the signer's verification key
+ '''
+ if len(sig) != crypto_sign_BYTES:
+ raise ValueError('Invalid signature')
+ if len(vk) != crypto_sign_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+
+ ret = nacl.crypto_sign_verify_detached(
+ sig,
+ msg,
+ ctypes.c_ulonglong(len(msg)),
+ vk)
+ if ret:
+ raise ValueError('Failed to validate message')
+ return msg
+
+
+# Authenticated Symmetric Encryption
+
+
+def crypto_secretbox(message, nonce, key):
+ """Encrypts and authenticates a message using the given secret key, and nonce
+
+ Args:
+ message (bytes): a message to encrypt
+ nonce (bytes): nonce, does not have to be confidential must be
+ `crypto_secretbox_NONCEBYTES` in length
+ key (bytes): secret key, must be `crypto_secretbox_KEYBYTES` in
+ length
+
+ Returns:
+ bytes: the ciphertext
+
+ Raises:
+ ValueError: if arguments' length is wrong or the operation has failed.
+ """
+ if len(key) != crypto_secretbox_KEYBYTES:
+ raise ValueError('Invalid key')
+
+ if len(nonce) != crypto_secretbox_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+
+ pad = b'\x00' * crypto_secretbox_ZEROBYTES + message
+ ctxt = ctypes.create_string_buffer(len(pad))
+ ret = nacl.crypto_secretbox(
+ ctxt, pad, ctypes.c_ulonglong(len(pad)), nonce, key)
+ if ret:
+ raise ValueError('Failed to encrypt message')
+ return ctxt.raw[crypto_secretbox_BOXZEROBYTES:]
+
+
+def crypto_secretbox_open(ctxt, nonce, key):
+ """
+ Decrypts a ciphertext ctxt given the receivers private key, and senders
+ public key
+ """
+ if len(key) != crypto_secretbox_KEYBYTES:
+ raise ValueError('Invalid key')
+
+ if len(nonce) != crypto_secretbox_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+
+ pad = b'\x00' * crypto_secretbox_BOXZEROBYTES + ctxt
+ msg = ctypes.create_string_buffer(len(pad))
+ ret = nacl.crypto_secretbox_open(
+ msg,
+ pad,
+ ctypes.c_ulonglong(len(pad)),
+ nonce,
+ key)
+ if ret:
+ raise ValueError('Failed to decrypt message')
+ return msg.raw[crypto_secretbox_ZEROBYTES:]
+
+# Authenticated Symmetric Encryption improved version
+
+
+def crypto_secretbox_easy(cmessage, nonce, key):
+ if len(key) != crypto_secretbox_KEYBYTES:
+ raise ValueError('Invalid key')
+
+ if len(nonce) != crypto_secretbox_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+
+
+ ctxt = ctypes.create_string_buffer(crypto_secretbox_MACBYTES + len(cmessage))
+ ret = nacl.crypto_secretbox_easy(ctxt, cmessage, ctypes.c_ulonglong(len(cmessage)), nonce, key)
+ if ret:
+ raise ValueError('Failed to encrypt message')
+ return ctxt.raw[0:]
+
+def crypto_secretbox_open_easy(ctxt, nonce, key):
+
+ if len(key) != crypto_secretbox_KEYBYTES:
+ raise ValueError('Invalid key')
+
+ if len(nonce) != crypto_secretbox_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+
+ msg = ctypes.create_string_buffer(len(ctxt))
+ ret = nacl.crypto_secretbox_open_easy(msg, ctxt, ctypes.c_ulonglong(len(ctxt)), nonce, key)
+ if ret:
+ raise ValueError('Failed to decrypt message')
+ return msg.raw[0:len(ctxt) - crypto_secretbox_MACBYTES]
+
+# Authenticated Symmetric Encryption with Additional Data
+
+
+def crypto_aead_aes256gcm_encrypt(message, aad, nonce, key):
+ """Encrypts and authenticates a message with public additional data using the given secret key, and nonce
+
+ Args:
+ message (bytes): a message to encrypt
+ aad (bytes): additional public data to authenticate
+ nonce (bytes): nonce, does not have to be confidential must be
+ `crypto_aead_aes256gcm_NPUBBYTES` in length
+ key (bytes): secret key, must be `crypto_aead_aes256gcm_KEYBYTES` in
+ length
+
+ Returns:
+ bytes: the ciphertext
+
+ Raises:
+ ValueError: if arguments' length is wrong or the operation has failed.
+ """
+ if not HAS_AEAD_AES256GCM:
+ raise ValueError('Underlying Sodium library does not support AES256-GCM AEAD')
+
+ if len(key) != crypto_aead_aes256gcm_KEYBYTES:
+ raise ValueError('Invalid key')
+
+ if len(nonce) != crypto_aead_aes256gcm_NPUBBYTES:
+ raise ValueError('Invalid nonce')
+
+ length = len(message) + crypto_aead_aes256gcm_ABYTES
+ clen = ctypes.c_ulonglong()
+ c = ctypes.create_string_buffer(length)
+ ret = nacl.crypto_aead_aes256gcm_encrypt(
+ c, ctypes.pointer(clen),
+ message, ctypes.c_ulonglong(len(message)),
+ aad, ctypes.c_ulonglong(len(aad)),
+ None,
+ nonce, key)
+ if ret:
+ raise ValueError('Failed to encrypt message')
+ return c.raw
+
+
+def crypto_aead_chacha20poly1305_ietf_encrypt(message, aad, nonce, key):
+ """Encrypts and authenticates a message with public additional data using the given secret key, and nonce
+
+ Args:
+ message (bytes): a message to encrypt
+ aad (bytes): additional public data to authenticate
+ nonce (bytes): nonce, does not have to be confidential must be
+ `crypto_aead_chacha20poly1305_ietf_NPUBBYTES` in length
+ key (bytes): secret key, must be `crypto_aead_chacha20poly1305_ietf_KEYBYTES` in
+ length
+
+ Returns:
+ bytes: the ciphertext
+
+ Raises:
+ ValueError: if arguments' length is wrong or the operation has failed.
+ """
+ if not HAS_AEAD_CHACHA20POLY1305_IETF:
+ raise ValueError('Underlying Sodium library does not support IETF variant of ChaCha20Poly1305 AEAD')
+
+ if len(key) != crypto_aead_chacha20poly1305_ietf_KEYBYTES:
+ raise ValueError('Invalid key')
+
+ if len(nonce) != crypto_aead_chacha20poly1305_ietf_NPUBBYTES:
+ raise ValueError('Invalid nonce')
+
+ length = len(message) + crypto_aead_chacha20poly1305_ietf_ABYTES
+ clen = ctypes.c_ulonglong()
+ c = ctypes.create_string_buffer(length)
+ ret = nacl.crypto_aead_chacha20poly1305_ietf_encrypt(
+ c, ctypes.pointer(clen),
+ message, ctypes.c_ulonglong(len(message)),
+ aad, ctypes.c_ulonglong(len(aad)),
+ None,
+ nonce, key)
+ if ret:
+ raise ValueError('Failed to encrypt message')
+ return c.raw
+
+
+def crypto_aead_aes256gcm_decrypt(ctxt, aad, nonce, key):
+ """
+ Decrypts a ciphertext ctxt given the key, nonce, and aad. If the aad
+ or ciphertext were altered then the decryption will fail.
+ """
+ if not HAS_AEAD_AES256GCM:
+ raise ValueError('Underlying Sodium library does not support AES256-GCM AEAD')
+
+ if len(key) != crypto_aead_aes256gcm_KEYBYTES:
+ raise ValueError('Invalid key')
+
+ if len(nonce) != crypto_aead_aes256gcm_NPUBBYTES:
+ raise ValueError('Invalid nonce')
+
+ length = len(ctxt)-crypto_aead_aes256gcm_ABYTES
+ mlen = ctypes.c_ulonglong()
+ m = ctypes.create_string_buffer(length)
+
+ ret = nacl.crypto_aead_aes256gcm_decrypt(
+ m, ctypes.byref(mlen),
+ None,
+ ctxt, ctypes.c_ulonglong(len(ctxt)),
+ aad, ctypes.c_ulonglong(len(aad)),
+ nonce, key)
+ if ret:
+ raise ValueError('Failed to decrypt message')
+ return m.raw
+
+
+def crypto_aead_chacha20poly1305_ietf_decrypt(ctxt, aad, nonce, key):
+ """
+ Decrypts a ciphertext ctxt given the key, nonce, and aad. If the aad
+ or ciphertext were altered then the decryption will fail.
+ """
+ if not HAS_AEAD_CHACHA20POLY1305_IETF:
+ raise ValueError('Underlying Sodium library does not support IETF variant of ChaCha20Poly1305 AEAD')
+
+ if len(key) != crypto_aead_chacha20poly1305_ietf_KEYBYTES:
+ raise ValueError('Invalid key')
+
+ if len(nonce) != crypto_aead_chacha20poly1305_ietf_NPUBBYTES:
+ raise ValueError('Invalid nonce')
+
+ length = len(ctxt)-crypto_aead_chacha20poly1305_ietf_ABYTES
+ mlen = ctypes.c_ulonglong()
+ m = ctypes.create_string_buffer(length)
+
+ ret = nacl.crypto_aead_chacha20poly1305_ietf_decrypt(
+ m, ctypes.byref(mlen),
+ None,
+ ctxt, ctypes.c_ulonglong(len(ctxt)),
+ aad, ctypes.c_ulonglong(len(aad)),
+ nonce, key)
+ if ret:
+ raise ValueError('Failed to decrypt message')
+ return m.raw
+
+
+# Symmetric Encryption
+
+
+def crypto_stream(slen, nonce, key):
+ '''
+ Generates a stream using the given secret key and nonce
+ '''
+ if len(key) != crypto_stream_KEYBYTES:
+ raise ValueError('Invalid secret key')
+ if len(nonce) != crypto_stream_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+
+ stream = ctypes.create_string_buffer(slen)
+ ret = nacl.crypto_stream(stream, ctypes.c_ulonglong(slen), nonce, key)
+ if ret:
+ raise ValueError('Failed to init stream')
+ return stream.raw
+
+
+def crypto_stream_xor(msg, nonce, key):
+ '''
+ Encrypts the given message using the given secret key and nonce
+
+ The crypto_stream_xor function guarantees that the ciphertext is the
+ plaintext (xor) the output of crypto_stream. Consequently
+ crypto_stream_xor can also be used to decrypt
+ '''
+ if len(key) != crypto_stream_KEYBYTES:
+ raise ValueError('Invalid secret key')
+ if len(nonce) != crypto_stream_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+
+ stream = ctypes.create_string_buffer(len(msg))
+ ret = nacl.crypto_stream_xor(
+ stream,
+ msg,
+ ctypes.c_ulonglong(len(msg)),
+ nonce,
+ key)
+ if ret:
+ raise ValueError('Failed to init stream')
+ return stream.raw
+
+
+# Authentication
+
+
+def crypto_auth(msg, key):
+ '''
+ Constructs a one time authentication token for the given message msg
+ using a given secret key
+ '''
+ if len(key) != crypto_auth_KEYBYTES:
+ raise ValueError('Invalid secret key')
+
+ tok = ctypes.create_string_buffer(crypto_auth_BYTES)
+ ret = nacl.crypto_auth(tok, msg, ctypes.c_ulonglong(len(msg)), key)
+ if ret:
+ raise ValueError('Failed to auth msg')
+ return tok.raw[:crypto_auth_BYTES]
+
+
+def crypto_auth_verify(tok, msg, key):
+ '''
+ Verifies that the given authentication token is correct for the given
+ message and key
+ '''
+ if len(key) != crypto_auth_KEYBYTES:
+ raise ValueError('Invalid secret key')
+ if len(tok) != crypto_auth_BYTES:
+ raise ValueError('Invalid authenticator')
+
+ ret = nacl.crypto_auth_verify(tok, msg, ctypes.c_ulonglong(len(msg)), key)
+ if ret:
+ raise ValueError('Failed to auth msg')
+ return msg
+
+# One time authentication
+
+
+def crypto_onetimeauth_primitive():
+ """
+ Return the onetimeauth underlying primitive
+
+ Returns:
+ str: always ``poly1305``
+ """
+ func = nacl.crypto_onetimeauth_primitive
+ func.restype = ctypes.c_char_p
+ return func().decode()
+
+
+def crypto_onetimeauth(message, key):
+ """
+ Constructs a one time authentication token for the given message using
+ a given secret key
+
+ Args:
+ message (bytes): message to authenticate.
+ key (bytes): secret key - must be of crypto_onetimeauth_KEYBYTES length.
+
+ Returns:
+ bytes: an authenticator, of crypto_onetimeauth_BYTES length.
+
+ Raises:
+ ValueError: if arguments' length is wrong.
+ """
+ if len(key) != crypto_onetimeauth_KEYBYTES:
+ raise ValueError('Invalid secret key')
+
+ tok = ctypes.create_string_buffer(crypto_onetimeauth_BYTES)
+ # cannot fail
+ _ = nacl.crypto_onetimeauth(
+ tok, message, ctypes.c_ulonglong(len(message)), key)
+
+ return tok.raw[:crypto_onetimeauth_BYTES]
+
+
+def crypto_onetimeauth_verify(token, message, key):
+ """
+ Verifies, in constant time, that ``token`` is a correct authenticator for
+ the message using the secret key.
+
+ Args:
+ token (bytes): an authenticator of crypto_onetimeauth_BYTES length.
+ message (bytes): The message to authenticate.
+ key: key (bytes): secret key - must be of crypto_onetimeauth_KEYBYTES
+ length.
+
+ Returns:
+ bytes: secret key - must be of crypto_onetimeauth_KEYBYTES length.
+
+ Raises:
+ ValueError: if arguments' length is wrong or verification has failed.
+ """
+ if len(key) != crypto_onetimeauth_KEYBYTES:
+ raise ValueError('Invalid secret key')
+ if len(token) != crypto_onetimeauth_BYTES:
+ raise ValueError('Invalid authenticator')
+
+ ret = nacl.crypto_onetimeauth_verify(
+ token, message, ctypes.c_ulonglong(len(message)), key)
+ if ret:
+ raise ValueError('Failed to auth message')
+ return message
+
+# Hashing
+
+
+def crypto_hash(msg):
+ '''
+ Compute a hash of the given message
+ '''
+ hbuf = ctypes.create_string_buffer(crypto_hash_BYTES)
+ nacl.crypto_hash(hbuf, msg, ctypes.c_ulonglong(len(msg)))
+ return hbuf.raw
+
+
+def crypto_hash_sha256(msg):
+ '''
+ Compute the sha256 hash of the given message
+ '''
+ hbuf = ctypes.create_string_buffer(crypto_hash_sha256_BYTES)
+ nacl.crypto_hash_sha256(hbuf, msg, ctypes.c_ulonglong(len(msg)))
+ return hbuf.raw
+
+
+def crypto_hash_sha512(msg):
+ '''
+ Compute the sha512 hash of the given message
+ '''
+ hbuf = ctypes.create_string_buffer(crypto_hash_sha512_BYTES)
+ nacl.crypto_hash_sha512(hbuf, msg, ctypes.c_ulonglong(len(msg)))
+ return hbuf.raw
+
+# Generic Hash
+
+
+def crypto_generichash(msg, key=None):
+ '''
+ Compute the blake2 hash of the given message with a given key
+ '''
+ hbuf = ctypes.create_string_buffer(crypto_generichash_BYTES)
+ if key:
+ key_len = len(key)
+ else:
+ key_len = 0
+ nacl.crypto_generichash(
+ hbuf,
+ ctypes.c_size_t(len(hbuf)),
+ msg,
+ ctypes.c_ulonglong(len(msg)),
+ key,
+ ctypes.c_size_t(key_len))
+ return hbuf.raw
+
+
+# String cmp
+
+
+def crypto_verify_16(string1, string2):
+ '''
+ Compares the first crypto_verify_16_BYTES of the given strings
+
+ The time taken by the function is independent of the contents of string1
+ and string2. In contrast, the standard C comparison function
+ memcmp(string1,string2,16) takes time that is dependent on the longest
+ matching prefix of string1 and string2. This often allows for easy
+ timing attacks.
+ '''
+ a, b, c = (len(string1) >= 16), (len(string2) >= 16), (not nacl.crypto_verify_16(string1, string2))
+ return a & b & c
+
+
+def crypto_verify_32(string1, string2):
+ '''
+ Compares the first crypto_verify_32_BYTES of the given strings
+
+ The time taken by the function is independent of the contents of string1
+ and string2. In contrast, the standard C comparison function
+ memcmp(string1,string2,32) takes time that is dependent on the longest
+ matching prefix of string1 and string2. This often allows for easy
+ timing attacks.
+ '''
+ a, b, c = (len(string1) >= 32), (len(string2) >= 32), (not nacl.crypto_verify_32(string1, string2))
+ return a & b & c
+
+
+def crypto_verify_64(string1, string2):
+ '''
+ Compares the first crypto_verify_64_BYTES of the given strings
+
+ The time taken by the function is independent of the contents of string1
+ and string2. In contrast, the standard C comparison function
+ memcmp(string1,string2,64) takes time that is dependent on the longest
+ matching prefix of string1 and string2. This often allows for easy
+ timing attacks.
+ '''
+ a, b, c = (len(string1) >= 64), (len(string2) >= 64), (not nacl.crypto_verify_64(string1, string2))
+ return a & b & c
+
+
+def bytes_eq(a, b):
+ '''
+ Compares two byte instances with one another. If `a` and `b` have
+ different lengths, return `False` immediately. Otherwise `a` and `b`
+ will be compared in constant time.
+
+ Return `True` in case `a` and `b` are equal. Otherwise `False`.
+
+ Raises :exc:`TypeError` in case `a` and `b` are not both of the type
+ :class:`bytes`.
+ '''
+ if not isinstance(a, bytes) or not isinstance(b, bytes):
+ raise TypeError('Both arguments must be bytes.')
+
+ len_a = len(a)
+ len_b = len(b)
+ if len_a != len_b:
+ return False
+
+ return nacl.sodium_memcmp(a, b, len_a) == 0
+
+# Random byte generation
+
+
+def randombytes(size):
+ '''
+ Return a string of random bytes of the given size
+ '''
+ buf = ctypes.create_string_buffer(size)
+ nacl.randombytes(buf, ctypes.c_ulonglong(size))
+ return buf.raw
+
+
+def randombytes_buf(size):
+ '''
+ Return a string of random bytes of the given size
+ '''
+ size = int(size)
+ buf = ctypes.create_string_buffer(size)
+ nacl.randombytes_buf(buf, size)
+ return buf.raw
+
+def randombytes_buf_deterministic(size, seed):
+ '''
+ Returns a string of random byles of the given size for a given seed.
+ For a given seed, this function will always output the same sequence.
+ Size can be up to 2^70 (256 GB).
+ '''
+
+ if not HAS_RAND_SEED:
+ raise ValueError('Underlying Sodium library does not support randombytes_seedbytes')
+ if len(seed) != randombytes_SEEDBYTES:
+ raise ValueError('Invalid key seed')
+
+ size = int(size)
+ buf = ctypes.create_string_buffer(size)
+ nacl.randombytes_buf_deterministic(buf, size, seed)
+ return buf.raw
+
+def randombytes_close():
+ '''
+ Close the file descriptor or the handle for the cryptographic service
+ provider
+ '''
+ nacl.randombytes_close()
+
+
+def randombytes_random():
+ '''
+ Return a random 32-bit unsigned value
+ '''
+ return nacl.randombytes_random()
+
+
+def randombytes_stir():
+ '''
+ Generate a new key for the pseudorandom number generator
+
+ The file descriptor for the entropy source is kept open, so that the
+ generator can be reseeded even in a chroot() jail.
+ '''
+ nacl.randombytes_stir()
+
+
+def randombytes_uniform(upper_bound):
+ '''
+ Return a value between 0 and upper_bound using a uniform distribution
+ '''
+ return nacl.randombytes_uniform(upper_bound)
+
+# Key derivation API
+
+def crypto_kdf_keygen():
+ '''
+ Returns a string of random bytes to generate a master key
+ '''
+ if not HAS_CRYPT_KDF:
+ raise ValueError('Underlying Sodium library does not support crypto_kdf_keybytes')
+ size = crypto_kdf_KEYBYTES
+ buf = ctypes.create_string_buffer(size)
+ nacl.crypto_kdf_keygen(buf)
+ return buf.raw
+
+def crypto_kdf_derive_from_key(subkey_size, subkey_id, context, master_key):
+ '''
+ Returns a subkey generated from a master key for a given subkey_id.
+ For a given subkey_id, the subkey will always be the same string.
+ '''
+ size = int(subkey_size)
+ buf = ctypes.create_string_buffer(size)
+ nacl.crypto_kdf_derive_from_key(buf, subkey_size, ctypes.c_ulonglong(subkey_id), context, master_key)
+ return buf.raw
+
+# Key Exchange API
+
+def crypto_kx_keypair():
+ '''
+ Generate and return a new keypair
+ '''
+ if not HAS_CRYPT_KX:
+ raise ValueError('Underlying Sodium library does not support crypto_kx')
+ pk = ctypes.create_string_buffer(crypto_kx_PUBLICKEYBYTES)
+ sk = ctypes.create_string_buffer(crypto_kx_SECRETKEYBYTES)
+ nacl.crypto_kx_keypair(pk, sk)
+ return pk.raw, sk.raw
+
+def crypto_kx_seed_keypair(seed):
+ '''
+ Generate and return a keypair from a key seed
+ '''
+ if not HAS_CRYPT_KX:
+ raise ValueError('Underlying Sodium library does not support crypto_kx')
+
+ if len(seed) != crypto_kx_SEEDBYTES:
+ raise ValueError('Invalid key seed')
+ pk = ctypes.create_string_buffer(crypto_kx_PUBLICKEYBYTES)
+ sk = ctypes.create_string_buffer(crypto_kx_SECRETKEYBYTES)
+ nacl.crypto_kx_seed_keypair(pk, sk, seed)
+ return pk.raw, sk.raw
+
+def crypto_kx_client_session_keys(client_pk, client_sk, server_pk):
+ '''
+ Computes a pair of shared keys (rx and tx) using the client's public key client_pk,
+ the client's secret key client_sk and the server's public key server_pk.
+ Status returns 0 on success, or -1 if the server's public key is not acceptable.
+ '''
+ if not HAS_CRYPT_KX:
+ raise ValueError('Underlying Sodium library does not support crypto_kx')
+
+ rx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES)
+ tx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES)
+ status = nacl.crypto_kx_client_session_keys(rx, tx, client_pk, client_sk, server_pk)
+ return rx.raw, tx.raw, status
+
+def crypto_kx_server_session_keys(server_pk, server_sk, client_pk):
+ '''
+ Computes a pair of shared keys (rx and tx) using the server's public key server_pk,
+ the server's secret key server_sk and the client's public key client_pk.
+ Status returns 0 on success, or -1 if the client's public key is not acceptable.
+ '''
+ if not HAS_CRYPT_KX:
+ raise ValueError('Underlying Sodium library does not support crypto_kx')
+
+ rx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES)
+ tx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES)
+ status = nacl.crypto_kx_server_session_keys(rx, tx, server_pk, server_sk, client_pk)
+ return rx.raw, tx.raw, status
+
+
+
+# Utility functions
+
+def sodium_library_version_major():
+ '''
+ Return the major version number
+ '''
+ return nacl.sodium_library_version_major()
+
+
+def sodium_library_version_minor():
+ '''
+ Return the minor version number
+ '''
+ return nacl.sodium_library_version_minor()
+
+
+def sodium_version_string():
+ '''
+ Return the version string
+ '''
+ func = nacl.sodium_version_string
+ func.restype = ctypes.c_char_p
+ return func()
+
+
+def crypto_sign_ed25519_pk_to_curve25519(ed25519_pk):
+ '''
+ Convert an Ed25519 public key to a Curve25519 public key
+ '''
+ if len(ed25519_pk) != crypto_sign_ed25519_PUBLICKEYBYTES:
+ raise ValueError('Invalid public key')
+
+ curve25519_pk = ctypes.create_string_buffer(crypto_scalarmult_curve25519_BYTES)
+ ret = nacl.crypto_sign_ed25519_pk_to_curve25519(curve25519_pk, ed25519_pk)
+ if ret:
+ raise CryptError('Failed to generate Curve25519 public key')
+ return curve25519_pk.raw
+
+
+def crypto_sign_ed25519_sk_to_curve25519(ed25519_sk):
+ '''
+ Convert an Ed25519 secret key to a Curve25519 secret key
+ '''
+ if len(ed25519_sk) != crypto_sign_ed25519_SECRETKEYBYTES:
+ raise ValueError('Invalid secret key')
+
+ curve25519_sk = ctypes.create_string_buffer(crypto_scalarmult_curve25519_BYTES)
+ ret = nacl.crypto_sign_ed25519_sk_to_curve25519(curve25519_sk, ed25519_sk)
+ if ret:
+ raise CryptError('Failed to generate Curve25519 secret key')
+ return curve25519_sk.raw
+
+
diff --git a/libnacl/aead.py b/libnacl/aead.py
new file mode 100644
index 0000000..db53b13
--- /dev/null
+++ b/libnacl/aead.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+'''
+Utilities to make secret box encryption simple
+'''
+# Import libnacl
+import libnacl
+import libnacl.utils
+import libnacl.base
+
+
+class AEAD(libnacl.base.BaseKey):
+ '''
+ Manage AEAD encryption using the IETF ChaCha20-Poly1305(default) or AES-GCM algorithm
+ '''
+
+ def __init__(self, key=None):
+ if key is None:
+ key = libnacl.utils.aead_key()
+ if len(key) != libnacl.crypto_aead_chacha20poly1305_ietf_KEYBYTES: # same size for both
+ raise ValueError('Invalid key')
+ self.sk = key
+ self.usingAES = False
+
+ def useAESGCM(self):
+ self.usingAES = True
+ return self
+
+ def encrypt(self, msg, aad, nonce=None, pack_nonce_aad=True):
+ '''
+ Encrypt the given message. If a nonce is not given it will be
+ generated via the rand_nonce function
+ '''
+ if nonce is None:
+ nonce = libnacl.utils.rand_aead_nonce()
+ if len(nonce) != libnacl.crypto_aead_aes256gcm_NPUBBYTES:
+ raise ValueError('Invalid nonce')
+ if self.usingAES:
+ ctxt = libnacl.crypto_aead_aes256gcm_encrypt(msg, aad, nonce, self.sk)
+ else:
+ ctxt = libnacl.crypto_aead_chacha20poly1305_ietf_encrypt(msg, aad, nonce, self.sk)
+
+ if pack_nonce_aad:
+ return aad + nonce + ctxt
+ else:
+ return aad, nonce, ctxt
+
+ def decrypt(self, ctxt, aadLen):
+ '''
+ Decrypt the given message, if no nonce or aad are given they will be
+ extracted from the message
+ '''
+ aad = ctxt[:aadLen]
+ nonce = ctxt[aadLen:aadLen+libnacl.crypto_aead_aes256gcm_NPUBBYTES]
+ ctxt = ctxt[aadLen+libnacl.crypto_aead_aes256gcm_NPUBBYTES:]
+ if len(nonce) != libnacl.crypto_aead_aes256gcm_NPUBBYTES:
+ raise ValueError('Invalid nonce')
+ if self.usingAES:
+ return libnacl.crypto_aead_aes256gcm_decrypt(ctxt, aad, nonce, self.sk)
+ return libnacl.crypto_aead_chacha20poly1305_ietf_decrypt(ctxt, aad, nonce, self.sk)
+
+ def decrypt_unpacked(self, aad, nonce, ctxt):
+ '''
+ Decrypt the given message, if no nonce or aad are given they will be
+ extracted from the message
+ '''
+ if len(nonce) != libnacl.crypto_aead_aes256gcm_NPUBBYTES:
+ raise ValueError('Invalid nonce')
+ if self.usingAES:
+ return libnacl.crypto_aead_aes256gcm_decrypt(ctxt, aad, nonce, self.sk)
+ return libnacl.crypto_aead_chacha20poly1305_ietf_decrypt(ctxt, aad, nonce, self.sk)
diff --git a/libnacl/base.py b/libnacl/base.py
new file mode 100644
index 0000000..b115a47
--- /dev/null
+++ b/libnacl/base.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+'''
+Implement the base key object for other keys to inherit convenience functions
+'''
+# Import libnacl libs
+import libnacl.encode
+
+# Import python libs
+import os
+import stat
+
+class BaseKey(object):
+ '''
+ Include methods for key management convenience
+ '''
+ def hex_sk(self):
+ if hasattr(self, 'sk'):
+ return libnacl.encode.hex_encode(self.sk)
+ else:
+ return ''
+
+ def hex_pk(self):
+ if hasattr(self, 'pk'):
+ return libnacl.encode.hex_encode(self.pk)
+
+ def hex_vk(self):
+ if hasattr(self, 'vk'):
+ return libnacl.encode.hex_encode(self.vk)
+
+ def hex_seed(self):
+ if hasattr(self, 'seed'):
+ return libnacl.encode.hex_encode(self.seed)
+
+ def for_json(self):
+ '''
+ Return a dictionary of the secret values we need to store.
+ '''
+ pre = {}
+ sk = self.hex_sk()
+ pk = self.hex_pk()
+ vk = self.hex_vk()
+ seed = self.hex_seed()
+ if sk:
+ pre['priv'] = sk.decode('utf-8')
+ if pk:
+ pre['pub'] = pk.decode('utf-8')
+ if vk:
+ pre['verify'] = vk.decode('utf-8')
+ if seed:
+ pre['sign'] = seed.decode('utf-8')
+
+ return pre
+
+ def save(self, path, serial='json'):
+ '''
+ Safely save keys with perms of 0400
+ '''
+ pre = self.for_json()
+
+ if serial == 'msgpack':
+ import msgpack
+ packaged = msgpack.dumps(pre)
+ elif serial == 'json':
+ import json
+ packaged = json.dumps(pre)
+
+ perm_other = stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH
+ perm_group = stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
+
+ cumask = os.umask(perm_other | perm_group)
+ with open(path, 'w+') as fp_:
+ fp_.write(packaged)
+ os.umask(cumask)
diff --git a/libnacl/blake.py b/libnacl/blake.py
new file mode 100644
index 0000000..8da7d69
--- /dev/null
+++ b/libnacl/blake.py
@@ -0,0 +1,44 @@
+'''
+Mimic very closely the python hashlib classes for blake2b
+
+NOTE:
+ This class does not yet implement streaming the msg into the
+ hash function via the update method
+'''
+
+# Import python libs
+import binascii
+
+# Import libnacl libs
+import libnacl
+
+
+class Blake2b(object):
+ '''
+ Manage a Blake2b hash
+ '''
+ def __init__(self, msg, key=None):
+ self.msg = msg
+ self.key = key
+ self.raw_digest = libnacl.crypto_generichash(msg, key)
+ self.digest_size = len(self.raw_digest)
+
+ def digest(self):
+ '''
+ Return the digest of the string
+ '''
+ return self.raw_digest
+
+ def hexdigest(self):
+ '''
+ Return the hex digest of the string
+ '''
+ return binascii.hexlify(self.raw_digest)
+
+
+def blake2b(msg, key=None):
+ '''
+ Create and return a Blake2b object to mimic the behavior of the python
+ hashlib functions
+ '''
+ return Blake2b(msg, key)
diff --git a/libnacl/dual.py b/libnacl/dual.py
new file mode 100644
index 0000000..c48fb67
--- /dev/null
+++ b/libnacl/dual.py
@@ -0,0 +1,34 @@
+'''
+The dual key system allows for the creation of keypairs that contain both
+cryptographic and signing keys
+'''
+# import libnacl libs
+import libnacl
+import libnacl.base
+import libnacl.public
+import libnacl.sign
+
+
+class DualSecret(libnacl.base.BaseKey):
+ '''
+ Manage crypt and sign keys in one object
+ '''
+ def __init__(self, crypt=None, sign=None):
+ self.crypt = libnacl.public.SecretKey(crypt)
+ self.signer = libnacl.sign.Signer(sign)
+ self.sk = self.crypt.sk
+ self.seed = self.signer.seed
+ self.pk = self.crypt.pk
+ self.vk = self.signer.vk
+
+ def sign(self, msg):
+ '''
+ Sign the given message
+ '''
+ return self.signer.sign(msg)
+
+ def signature(self, msg):
+ '''
+ Return just the signature for the message
+ '''
+ return self.signer.signature(msg)
diff --git a/libnacl/encode.py b/libnacl/encode.py
new file mode 100644
index 0000000..efbfe99
--- /dev/null
+++ b/libnacl/encode.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+'''
+Build in routines and classes to simplify encoding routines
+'''
+# Import python libs
+import base64
+import binascii
+
+
+def hex_encode(data):
+ '''
+ Hex encode data
+ '''
+ return binascii.hexlify(data)
+
+
+def hex_decode(data):
+ '''
+ Hex decode data
+ '''
+ return binascii.unhexlify(data)
+
+
+def base16_encode(data):
+ '''
+ Base32 encode data
+ '''
+ return base64.b16encode(data)
+
+
+def base16_decode(data):
+ '''
+ Base16 decode data
+ '''
+ return base64.b16decode(data)
+
+
+def base32_encode(data):
+ '''
+ Base16 encode data
+ '''
+ return base64.b32encode(data)
+
+
+def base32_decode(data):
+ '''
+ Base32 decode data
+ '''
+ return base64.b32decode(data)
+
+
+def base64_encode(data):
+ '''
+ Base16 encode data
+ '''
+ return base64.b64encode(data)
+
+
+def base64_decode(data):
+ '''
+ Base32 decode data
+ '''
+ return base64.b64decode(data)
diff --git a/libnacl/public.py b/libnacl/public.py
new file mode 100644
index 0000000..bac8609
--- /dev/null
+++ b/libnacl/public.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+'''
+High level classes and routines around public key encryption and decryption
+'''
+# import libnacl libs
+import libnacl
+import libnacl.utils
+import libnacl.encode
+import libnacl.dual
+import libnacl.base
+
+
+class PublicKey(libnacl.base.BaseKey):
+ '''
+ This class is used to manage public keys
+ '''
+ def __init__(self, pk):
+ if len(pk) == libnacl.crypto_box_PUBLICKEYBYTES:
+ self.pk = pk
+ else:
+ raise ValueError('Passed in invalid public key')
+
+ def __eq__(self, other):
+ if isinstance(other, self.__class__):
+ return self.pk == other.pk
+ else:
+ return False
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash(self.pk)
+
+
+class SecretKey(libnacl.base.BaseKey):
+ '''
+ This class is used to manage keypairs
+ '''
+ def __init__(self, sk=None):
+ '''
+ If a secret key is not passed in then it will be generated
+ '''
+ if sk is None:
+ self.pk, self.sk = libnacl.crypto_box_keypair()
+ elif len(sk) == libnacl.crypto_box_SECRETKEYBYTES:
+ self.sk = sk
+ self.pk = libnacl.crypto_scalarmult_base(sk)
+ else:
+ raise ValueError('Passed in invalid secret key')
+
+ def __eq__(self, other):
+ if isinstance(other, self.__class__):
+ return self.sk == other.sk and self.pk == other.pk
+ else:
+ return False
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash((self.sk, self.pk))
+
+
+class Box(object):
+ '''
+ TheBox class is used to create cryptographic boxes and unpack
+ cryptographic boxes
+ '''
+ def __init__(self, sk, pk):
+ if isinstance(sk, (SecretKey, libnacl.dual.DualSecret)):
+ sk = sk.sk
+ if isinstance(pk, (SecretKey, libnacl.dual.DualSecret)):
+ raise ValueError('Passed in secret key as public key')
+ if isinstance(pk, PublicKey):
+ pk = pk.pk
+ if pk and sk:
+ self._k = libnacl.crypto_box_beforenm(pk, sk)
+
+ def encrypt(self, msg, nonce=None, pack_nonce=True):
+ '''
+ Encrypt the given message with the given nonce, if the nonce is not
+ provided it will be generated from the libnacl.utils.rand_nonce
+ function
+ '''
+ if nonce is None:
+ nonce = libnacl.utils.rand_nonce()
+ elif len(nonce) != libnacl.crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce size')
+ ctxt = libnacl.crypto_box_afternm(msg, nonce, self._k)
+ if pack_nonce:
+ return nonce + ctxt
+ else:
+ return nonce, ctxt
+
+ def decrypt(self, ctxt, nonce=None):
+ '''
+ Decrypt the given message, if a nonce is passed in attempt to decrypt
+ it with the given nonce, otherwise assum that the nonce is attached
+ to the message
+ '''
+ if nonce is None:
+ nonce = ctxt[:libnacl.crypto_box_NONCEBYTES]
+ ctxt = ctxt[libnacl.crypto_box_NONCEBYTES:]
+ elif len(nonce) != libnacl.crypto_box_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ msg = libnacl.crypto_box_open_afternm(ctxt, nonce, self._k)
+ return msg
diff --git a/libnacl/sealed.py b/libnacl/sealed.py
new file mode 100644
index 0000000..7ce75f6
--- /dev/null
+++ b/libnacl/sealed.py
@@ -0,0 +1,33 @@
+import libnacl
+import libnacl.public
+import libnacl.dual
+
+class SealedBox(object):
+ '''
+ Sealed box is a variant of Box that does not authenticate sender.
+ '''
+
+ def __init__(self, pk, sk=None):
+ self.pk = pk
+ self.sk = sk
+
+ if isinstance(pk, (libnacl.public.SecretKey, libnacl.dual.DualSecret)):
+ self.pk = pk.pk
+ self.sk = pk.sk
+
+ if isinstance(pk, libnacl.public.PublicKey):
+ self.pk = pk.pk
+
+ def encrypt(self, msg):
+ '''
+ Encrypt the given message using the receiver's public key
+ '''
+ return libnacl.crypto_box_seal(msg, self.pk)
+
+ def decrypt(self, msg):
+ '''
+ Decrypt the given message using the receiver's public and private key
+ '''
+ if not self.sk:
+ raise ValueError('Secret key is not set')
+ return libnacl.crypto_box_seal_open(msg, self.pk, self.sk)
diff --git a/libnacl/secret.py b/libnacl/secret.py
new file mode 100644
index 0000000..278f4ee
--- /dev/null
+++ b/libnacl/secret.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+'''
+Utilities to make secret box encryption simple
+'''
+# Import libnacl
+import libnacl
+import libnacl.utils
+import libnacl.base
+
+
+class SecretBox(libnacl.base.BaseKey):
+ '''
+ Manage symetric encryption using the salsa20 algorithm
+ '''
+ def __init__(self, key=None):
+ if key is None:
+ key = libnacl.utils.salsa_key()
+ if len(key) != libnacl.crypto_secretbox_KEYBYTES:
+ raise ValueError('Invalid key')
+ self.sk = key
+
+ def encrypt(self, msg, nonce=None, pack_nonce=True):
+ '''
+ Encrypt the given message. If a nonce is not given it will be
+ generated via the rand_nonce function
+ '''
+ if nonce is None:
+ nonce = libnacl.utils.rand_nonce()
+ if len(nonce) != libnacl.crypto_secretbox_NONCEBYTES:
+ raise ValueError('Invalid nonce size')
+ ctxt = libnacl.crypto_secretbox(msg, nonce, self.sk)
+ if pack_nonce:
+ return nonce + ctxt
+ else:
+ return nonce, ctxt
+
+ def decrypt(self, ctxt, nonce=None):
+ '''
+ Decrypt the given message, if no nonce is given the nonce will be
+ extracted from the message
+ '''
+ if nonce is None:
+ nonce = ctxt[:libnacl.crypto_secretbox_NONCEBYTES]
+ ctxt = ctxt[libnacl.crypto_secretbox_NONCEBYTES:]
+ if len(nonce) != libnacl.crypto_secretbox_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ return libnacl.crypto_secretbox_open(ctxt, nonce, self.sk)
diff --git a/libnacl/secret_easy.py b/libnacl/secret_easy.py
new file mode 100644
index 0000000..15dea1b
--- /dev/null
+++ b/libnacl/secret_easy.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+'''
+Utilities to make secret box easy encryption simple
+'''
+# Import libnacl
+import libnacl
+import libnacl.utils
+import libnacl.base
+
+
+class SecretBoxEasy(libnacl.base.BaseKey):
+ '''
+ Manage symetric encryption using the salsa20 algorithm
+ '''
+ def __init__(self, key=None):
+ if key is None:
+ key = libnacl.utils.salsa_key()
+ if len(key) != libnacl.crypto_secretbox_KEYBYTES:
+ raise ValueError('Invalid key')
+ self.sk = key
+
+ def encrypt(self, msg, nonce=None, pack_nonce=True):
+ '''
+ Encrypt the given message. If a nonce is not given it will be
+ generated via the rand_nonce function
+ '''
+ if nonce is None:
+ nonce = libnacl.utils.rand_nonce()
+ if len(nonce) != libnacl.crypto_secretbox_NONCEBYTES:
+ raise ValueError('Invalid nonce size')
+ ctxt = libnacl.crypto_secretbox_easy(msg, nonce, self.sk)
+ if pack_nonce:
+ return nonce + ctxt
+ else:
+ return nonce, ctxt
+
+ def decrypt(self, ctxt, nonce=None):
+ '''
+ Decrypt the given message, if no nonce is given the nonce will be
+ extracted from the message
+ '''
+ if nonce is None:
+ nonce = ctxt[:libnacl.crypto_secretbox_NONCEBYTES]
+ ctxt = ctxt[libnacl.crypto_secretbox_NONCEBYTES:]
+ if len(nonce) != libnacl.crypto_secretbox_NONCEBYTES:
+ raise ValueError('Invalid nonce')
+ return libnacl.crypto_secretbox_open_easy(ctxt, nonce, self.sk)
diff --git a/libnacl/sign.py b/libnacl/sign.py
new file mode 100644
index 0000000..9dbd83f
--- /dev/null
+++ b/libnacl/sign.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+'''
+High level routines to maintain signing keys and to sign and verify messages
+'''
+# Import libancl libs
+import libnacl
+import libnacl.base
+import libnacl.encode
+
+
+class Signer(libnacl.base.BaseKey):
+ '''
+ The tools needed to sign messages
+ '''
+ def __init__(self, seed=None):
+ '''
+ Create a signing key, if not seed it supplied a keypair is generated
+ '''
+ if seed:
+ if len(seed) != libnacl.crypto_sign_SEEDBYTES:
+ raise ValueError('Invalid seed bytes')
+ self.vk, self.sk = libnacl.crypto_sign_seed_keypair(seed)
+ else:
+ seed = libnacl.randombytes(libnacl.crypto_sign_SEEDBYTES)
+ self.vk, self.sk = libnacl.crypto_sign_seed_keypair(seed)
+ self.seed = seed
+
+ def sign(self, msg):
+ '''
+ Sign the given message with this key
+ '''
+ return libnacl.crypto_sign(msg, self.sk)
+
+ def signature(self, msg):
+ '''
+ Return just the signature for the message
+ '''
+ return libnacl.crypto_sign(msg, self.sk)[:libnacl.crypto_sign_BYTES]
+
+
+class Verifier(libnacl.base.BaseKey):
+ '''
+ Verify signed messages
+ '''
+ def __init__(self, vk_hex):
+ '''
+ Create a verification key from a hex encoded vkey
+ '''
+ self.vk = libnacl.encode.hex_decode(vk_hex)
+
+ def verify(self, msg):
+ '''
+ Verify the message with tis key
+ '''
+ return libnacl.crypto_sign_open(msg, self.vk)
diff --git a/libnacl/utils.py b/libnacl/utils.py
new file mode 100644
index 0000000..e06e078
--- /dev/null
+++ b/libnacl/utils.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+
+import struct
+import sys
+import time
+
+# Import nacl libs
+import libnacl
+import libnacl.encode
+import libnacl.secret
+import libnacl.public
+import libnacl.sign
+import libnacl.dual
+
+
+def load_key(path_or_file, serial='json'):
+ '''
+ Read in a key from a file and return the applicable key object based on
+ the contents of the file
+ '''
+ if hasattr(path_or_file, 'read'):
+ stream = path_or_file
+ else:
+ if serial == 'json':
+ stream = open(path_or_file, 'r')
+ else:
+ stream = open(path_or_file, 'rb')
+
+ try:
+ if serial == 'msgpack':
+ import msgpack
+ key_data = msgpack.load(stream)
+ elif serial == 'json':
+ import json
+ if sys.version_info[0] >= 3:
+ key_data = json.loads(stream.read())
+ else:
+ key_data = json.loads(stream.read(), encoding='UTF-8')
+ finally:
+ if stream != path_or_file:
+ stream.close()
+
+ if 'priv' in key_data and 'sign' in key_data and 'pub' in key_data:
+ return libnacl.dual.DualSecret(
+ libnacl.encode.hex_decode(key_data['priv']),
+ libnacl.encode.hex_decode(key_data['sign']))
+ elif 'priv' in key_data and 'pub' in key_data:
+ return libnacl.public.SecretKey(
+ libnacl.encode.hex_decode(key_data['priv']))
+ elif 'sign' in key_data:
+ return libnacl.sign.Signer(
+ libnacl.encode.hex_decode(key_data['sign']))
+ elif 'pub' in key_data:
+ return libnacl.public.PublicKey(
+ libnacl.encode.hex_decode(key_data['pub']))
+ elif 'verify' in key_data:
+ return libnacl.sign.Verifier(key_data['verify'])
+ elif 'priv' in key_data:
+ return libnacl.secret.SecretBox(
+ libnacl.encode.hex_decode(key_data['priv']))
+ raise ValueError('Found no key data')
+
+
+def salsa_key():
+ '''
+ Generates a salsa2020 key
+ '''
+ return libnacl.randombytes(libnacl.crypto_secretbox_KEYBYTES)
+
+
+def aead_key():
+ '''
+ Generates an AEAD key (both implementations use the same size)
+ '''
+ return libnacl.randombytes(libnacl.crypto_aead_aes256gcm_KEYBYTES)
+
+
+def rand_aead_nonce():
+ '''
+ Generates and returns a random bytestring of the size defined in libsodium
+ as crypto_aead_aes256gcm_NPUBBYTES and crypto_aead_chacha20poly1305_ietf_NPUBBYTES
+ '''
+ return libnacl.randombytes(libnacl.crypto_aead_aes256gcm_NPUBBYTES)
+
+
+def rand_nonce():
+ '''
+ Generates and returns a random bytestring of the size defined in libsodium
+ as crypto_box_NONCEBYTES
+ '''
+ return libnacl.randombytes(libnacl.crypto_box_NONCEBYTES)
+
+
+def time_nonce():
+ '''
+ Generates and returns a nonce as in rand_nonce() but using a timestamp for the first 8 bytes.
+
+ This function now exists mostly for backwards compatibility, as rand_nonce() is usually preferred.
+ '''
+ nonce = rand_nonce()
+ return (struct.pack('=d', time.time()) + nonce)[:len(nonce)]
diff --git a/libnacl/version.py b/libnacl/version.py
new file mode 100644
index 0000000..baca2f5
--- /dev/null
+++ b/libnacl/version.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+version = "1.8.0"
diff --git a/pkg/rpm/python-libnacl.spec b/pkg/rpm/python-libnacl.spec
new file mode 100644
index 0000000..6b991c4
--- /dev/null
+++ b/pkg/rpm/python-libnacl.spec
@@ -0,0 +1,153 @@
+%if 0%{?fedora} > 12 || 0%{?rhel} > 6
+%global with_python3 1
+%endif
+
+%if 0%{?rhel} == 5
+%global pybasever 2.6
+%endif
+
+%{!?__python2: %global __python2 /usr/bin/python%{?pybasever}}
+%{!?python2_sitearch: %global python2_sitearch %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
+%{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
+
+%global srcname libnacl
+
+Name: python-%{srcname}
+Version: 1.4.3
+Release: 1%{?dist}
+Summary: Python bindings for libsodium based on ctypes
+
+Group: Development/Libraries
+License: ASL 2.0
+URL: https://github.com/saltstack/libnacl
+Source0: https://pypi.python.org/packages/source/l/%{srcname}/%{srcname}-%{version}.tar.gz
+
+BuildRoot: %{_tmppath}/%{srcname}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildArch: noarch
+
+BuildRequires: libsodium
+Requires: libsodium >= 0.5.0
+
+%if ! (0%{?rhel} == 5)
+BuildRequires: python
+BuildRequires: python-devel
+BuildRequires: python-setuptools
+%endif
+
+%if 0%{?with_python3}
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+%endif
+
+%description
+This library is used to gain direct access to the functions exposed by Daniel
+J. Bernstein's nacl library via libsodium. It has been constructed
+to maintain extensive documentation on how to use nacl as well as being
+completely portable. The file in libnacl/__init__.py can be pulled out and
+placed directly in any project to give a single file binding to all of nacl.
+
+This is the Python 2 build of the module.
+
+%if 0%{?with_python3}
+%package -n python3-%{srcname}
+Summary: Python bindings for libsodium based on ctypes
+Group: Development/Libraries
+Requires: libsodium
+
+%description -n python3-%{srcname}
+This library is used to gain direct access to the functions exposed by Daniel
+J. Bernstein's nacl library via libsodium. It has been constructed
+to maintain extensive documentation on how to use nacl as well as being
+completely portable. The file in libnacl/__init__.py can be pulled out and
+placed directly in any project to give a single file binding to all of nacl.
+
+This is the Python 3 build of the module.
+%endif
+
+%if 0%{?rhel} == 5
+%package -n python26-%{srcname}
+Summary: Python bindings for libsodium based on ctypes
+Group: Development/Libraries
+BuildRequires: python26
+BuildRequires: libsodium
+BuildRequires: python26-devel
+Requires: python26
+Requires: libsodium
+
+%description -n python26-%{srcname}
+This library is used to gain direct access to the functions exposed by Daniel
+J. Bernstein's nacl library via libsodium. It has been constructed
+to maintain extensive documentation on how to use nacl as well as being
+completely portable. The file in libnacl/__init__.py can be pulled out and
+placed directly in any project to give a single file binding to all of nacl.
+
+This is the Python 2 build of the module.
+%endif
+
+%prep
+%setup -q -n %{srcname}-%{version}
+
+%if 0%{?with_python3}
+rm -rf %{py3dir}
+cp -a . %{py3dir}
+%endif
+
+%build
+%{__python2} setup.py build
+
+%if 0%{?with_python3}
+pushd %{py3dir}
+%{__python3} setup.py build
+popd
+%endif
+
+%install
+rm -rf %{buildroot}
+%{__python2} setup.py install --skip-build --root %{buildroot}
+
+%if 0%{?with_python3}
+pushd %{py3dir}
+%{__python3} setup.py install --skip-build --root %{buildroot}
+popd
+%endif
+
+%clean
+rm -rf %{buildroot}
+
+%if 0%{?rhel} == 5
+%files -n python26-%{srcname}
+%defattr(-,root,root,-)
+%{python2_sitelib}/*
+%else
+%files
+%defattr(-,root,root,-)
+%{python2_sitelib}/*
+%endif
+
+%if 0%{?with_python3}
+%files -n python3-%{srcname}
+%defattr(-,root,root,-)
+%{python3_sitelib}/*
+%endif
+
+%changelog
+* Thu Sep 4 2014 Erik Johnson <erik@saltstack.com> - 1.3.5-1
+- Updated to 1.3.5
+
+* Fri Aug 22 2014 Erik Johnson <erik@saltstack.com> - 1.3.3-1
+- Updated to 1.3.3
+
+* Fri Aug 8 2014 Erik Johnson <erik@saltstack.com> - 1.3.2-1
+- Updated to 1.3.2
+
+* Fri Aug 8 2014 Erik Johnson <erik@saltstack.com> - 1.3.1-1
+- Updated to 1.3.1
+
+* Thu Aug 7 2014 Erik Johnson <erik@saltstack.com> - 1.3.0-1
+- Updated to 1.3.0
+
+* Fri Jun 20 2014 Erik Johnson <erik@saltstack.com> - 1.1.0-1
+- Updated to 1.1.0
+
+* Fri Jun 20 2014 Erik Johnson <erik@saltstack.com> - 1.0.0-1
+- Initial build
diff --git a/pkg/suse/python-libnacl.changes b/pkg/suse/python-libnacl.changes
new file mode 100644
index 0000000..4740656
--- /dev/null
+++ b/pkg/suse/python-libnacl.changes
@@ -0,0 +1,15 @@
+-------------------------------------------------------------------
+Wed Jul 2 18:28:08 UTC 2014 - aboe76@gmail.com
+
+- Updated to 1.1.0
+
+-------------------------------------------------------------------
+Fri Jun 20 15:10:52 UTC 2014 - aboe76@gmail.com
+
+- Simplified BuildRequirements to libsodium-devel
+
+-------------------------------------------------------------------
+Mon Jun 9 10:53:12 UTC 2014 - aboe76@gmail.com
+
+- initial package
+
diff --git a/pkg/suse/python-libnacl.spec b/pkg/suse/python-libnacl.spec
new file mode 100644
index 0000000..2dc9810
--- /dev/null
+++ b/pkg/suse/python-libnacl.spec
@@ -0,0 +1,59 @@
+#
+# spec file for package python-libnacl
+#
+# Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
+#
+
+Name: python-libnacl
+Version: 1.4.3
+Release: 0
+License: Apache-2.0
+Summary: Python bindings for libsodium based on ctypes
+Url: https://github.com/saltstack/libnacl
+Group: Development/Languages/Python
+Source0: https://pypi.python.org/packages/source/l/libnacl/libnacl-%{version}.tar.gz
+BuildRoot: %{_tmppath}/libnacl-%{version}-build
+
+BuildRequires: python-setuptools
+BuildRequires: python-devel
+BuildRequires: libsodium-devel
+BuildRequires: fdupes
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+%if 0%{?suse_version} && 0%{?suse_version} <= 1110
+%{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+%else
+BuildArch: noarch
+%endif
+
+%description
+This library is used to gain direct access to the functions exposed by Daniel J. Bernstein's nacl library via libsodium.
+It has been constructed to maintain extensive documentation on how to use nacl as well as being completely portable. The file
+in libnacl/__init__.py can be pulled out and placed directly in any project to give a single file binding to all of nacl.
+
+%prep
+%setup -q -n libnacl-%{version}
+
+%build
+python setup.py build
+
+%install
+python setup.py install --prefix=%{_prefix} --root=%{buildroot} --optimize=1
+%fdupes %{buildroot}%{_prefix}
+
+%files
+%defattr(-,root,root)
+%{python_sitelib}/*
+
+%changelog \ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..adf5ed7
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,7 @@
+[bdist_wheel]
+universal = 1
+
+[egg_info]
+tag_build =
+tag_date = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..79dda9c
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from setuptools import setup
+
+NAME = "libnacl"
+DESC = "Python bindings for libsodium based on ctypes"
+
+# Version info -- read without importing
+_locals = {}
+with open("libnacl/version.py") as fp:
+ exec(fp.read(), None, _locals)
+VERSION = _locals["version"]
+
+setup(
+ name=NAME,
+ version=VERSION,
+ description=DESC,
+ author="Thomas S Hatch",
+ author_email="thatch@saltstack.com",
+ url="https://libnacl.readthedocs.org/",
+ classifiers=[
+ "Operating System :: OS Independent",
+ "License :: OSI Approved :: Apache Software License",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2.6",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "Topic :: Security :: Cryptography",
+ ],
+ packages=["libnacl"],
+)
diff --git a/tests/runtests.py b/tests/runtests.py
new file mode 100644
index 0000000..447c0b3
--- /dev/null
+++ b/tests/runtests.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Import python libs
+import os
+import sys
+import unittest
+
+NACL_ROOT = os.path.abspath(os.path.dirname(os.path.abspath(os.path.dirname(__file__))))
+UNIT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), 'unit'))
+
+sys.path.insert(0, NACL_ROOT)
+
+
+def run_suite(path=UNIT_ROOT):
+ '''
+ Execute the unttest suite
+ '''
+ loader = unittest.TestLoader()
+ tests = loader.discover(path)
+ unittest.TextTestRunner(verbosity=2).run(tests)
+
+
+
+if __name__ == '__main__':
+ run_suite()
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/tests/unit/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/tests/unit/test_aead.py b/tests/unit/test_aead.py
new file mode 100644
index 0000000..b6dfa05
--- /dev/null
+++ b/tests/unit/test_aead.py
@@ -0,0 +1,41 @@
+# Import libnacl libs
+import libnacl.aead
+# Import python libs
+import unittest
+
+class TestAEAD(unittest.TestCase):
+ '''
+ '''
+ @unittest.skipUnless(libnacl.HAS_AEAD_AES256GCM, 'AES256-GCM AEAD not available')
+ def test_gcm_aead(self):
+ msg = b"You've got two empty halves of coconuts and your bangin' 'em together."
+ aad = b'\x00\x11\x22\x33'
+ box = libnacl.aead.AEAD().useAESGCM()
+ ctxt = box.encrypt(msg, aad)
+ self.assertNotEqual(msg, ctxt)
+
+ box2 = libnacl.aead.AEAD(box.sk).useAESGCM()
+ clear1 = box.decrypt(ctxt, len(aad))
+ self.assertEqual(msg, clear1)
+ clear2 = box2.decrypt(ctxt, len(aad))
+ self.assertEqual(clear1, clear2)
+ ctxt2 = box2.encrypt(msg, aad)
+ clear3 = box.decrypt(ctxt2, len(aad))
+ self.assertEqual(clear3, msg)
+
+ @unittest.skipUnless(libnacl.HAS_AEAD_CHACHA20POLY1305_IETF, 'IETF variant of ChaCha20Poly1305 AEAD not available')
+ def test_ietf_aead(self):
+ msg = b"Our King? Well i didn't vote for you!!"
+ aad = b'\x00\x11\x22\x33'
+ box = libnacl.aead.AEAD()
+ ctxt = box.encrypt(msg, aad)
+ self.assertNotEqual(msg, ctxt)
+
+ box2 = libnacl.aead.AEAD(box.sk)
+ clear1 = box.decrypt(ctxt, len(aad))
+ self.assertEqual(msg, clear1)
+ clear2 = box2.decrypt(ctxt, len(aad))
+ self.assertEqual(clear1, clear2)
+ ctxt2 = box2.encrypt(msg, aad)
+ clear3 = box.decrypt(ctxt2, len(aad))
+ self.assertEqual(clear3, msg)
diff --git a/tests/unit/test_auth_verify.py b/tests/unit/test_auth_verify.py
new file mode 100644
index 0000000..919011b
--- /dev/null
+++ b/tests/unit/test_auth_verify.py
@@ -0,0 +1,102 @@
+# Import nacl libs
+import libnacl
+import libnacl.utils
+
+# Import python libs
+import unittest
+
+
+class TestAuthVerify(unittest.TestCase):
+ '''
+ Test onetimeauth functions
+ '''
+ def test_auth_verify(self):
+ msg = b'Anybody can invent a cryptosystem he cannot break himself. Except Bruce Schneier.'
+ key1 = libnacl.utils.salsa_key()
+ key2 = libnacl.utils.salsa_key()
+
+ sig1 = libnacl.crypto_auth(msg, key1)
+ sig2 = libnacl.crypto_auth(msg, key2)
+
+ self.assertTrue(libnacl.crypto_auth_verify(sig1, msg, key1))
+ self.assertTrue(libnacl.crypto_auth_verify(sig2, msg, key2))
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_auth_verify(sig1, msg, key2)
+ self.assertTrue('Failed to auth msg' in context.exception.args)
+
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_auth_verify(sig2, msg, key1)
+ self.assertTrue('Failed to auth msg' in context.exception.args)
+
+ def test_onetimeauth_verify(self):
+ self.assertEqual("poly1305", libnacl.crypto_onetimeauth_primitive())
+
+ msg = b'Anybody can invent a cryptosystem he cannot break himself. Except Bruce Schneier.'
+ key1 = libnacl.randombytes(libnacl.crypto_onetimeauth_KEYBYTES)
+ key2 = libnacl.randombytes(libnacl.crypto_onetimeauth_KEYBYTES)
+
+ sig1 = libnacl.crypto_onetimeauth(msg, key1)
+ sig2 = libnacl.crypto_onetimeauth(msg, key2)
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_onetimeauth(msg, b'too_short')
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_onetimeauth_verify(sig1, msg, b'too_short')
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_onetimeauth_verify(b'too_short', msg, key1)
+
+ self.assertTrue(libnacl.crypto_onetimeauth_verify(sig1, msg, key1))
+ self.assertTrue(libnacl.crypto_onetimeauth_verify(sig2, msg, key2))
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_onetimeauth_verify(sig1, msg, key2)
+ self.assertTrue('Failed to auth message' in context.exception.args)
+
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_onetimeauth_verify(sig2, msg, key1)
+ self.assertTrue('Failed to auth message' in context.exception.args)
+
+ def test_auth_rejects_wrong_lengths(self):
+ msg = b'Time is an illusion. Lunchtime doubly so.'
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_auth(msg, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
+
+ def test_auth_verify_rejects_wrong_key_lengths(self):
+ msg = b"I'd take the awe of understanding over the awe of ignorance any day."
+ good_key = b'This valid key is 32 bytes long.'
+ good_token = b'This token is likewise also 32B.'
+
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_auth_verify(good_token, msg, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
+
+ for bad_token in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_auth_verify(bad_token, msg, good_key)
+ self.assertEqual(context.exception.args, ('Invalid authenticator',))
+
+ def test_onetimeauth_rejects_wrong_lengths(self):
+ msg = b"Are the most dangerous creatures the ones that use doors or the ones that don't?"
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_onetimeauth(msg, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
+
+ def test_onetimeauth_verify_rejects_wrong_key_lengths(self):
+ msg = b"Of all the dogs I've known in my life, I've never seen a better driver."
+ good_key = b'This valid key is 32 bytes long.'
+ good_token = b'1time tokens=16B'
+
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_onetimeauth_verify(good_token, msg, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
+
+ for bad_token in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_onetimeauth_verify(bad_token, msg, good_key)
+ self.assertEqual(context.exception.args, ('Invalid authenticator',))
diff --git a/tests/unit/test_blake.py b/tests/unit/test_blake.py
new file mode 100644
index 0000000..5dfff1d
--- /dev/null
+++ b/tests/unit/test_blake.py
@@ -0,0 +1,39 @@
+# Import nacl libs
+import libnacl.blake
+
+# Import python libs
+import unittest
+
+
+class TestBlake(unittest.TestCase):
+ '''
+ Test sign functions
+ '''
+ def test_keyless_blake(self):
+ msg1 = b'Are you suggesting coconuts migrate?'
+ msg2 = b'Not at all, they could be carried.'
+ chash1 = libnacl.crypto_generichash(msg1)
+ chash2 = libnacl.crypto_generichash(msg2)
+ self.assertNotEqual(msg1, chash1)
+ self.assertNotEqual(msg2, chash2)
+ self.assertNotEqual(chash2, chash1)
+
+ def test_key_blake(self):
+ msg1 = b'Are you suggesting coconuts migrate?'
+ msg2 = b'Not at all, they could be carried.'
+ key1 = libnacl.utils.rand_nonce()
+ key2 = libnacl.utils.rand_nonce()
+ khash1_1 = libnacl.blake.Blake2b(msg1, key1).digest()
+ khash1_1_2 = libnacl.blake.Blake2b(msg1, key1).digest()
+ khash1_2 = libnacl.blake.Blake2b(msg1, key2).digest()
+ khash2_1 = libnacl.blake.blake2b(msg2, key1).digest()
+ khash2_2 = libnacl.blake.blake2b(msg2, key2).digest()
+ self.assertNotEqual(msg1, khash1_1)
+ self.assertNotEqual(msg1, khash1_2)
+ self.assertNotEqual(msg2, khash2_1)
+ self.assertNotEqual(msg2, khash2_2)
+ self.assertNotEqual(khash1_1, khash1_2)
+ self.assertNotEqual(khash2_1, khash2_2)
+ self.assertNotEqual(khash1_1, khash2_1)
+ self.assertNotEqual(khash1_2, khash2_2)
+ self.assertEqual(khash1_1, khash1_1_2)
diff --git a/tests/unit/test_dual.py b/tests/unit/test_dual.py
new file mode 100644
index 0000000..d300c9e
--- /dev/null
+++ b/tests/unit/test_dual.py
@@ -0,0 +1,54 @@
+# Import libnacl libs
+import libnacl.public
+import libnacl.dual
+
+# Import python libs
+import unittest
+
+class TestDual(unittest.TestCase):
+ '''
+ '''
+ def test_secretkey(self):
+ '''
+ '''
+ msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.'
+ bob = libnacl.dual.DualSecret()
+ alice = libnacl.dual.DualSecret()
+ bob_box = libnacl.public.Box(bob.sk, alice.pk)
+ alice_box = libnacl.public.Box(alice.sk, bob.pk)
+ bob_ctxt = bob_box.encrypt(msg)
+ self.assertNotEqual(msg, bob_ctxt)
+ bclear = alice_box.decrypt(bob_ctxt)
+ self.assertEqual(msg, bclear)
+ alice_ctxt = alice_box.encrypt(msg)
+ self.assertNotEqual(msg, alice_ctxt)
+ aclear = alice_box.decrypt(alice_ctxt)
+ self.assertEqual(msg, aclear)
+ self.assertNotEqual(bob_ctxt, alice_ctxt)
+
+ def test_publickey(self):
+ '''
+ '''
+ msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.'
+ bob = libnacl.dual.DualSecret()
+ alice = libnacl.dual.DualSecret()
+ alice_pk = libnacl.public.PublicKey(alice.pk)
+ bob_box = libnacl.public.Box(bob.sk, alice_pk)
+ alice_box = libnacl.public.Box(alice.sk, bob.pk)
+ bob_ctxt = bob_box.encrypt(msg)
+ self.assertNotEqual(msg, bob_ctxt)
+ bclear = alice_box.decrypt(bob_ctxt)
+ self.assertEqual(msg, bclear)
+
+ def test_sign(self):
+ msg = (b'Well, that\'s no ordinary rabbit. That\'s the most foul, '
+ b'cruel, and bad-tempered rodent you ever set eyes on.')
+ signer = libnacl.dual.DualSecret()
+ signed = signer.sign(msg)
+ signature = signer.signature(msg)
+ self.assertNotEqual(msg, signed)
+ veri = libnacl.sign.Verifier(signer.hex_vk())
+ verified = veri.verify(signed)
+ verified2 = veri.verify(signature + msg)
+ self.assertEqual(verified, msg)
+ self.assertEqual(verified2, msg)
diff --git a/tests/unit/test_public.py b/tests/unit/test_public.py
new file mode 100644
index 0000000..61de5a9
--- /dev/null
+++ b/tests/unit/test_public.py
@@ -0,0 +1,41 @@
+# Import libnacl libs
+import libnacl.public
+
+# Import python libs
+import unittest
+
+class TestPublic(unittest.TestCase):
+ '''
+ '''
+ def test_secretkey(self):
+ '''
+ '''
+ msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.'
+ bob = libnacl.public.SecretKey()
+ alice = libnacl.public.SecretKey()
+ bob_box = libnacl.public.Box(bob.sk, alice.pk)
+ alice_box = libnacl.public.Box(alice.sk, bob.pk)
+ bob_ctxt = bob_box.encrypt(msg)
+ self.assertNotEqual(msg, bob_ctxt)
+ bclear = alice_box.decrypt(bob_ctxt)
+ self.assertEqual(msg, bclear)
+ alice_ctxt = alice_box.encrypt(msg)
+ self.assertNotEqual(msg, alice_ctxt)
+ aclear = alice_box.decrypt(alice_ctxt)
+ self.assertEqual(msg, aclear)
+ self.assertNotEqual(bob_ctxt, alice_ctxt)
+
+ def test_publickey(self):
+ '''
+ '''
+ msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.'
+ bob = libnacl.public.SecretKey()
+ alice = libnacl.public.SecretKey()
+ alice_pk = libnacl.public.PublicKey(alice.pk)
+ bob_box = libnacl.public.Box(bob.sk, alice_pk)
+ alice_box = libnacl.public.Box(alice.sk, bob.pk)
+ bob_ctxt = bob_box.encrypt(msg)
+ self.assertNotEqual(msg, bob_ctxt)
+ bclear = alice_box.decrypt(bob_ctxt)
+ self.assertEqual(msg, bclear)
+
diff --git a/tests/unit/test_raw_auth_sym.py b/tests/unit/test_raw_auth_sym.py
new file mode 100644
index 0000000..8737f1a
--- /dev/null
+++ b/tests/unit/test_raw_auth_sym.py
@@ -0,0 +1,20 @@
+# Import nacl libs
+import libnacl
+import libnacl.utils
+
+# Import python libs
+import unittest
+
+
+class TestSecretBox(unittest.TestCase):
+ '''
+ Test sign functions
+ '''
+ def test_secret_box(self):
+ msg = b'Are you suggesting coconuts migrate?'
+ sk1 = libnacl.utils.salsa_key()
+ nonce1 = libnacl.utils.rand_nonce()
+ enc_msg = libnacl.crypto_secretbox(msg, nonce1, sk1)
+ self.assertNotEqual(msg, enc_msg)
+ clear_msg = libnacl.crypto_secretbox_open(enc_msg, nonce1, sk1)
+ self.assertEqual(msg, clear_msg)
diff --git a/tests/unit/test_raw_auth_sym_easy.py b/tests/unit/test_raw_auth_sym_easy.py
new file mode 100644
index 0000000..945e6b0
--- /dev/null
+++ b/tests/unit/test_raw_auth_sym_easy.py
@@ -0,0 +1,20 @@
+# Import nacl libs
+import libnacl
+import libnacl.utils
+
+# Import python libs
+import unittest
+
+
+class TestSecretBox(unittest.TestCase):
+ '''
+ Test sign functions
+ '''
+ def test_secret_box_easy(self):
+ msg = b'Are you suggesting coconuts migrate?'
+ sk1 = libnacl.utils.salsa_key()
+ nonce1 = libnacl.utils.rand_nonce()
+ enc_msg = libnacl.crypto_secretbox_easy(msg, nonce1, sk1)
+ self.assertNotEqual(msg, enc_msg)
+ clear_msg = libnacl.crypto_secretbox_open_easy(enc_msg, nonce1, sk1)
+ self.assertEqual(msg, clear_msg)
diff --git a/tests/unit/test_raw_generichash.py b/tests/unit/test_raw_generichash.py
new file mode 100644
index 0000000..befca93
--- /dev/null
+++ b/tests/unit/test_raw_generichash.py
@@ -0,0 +1,39 @@
+# Import nacl libs
+import libnacl
+
+# Import python libs
+import unittest
+
+
+class TestGenericHash(unittest.TestCase):
+ '''
+ Test sign functions
+ '''
+ def test_keyless_generichash(self):
+ msg1 = b'Are you suggesting coconuts migrate?'
+ msg2 = b'Not at all, they could be carried.'
+ chash1 = libnacl.crypto_generichash(msg1)
+ chash2 = libnacl.crypto_generichash(msg2)
+ self.assertNotEqual(msg1, chash1)
+ self.assertNotEqual(msg2, chash2)
+ self.assertNotEqual(chash2, chash1)
+
+ def test_key_generichash(self):
+ msg1 = b'Are you suggesting coconuts migrate?'
+ msg2 = b'Not at all, they could be carried.'
+ key1 = libnacl.utils.rand_nonce()
+ key2 = libnacl.utils.rand_nonce()
+ khash1_1 = libnacl.crypto_generichash(msg1, key1)
+ khash1_1_2 = libnacl.crypto_generichash(msg1, key1)
+ khash1_2 = libnacl.crypto_generichash(msg1, key2)
+ khash2_1 = libnacl.crypto_generichash(msg2, key1)
+ khash2_2 = libnacl.crypto_generichash(msg2, key2)
+ self.assertNotEqual(msg1, khash1_1)
+ self.assertNotEqual(msg1, khash1_2)
+ self.assertNotEqual(msg2, khash2_1)
+ self.assertNotEqual(msg2, khash2_2)
+ self.assertNotEqual(khash1_1, khash1_2)
+ self.assertNotEqual(khash2_1, khash2_2)
+ self.assertNotEqual(khash1_1, khash2_1)
+ self.assertNotEqual(khash1_2, khash2_2)
+ self.assertEqual(khash1_1, khash1_1_2)
diff --git a/tests/unit/test_raw_hash.py b/tests/unit/test_raw_hash.py
new file mode 100644
index 0000000..a6dd50d
--- /dev/null
+++ b/tests/unit/test_raw_hash.py
@@ -0,0 +1,29 @@
+# Import nacl libs
+import libnacl
+from hashlib import sha256, sha512
+
+# Import python libs
+import unittest
+
+
+class TestHash(unittest.TestCase):
+ """
+ Test sign functions
+ """
+ def test_hash(self):
+ msg1 = b'Are you suggesting coconuts migrate?'
+ msg2 = b'Not at all, they could be carried.'
+ chash1 = libnacl.crypto_hash(msg1)
+ chash2 = libnacl.crypto_hash(msg2)
+ self.assertNotEqual(msg1, chash1)
+ self.assertNotEqual(msg2, chash2)
+ self.assertNotEqual(chash2, chash1)
+
+ ref256 = sha256(msg1)
+ self.assertEqual(ref256.digest_size, libnacl.crypto_hash_sha256_BYTES)
+ self.assertEqual(ref256.digest(), libnacl.crypto_hash_sha256(msg1))
+
+ ref512 = sha512(msg1)
+ self.assertEqual(ref512.digest_size, libnacl.crypto_hash_sha512_BYTES)
+ self.assertEqual(ref512.digest(), libnacl.crypto_hash_sha512(msg1))
+
diff --git a/tests/unit/test_raw_public.py b/tests/unit/test_raw_public.py
new file mode 100644
index 0000000..85fd63f
--- /dev/null
+++ b/tests/unit/test_raw_public.py
@@ -0,0 +1,91 @@
+# Import libnacl libs
+import libnacl
+import libnacl.utils
+
+# Import python libs
+import unittest
+
+
+class TestPublic(unittest.TestCase):
+ '''
+ Test public functions
+ '''
+ def test_gen(self):
+ pk1, sk1 = libnacl.crypto_box_keypair()
+ pk2, sk2 = libnacl.crypto_box_keypair()
+ pk3, sk3 = libnacl.crypto_box_keypair()
+ self.assertEqual(len(pk1), libnacl.crypto_box_PUBLICKEYBYTES)
+ self.assertEqual(len(sk1), libnacl.crypto_box_PUBLICKEYBYTES)
+ self.assertEqual(len(pk2), libnacl.crypto_box_PUBLICKEYBYTES)
+ self.assertEqual(len(sk2), libnacl.crypto_box_PUBLICKEYBYTES)
+ self.assertEqual(len(pk3), libnacl.crypto_box_PUBLICKEYBYTES)
+ self.assertEqual(len(sk3), libnacl.crypto_box_PUBLICKEYBYTES)
+ self.assertNotEqual(pk1, sk1)
+ self.assertNotEqual(pk2, sk2)
+ self.assertNotEqual(pk3, sk3)
+ self.assertNotEqual(pk1, pk2)
+ self.assertNotEqual(pk1, pk3)
+ self.assertNotEqual(sk1, sk2)
+ self.assertNotEqual(sk2, sk3)
+
+ def test_box(self):
+ msg = b'Are you suggesting coconuts migrate?'
+ # run 1
+ nonce1 = libnacl.utils.rand_nonce()
+ pk1, sk1 = libnacl.crypto_box_keypair()
+ pk2, sk2 = libnacl.crypto_box_keypair()
+ enc_msg = libnacl.crypto_box(msg, nonce1, pk2, sk1)
+ self.assertNotEqual(msg, enc_msg)
+ clear_msg = libnacl.crypto_box_open(enc_msg, nonce1, pk1, sk2)
+ self.assertEqual(clear_msg, msg)
+ # run 2
+ nonce2 = libnacl.utils.rand_nonce()
+ pk3, sk3 = libnacl.crypto_box_keypair()
+ pk4, sk4 = libnacl.crypto_box_keypair()
+ enc_msg2 = libnacl.crypto_box(msg, nonce2, pk4, sk3)
+ self.assertNotEqual(msg, enc_msg2)
+ clear_msg2 = libnacl.crypto_box_open(enc_msg2, nonce2, pk3, sk4)
+ self.assertEqual(clear_msg2, msg)
+ # Check bits
+ self.assertNotEqual(nonce1, nonce2)
+ self.assertNotEqual(enc_msg, enc_msg2)
+
+ def test_boxnm(self):
+ msg = b'Are you suggesting coconuts migrate?'
+ # run 1
+ nonce1 = libnacl.utils.rand_nonce()
+ pk1, sk1 = libnacl.crypto_box_keypair()
+ pk2, sk2 = libnacl.crypto_box_keypair()
+ k1 = libnacl.crypto_box_beforenm(pk2, sk1)
+ k2 = libnacl.crypto_box_beforenm(pk1, sk2)
+ enc_msg = libnacl.crypto_box_afternm(msg, nonce1, k1)
+ self.assertNotEqual(msg, enc_msg)
+ clear_msg = libnacl.crypto_box_open_afternm(enc_msg, nonce1, k2)
+ self.assertEqual(clear_msg, msg)
+
+ def test_box_seal(self):
+ msg = b'Are you suggesting coconuts migrate?'
+ # run 1
+ pk, sk = libnacl.crypto_box_keypair()
+ enc_msg = libnacl.crypto_box_seal(msg, pk)
+ self.assertNotEqual(msg, enc_msg)
+ clear_msg = libnacl.crypto_box_seal_open(enc_msg, pk, sk)
+ self.assertEqual(clear_msg, msg)
+ # run 2
+ pk2, sk2 = libnacl.crypto_box_keypair()
+ enc_msg2 = libnacl.crypto_box_seal(msg, pk2)
+ self.assertNotEqual(msg, enc_msg2)
+ clear_msg2 = libnacl.crypto_box_seal_open(enc_msg2, pk2, sk2)
+ self.assertEqual(clear_msg2, msg)
+ # Check bits
+ self.assertNotEqual(enc_msg, enc_msg2)
+
+ def test_scalarmult_rejects_wrong_length(self):
+ good_key = b'This valid key is 32 bytes long.'
+
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_scalarmult_base(bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
+
+ self.assertEqual(libnacl.crypto_box_PUBLICKEYBYTES, len(libnacl.crypto_scalarmult_base(good_key)))
diff --git a/tests/unit/test_raw_random.py b/tests/unit/test_raw_random.py
new file mode 100644
index 0000000..8821731
--- /dev/null
+++ b/tests/unit/test_raw_random.py
@@ -0,0 +1,104 @@
+"""
+Basic tests for randombytes_* functions
+"""
+
+import libnacl
+import unittest
+
+
+class TestRandomBytes(unittest.TestCase):
+ def test_randombytes_random(self):
+ self.assertIsInstance(libnacl.randombytes_random(), int)
+
+ def test_randombytes_uniform(self):
+ self.assertIsInstance(libnacl.randombytes_uniform(200), int)
+
+ freq = {libnacl.randombytes_uniform(256): 1 for _ in range(65536)}
+
+ self.assertEqual(256, len(freq))
+ self.assertTrue(all(freq.values()))
+
+ def test_randombytes(self):
+ 'copied from libsodium default/randombytes.c'
+
+ data = libnacl.randombytes(65536)
+
+ freq = {x: 1 for x in data}
+
+ self.assertEqual(256, len(freq))
+ self.assertTrue(all(freq.values()))
+
+ def test_randombytes_buf_deterministic(self):
+
+ seed = libnacl.randombytes_buf(32)
+ seed2 = libnacl.randombytes_buf(32)
+ data = libnacl.randombytes_buf_deterministic(32, seed)
+ data2 = libnacl.randombytes_buf_deterministic(32, seed)
+ data3 = libnacl.randombytes_buf_deterministic(32, seed2)
+
+ self.assertEqual(32, len(data))
+ self.assertEqual(32, len(data))
+ self.assertEqual(32, len(data))
+ self.assertEqual(data, data2)
+ self.assertNotEqual(data, data3)
+
+ def test_crypto_kdf_keygen(self):
+
+ master_key = libnacl.crypto_kdf_keygen()
+
+ freq = {x: 1 for x in master_key}
+
+ self.assertEqual(32, len(master_key))
+ self.assertTrue(all(freq.values()))
+
+ def test_crypto_kdf_derive_from_key(self):
+
+ master_key = libnacl.crypto_kdf_keygen()
+ subkey = libnacl.crypto_kdf_derive_from_key(16, 1, "Examples", master_key)
+ subkey2 = libnacl.crypto_kdf_derive_from_key(16, 1, "Examples", master_key)
+ subkey3 = libnacl.crypto_kdf_derive_from_key(16, 2, "Examples", master_key)
+
+ self.assertEqual(16, len(subkey))
+ self.assertEqual(16, len(subkey2))
+ self.assertEqual(16, len(subkey3))
+ self.assertEqual(subkey, subkey2)
+ self.assertNotEqual(subkey, subkey3)
+
+ def test_crypto_kx_keypair(self):
+ pk, sk = libnacl.crypto_kx_keypair()
+ self.assertEqual(32, len(pk))
+ self.assertEqual(32, len(sk))
+
+ def test_crypto_kx_seed_keypair(self):
+ seed = libnacl.randombytes_buf(32)
+ seed2 = libnacl.randombytes_buf(32)
+ pk, sk = libnacl.crypto_kx_seed_keypair(seed)
+ pk2, sk2 = libnacl.crypto_kx_seed_keypair(seed)
+ pk3, sk3 = libnacl.crypto_kx_seed_keypair(seed2)
+
+ self.assertEqual(pk, pk2)
+ self.assertNotEqual(pk, pk3)
+ self.assertEqual(sk, sk2)
+ self.assertNotEqual(sk, sk3)
+
+ def test_crypto_kx_client_session_keys(self):
+ client_pk, client_sk = libnacl.crypto_kx_keypair()
+ server_pk, server_sk = libnacl.crypto_kx_keypair()
+ rx, tx, status = libnacl.crypto_kx_client_session_keys(client_pk, client_sk, server_pk)
+ rx2, tx2, status = libnacl.crypto_kx_client_session_keys(client_pk, client_sk, server_pk)
+
+ self.assertEqual(32, len(rx))
+ self.assertEqual(32, len(tx))
+ self.assertEqual(rx, rx2)
+ self.assertEqual(tx, tx2)
+
+ def test_crypto_kx_server_session_keys(self):
+ client_pk, client_sk = libnacl.crypto_kx_keypair()
+ server_pk, server_sk = libnacl.crypto_kx_keypair()
+ rx, tx, status = libnacl.crypto_kx_server_session_keys(client_pk, client_sk, server_pk)
+ rx2, tx2, status = libnacl.crypto_kx_server_session_keys(client_pk, client_sk, server_pk)
+
+ self.assertEqual(32, len(rx))
+ self.assertEqual(32, len(tx))
+ self.assertEqual(rx, rx2)
+ self.assertEqual(tx, tx2)
diff --git a/tests/unit/test_raw_secret.py b/tests/unit/test_raw_secret.py
new file mode 100644
index 0000000..1ca0296
--- /dev/null
+++ b/tests/unit/test_raw_secret.py
@@ -0,0 +1,33 @@
+# Import libnacl libs
+import libnacl
+import libnacl.utils
+
+# Import python libs
+import unittest
+
+
+class TestSecret(unittest.TestCase):
+ """
+ Test secret functions
+ """
+ def test_secretbox(self):
+ msg = b'Are you suggesting coconuts migrate?'
+
+ nonce = libnacl.utils.rand_nonce()
+ key = libnacl.utils.salsa_key()
+
+ c = libnacl.crypto_secretbox(msg, nonce, key)
+ m = libnacl.crypto_secretbox_open(c, nonce, key)
+ self.assertEqual(msg, m)
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_secretbox(msg, b'too_short', key)
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_secretbox(msg, nonce, b'too_short')
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_secretbox_open(c, b'too_short', key)
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_secretbox_open(c, nonce, b'too_short')
diff --git a/tests/unit/test_raw_secret_easy.py b/tests/unit/test_raw_secret_easy.py
new file mode 100644
index 0000000..77e0966
--- /dev/null
+++ b/tests/unit/test_raw_secret_easy.py
@@ -0,0 +1,33 @@
+# Import libnacl libs
+import libnacl
+import libnacl.utils
+
+# Import python libs
+import unittest
+
+
+class TestSecret(unittest.TestCase):
+ """
+ Test secret functions
+ """
+ def test_secretbox_easy(self):
+ msg = b'Are you suggesting coconuts migrate?'
+
+ nonce = libnacl.utils.rand_nonce()
+ key = libnacl.utils.salsa_key()
+
+ c = libnacl.crypto_secretbox_easy(msg, nonce, key)
+ m = libnacl.crypto_secretbox_open_easy(c, nonce, key)
+ self.assertEqual(msg, m)
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_secretbox_easy(msg, b'too_short', key)
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_secretbox_easy(msg, nonce, b'too_short')
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_secretbox_open_easy(c, b'too_short', key)
+
+ with self.assertRaises(ValueError):
+ libnacl.crypto_secretbox_open_easy(c, nonce, b'too_short')
diff --git a/tests/unit/test_raw_sign.py b/tests/unit/test_raw_sign.py
new file mode 100644
index 0000000..db1100d
--- /dev/null
+++ b/tests/unit/test_raw_sign.py
@@ -0,0 +1,38 @@
+# Import libnacl libs
+import libnacl
+import libnacl.utils
+
+# Import python libs
+import unittest
+
+
+class TestSign(unittest.TestCase):
+ '''
+ Test sign functions
+ '''
+ def test_gen(self):
+ vk1, sk1 = libnacl.crypto_sign_keypair()
+ vk2, sk2 = libnacl.crypto_sign_keypair()
+ vk3, sk3 = libnacl.crypto_sign_keypair()
+ self.assertEqual(len(vk1), libnacl.crypto_sign_PUBLICKEYBYTES)
+ self.assertEqual(len(sk1), libnacl.crypto_sign_SECRETKEYBYTES)
+ self.assertEqual(len(vk2), libnacl.crypto_sign_PUBLICKEYBYTES)
+ self.assertEqual(len(sk2), libnacl.crypto_sign_SECRETKEYBYTES)
+ self.assertEqual(len(vk3), libnacl.crypto_sign_PUBLICKEYBYTES)
+ self.assertEqual(len(sk3), libnacl.crypto_sign_SECRETKEYBYTES)
+ self.assertNotEqual(vk1, sk1)
+ self.assertNotEqual(vk2, sk2)
+ self.assertNotEqual(vk3, sk3)
+ self.assertNotEqual(vk1, vk2)
+ self.assertNotEqual(vk1, vk3)
+ self.assertNotEqual(sk1, sk2)
+ self.assertNotEqual(sk2, sk3)
+
+ def test_box(self):
+ msg = b'Are you suggesting coconuts migrate?'
+ # run 1
+ vk1, sk1 = libnacl.crypto_sign_keypair()
+ sig = libnacl.crypto_sign(msg, sk1)
+ self.assertEqual(msg, sig[libnacl.crypto_sign_BYTES:])
+ sig_msg = libnacl.crypto_sign_open(sig, vk1)
+ self.assertEqual(msg, sig_msg)
diff --git a/tests/unit/test_save.py b/tests/unit/test_save.py
new file mode 100644
index 0000000..ca98522
--- /dev/null
+++ b/tests/unit/test_save.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+# Import libnacl libs
+import libnacl.dual
+import libnacl.secret
+import libnacl.sign
+import libnacl.utils
+
+# Import pythonlibs
+import os
+import stat
+import unittest
+import tempfile
+import sys
+
+class TestSave(unittest.TestCase):
+ '''
+ '''
+ def test_save_load(self):
+ msg = b'then leap out of the rabbit, taking the French by surprise'
+ bob = libnacl.dual.DualSecret()
+ alice = libnacl.dual.DualSecret()
+ fh_, bob_path = tempfile.mkstemp()
+ os.close(fh_)
+ fh_, alice_path = tempfile.mkstemp()
+ os.close(fh_)
+ bob.save(bob_path)
+ alice.save(alice_path)
+ bob_box = libnacl.public.Box(bob, alice.pk)
+ alice_box = libnacl.public.Box(alice, bob.pk)
+ bob_enc = bob_box.encrypt(msg)
+ alice_enc = alice_box.encrypt(msg)
+ bob_load = libnacl.utils.load_key(bob_path)
+ alice_load = libnacl.utils.load_key(alice_path)
+ bob_load_box = libnacl.public.Box(bob_load, alice_load.pk)
+ alice_load_box = libnacl.public.Box(alice_load, bob_load.pk)
+ self.assertEqual(bob.sk, bob_load.sk)
+ self.assertEqual(bob.pk, bob_load.pk)
+ self.assertEqual(bob.vk, bob_load.vk)
+ self.assertEqual(bob.seed, bob_load.seed)
+ self.assertEqual(alice.sk, alice_load.sk)
+ self.assertEqual(alice.pk, alice_load.pk)
+ self.assertEqual(alice.vk, alice_load.vk)
+ self.assertEqual(alice.seed, alice_load.seed)
+ bob_dec = alice_load_box.decrypt(bob_enc)
+ alice_dec = bob_load_box.decrypt(alice_enc)
+ self.assertEqual(bob_dec, msg)
+ self.assertEqual(alice_dec, msg)
+
+ bob2 = libnacl.utils.load_key(bob_path)
+ self.assertEqual(bob.sk, bob2.sk)
+ self.assertEqual(bob.pk, bob2.pk)
+ self.assertEqual(bob.vk, bob2.vk)
+
+ os.remove(bob_path)
+ os.remove(alice_path)
+
+ def test_save_load_secret(self):
+ msg = b'then leap out of the rabbit, taking the French by surprise'
+ box = libnacl.secret.SecretBox()
+ fh_, box_path = tempfile.mkstemp()
+ os.close(fh_)
+ box.save(box_path)
+ lbox = libnacl.utils.load_key(box_path)
+ ctxt = box.encrypt(msg)
+ out_msg = lbox.decrypt(ctxt)
+ self.assertEqual(msg, out_msg)
+
+ def test_save_load_sign(self):
+ msg = b'then leap out of the rabbit, taking the French by surprise'
+ signer = libnacl.sign.Signer()
+ fh_, sign_path = tempfile.mkstemp()
+ os.close(fh_)
+ signer.save(sign_path)
+ signer_load = libnacl.utils.load_key(sign_path)
+ signed1 = signer.sign(msg)
+ signed2 = signer_load.sign(msg)
+ self.assertEqual(signed1, signed2)
+ os.remove(sign_path)
+
+ def test_save_perms(self):
+ bob = libnacl.dual.DualSecret()
+ fh_, bob_path = tempfile.mkstemp()
+ os.close(fh_)
+ bob.save(bob_path)
+ stats = os.stat(bob_path)
+ expected_perms = 0o100600 if sys.platform != 'win32' else 0o100666
+ self.assertEqual(stats[stat.ST_MODE], expected_perms)
+ os.remove(bob_path)
diff --git a/tests/unit/test_seal.py b/tests/unit/test_seal.py
new file mode 100644
index 0000000..0f68c13
--- /dev/null
+++ b/tests/unit/test_seal.py
@@ -0,0 +1,36 @@
+# Import libnacl libs
+import libnacl.sealed
+import libnacl.public
+
+# Import python libs
+import unittest
+
+class TestSealed(unittest.TestCase):
+ '''
+ '''
+ def test_secretkey(self):
+ '''
+ '''
+ msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.'
+ key = libnacl.public.SecretKey()
+ box = libnacl.sealed.SealedBox(key)
+ ctxt = box.encrypt(msg)
+ self.assertNotEqual(msg, ctxt)
+ bclear = box.decrypt(ctxt)
+ self.assertEqual(msg, bclear)
+
+ def test_publickey_only(self):
+ '''
+ '''
+ msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.'
+ key = libnacl.public.SecretKey()
+ key_public = libnacl.public.PublicKey(key.pk)
+
+ box = libnacl.sealed.SealedBox(key_public)
+ ctxt = box.encrypt(msg)
+ self.assertNotEqual(msg, ctxt)
+
+ decrypting_box = libnacl.sealed.SealedBox(key)
+ bclear = decrypting_box.decrypt(ctxt)
+ self.assertEqual(msg, bclear)
+
diff --git a/tests/unit/test_secret.py b/tests/unit/test_secret.py
new file mode 100644
index 0000000..3e49cc2
--- /dev/null
+++ b/tests/unit/test_secret.py
@@ -0,0 +1,22 @@
+# Import libnacl libs
+import libnacl.secret
+# Import python libs
+import unittest
+
+class TestSecret(unittest.TestCase):
+ '''
+ '''
+ def test_secret(self):
+ msg = b'But then of course African swallows are not migratory.'
+ box = libnacl.secret.SecretBox()
+ ctxt = box.encrypt(msg)
+ self.assertNotEqual(msg, ctxt)
+ box2 = libnacl.secret.SecretBox(box.sk)
+ clear1 = box.decrypt(ctxt)
+ self.assertEqual(msg, clear1)
+ clear2 = box2.decrypt(ctxt)
+ self.assertEqual(clear1, clear2)
+ ctxt2 = box2.encrypt(msg)
+ clear3 = box.decrypt(ctxt2)
+ self.assertEqual(clear3, msg)
+
diff --git a/tests/unit/test_secret_easy.py b/tests/unit/test_secret_easy.py
new file mode 100644
index 0000000..f77fdfc
--- /dev/null
+++ b/tests/unit/test_secret_easy.py
@@ -0,0 +1,22 @@
+# Import libnacl libs
+import libnacl.secret_easy
+# Import python libs
+import unittest
+
+class TestSecretEasy(unittest.TestCase):
+ '''
+ '''
+ def test_secret(self):
+ msg = b'But then of course African swallows are not migratory.'
+ box = libnacl.secret_easy.SecretBoxEasy()
+ ctxt = box.encrypt(msg)
+ self.assertNotEqual(msg, ctxt)
+ box2 = libnacl.secret_easy.SecretBoxEasy(box.sk)
+ clear1 = box.decrypt(ctxt)
+ self.assertEqual(msg, clear1)
+ clear2 = box2.decrypt(ctxt)
+ self.assertEqual(clear1, clear2)
+ ctxt2 = box2.encrypt(msg)
+ clear3 = box.decrypt(ctxt2)
+ self.assertEqual(clear3, msg)
+
diff --git a/tests/unit/test_sign.py b/tests/unit/test_sign.py
new file mode 100644
index 0000000..b639c95
--- /dev/null
+++ b/tests/unit/test_sign.py
@@ -0,0 +1,93 @@
+# Import libnacl libs
+import libnacl.sign
+
+# Import pythonlibs
+import unittest
+
+
+class TestSigning(unittest.TestCase):
+ '''
+ '''
+ def test_sign(self):
+ msg = (b'Well, that\'s no ordinary rabbit. That\'s the most foul, '
+ b'cruel, and bad-tempered rodent you ever set eyes on.')
+ signer = libnacl.sign.Signer()
+ signed = signer.sign(msg)
+ signature = signer.signature(msg)
+ self.assertNotEqual(msg, signed)
+ veri = libnacl.sign.Verifier(signer.hex_vk())
+ verified = veri.verify(signed)
+ verified2 = veri.verify(signature + msg)
+ self.assertEqual(verified, msg)
+ self.assertEqual(verified2, msg)
+
+ def test_key_decomposition(self):
+ prv_key = b'The two halves are understood to'
+ pub_key = b'be essentially arbitrary values.'
+ secret_key = prv_key + pub_key
+ # The following functions should simply decompose a secret key
+ # without performing real computation. libsodium understands secret
+ # keys to be (private seed bytes || derived public key bytes).
+ self.assertEqual(prv_key, libnacl.crypto_sign_ed25519_sk_to_seed(secret_key))
+ self.assertEqual(pub_key, libnacl.crypto_sign_ed25519_sk_to_pk(secret_key))
+
+ def test_key_decomposition_rejects_wrong_key_lengths(self):
+ """
+ Too few bytes in a key passed through to libsodium will lead to bytes past the end
+ of the string being read. We should be guarding against this dangerous case.
+ """
+ for test_func in (libnacl.crypto_sign_ed25519_sk_to_seed, libnacl.crypto_sign_ed25519_sk_to_pk):
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ test_func(bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
+
+ def test_sign_rejects_wrong_key_lengths(self):
+ """
+ Too few bytes in a key passed through to libsodium will lead to bytes past the end
+ of the string being read. We should be guarding against this dangerous case.
+ """
+ msg = b'The message does not matter.'
+ for test_func in (libnacl.crypto_sign, libnacl.crypto_sign_detached):
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ test_func(msg, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
+
+ def test_open_rejects_wrong_key_lengths(self):
+ """
+ Too few bytes in a key passed through to libsodium will lead to bytes past the end
+ of the string being read. We should be guarding against this dangerous case.
+ """
+ msg = b'The message does not matter.'
+ good_key = b'This valid key is 32 bytes long.'
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_sign_open(msg, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid public key',))
+
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_sign_open(msg, good_key)
+ self.assertEqual(context.exception.args, ('Failed to validate message',))
+
+ def test_verify_detached_rejects_wrong_key_lengths(self):
+ """
+ Too few bytes in a key passed through to libsodium will lead to bytes past the end
+ of the string being read. We should be guarding against this dangerous case.
+ """
+ msg = b'The message does not matter.'
+ good_signature = b'This is a valid signature; it is 64 bytes long, no more, no less'
+ good_key = b'This valid key is 32 bytes long.'
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_sign_verify_detached(good_signature, msg, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid public key',))
+
+ for bad_signature in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_sign_verify_detached(bad_signature, msg, good_key)
+ self.assertEqual(context.exception.args, ('Invalid signature',))
+
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_sign_verify_detached(good_signature, msg, good_key)
+ self.assertEqual(context.exception.args, ('Failed to validate message',))
diff --git a/tests/unit/test_stream.py b/tests/unit/test_stream.py
new file mode 100644
index 0000000..3629e9e
--- /dev/null
+++ b/tests/unit/test_stream.py
@@ -0,0 +1,43 @@
+# Import libnacl libs
+import libnacl.sign
+
+# Import pythonlibs
+import unittest
+
+
+class TestStream(unittest.TestCase):
+ def test_stream_rejects_wrong_lengths(self):
+ """
+ Too few bytes in a key or nonce passed through to libsodium will lead to bytes past the end
+ of the string being read. We should be guarding against this dangerous case.
+ """
+ msg_len = 100 # whatever
+ good_nonce= b'Nonces must be 24 bytes.'
+ good_key = b'This valid key is 32 bytes long.'
+ for bad_nonce in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_stream(msg_len, bad_nonce, good_key)
+ self.assertEqual(context.exception.args, ('Invalid nonce',))
+
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_stream(msg_len, good_nonce, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
+
+ def test_stream_xor_rejects_wrong_lengths(self):
+ """
+ Too few bytes in a key or nonce passed through to libsodium will lead to bytes past the end
+ of the string being read. We should be guarding against this dangerous case.
+ """
+ msg = b'The message does not matter.'
+ good_nonce = b'Nonces must be 24 bytes.'
+ good_key = b'This valid key is 32 bytes long.'
+ for bad_nonce in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_stream_xor(msg, bad_nonce, good_key)
+ self.assertEqual(context.exception.args, ('Invalid nonce',))
+
+ for bad_key in (b'too short', b'too long' * 100):
+ with self.assertRaises(ValueError) as context:
+ libnacl.crypto_stream_xor(msg, good_nonce, bad_key)
+ self.assertEqual(context.exception.args, ('Invalid secret key',))
diff --git a/tests/unit/test_verify.py b/tests/unit/test_verify.py
new file mode 100644
index 0000000..ea872f8
--- /dev/null
+++ b/tests/unit/test_verify.py
@@ -0,0 +1,76 @@
+"""
+Basic tests for verify functions
+"""
+
+import libnacl
+import unittest
+
+
+# These are copied from libsodium test suite
+class TestVerify(unittest.TestCase):
+ def test_verify16(self):
+ v16 = libnacl.randombytes_buf(16)
+ v16x = v16[:]
+ self.assertTrue(libnacl.crypto_verify_16(v16, v16x))
+ self.assertTrue(libnacl.bytes_eq(v16, v16x))
+ v16x = bytearray(v16x)
+ i = libnacl.randombytes_random() & 15
+ v16x[i] = (v16x[i] + 1) % 256
+ v16x = bytes(v16x)
+ self.assertFalse(libnacl.crypto_verify_16(v16, v16x))
+ self.assertFalse(libnacl.bytes_eq(v16, v16x))
+
+ self.assertEqual(libnacl.crypto_verify_16_BYTES, 16)
+
+ def test_verify32(self):
+ v32 = libnacl.randombytes_buf(32)
+ v32x = v32[:]
+ self.assertTrue(libnacl.crypto_verify_32(v32, v32x))
+ self.assertTrue(libnacl.bytes_eq(v32, v32x))
+ v32x = bytearray(v32x)
+ i = libnacl.randombytes_random() & 31
+ v32x[i] = (v32x[i] + 1) % 256
+ v32x = bytes(v32x)
+ self.assertFalse(libnacl.crypto_verify_32(v32, v32x))
+ self.assertFalse(libnacl.bytes_eq(v32, v32x))
+
+ self.assertEqual(libnacl.crypto_verify_32_BYTES, 32)
+
+ def test_verify64(self):
+ v64 = libnacl.randombytes_buf(64)
+ v64x = v64[:]
+ self.assertTrue(libnacl.crypto_verify_64(v64, v64x))
+ self.assertTrue(libnacl.bytes_eq(v64, v64x))
+ v64x = bytearray(v64x)
+ i = libnacl.randombytes_random() & 63
+ v64x[i] = (v64x[i] + 1) % 256
+ v64x = bytes(v64x)
+ self.assertFalse(libnacl.crypto_verify_64(v64, v64x))
+ self.assertFalse(libnacl.bytes_eq(v64, v64x))
+
+ self.assertEqual(libnacl.crypto_verify_64_BYTES, 64)
+
+
+class TestVerifyBytesEq(unittest.TestCase):
+ def test_equal(self):
+ a = libnacl.randombytes_buf(122)
+ b = a[:]
+ self.assertTrue(libnacl.bytes_eq(a, b))
+
+ def test_different(self):
+ a = libnacl.randombytes_buf(122)
+ b = bytearray(a)
+ b[87] = (b[87] + 1) % 256
+ b = bytes(b)
+ self.assertFalse(libnacl.bytes_eq(a, b))
+
+ def test_invalid_type(self):
+ a = libnacl.randombytes_buf(122)
+ b = bytearray(a)
+ with self.assertRaises(TypeError):
+ libnacl.bytes_eq(a, b)
+
+ def test_different_length(self):
+ a = libnacl.randombytes_buf(122)
+ b = a[:-1]
+ self.assertFalse(libnacl.bytes_eq(a, b))
diff --git a/tests/unit/test_version.py b/tests/unit/test_version.py
new file mode 100644
index 0000000..781bde7
--- /dev/null
+++ b/tests/unit/test_version.py
@@ -0,0 +1,22 @@
+"""
+Basic tests for version functions
+"""
+
+import libnacl
+import unittest
+
+
+# These are copied from libsodium test suite
+class TestSodiumVersion(unittest.TestCase):
+ def test_version_string(self):
+ self.assertIsNotNone(libnacl.sodium_version_string())
+
+ def test_library_version_major(self):
+ # Using assertTrue to keep tests "uniform" and keep compatibility with
+ # Python 2.6
+ self.assertTrue(libnacl.sodium_library_version_major() > 0)
+
+ def test_library_version_minor(self):
+ # Using assertTrue to keep tests "uniform" and keep compatibility with
+ # Python 2.6 (assertGreaterEqual appeared in Python 2.7 only)
+ self.assertTrue(libnacl.sodium_library_version_minor() >= 0)