Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unifying Capsule and ReconstructedCapsule into a single Capsule class. #38

Merged
merged 41 commits into from
Jan 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
418f7be
Test for single capsule, not ReconstructedCapsule.
jMyles Jan 18, 2018
6c0f260
Comparing newly reconstructed Capsule (which is just a Capsule) to or…
jMyles Jan 18, 2018
a11ea36
We can get a Capsule from either original or reconstructed bytes.
jMyles Jan 18, 2018
c3346ca
Point.from_bytes is now a classmethod.
jMyles Jan 18, 2018
da71b5c
We're not using default_backend at the moment.
jMyles Jan 18, 2018
50f416d
Some PEP8 cleanup.
jMyles Jan 18, 2018
0d2a697
New Capsule.__init__ allows instantiation from either component set.
jMyles Jan 18, 2018
cb32627
Methods from instantiating from bytes of either original or reconstru…
jMyles Jan 18, 2018
9dee6dc
Making byte attributes private.
jMyles Jan 18, 2018
22007a2
More fine-tuning of names.
jMyles Jan 18, 2018
2e2362e
Using method to get points.
jMyles Jan 18, 2018
33e655a
We don't know for sure that we'll have the original data to check - m…
jMyles Jan 18, 2018
6a18e5f
Method to get components for comparison.
jMyles Jan 18, 2018
f792fdf
PRE is no longer injected.
jMyles Jan 27, 2018
cc7649f
Removing methods for Point addition.
jMyles Jan 27, 2018
a0530ec
Private component names.
jMyles Jan 27, 2018
84b4f07
A quick sketch of logic for handling cases where we have a Capsule bu…
jMyles Jan 27, 2018
feb967d
Prviate reconstruct method.
jMyles Jan 27, 2018
c0782a2
Single from_bytes method. See https://github.com/nucypher/pyUmbral/p…
jMyles Jan 27, 2018
0eef0c2
Touching up TODOs.
jMyles Jan 27, 2018
d943ba2
Moving _reconstructed_bytes into to_bytes.
jMyles Jan 27, 2018
0240b0c
Removing logic branch for the case where we don't have original capsu…
jMyles Jan 28, 2018
211a920
Test for invalid capsule raising an exception.
jMyles Jan 28, 2018
68c9c27
Custom exception & raising it in proper place. Closes #39.
jMyles Jan 28, 2018
3311bb6
Removed random from test imports. Also some linting.
jMyles Jan 28, 2018
5e66579
Made decapsulate_reencrypted public.
jMyles Jan 29, 2018
b4744d8
pre.decrypt_reencryted moved to capsule.get_contents
jMyles Jan 29, 2018
f3be38b
Removing Capsule.open()
jMyles Jan 29, 2018
007ef5d
Test now uses decrypt() instead of get_contents().
jMyles Jan 29, 2018
b6624bc
Hanging contents() that was no longer used.
jMyles Jan 29, 2018
f375e99
Capsule.get_contents() is now Capsule.decrypt() and Capsule._get_cont…
jMyles Jan 29, 2018
5d9696a
Typo pointed out by @cygnusv.
jMyles Jan 29, 2018
3fb70db
Better test for equality for original capsule.
jMyles Jan 28, 2018
0df84c4
Test for compatibility from reconstructed bytes.
jMyles Jan 28, 2018
d95beda
Methods for establishing equality.
jMyles Jan 28, 2018
40d3bdc
A test for the unequal cases.
jMyles Jan 28, 2018
54acbf3
New, longer representation of a reconstructed (activated?) Capsule.
jMyles Jan 31, 2018
7b29302
Better names for some things.
jMyles Jan 31, 2018
218f33a
Reintroducing... PRE.decrypt!
jMyles Jan 31, 2018
cd64f1d
No more "is_reconstructed" needed.
jMyles Jan 31, 2018
422ae94
Moving opening and decrypting logic over to PRE.
jMyles Jan 31, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 86 additions & 33 deletions tests/test_umbral.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import pytest

from umbral import umbral, keys
from umbral.bignum import BigNum
from umbral.point import Point
from umbral.umbral import Capsule


