diff --git a/.travis.yml b/.travis.yml index 73efe9b..59a2df1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,10 @@ env: # Note travis times out with the following: # - TEST_PYCRYPTO="true" TEST_GMPY="false" - + +# Inorder to test with and without some libraries, we explicitly +# install deps instead of installing from the requirements file + python: - "3.3" - "3.4" @@ -21,7 +24,9 @@ before_install: - sudo apt-get install -qq python3-crypto - sudo apt-get install -qq libgmp-dev libmpfr-dev - sudo apt-get install -qq libgmp3-dev libgmp3c2 libmpfr4 libmpc-dev libmpc2 - # Install gmpy2 dependencies + - pip install --upgrade pip + - pip install --upgrade nose + # Install gmpy2 dependency - if [[ $TEST_GMPY == "true" ]]; then wget ftp://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gz; tar xzvf mpc-1.0.3.tar.gz; @@ -37,7 +42,7 @@ before_install: pip install pycrypto==2.6.1; fi install: - - pip install nose + - pip install numpy - pip install click -# command to run tests script: nosetests -v + diff --git a/README.rst b/README.rst index b072e80..d1adf3e 100644 --- a/README.rst +++ b/README.rst @@ -1,15 +1,17 @@ -python-paillier +python-paillier |release| =============== -+--------------+------------+-----------+ -| release | master | develop | -+==============+============+===========+ -| |release| | |travisM| | |travisD| | -+--------------+------------+-----------+ -| | |reqM| | |reqD| | -+--------------+------------+-----------+ -| | |rtdM| | |rtdD| | -+--------------+------------+-----------+ ++---------------------+-----------------+ +| master | develop | ++=====================+=================+ +| |travisM| | |travisD| | ++---------------------+-----------------+ +| |rtdM| | |rtdD| | ++---------------------+-----------------+ +| |coverageM| | |coverageD| | ++---------------------+-----------------+ +| |reqM| | |reqD| | ++---------------------+-----------------+ A library for Partially Homomorphic Encryption in Python. @@ -34,7 +36,7 @@ Or use nose:: Code History ------------ -Developed at Data61. +Developed at `Data61 | CSIRO `_. Parts derived from the Apache licensed Google project: https://code.google.com/p/encrypted-bigquery-client/ @@ -72,5 +74,13 @@ expert. The crypto parts are mercifully short, however. :alt: Documentation Status .. |rtdD| image:: https://readthedocs.org/projects/python-paillier/badge/?version=develop - :target: http://python-paillier.readthedocs.org/en/latest/?badge=develop - :alt: Documentation Status + :target: http://python-paillier.readthedocs.org/en/latest/?badge=develop + :alt: Documentation Status + +.. |coverageM| image:: https://coveralls.io/repos/NICTA/python-paillier/badge.svg?branch=master&service=github + :target: https://coveralls.io/github/NICTA/python-paillier?branch=master + +.. |coverageD| image:: https://coveralls.io/repos/NICTA/python-paillier/badge.svg?branch=develop&service=github + :target: https://coveralls.io/github/NICTA/python-paillier?branch=develop + + diff --git a/docs/cli.rst b/docs/cli.rst index 7888bf1..1cf7ef2 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -1,16 +1,10 @@ .. _cli: +==================== Command Line Utility ==================== -The command line utility is not installed by default. When installing with pip you -must specify the optional extra eg:: - - pip install "phe[CLI]>1.2" --upgrade - - -After :ref:`installation`, the **pheutil** command line program will be installed on your path. -This interface allows a user to: +This cli interface allows a user to: - generate and serialize key pairs (of different key sizes) - encrypt and serialize given a public key and a plaintext number @@ -20,6 +14,27 @@ This interface allows a user to: - multiply an encrypted number to a plaintext number +Installation +------------ + +The command line utility is not installed by default. When installing with pip you +must specify the optional extra eg:: + + pip install "phe[cli]" --upgrade + + +After :ref:`installation`, the **pheutil** command line program will be installed on your path. + + +To use the command line client without installing `python-paillier`, run the +:mod:`phe.command_line` module from the project root:: + + python -m phe.command_line + + +Usage Help +---------- + For commands, and examples call `--help`:: $ pheutil --help @@ -61,8 +76,45 @@ Each command also includes more detail, e.g. for `genpkey`:: --id TEXT Add an identifying comment to the key - -To use the command line client without installing `python-paillier`, run the -:mod:`phe.command_line` module from the project root:: - - python -m phe.command_line +Example Session +--------------- + +:: + + $ pheutil genpkey --keysize 1024 example_private_key.json + Generating a paillier keypair with keysize of 1024 + Keys generated + Private key written to example_private_key.json + $ pheutil extract example_private_key.json example_public_key.json + Loading paillier keypair + Public key written to example_public_key.json + $ pheutil encrypt --output test.enc example_public_key.json 5000 + Loading public key + Encrypting: +5000.0000000000000000 + $ cat test.enc | python -m json.tool + { + "e": -32, + "v": "8945468961282852256778220989238222172150456425808373953578229301775803205409565637223688006899379858518150634149268673387123813092667724639715011697847472787020974697972558733184395004744948252959649660835719161407306407854534355718203796283103451456746682405859634010362011442548072273622024024463167923466056606817150074423359137917704381669997696942809271828714079014827677816707229329379573217492868913536374239033718507818834874942682659422972598117458546894148344090333255242329686475806834331038677335462130194428967083103705644514152785933564702168267063628303275275994362218144323611010911197842705253655015" + } + $ pheutil add --output result.enc example_public_key.json test.enc 100 + Loading public key + Loading encrypted number + Loading unencrypted number + Adding + Exponent is less than -32 + $ pheutil decrypt example_private_key.json result.enc + Loading private key + Decrypting ciphertext + 5100.0 + + + +Bash Completion +--------------- + +Bash completion can be enabled by adding the following to your `.bashrc` file:: + + eval "$(_PHEUTIL_COMPLETE=source pheutil)" + +Further information on bash completion can be found in the `click `_ +documentation. diff --git a/docs/compatibility.rst b/docs/compatibility.rst index d192081..4eafc7b 100644 --- a/docs/compatibility.rst +++ b/docs/compatibility.rst @@ -9,3 +9,10 @@ This library may, with *care*, be used with other Paillier implementations. different Encoding scheme. Base of 2 is fixed (see :ref:`alternative-base`) - `paillier.js `_ - Early prototype library for Javascript/Typescript + + + +.. toctree:: + :maxdepth: 2 + + alternatives \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 2551300..f547244 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -70,6 +70,10 @@ def __getattr__(cls, name): #'sphinxcontrib.napoleon' # Sphinx 1.2 ] +# Don't test blocks that are not doctest directive blocks - e.g. all the +# code in alternitives.rst +doctest_test_doctest_blocks = "" + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/index.rst b/docs/index.rst index 935f21a..bcb896d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,3 @@ -.. phe documentation master file, created by - sphinx-quickstart on Wed Jun 11 15:00:09 2014. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. python-paillier =============== @@ -21,21 +17,19 @@ The homomorphic properties of the Paillier crypto system are: installation usage + serialisation caveats cli phe compatibility -.. toctree:: - :maxdepth: 1 - - alternatives - Example ------- +.. doctest:: + >>> from phe import paillier >>> public_key, private_key = paillier.generate_paillier_keypair() >>> secret_number_list = [3.141592653, 300, -4.6e-12] diff --git a/docs/installation.rst b/docs/installation.rst index f26d6f1..0401bed 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -3,6 +3,10 @@ Installation ============ +.. note:: + + This library requires a minimum Python version of at least 3.3. + Using pip --------- @@ -11,9 +15,9 @@ Using pip at the command line, to install the base library from `PyPi `, introduced at version 1.2:: - pip install "phe[CLI]>1.2" + pip install "phe[cli]>1.2" diff --git a/docs/phe.rst b/docs/phe.rst index a4b6c90..1619004 100644 --- a/docs/phe.rst +++ b/docs/phe.rst @@ -2,6 +2,10 @@ API Documentation ================= +Paillier +-------- + + .. automodule:: phe.paillier :members: :undoc-members: @@ -16,3 +20,13 @@ Utilities :members: :undoc-members: :show-inheritance: + + +CLI +---- + + +.. automodule:: phe.command_line + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/serialisation.rst b/docs/serialisation.rst new file mode 100644 index 0000000..8245aa5 --- /dev/null +++ b/docs/serialisation.rst @@ -0,0 +1,146 @@ +.. _serialisation: + +============= +Serialisation +============= + +This library does not enforce any particular serialisation scheme. + +Every :class:`~phe.paillier.EncryptedNumber` +instance has a :attr:`~phe.paillier.EncryptedNumber.public_key` attribute, and serialising each +:class:`~phe.paillier.EncryptedNumber` independently would be heinously inefficient when sending +a large list of instances. It is up to you to serialise in a way that is efficient for your use +case. + +.. _basic-serialisation: + +Basic JSON Serialisation +------------------------ + +This basic serialisation method is an example of serialising a vector of encrypted numbers. +Note that if you are only using the python-paillier library **g** will always be **n + 1**, +so these is no need to serialise it as part of the public key. + +To send a list of values encrypted against one public key, the following is one way to serialise:: + + >>> import json + >>> enc_with_one_pub_key = {} + >>> enc_with_one_pub_key['public_key'] = {'g': public_key.g, + ... 'n': public_key.n} + >>> enc_with_one_pub_key['values'] = [ + ... (str(x.ciphertext()), x.exponent) for x in encrypted_number_list + ... ] + >>> serialised = json.dumps(enc_with_one_pub_key) + +Deserialisation of the above scheme might look as follows:: + + >>> received_dict = json.loads(serialised) + >>> pk = received_dict['public_key'] + >>> public_key_rec = paillier.PaillierPublicKey(g=int(pk['g']), + ... n=int(pk['n'])) + >>> enc_nums_rec = [ + ... paillier.EncryptedNumber(public_key_rec, int(x[0]), int(x[1])) + ... for x in received_dict['values'] + ... ] + +If both parties already know `public_key`, then you might instead send a hash of the public key. + + +.. _json-serialisation: + +JWK Serialisation +----------------- + +This serialisation scheme is used by the :ref:`cli`, and is based on the +`JSON Web Key (JWK) `_ format. This +serialisation scheme should be used to increase compatibility between libraries. + +.. _b64: + +All cryptographic integers are represented as Base64UrlEncoded numbers. +Note the existence of :func:`~phe.util.base64_to_int` and :func:`~phe.util.int_to_base64`. + +"kty" (Key Type) Parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We define the family for all Paillier keys as "DAJ" for Damgard Jurik. + + +"alg" (Algorithm) Parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We identify the algorithm for our Paillier keys as: "PAI-GN1" + +"key_ops" (Key Operations) Parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Values will be "encrypt" and "decrypt" for public and private keys respectively. +We decided not to add homomorphic properties to the key operations. + +"kid" (Key Identifier) +~~~~~~~~~~~~~~~~~~~~~~ + +The **kid** may be set to any ascii string. Useful for storing key names, +generation tools, times etc. + +Public Key +~~~~~~~~~~ + +In addition to the "kty", "kid", "key_ops" and "alg" attributes, a public key will have: + +- **n** The public key's modulus - :ref:`Base64 url encoded ` + + +Example of a 256 bit public key:: + + + python -m phe.command_line genpkey --keysize 256 - | python -m phe.command_line extract - - + { + "kty": "DAJ", + "kid": "Example Paillier public key", + "key_ops": [ "encrypt" ], + "n": "m0lOEwDHVA_VieL2k3BKMjf_HIgagfhNIZy1YhgZF5M", + "alg": "PAI-GN1" + } + + +Private Key +~~~~~~~~~~~ + +.. note:: + + The serialised private key includes the public key. + +In addition to the "kty", "kid", "key_ops" and "alg" attributes, a private key will have: + +- **mu** and **lambda** - The private key's secrets. See Paillier's paper for details. +- **pub** - The Public Key serialised as described above. + + +Example of a 256 bit private key:: + + + python -m phe.command_line genpkey --keysize 256 - + { + "mu": "Dzq1_tz2qDX_-S4shia9Rw34Z9ix9b-fhPi3In76NaI", + "kty": "DAJ", + "key_ops": [ "decrypt" ], + "kid": "Paillier private key generated by pheutil on 2016-05-24 14:18:25", + "lambda": "haFTvA70KcI5XXReJUlQWRQdYHxaUS8baGQGug9dewA", + "pub": { + "alg": "PAI-GN1", + "n": "haFTvA70KcI5XXReJUlQWoZus12aSJJ5EXAvu93xR7k", + "kty": "DAJ", + "key_ops": [ "encrypt" ], + "kid": "Paillier public key generated by pheutil on 2016-05-24 14:18:25" + } + } + + + +.. warning:: + + "kty" and "alg" values should be registered in the + `IANA "JSON Web Key Types" registry `_ + established by JWA. We have not registered **DAJ** or **PAI-GN1** - however we intend to begin that + conversation. diff --git a/docs/usage.rst b/docs/usage.rst index 0ffa488..5456fe0 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -37,7 +37,7 @@ In any event, you can then start encrypting numbers:: >>> encrypted_number_list = [public_key.encrypt(x) for x in secret_number_list] Presumably, you would now share the ciphertext with whoever is playing Role 2 -(see `Serialisation`_ and :ref:`compatibility`). +(see :ref:`serialisation` and :ref:`compatibility`). Decryption @@ -63,7 +63,7 @@ This party does not have access to the private keys, and typically performs oper supplied encrypted data with their own, unencrypted data. Once this party has received some :class:`~phe.paillier.EncryptedNumber` instances (e.g. see -`Serialisation`_), it can perform basic mathematical operations supported by the Paillier +:ref:`serialisation`), it can perform basic mathematical operations supported by the Paillier encryption: 1. Addition of an :class:`~phe.paillier.EncryptedNumber` to a scalar @@ -110,36 +110,3 @@ In some cases it might be possible to boost performance by reducing the precisio >>> a_times_3_5_lp = a * paillier.EncodedNumber.encode(a.public_key, 3.5, 1e-2) -Serialisation -------------- - -This library does not do the serialisation for you. Every :class:`~phe.paillier.EncryptedNumber` -instance has a :attr:`~phe.paillier.EncryptedNumber.public_key` attribute, and serialising each -:class:`~phe.paillier.EncryptedNumber` independently would be heinously inefficient when sending -a large list of instances. It is up to you to serialise in a way that is efficient for your use -case. - -If you want to send a list of values encrypted against one public key, the following is one way to serialise:: - - >>> import json - >>> enc_with_one_pub_key = {} - >>> enc_with_one_pub_key['public_key'] = {'g': public_key.g, - ... 'n': public_key.n} - >>> enc_with_one_pub_key['values'] = [ - ... (str(x.ciphertext()), x.exponent) for x in encrypted_number_list - ... ] - >>> serialised = json.dumps(enc_with_one_pub_key) - -Deserialisation of the above scheme might look as follows:: - - >>> received_dict = json.loads(serialised) - >>> pk = received_dict['public_key'] - >>> public_key_rec = paillier.PaillierPublicKey(g=int(pk['g']), - ... n=int(pk['n'])) - >>> enc_nums_rec = [ - ... paillier.EncryptedNumber(public_key_rec, int(x[0]), int(x[1])) - ... for x in received_dict['values'] - ... ] - -If both parties already know `public_key`, then you might instead send a hash of the public key. - diff --git a/examples/alternative_base.py b/examples/alternative_base.py new file mode 100644 index 0000000..5378cd3 --- /dev/null +++ b/examples/alternative_base.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3.4 +import math +from phe import paillier + + +class ExampleEncodedNumber(paillier.EncodedNumber): + BASE = 64 + LOG2_BASE = math.log(BASE, 2) + +print("Generating paillier keypair") +public_key, private_key = paillier.generate_paillier_keypair() + + +def encode_and_encrypt_example(): + print("Encoding a large positive number. With a BASE 64 encoding scheme") + encoded = ExampleEncodedNumber.encode(public_key, 2.1 ** 20) + print("Checking that decoding gives the same number...") + assert 2.1 ** 20 == encoded.decode() + + print("Encrypting the encoded number") + encrypted = public_key.encrypt(encoded) + + print("Decrypting...") + decrypted_but_encoded = private_key.decrypt_encoded(encrypted, ExampleEncodedNumber) + + print("Checking the decrypted number is what we started with") + assert abs(2.1 ** 20 - decrypted_but_encoded.decode()) < 1e-12 + + +def math_example(): + print("Encoding two large positive numbers. With a BASE 64 encoding scheme") + + a = 102545 + (64 ** 8) + b = 123 + (8 ** 20) + + encoded_a = ExampleEncodedNumber.encode(public_key, a) + encoded_b = ExampleEncodedNumber.encode(public_key, b) + print("Checking that decoding gives the same number...") + assert a == encoded_a.decode() + assert b == encoded_b.decode() + + print("Encrypting the encoded numbers") + encrypted_a = public_key.encrypt(encoded_a) + encrypted_b = public_key.encrypt(encoded_b) + + print("Adding the encrypted numbers") + encrypted_c = encrypted_a + encrypted_b + + print("Decrypting the one encrypted sum") + decrypted_but_encoded = private_key.decrypt_encoded(encrypted_c, ExampleEncodedNumber) + + print("Checking the decrypted number is what we started with") + + print("Decrypted: {}".format(decrypted_but_encoded.decode())) + assert abs((a + b) - decrypted_but_encoded.decode()) < 1e-15 + +if __name__ == "__main__": + encode_and_encrypt_example() + + math_example() \ No newline at end of file diff --git a/benchmarks.py b/examples/benchmarks.py similarity index 97% rename from benchmarks.py rename to examples/benchmarks.py index be0bb4e..331bb18 100644 --- a/benchmarks.py +++ b/examples/benchmarks.py @@ -1,7 +1,6 @@ """ Benchmark key generation, encryption and decryption. -Also compares the speed of modexp and invert between OpenSSL and libGMP. """ import random diff --git a/phe/paillier.py b/phe/paillier.py index 91db757..020a2d8 100644 --- a/phe/paillier.py +++ b/phe/paillier.py @@ -32,7 +32,7 @@ DEFAULT_KEYSIZE = 2048 def generate_paillier_keypair(private_keyring=None, n_length=DEFAULT_KEYSIZE): - """Return a new PaillierPublicKey and PaillierPrivateKey. + """Return a new :class:`PaillierPublicKey` and :class:`PaillierPrivateKey`. Add the private key to *private_keyring* if given. @@ -44,7 +44,7 @@ def generate_paillier_keypair(private_keyring=None, n_length=DEFAULT_KEYSIZE): Returns: tuple: The generated :class:`PaillierPublicKey` and - :class:`PaillierPrivateKey` + :class:`PaillierPrivateKey` """ p = q = n = None n_len = 0 @@ -168,8 +168,25 @@ def encrypt(self, value, precision=None, r_value=None): ValueError: if *value* is out of range or *precision* is so high that *value* is rounded to zero. """ - encoding = EncodedNumber.encode(self, value, precision) + if isinstance(value, EncodedNumber): + encoding = value + else: + encoding = EncodedNumber.encode(self, value, precision) + + return self.encrypt_encoded(encoding, r_value) + + def encrypt_encoded(self, encoding, r_value): + """Paillier encrypt an encoded value. + + Args: + encoding: The EncodedNumber instance. + r_value (int): obfuscator for the ciphertext; by default (i.e. + if *r_value* is None), a random value is used. + + Returns: + EncryptedNumber: An encryption of *value*. + """ # If r_value is None, obfuscate in a call to .obfuscate() (below) obfuscator = r_value or 1 ciphertext = self.raw_encrypt(encoding.encoding, r_value=obfuscator) @@ -206,6 +223,9 @@ def __repr__(self): def decrypt(self, encrypted_number): """Return the decrypted & decoded plaintext of *encrypted_number*. + Uses the default :class:`EncodedNumber`, if using an alternative encoding + scheme, use :meth:`decrypt_encoded` or :meth:`raw_decrypt` instead. + Args: encrypted_number (EncryptedNumber): an :class:`EncryptedNumber` with a public key that matches this @@ -225,13 +245,16 @@ def decrypt(self, encrypted_number): encoded = self.decrypt_encoded(encrypted_number) return encoded.decode() - def decrypt_encoded(self, encrypted_number): - """Return the `EncodedNumber` decrypted from `encrypted_number`. + def decrypt_encoded(self, encrypted_number, Encoding=None): + """Return the :class:`EncodedNumber` decrypted from *encrypted_number*. Args: encrypted_number (EncryptedNumber): an :class:`EncryptedNumber` with a public key that matches this private key. + Encoding (class): A class to use instead of :class:`EncodedNumber`, the + encoding used for the *encrypted_number* - used to support alternative + encodings. Returns: :class:`EncodedNumber`: The decrypted plaintext. @@ -250,21 +273,23 @@ def decrypt_encoded(self, encrypted_number): raise ValueError('encrypted_number was encrypted against a ' 'different key!') + if Encoding is None: + Encoding = EncodedNumber + encoded = self.raw_decrypt(encrypted_number.ciphertext(be_secure=False)) - return EncodedNumber(self.public_key, encoded, + return Encoding(self.public_key, encoded, encrypted_number.exponent) def raw_decrypt(self, ciphertext): """Decrypt raw ciphertext and return raw plaintext. Args: - ciphertext (int): an int (usually from - :meth:`encrypted_number.ciphertext()`) that is to be - Paillier decrypted. + ciphertext (int): (usually from :meth:`EncryptedNumber.ciphertext()`) + that is to be Paillier decrypted. Returns: int: Paillier decryption of ciphertext. This is a positive - integer < :attr:`public_key.n`. + integer < :attr:`public_key.n`. Raises: TypeError: if ciphertext is not an int. @@ -281,7 +306,7 @@ def raw_decrypt(self, ciphertext): class PaillierPrivateKeyring(Mapping): """Holds several private keys and can decrypt using any of them. - Acts like a dict, supports :func:`del`, and :func:`[]` for getting, + Acts like a dict, supports :func:`del`, and indexing with **[]**, but adding keys is done using :meth:`add`. Args: @@ -321,14 +346,13 @@ def decrypt(self, encrypted_number): """Return the decrypted & decoded plaintext of *encrypted_number*. Args: - encrypted_number (EncryptedNumber): an - :class:`EncryptedNumber` encrypted against a known public + encrypted_number (EncryptedNumber): encrypted against a known public key, i.e., one for which the private key is on this keyring. Returns: the int or float that *encrypted_number* was holding. N.B. if - the number returned is an integer, it will not be of type - float. + the number returned is an integer, it will not be of type + float. Raises: KeyError: If the keyring does not hold the private key that @@ -348,6 +372,12 @@ class EncodedNumber(object): then use :meth:`encode`, if de-serializing then use :meth:`__init__`. + + .. note:: + If working with other Paillier libraries you will have to agree on + a specific :attr:`BASE` and :attr:`LOG2_BASE` - inheriting from this + class and overriding those two attributes will enable this. + Notes: Paillier encryption is only defined for non-negative integers less than :attr:`PaillierPublicKey.n`. Since we frequently want to use @@ -425,8 +455,8 @@ class EncodedNumber(object): """ BASE = 16 """Base to use when exponentiating. Larger `BASE` means - that :attr:`exponent` leaks less information. If you vary this, - you'll have to manually inform anyone decoding your numbers. + that :attr:`exponent` leaks less information. If you vary this, + you'll have to manually inform anyone decoding your numbers. """ LOG2_BASE = math.log(BASE, 2) FLOAT_MANTISSA_BITS = sys.float_info.mant_dig @@ -484,7 +514,7 @@ def encode(cls, public_key, scalar, precision=None, max_exponent=None): Returns: EncodedNumber: Encoded form of *scalar*, ready for encryption - against *public_key*. + against *public_key*. """ # Calculate the maximum exponent for desired precision if precision is None: diff --git a/phe/tests/math_test.py b/phe/tests/math_test.py index e0b6d47..4a89292 100644 --- a/phe/tests/math_test.py +++ b/phe/tests/math_test.py @@ -17,7 +17,6 @@ # You should have received a copy of the GNU General Public License # along with pyphe. If not, see . -import logging import unittest import numpy as np diff --git a/phe/tests/paillier_test.py b/phe/tests/paillier_test.py index 4161d82..2a39fc4 100644 --- a/phe/tests/paillier_test.py +++ b/phe/tests/paillier_test.py @@ -245,13 +245,22 @@ def testEncodeFloatDecodeFloat1(self): def testEncryptFloatDecryptFloat2(self): # large positive number - enc = self.EncodedNumberCls.encode(self.public_key, 2.1 ** 20) - self.assertEqual(2.1 ** 20, enc.decode()) + encoded = self.EncodedNumberCls.encode(self.public_key, 2.1 ** 20) + self.assertEqual(2.1 ** 20, encoded.decode()) + + encrypted = self.public_key.encrypt(encoded) + + decrypted_but_encoded = self.private_key.decrypt_encoded(encrypted, self.EncodedNumberCls) + + self.assertAlmostEqual(2.1 ** 20, decrypted_but_encoded.decode()) def testEncryptFloatDecryptFloat3(self): # large negative number - enc = self.EncodedNumberCls.encode(self.public_key, -2.1 ** 63) - self.assertAlmostEqual(-2.1 ** 63, enc.decode()) + encoded = self.EncodedNumberCls.encode(self.public_key, -2.1 ** 63) + self.assertAlmostEqual(-2.1 ** 63, encoded.decode()) + encrypted = self.public_key.encrypt(encoded) + decrypted_but_encoded = self.private_key.decrypt_encoded(encrypted, self.EncodedNumberCls) + self.assertAlmostEqual(-2.1 ** 63, decrypted_but_encoded.decode()) def testEncodedDecreaseExponentTo0(self): # Check that decrease_exponent_to does what it says