ristretto module

This module exports the classes point and scalar for representing points and scalars. It also exports the two wrapper classes/namespaces python and sodium that encapsulate pure-Python and shared/dynamic library variants of the above (respectively) and also include low-level operations that correspond more directly to the functions found in the underlying libraries.

  • Under all conditions, the wrapper class python is defined and encapsulates a pure-Python variant of every class exported by this module as a whole. It also includes pure-Python variants of low-level operations that correspond to functions found in the underlying libraries.

  • If a shared/dynamic library instance of the libsodium library is found on the system (and successfully loaded at the time this module is imported) or the optional rbcl package is installed, then the wrapper class sodium is defined. Otherwise, the exported variable sodium is assigned None.

  • If a dynamic/shared library instance is loaded, all classes exported by this module correspond to the variants defined within sodium. Otherwise, they correspond to the variants defined within python.

For most users, the classes point and scalar should be sufficient. When using the low-level operations that correspond to a specific implementation (e.g., oblivious.ristretto.sodium.add), users are responsible for ensuring that inputs have the type and/or representation appropriate for that operation.

class oblivious.ristretto.point(bs: Optional[bytes] = None)[source]

Bases: bytes

Class for representing a point. Because this class is derived from bytes, it inherits methods such as bytes.hex and bytes.fromhex.

>>> len(point.random())
32
>>> p = point.hash('123'.encode())
>>> p.hex()
'047f39a6c6dd156531a25fa605f017d4bec13b0b6c42f0e9b641c8ee73359c5f'
>>> point.fromhex(p.hex()) == p
True
classmethod random() oblivious.ristretto.point[source]

Return random point object.

>>> len(point.random())
32
classmethod bytes(bs: bytes) oblivious.ristretto.point[source]

Return the point object obtained by transforming the supplied bytes-like object.

>>> p = point.bytes(hashlib.sha512('123'.encode()).digest())
>>> p.hex()
'047f39a6c6dd156531a25fa605f017d4bec13b0b6c42f0e9b641c8ee73359c5f'
classmethod hash(bs: bytes) oblivious.ristretto.point[source]

Return point object by hashing supplied bytes-like object.

>>> point.hash('123'.encode()).hex()
'047f39a6c6dd156531a25fa605f017d4bec13b0b6c42f0e9b641c8ee73359c5f'
classmethod base(s: oblivious.ristretto.scalar) Optional[oblivious.ristretto.point][source]

Return base point multiplied by supplied scalar if the scalar is valid; otherwise, return None.

>>> point.base(scalar.hash('123'.encode())).hex()
'4c207a5377f3badf358914f20b505cd1e2a6396720a9c240e5aff522e2446005'

Use of the scalar corresponding to the zero residue is permitted.

>>> p = point()
>>> point.base(scalar.from_int(0)) + p == p
True
classmethod from_bytes(bs: bytes) oblivious.ristretto.point[source]

Return the instance corresponding to the supplied bytes-like object.

>>> p = point.bytes(hashlib.sha512('123'.encode()).digest())
>>> p == point.from_bytes(p.to_bytes())
True
classmethod from_base64(s: str) oblivious.ristretto.point[source]

Construct an instance from its Base64 UTF-8 string representation.

>>> point.from_base64('hoVaKq3oIlxEndP2Nqv3Rdbmiu4iinZE6Iwo+kcKAik=').hex()
'86855a2aade8225c449dd3f636abf745d6e68aee228a7644e88c28fa470a0229'
canonical() oblivious.ristretto.point[source]

Normalize the representation of this instance into its canonical form.

>>> p = point.hash('123'.encode())
>>> p.canonical() == p
True
__mul__(other: Any) NoReturn[source]

A point cannot be a left-hand argument for a multiplication operation.

>>> point() * scalar()
Traceback (most recent call last):
  ...
TypeError: point must be on right-hand side of multiplication operator
__rmul__(other: Any) NoReturn[source]

