KFrags now has 2 signatures (Bob and proxy) and a new point

* point_precursor removes need for point_xcoord and point_noninteractive
* Fixes access to protected attributes in some places
pull/220/head
David Núñez 2018-10-02 17:33:35 +02:00
parent f535f926b9
commit 054777eb53
14 changed files with 200 additions and 210 deletions

View File

@ -128,32 +128,6 @@ def test_cfrag_with_missing_proof_cannot_be_attached(kfrags, prepared_capsule):
capsule.attach_cfrag(cfrag)
def test_inconsistent_cfrags(bobs_keys, kfrags, prepared_capsule):
receiving_privkey, receiving_pubkey = bobs_keys
capsule = prepared_capsule
cfrags = []
for kfrag in kfrags:
cfrag = pre.reencrypt(kfrag, capsule)
cfrags.append(cfrag)
# For all cfrags that belong to the same policy, the values
# cfrag._point_noninteractive and cfrag._point_noninteractive
# must be the same. If we swap them, it shouldn't be possible
# to attach the cfrag to the capsule. Let's mangle the first CFrag
cfrags[0]._point_noninteractive, cfrags[0]._point_xcoord = cfrags[0]._point_xcoord, cfrags[0]._point_noninteractive
with pytest.raises(pre.UmbralCorrectnessError):
capsule.attach_cfrag(cfrags[0])
#  The remaining M cfrags should be fine.
for cfrag in cfrags[1:]:
capsule.attach_cfrag(cfrag)
# Just for fun, let's try to reconstruct the capsule with them:
capsule._reconstruct_shamirs_secret(receiving_privkey)
def test_kfrags_signed_without_correctness_keys(alices_keys, bobs_keys, capsule):
delegating_privkey, signing_privkey = alices_keys
delegating_pubkey = delegating_privkey.get_pubkey()

View File

@ -188,7 +188,7 @@ def test_cfrags():
assert new_cfrag._point_e1 == cfrag._point_e1
assert new_cfrag._point_v1 == cfrag._point_v1
assert new_cfrag._kfrag_id == cfrag._kfrag_id
assert new_cfrag._point_noninteractive == cfrag._point_noninteractive
assert new_cfrag._point_precursor == cfrag._point_precursor
assert new_cfrag._point_xcoord == cfrag._point_xcoord
assert new_cfrag.proof is None
assert cfrag.to_bytes() == new_cfrag.to_bytes()

View File

@ -97,4 +97,4 @@ def test_simple_api(N, M, curve=default_curve()):
@pytest.mark.parametrize("N, M", parameters)
def test_simple_api_on_multiple_curves(N, M, curve):
test_simple_api(N, M, curve)

View File

@ -44,8 +44,7 @@ def test_cannot_attach_cfrag_without_keys():
cfrag = CapsuleFrag(point_e1=Point.gen_rand(),
point_v1=Point.gen_rand(),
kfrag_id=os.urandom(10),
point_noninteractive=Point.gen_rand(),
point_xcoord=Point.gen_rand(),
point_precursor=Point.gen_rand(),
)
with pytest.raises(TypeError):
@ -67,8 +66,7 @@ def test_cannot_attach_cfrag_without_proof():
cfrag = CapsuleFrag(point_e1=Point.gen_rand(),
point_v1=Point.gen_rand(),
kfrag_id=os.urandom(10),
point_noninteractive=Point.gen_rand(),
point_xcoord=Point.gen_rand(),
point_precursor=Point.gen_rand(),
)
key_details = capsule.set_correctness_keys(
UmbralPrivateKey.gen_key().get_pubkey(),

View File

@ -71,7 +71,7 @@ def test_capsule_equality():
activated_capsule = Capsule(params,
point_e_prime=Point.gen_rand(),
point_v_prime=Point.gen_rand(),
point_noninteractive=Point.gen_rand())
point_precursor=Point.gen_rand())
assert activated_capsule != one_capsule

View File

@ -59,7 +59,6 @@ def test_activated_capsule_serialization(prepared_capsule, kfrags, bobs_keys):
capsule.attach_cfrag(cfrag)
capsule._reconstruct_shamirs_secret(receiving_privkey)
rec_capsule_bytes = capsule.to_bytes()
@ -74,7 +73,7 @@ def test_activated_capsule_serialization(prepared_capsule, kfrags, bobs_keys):
assert new_rec_capsule._point_e_prime == capsule._point_e_prime
assert new_rec_capsule._point_v_prime == capsule._point_v_prime
assert new_rec_capsule._point_noninteractive == capsule._point_noninteractive
assert new_rec_capsule._point_precursor == capsule._point_precursor
def test_cannot_create_capsule_from_bogus_material(alices_keys):

