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

Stateless API #258

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 12 additions & 14 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ Alice can open the capsule and decrypt the ciphertext with her private key.
ciphertext, capsule = pre.encrypt(alices_public_key, plaintext)

# Decrypt data with Alice's private key.
cleartext = pre.decrypt(ciphertext=ciphertext,
capsule=capsule,
decrypting_key=alices_private_key)
cleartext = pre.decrypt_original(ciphertext=ciphertext,
capsule=capsule,
decrypting_key=alices_private_key)


**Re-Encryption Key Fragments**
Expand Down Expand Up @@ -130,13 +130,13 @@ Bob must gather at least ``threshold`` cfrags in order to activate the capsule.
# Several Ursulas perform re-encryption, and Bob collects the resulting `cfrags`.
# He must gather at least `threshold` `cfrags` in order to activate the capsule.

capsule.set_correctness_keys(delegating=alices_public_key,
receiving=bobs_public_key,
verifying=alices_verifying_key)
prepared_capsule = capsule.with_correctness_keys(delegating=alices_public_key,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads like a layer of indirection to me. We had a "capsule", but if we add something to it, we get not the same "capsule", but a different "capsule"? Of a different class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above about having a VerifyingMetadata (or something along these lines) instead that would wrap these three keys. What do you think about this approach?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And then compose that on this capsule?

I'm interested to hear about the why in addition to the what. Why is this change (and the rest of the capsule fissure) helpful in your mind?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the verifying metadata is secondary to the capsule. A capsule can be used without it. You can keep it as a separate object, or as a part of another object combining both the capsule and the keys, that's details. The change I'm introducing here is to remove the mutability. I do not understand why you are calling it a fissure.

receiving=bobs_public_key,
verifying=alices_verifying_key)

cfrags = list() # Bob's cfrag collection
for kfrag in kfrags[:10]:
cfrag = pre.reencrypt(kfrag=kfrag, capsule=capsule)
cfrag = pre.reencrypt(kfrag=kfrag, prepared_capsule=prepared_capsule)
cfrags.append(cfrag) # Bob collects a cfrag


Expand All @@ -148,12 +148,10 @@ and then decrypts the re-encrypted ciphertext.
.. code-block:: python

# Bob activates and opens the capsule
for cfrag in cfrags:
capsule.attach_cfrag(cfrag)

bob_cleartext = pre.decrypt(ciphertext=ciphertext,
capsule=capsule,
decrypting_key=bobs_private_key)
bob_cleartext = pre.decrypt_reencrypted(ciphertext=ciphertext,
capsule=prepared_capsule,
cfrags=cfrags,
decrypting_key=bobs_private_key)
assert bob_cleartext == plaintext

See more detailed usage examples in the docs_ directory.
Expand All @@ -171,7 +169,7 @@ To install pyUmbral, simply use ``pip``:
$ pip3 install umbral


Alternatively, you can checkout the repo and install it from there.
Alternatively, you can checkout the repo and install it from there.
The NuCypher team uses ``pipenv`` for managing pyUmbral's dependencies.
The recommended installation procedure is as follows:

Expand Down
52 changes: 23 additions & 29 deletions docs/examples/umbral_simple_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@
# Since data was encrypted with Alice's public key,
# Alice can open the capsule and decrypt the ciphertext with her private key.

cleartext = pre.decrypt(ciphertext=ciphertext,
capsule=capsule,
decrypting_key=alices_private_key)
cleartext = pre.decrypt_original(ciphertext=ciphertext,
capsule=capsule,
decrypting_key=alices_private_key)
print(cleartext)

#5
Expand All @@ -72,19 +72,19 @@
#7
# Attempt Bob's decryption (fail)
try:
fail_decrypted_data = pre.decrypt(ciphertext=ciphertext,
capsule=bob_capsule,
decrypting_key=bobs_private_key)
fail_decrypted_data = pre.decrypt_original(ciphertext=ciphertext,
capsule=bob_capsule,
decrypting_key=bobs_private_key)
except pre.UmbralDecryptionError:
print("Decryption failed! Bob doesn't has access granted yet.")