This functionality is implemented exclusively in the method scalar.__mul__, as that method pre-empts this method when the second argument has the correct type (i.e., it is a scalar instance). This method is included so that an exception can be raised if an incorrect argument is supplied.

>>> p = point.hash('123'.encode())
>>> 2 * p
Traceback (most recent call last):
  ...
TypeError: point can only be multiplied by a scalar
__add__(other: oblivious.ristretto.point) Optional[oblivious.ristretto.point][source]

Return the sum of this instance and another point.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> (p + q).hex()
'7076739c9df665d416e68b9512f5513bf1d0181a2aacefdeb1b7244528a4dd77'
>>> p + (q - q) == p
True
__sub__(other: oblivious.ristretto.point) Optional[oblivious.ristretto.point][source]

Return the result of subtracting another point from this instance.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> (p - q).hex()
'1a3199ca7debfe31a90171696d8bab91b99eb23a541b822a7061b09776e1046c'
>>> p - p == point.base(scalar.from_int(0))
True
__neg__() oblivious.ristretto.point[source]

Return the negation of this instance.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> ((p + q) + (-q)) == p
True
to_bytes() bytes[source]

Return the bytes-like object that represents this instance.

>>> p = point()
>>> p.to_bytes() == p
True
to_base64() str[source]

Return the Base64 UTF-8 string representation of this instance.

>>> p = point.from_base64('hoVaKq3oIlxEndP2Nqv3Rdbmiu4iinZE6Iwo+kcKAik=')
>>> p.to_base64()
'hoVaKq3oIlxEndP2Nqv3Rdbmiu4iinZE6Iwo+kcKAik='
class oblivious.ristretto.scalar(bs: Optional[bytes] = None)[source]

Bases: bytes

Class for representing a scalar. Because this class is derived from bytes, it inherits methods such as bytes.hex and bytes.fromhex.

>>> len(scalar.random())
32
>>> s = scalar.hash('123'.encode())
>>> s.hex()
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27a03'
>>> scalar.fromhex(s.hex()) == s
True
classmethod random() oblivious.ristretto.scalar[source]

Return random non-zero scalar object.

>>> len(scalar.random())
32
classmethod bytes(bs: bytes) Optional[oblivious.ristretto.scalar][source]

Return scalar object obtained by transforming supplied bytes-like object if it is possible to do; otherwise, return None.

>>> s = python.scl()
>>> t = scalar.bytes(s)
>>> s.hex() == t.hex()
True
classmethod hash(bs: bytes) oblivious.ristretto.scalar[source]

Return scalar object by hashing supplied bytes-like object.

>>> scalar.hash('123'.encode()).hex()
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27a03'
classmethod from_int(i: int) oblivious.ristretto.scalar[source]

Construct an instance from its integer (i.e., residue) representation.

>>> p = point()
>>> zero = scalar.from_int(0)
>>> zero * p == p - p
True
>>> one = scalar.from_int(1)
>>> one * p == p
True
>>> two = scalar.from_int(2)
>>> two * p == p + p
True

Negative integers are supported (and automatically converted into their corresponding least nonnegative residues).

>>> q = point()
>>> p - p == scalar.from_int(0) * p
True
>>> q - p - p == q + (scalar.from_int(-2) * p)
True
classmethod from_bytes(bs: bytes) Optional[oblivious.ristretto.scalar][source]

Return the instance corresponding to the supplied bytes-like object.

>>> s = python.scl()
>>> t = scalar.from_bytes(s)
>>> s.hex() == t.hex()
True
classmethod from_base64(s: str) oblivious.ristretto.scalar[source]

Construct an instance from its Base64 UTF-8 string representation.

>>> scalar.from_base64('MS0MkTD2kVO+yfXQOGqVE160XuvxMK9fH+0cbtFfJQA=').hex()
'312d0c9130f69153bec9f5d0386a95135eb45eebf130af5f1fed1c6ed15f2500'
__invert__() oblivious.ristretto.scalar[source]

