Source code for umbral.capsule_frag

from typing import Optional, Tuple, Type

from .capsule import Capsule
from .curve_point import CurvePoint
from .curve_scalar import CurveScalar
from .errors import VerificationError
from .hashing import hash_to_cfrag_verification, kfrag_signature_message
from .keys import PublicKey
from .key_frag import KeyFrag, KeyFragID
from .params import PARAMETERS
from .serializable import Serializable, Deserializable, HasSerializedSize
from .signing import Signature


class CapsuleFragProof(Serializable, Deserializable):

    def __init__(self,
                 point_e2: CurvePoint,
                 point_v2: CurvePoint,
                 kfrag_commitment: CurvePoint,
                 kfrag_pok: CurvePoint,
                 signature: CurveScalar,
                 kfrag_signature: Signature,
                 ):

        self.point_e2 = point_e2
        self.point_v2 = point_v2
        self.kfrag_commitment = kfrag_commitment
        self.kfrag_pok = kfrag_pok
        self.signature = signature
        self.kfrag_signature = kfrag_signature

    def _components(self):
        return (self.point_e2, self.point_v2, self.kfrag_commitment,
                self.kfrag_pok, self.signature, self.kfrag_signature)

    _COMPONENT_TYPES: Tuple[Type[HasSerializedSize], ...] = (
        CurvePoint, CurvePoint, CurvePoint, CurvePoint, CurveScalar, Signature)
    _SERIALIZED_SIZE = sum(tp.serialized_size() for tp in _COMPONENT_TYPES)

    def __eq__(self, other):
        return self._components() == other._components()

    @classmethod
    def serialized_size(cls):
        return cls._SERIALIZED_SIZE

    @classmethod
    def _from_exact_bytes(cls, data):
        return cls(*cls._split(data, *cls._COMPONENT_TYPES))

    def __bytes__(self):
        return b''.join(bytes(comp) for comp in self._components())

    @classmethod
    def from_kfrag_and_cfrag(cls,
                             capsule: Capsule,
                             kfrag: KeyFrag,
                             cfrag_e1: CurvePoint,
                             cfrag_v1: CurvePoint,
                             ) -> 'CapsuleFragProof':

        params = PARAMETERS

        rk = kfrag.key
        t = CurveScalar.random_nonzero()

        # Here are the formulaic constituents shared with `CapsuleFrag.verify()`.

        e = capsule.point_e
        v = capsule.point_v

        e1 = cfrag_e1
        v1 = cfrag_v1

        u = params.u
        u1 = kfrag.proof.commitment

        e2 = e * t
        v2 = v * t
        u2 = u * t

        h = hash_to_cfrag_verification([e, e1, e2, v, v1, v2, u, u1, u2])

        ###

        z3 = t + rk * h

        return cls(point_e2=e2,
                   point_v2=v2,
                   kfrag_commitment=u1,
                   kfrag_pok=u2,
                   signature=z3,
                   kfrag_signature=kfrag.proof.signature_for_receiver,
                   )


