mirror of https://github.com/nucypher/pyUmbral.git
302 lines
8.8 KiB
Plaintext
302 lines
8.8 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# 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": {},
|
|
"source": [
|
|
"## Generate Umbral Keys for Alice\n",
|
|
"First, Let's generate two asymmetric key pairs for Alice:\n",
|
|
"A *delegating* key pair and a *signing* key pair.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from umbral import keys, signing\n",
|
|
"\n",
|
|
"\n",
|
|
"# Alice's Keys\n",
|
|
"alices_private_key = keys.UmbralPrivateKey.gen_key()\n",
|
|
"alices_public_key = alices_private_key.get_pubkey()\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)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Encrypt some data for Alice\n",
|
|
"Now let's encrypt data with Alice's public key. Invocation of `pre.encrypt` returns both the `ciphertext`,\n",
|
|
"and a `capsule`. Anyone with Alice's public key can perform this operation."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {
|
|
"tags": [
|
|
"nbval-ignore-output"
|
|
]
|
|
},
|
|
"outputs": [
|
|
{
|
|
"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"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"from umbral import pre\n",
|
|
"\n",
|
|
"\n",
|
|
"plaintext = b'Proxy Re-encryption is cool!'\n",
|
|
"ciphertext, capsule = pre.encrypt(alices_public_key, plaintext)\n",
|
|
"print(ciphertext)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Alice decrypts data for self\n",
|
|
"Since data was encrypted with Alice's public key, Alice can open the capsule and decrypt the ciphertext with her private key."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"b'Proxy Re-encryption is cool!'\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"cleartext = pre.decrypt(ciphertext=ciphertext, \n",
|
|
" capsule=capsule, \n",
|
|
" decrypting_key=alices_private_key)\n",
|
|
"print(cleartext)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Enter Bob\n",
|
|
"Apart from generating his keypair, we will also assume that Bob receives a capsule through a side channel (s3, ipfs, Google Cloud, etc). "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"bobs_private_key = keys.UmbralPrivateKey.gen_key()\n",
|
|
"bobs_public_key = bobs_private_key.get_pubkey()\n",
|
|
"\n",
|
|
"bob_capsule = capsule"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Attempt Bob's decryption (fail)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Decryption failed! Bob doesn't has access granted yet.\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"try:\n",
|
|
" fail_decrypted_data = pre.decrypt(ciphertext=ciphertext, \n",
|
|
" capsule=capsule, \n",
|
|
" decrypting_key=bobs_private_key)\n",
|
|
"except:\n",
|
|
" print(\"Decryption failed! Bob doesn't has access granted yet.\")\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Proxy Re-encryption\n",
|
|
"\n",
|
|
"<img src=\"https://cdn-images-1.medium.com/max/1200/0*yTKUeeuKPu-aIZdw.\" alt=\"Proxy Re-Encryption\" width=\"500\"/>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"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"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"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",
|
|
"cfrags = list() # Bob's cfrag collection\n",
|
|
"for kfrag in kfrags:\n",
|
|
" cfrag = pre.reencrypt(kfrag=kfrag, capsule=bob_capsule)\n",
|
|
" cfrags.append(cfrag) # Bob collects a cfrag\n",
|
|
"\n",
|
|
"assert len(cfrags) == 10\n"
|
|
]
|
|
},
|
|
{
|
|
"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."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"b'Proxy Re-encryption is cool!'\n"
|
|
]
|
|
}
|
|
],
|
|
"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",
|
|
"print(bob_cleartext)\n",
|
|
"assert bob_cleartext == plaintext\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.7.0"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|