mirror of https://github.com/nucypher/pyUmbral.git
commit
07e626438d
|
@ -13,7 +13,7 @@ parameters = [
|
|||
]
|
||||
|
||||
|
||||
def test_encrypt_decrypt():
|
||||
def test_encapsulation():
|
||||
pre = umbral.PRE()
|
||||
|
||||
priv_key = pre.gen_priv()
|
||||
|
@ -22,7 +22,7 @@ def test_encrypt_decrypt():
|
|||
sym_key, capsule = pre.encapsulate(pub_key)
|
||||
assert len(sym_key) == 32
|
||||
|
||||
# The symmetric key sym_key should be used for block cipher
|
||||
# The symmetric key sym_key is perhaps used for block cipher here in a real-world scenario.
|
||||
|
||||
sym_key_2 = pre.decapsulate_original(priv_key, capsule)
|
||||
assert sym_key_2 == sym_key
|
||||
|
@ -36,6 +36,7 @@ def test_m_of_n(N, threshold):
|
|||
priv_bob = pre.gen_priv()
|
||||
pub_bob = pre.priv2pub(priv_bob)
|
||||
|
||||
|
||||
sym_key, capsule_alice = pre.encapsulate(pub_alice)
|
||||
|
||||
kfrags, vkeys = pre.split_rekey(priv_alice, pub_bob, threshold, N)
|
||||
|
@ -44,18 +45,20 @@ def test_m_of_n(N, threshold):
|
|||
assert pre.check_kFrag_signature(kfrag, pub_alice)
|
||||
assert pre.check_kFrag_consistency(kfrag, vkeys)
|
||||
|
||||
cFrags = []
|
||||
cfrags = []
|
||||
for kFrag in kfrags[:threshold]:
|
||||
cFrag = pre.reencrypt(kFrag, capsule_alice)
|
||||
ch = pre.challenge(kFrag, capsule_alice, cFrag)
|
||||
assert pre.check_challenge(capsule_alice, cFrag, ch, pub_alice)
|
||||
cFrags.append(cFrag)
|
||||
cfrags.append(cFrag)
|
||||
|
||||
capsule_bob = pre.reconstruct_capsule(cFrags)
|
||||
capsule_bob = pre.reconstruct_capsule(cfrags)
|
||||
|
||||
sym_key_2 = pre.decapsulate_reencrypted(pub_bob, priv_bob, pub_alice, capsule_bob, capsule_alice)
|
||||
|
||||
sym_key_2 = pre.decapsulate_reencrypted(pub_bob, priv_bob, capsule_bob, pub_alice, capsule_alice)
|
||||
assert sym_key_2 == sym_key
|
||||
|
||||
|
||||
@pytest.mark.parametrize("N,threshold", parameters)
|
||||
def test_cheating_Ursula_replays_old_reencryption(N, threshold):
|
||||
pre = umbral.PRE()
|
||||
|
@ -72,31 +75,31 @@ def test_cheating_Ursula_replays_old_reencryption(N, threshold):
|
|||
for kfrag in kfrags:
|
||||
assert pre.check_kFrag_consistency(kfrag, vkeys)
|
||||
|
||||
cFrags = []
|
||||
cfrags = []
|
||||
challenges = []
|
||||
for kFrag in kfrags[:threshold]:
|
||||
cFrag = pre.reencrypt(kFrag, capsule_alice)
|
||||
challenge = pre.challenge(kFrag, capsule_alice, cFrag)
|
||||
|
||||
#assert pre.check_challenge(ekey_alice, cFrag, ch, pub_alice)
|
||||
cFrags.append(cFrag)
|
||||
cfrags.append(cFrag)
|
||||
challenges.append(challenge)
|
||||
|
||||
# Let's put the re-encryption of a different Alice ciphertext
|
||||
cFrags[0] = pre.reencrypt(kfrags[0], other_capsule_alice)
|
||||
cfrags[0] = pre.reencrypt(kfrags[0], other_capsule_alice)
|
||||
|
||||
capsule_bob = pre.reconstruct_capsule(cFrags)
|
||||
capsule_bob = pre.reconstruct_capsule(cfrags)
|
||||
|
||||
try:
|
||||
# This line should always raise an AssertionError ("Generic Umbral Error")
|
||||
sym_key_2 = pre.decapsulate_reencrypted(pub_bob, priv_bob, capsule_bob, pub_alice, capsule_alice)
|
||||
sym_key_2 = pre.decapsulate_reencrypted(pub_bob, priv_bob, pub_alice, capsule_bob, capsule_alice)
|
||||
assert not sym_key_2 == sym_key
|
||||
except AssertionError as e:
|
||||
assert str(e) == "Generic Umbral Error"
|
||||
assert not pre.check_challenge(capsule_alice, cFrags[0], challenges[0], pub_alice)
|
||||
assert not pre.check_challenge(capsule_alice, cfrags[0], challenges[0], pub_alice)
|
||||
# The response of cheating Ursula is in capsules[0],
|
||||
# so the rest of challenges chould be correct:
|
||||
for (cFrag,ch) in zip(cFrags[1:], challenges[1:]):
|
||||
for (cFrag,ch) in zip(cfrags[1:], challenges[1:]):
|
||||
assert pre.check_challenge(capsule_alice, cFrag, ch, pub_alice)
|
||||
|
||||
|
||||
|
@ -115,35 +118,36 @@ def test_cheating_ursula_sends_gargabe(N, threshold):
|
|||
for kfrag in kfrags:
|
||||
assert pre.check_kFrag_consistency(kfrag, vkeys)
|
||||
|
||||
cFrags = []
|
||||
cfrags = []
|
||||
challenges = []
|
||||
for kFrag in kfrags[0:threshold]:
|
||||
cFrag = pre.reencrypt(kFrag, capsule_alice)
|
||||
challenge = pre.challenge(kFrag, capsule_alice, cFrag)
|
||||
|
||||
#assert pre.check_challenge(ekey_alice, cFrag, ch, pub_alice)
|
||||
cFrags.append(cFrag)
|
||||
cfrags.append(cFrag)
|
||||
challenges.append(challenge)
|
||||
|
||||
# Let's put a random garbage in one of the cFrags
|
||||
cFrags[0].e1 = Point.gen_rand(pre.curve)
|
||||
cFrags[0].v1 = Point.gen_rand(pre.curve)
|
||||
cfrags[0].e1 = Point.gen_rand(pre.curve)
|
||||
cfrags[0].v1 = Point.gen_rand(pre.curve)
|
||||
|
||||
|
||||
capsule_bob = pre.reconstruct_capsule(cFrags)
|
||||
capsule_bob = pre.reconstruct_capsule(cfrags)
|
||||
|
||||
try:
|
||||
# This line should always raise an AssertionError ("Generic Umbral Error")
|
||||
sym_key_2 = pre.decapsulate_reencrypted(pub_bob, priv_bob, capsule_bob, pub_alice, capsule_alice)
|
||||
sym_key_2 = pre.decapsulate_reencrypted(pub_bob, priv_bob, pub_alice, capsule_bob, capsule_alice)
|
||||
assert not sym_key_2 == sym_key
|
||||
except AssertionError as e:
|
||||
assert str(e) == "Generic Umbral Error"
|
||||
assert not pre.check_challenge(capsule_alice, cFrags[0], challenges[0], pub_alice)
|
||||
assert not pre.check_challenge(capsule_alice, cfrags[0], challenges[0], pub_alice)
|
||||
# The response of cheating Ursula is in capsules[0],
|
||||
# so the rest of challenges chould be correct:
|
||||
for (cFrag,ch) in zip(cFrags[1:], challenges[1:]):
|
||||
for (cFrag,ch) in zip(cfrags[1:], challenges[1:]):
|
||||
assert pre.check_challenge(capsule_alice, cFrag, ch, pub_alice)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("N,threshold", parameters)
|
||||
def test_alice_sends_fake_kFrag_to_ursula(N, threshold):
|
||||
pre = umbral.PRE()
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
||||
|
||||
from umbral.bignum import BigNum
|
||||
from umbral.point import Point
|
||||
from umbral.utils import poly_eval,lambda_coeff
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
||||
from umbral.utils import poly_eval, lambda_coeff
|
||||
|
||||
|
||||
class KFrag(object):
|
||||
def __init__(self, id_, key, x, u1, z1, z2):
|
||||
|
@ -15,12 +17,14 @@ class KFrag(object):
|
|||
self.bn_sig1 = z1
|
||||
self.bn_sig2 = z2
|
||||
|
||||
|
||||
class Capsule(object):
|
||||
def __init__(self, point_eph_e, point_eph_v, bn_sig):
|
||||
self.point_eph_e = point_eph_e
|
||||
self.point_eph_v = point_eph_v
|
||||
self.bn_sig = bn_sig
|
||||
|
||||
|
||||
class CapsuleFrag(object):
|
||||
def __init__(self, e1, v1, id_, x):
|
||||
self.e1 = e1
|
||||
|
@ -28,12 +32,14 @@ class CapsuleFrag(object):
|
|||
self.bn_kfrag_id = id_
|
||||
self.point_eph_ni = x
|
||||
|
||||
|
||||
class ReconstructedCapsule(object):
|
||||
def __init__(self, e_prime, v_prime, x):
|
||||
self.e_prime = e_prime
|
||||
self.v_prime = v_prime
|
||||
self.point_eph_ni = x
|
||||
|
||||
|
||||
class ChallengeResponse(object):
|
||||
def __init__(self, e2, v2, u1, u2, z1, z2, z3):
|
||||
self.e2 = e2
|
||||
|
@ -44,9 +50,11 @@ class ChallengeResponse(object):
|
|||
self.bn_kfrag_sig2 = z2
|
||||
self.bn_sig = z3
|
||||
|
||||
|
||||
# minVal = (1 << 256) % self.order (i.e., 2^256 % order)
|
||||
MINVAL_SECP256K1_HASH_256 = 432420386565659656852420866394968145599
|
||||
|
||||
|
||||
class PRE(object):
|
||||
def __init__(self):
|
||||
self.backend = default_backend()
|
||||
|
@ -59,14 +67,14 @@ class PRE(object):
|
|||
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
|
||||
for x in list:
|
||||
if isinstance(x, Point):
|
||||
bytes = x.to_bytes()
|
||||
bytes = x.to_bytes()
|
||||
elif isinstance(x, BigNum):
|
||||
bytes = int(x).to_bytes(32, byteorder='big')
|
||||
else:
|
||||
#print(type(x))
|
||||
# print(type(x))
|
||||
bytes = x
|
||||
digest.update(bytes)
|
||||
|
||||
|
||||
i = 0
|
||||
h = 0
|
||||
while h < MINVAL_SECP256K1_HASH_256:
|
||||
|
@ -76,14 +84,13 @@ class PRE(object):
|
|||
h = int.from_bytes(hash, byteorder='big', signed=False)
|
||||
i += 1
|
||||
hash_bn = h % int(self.order)
|
||||
#print()
|
||||
#print("hash_bn: ", hash_bn)
|
||||
#print("order: ", int(self.order))
|
||||
# print()
|
||||
# print("hash_bn: ", hash_bn)
|
||||
# print("order: ", int(self.order))
|
||||
res = BigNum.from_int(hash_bn, self.curve)
|
||||
#print("res: ", int(res))
|
||||
# print("res: ", int(res))
|
||||
return res
|
||||
|
||||
|
||||
def gen_priv(self):
|
||||
return BigNum.gen_rand(self.curve)
|
||||
|
||||
|
@ -123,7 +130,7 @@ class PRE(object):
|
|||
rk = poly_eval(coeffs, id_)
|
||||
|
||||
u1 = u * rk
|
||||
y = BigNum.gen_rand(self.curve)
|
||||
y = BigNum.gen_rand(self.curve)
|
||||
|
||||
z1 = self.hash_to_bn([xcomp, u1, self.g * y, id_])
|
||||
z2 = y - priv_a * z1
|
||||
|
@ -136,7 +143,7 @@ class PRE(object):
|
|||
def check_kFrag_consistency(self, kFrag, vKeys):
|
||||
if vKeys is None or len(vKeys) == 0:
|
||||
raise ValueError('vKeys must not be empty')
|
||||
|
||||
|
||||
# TODO: change this!
|
||||
h = self.g
|
||||
lh_exp = h * kFrag.point_key
|
||||
|
@ -150,7 +157,7 @@ class PRE(object):
|
|||
return lh_exp == rh_exp
|
||||
|
||||
def check_kFrag_signature(self, kFrag, pub_a):
|
||||
|
||||
|
||||
u1 = kFrag.point_commitment
|
||||
z1 = kFrag.bn_sig1
|
||||
z2 = kFrag.bn_sig2
|
||||
|
@ -231,10 +238,9 @@ class PRE(object):
|
|||
|
||||
return check31 & check32 & check33
|
||||
|
||||
|
||||
def encapsulate(self, pub_key, key_length=32):
|
||||
"""Generates a symmetric key and its associated KEM ciphertext"""
|
||||
|
||||
|
||||
priv_r = BigNum.gen_rand(self.curve)
|
||||
pub_r = self.g * priv_r
|
||||
|
||||
|
@ -273,7 +279,7 @@ class PRE(object):
|
|||
|
||||
def reconstruct_capsule(self, cFrags):
|
||||
cFrag_0 = cFrags[0]
|
||||
|
||||
|
||||
if len(cFrags) > 1:
|
||||
ids = [cFrag.bn_kfrag_id for cFrag in cFrags]
|
||||
lambda_0 = lambda_coeff(cFrag_0.bn_kfrag_id, ids)
|
||||
|
@ -285,28 +291,27 @@ class PRE(object):
|
|||
v = v + (cFrag.v1 * lambda_i)
|
||||
|
||||
return ReconstructedCapsule(e_prime=e, v_prime=v, x=cFrag_0.point_eph_ni)
|
||||
|
||||
else: #if len(reencrypted_keys) == 1:
|
||||
return ReconstructedCapsule(e_prime=cFrag_0.e1, v_prime=cFrag_0.v1, x=cFrag_0.point_eph_ni)
|
||||
|
||||
def decapsulate_reencrypted(self, pub_key, priv_key, ctxt_combined, orig_pk, orig_ciphertext, key_length=32):
|
||||
def decapsulate_reencrypted(self, pub_key: Point, priv_key: BigNum, orig_pub_key: Point,
|
||||
recapsule: ReconstructedCapsule, original_capsule: Capsule, key_length=32):
|
||||
"""Derive the same symmetric key"""
|
||||
|
||||
xcomp = ctxt_combined.point_eph_ni
|
||||
xcomp = recapsule.point_eph_ni
|
||||
d = self.hash_to_bn([xcomp, pub_key, xcomp * priv_key])
|
||||
|
||||
e_prime = ctxt_combined.e_prime
|
||||
v_prime = ctxt_combined.v_prime
|
||||
|
||||
e_prime = recapsule.e_prime
|
||||
v_prime = recapsule.v_prime
|
||||
|
||||
shared_key = (e_prime + v_prime) * d
|
||||
key = self.kdf(shared_key, key_length)
|
||||
|
||||
e = orig_ciphertext.point_eph_e
|
||||
v = orig_ciphertext.point_eph_v
|
||||
s = orig_ciphertext.bn_sig
|
||||
e = original_capsule.point_eph_e
|
||||
v = original_capsule.point_eph_v
|
||||
s = original_capsule.bn_sig
|
||||
h = self.hash_to_bn([e, v])
|
||||
inv_d = ~d
|
||||
assert orig_pk * (s * inv_d) == v_prime + (e_prime * h), "Generic Umbral Error"
|
||||
assert orig_pub_key * (s * inv_d) == v_prime + (e_prime * h), "Generic Umbral Error"
|
||||
|
||||
return key
|
||||
|
||||
|
|
Loading…
Reference in New Issue