Update and test existing documentation with sphinx doctest

pull/173/head
Kieran Prasch 2018-06-25 17:39:24 -07:00
parent d2d53d56b7
commit 89ec7a6f37
3 changed files with 163 additions and 41 deletions

View File

@ -14,26 +14,52 @@ Be careful when choosing a curve - the security of your application depends on i
We provide SECP256K1 as a default because it is the basis for a number of crypto-blockchain projects; We provide SECP256K1 as a default because it is the basis for a number of crypto-blockchain projects;
we don't otherwise endorse its security. we don't otherwise endorse its security.
.. testsetup::
from umbral import config, keys
from cryptography.hazmat.primitives.asymmetric import ec
Setting a default curve Setting a default curve
-------------------------- --------------------------
Before you perform any ECC operations, you can set a default curve. Before you perform any ECC operations, you can set a default curve.
.. doctest::
>>> config._CONFIG.___CONFIG__curve = None
>>> config._CONFIG.___CONFIG__params = None
>>> config.set_default_curve(ec.SECP256K1)
.. code-block:: python .. code-block:: python
from umbral import config
from cryptography.hazmat.primitives.asymmetric import ec
config.set_default_curve(ec.SECP256K1) config.set_default_curve(ec.SECP256K1)
If you don't set a default curve, then SECP256K1 will be set for you when you perform the first ECC 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. operation. This causes a small one-time performance penalty.
.. doctest::
>>> config._CONFIG.___CONFIG__curve = None
>>> config._CONFIG.___CONFIG__params = None
>>> 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().
.. code-block:: python .. code-block:: python
keys.UmbralPrivateKey.gen_key() from umbral import keys
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(). 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().
If you want SECP256K1 and want to avoid this penalty, you can simply call `set_default_curve()` with no argument: If you want SECP256K1 and want to avoid this penalty, you can simply call `set_default_curve()` with no argument:
.. doctest::
>>> config._CONFIG.___CONFIG__curve = None
>>> config._CONFIG.___CONFIG__params = None
>>> config.set_default_curve()
.. code-block:: python .. code-block:: python
config.set_default_curve() config.set_default_curve()
@ -41,9 +67,20 @@ If you want SECP256K1 and want to avoid this penalty, you can simply call `set_d
Attempting to set the default curve twice in the same runtime will raise Attempting to set the default curve twice in the same runtime will raise
a `UmbralConfigurationError`. a `UmbralConfigurationError`.
.. doctest::
>>> config._CONFIG.___CONFIG__curve = None
>>> config._CONFIG.___CONFIG__params = None
>>> config.set_default_curve()
>>> config.set_default_curve()
Traceback (most recent call last):
...
umbral.config._CONFIG.UmbralConfigurationError:
.. code-block:: python .. code-block:: python
config.set_default_curve() config.set_default_curve()
Traceback (most recent call last): Traceback (most recent call last):
... ...
umbral.config.UmbralConfigurationError: You can only set the default curve once. Do it once and then leave it alone. umbral.config.UmbralConfigurationError: You can only set the default curve once. Do it once and then leave it alone.

View File

@ -42,6 +42,7 @@ extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.autodoc',
'sphinx.ext.mathjax', 'sphinx.ext.mathjax',
'sphinx.ext.viewcode', 'sphinx.ext.viewcode',
'sphinx.ext.doctest',
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.

View File

@ -6,9 +6,17 @@ Using pyUmbral
Import umbral modules Import umbral modules
.. testsetup::
import sys
import os
sys.path.append(os.path.abspath(os.getcwd()))
from umbral import pre, keys, config, signing
.. code-block:: python .. code-block:: python
from umbral import pre, keys, config from umbral import pre, keys, config, signing
Configuration Configuration
@ -20,25 +28,46 @@ Setting the default curve
The best way to start using pyUmbral is to decide on a elliptic curve to use and set it as your default. The best way to start using pyUmbral is to decide on a elliptic curve to use and set it as your default.
.. doctest::
>>> config._CONFIG.___CONFIG__curve = None
>>> config._CONFIG.___CONFIG__params = None
>>> from cryptography.hazmat.primitives.asymmetric import ec
>>> config.set_default_curve(ec.SECP256K1)
.. code-block:: python .. code-block:: python
config.set_default_curve(ec.SECP256K1) config.set_default_curve(ec.SECP256K1)
For more information on curves, see :doc:`choosing_and_using_curves`. For more information on curves, see :doc:`choosing_and_using_curves`.
Encryption and Encapsulation Encryption
============================= ==========
Generate an Umbral key pair Generate an Umbral key pair
----------------------------- -----------------------------
First, Let's generate an asymmetric key pair for Alice. First, Let's generate two asymmetric key pairs for Alice:
A delegating key pair and a Signing key pair.
.. doctest::
>>> alices_private_key = keys.UmbralPrivateKey.gen_key()
>>> alices_public_key = alices_private_key.get_pubkey()
>>> alices_signing_key = keys.UmbralPrivateKey.gen_key()
>>> alices_verifying_key = alices_signing_key.get_pubkey()
>>> alices_signer = signing.Signer(private_key=alices_signing_key)
.. code-block:: python .. code-block:: python
alices_private_key = keys.UmbralPrivateKey.gen_key() alices_private_key = keys.UmbralPrivateKey.gen_key()
alices_public_key = private_key.get_pubkey() alices_public_key = alices_private_key.get_pubkey()
alices_signing_key = keys.UmbralPrivateKey.gen_key()
alices_verifying_key = alices_signing_key.get_pubkey()
alices_signer = signing.Signer(private_key=alices_signing_key)
Encrypt with a public key Encrypt with a public key
@ -48,11 +77,16 @@ Invocation of `umbral.encrypt` returns both the `ciphertext`,
and a `capsule`, Anyone with Alice's public key can perform and a `capsule`, Anyone with Alice's public key can perform
this operation. this operation.
.. doctest::
>>> plaintext = b'Proxy Re-encryption is cool!'
>>> ciphertext, capsule = pre.encrypt(alices_public_key, plaintext)
.. code-block:: python .. code-block:: python
plaintext = b'Proxy Re-encryption is cool!' plaintext = b'Proxy Re-encryption is cool!'
ciphertext, capsule = umbral.encrypt(alices_public_key, ciphertext, capsule = pre.encrypt(alices_public_key, plaintext)
plaintext)
Decrypt with a private key Decrypt with a private key
@ -60,15 +94,34 @@ Decrypt with a private key
Since data was encrypted with Alice's public key, Since data was encrypted with Alice's public key,
Alice can open the capsule and decrypt the ciphertext with her private key. Alice can open the capsule and decrypt the ciphertext with her private key.
.. doctest::
>>> cleartext = pre.decrypt(ciphertext=ciphertext, capsule=capsule, decrypting_key=alices_private_key)
.. code-block:: python .. code-block:: python
cleartext = umbral.decrypt(capsule, alices_private_key, cleartext = pre.decrypt(ciphertext=ciphertext, capsule=capsule,
ciphertext, alices_public_key) decrypting_key=alices_private_key)
Threshold split-key re-encryption Threshold split-key re-encryption
================================== ==================================
Bob Exists
-----------
.. doctest::
>>> bobs_private_key = keys.UmbralPrivateKey.gen_key()
>>> bobs_public_key = bobs_private_key.get_pubkey()
.. code-block:: python
# Generate umbral keys for Bob.
bobs_private_key = keys.UmbralPrivateKey.gen_key()
bobs_public_key = bobs_private_key.get_pubkey()
Alice grants access to Bob by generating kfrags Alice grants access to Bob by generating kfrags
----------------------------------------------- -----------------------------------------------
@ -80,13 +133,18 @@ which are next sent to N proxies or *Ursulas*.
| `M` - Minimum threshold of key fragments needed to activate a capsule. | `M` - Minimum threshold of key fragments needed to activate a capsule.
| `N` - Total number of key fragments to generate. | `N` - Total number of key fragments to generate.
.. doctest::
>>> kfrags = pre.split_rekey(delegating_privkey=alices_private_key, signer=alices_signer, receiving_pubkey=bobs_public_key, threshold=10, N=20)
.. code-block:: python .. code-block:: python
kfrags = umbral.split_rekey(alices_private_key, kfrags = pre.split_rekey(delegating_privkey=alices_private_key,
bobs_public_key, signer=alices_signer,
10, # M - Threshold receiving_pubkey=bobs_public_key,
20) # N - Total threshold=10,
N=20)
Bob receives a capsule Bob receives a capsule
----------------------- -----------------------
@ -96,12 +154,8 @@ S3, IPFS, Google Cloud, Sneakernet, etc.
.. code-block:: python .. code-block:: python
# Generate a key pair for Bob # Bob receives the capsule through a side-channel
bobs_private_key = keys.UmbralPrivateKey.gen_key() capsule = capsule
bobs_public_key = private_key.get_pubkey()
# Bob receives the capsule
capsule = <fetch a capsule through side channel>
Bob fails to open the capsule Bob fails to open the capsule
@ -109,13 +163,11 @@ Bob fails to open the capsule
If Bob attempts to open a capsule that was not encrypted for his public key, If Bob attempts to open a capsule that was not encrypted for his public key,
or re-encrypted for him by Ursula, he will not be able to open it. or re-encrypted for him by Ursula, he will not be able to open it.
.. code-block:: python .. code-block:: python
try: try:
fail = umbral.decrypt(capsule, fail = pre.decrypt(ciphertext=ciphertext, capsule=capsule, decrypting_key=bobs_private_key)
bobs_private_key,
ciphertext,
alices_public_key)
except: except:
print("Decryption failed!") print("Decryption failed!")
@ -132,17 +184,30 @@ Bob collects the resulting `cfrags` from several Ursulas.
Bob must gather at least `M` `cfrags` in order to activate the capsule. Bob must gather at least `M` `cfrags` in order to activate the capsule.
.. doctest::
>>> import random
>>> kfrags = random.sample(kfrags, 10)
>>> cfrags = list()
>>> for kfrag in kfrags:
... cfrag = pre.reencrypt(kfrag=kfrag, capsule=capsule)
... cfrags.append(cfrag)
>>> assert len(cfrags) == 10
.. code-block:: python .. code-block:: python
import random import random
kfrags = random.sample(kfrags, # All kfrags from above kfrags = random.sample(kfrags, # All kfrags from above
10) # M - Threshold 10) # M - Threshold
cfrags = list() # Bob's cfrag collection cfrags = list() # Bob's cfrag collection
for kfrag in kfrags: for kfrag in kfrags:
cfrag = umbral.reencrypt(kfrag, capsule) cfrag = pre.reencrypt(kfrag=kfrag, capsule=capsule)
cfrags.append(cfrag) # Bob collects a cfrag cfrags.append(cfrag) # Bob collects a cfrag
Bob attaches cfrags to the capsule Bob attaches cfrags to the capsule
@ -150,8 +215,16 @@ Bob attaches cfrags to the capsule
Bob attaches at least `M` `cfrags` to the capsule; Bob attaches at least `M` `cfrags` to the capsule;
Then it can become *activated*. Then it can become *activated*.
.. doctest::
>>> capsule.set_correctness_keys(delegating=alices_public_key, receiving=bobs_public_key, verifying=alices_verifying_key)
(True, True, True)
>>> for cfrag in cfrags:
... capsule.attach_cfrag(cfrag)
.. code-block:: python .. code-block:: python
capsule.set_correctness_keys(delegating=alices_public_key, receiving=bobs_public_key, verifying=alices_verifying_key)
for cfrag in cfrags: for cfrag in cfrags:
capsule.attach_cfrag(cfrag) capsule.attach_cfrag(cfrag)
@ -161,7 +234,18 @@ Bob activates and opens the capsule
Finally, Bob activates and opens the capsule, Finally, Bob activates and opens the capsule,
then decrypts the re-encrypted ciphertext. then decrypts the re-encrypted ciphertext.
.. doctest::
>>> capsule.set_correctness_keys(delegating=alices_public_key, receiving=bobs_public_key, verifying=alices_verifying_key)
(True, True, True)
>>> for cfrag in cfrags:
... capsule.attach_cfrag(cfrag)
>>> cleartext = pre.decrypt(ciphertext=ciphertext, capsule=capsule, decrypting_key=bobs_private_key)
>>> assert cleartext == plaintext
.. code-block:: python .. code-block:: python
cleartext = umbral.decrypt(capsule, bobs_private_key, cleartext = pre.decrypt(ciphertext=ciphertext,
ciphertext, alices_public_key) capsule=capsule,
decrypting_key=bobs_private_key)