2018-02-12 18:51:11 +00:00
|
|
|
from typing import Tuple, Union, List
|
|
|
|
|
2018-03-13 12:08:30 +00:00
|
|
|
from cryptography.hazmat.backends.openssl import backend
|
2018-01-03 17:27:08 +00:00
|
|
|
from cryptography.hazmat.primitives.asymmetric import ec
|
2018-03-13 12:08:30 +00:00
|
|
|
from cryptography.hazmat.primitives import hashes
|
2018-01-10 14:48:33 +00:00
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
from umbral.curvebn import CurveBN
|
2018-02-08 04:09:47 +00:00
|
|
|
from umbral.config import default_params, default_curve
|
2018-01-23 10:13:08 +00:00
|
|
|
from umbral.dem import UmbralDEM
|
2018-04-17 10:17:14 +00:00
|
|
|
from umbral.fragments import KFrag, CapsuleFrag, CorrectnessProof
|
2018-01-31 08:22:55 +00:00
|
|
|
from umbral.keys import UmbralPrivateKey, UmbralPublicKey
|
2018-02-01 08:47:06 +00:00
|
|
|
from umbral.params import UmbralParameters
|
2018-02-01 17:06:11 +00:00
|
|
|
from umbral.point import Point
|
2018-02-08 21:38:43 +00:00
|
|
|
from umbral.utils import poly_eval, lambda_coeff, kdf, get_curve_keysize_bytes
|
2018-01-03 17:27:08 +00:00
|
|
|
|
2018-02-08 20:15:44 +00:00
|
|
|
from io import BytesIO
|
|
|
|
|
2018-01-18 07:30:36 +00:00
|
|
|
|
2018-03-08 01:37:33 +00:00
|
|
|
CHACHA20_KEY_SIZE = 32
|
|
|
|
|
|
|
|
|
2018-02-12 22:48:34 +00:00
|
|
|
class GenericUmbralError(Exception):
|
|
|
|
pass
|
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
|
2018-04-18 20:59:27 +00:00
|
|
|
class UmbralCorrectnessError(GenericUmbralError):
|
|
|
|
def __init__(self, message, offending_cfrags):
|
|
|
|
super().__init__(message)
|
|
|
|
self.offending_cfrags = offending_cfrags
|
2018-02-12 22:48:34 +00:00
|
|
|
|
2018-02-21 07:34:58 +00:00
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
class Capsule(object):
|
2018-02-08 22:25:54 +00:00
|
|
|
def __init__(self,
|
2018-04-23 09:33:43 +00:00
|
|
|
point_e=None,
|
|
|
|
point_v=None,
|
2018-02-08 22:25:54 +00:00
|
|
|
bn_sig=None,
|
2018-04-23 09:33:43 +00:00
|
|
|
point_e_prime=None,
|
|
|
|
point_v_prime=None,
|
|
|
|
point_noninteractive=None):
|
|
|
|
|
|
|
|
if isinstance(point_e, Point):
|
2018-04-23 18:03:18 +00:00
|
|
|
if not isinstance(point_v, Point) or not isinstance(bn_sig, CurveBN):
|
2018-04-23 09:33:43 +00:00
|
|
|
raise TypeError("Need point_e, point_v, and bn_sig to make a Capsule.")
|
|
|
|
elif isinstance(point_e_prime, Point):
|
|
|
|
if not isinstance(point_v_prime, Point) or not isinstance(point_noninteractive, Point):
|
|
|
|
raise TypeError("Need e_prime, v_prime, and point_noninteractive to make an activated Capsule.")
|
2018-02-23 00:13:16 +00:00
|
|
|
else:
|
2018-02-23 06:44:26 +00:00
|
|
|
raise TypeError(
|
2018-04-23 18:03:18 +00:00
|
|
|
"Need proper Points and/or CurveBNs to make a Capsule. Pass either Alice's data or Bob's. " \
|
2018-02-23 06:44:26 +00:00
|
|
|
"Passing both is also fine.")
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
self._point_e = point_e
|
|
|
|
self._point_v = point_v
|
2018-02-08 22:25:54 +00:00
|
|
|
self._bn_sig = bn_sig
|
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
self._point_e_prime = point_e_prime
|
|
|
|
self._point_v_prime = point_v_prime
|
|
|
|
self._point_noninteractive = point_noninteractive
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-04-24 22:20:48 +00:00
|
|
|
self._attached_cfrags = list()
|
2018-02-08 22:25:54 +00:00
|
|
|
|
|
|
|
class NotValid(ValueError):
|
|
|
|
"""
|
2018-04-23 09:33:43 +00:00
|
|
|
raised if the capsule does not pass verification.
|
2018-02-08 22:25:54 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_bytes(cls, capsule_bytes: bytes, curve: ec.EllipticCurve = None):
|
|
|
|
"""
|
|
|
|
Instantiates a Capsule object from the serialized data.
|
|
|
|
"""
|
|
|
|
curve = curve if curve is not None else default_curve()
|
|
|
|
key_size = get_curve_keysize_bytes(curve)
|
|
|
|
capsule_buff = BytesIO(capsule_bytes)
|
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
# CurveBNs are the keysize in bytes, Points are compressed and the
|
2018-02-08 22:25:54 +00:00
|
|
|
# keysize + 1 bytes long.
|
|
|
|
if len(capsule_bytes) == 197:
|
2018-04-23 09:33:43 +00:00
|
|
|
e = Point.from_bytes(capsule_buff.read(key_size + 1), curve)
|
|
|
|
v = Point.from_bytes(capsule_buff.read(key_size + 1), curve)
|
2018-04-23 18:03:18 +00:00
|
|
|
sig = CurveBN.from_bytes(capsule_buff.read(key_size), curve)
|
2018-02-08 22:25:54 +00:00
|
|
|
e_prime = Point.from_bytes(capsule_buff.read(key_size + 1), curve)
|
|
|
|
v_prime = Point.from_bytes(capsule_buff.read(key_size + 1), curve)
|
2018-04-23 09:33:43 +00:00
|
|
|
ni = Point.from_bytes(capsule_buff.read(key_size + 1), curve)
|
2018-02-08 22:25:54 +00:00
|
|
|
else:
|
2018-04-23 09:33:43 +00:00
|
|
|
e = Point.from_bytes(capsule_buff.read(key_size + 1), curve)
|
|
|
|
v = Point.from_bytes(capsule_buff.read(key_size + 1), curve)
|
2018-04-23 18:03:18 +00:00
|
|
|
sig = CurveBN.from_bytes(capsule_buff.read(key_size), curve)
|
2018-04-23 09:33:43 +00:00
|
|
|
e_prime = v_prime = ni = None
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
return cls(point_e=e, point_v=v, bn_sig=sig,
|
|
|
|
point_e_prime=e_prime, point_v_prime=v_prime,
|
|
|
|
point_noninteractive=ni)
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-02-26 09:26:50 +00:00
|
|
|
def _original_to_bytes(self) -> bytes:
|
|
|
|
return bytes().join(c.to_bytes() for c in self.original_components())
|
|
|
|
|
2018-02-12 18:51:11 +00:00
|
|
|
def to_bytes(self) -> bytes:
|
2018-02-08 22:25:54 +00:00
|
|
|
"""
|
|
|
|
Serialize the Capsule into a bytestring.
|
|
|
|
"""
|
2018-02-26 09:26:50 +00:00
|
|
|
bytes_representation = self._original_to_bytes()
|
2018-02-08 22:25:54 +00:00
|
|
|
if all(self.activated_components()):
|
|
|
|
bytes_representation += bytes().join(c.to_bytes() for c in self.activated_components())
|
|
|
|
return bytes_representation
|
|
|
|
|
2018-02-12 18:51:11 +00:00
|
|
|
def verify(self, params: UmbralParameters=None) -> bool:
|
2018-02-08 22:25:54 +00:00
|
|
|
params = params if params is not None else default_params()
|
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
e = self._point_e
|
|
|
|
v = self._point_v
|
2018-02-08 22:25:54 +00:00
|
|
|
s = self._bn_sig
|
2018-05-01 23:14:42 +00:00
|
|
|
h = CurveBN.hash(e, v, params=params)
|
2018-02-08 22:25:54 +00:00
|
|
|
|
|
|
|
return s * params.g == v + (h * e)
|
|
|
|
|
2018-02-12 18:51:11 +00:00
|
|
|
def attach_cfrag(self, cfrag: CapsuleFrag) -> None:
|
2018-04-24 22:20:48 +00:00
|
|
|
self._attached_cfrags.append(cfrag)
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
def original_components(self) -> Tuple[Point, Point, CurveBN]:
|
2018-04-23 09:33:43 +00:00
|
|
|
return self._point_e, self._point_v, self._bn_sig
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-04-23 18:54:06 +00:00
|
|
|
def activated_components(self) -> Union[Tuple[None, None, None], Tuple[Point, Point, Point]]:
|
2018-04-23 09:33:43 +00:00
|
|
|
return self._point_e_prime, self._point_v_prime, self._point_noninteractive
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-03-13 12:08:30 +00:00
|
|
|
def _reconstruct_shamirs_secret(self,
|
|
|
|
pub_a: Union[UmbralPublicKey, Point],
|
2018-04-23 18:03:18 +00:00
|
|
|
priv_b: Union[UmbralPrivateKey, CurveBN],
|
2018-03-31 19:12:33 +00:00
|
|
|
params: UmbralParameters=None) -> None:
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-03-31 19:12:33 +00:00
|
|
|
params = params if params is not None else default_params()
|
2018-03-13 12:08:30 +00:00
|
|
|
|
|
|
|
if isinstance(priv_b, UmbralPrivateKey):
|
|
|
|
priv_b = priv_b.bn_key
|
|
|
|
|
|
|
|
if isinstance(pub_a, UmbralPublicKey):
|
|
|
|
pub_a = pub_a.point_key
|
|
|
|
|
|
|
|
g = params.g
|
2018-03-14 09:37:41 +00:00
|
|
|
pub_b = priv_b * g
|
|
|
|
g_ab = priv_b * pub_a
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-03-13 14:43:25 +00:00
|
|
|
blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
|
|
|
|
blake2b.update(pub_a.to_bytes())
|
|
|
|
blake2b.update(pub_b.to_bytes())
|
|
|
|
blake2b.update(g_ab.to_bytes())
|
|
|
|
hashed_dh_tuple = blake2b.finalize()
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-04-24 22:20:48 +00:00
|
|
|
cfrag_0 = self._attached_cfrags[0]
|
|
|
|
id_0 = cfrag_0._bn_kfrag_id
|
2018-05-01 23:14:42 +00:00
|
|
|
x_0 = CurveBN.hash(id_0, hashed_dh_tuple, params=params)
|
2018-04-24 22:20:48 +00:00
|
|
|
if len(self._attached_cfrags) > 1:
|
2018-05-01 23:14:42 +00:00
|
|
|
xs = [CurveBN.hash(cfrag._bn_kfrag_id, hashed_dh_tuple, params=params)
|
2018-04-24 22:20:48 +00:00
|
|
|
for cfrag in self._attached_cfrags]
|
2018-03-13 12:08:30 +00:00
|
|
|
lambda_0 = lambda_coeff(x_0, xs)
|
2018-04-23 09:33:43 +00:00
|
|
|
e = lambda_0 * cfrag_0._point_e1
|
|
|
|
v = lambda_0 * cfrag_0._point_v1
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-04-24 22:20:48 +00:00
|
|
|
for cfrag in self._attached_cfrags[1:]:
|
2018-05-01 23:14:42 +00:00
|
|
|
x_i = CurveBN.hash(cfrag._bn_kfrag_id, hashed_dh_tuple, params=params)
|
2018-03-13 12:08:30 +00:00
|
|
|
lambda_i = lambda_coeff(x_i, xs)
|
2018-04-23 09:33:43 +00:00
|
|
|
e = e + (lambda_i * cfrag._point_e1)
|
|
|
|
v = v + (lambda_i * cfrag._point_v1)
|
2018-02-08 22:25:54 +00:00
|
|
|
else:
|
2018-04-23 09:33:43 +00:00
|
|
|
e = cfrag_0._point_e1
|
|
|
|
v = cfrag_0._point_v1
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
self._point_e_prime = e
|
|
|
|
self._point_v_prime = v
|
|
|
|
self._point_noninteractive = cfrag_0._point_noninteractive
|
2018-02-08 22:25:54 +00:00
|
|
|
|
|
|
|
def __bytes__(self):
|
2018-02-13 20:47:21 +00:00
|
|
|
return self.to_bytes()
|
2018-02-08 22:25:54 +00:00
|
|
|
|
|
|
|
def __eq__(self, other):
|
2018-02-21 07:34:58 +00:00
|
|
|
"""
|
|
|
|
If both Capsules are activated, we compare only the activated components.
|
|
|
|
Otherwise, we compare only original components.
|
2018-04-23 18:03:18 +00:00
|
|
|
Each component is compared to its counterpart in constant time per the __eq__ of Point and CurveBN.
|
2018-02-21 07:34:58 +00:00
|
|
|
"""
|
2018-02-08 22:25:54 +00:00
|
|
|
if all(self.activated_components() + other.activated_components()):
|
2018-02-23 00:13:16 +00:00
|
|
|
activated_match = self.activated_components() == other.activated_components()
|
|
|
|
return activated_match
|
2018-02-08 22:25:54 +00:00
|
|
|
elif all(self.original_components() + other.original_components()):
|
2018-02-23 00:13:16 +00:00
|
|
|
original_match = self.original_components() == other.original_components()
|
|
|
|
return original_match
|
2018-02-08 22:25:54 +00:00
|
|
|
else:
|
2018-02-23 00:13:16 +00:00
|
|
|
# This is not constant time obviously, but it's hard to imagine how this is valuable as
|
|
|
|
# an attacker already knows about her own Capsule. It's possible that a Bob, having
|
|
|
|
# activated a Capsule, will make it available for comparison via an API amidst other
|
|
|
|
# (dormat) Capsules. Then an attacker can, by alternating between activated and dormant
|
|
|
|
# Capsules, determine if a given Capsule is activated. Do we care about this?
|
|
|
|
# Again, it's hard to imagine why.
|
2018-02-08 22:25:54 +00:00
|
|
|
return False
|
|
|
|
|
2018-02-21 07:35:57 +00:00
|
|
|
def __hash__(self):
|
|
|
|
# We only ever want to store in a hash table based on original components;
|
|
|
|
# A Capsule that is part of a dict needs to continue to be lookup-able even
|
|
|
|
# after activation.
|
|
|
|
# Note: In case this isn't obvious, don't use this as a secure hash. Use BLAKE2b or something.
|
2018-02-23 02:53:34 +00:00
|
|
|
component_bytes = tuple(component.to_bytes() for component in self.original_components())
|
2018-02-21 07:35:57 +00:00
|
|
|
return hash(component_bytes)
|
|
|
|
|
2018-02-08 22:25:54 +00:00
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
def split_rekey(priv_a: Union[UmbralPrivateKey, CurveBN],
|
2018-05-06 02:03:53 +00:00
|
|
|
point_bob: Union[UmbralPublicKey, Point],
|
2018-02-12 20:44:07 +00:00
|
|
|
threshold: int, N: int,
|
2018-02-26 14:36:56 +00:00
|
|
|
params: UmbralParameters=None) -> List[KFrag]:
|
2018-02-08 09:30:56 +00:00
|
|
|
"""
|
2018-02-26 14:36:56 +00:00
|
|
|
Creates a re-encryption key from Alice to Bob and splits it in KFrags,
|
|
|
|
using Shamir's Secret Sharing. Requires a threshold number of KFrags
|
|
|
|
out of N to guarantee correctness of re-encryption.
|
2018-02-08 05:26:38 +00:00
|
|
|
|
2018-02-26 14:36:56 +00:00
|
|
|
Returns a list of KFrags.
|
2018-02-08 09:30:56 +00:00
|
|
|
"""
|
|
|
|
params = params if params is not None else default_params()
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-02-12 18:51:11 +00:00
|
|
|
if isinstance(priv_a, UmbralPrivateKey):
|
2018-02-08 09:30:56 +00:00
|
|
|
priv_a = priv_a.bn_key
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-05-06 02:03:53 +00:00
|
|
|
if isinstance(point_bob, UmbralPublicKey):
|
|
|
|
point_bob = point_bob.point_key
|
2018-01-17 16:54:37 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
g = params.g
|
2018-05-06 02:03:53 +00:00
|
|
|
point_alice = priv_a * g
|
2017-12-28 14:02:53 +00:00
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
x = CurveBN.gen_rand(params.curve)
|
2018-02-08 09:30:56 +00:00
|
|
|
xcomp = x * g
|
2018-05-06 02:03:53 +00:00
|
|
|
d = CurveBN.hash(xcomp, point_bob, point_bob * x, params=params)
|
2017-12-28 14:02:53 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
coeffs = [priv_a * (~d)]
|
2018-04-23 18:03:18 +00:00
|
|
|
coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)]
|
2017-12-28 14:02:53 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
u = params.u
|
2017-12-28 14:02:53 +00:00
|
|
|
|
2018-05-06 02:03:53 +00:00
|
|
|
g_ab = priv_a * point_bob
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-03-13 14:43:25 +00:00
|
|
|
blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
|
2018-05-06 02:03:53 +00:00
|
|
|
blake2b.update(point_alice.to_bytes())
|
|
|
|
blake2b.update(point_bob.to_bytes())
|
2018-03-13 14:43:25 +00:00
|
|
|
blake2b.update(g_ab.to_bytes())
|
|
|
|
hashed_dh_tuple = blake2b.finalize()
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-02-26 14:36:56 +00:00
|
|
|
kfrags = []
|
2018-02-08 09:30:56 +00:00
|
|
|
for _ in range(N):
|
2018-04-23 18:03:18 +00:00
|
|
|
id_kfrag = CurveBN.gen_rand(params.curve)
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-05-01 23:14:42 +00:00
|
|
|
share_x = CurveBN.hash(id_kfrag, hashed_dh_tuple, params=params)
|
2018-03-13 12:08:30 +00:00
|
|
|
|
|
|
|
rk = poly_eval(coeffs, share_x)
|
2017-12-28 14:02:53 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
u1 = rk * u
|
2018-04-23 18:03:18 +00:00
|
|
|
y = CurveBN.gen_rand(params.curve)
|
2017-12-28 14:02:53 +00:00
|
|
|
|
2018-05-06 02:03:53 +00:00
|
|
|
signature_input = [y * g, id_kfrag, point_alice, point_bob, u1, xcomp]
|
|
|
|
z1 = CurveBN.hash(*signature_input, params=params)
|
2018-02-08 09:30:56 +00:00
|
|
|
z2 = y - priv_a * z1
|
2017-12-28 14:02:53 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
kfrag = KFrag(bn_id=id_kfrag, bn_key=rk,
|
|
|
|
point_noninteractive=xcomp, point_commitment=u1,
|
|
|
|
bn_sig1=z1, bn_sig2=z2)
|
2018-02-26 14:36:56 +00:00
|
|
|
kfrags.append(kfrag)
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-02-26 14:36:56 +00:00
|
|
|
return kfrags
|
2018-01-18 07:30:36 +00:00
|
|
|
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-04-22 21:43:39 +00:00
|
|
|
def reencrypt(kfrag: KFrag, capsule: Capsule, params: UmbralParameters=None,
|
|
|
|
provide_proof=True, metadata: bytes=None) -> CapsuleFrag:
|
2018-04-17 10:17:14 +00:00
|
|
|
if params is None:
|
|
|
|
params = default_params()
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
if not capsule.verify(params):
|
|
|
|
raise capsule.NotValid
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
e1 = kfrag._bn_key * capsule._point_e
|
|
|
|
v1 = kfrag._bn_key * capsule._point_v
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
cfrag = CapsuleFrag(point_e1=e1, point_v1=v1, bn_kfrag_id=kfrag._bn_id,
|
|
|
|
point_noninteractive=kfrag._point_noninteractive)
|
2018-04-17 10:17:14 +00:00
|
|
|
|
2018-04-22 21:43:39 +00:00
|
|
|
if provide_proof:
|
|
|
|
_prove_correctness(cfrag, kfrag, capsule, metadata, params)
|
2018-04-17 10:17:14 +00:00
|
|
|
|
2018-03-29 03:59:28 +00:00
|
|
|
return cfrag
|
2017-12-29 00:13:16 +00:00
|
|
|
|
|
|
|
|
2018-04-22 21:43:39 +00:00
|
|
|
def _prove_correctness(cfrag: CapsuleFrag, kfrag: KFrag, capsule: Capsule,
|
|
|
|
metadata: bytes=None, params: UmbralParameters=None
|
|
|
|
) -> CorrectnessProof:
|
2018-05-06 00:02:43 +00:00
|
|
|
t = CurveBN.gen_rand(params.curve)
|
|
|
|
rk = kfrag._bn_key
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-05-06 00:02:43 +00:00
|
|
|
params = params if params is not None else default_params()
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-05-06 00:02:43 +00:00
|
|
|
####
|
|
|
|
## Here are the formulaic constituents shared with `verify_correctness`.
|
|
|
|
####
|
2018-04-23 09:33:43 +00:00
|
|
|
e = capsule._point_e
|
|
|
|
v = capsule._point_v
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-05-06 00:02:43 +00:00
|
|
|
e1 = cfrag._point_e1
|
|
|
|
v1 = cfrag._point_v1
|
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
u = params.u
|
2018-04-23 09:33:43 +00:00
|
|
|
u1 = kfrag._point_commitment
|
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
e2 = t * e
|
|
|
|
v2 = t * v
|
|
|
|
u2 = t * u
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-03-13 14:33:23 +00:00
|
|
|
hash_input = [e, e1, e2, v, v1, v2, u, u1, u2]
|
2018-04-19 11:14:33 +00:00
|
|
|
if metadata is not None:
|
|
|
|
hash_input.append(metadata)
|
2018-05-01 23:14:42 +00:00
|
|
|
h = CurveBN.hash(*hash_input, params=params)
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-05-06 00:02:43 +00:00
|
|
|
z1 = kfrag._bn_sig1
|
|
|
|
z2 = kfrag._bn_sig2
|
2018-04-23 09:33:43 +00:00
|
|
|
z3 = t + h * rk
|
2018-05-06 00:02:43 +00:00
|
|
|
########
|
|
|
|
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
cfrag.proof = CorrectnessProof(point_e2=e2,
|
|
|
|
point_v2=v2,
|
|
|
|
point_kfrag_commitment=u1,
|
|
|
|
point_kfrag_pok=u2,
|
2018-05-06 00:02:43 +00:00
|
|
|
bn_kfrag_sig1=z1,
|
|
|
|
bn_kfrag_sig2=z2,
|
2018-04-23 09:33:43 +00:00
|
|
|
bn_sig=z3,
|
|
|
|
metadata=metadata)
|
2018-04-22 21:43:39 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
# Check correctness of original ciphertext (check nº 2) at the end
|
|
|
|
# to avoid timing oracles
|
2018-02-12 22:48:34 +00:00
|
|
|
if not capsule.verify(params):
|
|
|
|
raise capsule.NotValid("Capsule verification failed.")
|
|
|
|
|
2017-12-28 01:07:37 +00:00
|
|
|
|
2018-02-12 20:44:07 +00:00
|
|
|
def _encapsulate(alice_pub_key: Point, key_length=32,
|
2018-02-12 18:51:11 +00:00
|
|
|
params: UmbralParameters=None) -> Tuple[bytes, Capsule]:
|
2018-02-08 09:30:56 +00:00
|
|
|
"""Generates a symmetric key and its associated KEM ciphertext"""
|
|
|
|
params = params if params is not None else default_params()
|
2017-12-28 01:07:37 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
g = params.g
|
2017-12-28 01:07:37 +00:00
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
priv_r = CurveBN.gen_rand(params.curve)
|
2018-02-08 09:30:56 +00:00
|
|
|
pub_r = priv_r * g
|
2017-12-28 01:07:37 +00:00
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
priv_u = CurveBN.gen_rand(params.curve)
|
2018-02-08 09:30:56 +00:00
|
|
|
pub_u = priv_u * g
|
2018-01-03 17:27:27 +00:00
|
|
|
|
2018-05-01 23:14:42 +00:00
|
|
|
h = CurveBN.hash(pub_r, pub_u, params=params)
|
2018-02-08 09:30:56 +00:00
|
|
|
s = priv_u + (priv_r * h)
|
2018-01-19 23:48:49 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
shared_key = (priv_r + priv_u) * alice_pub_key
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
# Key to be used for symmetric encryption
|
|
|
|
key = kdf(shared_key, key_length)
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
return key, Capsule(point_e=pub_r, point_v=pub_u, bn_sig=s)
|
2017-12-29 00:13:16 +00:00
|
|
|
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
def _decapsulate_original(priv_key: CurveBN, capsule: Capsule, key_length=32,
|
2018-02-12 18:51:11 +00:00
|
|
|
params: UmbralParameters=None) -> bytes:
|
2018-02-08 09:30:56 +00:00
|
|
|
"""Derive the same symmetric key"""
|
|
|
|
params = params if params is not None else default_params()
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
shared_key = priv_key * (capsule._point_e+capsule._point_v)
|
2018-02-08 09:30:56 +00:00
|
|
|
key = kdf(shared_key, key_length)
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-02-12 22:48:34 +00:00
|
|
|
if not capsule.verify(params):
|
|
|
|
# Check correctness of original ciphertext
|
|
|
|
# (check nº 2) at the end to avoid timing oracles
|
|
|
|
raise capsule.NotValid("Capsule verification failed.")
|
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
return key
|
2018-01-23 10:13:08 +00:00
|
|
|
|
|
|
|
|
2018-04-23 18:03:18 +00:00
|
|
|
def _decapsulate_reencrypted(pub_key: Point, priv_key: CurveBN,
|
2018-02-12 20:44:07 +00:00
|
|
|
orig_pub_key: Point, capsule: Capsule,
|
|
|
|
key_length=32, params: UmbralParameters=None) -> bytes:
|
2018-02-08 09:30:56 +00:00
|
|
|
"""Derive the same symmetric key"""
|
|
|
|
params = params if params is not None else default_params()
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
xcomp = capsule._point_noninteractive
|
2018-05-01 23:14:42 +00:00
|
|
|
d = CurveBN.hash(xcomp, pub_key, priv_key * xcomp, params=params)
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
e_prime = capsule._point_e_prime
|
|
|
|
v_prime = capsule._point_v_prime
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
shared_key = d * (e_prime + v_prime)
|
2018-01-23 10:13:08 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
key = kdf(shared_key, key_length)
|
|
|
|
|
2018-04-23 09:33:43 +00:00
|
|
|
e = capsule._point_e
|
|
|
|
v = capsule._point_v
|
2018-02-08 09:30:56 +00:00
|
|
|
s = capsule._bn_sig
|
2018-05-01 23:14:42 +00:00
|
|
|
h = CurveBN.hash(e, v, params=params)
|
2018-02-08 09:30:56 +00:00
|
|
|
inv_d = ~d
|
|
|
|
|
2018-02-12 22:48:34 +00:00
|
|
|
if not (s*inv_d) * orig_pub_key == (h*e_prime) + v_prime:
|
|
|
|
raise GenericUmbralError()
|
2018-02-08 09:30:56 +00:00
|
|
|
return key
|
|
|
|
|
|
|
|
|
2018-03-31 19:12:33 +00:00
|
|
|
def encrypt(alice_pubkey: UmbralPublicKey, plaintext: bytes,
|
|
|
|
params: UmbralParameters=None) -> Tuple[bytes, Capsule]:
|
2018-02-08 09:30:56 +00:00
|
|
|
"""
|
|
|
|
Performs an encryption using the UmbralDEM object and encapsulates a key
|
|
|
|
for the sender using the public key provided.
|
|
|
|
|
|
|
|
Returns the ciphertext and the KEM Capsule.
|
|
|
|
"""
|
2018-03-31 19:12:33 +00:00
|
|
|
params = params if params is not None else default_params()
|
|
|
|
|
|
|
|
key, capsule = _encapsulate(alice_pubkey.point_key, CHACHA20_KEY_SIZE, params=params)
|
2018-02-08 09:30:56 +00:00
|
|
|
|
2018-02-27 08:08:27 +00:00
|
|
|
capsule_bytes = bytes(capsule)
|
2018-02-26 09:29:02 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
dem = UmbralDEM(key)
|
2018-02-27 08:08:27 +00:00
|
|
|
ciphertext = dem.encrypt(plaintext, authenticated_data=capsule_bytes)
|
2018-02-08 09:30:56 +00:00
|
|
|
|
2018-02-12 18:51:11 +00:00
|
|
|
return ciphertext, capsule
|
2018-02-08 09:30:56 +00:00
|
|
|
|
|
|
|
|
2018-04-18 13:14:10 +00:00
|
|
|
def _open_capsule(capsule: Capsule, bob_privkey: UmbralPrivateKey,
|
2018-04-24 08:21:43 +00:00
|
|
|
alice_pubkey: UmbralPublicKey, params: UmbralParameters=None,
|
|
|
|
check_proof=True) -> bytes:
|
2018-02-08 09:30:56 +00:00
|
|
|
"""
|
|
|
|
Activates the Capsule from the attached CFrags,
|
|
|
|
opens the Capsule and returns what is inside.
|
|
|
|
|
|
|
|
This will often be a symmetric key.
|
|
|
|
"""
|
2018-03-31 19:12:33 +00:00
|
|
|
params = params if params is not None else default_params()
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-04-18 13:14:10 +00:00
|
|
|
priv_b = bob_privkey.bn_key
|
2018-05-06 00:02:05 +00:00
|
|
|
bob_pubkey = bob_privkey.get_pubkey()
|
2018-04-18 13:14:10 +00:00
|
|
|
|
|
|
|
# TODO: Change dict for a list if issue #116 goes through
|
2018-04-24 08:21:43 +00:00
|
|
|
if check_proof:
|
|
|
|
offending_cfrags = []
|
2018-04-24 22:20:48 +00:00
|
|
|
for cfrag in capsule._attached_cfrags:
|
2018-05-06 00:02:05 +00:00
|
|
|
if not cfrag.verify_correctness(capsule, alice_pubkey,
|
|
|
|
bob_pubkey, params):
|
2018-04-24 08:21:43 +00:00
|
|
|
offending_cfrags.append(cfrag)
|
2018-04-18 20:59:27 +00:00
|
|
|
|
2018-04-24 08:21:43 +00:00
|
|
|
if offending_cfrags:
|
2018-04-23 09:33:43 +00:00
|
|
|
error_msg = "Decryption error: Some CFrags are not correct"
|
|
|
|
raise UmbralCorrectnessError(error_msg, offending_cfrags)
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-05-06 00:02:05 +00:00
|
|
|
capsule._reconstruct_shamirs_secret(alice_pubkey.point_key, priv_b, params=params)
|
2018-03-13 12:08:30 +00:00
|
|
|
|
2018-05-06 00:02:05 +00:00
|
|
|
key = _decapsulate_reencrypted(bob_pubkey.point_key, priv_b, alice_pubkey.point_key, capsule, params=params)
|
2018-02-08 09:30:56 +00:00
|
|
|
return key
|
|
|
|
|
|
|
|
|
2018-04-24 08:21:43 +00:00
|
|
|
def decrypt(ciphertext: bytes, capsule: Capsule,
|
|
|
|
priv_key: UmbralPrivateKey, alice_pub_key: UmbralPublicKey=None,
|
|
|
|
params: UmbralParameters=None, check_proof=True) -> bytes:
|
2018-02-08 09:30:56 +00:00
|
|
|
"""
|
|
|
|
Opens the capsule and gets what's inside.
|
|
|
|
|
|
|
|
We hope that's a symmetric key, which we use to decrypt the ciphertext
|
|
|
|
and return the resulting cleartext.
|
|
|
|
"""
|
2018-03-31 19:12:33 +00:00
|
|
|
params = params if params is not None else default_params()
|
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
if capsule._attached_cfrags:
|
|
|
|
# Since there are cfrags attached, we assume this is Bob opening the Capsule.
|
2018-04-18 09:37:40 +00:00
|
|
|
# (i.e., this is a re-encrypted capsule)
|
2018-04-18 13:14:10 +00:00
|
|
|
|
2018-02-08 09:30:56 +00:00
|
|
|
bob_priv_key = priv_key
|
2018-04-18 13:14:10 +00:00
|
|
|
|
2018-04-24 08:21:43 +00:00
|
|
|
encapsulated_key = _open_capsule(capsule, bob_priv_key, alice_pub_key,
|
|
|
|
params=params, check_proof=check_proof)
|
2018-04-18 13:14:10 +00:00
|
|
|
dem = UmbralDEM(encapsulated_key)
|
2018-02-26 09:29:02 +00:00
|
|
|
|
2018-02-27 08:08:27 +00:00
|
|
|
original_capsule_bytes = capsule._original_to_bytes()
|
|
|
|
cleartext = dem.decrypt(ciphertext, authenticated_data=original_capsule_bytes)
|
2018-02-08 09:30:56 +00:00
|
|
|
else:
|
2018-03-14 11:00:04 +00:00
|
|
|
# Since there aren't cfrags attached, we assume this is Alice opening the Capsule.
|
2018-04-18 09:37:40 +00:00
|
|
|
# (i.e., this is an original capsule)
|
2018-04-18 13:14:10 +00:00
|
|
|
encapsulated_key = _decapsulate_original(priv_key.bn_key, capsule, params=params)
|
|
|
|
dem = UmbralDEM(encapsulated_key)
|
2018-02-26 09:29:02 +00:00
|
|
|
|
2018-02-27 08:08:27 +00:00
|
|
|
capsule_bytes = bytes(capsule)
|
|
|
|
cleartext = dem.decrypt(ciphertext, authenticated_data=capsule_bytes)
|
2018-02-12 18:51:11 +00:00
|
|
|
|
|
|
|
return cleartext
|