Skip to content
This repository has been archived by the owner. It is now read-only.
C++ Python Other
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

PyAono Build Status

❗ This library has been deprecated due to changes in strategy and roadmap. To actively contribute based on our current roadmap, checkout OpenMined, PySyft, or join our slack

A Python implementation of the homomorphic encryption scheme by Yoshinori Aono et al.

This scheme was first introduced in the paper - "Fast and Secure Linear Regression and Biometric Authentication with Security Update" by Yoshinori Aono et al. A really unique feature supported by this proposal is the support for key switching. All the code is written in PARI library in C++.

This code also has an independent header containing a PARI implementation of the Knuth-Yao's Algorithm for sampling from a discrete Gaussian distribution. The paper followed for understanding and implementing the algorithm - High Precision Discrete Gaussian Sampling on FPGAs.

NOTE : Running the homomorphic multiplication gives a equation matrix as opposed to the message being equation. This is because after homomorphic multiplication, we don't get message but we get equation.


  1. Install Docker

    • If you're on macOS and use Homebrew, run brew cask install docker; open -a docker
  2. Run these commands:

make build
make run


  1. Install Docker

    • If you're on macOS and use Homebrew, run brew cask install docker; open -a docker
  2. Run make build

  3. Edit source code as you wish

  4. Run make dev


What's new in the latest version?

We just rolled out the support for key switching/rotation. Check it out by running You can also run to check out homomorphic operations working.
This scheme has an advantage over other schemes that the key switching can both, increase as well as decrease the security level of the ciphertext!


New here? Check out our sample tutorial on YouTube recorded by Mayank. The tutorial covers a basic overview of how to use the API. More in-depth tutorials coming soon. Keep checking out YouTube channel and our GitHub repositories.


Welcome! If you are intrigued by our work and want to contribute, then please go through the tutorial as mentioned in the News section and contact any of the contributors. You should also go through this introductory video by Andrew Trask, of the OpenMined platform.

Difference between Key Rotation and Security Update

Key Rotation means that we change ciphertext associated with the key-pair (pk1, sk1), where pk1 is used to encrypt the message and sk1 is used to decrypt to message to another ciphertext associated with keypair (pk2, sk2), without decrypting the first ciphertext.
We specifically call it Key Rotation when the parameters have this relation n1 = n2.
We call it Security Update if the paramters have this relation n1 < n2, meaning that the security of new ciphertext has been updated and increased.

Notes regarding the use and order of homomorphic function and key switching [Important]

The following cases of operations have been implemented. Support for other cases might require some more literature reading and will follow shortly. Please take a look at he following options which are presently supported:


The scheme only supports ONE homomorphic multiplication at the moment.


  • normal-ciphertext means a ciphertext which has never been refreshed via either key rotation or security update and has only been operated additively or has never undergone any operation is just fresh.
  • mult-ciphertext means a ciphertext which has undergone ONE multiplication no matter if it has undergone key rotation or security update before or not. Note that a mult-ciphertext CANNOT be rotated or refreshed by key rotation or security update because of it's peculiar structure. It has to be decrypted.
  • rotated-ciphertext means a ciphertext which has undergone refreshment via either key rotation or security update.


If + is supported it implies - is permitted as well.

Permitted operations and their outputs:

  • normal-ciphertext + normal-ciphertext = normal-ciphertext and normal-ciphertext * normal-ciphertext = mult-ciphertext
  • mult-ciphertext + mult-ciphertext = mult-ciphertext
  • rotated-ciphertext + rotated-ciphertext = rotated-ciphertext and rotated-ciphertext * rotated-ciphertext = mult-ciphertext
  • rotated-ciphertext + normal-ciphertext = rotated-ciphertext and rotated-ciphertext * normal-ciphertext = mult-ciphertext
  • normal-ciphertext + rotated-ciphertext = rotated-ciphertext and normal-ciphertext * rotated-ciphertext = mult-ciphertext
  • mult-ciphertext + normal-ciphertext = mult-ciphertext and normal-ciphertext + mult-ciphertext = mult-ciphertext[?]
  • mult-ciphertext + rotated-ciphertext = mult-ciphertext and rotated-ciphertext + mult-ciphertext = mult-ciphertext[?]

[?] Meaning:

