Skip to content

Commit

Permalink
python: add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Dec 3, 2020
1 parent 77239d9 commit 702d2ec
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 30 deletions.
1 change: 1 addition & 0 deletions umbral-pre-python/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ __pycache__
*.so
build/
dist/
docs/_build
20 changes: 20 additions & 0 deletions umbral-pre-python/docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
55 changes: 55 additions & 0 deletions umbral-pre-python/docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))


# -- Project information -----------------------------------------------------

project = 'umbral-pre'
copyright = '2020, Bogdan Opanchuk'
author = 'Bogdan Opanchuk'

# The full version, including alpha/beta/rc tags
release = '0.0.1'


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
94 changes: 94 additions & 0 deletions umbral-pre-python/docs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
Welcome to umbral-pre's documentation!
======================================

.. toctree::
:maxdepth: 2
:caption: Contents:

This package contains the Python bindings for `the main library <https://github.com/nucypher/rust-umbral/tree/master/umbral-pre>`_ written in Rust. It implements the `Umbral <https://github.com/nucypher/umbral-doc/blob/master/umbral-doc.pdf>`_ proxy reencryption scheme.

Usage example
-------------

.. literalinclude:: ../example/example.py


API reference
-------------

.. py:module:: umbral_pre
.. py:class:: SecretKey
An ``umbral-pre`` secret key object.

.. py:staticmethod:: random() -> SecretKey
Generates a new secret key.

.. py:class:: PublicKey
An ``umbral-pre`` public key object.

.. py:staticmethod:: from_secret_key(sk: SecretKey) -> PublicKey
Creates a public key corresponding to the given secret key.


.. py:class:: Parameters()
A scheme parameters object.


.. py:class:: Capsule
An encapsulated symmetric key.


.. py:function:: encrypt(params: Parameters, pk: PublicKey, plaintext: bytes) -> Tuple[Capsule, bytes]
Creates a symmetric key, encrypts ``plaintext`` with it, and returns the encapsulated symmetric key along with the ciphertext. ``pk`` is the public key of the recipient.

.. py:function:: decrypt_original(sk: SecretKey, capsule: Capsule, ciphertext: bytes) -> bytes
Decrypts ``ciphertext`` with the key used to encrypt it.

.. py:function:: generate_kfrags(params: Parameters, delegating_sk: SecretKey, receiving_pk: PublicKey, signing_sk: SecretKey, threshold: int, num_kfrags: int, sign_delegating_key: bool, sign_receiving_key: bool) -> List[KeyFrag]
Generates ``num_kfrags`` key fragments that can be used to reencrypt the capsule for the holder of the secret key corresponding to ``receiving_pk``. ``threshold`` fragments will be enough for decryption.

If ``sign_delegating_key`` or ``sign_receiving_key`` are ``True``, include these keys in the signature allowing proxies to verify the fragments were created with a given key or for a given key, respectively.

.. py:function:: reencrypt(capsule: Capsule, kfrag: KeyFrag, metadata: Optional[bytes]) -> CapsuleFrag
Reencrypts a capsule using a key fragment.
May include optional ``metadata`` in the resulting capsule fragment.


.. py:function:: decrypt_reencrypted(decrypting_sk: SecretKey, delegating_pk: PublicKey, capsule: Capsule, cfrags: Sequence[CapsuleFrag], ciphertext: bytes) -> Optional[bytes]
Attempts to decrypt the plaintext using the original capsule and reencrypted capsule fragments (at least ``threshold`` of them, see :py:func:`generate_kfrags`).

.. py:class:: KeyFrag
A fragment of a public key used by proxies during reencryption.

.. py:method:: verify(signing_pk: PublicKey, delegating_pk: Optional[PublicKey], receiving_pk: Optional[PublicKey]) -> bool:
Verifies the integrity of the fragment using the signing key and, optionally, the delegating and the receiving keys (if they were included in the signature in :py:func:`generate_kfrags`).

.. py:class:: CapsuleFrag
A reencrypted fragment of an encapsulated symmetric key.

.. py:method:: verify(capsule: Capsule, signing_pk: PublicKey, delegating_pk: PublicKey, receiving_pk: PublicKey) -> bool
Verifies the integrity of the fragment.


Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
35 changes: 35 additions & 0 deletions umbral-pre-python/docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
95 changes: 65 additions & 30 deletions umbral-pre-python/example/example.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,86 @@
import umbral_pre

# Generation of global parameters
params = umbral_pre.Parameters()