#8
# Alice grants access to Bob by generating kfrags
# Alice grants access to Bob by generating kfrags
# -----------------------------------------------
# When Alice wants to grant Bob access to open her encrypted messages,
# she creates *threshold split re-encryption keys*, or *"kfrags"*,
# which are next sent to N proxies or *Ursulas*.
# She uses her private key, and Bob's public key, and she sets a minimum
# When Alice wants to grant Bob access to open her encrypted messages,
# she creates *threshold split re-encryption keys*, or *"kfrags"*,
# which are next sent to N proxies or *Ursulas*.
# She uses her private key, and Bob's public key, and she sets a minimum
# threshold of 10, for 20 total shares

kfrags = pre.generate_kfrags(delegating_privkey=alices_private_key,
Expand All @@ -96,8 +96,8 @@
#9
# Ursulas perform re-encryption
# ------------------------------
# Bob asks several Ursulas to re-encrypt the capsule so he can open it.
# Each Ursula performs re-encryption on the capsule using the `kfrag`
# Bob asks several Ursulas to re-encrypt the capsule so he can open it.
# Each Ursula performs re-encryption on the capsule using the `kfrag`
# provided by Alice, obtaining this way a "capsule fragment", or `cfrag`.
# Let's mock a network or transport layer by sampling `threshold` random `kfrags`,
# one for each required Ursula.
Expand All @@ -107,35 +107,29 @@
kfrags = random.sample(kfrags, # All kfrags from above
10) # M - Threshold

# Bob collects the resulting `cfrags` from several Ursulas.
# Bob collects the resulting `cfrags` from several Ursulas.
# Bob must gather at least `threshold` `cfrags` in order to activate the capsule.

bob_capsule.set_correctness_keys(delegating=alices_public_key,
receiving=bobs_public_key,
verifying=alices_verifying_key)
prepared_bob_capsule = bob_capsule.with_correctness_keys(delegating=alices_public_key,
receiving=bobs_public_key,
verifying=alices_verifying_key)

cfrags = list() # Bob's cfrag collection
for kfrag in kfrags:
cfrag = pre.reencrypt(kfrag=kfrag, capsule=bob_capsule)
cfrag = pre.reencrypt(kfrag=kfrag, capsule=prepared_bob_capsule)
cfrags.append(cfrag) # Bob collects a cfrag

assert len(cfrags) == 10

#10
# Bob attaches cfrags to the capsule
# ----------------------------------
# Bob attaches at least `threshold` `cfrags` to the capsule;
# then it can become *activated*.

for cfrag in cfrags:
bob_capsule.attach_cfrag(cfrag)

#11
# Bob activates and opens the capsule
# ------------------------------------
# Finally, Bob activates and opens the capsule,
# Finally, Bob opens the capsule, using at least `threshold` `cfrags`,
# then decrypts the re-encrypted ciphertext.

bob_cleartext = pre.decrypt(ciphertext=ciphertext, capsule=bob_capsule, decrypting_key=bobs_private_key)
bob_cleartext = pre.decrypt_reencrypted(ciphertext=ciphertext,
capsule=prepared_bob_capsule,
cfrags=cfrags,
decrypting_key=bobs_private_key)
print(bob_cleartext)
assert bob_cleartext == plaintext
29 changes: 11 additions & 18 deletions docs/notebooks/pyUmbral Simple API.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@
}
],
"source": [
"cleartext = pre.decrypt(ciphertext=ciphertext, \n",
" capsule=capsule, \n",
" decrypting_key=alices_private_key)\n",
"cleartext = pre.decrypt_original(ciphertext=ciphertext, \n",
" capsule=capsule, \n",
" decrypting_key=alices_private_key)\n",
"print(cleartext)\n"
]
},
Expand Down Expand Up @@ -163,9 +163,9 @@
],
"source": [
"try:\n",
" fail_decrypted_data = pre.decrypt(ciphertext=ciphertext, \n",
" capsule=capsule, \n",
" decrypting_key=bobs_private_key)\n",
" fail_decrypted_data = pre.decrypt_original(ciphertext=ciphertext, \n",
" capsule=capsule, \n",
" decrypting_key=bobs_private_key)\n",
"except pre.UmbralDecryptionError:\n",
" print(\"Decryption failed! Bob doesn't has access granted yet.\")\n"
]
Expand Down Expand Up @@ -220,13 +220,13 @@
"kfrags = random.sample(kfrags, # All kfrags from above\n",
" 10) # M - Threshold\n",
"\n",
"bob_capsule.set_correctness_keys(delegating=alices_public_key,\n",
" receiving=bobs_public_key,\n",
" verifying=alices_verifying_key)\n",
"prepared_bob_capsule = bob_capsule.with_correctness_keys(delegating=alices_public_key,\n",
" receiving=bobs_public_key,\n",
" verifying=alices_verifying_key)\n",
"\n",
"cfrags = list() # Bob's cfrag collection\n",
"for kfrag in kfrags:\n",
" cfrag = pre.reencrypt(kfrag=kfrag, capsule=bob_capsule)\n",
" cfrag = pre.reencrypt(kfrag=kfrag, prepared_capsule=prepared_bob_capsule)\n",
" cfrags.append(cfrag) # Bob collects a cfrag\n",
"\n",
"assert len(cfrags) == 10\n"
Expand Down Expand Up @@ -256,14 +256,7 @@
}
],
"source": [
"bob_capsule.set_correctness_keys(delegating=alices_public_key,\n",
" receiving=bobs_public_key,\n",
" verifying=alices_verifying_key)\n",
"\n",
"for cfrag in cfrags:\n",
" bob_capsule.attach_cfrag(cfrag)\n",
" \n",
"bob_cleartext = pre.decrypt(ciphertext=ciphertext, capsule=capsule, decrypting_key=bobs_private_key)\n",
"bob_cleartext = pre.decrypt_reencrypted(ciphertext=ciphertext, capsule=prepared_bob_capsule, cfrags=cfrags, decrypting_key=bobs_private_key)\n",
"print(bob_cleartext)\n",
"assert bob_cleartext == plaintext\n",
"\n"
Expand Down
62 changes: 26 additions & 36 deletions docs/source/using_pyumbral.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ Alice can open the capsule and decrypt the ciphertext with her private key.

.. doctest:: capsule_story

>>> cleartext = pre.decrypt(ciphertext=ciphertext,
... capsule=capsule,
... decrypting_key=alices_private_key)
>>> cleartext = pre.decrypt_original(ciphertext=ciphertext,
... capsule=capsule,
... decrypting_key=alices_private_key)


Threshold Re-Encryption
Expand All @@ -98,9 +98,9 @@ Bob Exists
>>> bobs_public_key = bobs_private_key.get_pubkey()


Alice grants access to Bob by generating kfrags
Alice grants access to Bob by generating kfrags
-----------------------------------------------
When Alice wants to grant Bob access to open her encrypted messages,
When Alice wants to grant Bob access to open her encrypted messages,
she creates *re-encryption key fragments*, or *"kfrags"*,
which are next sent to N proxies or *Ursulas*.

Expand Down Expand Up @@ -137,21 +137,21 @@ or re-encrypted for him by Ursula, he will not be able to open it.

.. doctest:: capsule_story

>>> fail = pre.decrypt(ciphertext=ciphertext,
... capsule=capsule,
... decrypting_key=bobs_private_key)
>>> fail = pre.decrypt_original(ciphertext=ciphertext,
... capsule=capsule,
... decrypting_key=bobs_private_key)
Traceback (most recent call last):
...
umbral.pre.UmbralDecryptionError


Ursulas perform re-encryption
------------------------------
Bob asks several Ursulas to re-encrypt the capsule so he can open it.
Bob asks several Ursulas to re-encrypt the capsule so he can open it.
Each Ursula performs re-encryption on the capsule using the ``kfrag``
provided by Alice, obtaining this way a "capsule fragment", or ``cfrag``.
Let's mock a network or transport layer by sampling ``threshold`` random kfrags,
one for each required Ursula. Note that each Ursula must prepare the received
one for each required Ursula. Note that each Ursula must prepare the received
capsule before re-encryption by setting the proper correctness keys.

Bob collects the resulting cfrags from several Ursulas.
Expand All @@ -164,14 +164,13 @@ Bob must gather at least ``threshold`` cfrags in order to activate the capsule.
>>> kfrags = random.sample(kfrags, # All kfrags from above
... 10) # M - Threshold

>>> capsule.set_correctness_keys(delegating=alices_public_key,
... receiving=bobs_public_key,
... verifying=alices_verifying_key)
(True, True, True)
>>> prepared_capsule = capsule.with_correctness_keys(delegating=alices_public_key,
... receiving=bobs_public_key,
... verifying=alices_verifying_key)

>>> cfrags = list() # Bob's cfrag collection
>>> for kfrag in kfrags:
... cfrag = pre.reencrypt(kfrag=kfrag, capsule=capsule)
... cfrag = pre.reencrypt(kfrag=kfrag, prepared_capsule=prepared_capsule)
... cfrags.append(cfrag) # Bob collects a cfrag

.. doctest:: capsule_story
Expand All @@ -183,32 +182,23 @@ Bob must gather at least ``threshold`` cfrags in order to activate the capsule.
Decryption
==================================

Bob attaches cfrags to the capsule
----------------------------------
Bob attaches at least ``threshold`` cfrags to the capsule,
which has to be prepared in advance with the necessary correctness keys.
Only then it can become *activated*.

.. doctest:: capsule_story

>>> capsule.set_correctness_keys(delegating=alices_public_key,
... receiving=bobs_public_key,
... verifying=alices_verifying_key)
(False, False, False)

>>> for cfrag in cfrags:
... capsule.attach_cfrag(cfrag)
Bob assembles cfrags for the capsule
------------------------------------
Bob needs to get at least ``threshold`` cfrags for the capsule,
which has to be prepared in advance with the necessary correctness keys.
Only then it can be decrypted.


Bob activates and opens the capsule
------------------------------------
Finally, Bob decrypts the re-encrypted ciphertext using the activated capsule.
Bob opens the capsule
---------------------
Finally, Bob decrypts the re-encrypted ciphertext using the capsule and cfrags.

.. doctest:: capsule_story

>>> cleartext = pre.decrypt(ciphertext=ciphertext,
... capsule=capsule,
... decrypting_key=bobs_private_key)
>>> cleartext = pre.decrypt_reencrypted(ciphertext=ciphertext,
... capsule=prepared_capsule,
... cfrags=cfrags,
... decrypting_key=bobs_private_key)

.. doctest:: capsule_story
:hide:
Expand Down
21 changes: 14 additions & 7 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,27 @@ def ciphertext_and_capsule(alices_keys, message):
return chimney, capsule


@pytest.fixture
def ciphertext_and_prepared_capsule(alices_keys, bobs_keys, ciphertext_and_capsule):
ciphertext, capsule = ciphertext_and_capsule
delegating_privkey, signing_privkey = alices_keys
_receiving_privkey, receiving_pubkey = bobs_keys
prepared_capsule = capsule.with_correctness_keys(delegating=delegating_privkey.get_pubkey(),
receiving=receiving_pubkey,
verifying=signing_privkey.get_pubkey())
return ciphertext, prepared_capsule


@pytest.fixture
def capsule(ciphertext_and_capsule):
ciphertext, capsule = ciphertext_and_capsule
return capsule


@pytest.fixture
def prepared_capsule(alices_keys, bobs_keys, capsule):
delegating_privkey, signing_privkey = alices_keys
_receiving_privkey, receiving_pubkey = bobs_keys
capsule.set_correctness_keys(delegating=delegating_privkey.get_pubkey(),
receiving=receiving_pubkey,
verifying=signing_privkey.get_pubkey())
return capsule
def prepared_capsule(ciphertext_and_prepared_capsule):
ciphertext, prepared_capsule = ciphertext_and_prepared_capsule
return prepared_capsule


@pytest.fixture
Expand Down
Loading