Skip to content

Commit

Permalink
#6 Command to add encrypted numbers together.
Browse files Browse the repository at this point in the history
Including cli tests.
  • Loading branch information
hardbyte committed Jan 11, 2016
1 parent ded6b3f commit d013d87
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 6 deletions.
54 changes: 48 additions & 6 deletions phe/command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,19 @@ def encrypt(public, plaintext, output=None):

log("Encrypting: {:+.16f}".format(num))
enc = pub.encrypt(num)
enc = enc.decrease_exponent_to(-32)
serialised = serialise_encrypted(enc)
print(serialised, file=output)

assert enc.exponent == -32

def serialise_encrypted(enc):
enc = enc.decrease_exponent_to(-32)
assert enc.exponent == -32
# TODO #8 Decide on serialization for encrypted number...
obj = json.dumps({
"v": str(enc.ciphertext()),
"e": enc.exponent
})
print('{}'.format(obj), file=output)
return obj


@cli.command()
Expand Down Expand Up @@ -153,14 +156,53 @@ def decrypt(private, ciphertext, output):
priv = phe.PaillierPrivateKey(pub, _lambda, _mu)

log("Decrypting ciphertext")
ciphertext_data = json.load(ciphertext)
enc = load_encrypted_number(ciphertext, pub)
out = priv.decrypt(enc)
print(out, file=output)


def load_encrypted_number(enc_number_file, pub):
ciphertext_data = json.load(enc_number_file)
assert 'v' in ciphertext_data
assert 'e' in ciphertext_data
assert ciphertext_data['e'] == -32

enc = phe.EncryptedNumber(pub,
int(ciphertext_data['v']),
exponent=ciphertext_data['e']
)
out = priv.decrypt(enc)
print(out, file=output)
return enc



@cli.command("addenc")
@click.argument('public', type=click.File('r'))
@click.argument('encrypted_a', type=click.File('r'))
@click.argument('encrypted_b', type=click.File('r'))
@click.option('--output', type=click.File('w'),
help="Save to file instead of stdout")
def add_encrypted(public, encrypted_a, encrypted_b, output):
"""Add two encrypted numbers together.
"""
log("Loading public key")
publickeydata = json.load(public)
pub = load_public_key(publickeydata)

log("Loading first encrypted number")
enc_a = load_encrypted_number(encrypted_a, pub)

log("Loading second encrypted number")
enc_b = load_encrypted_number(encrypted_b, pub)

log("Adding encrypted numbers together")

enc_result = enc_a + enc_b
serialised_result = serialise_encrypted(enc_result)
print(serialised_result, file=output)




if __name__ == "__main__":
cli()
105 changes: 105 additions & 0 deletions phe/tests/cli_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import random
from unittest import TestCase
import tempfile

Expand Down Expand Up @@ -172,3 +173,107 @@ def test_decrypt_float(self):

out = outfile.read()
self.assertAlmostEqual(float(num), float(out))


class TestConsoleHelpers(TestCase):

@classmethod
def setUpClass(cls):
"""Generate a keypair, extract the public key, and encrypt
a list of numbers
"""
cls.private_keyfile = tempfile.NamedTemporaryFile()
cls.public_keyfile = tempfile.NamedTemporaryFile()


cls.runner = CliRunner()
cls.runner.invoke(cli, ['genpkey', '--keysize', '256', cls.private_keyfile.name])
cls.runner.invoke(cli, ['extract', cls.private_keyfile.name, cls.public_keyfile.name])

def setUp(self):
self.enc_a_file = tempfile.NamedTemporaryFile()
self.enc_b_file = tempfile.NamedTemporaryFile()
self.enc_result_file = tempfile.NamedTemporaryFile()

def adder_helper(self, a, b):
self.runner.invoke(cli,
['encrypt', self.public_keyfile.name, '--output', self.enc_a_file.name, '--', str(a)])
self.runner.invoke(cli,
['encrypt', self.public_keyfile.name, '--output', self.enc_b_file.name, '--', str(b)])

result = self.runner.invoke(cli, [
'addenc',
self.public_keyfile.name,
self.enc_a_file.name,
self.enc_b_file.name,
'--output',
self.enc_result_file.name
])

assert result.exit_code == 0

with tempfile.NamedTemporaryFile() as outfile:
result = self.runner.invoke(cli, [
'decrypt', self.private_keyfile.name, self.enc_result_file.name, '--output', outfile.name
])
assert result.exit_code == 0

out = outfile.read()
return float(out)


class TestConsoleAddition(TestConsoleHelpers):
def test_add_int(self):
a, b = 12345, 6789
out = self.adder_helper(a, b)
self.assertAlmostEqual(float(a + b), float(out))

def test_add_large_ints(self):
"""Test adding large integers.
"""
a, b = int(1.2e10), int(1e15)
out = self.adder_helper(a, b)
self.assertAlmostEqual(float(a + b), float(out))


def test_add_signed_int(self):
a, b = 12345, -6789
out = self.adder_helper(a, b)
self.assertAlmostEqual(float(a + b), float(out))

def test_add_floats(self):
a, b = 123.45, 67.89
out = self.adder_helper(a, b)
self.assertAlmostEqual(float(a + b), float(out))

def test_add_large_floats(self):
"""Test adding large integers.
"""
a, b = 2.3e32, 1.4e32
out = self.adder_helper(a, b)
self.assertAlmostEqual(float(a + b), float(out))


class TestFuzz(TestConsoleHelpers):

def test_add_random_ints(self):
"""Test adding random ints
"""
MAX = 1000000000000000
MIN = -MAX

for _ in range(20):
a, b = random.randrange(MIN, MAX), random.randrange(MIN, MAX)
out = self.adder_helper(a, b)
self.assertAlmostEqual(float(a + b), float(out))

def test_add_random_floats(self):
"""Test adding random floating point numbers from the range [0.0, 1.0)
"""
for _ in range(20):
a, b = random.random(), random.random()
out = self.adder_helper(a, b)
self.assertAlmostEqual(float(a + b), float(out))


0 comments on commit d013d87

Please sign in to comment.