# Key Generation (Alice)
delegating_sk = umbral_pre.SecretKey.random()
delegating_pk = umbral_pre.PublicKey.from_secret_key(delegating_sk)
# As in any public-key cryptosystem, users need a pair
# of public and private keys.
# Additionally, users that delegate access to their data
# (like Alice, in this example) need a signing keypair.

# Key Generation (on Alice's side)
alice_sk = umbral_pre.SecretKey.random()
alice_pk = umbral_pre.PublicKey.from_secret_key(alice_sk)
signing_sk = umbral_pre.SecretKey.random()
signing_pk = umbral_pre.PublicKey.from_secret_key(signing_sk)

# Key Generation (Bob)
receiving_sk = umbral_pre.SecretKey.random()
receiving_pk = umbral_pre.PublicKey.from_secret_key(receiving_sk)
# Key Generation (on Bob's side)
bob_sk = umbral_pre.SecretKey.random()
bob_pk = umbral_pre.PublicKey.from_secret_key(bob_sk)

# Now let's encrypt data with Alice's public key.
# Invocation of `encrypt()` returns both the ciphertext
# and the encapsulated symmetric key use to encrypt it.
# Note that anyone with Alice's public key
# can perform this operation.

# Encryption by an unnamed data source
params = umbral_pre.Parameters()
plaintext = b"peace at dawn"
capsule, ciphertext = umbral_pre.encrypt(params, delegating_pk, plaintext)
capsule, ciphertext = umbral_pre.encrypt(
params, alice_pk, plaintext)

# Since data was encrypted with Alice's public key,
# Alice can open the capsule and decrypt the ciphertext
# with her private key.

# Decryption by Alice
plaintext_alice = umbral_pre.decrypt_original(delegating_sk, capsule, ciphertext);
plaintext_alice = umbral_pre.decrypt_original(
alice_sk, capsule, ciphertext);
assert plaintext_alice == plaintext

threshold = 2
num_frags = threshold + 1
# When Alice wants to grant Bob access to open her encrypted
# messages, she creates re-encryption key fragments,
# or "kfrags", which are then sent to `n` proxies or Ursulas.

n = 3 # how many fragments to create
m = 2 # how many should be enough to decrypt

# Split Re-Encryption Key Generation (aka Delegation)
kfrags = umbral_pre.generate_kfrags(
params,
delegating_sk,
receiving_pk,
signing_sk,
threshold,
num_frags,
True,
True,
params, alice_sk, bob_pk, signing_sk, m, n,
True, # add the delegating key (alice_pk) to the signature
True, # add the receiving key (bob_pk) to the signature
)

# Ursulas check that the received kfrags are valid
assert all(kfrag.verify(signing_pk, delegating_pk, receiving_pk) for kfrag in kfrags)
# 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, thus obtaining
# a "capsule fragment", or cfrag.

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

# Ursulas can optionally check that the received kfrags
# are valid and perform the reencryption.

metadata = b"metadata"

# Ursula 0
assert kfrags[0].verify(signing_pk, alice_pk, bob_pk)
cfrag0 = umbral_pre.reencrypt(capsule, kfrags[0], metadata)

# Ursula 1
assert kfrags[1].verify(signing_pk, alice_pk, bob_pk)
cfrag1 = umbral_pre.reencrypt(capsule, kfrags[1], metadata)

# ...

# Bob requests re-encryption to some set of `threshold` ursulas
cfrags = [umbral_pre.reencrypt(capsule, kfrag, b"metadata") for kfrag in kfrags]
# Finally, Bob opens the capsule by using at least `m` cfrags,
# and then decrypts the re-encrypted ciphertext.

# Bob checks that the received cfrags are valid
assert all(cfrag.verify(capsule, delegating_pk, receiving_pk, signing_pk) for cfrag in cfrags)
# Bob can optionally check that cfrags are valid
assert cfrag0.verify(capsule, alice_pk, bob_pk, signing_pk)
assert cfrag1.verify(capsule, alice_pk, bob_pk, signing_pk)

# Decryption by Bob
plaintext_bob = umbral_pre.decrypt_reencrypted(receiving_sk, delegating_pk, capsule, cfrags, ciphertext)
plaintext_bob = umbral_pre.decrypt_reencrypted(
bob_sk, alice_pk, capsule, [cfrag0, cfrag1], ciphertext)
assert plaintext_bob == plaintext

0 comments on commit 702d2ec

Please sign in to comment.