[docs]class CapsuleFrag(Serializable, Deserializable): """ Re-encrypted fragment of :py:class:`Capsule`. """ def __init__(self, point_e1: CurvePoint, point_v1: CurvePoint, kfrag_id: KeyFragID, precursor: CurvePoint, proof: CapsuleFragProof, ): self.point_e1 = point_e1 self.point_v1 = point_v1 self.kfrag_id = kfrag_id self.precursor = precursor self.proof = proof def _components(self): return (self.point_e1, self.point_v1, self.kfrag_id, self.precursor, self.proof) _COMPONENT_TYPES: Tuple[Type[HasSerializedSize], ...] = ( CurvePoint, CurvePoint, KeyFragID, CurvePoint, CapsuleFragProof) _SERIALIZED_SIZE = sum(tp.serialized_size() for tp in _COMPONENT_TYPES)
[docs] def __eq__(self, other): return self._components() == other._components()
[docs] def __hash__(self): return hash((self.__class__, bytes(self)))
def __str__(self): return f"{self.__class__.__name__}:{bytes(self).hex()[:16]}"
[docs] @classmethod def serialized_size(cls): return cls._SERIALIZED_SIZE
@classmethod def _from_exact_bytes(cls, data): return cls(*cls._split(data, *cls._COMPONENT_TYPES)) def __bytes__(self): return b''.join(bytes(comp) for comp in self._components()) @classmethod def reencrypted(cls, capsule: Capsule, kfrag: KeyFrag) -> 'CapsuleFrag': rk = kfrag.key e1 = capsule.point_e * rk v1 = capsule.point_v * rk proof = CapsuleFragProof.from_kfrag_and_cfrag(capsule, kfrag, e1, v1) return cls(point_e1=e1, point_v1=v1, kfrag_id=kfrag.id, precursor=kfrag.precursor, proof=proof, )
[docs] def verify(self, capsule: Capsule, verifying_pk: PublicKey, delegating_pk: PublicKey, receiving_pk: PublicKey, ) -> 'VerifiedCapsuleFrag': """ Verifies the validity of this fragment. """ params = PARAMETERS # Here are the formulaic constituents shared with # `CapsuleFragProof.from_kfrag_and_cfrag`. e = capsule.point_e v = capsule.point_v e1 = self.point_e1 v1 = self.point_v1 u = params.u u1 = self.proof.kfrag_commitment e2 = self.proof.point_e2 v2 = self.proof.point_v2 u2 = self.proof.kfrag_pok h = hash_to_cfrag_verification([e, e1, e2, v, v1, v2, u, u1, u2]) ### precursor = self.precursor kfrag_id = self.kfrag_id kfrag_message = kfrag_signature_message(kfrag_id=self.kfrag_id, commitment=self.proof.kfrag_commitment, precursor=self.precursor, maybe_delegating_pk=delegating_pk, maybe_receiving_pk=receiving_pk) if not self.proof.kfrag_signature.verify(verifying_pk, kfrag_message): raise VerificationError("Invalid KeyFrag signature") z = self.proof.signature # TODO: if one or more of the values here are incorrect, # we'll get the wrong `h` (since they're all hashed into it), # so perhaps it's enough to check only one of these equations. # See https://github.com/nucypher/rust-umbral/issues/46 for details. correct_reencryption_of_e = e * z == e2 + e1 * h correct_reencryption_of_v = v * z == v2 + v1 * h correct_rk_commitment = u * z == u2 + u1 * h if not (correct_reencryption_of_e and correct_reencryption_of_v and correct_rk_commitment): raise VerificationError("Failed to verify reencryption proof") return VerifiedCapsuleFrag(self)
[docs]class VerifiedCapsuleFrag(Serializable): """ Verified capsule frag, good for decryption. Can be cast to ``bytes``, but cannot be deserialized from bytes directly. It can only be obtained from :py:meth:`CapsuleFrag.verify`. """ def __init__(self, cfrag: CapsuleFrag): self.cfrag = cfrag def __bytes__(self): return bytes(self.cfrag)
[docs] @classmethod def serialized_size(cls): return CapsuleFrag.serialized_size()
[docs] @classmethod def from_verified_bytes(cls, data) -> 'VerifiedCapsuleFrag': """ Restores a verified capsule frag directly from serialized bytes, skipping :py:meth:`CapsuleFrag.verify` call. Intended for internal storage; make sure that the bytes come from a trusted source. """ cfrag = CapsuleFrag.from_bytes(data) return cls(cfrag)
[docs] def __eq__(self, other): return self.cfrag == other.cfrag
[docs] def __hash__(self): return hash((self.__class__, bytes(self)))
def __str__(self): return f"{self.__class__.__name__}:{bytes(self).hex()[:16]}"