The above bullets marked with [?] means that these operations don't exist conventionally in the actual paper. I have implemented them as my own decision given they might come in handy during the course of our project. I implemented them in this way (illustrating just one example, rest are similar to this one):
mult-ciphertext + (normal-ciphertext-of-1s * normal-ciphertext) = mult-ciphertext
normal-ciphertext-of-1s mean that it is a ciphertext which is deliberately made my encrypting a vector of just all 1s, so that if it is multiplied with anything (say x), this x remains same, but since x is of type - normal-ciphertext and also this normal-ciphertext-of-1s is also of the type - normal-ciphertext, this means their homomorphic multiplication * will yield a ciphertext encrypting x of the type - mult-ciphertext. This makes the whole above homomorphic addition as mult-ciphertext + mult-ciphertext = mult-ciphertext.

Importing API

The API can be imported using the command import Aono. It currently supports the following functions and classes:

Functions Supported:

1. pari_init(pari_size, max_prime)

pari_init() is the function that needs to be called before dealing with this API. pari_size defines the size of stack we'll be using, and max_prime defines the pre computed prime table.

Arguments: pari_size (int), max_prime (int)

2. pari_close()

pari_close() function has to be called at the end of each program to clear the memory used.



This class abstracts the GEN variable in C++, making it available through python interface. The class is compatible with +, *, /, -, __getitem__ , %, and print.
  • Class Data:

    1. value (GEN)
  • __init__(self, x) The constructor converts x to a GEN variable. Arguments: x (int)


  • Class Data:
    1. n, s, sigma, l, lambda (ints)
    2. q, p (pari_GEN)


  • Class Data:

    1. sk (pari_GEN)
    2. params (parameters*)
  • __init__(self, sk = None, parmas = None)
    The constructor initiates class data. Arguments: sk (pari_GEN), params (parameters*)

  • decrypt(self, ct)
    decrypt() method returns the plaintext (pari_GEN) encrypted in ciphertext ct. Arguments: ct (pari_GEN)

  • serialize(self)


  • Class Data:

    1. pk (public_key_pack*)
    2. params (parameters*)
    3. g (globalvars*)
  • __init__(self, pk = None, params = None, g = None) The constructor initiates the class data. Arguments: pk (public_key_pack*), params (parameters*), g (globalvars*)

  • encrypt(self, pt)
    encrypt() method returns the ciphertext (GEN) which encrypts plaintext pt. Arguments: pt (pari_GEN)

  • serialize(self)


  • Class Data:
    1. sk (secret_key)
    2. pk (public_key)


  • generate_key(self, lambda, l, n, s, sigma, degree_p) generate_key() method returns the keys, which is of type key_pair. Here s defines the tailprune and degree_p defines the bit_size of p to be generated. Arguments: lambda (int), l (int), n (int), s (int), sigma (int), degree_p (int).

  • deserialize(self)


The class is compatible with `’+’, '*', and '-' operators`
  • Class Data:

    1. value (pari_GEN)
    2. pk (public_key*)
    3. params (parameters*)
  • __init__(self, plaintext = None, pk, params)
    The constuctor method takes two arguments: plaintext (pari_GEN variable), pk (public_key*), params (parameters*)

TODO - Remove params, it can be taken from pk.

  • decrypt(self, sk)
    decrypt() method returns the decrypted ciphertext which is pari_GEN variable. Arguments: sk (secret_key*)


  • Class Data:

    1. pk (public_key_pack*)
    2. params (parameters*)
    3. old_params (parameters*)
    4. g (globalvars*)
    5. XComponent (pari_GEN)
    6. YComponent (pari_GEN)
    • __init__(self, X, Y, params, params_old, g, pk) The constructor initiates the class data. Arguments: pk (public_key_pack*), params (parameters*), g (globalvars*)

    • cipher_switch(self, ciphertext)
      cipher_switch() method returns the ciphertext (ciphertext) which is obtained by switching the ciphertext passed to function with the updatation key. Arguments: ct (ciphertext)


  • generate_key(self, key_pair*, key_pair*)
    generate_key() method returns the updation key (updation_key) which is the key that can be used to switch any ciphertext which is associated with the first (in order of arguments) key_pair to the ciphertext which is associated with the second key_pair. Arguments: key1 (key_pair), key2 (key_pair)


  • Tweaks to homomorphic functions to support rotated or updated ciphertexts.
  • Support for plaintext-ciphertext multiplication.
  • Basic testing suite.
  • Taking care of negative errors by mapping ciphertext to a symmteric group before decryption.
  • Add a matrix reading support to testing suite.
  • Add key switching tests to testing suite.
  • Update README with pari_GEN.
  • Add bootstrapping support.
  • Serialization and Deserialization support.
You can’t perform that action at this time.