Skip to main content

Python library that serves as an API for common cryptographic primitives used to implement OPRF, OT, and PSI protocols.

Project description

Python library that serves as an API for common cryptographic primitives used to implement OPRF, OT, and PSI protocols.

PyPI version and link. Read the Docs documentation status. GitHub Actions status. Coveralls test coverage summary.

Purpose

This library provides native Python implementations, Python libsodium wrappers, and additional utility methods for cryptographic primitives that are often used to implement oblivious pseudorandom function (OPRF), oblivious transfer (OT), and private set intersection (PSI) protocols.

For more information and background about the underlying mathematical structures and primitives, consult materials about Curve25519, the Ristretto group, and the related Ed25519 system.

Package Installation and Usage

The package is available on PyPI:

python -m pip install oblivious

The library can be imported in the usual ways:

import oblivious
from oblivious import *

Examples

This library supports concise construction of elliptic curve points and scalars:

>>> from oblivious import point, scalar
>>> p = point.hash('abc'.encode()) # Point derived from a hash of a string.
>>> s = scalar() # Random scalar.

Built-in Python operators are overloaded to support point operations (addition, subtraction, negation, and equality) and scalar operations (multiplication by a scalar and inversion of scalars):

>>> q = s * p
>>> p == (~s) * q
True
>>> p == (((~s) * s)) * p
True
>>> p + q == q + p
True

Because the classes point and scalar are derived from bytes, all methods and other operators supported by bytes objects are supported by point and scalar objects:

>>> hex = '35c141f1c2c43543de9d188805a210abca3cd39a1e986304991ceded42b11709'
>>> s = scalar.fromhex(hex)
>>> s.hex()
'35c141f1c2c43543de9d188805a210abca3cd39a1e986304991ceded42b11709'

In addition, Base64 conversion methods are included to support concise encoding and decoding of point and scalar objects:

>>> s.to_base64()
'NcFB8cLENUPenRiIBaIQq8o805oemGMEmRzt7UKxFwk='
>>> s == scalar.from_base64('NcFB8cLENUPenRiIBaIQq8o805oemGMEmRzt7UKxFwk=')
True

Using Native Python or Shared/Dynamic Library

In addition to the operations and classes exported by this library, two wrapper classes/namespaces are also exported: native and sodium. These encapsulate pure Python implementations and shared/dynamic library (i.e., libsodium) wrappers, respectively, of all operations and classes available in the oblivious module. This makes it possible to explicitly choose whether an operation requires only Python or also requires the presence of a compiled copy of libsodium on the host system.

The example below uses native Python implementations of the scalar multiplication operation (relying on the ge25519 library):

>>> from oblivious import native
>>> p = native.point.hash('abc'.encode())
>>> s = native.scalar.hash('123'.encode())
>>> (s * p).to_base64()
'SrC7vA9sSR5f4E27ALxk14MPotTYR6B33B4ZN+mQXFA='

To check whether an instance of the libsodium shared/dynamic library has been loaded successfully, the check below can be performed:

>>> from oblivious import sodium
>>> sodium is not None # Was the dynamic/shared library loaded?
True

In the example below, the scalar multiplication operation invokes a binding for the crypto_scalarmult_ristretto255 function exported by libsodium:

>>> p = sodium.point.hash('abc'.encode())
>>> s = sodium.scalar.hash('123'.encode())
>>> (s * p).to_base64()
'SrC7vA9sSR5f4E27ALxk14MPotTYR6B33B4ZN+mQXFA='

The operations and class methods exported by the oblivious module directly (e.g., the method __add__ within the class point that is imported via the statement from oblivious import point) correspond either (A) to libsodium wrappers if an instance of libsodium is found and loaded or (B) to pure Python implementations if all attempts to load a working instances of libsodium fail. The ordered list below summarizes what definitions are exported under various conditions and the ordered sequence of attempts to locate and load an instance of libsodium.

  1. Under all conditions, the wrapper class native is defined and encapsulates a pure Python variant of every operation and class method available in the oblivious module. As a starting default, all operations and classes exported directly by the oblivious module correspond to the pure Python implementations.

  2. If a shared/dynamic library instance of libsodium is found on the system and successfully loaded during one of the attempts below, then the wrapper class sodium is defined:

    1. the built-in ctypes.util.find_library function is able to locate 'sodium' or 'libsodium' and it is loaded successfully;

    2. a file libsodium.so or libsodium.dll in the paths specified by the PATH and LD_LIBRARY_PATH environment variables is found and loaded successfully; or

    3. the optional rbcl package is installed and the compiled subset of libsodium included in that package is loaded successfully.

  3. If sodium is not None, then the sodium class encapsulates libsodium wrappers for every operation and class supported by the oblivious module. Furthermore, those operations and classes exported directly by the library are redefined to use the bindings available in the loaded instance of libsodium. The native class is still exported, as well, and all operations and class methods encapsulated within native remain as-is (i.e., pure Python implementations).

Documentation

The documentation can be generated automatically from the source files using Sphinx:

cd docs
python -m pip install -r requirements.txt
sphinx-apidoc -f -E --templatedir=_templates -o _source .. ../setup.py && make html

Testing and Conventions

All unit tests are executed and their coverage is measured when using pytest (see setup.cfg for configuration details, and note that unit tests that require rbcl are skipped if that optional package is not installed):

python -m pip install pytest pytest-cov
python -m pytest

Concise unit tests are implemented with the help of fountains; new reference specifications for these tests can be generated by running the testing module directly:

python test/test_oblivious.py

Style conventions are enforced using Pylint:

python -m pip install pylint
python -m pylint oblivious ./test/test_oblivious.py

Contributions

In order to contribute to the source code, open an issue or submit a pull request on the GitHub page for this library.

Versioning

Beginning with version 0.1.0, the version number format for this library and the changes to the library associated with version number increments conform with Semantic Versioning 2.0.0.

Publishing

This library can be published as a package on PyPI by a package maintainer. Install the wheel package, remove any old build/distribution files, and package the source into a distribution archive:

python -m pip install wheel
rm -rf dist *.egg-info
python setup.py sdist bdist_wheel

Next, install the twine package and upload the package distribution archive to PyPI:

python -m pip install twine
python -m twine upload dist/*

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oblivious-5.0.0.tar.gz (19.5 kB view hashes)

Uploaded Source

Built Distribution

oblivious-5.0.0-py3-none-any.whl (13.4 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page