Return the inverse of this instance (modulo 2**252 + 27742317777372353535851937790883648493).

>>> s = scalar()
>>> p = point()
>>> ((~s) * (s * p)) == p
True

The scalar corresponding to the zero residue cannot be inverted.

>>> ~scalar.from_int(0)
Traceback (most recent call last):
  ...
ValueError: cannot invert scalar corresponding to zero
__mul__(other: Union[oblivious.ristretto.scalar, oblivious.ristretto.point]) Union[oblivious.ristretto.scalar, oblivious.ristretto.point][source]

Multiply the supplied scalar or point by this instance.

>>> p = point.hash('123'.encode())
>>> s = scalar.hash('456'.encode())
>>> (s * p).hex()
'f61b377aa86050aaa88c90f4a4a0f1e36b0000cf46f6a34232c2f1da7a799f16'
>>> p = point.from_base64('hoVaKq3oIlxEndP2Nqv3Rdbmiu4iinZE6Iwo+kcKAik=')
>>> s = scalar.from_base64('MS0MkTD2kVO+yfXQOGqVE160XuvxMK9fH+0cbtFfJQA=')
>>> (s * s).hex()
'd4aecf034f60edc5cb32cdd5a4be6d069959aa9fd133c51c9dcfd960ee865e0f'
>>> isinstance(s * s, scalar)
True
>>> (s * p).hex()
'2208082412921a67f42ea399748190d2b889228372509f2f2d9929813d074e1b'
>>> isinstance(s * p, point)
True

Multiplying any point or scalar by the scalar corresponding to the zero residue yields the point or scalar corresponding to zero.

>>> scalar.from_int(0) * point() == p - p
True
>>> scalar.from_int(0) * scalar() == scalar.from_int(0)
True

Any attempt to multiply a value or object of an incompatible type by this instance raises an exception.

>>> s * 2
Traceback (most recent call last):
  ...
TypeError: multiplication by a scalar is defined only for scalars and points
__rmul__(other: Union[oblivious.ristretto.scalar, oblivious.ristretto.point])[source]

A scalar cannot be on the right-hand side of a non-scalar.

>>> point() * scalar()
Traceback (most recent call last):
  ...
TypeError: point must be on right-hand side of multiplication operator
to_bytes() bytes[source]

Return the bytes-like object that represents this instance.

>>> s = scalar()
>>> s.to_bytes() == s
True
__int__() int[source]

Return the integer (i.e., least nonnegative residue) representation of this instance.

>>> s = scalar()
>>> int(s * (~s))
1
to_int() int[source]

Return the integer (i.e., least nonnegative residue) representation of this instance.

>>> s = scalar()
>>> (s * (~s)).to_int()
1
to_base64() str[source]

Return the Base64 UTF-8 string representation of this instance.

>>> s = scalar.from_base64('MS0MkTD2kVO+yfXQOGqVE160XuvxMK9fH+0cbtFfJQA=')
>>> s.to_base64()
'MS0MkTD2kVO+yfXQOGqVE160XuvxMK9fH+0cbtFfJQA='
class oblivious.ristretto.python[source]

Bases: object

Wrapper class for pure-Python implementations of primitive operations.

This class encapsulates pure-Python variants of all low-level operations and of both classes exported by this module: python.scl, python.rnd, python.inv, python.smu, python.pnt, python.bas, python.can, python.mul, python.add, python.sub, python.neg, python.point, and python.scalar. For example, you can perform addition of points using the pure-Python point addition implementation.

>>> p = python.pnt()
>>> q = python.pnt()
>>> python.add(p, q) == python.add(q, p)
True

Pure-Python variants of the python.point and python.scalar classes always employ pure-Python implementations of operations when their methods are invoked.

>>> p = python.point()
>>> q = python.point()
>>> p + q == q + p
True

Nevertheless, all bytes-like objects, point objects, and scalar objects accepted and emitted by the various operations and class methods in python are compatible with those accepted and emitted by the operations and class methods in sodium.

