mirror of https://github.com/nucypher/pyUmbral.git
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 placespull/220/head
parent
f535f926b9
commit
054777eb53
|
@ -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()
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
223
umbral/pre.py
223
umbral/pre.py
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue