Add some API docs

pull/263/head
Bogdan Opanchuk 2021-03-20 20:06:03 -07:00
parent bcb0071f9e
commit 0f82580c7e
9 changed files with 137 additions and 9 deletions

57
docs/source/api.rst Normal file
View File

@ -0,0 +1,57 @@
Public API
==========
.. automodule:: umbral
Keys
----
.. autoclass:: SecretKey()
:members:
:show-inheritance:
.. autoclass:: PublicKey()
:members:
:special-members: __eq__, __hash__
:show-inheritance:
.. autoclass:: SecretKeyFactory()
:members:
:show-inheritance:
Intermediate objects
--------------------
.. autoclass:: Capsule()
:special-members: __eq__, __hash__
:show-inheritance:
.. autoclass:: KeyFrag()
:members: verify
:special-members: __eq__, __hash__
:show-inheritance:
.. autoclass:: CapsuleFrag()
:members: verify
:special-members: __eq__, __hash__
:show-inheritance:
Encryption, re-encryption and decryption
----------------------------------------
.. autofunction:: encrypt
.. autofunction:: decrypt_original
.. autofunction:: generate_kfrags
.. autofunction:: reencrypt
.. autofunction:: decrypt_reencrypted
Utilities
---------
.. autoclass:: umbral.serializable.Serializable
:members: from_bytes
:special-members: __bytes__

View File

@ -59,6 +59,7 @@ a proxy re-encryption network to empower privacy in decentralized systems.
installation
using_pyumbral
api
Academic Whitepaper

View File

@ -1,7 +1,6 @@
Installing pyUmbral
====================
v0.1.3-alpha.2
Using pip
-------------------------

View File