View File

@ -37,7 +37,7 @@ def test_cfrag_serialization_with_proof_and_metadata(prepared_capsule, kfrags):
assert new_cfrag._point_e1 == cfrag._point_e1
assert new_cfrag._point_v1 == cfrag._point_v1
assert new_cfrag._kfrag_id == cfrag._kfrag_id
assert new_cfrag._point_noninteractive == cfrag._point_noninteractive
assert new_cfrag._point_precursor == cfrag._point_precursor
new_proof = new_cfrag.proof
assert new_proof is not None
@ -68,7 +68,7 @@ def test_cfrag_serialization_with_proof_but_no_metadata(prepared_capsule, kfrags
assert new_cfrag._point_e1 == cfrag._point_e1
assert new_cfrag._point_v1 == cfrag._point_v1
assert new_cfrag._kfrag_id == cfrag._kfrag_id
assert new_cfrag._point_noninteractive == cfrag._point_noninteractive
assert new_cfrag._point_precursor == cfrag._point_precursor
new_proof = new_cfrag.proof
assert new_proof is not None
@ -95,7 +95,7 @@ def test_cfrag_serialization_no_proof_no_metadata(prepared_capsule, kfrags):
assert new_cfrag._point_e1 == cfrag._point_e1
assert new_cfrag._point_v1 == cfrag._point_v1
assert new_cfrag._kfrag_id == cfrag._kfrag_id
assert new_cfrag._point_noninteractive == cfrag._point_noninteractive
assert new_cfrag._point_precursor == cfrag._point_precursor
new_proof = new_cfrag.proof
assert new_proof is None

View File

@ -30,11 +30,10 @@ def test_kfrag_serialization(alices_keys, bobs_keys, kfrags):
assert len(kfrag_bytes) == KFrag.expected_bytes_length()
new_kfrag = KFrag.from_bytes(kfrag_bytes)
assert new_kfrag._id == kfrag._id
assert new_kfrag.id == kfrag.id
assert new_kfrag._bn_key == kfrag._bn_key
assert new_kfrag._point_noninteractive == kfrag._point_noninteractive
assert new_kfrag._point_precursor == kfrag._point_precursor
assert new_kfrag._point_commitment == kfrag._point_commitment
assert new_kfrag._point_xcoord == kfrag._point_xcoord
assert new_kfrag.verify(signing_pubkey=signing_privkey.get_pubkey(),
delegating_pubkey=delegating_privkey.get_pubkey(),
@ -48,7 +47,7 @@ def test_kfrag_verify_for_capsule(prepared_capsule, kfrags):
assert kfrag.verify_for_capsule(prepared_capsule)
# If we alter some element, the verification fails
previous_id, kfrag._id = kfrag._id, bytes(32)
previous_id, kfrag._id = kfrag.id, bytes(32)
assert not kfrag.verify_for_capsule(prepared_capsule)
# Let's restore the KFrag, and alter the re-encryption key instead

View File

@ -49,12 +49,12 @@ signatures = tuples(integers(min_value=1, max_value=backend._bn_to_int(curve.ord
# # utility
def assert_kfrag_eq(k0, k1):
assert(all([ k0._id == k1._id
, k0._bn_key == k1._bn_key
, k0._point_noninteractive == k1._point_noninteractive
, k0._point_commitment == k1._point_commitment
, k0._point_xcoord == k1._point_xcoord
, k0.signature == k1.signature
assert(all([ k0.id == k1.id
, k0._bn_key == k1._bn_key
, k0._point_precursor == k1._point_precursor
, k0._point_commitment == k1._point_commitment
, k0.signature_for_bob == k1.signature_for_bob
, k0.signature_for_proxy == k1.signature_for_proxy
]))
def assert_cp_eq(c0, c1):
@ -79,10 +79,11 @@ def test_bn_roundtrip(bn):
def test_point_roundtrip(p, c):
assert(p == Point.from_bytes(p.to_bytes(is_compressed=c)))
@given(binary(min_size=bn_size, max_size=bn_size), bns, points, points, points, signatures)
@given(binary(min_size=bn_size, max_size=bn_size), bns, points, points, signatures, signatures)
@settings(max_examples=max_examples, timeout=unlimited)
def test_kfrag_roundtrip(d, b0, p0, p1, p2, sig):
k = KFrag(d, b0, p0, p1, p2, sig)
def test_kfrag_roundtrip(d, b0, p0, p1, sig_proxy, sig_bob):
k = KFrag(identifier=d, bn_key=b0, point_commitment=p0, point_precursor=p1,
signature_for_proxy=sig_proxy, signature_for_bob=sig_bob)
assert_kfrag_eq(k, KFrag.from_bytes(k.to_bytes()))
@given(points, points, bns)

View File

@ -8,7 +8,7 @@ __title__ = "umbral"
__url__ = "https://github.com/nucypher/pyUmbral"
__summary__ = 'Nucypher\'s Umbral Proxy Re-Encryption Implementation',
__summary__ = 'NuCypher\'s Umbral Proxy Re-Encryption Implementation',
__version__ = "0.1.0-alpha.4"

View File

@ -31,7 +31,7 @@ def prove_cfrag_correctness(cfrag: 'CapsuleFrag',
metadata: Optional[bytes] = None
) -> None:
params = capsule._umbral_params
params = capsule.params
# Check correctness of original ciphertext
if not capsule.verify():
@ -63,7 +63,7 @@ def prove_cfrag_correctness(cfrag: 'CapsuleFrag',
z3 = t + h * rk
cfrag.attach_proof(e2, v2, u1, u2, metadata=metadata, z3=z3, kfrag_signature=kfrag.signature)
cfrag.attach_proof(e2, v2, u1, u2, metadata=metadata, z3=z3, kfrag_signature=kfrag.signature_for_bob)
def assess_cfrag_correctness(cfrag: 'CapsuleFrag', capsule: 'Capsule') -> bool:
@ -74,7 +74,7 @@ def assess_cfrag_correctness(cfrag: 'CapsuleFrag', capsule: 'Capsule') -> bool:
signing_pubkey = correctness_keys['verifying']
receiving_pubkey = correctness_keys['receiving']
params = capsule._umbral_params
params = capsule.params
####
# Here are the formulaic constituents shared with `prove_cfrag_correctness`.
@ -104,8 +104,7 @@ def assess_cfrag_correctness(cfrag: 'CapsuleFrag', capsule: 'Capsule') -> bool:
h = CurveBN.hash(*hash_input, params=params)
########
ni = cfrag._point_noninteractive
xcoord = cfrag._point_xcoord
precursor = cfrag._point_precursor
kfrag_id = cfrag._kfrag_id
pubkey_size = UmbralPublicKey.expected_bytes_length(curve=params.curve)
@ -116,7 +115,7 @@ def assess_cfrag_correctness(cfrag: 'CapsuleFrag', capsule: 'Capsule') -> bool:
if receiving_pubkey is None:
receiving_pubkey = b'\x00' * pubkey_size
validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1, ni, xcoord)
validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1, precursor)
kfrag_validity_message = bytes().join(bytes(item) for item in validity_input)
valid_kfrag_signature = cfrag.proof.kfrag_signature.verify(kfrag_validity_message, signing_pubkey)
@ -152,11 +151,10 @@ def verify_kfrag(kfrag: 'KFrag',
u = params.u
kfrag_id = kfrag._id
kfrag_id = kfrag.id
key = kfrag._bn_key
u1 = kfrag._point_commitment
ni = kfrag._point_noninteractive
xcoord = kfrag._point_xcoord
precursor = kfrag._point_precursor
#  We check that the commitment u1 is well-formed
correct_commitment = u1 == key * u
@ -169,9 +167,9 @@ def verify_kfrag(kfrag: 'KFrag',
if receiving_pubkey is None:
receiving_pubkey = b'\x00' * pubkey_size
validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1, ni, xcoord)
validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1, precursor)
kfrag_validity_message = bytes().join(bytes(item) for item in validity_input)
valid_kfrag_signature = kfrag.signature.verify(kfrag_validity_message, signing_pubkey)
valid_kfrag_signature = kfrag.signature_for_proxy.verify(kfrag_validity_message, signing_pubkey)
return correct_commitment & valid_kfrag_signature

View File

@ -34,20 +34,26 @@ from umbral.params import UmbralParameters
class KFrag(object):
def __init__(self,
identifier: bytes,
bn_key: CurveBN,
point_commitment: Point,
point_precursor: Point,
signature_for_proxy: Signature,
signature_for_bob: Signature,
) -> None:
self.id = identifier
self._bn_key = bn_key
self._point_commitment = point_commitment
self._point_precursor = point_precursor
self.signature_for_proxy = signature_for_proxy
self.signature_for_bob = signature_for_bob
class NotValid(ValueError):
"""
raised if the KFrag does not pass verification.
"""
def __init__(self, id: bytes, bn_key: CurveBN, point_noninteractive: Point,
point_commitment: Point, point_xcoord: Point, signature: Signature) -> None:
self._id = id
self._bn_key = bn_key
self._point_noninteractive = point_noninteractive
self._point_commitment = point_commitment
self._point_xcoord = point_xcoord
self.signature = signature
@classmethod
def expected_bytes_length(cls, curve: Optional[EllipticCurve] = None) -> int:
"""
@ -58,7 +64,14 @@ class KFrag(object):
bn_size = CurveBN.expected_bytes_length(curve)
point_size = Point.expected_bytes_length(curve)
return (bn_size * 4) + (point_size * 3)
# self.id --> 1 bn_size
# self._bn_key --> 1 bn_size
# self._point_commitment --> 1 point_size
# self._point_precursor --> 1 point_size
# self.signature_for_proxy --> 2 bn_size
# self.signature_for_bob --> 2 bn_size
return bn_size * 6 + point_size * 2
@classmethod
def from_bytes(cls, data: bytes, curve: Optional[EllipticCurve] = None) -> 'KFrag':
@ -69,15 +82,16 @@ class KFrag(object):
bn_size = CurveBN.expected_bytes_length(curve)
point_size = Point.expected_bytes_length(curve)
signature_size = Signature.expected_bytes_length(curve)
arguments = {'curve': curve}
splitter = BytestringSplitter(
bn_size, # id
(CurveBN, bn_size, arguments), # bn_key
(Point, point_size, arguments), # point_noninteractive
(Point, point_size, arguments), # point_commitment
(Point, point_size, arguments), # point_xcoord
(Signature, Signature.expected_bytes_length(curve), arguments)
(Point, point_size, arguments), # point_precursor
(Signature, signature_size, arguments), # signature_for_proxy
(Signature, signature_size, arguments), # signature_for_bob
)
components = splitter(data)
@ -88,20 +102,20 @@ class KFrag(object):
Serialize the KFrag into a bytestring.
"""
key = self._bn_key.to_bytes()
ni = self._point_noninteractive.to_bytes()
commitment = self._point_commitment.to_bytes()
xcoord = self._point_xcoord.to_bytes()
signature = bytes(self.signature)
precursor = self._point_precursor.to_bytes()
signature_for_proxy = bytes(self.signature_for_proxy)
signature_for_bob = bytes(self.signature_for_bob)
return self._id + key + ni + commitment + xcoord + signature
return self.id + key + commitment + precursor \
+ signature_for_proxy + signature_for_bob
def verify(self,
signing_pubkey: UmbralPublicKey,
delegating_pubkey: UmbralPublicKey = None,
receiving_pubkey: UmbralPublicKey = None,
params: Optional[UmbralParameters] = None,
) -> bool:
) -> bool:
if params is None:
params = default_params()
return verify_kfrag(kfrag=self,
@ -111,10 +125,9 @@ class KFrag(object):
receiving_pubkey=receiving_pubkey)
def verify_for_capsule(self, capsule: 'Capsule') -> bool:
correctness_keys = capsule.get_correctness_keys()
return self.verify(params=capsule._umbral_params,
return self.verify(params=capsule.params,
signing_pubkey=correctness_keys["verifying"],
delegating_pubkey=correctness_keys["delegating"],
receiving_pubkey=correctness_keys["receiving"])
@ -126,10 +139,10 @@ class KFrag(object):
return hmac.compare_digest(bytes(self), bytes(other))
def __hash__(self):
return hash(bytes(self._id))
return hash(bytes(self.id))
def __repr__(self):
return "{}:{}".format(self.__class__.__name__, self._id.hex()[:15])
return "{}:{}".format(self.__class__.__name__, self.id.hex()[:15])
class CorrectnessProof(object):
@ -207,14 +220,12 @@ class CapsuleFrag(object):
point_e1: Point,
point_v1: Point,
kfrag_id: bytes,
point_noninteractive: Point,
point_xcoord: Point,
point_precursor: Point,
proof: Optional[CorrectnessProof] = None) -> None:
self._point_e1 = point_e1
self._point_v1 = point_v1
self._kfrag_id = kfrag_id
self._point_noninteractive = point_noninteractive
self._point_xcoord = point_xcoord
self._point_precursor = point_precursor
self.proof = proof
class NoProofProvided(TypeError):
@ -233,7 +244,7 @@ class CapsuleFrag(object):
bn_size = CurveBN.expected_bytes_length(curve)
point_size = Point.expected_bytes_length(curve)
return (bn_size * 1) + (point_size * 4)
return (bn_size * 1) + (point_size * 3)
@classmethod
def from_bytes(cls, data: bytes, curve: Optional[EllipticCurve] = None) -> 'CapsuleFrag':
@ -250,8 +261,7 @@ class CapsuleFrag(object):
(Point, point_size, arguments), # point_e1
(Point, point_size, arguments), # point_v1
bn_size, # kfrag_id
(Point, point_size, arguments), # point_noninteractive
(Point, point_size, arguments) # point_xcoord
(Point, point_size, arguments), # point_precursor
)
components = splitter(data, return_remainder=True)
@ -265,10 +275,9 @@ class CapsuleFrag(object):
"""
e1 = self._point_e1.to_bytes()
v1 = self._point_v1.to_bytes()
ni = self._point_noninteractive.to_bytes()
xcoord = self._point_xcoord.to_bytes()
precursor = self._point_precursor.to_bytes()
serialized_cfrag = e1 + v1 + self._kfrag_id + ni + xcoord
serialized_cfrag = e1 + v1 + self._kfrag_id + precursor
if self.proof is not None:
serialized_cfrag += self.proof.to_bytes()
@ -278,8 +287,15 @@ class CapsuleFrag(object):
def verify_correctness(self, capsule: 'Capsule') -> bool:
return assess_cfrag_correctness(self, capsule)
def attach_proof(self, e2: Point, v2: Point, u1: Point, u2: Point, z3: CurveBN, kfrag_signature: Signature,
def attach_proof(self,
e2: Point,
v2: Point,
u1: Point,
u2: Point,
z3: CurveBN,
kfrag_signature: Signature,
metadata: Optional[bytes]) -> None:
self.proof = CorrectnessProof(point_e2=e2,
point_v2=v2,
point_kfrag_commitment=u1,
@ -293,4 +309,4 @@ class CapsuleFrag(object):
return self.to_bytes()
def __repr__(self):
return "CFrag:{}".format(self._point_e1.to_bytes().hex()[2:17])
return "CFrag:{}".format(self._point_e1.to_bytes().hex()[2:17])

View File

@ -21,8 +21,6 @@ import os
import typing
from typing import Dict, List, Optional, Tuple, Union
from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve
from bytestring_splitter import BytestringSplitter
@ -56,19 +54,19 @@ class Capsule(object):
bn_sig: Optional[CurveBN] = None,
point_e_prime: Optional[Point] = None,
point_v_prime: Optional[Point] = None,
point_noninteractive: Optional[Point] = None,
point_precursor: Optional[Point] = None,
delegating_pubkey: Optional[UmbralPublicKey] = None,
receiving_pubkey: Optional[UmbralPublicKey] = None,
verifying_pubkey: None = None
) -> None:
self._umbral_params = params # TODO: Change to self.params (#167)
self.params = params
if isinstance(point_e, Point):
if not isinstance(point_v, Point) or not isinstance(bn_sig, CurveBN):
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):
if not isinstance(point_v_prime, Point) or not isinstance(point_precursor, Point):
raise TypeError("Need e_prime, v_prime, and point_noninteractive to make an activated Capsule.")
else:
raise TypeError(
@ -85,7 +83,7 @@ class Capsule(object):
self._point_e_prime = point_e_prime
self._point_v_prime = point_v_prime
self._point_noninteractive = point_noninteractive
self._point_precursor = point_precursor
self._attached_cfrags = list() # type: list
@ -136,7 +134,7 @@ class Capsule(object):
(CurveBN, bn_size, arguments), # bn_sig
(Point, point_size, arguments), # point_e_prime
(Point, point_size, arguments), # point_v_prime
(Point, point_size, arguments) # point_noninteractive
(Point, point_size, arguments) # point_precursor
)
else:
raise ValueError("Byte string does not have a valid length for a Capsule")
@ -153,7 +151,7 @@ class Capsule(object):
if current_key is None:
if key is None:
return False
elif self._umbral_params != key.params:
elif self.params != key.params:
raise TypeError("You are trying to set a key with different UmbralParameters.")
else:
self._cfrag_correctness_keys[key_type] = key
@ -193,11 +191,11 @@ class Capsule(object):
def verify(self) -> bool:
g = self._umbral_params.g
g = self.params.g
e = self._point_e
v = self._point_v
s = self._bn_sig
h = CurveBN.hash(e, v, params=self._umbral_params)
h = CurveBN.hash(e, v, params=self.params)
result = s * g == v + (h * e) # type: bool
return result
@ -213,51 +211,44 @@ class Capsule(object):
return self._point_e, self._point_v, self._bn_sig
def activated_components(self) -> Union[Tuple[None, None, None], Tuple[Point, Point, Point]]:
return self._point_e_prime, self._point_v_prime, self._point_noninteractive
return self._point_e_prime, self._point_v_prime, self._point_precursor
def _reconstruct_shamirs_secret(self, priv_b: UmbralPrivateKey) -> None:
params = self._umbral_params
g = params.g
params = self.params
pub_b = priv_b.get_pubkey()
priv_b = priv_b.bn_key
cfrag_0 = self._attached_cfrags[0]
id_0 = cfrag_0._kfrag_id
ni = cfrag_0._point_noninteractive
xcoord = cfrag_0._point_xcoord
dh_xcoord = priv_b * xcoord
blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
blake2b.update(xcoord.to_bytes())
blake2b.update(pub_b.to_bytes())
blake2b.update(dh_xcoord.to_bytes())
hashed_dh_tuple = blake2b.finalize()
precursor = self._attached_cfrags[0]._point_precursor
dh_point = priv_b * precursor
if len(self._attached_cfrags) > 1:
xs = [CurveBN.hash(cfrag._kfrag_id, hashed_dh_tuple, params=params)
xs = [CurveBN.hash(precursor,
pub_b,
dh_point,
b"X-COORDINATE",
cfrag._kfrag_id,
params=params)
for cfrag in self._attached_cfrags]
x_0 = CurveBN.hash(id_0, hashed_dh_tuple, params=params)
lambda_0 = lambda_coeff(x_0, xs)
e = lambda_0 * cfrag_0._point_e1
v = lambda_0 * cfrag_0._point_v1
for cfrag in self._attached_cfrags[1:]:
if (ni, xcoord) != (cfrag._point_noninteractive, cfrag._point_xcoord):
e_summands = list()
v_summands = list()
for cfrag, x in zip(self._attached_cfrags, xs):
if precursor != cfrag._point_precursor:
raise ValueError("Attached CFrags are not pairwise consistent")
x_i = CurveBN.hash(cfrag._kfrag_id, hashed_dh_tuple, params=params)
lambda_i = lambda_coeff(x_i, xs)
e = e + (lambda_i * cfrag._point_e1)
v = v + (lambda_i * cfrag._point_v1)
else:
e = cfrag_0._point_e1
v = cfrag_0._point_v1
lambda_i = lambda_coeff(x, xs)
e_summands.append(lambda_i * cfrag._point_e1)
v_summands.append(lambda_i * cfrag._point_v1)
self._point_e_prime = e
self._point_v_prime = v
self._point_noninteractive = ni
self._point_e_prime = sum(e_summands[1:], e_summands[0])
self._point_v_prime = sum(v_summands[1:], v_summands[0])
else:
self._point_e_prime = self._attached_cfrags[0]._point_e1
self._point_v_prime = self._attached_cfrags[0]._point_v1
self._point_precursor = precursor
def __bytes__(self) -> bytes:
return self.to_bytes()
@ -308,11 +299,11 @@ def split_rekey(delegating_privkey: UmbralPrivateKey,
sign_receiving_key : Optional[bool] = True,
) -> List[KFrag]:
"""
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.
Creates a re-encryption key from Alice's delegating public key to Bob's
receiving public key, and splits it in KFrags, using Shamir's Secret Sharing.
Requires a threshold number of KFrags out of N.
Returns a list of KFrags.
Returns a dictionary which includes the list of N KFrags
"""
if threshold <= 0 or threshold > N:
@ -328,64 +319,77 @@ def split_rekey(delegating_privkey: UmbralPrivateKey,
delegating_pubkey = delegating_privkey.get_pubkey()
delegating_privkey = delegating_privkey.bn_key
pubkey_b_point = receiving_pubkey.point_key
bob_pubkey_point = receiving_pubkey.point_key
# 'ni' stands for 'Non Interactive'.
# This point is used as an ephemeral public key in a DH key exchange,
# and the resulting shared secret 'd' allows to make Umbral non-interactive
priv_ni = CurveBN.gen_rand(params.curve)
ni = priv_ni * g
d = CurveBN.hash(ni, pubkey_b_point, pubkey_b_point * priv_ni, params=params)
# The precursor point is used as an ephemeral public key in a DH key exchange,
# and the resulting shared secret 'dh_point' is used to derive other secret values
private_precursor = CurveBN.gen_rand(params.curve)
precursor = private_precursor * g
coeffs = [delegating_privkey * (~d)]
coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)]
dh_point = private_precursor * bob_pubkey_point
u = params.u
# Secret value 'd' allows to make Umbral non-interactive
d = CurveBN.hash(precursor,
bob_pubkey_point,
dh_point,
b"NON-INTERACTIVE",
params=params)
# 'xcoord' stands for 'X coordinate'.
# This point is used as an ephemeral public key in a DH key exchange,
# and the resulting shared secret 'dh_xcoord' contributes to prevent
# reconstruction of the re-encryption key without Bob's intervention
priv_xcoord = CurveBN.gen_rand(params.curve)
xcoord = priv_xcoord * g
dh_xcoord = priv_xcoord * pubkey_b_point
blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
blake2b.update(xcoord.to_bytes())
blake2b.update(pubkey_b_point.to_bytes())
blake2b.update(dh_xcoord.to_bytes())
hashed_dh_tuple = blake2b.finalize()
# Coefficients of the generating polynomial
coefficients = [delegating_privkey * (~d)]
coefficients += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)]
bn_size = CurveBN.expected_bytes_length(params.curve)
kfrags = []
kfrags = list()
for _ in range(N):
kfrag_id = os.urandom(bn_size)
share_x = CurveBN.hash(kfrag_id, hashed_dh_tuple, params=params)
# The index of the re-encryption key share (which in Shamir's Secret
# Sharing corresponds to x in the tuple (x, f(x)), with f being the
# generating polynomial), is used to prevent reconstruction of the
# re-encryption key without Bob's intervention
share_index = CurveBN.hash(precursor,
bob_pubkey_point,
dh_point,
b"X-COORDINATE",
kfrag_id,
params=params)
rk = poly_eval(coeffs, share_x)
# The re-encryption key share is the result of evaluating the generating
# polynomial for the index value
rk = poly_eval(coefficients, share_index)
u1 = rk * u
commitment = rk * params.u
validity_message_for_bob = (kfrag_id,
delegating_pubkey,
receiving_pubkey,
commitment,
precursor,
)
validity_message_for_bob = bytes().join(bytes(item) for item in validity_message_for_bob)
signature_for_bob = signer(validity_message_for_bob)
pubkey_size = UmbralPublicKey.expected_bytes_length(curve=params.curve)
if not sign_delegating_key:
delegating_pubkey = b'\x00' * pubkey_size
if not sign_receiving_key:
receiving_pubkey = b'\x00' * pubkey_size
blank_pubkey = b'\x00' * pubkey_size
validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1, ni, xcoord)
validity_message_for_proxy = (kfrag_id,
delegating_pubkey if sign_delegating_key else blank_pubkey,
receiving_pubkey if sign_receiving_key else blank_pubkey,
commitment,
precursor,
)
validity_message_for_proxy = bytes().join(bytes(item) for item in validity_message_for_proxy)
signature_for_proxy = signer(validity_message_for_proxy)
kfrag_validity_message = bytes().join(bytes(item) for item in validity_input)
signature = signer(kfrag_validity_message)
kfrag = KFrag(id=kfrag_id,
kfrag = KFrag(identifier=kfrag_id,
bn_key=rk,
point_noninteractive=ni,
point_commitment=u1,
point_xcoord=xcoord,
signature=signature)
point_commitment=commitment,
point_precursor=precursor,
signature_for_proxy=signature_for_proxy,
signature_for_bob=signature_for_bob,
)
kfrags.append(kfrag)
@ -405,9 +409,8 @@ def reencrypt(kfrag: KFrag, capsule: Capsule, provide_proof: bool = True,
e1 = rk * capsule._point_e
v1 = rk * capsule._point_v
cfrag = CapsuleFrag(point_e1=e1, point_v1=v1, kfrag_id=kfrag._id,
point_noninteractive=kfrag._point_noninteractive,
point_xcoord=kfrag._point_xcoord)
cfrag = CapsuleFrag(point_e1=e1, point_v1=v1, kfrag_id=kfrag.id,
point_precursor=kfrag._point_precursor)
if provide_proof:
prove_cfrag_correctness(cfrag, kfrag, capsule, metadata)
@ -439,51 +442,54 @@ def _encapsulate(alice_pubkey: UmbralPublicKey,
return key, Capsule(point_e=pub_r, point_v=pub_u, bn_sig=s, params=params)
def _decapsulate_original(priv_key: UmbralPrivateKey, capsule: Capsule,
def _decapsulate_original(priv_key: UmbralPrivateKey,
capsule: Capsule,
key_length: int = DEM_KEYSIZE) -> bytes:
"""Derive the same symmetric key"""
priv_key = priv_key.bn_key
shared_key = priv_key * (capsule._point_e + capsule._point_v)
key = kdf(shared_key, key_length)
if not capsule.verify():
# Check correctness of original ciphertext
# (check nº 2) at the end to avoid timing oracles
raise capsule.NotValid("Capsule verification failed.")
shared_key = priv_key.bn_key * (capsule._point_e + capsule._point_v)
key = kdf(shared_key, key_length)
return key
def _decapsulate_reencrypted(receiving_privkey: UmbralPrivateKey, capsule: Capsule,
key_length: int = DEM_KEYSIZE) -> bytes:
"""Derive the same symmetric key"""
params = capsule._umbral_params
"""Derive the same symmetric encapsulated_key"""
params = capsule.params
pub_key = receiving_privkey.get_pubkey().point_key
priv_key = receiving_privkey.bn_key
ni = capsule._point_noninteractive
d = CurveBN.hash(ni, pub_key, priv_key * ni, params=params)
precursor = capsule._point_precursor
e_prime = capsule._point_e_prime
v_prime = capsule._point_v_prime
dh_point = priv_key * precursor
shared_key = d * (e_prime + v_prime)
key = kdf(shared_key, key_length)
# Secret value 'd' allows to make Umbral non-interactive
d = CurveBN.hash(precursor,
pub_key,
dh_point,
b"NON-INTERACTIVE",
params=params)
inv_d = ~d
e = capsule._point_e
v = capsule._point_v
s = capsule._bn_sig
h = CurveBN.hash(e, v, params=params)
inv_d = ~d
e_prime = capsule._point_e_prime
v_prime = capsule._point_v_prime
orig_pub_key = capsule.get_correctness_keys()['delegating'].point_key
if not (s * inv_d) * orig_pub_key == (h * e_prime) + v_prime:
raise GenericUmbralError()
return key
shared_key = d * (e_prime + v_prime)
encapsulated_key = kdf(shared_key, key_length)
return encapsulated_key
def encrypt(alice_pubkey: UmbralPublicKey, plaintext: bytes) -> Tuple[bytes, Capsule]:
@ -553,5 +559,4 @@ def decrypt(ciphertext: bytes, capsule: Capsule, decrypting_key: UmbralPrivateKe
dem = UmbralDEM(encapsulated_key)
cleartext = dem.decrypt(ciphertext, authenticated_data=capsule_bytes)
return cleartext

View File

@ -60,9 +60,9 @@ plain_data = b'peace at dawn'
ciphertext, capsule = pre.encrypt(delegating_key, plain_data)
cfrag = pre.reencrypt(kfrags[0], capsule)
points = [ capsule._point_e, cfrag._point_e1, cfrag.proof._point_e2,
capsule._point_v, cfrag._point_v1, cfrag.proof._point_v2,
capsule._umbral_params.u, cfrag.proof._point_kfrag_commitment, cfrag.proof._point_kfrag_pok ]
points = [capsule._point_e, cfrag._point_e1, cfrag.proof._point_e2,
capsule._point_v, cfrag._point_v1, cfrag.proof._point_v2,
capsule.params.u, cfrag.proof._point_kfrag_commitment, cfrag.proof._point_kfrag_pok]
z = cfrag.proof.bn_sig