Skip to content

Commit

Permalink
Add some API docs
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Mar 21, 2021
1 parent 6027c36 commit 5041663
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 15 deletions.
51 changes: 51 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Public API
==========

.. automodule:: umbral

Keys
----

.. autoclass:: SecretKey()
:members:

.. autoclass:: PublicKey()
:members:
:show-inheritance:

.. autoclass:: SecretKeyFactory()
:members:

Intermediate objects
--------------------

.. autoclass:: Capsule()
:show-inheritance:

.. autoclass:: KeyFrag()
:members: verify
:show-inheritance:

.. autoclass:: CapsuleFrag()
:members: verify
: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__
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ a proxy re-encryption network to empower privacy in decentralized systems.

installation
using_pyumbral
api


Academic Whitepaper
Expand Down
3 changes: 1 addition & 2 deletions docs/source/installation.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

Installing pyUmbral
====================
v0.1.3-alpha.2


Using pip
-------------------------
Expand Down
3 changes: 3 additions & 0 deletions umbral/capsule.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ def lambda_coeff(xs: Sequence[CurveScalar], i: int) -> CurveScalar:


class Capsule(Serializable):
"""
Encapsulated symmetric key.
"""

class NotValid(ValueError):
"""
Expand Down
8 changes: 8 additions & 0 deletions umbral/capsule_frag.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ def from_kfrag_and_cfrag(cls,


class CapsuleFrag(Serializable):
"""
Re-encrypted fragment of :py:class:`Capsule`.
"""

def __init__(self,
point_e1: CurvePoint,
Expand Down Expand Up @@ -158,6 +161,11 @@ def verify(self,
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

Expand Down
23 changes: 19 additions & 4 deletions umbral/key_frag.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,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,
Expand Down Expand Up @@ -158,10 +161,10 @@ def __hash__(self):

@classmethod
def from_base(cls,
base: 'KeyFragBase',
sign_delegating_key: bool,
sign_receiving_key: bool,
) -> 'KeyFrag':
base: 'KeyFragBase',
sign_delegating_key: bool,
sign_receiving_key: bool,
) -> 'KeyFrag':

kfrag_id = KeyFragID.random()

Expand Down Expand Up @@ -193,6 +196,12 @@ def verify(self,
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

Expand Down Expand Up @@ -278,6 +287,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)

Expand Down
41 changes: 36 additions & 5 deletions umbral/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@


class SecretKey:
"""
Umbral secret (private) key.
"""

__SERIALIZATION_INFO = b"SECRET_KEY"

Expand All @@ -19,7 +22,7 @@ def __init__(self, scalar_key: CurveScalar):
@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())

Expand All @@ -31,17 +34,26 @@ def secret_scalar(self):

@classmethod
def from_encrypted_bytes(cls, key_material: bytes, encrypted_bytes: bytes) -> 'SecretKey':
"""
Restores a secret key from serialized encrypted form.
"""
secret_box = DEM(key_material, info=cls.__SERIALIZATION_INFO)
plain_bytes = secret_box.decrypt(encrypted_bytes)
scalar_key = CurveScalar.from_bytes(plain_bytes)
return cls(scalar_key)

def to_encrypted_bytes(self, key_material: bytes) -> bytes:
"""
Serializes the secret key and encrypts it.
"""
secret_box = DEM(key_material, info=self.__SERIALIZATION_INFO)
return secret_box.encrypt(bytes(self._scalar_key))


class PublicKey(Serializable):
"""
Umbral public key.
"""

def __init__(self, point_key: CurvePoint):
self._point_key = point_key
Expand All @@ -51,6 +63,9 @@ def point(self):

@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
Expand All @@ -63,12 +78,15 @@ def __bytes__(self) -> bytes:

def hex(self) -> str:
"""
Returns an Umbral public key as hex string.
Serializes the public key as a hex string.
"""
return bytes(self).hex()

@classmethod
def from_hex(cls, hex_string: str) -> 'PublicKey':
"""
Restores a public key from a hex string.
"""
return cls.from_bytes(bytes.fromhex(hex_string))

def __repr__(self):
Expand All @@ -83,8 +101,9 @@ def __hash__(self) -> int:

class SecretKeyFactory:
"""
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.
"""

Expand All @@ -94,10 +113,16 @@ def __init__(self, keyring_material: bytes):
self.__keyring_material = keyring_material

@classmethod
def random(cls):
def random(cls) -> 'SecretKeyFactory':
"""
Creates a random factory.
"""
return cls(os.urandom(64))

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.__keyring_material, 64, info=tag)

Expand All @@ -110,10 +135,16 @@ def secret_key_by_label(self, label: bytes) -> SecretKey:

@classmethod
def from_encrypted_bytes(cls, key_material: bytes, encrypted_bytes: bytes) -> 'SecretKeyFactory':
"""
Restores a secret key factory from serialized encrypted form.
"""
secret_box = DEM(key_material, info=cls.__SERIALIZATION_INFO)
keyring_material = secret_box.decrypt(encrypted_bytes)
return cls(keyring_material)

def to_encrypted_bytes(self, key_material: bytes) -> bytes:
"""
Serializes the secret key factory and encrypts it.
"""
secret_box = DEM(key_material, info=self.__SERIALIZATION_INFO)
return secret_box.encrypt(bytes(self.__keyring_material))
13 changes: 11 additions & 2 deletions umbral/pre.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

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.
"""
Expand All @@ -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)


Expand All @@ -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?
Expand Down
22 changes: 22 additions & 0 deletions umbral/serializable.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,37 @@


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}")
return obj

@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)
Expand All @@ -30,10 +43,19 @@ def __take_types__(cls, data: bytes, *types: Type) -> Tuple[List[Any], bytes]:
@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


Expand Down
2 changes: 0 additions & 2 deletions umbral/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
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):
Expand Down

0 comments on commit 5041663

Please sign in to comment.