@ -20,6 +20,9 @@ def lambda_coeff(xs: Sequence[CurveScalar], i: int) -> CurveScalar:
class Capsule(Serializable):
"""
Encapsulated symmetric key.
"""
class NotValid(ValueError):
"""

View File

@ -95,6 +95,9 @@ class CapsuleFragProof(Serializable):
class CapsuleFrag(Serializable):
"""
Re-encrypted fragment of :py:class:`Capsule`.
"""
def __init__(self,
point_e1: CurvePoint,
@ -160,6 +163,11 @@ class CapsuleFrag(Serializable):
signing_pk: PublicKey,
metadata: Optional[bytes] = None,
) -> bool:
"""
Verifies the validity of this fragment.
``metadata`` should coincide with the one given to :py:func:`reencrypt`.
"""
params = PARAMETERS

View File

@ -126,6 +126,9 @@ def poly_eval(coeffs: List[CurveScalar], x: CurveScalar) -> CurveScalar:
class KeyFrag(Serializable):
"""
A signed fragment of the delegating key.
"""
def __init__(self,
id_: KeyFragID,
@ -195,6 +198,12 @@ class KeyFrag(Serializable):
delegating_pk: Optional[PublicKey] = None,
receiving_pk: Optional[PublicKey] = None,
) -> bool:
"""
Verifies the validity of this fragment.
If the delegating and/or receiving key were not signed in :py:func:`generate_kfrags`,
but are given to this function, they are ignored.
"""
u = PARAMETERS.u
@ -280,6 +289,12 @@ def generate_kfrags(delegating_sk: SecretKey,
sign_delegating_key: bool = True,
sign_receiving_key: bool = True,
) -> List[KeyFrag]:
"""
Generates ``num_kfrags`` key fragments to pass to proxies for re-encryption.
At least ``threshold`` of them will be needed for decryption.
If ``sign_delegating_key`` or ``sign_receiving_key`` are ``True``,
the corresponding keys will have to be provided to :py:meth:`KeyFrag.verify`.
"""
base = KeyFragBase(delegating_sk, receiving_pk, signing_sk, threshold)

View File

@ -17,6 +17,9 @@ if TYPE_CHECKING: # pragma: no cover
class SecretKey(Serializable):
"""
Umbral secret (private) key.
"""
__SERIALIZATION_INFO = b"SECRET_KEY"
@ -26,7 +29,7 @@ class SecretKey(Serializable):
@classmethod
def random(cls) -> 'SecretKey':
"""
Generates a secret key and returns it.
Generates a random secret key and returns it.
"""
return cls(CurveScalar.random_nonzero())
@ -74,8 +77,6 @@ class SecretKey(Serializable):
class Signature(Serializable):
"""
Wrapper for ECDSA signatures.
We store signatures as r and s; this class allows interoperation
between (r, s) and DER formatting.
"""
def __init__(self, r: CurveScalar, s: CurveScalar):
@ -114,6 +115,9 @@ class Signature(Serializable):
class PublicKey(Serializable):
"""
Umbral public key.
"""
def __init__(self, point_key: CurvePoint):
self._point_key = point_key
@ -123,6 +127,9 @@ class PublicKey(Serializable):
@classmethod
def from_secret_key(cls, sk: SecretKey) -> 'PublicKey':
"""
Creates the public key corresponding to the given secret key.
"""
return cls(CurvePoint.generator() * sk.secret_scalar())
@classmethod
@ -145,8 +152,9 @@ class PublicKey(Serializable):
class SecretKeyFactory(Serializable):
"""
This class handles keying material for Umbral, by allowing deterministic
derivation of SecretKeys based on labels.
This class handles keyring material for Umbral, by allowing deterministic
derivation of :py:class:`SecretKey` objects based on labels.
Don't use this key material directly as a key.
"""
@ -158,9 +166,15 @@ class SecretKeyFactory(Serializable):
@classmethod
def random(cls) -> 'SecretKeyFactory':
"""
Creates a random factory.
"""
return cls(os.urandom(cls._KEY_SEED_SIZE))
def secret_key_by_label(self, label: bytes) -> SecretKey:
"""
Creates a :py:class:`SecretKey` from the given label.
"""
tag = b"KEY_DERIVATION/" + label
key = kdf(self.__key_seed, self._DERIVED_KEY_SIZE, info=tag)

View File

@ -9,8 +9,7 @@ from .key_frag import KeyFrag
def encrypt(pk: PublicKey, plaintext: bytes) -> Tuple[Capsule, bytes]:
"""
Performs an encryption using the UmbralDEM object and encapsulates a key
for the sender using the public key provided.
Generates and encapsulates a symmetric key and uses it to encrypt the given plaintext.
Returns the KEM Capsule and the ciphertext.
"""
@ -32,6 +31,13 @@ def decrypt_original(sk: SecretKey, capsule: Capsule, ciphertext: bytes) -> byte
def reencrypt(capsule: Capsule, kfrag: KeyFrag, metadata: Optional[bytes] = None) -> CapsuleFrag:
"""
Creates a capsule fragment using the given key fragment.
Capsule fragments can later be used to decrypt the ciphertext.
If `metadata` is provided, it will have to be used for verification in
:py:meth:`CapsuleFrag.verify`.
"""
return CapsuleFrag.reencrypted(capsule, kfrag, metadata)
@ -41,6 +47,9 @@ def decrypt_reencrypted(decrypting_sk: SecretKey,
cfrags: Sequence[CapsuleFrag],
ciphertext: bytes,
) -> bytes:
"""
Decrypts the ciphertext using the original capsule and the reencrypted capsule fragments.
"""
key_seed = capsule.open_reencrypted(decrypting_sk, delegating_pk, cfrags)
# TODO: add salt and info here?

View File

@ -3,11 +3,17 @@ from typing import Tuple, Type, List, Any, TypeVar
class Serializable(ABC):
"""
A mixin for composable serialization.
"""
_T = TypeVar('_T', bound='Serializable')
@classmethod
def from_bytes(cls: Type[_T], data: bytes) -> _T:
"""
Restores the object from serialized bytes.
"""
obj, remainder = cls.__take__(data)
if len(remainder) != 0:
raise ValueError(f"{len(remainder)} bytes remaining after deserializing {cls}")
@ -15,12 +21,19 @@ class Serializable(ABC):
@classmethod
def __take_bytes__(cls, data: bytes, size: int) -> Tuple[bytes, bytes]:
"""
Takes ``size`` bytes from the bytestring and returns them along with the remainder.
"""
if len(data) < size:
raise ValueError(f"{cls} cannot take {size} bytes from a bytestring of size {len(data)}")
return data[:size], data[size:]
@classmethod
def __take_types__(cls, data: bytes, *types: Type) -> Tuple[List[Any], bytes]:
"""
Given a list of ``Serializable`` types, attempts to deserialize them from the bytestring
one by one and returns the list of the resulting objects and the remaining bytestring.
"""
objs = []
for tp in types:
obj, data = tp.__take__(data)
@ -30,10 +43,19 @@ class Serializable(ABC):
@classmethod
@abstractmethod
def __take__(cls: Type[_T], data: bytes) -> Tuple[_T, bytes]:
"""
Take however much is necessary from ``data`` and instantiate the object,
returning it and the remaining bytestring.
Must be implemented by the derived class.
"""
raise NotImplementedError
@abstractmethod
def __bytes__(self):
"""
Serializes the object into bytes.
"""
raise NotImplementedError