Fully Homomorphic Encryption and Privacy

by Thor R. Mirchandani

In the modem world, people are becoming more and more dependent on using other people's computers for their storage and computing needs.  Cloud technologies, phone apps, and Software as a Service (SaaS) are just a few examples of applications that rely on other people's machines.

Most people understand the absolute necessity for securing their data in the Cloud and rely on using some form of encryption.  Unfortunately, encrypting data in transit or on a cloud disk using most of the common encrypt ion algorithms is not sufficient to ensure privacy.

When you browse, view, or manipulate the data, it is decrypted to plain text and becomes visible to a sufficiently privileged software program.  Can you really know for sure who else is using your cloud instance?

Even on a hardened system, data can be read directly from CPU registers and data buses by a motivated attacker.  If that sounds far-fetched, this is exactly how hardware hacker extraordinaire Bunnie Huang hacked the Xbox!  For more frivolous examples, consider the technical underpinnings of Kraftwerk's 1981 song Pocket Calculator.  If individuals can do it, what are the capabilities of more well-funded organizations?

Fully Homomorphic Encryption

The bottom line is that to be usable, information encrypted with traditional methods has to be visible in plain text at some point, if only for a brief moment.  Another way to look at it is that a "man-in-the-middle" attack is always possible and as long as the attacker is creative when it comes to defining where the "middle" is!

Does it have to be that way?  What if we could reliably manipulate encrypted information without ever decrypting it?  Turns out that we can.  Enter Fully Homomorphic Encryption (FHE).

FHE is a class of ciphers that have the interesting quality that an arbitrary computation on ciphertexts generates an encrypted result which, when decrypted, matches what you would see had the same computations been performed on the plaintext.  Sounds like black magic, doesn't it?

Theoretical FHE systems were postulated in the late 1970s.  In the following decades, researchers implemented systems that permitted a limited number and limited types of computations.  Then in 2009, Craig Gentry described a system that could perform any computation, albeit very slowly.  Basic computations would take hours!  But it didn't take long for Gentry and other researchers to come up with implementations many orders of magnitude faster.  Those systems are finding practical uses today.  (Crypto Trivia: Craig Gentry received a MacArthur Genius Award for his work on encryption.)

A Practical SaaS Example

One application for FHE is SaaS.  Alice might have valuable data and Bob might have a valuable algorithm.  Neither wants to reveal their "secret sauce" to the other.  With traditional encryption methods, this would not be possible: The algorithm would have to operate on plaintext data, and Alice and Bob would have to duke it out regarding who should lift the skirt.  Typical solutions to the dilemma involve lawyers and NDAs.

Moments before he took his last breath, Alice's grandfather gave her three top secret numbers that will lead to the map coordinates of the spot where his treasure is hidden.  To get the real coordinates, Alice must add two of the numbers and multiply the third by a constant.  Alas, while cryptographically savvy, Alice is arithmetically challenged and has to enlist outside help.

Fortunately, Bob runs a service that can add and multiply encrypted numbers.  Alice agrees to send Bob her FHE encrypted numbers.  Bob will then perform the calculations on the two numbers without ever seeing them in plaintext.  Calculations completed, Bob returns the encrypted results to Alice without ever seeing the plaintext results.  When Alice gets the results, she can simply decrypt them to get the coordinates.

We are implementing this interaction in Python - see the listing for fullyhomo.py that follows this article.

The code was written for Python 3, but should work fine with Python 2 as well.  It will run on Ubuntu Linux using anyone of the following three commands:

$ ./fullhomo.py
 or
$ python3 fullhomo.py
 or
$ python fullhomo.py

Similar commands are available on Windows.  Here is a typical output from running the program:

$ ./fullyhomo.py
SaaS Example:
Alice wants to use Bob's calculation service to calculate 5 + 10
She encrypts 5
...and the encrypted value is 408231311223330758911876050904...
She then encrypts 10
...and the encypted value is 6811593647043826157618544194678...

Alice also wants to to multiply 6 with the constant 3
She encrypts 6
...and the encrypted value is 275872367736262799842862895600..

Then Alice sends the encrypted values to Bob along with her public key

Bob adds the two encrypted values without knowing what they are
the encrypted result is 3509690235178988491246734744677382694...
Bob multiplies the third encrypted value with the constant
the encrypted result is 8919545079897387397953169089569936011...

Bob sends the encrypted results back to Alice
Alice uses her private key to view the plain text results:
Addition: 15
Multiplication: 18

Armed with the coordinates, Alice packs her shovel and books a trip to Niger.  Or did he mean Mauritania?  Or maybe Namibia?  Surely the treasure isn't in the middle of the Atlantic?!?!  East versus West, North versus South, these things do matter!

The Code

The Python code implements an FHE algorithm called the Paillier cryptosystem.  To keep things brief and simple, the code only implements the operations required to for the addition and multiplication operations.  Also, the key pair is hard coded for the sake of simplicity.  A full fledged implementation would provide code to generate random keys.

The class FullyHomoCipher on line 14 is the Paillier encryption code.  The class BobsCalculationService on line 54 defines the operations for addition and multiplication of Paillier-encrypted values.

Our treasure hunt adventure starts on line 75 and uses the two classes described above.  It's extensively commented in order to make it easy for the interested reader to modify and experiment.

A note of caution for readers that aren't familiar with the Python language: Unlike most languages, Python is white-space sensitive, and indentation matters.  It's important to preserve the indentation or the program will not execute properly.

FHE Now and Tomorrow

Our SaaS example is obviously a toy, but that's to be expected from about 140 lines of commented Python code.  More robust, fully featured FHEs built around stronger algorithms are finding new applications every day.

