mirror of https://github.com/nucypher/pyUmbral.git
Update docs
parent
c401c52e92
commit
bcb0071f9e
67
README.rst
67
README.rst
|
@ -62,19 +62,18 @@ Additionally, users that delegate access to their data (like Alice, in this exam
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from umbral import pre, keys, signing
|
||||
from umbral import SecretKey, PublicKey
|
||||
|
||||
# Generate Umbral keys for Alice.
|
||||
alices_private_key = keys.UmbralPrivateKey.gen_key()
|
||||
alices_public_key = alices_private_key.get_pubkey()
|
||||
alices_secret_key = SecretKey.random()
|
||||
alices_public_key = PublicKey.from_secret_key(alices_secret_key)
|
||||
|
||||
alices_signing_key = keys.UmbralPrivateKey.gen_key()
|
||||
alices_verifying_key = alices_signing_key.get_pubkey()
|
||||
alices_signer = signing.Signer(private_key=alices_signing_key)
|
||||
alices_signing_key = SecretKey.random()
|
||||
alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)
|
||||
|
||||
# Generate Umbral keys for Bob.
|
||||
bobs_private_key = keys.UmbralPrivateKey.gen_key()
|
||||
bobs_public_key = bobs_private_key.get_pubkey()
|
||||
bobs_secret_key = SecretKey.random()
|
||||
bobs_public_key = PublicKey.from_secret_key(bobs_secret_key)
|
||||
|
||||
|
||||
**Encryption**
|
||||
|
@ -89,14 +88,14 @@ Alice can open the capsule and decrypt the ciphertext with her private key.
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from umbral import encrypt, decrypt_original
|
||||
|
||||
# Encrypt data with Alice's public key.
|
||||
plaintext = b'Proxy Re-Encryption is cool!'
|
||||
ciphertext, capsule = pre.encrypt(alices_public_key, plaintext)
|
||||
capsule, ciphertext = 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 = decrypt_original(alices_secret_key, capsule, ciphertext)
|
||||
|
||||
|
||||
**Re-Encryption Key Fragments**
|
||||
|
@ -107,13 +106,15 @@ which are next sent to N proxies or *Ursulas*.
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from umbral import generate_kfrags
|
||||
|
||||
# Alice generates "M of N" re-encryption key fragments (or "KFrags") for Bob.
|
||||
# In this example, 10 out of 20.
|
||||
kfrags = pre.generate_kfrags(delegating_privkey=alices_private_key,
|
||||
signer=alices_signer,
|
||||
receiving_pubkey=bobs_public_key,
|
||||
threshold=10,
|
||||
N=20)
|
||||
kfrags = generate_kfrags(delegating_sk=alices_secret_key,
|
||||
receiving_pk=bobs_public_key,
|
||||
signing_sk=alices_signing_key,
|
||||
threshold=10,
|
||||
num_kfrags=20)
|
||||
|
||||
|
||||
**Re-Encryption**
|
||||
|
@ -127,17 +128,13 @@ Bob must gather at least ``threshold`` cfrags in order to activate the capsule.
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
# Several Ursulas perform re-encryption, and Bob collects the resulting `cfrags`.
|
||||
# He must gather at least `threshold` `cfrags` in order to activate the capsule.
|
||||
from umbral import reencrypt
|
||||
|
||||
capsule.set_correctness_keys(delegating=alices_public_key,
|
||||
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)
|
||||
cfrags.append(cfrag) # Bob collects a cfrag
|
||||
# Several Ursulas perform re-encryption, and Bob collects the resulting `cfrags`.
|
||||
cfrags = list() # Bob's cfrag collection
|
||||
for kfrag in kfrags[:10]:
|
||||
cfrag = pre.reencrypt(capsule=capsule, kfrag=kfrag)
|
||||
cfrags.append(cfrag) # Bob collects a cfrag
|
||||
|
||||
|
||||
**Decryption by Bob**
|
||||
|
@ -147,14 +144,14 @@ and then decrypts the re-encrypted ciphertext.
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
# Bob activates and opens the capsule
|
||||
for cfrag in cfrags:
|
||||
capsule.attach_cfrag(cfrag)
|
||||
from umbral import decrypt_reencrypted
|
||||
|
||||
bob_cleartext = pre.decrypt(ciphertext=ciphertext,
|
||||
capsule=capsule,
|
||||
decrypting_key=bobs_private_key)
|
||||
assert bob_cleartext == plaintext
|
||||
bob_cleartext = pre.decrypt_reencrypted(decrypting_sk=bobs_secret_key,
|
||||
delegating_pk=alices_public_key,
|
||||
capsule=capsule,
|
||||
cfrags=cfrags,
|
||||
ciphertext=ciphertext)
|
||||
assert bob_cleartext == plaintext
|
||||
|
||||
See more detailed usage examples in the docs_ directory.
|
||||
|
||||
|
@ -171,7 +168,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:
|
||||
|
||||
|
|
|
@ -1,41 +1,20 @@
|
|||
"""
|
||||
This file is part of pyUmbral.
|
||||
|
||||
pyUmbral is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
pyUmbral is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with pyUmbral. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
#1
|
||||
# Sets a default curve (secp256k1)
|
||||
import random
|
||||
from umbral import pre, keys, config, signing
|
||||
from umbral import (
|
||||
SecretKey, PublicKey,
|
||||
encrypt, generate_kfrags, reencrypt, decrypt_original, decrypt_reencrypted)
|
||||
from umbral.dem import ErrorInvalidTag
|
||||
|
||||
config.set_default_curve()
|
||||
|
||||
#2
|
||||
# Generate an Umbral key pair
|
||||
# ---------------------------
|
||||
# First, Let's generate two asymmetric key pairs for Alice:
|
||||
# A delegating key pair and a Signing key pair.
|
||||
|
||||
alices_private_key = keys.UmbralPrivateKey.gen_key()
|
||||
alices_public_key = alices_private_key.get_pubkey()
|
||||
alices_secret_key = SecretKey.random()
|
||||
alices_public_key = PublicKey.from_secret_key(alices_secret_key)
|
||||
|
||||
alices_signing_key = keys.UmbralPrivateKey.gen_key()
|
||||
alices_verifying_key = alices_signing_key.get_pubkey()
|
||||
alices_signer = signing.Signer(private_key=alices_signing_key)
|
||||
alices_signing_key = SecretKey.random()
|
||||
alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)
|
||||
|
||||
#3
|
||||
# Encrypt some data for Alice
|
||||
# ---------------------------
|
||||
# Now let's encrypt data with Alice's public key.
|
||||
|
@ -44,98 +23,86 @@ alices_signer = signing.Signer(private_key=alices_signing_key)
|
|||
# this operation.
|
||||
|
||||
plaintext = b'Proxy Re-encryption is cool!'
|
||||
ciphertext, capsule = pre.encrypt(alices_public_key, plaintext)
|
||||
capsule, ciphertext = encrypt(alices_public_key, plaintext)
|
||||
print(ciphertext)
|
||||
|
||||
#4
|
||||
# Decrypt data for Alice
|
||||
# ----------------------
|
||||
# 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 = decrypt_original(alices_secret_key, capsule, ciphertext)
|
||||
print(cleartext)
|
||||
|
||||
#5
|
||||
# Bob Exists
|
||||
# -----------
|
||||
|
||||
bobs_private_key = keys.UmbralPrivateKey.gen_key()
|
||||
bobs_public_key = bobs_private_key.get_pubkey()
|
||||
bobs_secret_key = SecretKey.random()
|
||||
bobs_public_key = PublicKey.from_secret_key(bobs_secret_key)
|
||||
|
||||
#6
|
||||
# Bob receives a capsule through a side channel (s3, ipfs, Google cloud, etc)
|
||||
bob_capsule = capsule
|
||||
|
||||
#7
|
||||
# Attempt Bob's decryption (fail)
|
||||
try:
|
||||
fail_decrypted_data = pre.decrypt(ciphertext=ciphertext,
|
||||
capsule=bob_capsule,
|
||||
decrypting_key=bobs_private_key)
|
||||
except pre.UmbralDecryptionError:
|
||||
fail_decrypted_data = decrypt_original(bobs_secret_key, bob_capsule, ciphertext)
|
||||
except ErrorInvalidTag:
|
||||
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,
|
||||
signer=alices_signer,
|
||||
receiving_pubkey=bobs_public_key,
|
||||
threshold=10,
|
||||
N=20)
|
||||
kfrags = generate_kfrags(delegating_sk=alices_secret_key,
|
||||
receiving_pk=bobs_public_key,
|
||||
signing_sk=alices_signing_key,
|
||||
threshold=10,
|
||||
num_kfrags=20)
|
||||
|
||||
#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.
|
||||
|
||||
import random
|
||||
|
||||
kfrags = random.sample(kfrags, # All kfrags from above
|
||||
10) # M - Threshold
|
||||
|
||||
# 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)
|
||||
# Bob collects the resulting `cfrags` from several Ursulas.
|
||||
# Bob must gather at least `threshold` `cfrags` in order to open the capsule.
|
||||
|
||||
cfrags = list() # Bob's cfrag collection
|
||||
for kfrag in kfrags:
|
||||
cfrag = pre.reencrypt(kfrag=kfrag, capsule=bob_capsule)
|
||||
cfrag = reencrypt(capsule=capsule, kfrag=kfrag)
|
||||
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*.
|
||||
# Bob checks the capsule fragments
|
||||
# --------------------------------
|
||||
# Bob can verify that the capsule fragments are valid and really originate from Alice,
|
||||
# using Alice's public keys.
|
||||
|
||||
for cfrag in cfrags:
|
||||
bob_capsule.attach_cfrag(cfrag)
|
||||
assert all(cfrag.verify(capsule,
|
||||
delegating_pk=alices_public_key,
|
||||
receiving_pk=bobs_public_key,
|
||||
signing_pk=alices_verifying_key)
|
||||
for cfrag in cfrags)
|
||||
|
||||
#11
|
||||
# Bob activates and opens the capsule
|
||||
# Bob opens the capsule
|
||||
# ------------------------------------
|
||||
# Finally, Bob activates and opens the capsule,
|
||||
# then decrypts the re-encrypted ciphertext.
|
||||
# Finally, Bob decrypts the re-encrypted ciphertext using his key.
|
||||
|
||||
bob_cleartext = pre.decrypt(ciphertext=ciphertext, capsule=bob_capsule, decrypting_key=bobs_private_key)
|
||||
bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key,
|
||||
delegating_pk=alices_public_key,
|
||||
capsule=bob_capsule,
|
||||
cfrags=cfrags,
|
||||
ciphertext=ciphertext)
|
||||
print(bob_cleartext)
|
||||
assert bob_cleartext == plaintext
|
||||
|
|
|
@ -7,30 +7,6 @@
|
|||
"# pyUmbral Python API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setting the default curve\n",
|
||||
"\n",
|
||||
"The first time you use umbral, you may want to specify an elliptic curve to use. If you do not specify a curve, secp256k1 will be used for all operations, with a slight performace hit for the lookup.\n",
|
||||
"\n",
|
||||
"To set the default curve use `umbral.config.set_default_curve()`\n",
|
||||
"\n",
|
||||
"Note: you can only set the dafault once, or `UmbralConfigurationError` will be raised."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from umbral.config import set_default_curve\n",
|
||||
"\n",
|
||||
"set_default_curve()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
|
@ -46,16 +22,15 @@
|
|||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from umbral import keys, signing\n",
|
||||
"from umbral import SecretKey, PublicKey\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Alice's Keys\n",
|
||||
"alices_private_key = keys.UmbralPrivateKey.gen_key()\n",
|
||||
"alices_public_key = alices_private_key.get_pubkey()\n",
|
||||
"alices_private_key = SecretKey.random()\n",
|
||||
"alices_public_key = PublicKey.from_secret_key(alices_private_key)\n",
|
||||
"\n",
|
||||
"alices_signing_key = keys.UmbralPrivateKey.gen_key()\n",
|
||||
"alices_verifying_key = alices_signing_key.get_pubkey()\n",
|
||||
"alices_signer = signing.Signer(private_key=alices_signing_key)"
|
||||
"alices_signing_key = SecretKey.random()\n",
|
||||
"alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -80,16 +55,16 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"b'#\\xebQ\\xd4\\xad\\x8ah,9\\x8f\\xc9\\x18\\x84[\\x95M\\x8e\\xb1\\x85\\xf9\\xbe\\x97\\x07\\xf3\\x80@\\x11\\xab\\x82\\xac\\xa1\\xbf\\xc0\\x00e\\xecpTq\\xef\\x94\\xd94\\x94\\x1a\\xdf\\xf0\\x04)\\xf5\\r\\xc4\\xbd/:\\x8c'\n"
|
||||
"b'\\x1c\\xa0\\xa83\\x0cv\\x97\\x02d\\xe9\\xe9\\xc5_\\x9d5NRGRx\\xd4\\xc9\\x17%\\x9b\\xb4\\x05\\xd1\\xc2\\x1e\\x9d\\x0b\\xbf\\xb4g\\xf0n\\xfe\\x9eM\\x93\\xe0\\xbf#l\\xf9\\x033\\xb00\\xf5\\r\\xff\\xc9\\x133C\\xf0\\xa3\\xc0\\xd1e\\xdb~.E$%'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from umbral import pre\n",
|
||||
"from umbral import encrypt\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"plaintext = b'Proxy Re-encryption is cool!'\n",
|
||||
"ciphertext, capsule = pre.encrypt(alices_public_key, plaintext)\n",
|
||||
"capsule, ciphertext = encrypt(alices_public_key, plaintext)\n",
|
||||
"print(ciphertext)"
|
||||
]
|
||||
},
|
||||
|
@ -115,10 +90,13 @@
|
|||
}
|
||||
],
|
||||
"source": [
|
||||
"cleartext = pre.decrypt(ciphertext=ciphertext, \n",
|
||||
" capsule=capsule, \n",
|
||||
" decrypting_key=alices_private_key)\n",
|
||||
"print(cleartext)\n"
|
||||
"from umbral import decrypt_original\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"cleartext = decrypt_original(sk=alices_private_key,\n",
|
||||
" capsule=capsule,\n",
|
||||
" ciphertext=ciphertext)\n",
|
||||
"print(cleartext)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -135,8 +113,8 @@
|
|||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"bobs_private_key = keys.UmbralPrivateKey.gen_key()\n",
|
||||
"bobs_public_key = bobs_private_key.get_pubkey()\n",
|
||||
"bobs_private_key = SecretKey.random()\n",
|
||||
"bobs_public_key = PublicKey.from_secret_key(bobs_private_key)\n",
|
||||
"\n",
|
||||
"bob_capsule = capsule"
|
||||
]
|
||||
|
@ -162,11 +140,13 @@
|
|||
}
|
||||
],
|
||||
"source": [
|
||||
"from umbral.dem import ErrorInvalidTag\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" fail_decrypted_data = pre.decrypt(ciphertext=ciphertext, \n",
|
||||
" capsule=capsule, \n",
|
||||
" decrypting_key=bobs_private_key)\n",
|
||||
"except pre.UmbralDecryptionError:\n",
|
||||
" fail_decrypted_data = decrypt_original(sk=bobs_private_key,\n",
|
||||
" capsule=capsule,\n",
|
||||
" ciphertext=ciphertext)\n",
|
||||
"except ErrorInvalidTag:\n",
|
||||
" print(\"Decryption failed! Bob doesn't has access granted yet.\")\n"
|
||||
]
|
||||
},
|
||||
|
@ -184,30 +164,7 @@
|
|||
"metadata": {},
|
||||
"source": [
|
||||
"## Alice grants access to Bob by generating KFrags \n",
|
||||
"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*. She uses her private key, and Bob's public key, and she sets a minimum threshold of 10, for 20 total shares\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"M, N = 10, 20\n",
|
||||
"kfrags = pre.generate_kfrags(delegating_privkey=alices_private_key, \n",
|
||||
" receiving_pubkey=bobs_public_key, \n",
|
||||
" signer=alices_signer,\n",
|
||||
" threshold=M, \n",
|
||||
" N=N)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"## Ursulas Re-encrypt; Bob attaches fragments to `capsule`\n",
|
||||
"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 `M` random `kfrags`, 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. He must gather at least `M` `cfrags` in order to activate the capsule.\n"
|
||||
"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*. She uses her private key, and Bob's public key, and she sets a minimum threshold of 10, for 20 total shares\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -215,18 +172,44 @@
|
|||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from umbral import generate_kfrags\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"M, N = 10, 20\n",
|
||||
"kfrags = generate_kfrags(delegating_sk=alices_private_key,\n",
|
||||
" receiving_pk=bobs_public_key,\n",
|
||||
" signing_sk=alices_signing_key,\n",
|
||||
" threshold=10,\n",
|
||||
" num_kfrags=20)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"## Ursulas Re-encrypt; Bob attaches fragments to `capsule`\n",
|
||||
"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 `M` random `kfrags`, one for each required Ursula. Bob collects the resulting `cfrags` from several Ursulas. He must gather at least `M` `cfrags` in order to activate the capsule.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import random\n",
|
||||
"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",
|
||||
"\n",
|
||||
"from umbral import reencrypt\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"cfrags = list() # Bob's cfrag collection\n",
|
||||
"for kfrag in kfrags:\n",
|
||||
" cfrag = pre.reencrypt(kfrag=kfrag, capsule=bob_capsule)\n",
|
||||
" cfrag = reencrypt(capsule=capsule, kfrag=kfrag)\n",
|
||||
" cfrags.append(cfrag) # Bob collects a cfrag\n",
|
||||
"\n",
|
||||
"assert len(cfrags) == 10\n"
|
||||
|
@ -236,15 +219,36 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Bob activates and opens the capsule; Decrypts data from Alice.\n",
|
||||
"The `capsule` can become *activated* once Bob attaches at least `M` `cfrags` to it. Note that it has to be prepared in advance with the necessary `correctness_keys` (specifically, Alice's public key, Alice's signature verification key and his own public key). \n",
|
||||
"\n",
|
||||
"Finally, Bob activates and opens the capsule, then decrypts the re-encrypted ciphertext."
|
||||
"## Bob checks the capsule fragments\n",
|
||||
"Bob can verify that the capsule fragments are valid and really originate from Alice, using Alice's public keys."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 11,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"assert all(cfrag.verify(capsule,\n",
|
||||
" delegating_pk=alices_public_key,\n",
|
||||
" receiving_pk=bobs_public_key,\n",
|
||||
" signing_pk=alices_verifying_key)\n",
|
||||
" for cfrag in cfrags)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Bob opens the capsule; Decrypts data from Alice.\n",
|
||||
"Finally, Bob decrypts the re-encrypted ciphertext using his key."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
|
@ -256,17 +260,16 @@
|
|||
}
|
||||
],
|
||||
"source": [
|
||||
"bob_capsule.set_correctness_keys(delegating=alices_public_key,\n",
|
||||
" receiving=bobs_public_key,\n",
|
||||
" verifying=alices_verifying_key)\n",
|
||||
"from umbral import decrypt_reencrypted\n",
|
||||
"\n",
|
||||
"bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_private_key,\n",
|
||||
" delegating_pk=alices_public_key,\n",
|
||||
" capsule=capsule,\n",
|
||||
" cfrags=cfrags,\n",
|
||||
" ciphertext=ciphertext)\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",
|
||||
"print(bob_cleartext)\n",
|
||||
"assert bob_cleartext == plaintext\n",
|
||||
"\n"
|
||||
"assert bob_cleartext == plaintext"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -293,7 +296,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.0"
|
||||
"version": "3.8.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
=========================
|
||||
Choosing and Using Curves
|
||||
=========================
|
||||
|
||||
|
||||
The matter of which curve to use is the subject of some debate. If you aren't sure, you might start here:
|
||||
https://safecurves.cr.yp.to/
|
||||
|
||||
A number of curves are available in the Cryptography.io_ library, on which pyUmbral depends.
|
||||
You can find them in the ``cryptography.hazmat.primitives.asymmetric.ec`` module.
|
||||
|
||||
.. _Cryptography.io: https://cryptography.io/en/latest/
|
||||
|
||||
Be careful when choosing a curve - the security of your application depends on it.
|
||||
|
||||
We provide curve ``SECP256K1`` as a default because it is the basis for a number of crypto-blockchain projects;
|
||||
we don't otherwise endorse its security.
|
||||
We additionally support curves ``SECP256R1`` (also known as "NIST P-256") and ``SECP384R1`` ("NIST P-384").
|
||||
|
||||
|
||||
Setting a default curve
|
||||
--------------------------
|
||||
|
||||
Before you perform any ECC operations, you can set a default curve.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from umbral.curve import SECP256K1
|
||||
>>> config.set_default_curve(SECP256K1)
|
||||
|
||||
If you don't set a default curve, then SECP256K1 will be set for you when you perform the first ECC
|
||||
operation. This causes a small one-time performance penalty.
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from umbral import keys
|
||||
>>> private_key = keys.UmbralPrivateKey.gen_key()
|
||||
|
||||
RuntimeWarning: No default curve has been set. Using SECP256K1.
|
||||
A slight performance penalty has been incurred for only this call.
|
||||
Set a default curve with umbral.config.set_default_curve().
|
||||
|
||||
|
||||
To use SECP256K1 and avoid this penalty, you can simply call ``set_default_curve()`` with no argument:
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> config.set_default_curve()
|
||||
|
||||
Attempting to set the default curve twice in the same runtime will raise
|
||||
a ``UmbralConfigurationError``.
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from umbral import config
|
||||
>>> config.set_default_curve()
|
||||
>>> config.set_default_curve()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
umbral.config._CONFIG.UmbralConfigurationError
|
|
@ -31,7 +31,7 @@ pyUmbral
|
|||
.. end-badges
|
||||
|
||||
pyUmbral is the reference implementation of the Umbral_ threshold proxy re-encryption scheme.
|
||||
It is open-source, built with Python, and uses OpenSSL_ and Cryptography.io_.
|
||||
It is open-source, built with Python, and uses OpenSSL_ via Cryptography.io_ and libsodium_ via PyNaCl_.
|
||||
|
||||
Using Umbral, Alice (the data owner) can *delegate decryption rights* to Bob for
|
||||
any ciphertext intended to her, through a re-encryption process performed by a
|
||||
|
@ -50,6 +50,8 @@ a proxy re-encryption network to empower privacy in decentralized systems.
|
|||
.. _Cryptography.io: https://cryptography.io/en/latest/
|
||||
.. _OpenSSL: https://www.openssl.org/
|
||||
.. _nucypher: https://github.com/nucypher/nucypher
|
||||
.. _libsodium: https://github.com/jedisct1/libsodium
|
||||
.. _PyNaCl: https://pynacl.readthedocs.io/en/latest/
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
|
|
@ -12,30 +12,22 @@ Using pyUmbral
|
|||
sys.path.append(os.path.abspath(os.getcwd()))
|
||||
|
||||
|
||||
.. testcleanup:: capsule_story
|
||||
Elliptic Curves
|
||||
===============
|
||||
|
||||
from umbral import config
|
||||
config._CONFIG.___CONFIG__curve = None
|
||||
config._CONFIG.___CONFIG__params = None
|
||||
The matter of which curve to use is the subject of some debate. If you aren't sure, you might start here:
|
||||
https://safecurves.cr.yp.to/
|
||||
|
||||
A number of curves are available in the Cryptography.io_ library, on which pyUmbral depends.
|
||||
You can find them in the ``cryptography.hazmat.primitives.asymmetric.ec`` module.
|
||||
|
||||
Configuration
|
||||
==============
|
||||
.. _Cryptography.io: https://cryptography.io/en/latest/
|
||||
|
||||
Be careful when choosing a curve - the security of your application depends on it.
|
||||
|
||||
Setting the default curve
|
||||
--------------------------
|
||||
|
||||
The best way to start using pyUmbral is to decide on an elliptic curve to use and set it as your default.
|
||||
|
||||
|
||||
.. doctest:: capsule_story
|
||||
|
||||
>>> from umbral import config
|
||||
>>> from umbral.curve import SECP256K1
|
||||
>>> config.set_default_curve(SECP256K1)
|
||||
|
||||
For more information on curves, see :doc:`choosing_and_using_curves`.
|
||||
We provide curve ``SECP256K1`` as a default because it is the basis for a number of crypto-blockchain projects;
|
||||
we don't otherwise endorse its security.
|
||||
We additionally support curves ``SECP256R1`` (also known as "NIST P-256") and ``SECP384R1`` ("NIST P-384"), but they cannot currently be selected via the public API.
|
||||
|
||||
|
||||
Encryption
|
||||
|
@ -49,28 +41,27 @@ A delegating key pair and a signing key pair.
|
|||
|
||||
.. doctest:: capsule_story
|
||||
|
||||
>>> from umbral import keys, signing
|
||||
>>> from umbral import SecretKey, PublicKey
|
||||
|
||||
>>> alices_private_key = keys.UmbralPrivateKey.gen_key()
|
||||
>>> alices_public_key = alices_private_key.get_pubkey()
|
||||
>>> alices_secret_key = SecretKey.random()
|
||||
>>> alices_public_key = PublicKey.from_secret_key(alices_secret_key)
|
||||
|
||||
>>> alices_signing_key = keys.UmbralPrivateKey.gen_key()
|
||||
>>> alices_verifying_key = alices_signing_key.get_pubkey()
|
||||
>>> alices_signer = signing.Signer(private_key=alices_signing_key)
|
||||
>>> alices_signing_key = SecretKey.random()
|
||||
>>> alices_verifying_key = PublicKey.from_secret_key(alices_signing_key)
|
||||
|
||||
|
||||
Encrypt with a public key
|
||||
--------------------------
|
||||
Now let's encrypt data with Alice's public key.
|
||||
Invocation of ``pre.encrypt`` returns both the ``ciphertext`` and a ``capsule``.
|
||||
Invocation of :py:func:`encrypt` returns both a ``capsule`` and a ``ciphertext``.
|
||||
Note that anyone with Alice's public key can perform this operation.
|
||||
|
||||
|
||||
.. doctest:: capsule_story
|
||||
|
||||
>>> from umbral import pre
|
||||
>>> from umbral import encrypt
|
||||
>>> plaintext = b'Proxy Re-encryption is cool!'
|
||||
>>> ciphertext, capsule = pre.encrypt(alices_public_key, plaintext)
|
||||
>>> capsule, ciphertext = encrypt(alices_public_key, plaintext)
|
||||
|
||||
|
||||
Decrypt with a private key
|
||||
|
@ -80,9 +71,8 @@ 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)
|
||||
>>> from umbral import decrypt_original
|
||||
>>> cleartext = decrypt_original(alices_secret_key, capsule, ciphertext)
|
||||
|
||||
|
||||
Threshold Re-Encryption
|
||||
|
@ -93,29 +83,29 @@ Bob Exists
|
|||
|
||||
.. doctest:: capsule_story
|
||||
|
||||
>>> from umbral import keys
|
||||
>>> bobs_private_key = keys.UmbralPrivateKey.gen_key()
|
||||
>>> bobs_public_key = bobs_private_key.get_pubkey()
|
||||
>>> bobs_secret_key = SecretKey.random()
|
||||
>>> bobs_public_key = PublicKey.from_secret_key(bobs_secret_key)
|
||||
|
||||
|
||||
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*.
|
||||
|
||||
Alice must specify ``N`` (the total number of kfrags),
|
||||
Alice must specify ``num_kfrags`` (the total number of kfrags),
|
||||
and a ``threshold`` (the minimum number of kfrags needed to activate a capsule).
|
||||
In the following example, Alice creates 20 kfrags,
|
||||
but Bob needs to get only 10 re-encryptions to activate the capsule.
|
||||
|
||||
.. doctest:: capsule_story
|
||||
|
||||
>>> kfrags = pre.generate_kfrags(delegating_privkey=alices_private_key,
|
||||
... signer=alices_signer,
|
||||
... receiving_pubkey=bobs_public_key,
|
||||
... threshold=10,
|
||||
... N=20)
|
||||
>>> from umbral import generate_kfrags
|
||||
>>> kfrags = generate_kfrags(delegating_sk=alices_secret_key,
|
||||
... receiving_pk=bobs_public_key,
|
||||
... signing_sk=alices_signing_key,
|
||||
... threshold=10,
|
||||
... num_kfrags=20)
|
||||
|
||||
|
||||
Bob receives a capsule
|
||||
|
@ -137,25 +127,24 @@ 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 = decrypt_original(sk=bobs_secret_key,
|
||||
... capsule=capsule,
|
||||
... ciphertext=ciphertext)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
umbral.pre.UmbralDecryptionError
|
||||
umbral.dem.ErrorInvalidTag
|
||||
|
||||
|
||||
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
|
||||
capsule before re-encryption by setting the proper correctness keys.
|
||||
one for each required Ursula.
|
||||
|
||||
Bob collects the resulting cfrags from several Ursulas.
|
||||
Bob must gather at least ``threshold`` cfrags in order to activate the capsule.
|
||||
Bob must gather at least ``threshold`` cfrags in order to open the capsule.
|
||||
|
||||
|
||||
.. doctest:: capsule_story
|
||||
|
@ -164,14 +153,10 @@ 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)
|
||||
|
||||
>>> from umbral import reencrypt
|
||||
>>> cfrags = list() # Bob's cfrag collection
|
||||
>>> for kfrag in kfrags:
|
||||
... cfrag = pre.reencrypt(kfrag=kfrag, capsule=capsule)
|
||||
... cfrag = reencrypt(capsule=capsule, kfrag=kfrag)
|
||||
... cfrags.append(cfrag) # Bob collects a cfrag
|
||||
|
||||
.. doctest:: capsule_story
|
||||
|
@ -183,32 +168,34 @@ 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*.
|
||||
Bob checks the capsule fragments
|
||||
--------------------------------
|
||||
Bob can verify that the capsule fragments are valid and really originate from Alice,
|
||||
using Alice's public keys.
|
||||
|
||||
.. 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)
|
||||
>>> all(cfrag.verify(capsule,
|
||||
... delegating_pk=alices_public_key,
|
||||
... receiving_pk=bobs_public_key,
|
||||
... signing_pk=alices_verifying_key)
|
||||
... for cfrag in cfrags)
|
||||
True
|
||||
|
||||
|
||||
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 his key.
|
||||
|
||||
.. doctest:: capsule_story
|
||||
|
||||
>>> cleartext = pre.decrypt(ciphertext=ciphertext,
|
||||
... capsule=capsule,
|
||||
... decrypting_key=bobs_private_key)
|
||||
>>> from umbral import decrypt_reencrypted
|
||||
>>> cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key,
|
||||
... delegating_pk=alices_public_key,
|
||||
... capsule=capsule,
|
||||
... cfrags=cfrags,
|
||||
... ciphertext=ciphertext)
|
||||
|
||||
|
||||
.. doctest:: capsule_story
|
||||
:hide:
|
||||
|
|
Loading…
Reference in New Issue