diff options
author | Colin Watson <cjwatson@debian.org> | 2023-08-08 16:13:00 +0100 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2023-08-08 16:13:00 +0100 |
commit | 2287f5d72438282d26312d4ecb20c50230475a8e (patch) | |
tree | 609e7232f4f6c60e0b79637763a474afbc2a1be3 | |
parent | 1cfc83800c8b82e6919291bb4f4bde580ea89821 (diff) |
New upstream version 2.1.0
38 files changed, 604 insertions, 430 deletions
diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index c43beb7..0000000 --- a/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -Thomas S Hatch -Sam Smith -Pedro Algarvio @@ -186,7 +186,7 @@ Apache License same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {2014} Thomas S Hatch + Copyright {2023} 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. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c5c3bf6..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include LICENSE -include AUTHORS -include README.rst -recursive-include tests *.py -recursive-include doc * -recursive-include pkg * @@ -1,25 +1,92 @@ Metadata-Version: 2.1 Name: libnacl -Version: 1.8.0 +Version: 2.1.0 Summary: Python bindings for libsodium based on ctypes -Home-page: https://libnacl.readthedocs.org/ +Home-page: https://libnacl.readthedocs.org +License: Apache-2.0 Author: Thomas S Hatch -Author-email: thatch@saltstack.com -License: UNKNOWN -Platform: UNKNOWN -Classifier: Operating System :: OS Independent +Author-email: thatch45@gmail.com +Requires-Python: >=3.4,<4.0 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 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: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 Classifier: Topic :: Security :: Cryptography -License-File: LICENSE -License-File: AUTHORS +Project-URL: Documentation, https://libnacl.readthedocs.org +Project-URL: Repository, https://github.com/saltstack/libnacl +Description-Content-Type: text/x-rst + +============== +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 +------------------- -UNKNOWN +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/conf.py b/doc/conf.py index 26dd69f..096bf5e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -15,7 +15,6 @@ 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 @@ -48,7 +47,7 @@ master_doc = 'index' # General information about the project. project = u'libnacl' -copyright = u'2020, Thomas S Hatch' +copyright = u'2023, 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 @@ -56,8 +55,8 @@ copyright = u'2020, Thomas S Hatch' # # The short X.Y version. # The full version, including alpha/beta/rc tags. -release = version - +release = "2.1.0" +version = release # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None @@ -267,7 +266,7 @@ texinfo_documents = [ epub_title = u'libnacl' epub_author = u'Thomas S Hatch' epub_publisher = u'Thomas S Hatch' -epub_copyright = u'2020, Thomas S Hatch' +epub_copyright = u'2023, Thomas S Hatch' # The basename for the epub file. It defaults to the project name. #epub_basename = u'libnacl' diff --git a/doc/index.rst b/doc/index.rst index 4638f71..8c146ed 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -11,6 +11,7 @@ Contents: topics/secret topics/sign topics/dual + topics/aead topics/utils topics/raw_public topics/raw_sealed diff --git a/doc/topics/aead.rst b/doc/topics/aead.rst new file mode 100644 index 0000000..9a5925e --- /dev/null +++ b/doc/topics/aead.rst @@ -0,0 +1,33 @@ +============================================= +Authenticated Encryption with Associated Data +============================================= + +One of the most powerful symmetric encryption models available i s known as AEAD. +The libsodium library enables four models of AEAD encryption. As of libnacl 2.0 +we expose 3 of them. + +Using AEAD with libnacl is very easy and can be executed following the same models +as the rest of libnacl. + +The recommended algorithm to use is `XChaCha20-Poly1305-IETF`. Some organizations +require the use of AES, in these cases please use AESGCM. + +For more information on AEAD please see the libsodium documentation + +Using the AEAD system is very easy. + +.. code-block:: python + + import libnacl.aead + + msg = b"Our King? Well i didn't vote for you!!" + aad = b'\x00\x11\x22\x33' + box = libnacl.aead.AEAD_XCHACHA() + ctxt = box.encrypt(msg, aad) + + box2 = libnacl.aead.AEAD_XCHACHA(box.sk) + clear1 = box.decrypt(ctxt, len(aad)) + + ctxt2 = box2.encrypt(msg, aad) + clear3 = box.decrypt(ctxt2, len(aad)) + diff --git a/doc/topics/kx.rst b/doc/topics/kx.rst new file mode 100644 index 0000000..0cec43e --- /dev/null +++ b/doc/topics/kx.rst @@ -0,0 +1,38 @@ +======================= +Key Exchange Encryption +======================= + +The X25519 key exchange algorithm in libsodium allows for key exchange encryption +to be safely executed. The ExchangeKey class makes it easy to use key exchange wrapping +AEAD encryption. This class works similarly to sealed boxes, but offers more functionality +and better security. + +When using the ExchangeKey encryption class you can select which AEAD encryption +subsystem to use, but it is recommended to stick with the default XChaCha algorithm. +The options are: `aesgcm` for the AES256-GCM construct, `xchacha` for the XChaCha20-Poly1305-IETF +construct, and `chacha` for the ChaCha20-Poly1305-IETF construct. + +To use the ExchangeKey system, simply create an ExchangeKey class for `bob` and `alice` +and then encrypt a message and additional unencrypted data and send them back and forth. + +In this example, bob acts as the client, and alice acts as the server. The underlying +nature of the connections are irrelevant, just that once end needs to call the server +functions and the other needs to call the client functions. + +.. code-block:: python + + # Import libnacl libs + import libnacl.kx + + msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.' + aad = b'A Duck!' + # Make Bob and Alice Exchange Keys + bob = libnacl.kx.ExchangeKey() + alice = libnacl.kx.ExchangeKey() + # Encrypt with bob as client and alice as server + bob_ctxt = bob.encrypt_client(alice.kx_pk, msg, aad) + bclear, clear_aad = alice.decrypt_server(bob.kx_pk, bob_ctxt, len(aad)) + # Similarly you can have alice encrypt as server and bob decrypt as client + alice_ctxt = alice.encrypt_server(bob.kx_pk, msg, aad) + aclear, clear_aad = bob.decrypt_client(alice.kx_pk, alice_ctxt, len(aad)) + diff --git a/doc/topics/public.rst b/doc/topics/public.rst index eb2f1bb..34f60be 100644 --- a/doc/topics/public.rst +++ b/doc/topics/public.rst @@ -84,7 +84,9 @@ To manage only the public key end, a public key object exists: import libnacl.public - tom = libnacl.public.PublicKey(tom_public_key_hex) + tom_secret = libnacl.public.SecretKey() + + tom = libnacl.public.PublicKey(tom_secret.pk) raw_pk = tom.pk hex_pk = tom.hex_pk() diff --git a/doc/topics/raw_sealed.rst b/doc/topics/raw_sealed.rst index b2c890e..c1ce444 100644 --- a/doc/topics/raw_sealed.rst +++ b/doc/topics/raw_sealed.rst @@ -2,7 +2,7 @@ Raw Sealed Box Encryption ========================= -Sealed box is a variant of :doc:`public key encryption scheme </raw_public.rst>` +Sealed box is a variant of :doc:`public key encryption scheme </topics/raw_public>` 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. diff --git a/doc/topics/releases/1.9.0.rst b/doc/topics/releases/1.9.0.rst new file mode 100644 index 0000000..5a89077 --- /dev/null +++ b/doc/topics/releases/1.9.0.rst @@ -0,0 +1,7 @@ +=========================== +libnacl 1.9.0 Release Notes +=========================== + +This release is a little overdue, it fixes a number of documentation issues +and adds a few convenience features. It also migrates the build system to poetry +and fixes the documentation build on readthedocs diff --git a/doc/topics/releases/2.0.0.rst b/doc/topics/releases/2.0.0.rst new file mode 100644 index 0000000..2ab6d2d --- /dev/null +++ b/doc/topics/releases/2.0.0.rst @@ -0,0 +1,9 @@ +=========================== +libnacl 2.0.0 Release Notes +=========================== + +Add Support for AEAD and AEAD Classes +===================================== + +Added classes to the libnacl.aead module allowing for the use of +XChaCha20-Poly1305-IETF, ChaCha20-Poly1305-IETF, and AES256-GCM. diff --git a/doc/topics/releases/2.1.0.rst b/doc/topics/releases/2.1.0.rst new file mode 100644 index 0000000..ba22ebc --- /dev/null +++ b/doc/topics/releases/2.1.0.rst @@ -0,0 +1,21 @@ +=========================== +libnacl 2.1.0 Release Notes +=========================== + +Add Support for the Key Exchange System +======================================= + +Added the libnacl.kx module. This module contains the ExchangeKey class. + +The ExchangeKey class makes it easy to use AEAD encryption with an +exchange key setup. The class works much like a sealed box but allows +for the creation of the exchange keys. + +This makes it very easy to set up a system using AEAD and exchange keys. + +Fix issues with pyproject.toml +============================== + +The 2.0.0 release introduced the use of poetry into libnacl, unfortunately I +made a mistake in the pyproject.toml file. Thanks for @mgorny for catching the +issue and getting a PR in. diff --git a/doc/topics/secret.rst b/doc/topics/secret.rst index 3690704..cffd224 100644 --- a/doc/topics/secret.rst +++ b/doc/topics/secret.rst @@ -6,19 +6,21 @@ 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 +The SecretBoxEasy class in libnacl.secret_easy makes this type of encryption very easy to execute: .. code-block:: python + import libnacl.secret_easy + 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() + box = libnacl.secret_easy.SecretBoxEasy() # 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) + box2 = libnacl.secret_easy.SecretBoxEasy(box.sk) # Messages can now be easily encrypted and decrypted clear1 = box.decrypt(ctxt) clear2 = box2.decrypt(ctxt) diff --git a/libnacl.egg-info/PKG-INFO b/libnacl.egg-info/PKG-INFO deleted file mode 100644 index 5b4fd06..0000000 --- a/libnacl.egg-info/PKG-INFO +++ /dev/null @@ -1,25 +0,0 @@ -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 deleted file mode 100644 index 2b20d14..0000000 --- a/libnacl.egg-info/SOURCES.txt +++ /dev/null @@ -1,87 +0,0 @@ -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 deleted file mode 100644 index 8b13789..0000000 --- a/libnacl.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/libnacl.egg-info/top_level.txt b/libnacl.egg-info/top_level.txt deleted file mode 100644 index d238ddf..0000000 --- a/libnacl.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -libnacl diff --git a/libnacl/__init__.py b/libnacl/__init__.py index 7649013..0f80f49 100644 --- a/libnacl/__init__.py +++ b/libnacl/__init__.py @@ -4,7 +4,7 @@ Wrap libsodium routines ''' # pylint: disable=C0103 # Import python libs -import ctypes +import ctypes, ctypes.util import sys import os @@ -16,6 +16,10 @@ def _get_nacl(): Locate the nacl c libs to use ''' # Import libsodium + l_path = ctypes.util.find_library('sodium') + if l_path is not None: + return ctypes.cdll.LoadLibrary(l_path) + if sys.platform.startswith('win'): try: return ctypes.cdll.LoadLibrary('libsodium') @@ -113,11 +117,16 @@ if not DOC_RUN: 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() + crypto_aead_xchacha20poly1305_ietf_KEYBYTES = nacl.crypto_aead_xchacha20poly1305_ietf_keybytes() + crypto_aead_xchacha20poly1305_ietf_NPUBBYTES = nacl.crypto_aead_xchacha20poly1305_ietf_npubbytes() + crypto_aead_xchacha20poly1305_ietf_ABYTES = nacl.crypto_aead_xchacha20poly1305_ietf_abytes() HAS_AEAD_CHACHA20POLY1305_IETF = True + HAS_AEAD_XCHACHA20POLY1305_IETF = True HAS_AEAD = True except AttributeError: HAS_AEAD_AES256GCM = False HAS_AEAD_CHACHA20POLY1305_IETF = False + HAS_AEAD_XCHACHA20POLY1305_IETF = False HAS_AEAD = False crypto_box_SECRETKEYBYTES = nacl.crypto_box_secretkeybytes() @@ -770,6 +779,46 @@ def crypto_aead_chacha20poly1305_ietf_encrypt(message, aad, nonce, key): return c.raw +def crypto_aead_xchacha20poly1305_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_xchacha20poly1305_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_XCHACHA20POLY1305_IETF: + raise ValueError('Underlying Sodium library does not support IETF variant of ChaCha20Poly1305 AEAD') + + if len(key) != crypto_aead_xchacha20poly1305_ietf_KEYBYTES: + raise ValueError('Invalid key') + + if len(nonce) != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: + raise ValueError('Invalid nonce') + + length = len(message) + crypto_aead_xchacha20poly1305_ietf_ABYTES + clen = ctypes.c_ulonglong() + c = ctypes.create_string_buffer(length) + ret = nacl.crypto_aead_xchacha20poly1305_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 @@ -828,6 +877,34 @@ def crypto_aead_chacha20poly1305_ietf_decrypt(ctxt, aad, nonce, key): return m.raw +def crypto_aead_xchacha20poly1305_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_xchacha20poly1305_ietf_KEYBYTES: + raise ValueError('Invalid key') + + if len(nonce) != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: + raise ValueError('Invalid nonce') + + length = len(ctxt)-crypto_aead_xchacha20poly1305_ietf_ABYTES + mlen = ctypes.c_ulonglong() + m = ctypes.create_string_buffer(length) + + ret = nacl.crypto_aead_xchacha20poly1305_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 diff --git a/libnacl/aead.py b/libnacl/aead.py index db53b13..0a519b1 100644 --- a/libnacl/aead.py +++ b/libnacl/aead.py @@ -20,21 +20,36 @@ class AEAD(libnacl.base.BaseKey): raise ValueError('Invalid key') self.sk = key self.usingAES = False + self.usingXCHACHA = False + super().__init__() def useAESGCM(self): self.usingAES = True return self + def useXCHACHA(self): + self.usingXCHACHA = 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: + if self.usingXCHACHA: + nonce = libnacl.utils.rand_aead_xchacha_nonce() + else: + nonce = libnacl.utils.rand_aead_nonce() + if self.usingXCHACHA: + if len(nonce) != libnacl.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: + raise ValueError('Invalid nonce') + else: + if len(nonce) != libnacl.crypto_aead_aes256gcm_NPUBBYTES: + raise ValueError('Invalid nonce') + if self.usingXCHACHA: + ctxt = libnacl.crypto_aead_xchacha20poly1305_ietf_encrypt(msg, aad, nonce, self.sk) + elif 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) @@ -50,10 +65,18 @@ class AEAD(libnacl.base.BaseKey): 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.usingXCHACHA: + nonce = ctxt[aadLen:aadLen+libnacl.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] + ctxt = ctxt[aadLen+libnacl.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES:] + if len(nonce) != libnacl.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: + raise ValueError('Invalid nonce') + else: + 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.usingXCHACHA: + return libnacl.crypto_aead_xchacha20poly1305_ietf_decrypt(ctxt, aad, nonce, self.sk) 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) @@ -68,3 +91,20 @@ class AEAD(libnacl.base.BaseKey): 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) + + +class AEAD_AESGCM(AEAD): + def __init__(self, key=None): + super().__init__(key) + self.useAESGCM() + + +class AEAD_XCHACHA(AEAD): + def __init__(self, key=None): + super().__init__(key) + self.useXCHACHA() + + +class AEAD_CHACHA(AEAD): + def __init__(self, key=None): + super().__init__(key) diff --git a/libnacl/base.py b/libnacl/base.py index b115a47..66239a7 100644 --- a/libnacl/base.py +++ b/libnacl/base.py @@ -13,16 +13,29 @@ class BaseKey(object): ''' Include methods for key management convenience ''' + def __init__(self): + self.sk_hex = self.hex_sk() + self.pk_hex = self.hex_pk() + self.kx_sk_hex = self.hex_kx_sk() + self.kx_pk_hex = self.hex_kx_pk() + self.vk_hex = self.hex_vk() + 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_kx_pk(self): + if hasattr(self, 'kx_pk'): + return libnacl.encode.hex_encode(self.kx_pk) + + def hex_kx_sk(self): + if hasattr(self, 'kx_sk'): + return libnacl.encode.hex_encode(self.kx_sk) + def hex_vk(self): if hasattr(self, 'vk'): return libnacl.encode.hex_encode(self.vk) @@ -38,12 +51,18 @@ class BaseKey(object): pre = {} sk = self.hex_sk() pk = self.hex_pk() + kx_sk = self.hex_kx_sk() + kx_pk = self.hex_kx_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 kx_sk: + pre['kx_priv'] = kx_sk.decode('utf-8') + if kx_pk: + pre['kx_pub'] = kx_pk.decode('utf-8') if vk: pre['verify'] = vk.decode('utf-8') if seed: diff --git a/libnacl/dual.py b/libnacl/dual.py index c48fb67..6203c19 100644 --- a/libnacl/dual.py +++ b/libnacl/dual.py @@ -20,6 +20,7 @@ class DualSecret(libnacl.base.BaseKey): self.seed = self.signer.seed self.pk = self.crypt.pk self.vk = self.signer.vk + super().__init__() def sign(self, msg): ''' diff --git a/libnacl/kx.py b/libnacl/kx.py new file mode 100644 index 0000000..4d995f6 --- /dev/null +++ b/libnacl/kx.py @@ -0,0 +1,83 @@ +""" +Implementation of the X25519 Key Exchange function. These classes make executing a key +exchange simple. +""" +import libnacl +import libnacl.base +import libnacl.utils + + +class ExchangeKey(libnacl.base.BaseKey): + """ + The class used to manage key exchange keys + """ + def __init__(self, kx_sk=None, enc=None): + if kx_sk is None: + self.kx_pk, self.kx_sk = libnacl.crypto_kx_keypair() + elif len(kx_sk) == libnacl.libnacl.crypto_kx_SECRETKEYBYTES: + self.kx_sk = kx_sk + self.kx_pk = libnacl.crypto_scalarmult_base(kx_sk) + if enc is None: + self.enc = "xchacha" + elif enc in ("xchacha", "aesgcm", "chacha"): + self.enc = enc + else: + raise ValueError(f"Invalid encryption type passed: {enc}") + + def get_crypt(self, key): + return getattr(self, f"get_{self.enc}")(key) + + def get_xchacha(self, key): + return libnacl.aead.AEAD_XCHACHA(key) + + def get_chacha(self, key): + return libnacl.aead.AEAD_CHACHA(key) + + def get_aesgcm(self, key): + return libnacl.aead.AEAD_AESGCM(key) + + def client_session_keys(self, remote_pk): + """ + Takes a remote public key and derives the rx and tx session keys + """ + return libnacl.crypto_kx_client_session_keys(self.kx_pk, self.kx_sk, remote_pk) + + def server_session_keys(self, remote_pk): + """ + Takes a remote public key and derives the rx and tx session keys + """ + return libnacl.crypto_kx_server_session_keys(self.kx_pk, self.kx_sk, remote_pk) + + def encrypt_client(self, remote_pk, msg, ad): + """ + Encrypt the given message using the remote_sk + """ + rx, tx, status = self.client_session_keys(remote_pk) + + crypter = self.get_crypt(tx) + return crypter.encrypt(msg, ad) + + def encrypt_server(self, remote_pk, msg, ad): + """ + Encrypt the given message using the remote_sk + """ + rx, tx, status = self.server_session_keys(remote_pk) + + crypter = self.get_crypt(tx) + return crypter.encrypt(msg, ad) + + def decrypt_client(self, remote_pk, ctxt, len_ad): + rx, tx, status = self.client_session_keys(remote_pk) + + crypter = self.get_crypt(rx) + clear = crypter.decrypt(ctxt, len_ad) + #ad = ctxt[:len_ad] + return clear + + def decrypt_server(self, remote_pk, ctxt, len_ad): + rx, tx, status = self.server_session_keys(remote_pk) + + crypter = self.get_crypt(rx) + clear = crypter.decrypt(ctxt, len_ad) + #ad = ctxt[:len_ad] + return clear diff --git a/libnacl/public.py b/libnacl/public.py index bac8609..0e6afa5 100644 --- a/libnacl/public.py +++ b/libnacl/public.py @@ -19,6 +19,7 @@ class PublicKey(libnacl.base.BaseKey): self.pk = pk else: raise ValueError('Passed in invalid public key') + super().__init__() def __eq__(self, other): if isinstance(other, self.__class__): @@ -48,6 +49,7 @@ class SecretKey(libnacl.base.BaseKey): self.pk = libnacl.crypto_scalarmult_base(sk) else: raise ValueError('Passed in invalid secret key') + super().__init__() def __eq__(self, other): if isinstance(other, self.__class__): diff --git a/libnacl/secret_easy.py b/libnacl/secret_easy.py index 15dea1b..7ffad35 100644 --- a/libnacl/secret_easy.py +++ b/libnacl/secret_easy.py @@ -18,6 +18,7 @@ class SecretBoxEasy(libnacl.base.BaseKey): if len(key) != libnacl.crypto_secretbox_KEYBYTES: raise ValueError('Invalid key') self.sk = key + super().__init__() def encrypt(self, msg, nonce=None, pack_nonce=True): ''' diff --git a/libnacl/sign.py b/libnacl/sign.py index 9dbd83f..03254cb 100644 --- a/libnacl/sign.py +++ b/libnacl/sign.py @@ -24,6 +24,7 @@ class Signer(libnacl.base.BaseKey): seed = libnacl.randombytes(libnacl.crypto_sign_SEEDBYTES) self.vk, self.sk = libnacl.crypto_sign_seed_keypair(seed) self.seed = seed + super().__init__() def sign(self, msg): ''' @@ -47,6 +48,7 @@ class Verifier(libnacl.base.BaseKey): Create a verification key from a hex encoded vkey ''' self.vk = libnacl.encode.hex_decode(vk_hex) + super().__init__() def verify(self, msg): ''' diff --git a/libnacl/utils.py b/libnacl/utils.py index e06e078..bd22b91 100644 --- a/libnacl/utils.py +++ b/libnacl/utils.py @@ -47,6 +47,9 @@ def load_key(path_or_file, serial='json'): elif 'priv' in key_data and 'pub' in key_data: return libnacl.public.SecretKey( libnacl.encode.hex_decode(key_data['priv'])) + elif 'kx_priv' in key_data and 'kx_pub' in key_data: + return libnacl.kx.ExchangeKey( + libnacl.encode.hex_decode(key_data['kx_priv'])) elif 'sign' in key_data: return libnacl.sign.Signer( libnacl.encode.hex_decode(key_data['sign'])) @@ -83,6 +86,14 @@ def rand_aead_nonce(): return libnacl.randombytes(libnacl.crypto_aead_aes256gcm_NPUBBYTES) +def rand_aead_xchacha_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_xchacha20poly1305_ietf_NPUBBYTES) + + def rand_nonce(): ''' Generates and returns a random bytestring of the size defined in libsodium diff --git a/libnacl/version.py b/libnacl/version.py deleted file mode 100644 index baca2f5..0000000 --- a/libnacl/version.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -version = "1.8.0" diff --git a/pkg/rpm/python-libnacl.spec b/pkg/rpm/python-libnacl.spec deleted file mode 100644 index 6b991c4..0000000 --- a/pkg/rpm/python-libnacl.spec +++ /dev/null @@ -1,153 +0,0 @@ -%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 deleted file mode 100644 index 4740656..0000000 --- a/pkg/suse/python-libnacl.changes +++ /dev/null @@ -1,15 +0,0 @@ -------------------------------------------------------------------- -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 deleted file mode 100644 index 2dc9810..0000000 --- a/pkg/suse/python-libnacl.spec +++ /dev/null @@ -1,59 +0,0 @@ -# -# 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/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..514ddb0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,40 @@ +[tool.poetry] +name = "libnacl" +version = "2.1.0" +description = "Python bindings for libsodium based on ctypes" +authors = ["Thomas S Hatch <thatch45@gmail.com>"] +license = "Apache-2.0" +homepage="https://libnacl.readthedocs.org" +repository="https://github.com/saltstack/libnacl" +documentation="https://libnacl.readthedocs.org" +readme = "README.rst" +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", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Topic :: Security :: Cryptography", + ] +include = [ + { path = "doc", format = "sdist" }, + { path = "tests", format = "sdist" }, +] + +[tool.poetry.dependencies] +python = "^3.4" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index adf5ed7..0000000 --- a/setup.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[bdist_wheel] -universal = 1 - -[egg_info] -tag_build = -tag_date = 0 - diff --git a/setup.py b/setup.py deleted file mode 100644 index 79dda9c..0000000 --- a/setup.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/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/unit/test_aead.py b/tests/unit/test_aead.py index b6dfa05..b404975 100644 --- a/tests/unit/test_aead.py +++ b/tests/unit/test_aead.py @@ -23,6 +23,23 @@ class TestAEAD(unittest.TestCase): clear3 = box.decrypt(ctxt2, len(aad)) self.assertEqual(clear3, msg) + @unittest.skipUnless(libnacl.HAS_AEAD_AES256GCM, 'AES256-GCM AEAD not available') + def test_gcm_aead_class(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_AESGCM() + ctxt = box.encrypt(msg, aad) + self.assertNotEqual(msg, ctxt) + + box2 = libnacl.aead.AEAD_AESGCM(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) + @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!!" @@ -38,4 +55,57 @@ class TestAEAD(unittest.TestCase): self.assertEqual(clear1, clear2) ctxt2 = box2.encrypt(msg, aad) clear3 = box.decrypt(ctxt2, len(aad)) + + @unittest.skipUnless(libnacl.HAS_AEAD_CHACHA20POLY1305_IETF, 'IETF variant of ChaCha20Poly1305 AEAD not available') + def test_ietf_aead_class(self): + msg = b"Our King? Well i didn't vote for you!!" + aad = b'\x00\x11\x22\x33' + box = libnacl.aead.AEAD_CHACHA() + ctxt = box.encrypt(msg, aad) + self.assertNotEqual(msg, ctxt) + + box2 = libnacl.aead.AEAD_CHACHA(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)) + + @unittest.skipUnless(libnacl.HAS_AEAD_XCHACHA20POLY1305_IETF, 'IETF variant of xChaCha20Poly1305 AEAD not available') + def test_ietf_aead_xchacha(self): + msg = b"Our King? Well i didn't vote for you!!" + aad = b'\x00\x11\x22\x33' + box = libnacl.aead.AEAD().useXCHACHA() + ctxt = box.encrypt(msg, aad) + self.assertNotEqual(msg, ctxt) + + box2 = libnacl.aead.AEAD(box.sk).useXCHACHA() + 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) + self.assertEqual(clear3, msg) + + + @unittest.skipUnless(libnacl.HAS_AEAD_XCHACHA20POLY1305_IETF, 'IETF variant of xChaCha20Poly1305 AEAD not available') + def test_ietf_aead_xchacha_class(self): + msg = b"Our King? Well i didn't vote for you!!" + aad = b'\x00\x11\x22\x33' + box = libnacl.aead.AEAD_XCHACHA() + ctxt = box.encrypt(msg, aad) + self.assertNotEqual(msg, ctxt) + + box2 = libnacl.aead.AEAD_XCHACHA(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) self.assertEqual(clear3, msg) + diff --git a/tests/unit/test_dual.py b/tests/unit/test_dual.py index d300c9e..04a8de9 100644 --- a/tests/unit/test_dual.py +++ b/tests/unit/test_dual.py @@ -25,6 +25,9 @@ class TestDual(unittest.TestCase): aclear = alice_box.decrypt(alice_ctxt) self.assertEqual(msg, aclear) self.assertNotEqual(bob_ctxt, alice_ctxt) + self.assertEqual(bob.pk_hex, bob.hex_pk()) + self.assertEqual(bob.sk_hex, bob.hex_sk()) + self.assertEqual(bob.vk_hex, bob.hex_vk()) def test_publickey(self): ''' @@ -39,6 +42,9 @@ class TestDual(unittest.TestCase): self.assertNotEqual(msg, bob_ctxt) bclear = alice_box.decrypt(bob_ctxt) self.assertEqual(msg, bclear) + self.assertEqual(bob.pk_hex, bob.hex_pk()) + self.assertEqual(bob.sk_hex, bob.hex_sk()) + self.assertEqual(bob.vk_hex, bob.hex_vk()) def test_sign(self): msg = (b'Well, that\'s no ordinary rabbit. That\'s the most foul, ' diff --git a/tests/unit/test_kx.py b/tests/unit/test_kx.py new file mode 100644 index 0000000..f057916 --- /dev/null +++ b/tests/unit/test_kx.py @@ -0,0 +1,35 @@ +# Import libnacl libs +import libnacl.kx + +# Import python libs +import unittest + +class TestKX(unittest.TestCase): + ''' + ''' + def test_exchange_key(self): + ''' + ''' + msg = b'You\'ve got two empty halves of coconut and you\'re bangin\' \'em together.' + ad = b'A Duck!' + # Make Bob and Alice Exchange Keys + bob = libnacl.kx.ExchangeKey() + alice = libnacl.kx.ExchangeKey() + # Encrypt with bob as clientm alic as server + bob_ctxt = bob.encrypt_client(alice.kx_pk, msg, ad) + self.assertNotEqual(msg, bob_ctxt) + bclear = alice.decrypt_server(bob.kx_pk, bob_ctxt, len(ad)) + self.assertEqual(ad, bob_ctxt[:len(ad)]) + self.assertEqual(msg, bclear) + alice_ctxt = alice.encrypt_server(bob.kx_pk, msg, ad) + aclear = bob.decrypt_client(alice.kx_pk, alice_ctxt, len(ad)) + self.assertEqual(ad, alice_ctxt[:len(ad)]) + self.assertEqual(msg, aclear) + self.assertNotEqual(msg, alice_ctxt) + # Encrypt with Alice as client bob as server + alice_ctxt = alice.encrypt_client(bob.kx_pk, msg, ad) + self.assertNotEqual(msg, alice_ctxt) + bclear = bob.decrypt_server(alice.kx_pk, alice_ctxt, len(ad)) + self.assertEqual(ad, alice_ctxt[:len(ad)]) + self.assertEqual(msg, bclear) + diff --git a/tests/unit/test_public.py b/tests/unit/test_public.py index 61de5a9..c3aa6b0 100644 --- a/tests/unit/test_public.py +++ b/tests/unit/test_public.py @@ -24,6 +24,8 @@ class TestPublic(unittest.TestCase): aclear = alice_box.decrypt(alice_ctxt) self.assertEqual(msg, aclear) self.assertNotEqual(bob_ctxt, alice_ctxt) + self.assertEqual(bob.pk_hex, bob.hex_pk()) + self.assertEqual(bob.sk_hex, bob.hex_sk()) def test_publickey(self): ''' @@ -38,4 +40,5 @@ class TestPublic(unittest.TestCase): self.assertNotEqual(msg, bob_ctxt) bclear = alice_box.decrypt(bob_ctxt) self.assertEqual(msg, bclear) + self.assertEqual(bob.sk_hex, bob.hex_sk()) |