static pnt(h: Optional[bytes] = None) bytes[source]

Return point from 64-byte vector (normally obtained via hashing).

>>> p = python.pnt(hashlib.sha512('123'.encode()).digest())
>>> p.hex()
'047f39a6c6dd156531a25fa605f017d4bec13b0b6c42f0e9b641c8ee73359c5f'
static bas(s: bytes) bytes[source]

Return base point multiplied by supplied scalar.

>>> python.bas(scalar.hash('123'.encode())).hex()
'4c207a5377f3badf358914f20b505cd1e2a6396720a9c240e5aff522e2446005'
static can(p: bytes) bytes[source]

Normalize the representation of a point into its canonical form.

>>> p = point.hash('123'.encode())
>>> python.can(p) == p
True
static mul(s: bytes, p: bytes) bytes[source]

Multiply the point by the supplied scalar and return the result.

>>> p = python.pnt(hashlib.sha512('123'.encode()).digest())
>>> s = python.scl(bytes.fromhex(
...     '35c141f1c2c43543de9d188805a210abca3cd39a1e986304991ceded42b11709'
... ))
>>> python.mul(s, p).hex()
'183a06e0fe6af5d7913afb40baefc4dd52ae718fee77a3a0af8777c89fe16210'
static add(p: bytes, q: bytes) bytes[source]

Return sum of the supplied points.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> python.add(p, q).hex()
'7076739c9df665d416e68b9512f5513bf1d0181a2aacefdeb1b7244528a4dd77'
static sub(p: bytes, q: bytes) bytes[source]

Return result of subtracting second point from first point.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> python.sub(p, q).hex()
'1a3199ca7debfe31a90171696d8bab91b99eb23a541b822a7061b09776e1046c'
static neg(p: bytes) bytes[source]

Return the additive inverse of a point.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> python.add(python.neg(p), python.add(p, q)) == q
True
static rnd() bytes[source]

Return random non-zero scalar.

>>> len(python.rnd())
32
classmethod scl(s: Optional[bytes] = None) Optional[bytes][source]

Return supplied byte vector if it is a valid scalar; otherwise, return None. If no byte vector is supplied, return a random scalar.

>>> s = python.scl()
>>> t = python.scl(s)
>>> s == t
True
>>> python.scl(bytes([255] * 32)) is None
True
static inv(s: bytes) bytes[source]

Return the inverse of a scalar (modulo 2**252 + 27742317777372353535851937790883648493).

>>> s = python.scl()
>>> p = python.pnt()
>>> python.mul(python.inv(s), python.mul(s, p)) == p
True
static smu(s: bytes, t: bytes) bytes[source]

Return scalar multiplied by another scalar.

>>> s = python.scl()
>>> t = python.scl()
>>> python.smu(s, t) == python.smu(t, s)
True
class point(bs: Optional[bytes] = None)

Bases: bytes

class scalar(bs: Optional[bytes] = None)

Bases: bytes

class oblivious.ristretto.sodium[source]

Bases: object

Wrapper class for binary implementations of primitive operations.

When this module is imported, it makes a number of attempts to locate an instance of the shared/dynamic library file of the libsodium library on the host system. The sequence of attempts is listed below, in order.

1. It uses ctypes.util.find_library to look for 'sodium' or 'libsodium'.

  1. It attempts to find a file libsodium.so or libsodium.dll in the paths specified by the PATH and LD_LIBRARY_PATH environment variables.

  2. If the rbcl package is installed, it reverts to the compiled subset of libsodium included in that package.

If all of the above fail, then sodium is assigned the value None and all classes exported by this module default to their pure-Python variants (i.e., those encapsulated within python). To confirm that a dynamic/shared library has been found when this module is imported, evaluate the expression sodium is not None.