Software as a Service is only one application that's a good match for FHE.  Other types of applications include smart contracts, block chain systems, data mining, "vanity" hashes, end-to-end encrypted database queries, anonymous identity systems, data integrity verification, and so on.  With the rapid development in the field, we can expect many other uses in the very near future.

FHE is currently deployed across several industries and problem domains, including electronic voting systems, genomics, and payment systems, and we predict widespread adoption in areas such as health care, smart power grids, and finance to take place very soon.

#!/usr/bin/env python3

import random

# Alice's Private/Public key pair, hard coded for simplicity
class PrivateKey():
        lambdA = 73842165240981452554905699590449542088961757004289873177979834078905122488912
        mu = 14623866067924162049758185900912462985982902037214491087468255488155427133263

class PublicKey():
        n = 73842165240981452554905699590449542089513117936657660262085094366199671389241
        n2 = 5452665367476409421070096932669081750981552257584472365472731868555542659457163641469759175897028833132670904633495629764635203858875472896093430930556081
        g = 73842165240981452554905699590449542089513117936657660262085094366199671389242

# Alice's Implementation of a fully homomorphic Paillier cipher
class FullyHomoCipher():
        def __init__(self, a1, b1):
                self.a = a1
                self.b = b1
        def expCalc(self, base,exponent,modulus):
                result = 1
                while exponent > 0:
                        if exponent & 1 == 1:
                                result = (result * base) % modulus
                        exponent = exponent >> 1
                        base = (base * base) % modulus
                return result

        def encrypt(self, pub, plain):
                while True:
                        r = random.getrandbits(128)
                        if r > 0 and r < pub.n:
                                break
                x = self.expCalc(r, pub.n, pub.n2)
                cipher = (self.expCalc(pub.g, plain, pub.n2) * x) % pub.n2
                return cipher

        def decrypt(self, priv, pub, cipher):
                x = self.expCalc(cipher, priv.lambdA, pub.n2) - 1
                plain = ((x // pub.n) * priv.mu) % pub.n
                return plain

        def encrypt_message(self, pub, m):
                r = random.randrange(256, pub.n)
                b = self.encrypt(pub, r)
                a = (m-r) % pub.n
                self.a = a
                self.b = b

        def decrypt_message(self, priv, pub):
                val = (self.a + self.decrypt(priv, pub, self.b)) % pub.n
                return val

# Bob's encrypted calculation service
class BobsCalculationService():
        # Add two encrypted numbers
        def encrypted_add(self, pub, a, b):
                return a * b % pub.n2
        def sum (self, c1, c2, pub):
                a = (c1.a + c2.a) % pub.n
                b = self.encrypted_add(pub, c1.b, c2.b)
                c = FullyHomoCipher(a, b)
                return c
        # Multiply two encrypted numbers with a constant (Bob)
        def encrypted_mult(self, pub, a, n):
                return FullyHomoCipher(-1, -1).expCalc(a, n, pub.n2)
        def product(self, const, c1, pub):
                a = (c1.a * const) % pub.n
                b = self.encrypted_mult(pub, c1.b, const)
                c = FullyHomoCipher(a, b)
                return c

# THE SAAS EXAMPLE BEGINS HERE
if __name__ == '__main__':
         # Alice's Key Pair
         pub = PublicKey
         priv = PrivateKey
         # The top secret numbers Alice wants to use
         secretNumber1 = 5
         secretNumber2 = 10
         secretNumber3 = 6
         const = 3
         # The Cipher objects Alice uses for encryption
         alice1 = FullyHomoCipher(-1,-1)
         alice2 = FullyHomoCipher(-1,-1)
         alice3 = FullyHomoCipher(-1,-1)
         # Alice performs encryption
         print ("SaaS Example:")
         print ("Alice wants to use Bob's calculation service to calculate ", secretNumber1, "+", secretNumber2)
         print ("She encrypts ", secretNumber1)
         alice1.encrypt_message(pub, secretNumber1)
         print ("...and the encrypted value is ", alice1.a, alice1.b)
         print ("She then encrypts ", secretNumber2)
         alice2.encrypt_message(pub, secretNumber2)
         print ("...and the encypted value is ", alice2.a, alice2.b)
         print ("")
         print ("Alice also wants to to multiply ", secretNumber3," with the constant ", const)
         print ("She encrypts ", secretNumber3)
         alice3.encrypt_message(pub, secretNumber3)
         print ("...and the encrypted value is ", alice3.a, alice3.b)
         print ("")
         print ("Then Alice sends the encrypted values to Bob along with her public key")
         print ("")
         # These are the encrypted values Alice sends to Bob
         encr1_a=alice1.a
         encr1_b=alice1.b
         encr2_a=alice2.a
         encr2_b=alice2.b
         encr3_a=alice3.a
         encr3_b=alice3.b
         # Bob's Cipher objects, initialized with Alice's encrypted numbers
         # Since Bob doesn't have the private key he can't decrypt the numbers
         bob1 = FullyHomoCipher(encr1_a, encr1_b)
         bob2 = FullyHomoCipher(encr2_a, encr2_b)
         bob3 = FullyHomoCipher(encr3_a, encr3_b)
         # Addition
         print ("Bob adds the two encrypted values without knowing what they are")
         result1=BobsCalculationService().sum(bob1, bob2, pub)
         print ("the encrypted result is ", result1.a, result1.b)
         # Multiplication with a constant
         print ("Bob multiplies the third encrypted value with the constant")
         result2=BobsCalculationService().product(const, bob3, pub)
         print ("the encrypted result is ", result2.a, result2.b)
         print ("")
         print ("Bob sends the encrypted results back to Alice")
         print ("Alice uses her private key to view the plain text results:")
         print ("Addition: ", result1.decrypt_message(priv, pub))
         print ("Multiplication: ", result2.decrypt_message(priv, pub))

Code: fullyhomo.py

Return to $2600 Index