# (N,threshold)
parameters = [
# (N,threshold)
(1, 1),
(6, 1),
(6, 4),
Expand Down Expand Up @@ -40,20 +42,55 @@ def test_simple_api(N, threshold):
pub_key_bob = priv_key_bob.get_pub_key(params)

plain_data = b'attack at dawn'
enc_data, capsule = pre.encrypt(pub_key_alice, plain_data)
ciphertext, capsule = pre.encrypt(pub_key_alice, plain_data)

dec_data = pre.decrypt(priv_key_alice, capsule, enc_data)
assert dec_data == plain_data
cleartext = pre.decrypt(capsule, priv_key_alice, ciphertext, pre)
assert cleartext == plain_data

rekeys, _unused_vkeys = pre.split_rekey(priv_key_alice, pub_key_bob, threshold, N)
for rekey in rekeys:
cFrag = pre.reencrypt(rekey, capsule)
capsule.attach_cfrag(cFrag)

reenc_dec_data = pre.decrypt_reencrypted(
priv_key_bob, pub_key_alice, capsule, enc_data
reenc_cleartext = pre.decrypt(
capsule, priv_key_bob, ciphertext, pre, pub_key_alice
)
assert reenc_dec_data == plain_data
assert reenc_cleartext == plain_data


def test_bad_capsule_fails_reencryption():
pre = umbral.PRE()

priv_key_alice = keys.UmbralPrivateKey.gen_key(pre.params)
pub_key_alice = priv_key_alice.get_pub_key(pre.params)

k_frags, _unused_vkeys = pre.split_rekey(priv_key_alice, pub_key_alice, 1, 2)

bollocks_capsule = Capsule(point_eph_e=Point.gen_rand(curve=pre.params.curve),
point_eph_v=Point.gen_rand(curve=pre.params.curve),
bn_sig=BigNum.gen_rand(curve=pre.params.curve))

with pytest.raises(Capsule.NotValid):
pre.reencrypt(k_frags[0], bollocks_capsule)


def test_two_unequal_capsules():
pre = umbral.PRE()
one_capsule = Capsule(point_eph_e=Point.gen_rand(curve=pre.params.curve),
point_eph_v=Point.gen_rand(curve=pre.params.curve),
bn_sig=BigNum.gen_rand(curve=pre.params.curve))

another_capsule = Capsule(point_eph_e=Point.gen_rand(curve=pre.params.curve),
point_eph_v=Point.gen_rand(curve=pre.params.curve),
bn_sig=BigNum.gen_rand(curve=pre.params.curve))

assert one_capsule != another_capsule

reconstructed_capsule = Capsule(e_prime=Point.gen_rand(curve=pre.params.curve),
v_prime=Point.gen_rand(curve=pre.params.curve),
noninteractive_point=Point.gen_rand(curve=pre.params.curve))

assert reconstructed_capsule != one_capsule


@pytest.mark.parametrize("N,threshold", parameters)
Expand All @@ -64,25 +101,25 @@ def test_m_of_n(N, threshold):
priv_bob = pre.gen_priv()
pub_bob = pre.priv2pub(priv_bob)

sym_key, capsule_alice = pre._encapsulate(pub_alice)

sym_key, capsule = pre._encapsulate(pub_alice)
kfrags, vkeys = pre.split_rekey(priv_alice, pub_bob, threshold, N)

for kfrag in kfrags:
assert kfrag.verify(pub_alice, pub_bob, pre.params)
assert kfrag.is_consistent(vkeys, pre.params)

for kfrag in kfrags[:threshold]:
cfrag = pre.reencrypt(kfrag, capsule_alice)
capsule_alice.attach_cfrag(cfrag)
ch = pre.challenge(kfrag, capsule_alice, cfrag)
assert pre.check_challenge(capsule_alice, cfrag, ch, pub_alice, pub_bob)
cfrag = pre.reencrypt(kfrag, capsule)
capsule.attach_cfrag(cfrag)
ch = pre.challenge(kfrag, capsule, cfrag)
assert pre.check_challenge(capsule, cfrag, ch, pub_alice, pub_bob)

capsule_bob = capsule_alice.reconstruct()
# assert capsule.is_openable_by_bob() # TODO: Is it possible to check here if >= m cFrags have been attached?
# capsule.open(pub_bob, priv_bob, pub_alice)

sym_key_2 = pre._decapsulate_reencrypted(pub_bob, priv_bob, pub_alice, capsule_bob, capsule_alice)

assert sym_key_2 == sym_key
capsule._reconstruct()
sym_key_from_capsule = pre.decapsulate_reencrypted(pub_bob, priv_bob, pub_alice, capsule)
assert sym_key == sym_key_from_capsule


def test_kfrag_serialization():
Expand Down Expand Up @@ -140,14 +177,22 @@ def test_capsule_serialization():
capsule_bytes = capsule.to_bytes()

# A Capsule can be represented as the 98 total bytes of two Points (33 each) and a BigNum (32).
# TODO: Do we want to include the cfrags as well? See #20.
assert len(capsule_bytes) == 33 + 33 + 32 == 98

new_capsule = umbral.Capsule.from_bytes(capsule_bytes,
umbral.UmbralParameters().curve)
assert new_capsule.point_eph_e == capsule.point_eph_e
assert new_capsule.point_eph_v == capsule.point_eph_v
assert new_capsule.bn_sig == capsule.bn_sig

# Three ways to think about equality.
# First, the public approach for the Capsule. Simply:
new_capsule == capsule

# Second, we show that the original components (which is all we have here since we haven't reconstructed) are the same:
assert new_capsule.original_components() == capsule.original_components()

# Third, we can directly compare the private original component attributes (though this is not a supported approach):
assert new_capsule._point_eph_e == capsule._point_eph_e
assert new_capsule._point_eph_v == capsule._point_eph_v
assert new_capsule._bn_sig == capsule._bn_sig


def test_reconstructed_capsule_serialization():
Expand All @@ -163,18 +208,26 @@ def test_reconstructed_capsule_serialization():

capsule.attach_cfrag(cfrag)

rec_capsule = capsule.reconstruct()
rec_capsule_bytes = rec_capsule.to_bytes()
capsule._reconstruct()
rec_capsule_bytes = capsule.to_bytes()

# A reconstructed Capsule is:
# three points, representable as 33 bytes each (the original), and
# two points and a bignum (32 bytes) (the activated components), for 197 total.
assert len(rec_capsule_bytes) == (33 * 3) + (33 + 33 + 32)

new_rec_capsule = umbral.Capsule.from_bytes(
rec_capsule_bytes,
umbral.UmbralParameters().curve)

# Again, the same three perspectives on equality.
new_rec_capsule == capsule

# A reconstructed Capsule is three points, representable as 33 bytes each.
assert len(rec_capsule_bytes) == 99
assert new_rec_capsule.reconstructed_components() == capsule.reconstructed_components()

new_rec_capsule = umbral.ReconstructedCapsule.from_bytes(
rec_capsule_bytes,
umbral.UmbralParameters().curve)
assert new_rec_capsule.point_eph_e_prime == rec_capsule.point_eph_e_prime
assert new_rec_capsule.point_eph_v_prime == rec_capsule.point_eph_v_prime
assert new_rec_capsule.point_eph_ni == rec_capsule.point_eph_ni
assert new_rec_capsule._point_eph_e_prime == capsule._point_eph_e_prime
assert new_rec_capsule._point_eph_v_prime == capsule._point_eph_v_prime
assert new_rec_capsule._point_noninteractive == capsule._point_noninteractive


def test_challenge_response_serialization():
Expand All @@ -197,7 +250,7 @@ def test_challenge_response_serialization():
assert len(ch_resp_bytes) == (33 * 4) + (32 * 3) == 228

new_ch_resp = umbral.ChallengeResponse.from_bytes(
ch_resp_bytes, umbral.UmbralParameters().curve)
ch_resp_bytes, umbral.UmbralParameters().curve)
assert new_ch_resp.point_eph_e2 == ch_resp.point_eph_e2
assert new_ch_resp.point_eph_v2 == ch_resp.point_eph_v2
assert new_ch_resp.point_kfrag_commitment == ch_resp.point_kfrag_commitment
Expand Down
10 changes: 5 additions & 5 deletions umbral/dem.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ def encrypt(self, data: bytes, authenticated_data: bytes=None):
# Ciphertext will be a 12 byte nonce, the ciphertext, and a 16 byte tag.
return nonce + enc_data

def decrypt(self, enc_data: bytes, authenticated_data: bytes=None):
def decrypt(self, ciphertext: bytes, authenticated_data: bytes=None):
"""
Decrypts data using ChaCha20-Poly1305 and validates the provided
authenticated data.
"""
nonce = enc_data[:DEM_NONCE_SIZE]
ciphertext = enc_data[DEM_NONCE_SIZE:]
plaintext = self.cipher.decrypt(nonce, ciphertext, authenticated_data)
return plaintext
nonce = ciphertext[:DEM_NONCE_SIZE]
ciphertext = ciphertext[DEM_NONCE_SIZE:]
cleartext = self.cipher.decrypt(nonce, ciphertext, authenticated_data)
return cleartext
6 changes: 3 additions & 3 deletions umbral/point.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def to_affine(self):
return (backend._bn_to_int(affine_x), backend._bn_to_int(affine_y))

@classmethod
def from_bytes(self, data, curve):
def from_bytes(cls, data, curve):
"""
Returns a Point object from the given byte data on the curve provided.
"""
Expand Down Expand Up @@ -125,7 +125,7 @@ def from_bytes(self, data, curve):
)
backend.openssl_assert(res == 1)

return Point(ec_point, curve_nid, affine_x.group)
return cls(ec_point, curve_nid, affine_x.group)

# Handle uncompressed point
elif data[0] == 4:
Expand Down Expand Up @@ -176,7 +176,7 @@ def get_generator_from_curve(cls, curve):
generator = backend._lib.EC_GROUP_get0_generator(group)
backend.openssl_assert(generator != backend._ffi.NULL)

return Point(generator, curve_nid, group)
return cls(generator, curve_nid, group)

@classmethod
def get_order_from_curve(cls, curve):
Expand Down
Loading