If a shared/dynamic library file has been loaded successfully, this class encapsulates shared/dynamic library variants of both classes exported by this module and of all the underlying low-level operations: sodium.scl, sodium.rnd, sodium.inv, sodium.smu, sodium.pnt, sodium.bas, sodium.can, sodium.mul, sodium.add, sodium.sub, sodium.neg, sodium.point, and sodium.scalar. For example, you can perform addition of points using the point addition implementation found in the libsodium shared/dynamic library found on the host system.

>>> p = sodium.pnt()
>>> q = sodium.pnt()
>>> sodium.add(p, q) == sodium.add(q, p)
True

Methods found in the shared/dynamic library variants of the point and scalar classes are wrappers for the shared/dynamic library implementations of the underlying operations.

>>> p = sodium.point()
>>> q = sodium.point()
>>> p + q == q + p
True

Nevertheless, all bytes-like objects, point objects, and scalar objects accepted and emitted by the various operations and class methods in sodium are compatible with those accepted and emitted by the operations and class methods in python.

static pnt(h: Optional[bytes] = None) bytes[source]

Construct a point from its 64-byte vector representation (normally obtained via hashing).

>>> p = sodium.pnt(hashlib.sha512('123'.encode()).digest())
>>> p.hex()
'047f39a6c6dd156531a25fa605f017d4bec13b0b6c42f0e9b641c8ee73359c5f'
static bas(s: bytes) bytes[source]

Return the base point multiplied by the supplied scalar.

>>> sodium.bas(scalar.hash('123'.encode())).hex()
'4c207a5377f3badf358914f20b505cd1e2a6396720a9c240e5aff522e2446005'
static can(p: bytes) bytes[source]

Normalize the representation of a point into its canonical form.

>>> p = point.hash('123'.encode())
>>> sodium.can(p) == p
True
static mul(s: bytes, p: bytes) bytes[source]

Multiply a point by a scalar and return the result.

>>> p = sodium.pnt(hashlib.sha512('123'.encode()).digest())
>>> s = sodium.scl(bytes.fromhex(
...     '35c141f1c2c43543de9d188805a210abca3cd39a1e986304991ceded42b11709'
... ))
>>> sodium.mul(s, p).hex()
'183a06e0fe6af5d7913afb40baefc4dd52ae718fee77a3a0af8777c89fe16210'
static add(p: bytes, q: bytes) bytes[source]

Return the sum of the supplied points.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> sodium.add(p, q).hex()
'7076739c9df665d416e68b9512f5513bf1d0181a2aacefdeb1b7244528a4dd77'
static sub(p: bytes, q: bytes) bytes[source]

Return the result of subtracting the right-hand point from the left-hand point.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> sodium.sub(p, q).hex()
'1a3199ca7debfe31a90171696d8bab91b99eb23a541b822a7061b09776e1046c'
static neg(p: bytes) bytes[source]

Return the additive inverse of a point.

>>> p = point.hash('123'.encode())
>>> q = point.hash('456'.encode())
>>> sodium.add(sodium.neg(p), sodium.add(p, q)) == q
True
static rnd() bytes[source]

Return random non-zero scalar.

>>> len(sodium.rnd())
32
classmethod scl(s: Optional[bytes] = None) Optional[bytes][source]

Return supplied byte vector if it is a valid scalar; otherwise, return None. If no byte vector is supplied, return a random scalar.

>>> s = sodium.scl()
>>> t = sodium.scl(s)
>>> s == t
True
>>> sodium.scl(bytes([255] * 32)) is None
True
static inv(s: bytes) bytes[source]

Return the inverse of a scalar (modulo 2**252 + 27742317777372353535851937790883648493).

>>> s = sodium.scl()
>>> p = sodium.pnt()
>>> sodium.mul(sodium.inv(s), sodium.mul(s, p)) == p
True
static smu(s: bytes, t: bytes) bytes[source]

Return the product of two scalars.

>>> s = sodium.scl()
>>> t = sodium.scl()
>>> sodium.smu(s, t) == sodium.smu(t, s)
True
class point(bs: Optional[bytes] = None)

Bases: bytes

class scalar(bs: Optional[bytes] = None)

Bases: bytes