Simplified public API for umbral.pre

* Functions in `pre` now only take Umbral keys as arguments, rather than primitive types (Point, CurveBN)
* Remove unnecessary arguments from public facing and internal methods when they can be extracted from a Capsule, UmbralPublicKey or UmbralPrivateKey
* Adds a getter in Capsule for correctness keys
* Adapts the test suite to new simplified API
pull/159/head
David Núñez 2018-06-04 14:37:32 +02:00
parent d5b434c013
commit 5618b25292
8 changed files with 85 additions and 125 deletions

View File

@ -62,11 +62,11 @@ def test_decapsulation_by_alice(alices_keys):
delegating_privkey, _signing_privkey = alices_keys
sym_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey().point_key, params)
sym_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
assert len(sym_key) == 32
# The symmetric key sym_key is perhaps used for block cipher here in a real-world scenario.
sym_key_2 = pre._decapsulate_original(delegating_privkey.bn_key, capsule)
sym_key_2 = pre._decapsulate_original(delegating_privkey, capsule)
assert sym_key_2 == sym_key
@ -115,8 +115,7 @@ def test_capsule_as_dict_key(alices_keys, bobs_keys):
capsule.attach_cfrag(cfrag)
# Even if we activate the capsule, it still serves as the same key.
cleartext = pre.decrypt(ciphertext, capsule, receiving_privkey,
delegating_pubkey, signing_pubkey)
cleartext = pre.decrypt(ciphertext, capsule, receiving_privkey)
assert some_dict[capsule] == "Thing that Bob wants to try per-Capsule"
assert cleartext == plain_data
@ -132,14 +131,14 @@ def test_capsule_length(alices_keys, bobs_keys):
priv_key_bob, pub_key_bob = bobs_keys
sym_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey().point_key)
sym_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, 10, 15)
capsule.set_correctness_keys(delegating=delegating_privkey.get_pubkey(),
receiving=pub_key_bob,
verifying=signing_privkey.get_pubkey())
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, 10, 15)
for counter, kfrag in enumerate(kfrags):
assert len(capsule) == counter
cfrag = pre.reencrypt(kfrag, capsule)

View File

@ -11,7 +11,7 @@ def test_capsule_serialization(alices_keys):
delegating_privkey, _signing_privkey = alices_keys
params = delegating_privkey.params
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey().point_key, params)
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
capsule_bytes = capsule.to_bytes()
capsule_bytes_casted = bytes(capsule)
assert capsule_bytes == capsule_bytes_casted
@ -44,16 +44,17 @@ def test_activated_capsule_serialization(alices_keys, bobs_keys):
receiving_privkey, receiving_pubkey = bobs_keys
_unused_key, capsule = pre._encapsulate(delegating_pubkey.point_key, params)
capsule.set_correctness_keys(delegating=delegating_pubkey,
receiving=receiving_pubkey,
verifying=signing_privkey.get_pubkey())
_unused_key, capsule = pre._encapsulate(delegating_pubkey)
kfrags = pre.split_rekey(delegating_privkey, signer_alice, receiving_pubkey, 1, 2)
cfrag = pre.reencrypt(kfrags[0], capsule)
capsule.set_correctness_keys(delegating=delegating_pubkey,
receiving=receiving_pubkey,
verifying=signing_privkey.get_pubkey())
capsule.attach_cfrag(cfrag)
capsule._reconstruct_shamirs_secret(receiving_privkey)

View File

@ -17,7 +17,7 @@ def test_correctness_proof_serialization(alices_keys):
params = delegating_privkey.params
_unused_key, capsule = pre._encapsulate(delegating_pubkey.point_key, params)
_unused_key, capsule = pre._encapsulate(delegating_pubkey)
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, 1, 2)
# Example of potential metadata to describe the re-encryption request
@ -53,15 +53,15 @@ def test_cheating_ursula_replays_old_reencryption(N, M, alices_keys):
params = delegating_privkey.params
_unused_key1, capsule_alice1 = pre._encapsulate(delegating_pubkey.point_key, params)
_unused_key2, capsule_alice2 = pre._encapsulate(delegating_pubkey.point_key, params)
_unused_key1, capsule_alice1 = pre._encapsulate(delegating_pubkey)
_unused_key2, capsule_alice2 = pre._encapsulate(delegating_pubkey)
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, M, N)
capsule_alice1.set_correctness_keys(delegating=delegating_pubkey,
receiving=pub_key_bob,
verifying=signing_privkey.get_pubkey())
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, M, N)
cfrags, metadata = [], []
for i, kfrag in enumerate(kfrags[:M]):
@ -76,34 +76,25 @@ def test_cheating_ursula_replays_old_reencryption(N, M, alices_keys):
else:
cfrag = pre.reencrypt(kfrag, capsule_alice1, metadata=metadata_i)
capsule_alice1.attach_cfrag(cfrag)
# Next, we bypass the public method to attach CFrags to the capsule,
# -- called Capsule.append(cfrag) -- and insert it directly in the private
# list of CFrags. In case you were wondering...DON'T DO THIS!
capsule_alice1._attached_cfrags.append(cfrag)
cfrags.append(cfrag)
# Let's activate the capsule
capsule_alice1._reconstruct_shamirs_secret(priv_key_bob)
with pytest.raises(pre.GenericUmbralError):
sym_key = pre._decapsulate_reencrypted(pub_key_bob.point_key,
priv_key_bob.bn_key,
delegating_pubkey.point_key,
capsule_alice1
)
sym_key = pre._decapsulate_reencrypted(priv_key_bob, capsule_alice1)
assert not cfrags[0].verify_correctness(capsule_alice1,
delegating_pubkey,
signing_privkey.get_pubkey(),
pub_key_bob,
)
assert not cfrags[0].verify_correctness(capsule_alice1)
# The response of cheating Ursula is in cfrags[0],
# so the rest of CFrags should be correct:
correct_cases = 0
for cfrag_i, metadata_i in zip(cfrags[1:], metadata[1:]):
if cfrag_i.verify_correctness(capsule_alice1,
delegating_pubkey,
signing_privkey.get_pubkey(),
pub_key_bob,
):
if cfrag_i.verify_correctness(capsule_alice1):
correct_cases += 1
else:
pytest.fail("One of the cfrags that was supposed to be correct wasn't.")
@ -112,11 +103,7 @@ def test_cheating_ursula_replays_old_reencryption(N, M, alices_keys):
# Alternatively, we can try to open the capsule directly.
# We should get an exception with an attached list of incorrect cfrags
with pytest.raises(pre.UmbralCorrectnessError) as exception_info:
_ = pre._open_capsule(capsule_alice1,
priv_key_bob,
delegating_pubkey,
signing_privkey.get_pubkey(),
)
_ = pre._open_capsule(capsule_alice1, priv_key_bob)
correctness_error = exception_info.value
assert cfrags[0] in correctness_error.offending_cfrags
assert len(correctness_error.offending_cfrags) == 1
@ -133,14 +120,14 @@ def test_cheating_ursula_sends_garbage(N, M, alices_keys):
params = delegating_privkey.params
sym_key, capsule_alice = pre._encapsulate(delegating_pubkey.point_key, params)
sym_key, capsule_alice = pre._encapsulate(delegating_pubkey)
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, M, N)
capsule_alice.set_correctness_keys(delegating=delegating_pubkey,
receiving=pub_key_bob,
verifying=signing_privkey.get_pubkey())
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, M, N)
cfrags, metadata = [], []
for i, kfrag in enumerate(kfrags[:M]):
# Example of potential metadata to describe the re-encryption request
@ -159,32 +146,20 @@ def test_cheating_ursula_sends_garbage(N, M, alices_keys):
capsule_alice._reconstruct_shamirs_secret(priv_key_bob) # activate capsule
with pytest.raises(pre.GenericUmbralError):
_unused_key = pre._decapsulate_reencrypted(pub_key_bob.point_key,
priv_key_bob.bn_key,
delegating_pubkey.point_key,
capsule_alice)
_unused_key = pre._decapsulate_reencrypted(priv_key_bob, capsule_alice)
assert not cfrags[0].verify_correctness(capsule_alice,
delegating_pubkey,
signing_privkey.get_pubkey(),
pub_key_bob,
)
assert not cfrags[0].verify_correctness(capsule_alice)
# The response of cheating Ursula is in cfrags[0],
# so the rest of CFrags chould be correct:
for cfrag_i, metadata_i in zip(cfrags[1:], metadata[1:]):
assert cfrag_i.verify_correctness(capsule_alice,
delegating_pubkey,
signing_privkey.get_pubkey(),
pub_key_bob,
)
assert cfrag_i.verify_correctness(capsule_alice)
# Alternatively, we can try to open the capsule directly.
# We should get an exception with an attached list of incorrect cfrags
with pytest.raises(pre.UmbralCorrectnessError) as exception_info:
_decapsulated_key = pre._open_capsule(capsule_alice, priv_key_bob,
delegating_pubkey,
signing_privkey.get_pubkey())
_decapsulated_key = pre._open_capsule(capsule_alice, priv_key_bob)
correctness_error = exception_info.value
assert cfrags[0] in correctness_error.offending_cfrags
assert len(correctness_error.offending_cfrags) == 1
@ -202,12 +177,12 @@ def test_decryption_fails_when_it_expects_a_proof_and_there_isnt(N, M, alices_ke
plain_data = b'peace at dawn'
ciphertext, capsule = pre.encrypt(delegating_privkey.get_pubkey(), plain_data)
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, M, N)
capsule.set_correctness_keys(delegating=delegating_privkey.get_pubkey(),
receiving=pub_key_bob,
verifying=signing_privkey.get_pubkey())
kfrags = pre.split_rekey(delegating_privkey, signer, pub_key_bob, M, N)
for kfrag in kfrags:
for kfrag in kfrags[:M]:
cfrag = pre.reencrypt(kfrag, capsule)
capsule.attach_cfrag(cfrag)
@ -217,9 +192,7 @@ def test_decryption_fails_when_it_expects_a_proof_and_there_isnt(N, M, alices_ke
cfrag.proof = None
with pytest.raises(cfrag.NoProofProvided):
_cleartext = pre.decrypt(ciphertext, capsule, priv_key_bob,
delegating_privkey.get_pubkey(),
signing_privkey.get_pubkey())
_cleartext = pre.decrypt(ciphertext, capsule, priv_key_bob)
@pytest.mark.parametrize("N, M", parameters)
@ -232,7 +205,7 @@ def test_m_of_n(N, M, alices_keys, bobs_keys):
params = delegating_privkey.params
sym_key, capsule = pre._encapsulate(delegating_pubkey.point_key, params)
sym_key, capsule = pre._encapsulate(delegating_pubkey)
capsule.set_correctness_keys(delegating=delegating_privkey.get_pubkey(),
receiving=pub_key_bob,
@ -251,10 +224,7 @@ def test_m_of_n(N, M, alices_keys, bobs_keys):
cfrag = pre.reencrypt(kfrag, capsule, metadata=metadata)
capsule.attach_cfrag(cfrag)
assert cfrag.verify_correctness(capsule, delegating_pubkey,
signing_privkey.get_pubkey(), pub_key_bob,
)
assert cfrag.verify_correctness(capsule)
sym_key_from_capsule = pre._open_capsule(capsule, priv_key_bob, delegating_pubkey,
signing_privkey.get_pubkey())
sym_key_from_capsule = pre._open_capsule(capsule, priv_key_bob)
assert sym_key == sym_key_from_capsule

View File

@ -32,7 +32,7 @@ def test_cfrag_serialization_with_proof_and_metadata(alices_keys, bobs_keys):
_receiving_privkey, receiving_pubkey = bobs_keys
_unused_key, capsule = pre._encapsulate(delegating_pubkey.point_key, delegating_pubkey.params)
_unused_key, capsule = pre._encapsulate(delegating_pubkey)
kfrags = pre.split_rekey(delegating_privkey, signer_alice,
receiving_pubkey, 1, 2)
@ -70,7 +70,7 @@ def test_cfrag_serialization_with_proof_but_no_metadata(alices_keys, bobs_keys):
_receiving_privkey, receiving_pubkey = bobs_keys
signer_alice = Signer(signing_privkey)
_unused_key, capsule = pre._encapsulate(delegating_pubkey.point_key, delegating_pubkey.params)
_unused_key, capsule = pre._encapsulate(delegating_pubkey)
kfrags = pre.split_rekey(delegating_privkey, signer_alice,
receiving_pubkey, 1, 2)
@ -108,7 +108,7 @@ def test_cfrag_serialization_no_proof_no_metadata(alices_keys, bobs_keys):
_receiving_privkey, receiving_pubkey = bobs_keys
signer_alice = Signer(signing_privkey)
_unused_key, capsule = pre._encapsulate(delegating_pubkey.point_key, delegating_pubkey.params)
_unused_key, capsule = pre._encapsulate(delegating_pubkey)
kfrags = pre.split_rekey(delegating_privkey, signer_alice,
receiving_pubkey, 1, 2)

View File

@ -46,8 +46,7 @@ def test_simple_api(N, M, curve=default_curve()):
cfrag = pre.reencrypt(kfrag, capsule)
capsule.attach_cfrag(cfrag)
reenc_cleartext = pre.decrypt(ciphertext, capsule, receiving_privkey,
delegating_pubkey, signing_pubkey)
reenc_cleartext = pre.decrypt(ciphertext, capsule, receiving_privkey)
assert reenc_cleartext == plain_data

View File

@ -46,10 +46,20 @@ def prove_cfrag_correctness(cfrag: "CapsuleFrag",
def assess_cfrag_correctness(cfrag,
capsule: "Capsule",
delegating_point,
signing_pubkey,
receiving_point):
capsule: "Capsule"):
correctness_keys = capsule.get_correctness_keys()
delegating_pubkey = correctness_keys['delegating']
signing_pubkey = correctness_keys['verifying']
receiving_pubkey = correctness_keys['receiving']
if not all((delegating_pubkey, signing_pubkey, receiving_pubkey)):
raise TypeError("Need all three keys to verify correctness.")
delegating_point = delegating_pubkey.point_key
receiving_point = receiving_pubkey.point_key
params = capsule._umbral_params
####

View File

@ -220,19 +220,8 @@ class CapsuleFrag(object):
return serialized_cfrag
def verify_correctness(self,
capsule: "Capsule",
delegating_pubkey: UmbralPublicKey,
signing_pubkey,
receiving_pubkey: UmbralPublicKey):
if not all((delegating_pubkey, signing_pubkey, receiving_pubkey)):
raise TypeError("Need all three keys to verify correctness.")
pubkey_a_point = delegating_pubkey.point_key
pubkey_b_point = receiving_pubkey.point_key
return assess_cfrag_correctness(self, capsule, pubkey_a_point,
signing_pubkey, pubkey_b_point)
def verify_correctness(self, capsule: "Capsule"):
return assess_cfrag_correctness(self, capsule)
def attach_proof(self, e2, v2, u1, u2, z3, kfrag_signature, metadata):
self.proof = CorrectnessProof(point_e2=e2,

View File

@ -139,6 +139,10 @@ class Capsule(object):
else:
raise ValueError("The {} key is already set; you can't set it again.".format(key_type))
def get_correctness_keys(self):
return dict(self._cfrag_correctness_keys)
def set_correctness_keys(self,
delegating: UmbralPublicKey = None,
receiving: UmbralPublicKey = None,
@ -189,17 +193,12 @@ class Capsule(object):
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
def _reconstruct_shamirs_secret(self,
priv_b: Union[UmbralPrivateKey, CurveBN]
) -> None:
def _reconstruct_shamirs_secret(self, priv_b: UmbralPrivateKey) -> None:
params = self._umbral_params
g = params.g
if isinstance(priv_b, UmbralPrivateKey):
pub_b = priv_b.get_pubkey()
priv_b = priv_b.bn_key
else:
pub_b = priv_b * g
pub_b = priv_b.get_pubkey()
priv_b = priv_b.bn_key
cfrag_0 = self._attached_cfrags[0]
id_0 = cfrag_0._kfrag_id
@ -369,10 +368,11 @@ def reencrypt(kfrag: KFrag, capsule: Capsule, provide_proof = True,
return cfrag
def _encapsulate(alice_pubkey: Point, params: UmbralParameters,
def _encapsulate(alice_pubkey: UmbralPublicKey,
key_length = DEM_KEYSIZE) -> Tuple[bytes, Capsule]:
"""Generates a symmetric key and its associated KEM ciphertext"""
params = alice_pubkey.params
g = params.g
priv_r = CurveBN.gen_rand(params.curve)
@ -384,7 +384,7 @@ def _encapsulate(alice_pubkey: Point, params: UmbralParameters,
h = CurveBN.hash(pub_r, pub_u, params=params)
s = priv_u + (priv_r * h)
shared_key = (priv_r + priv_u) * alice_pubkey
shared_key = (priv_r + priv_u) * alice_pubkey.point_key
# Key to be used for symmetric encryption
key = kdf(shared_key, key_length)
@ -392,10 +392,12 @@ def _encapsulate(alice_pubkey: Point, params: UmbralParameters,
return key, Capsule(point_e=pub_r, point_v=pub_u, bn_sig=s, params=params)
def _decapsulate_original(priv_key: CurveBN, capsule: Capsule,
def _decapsulate_original(priv_key: UmbralPrivateKey, capsule: Capsule,
key_length = 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)
@ -407,12 +409,14 @@ def _decapsulate_original(priv_key: CurveBN, capsule: Capsule,
return key
def _decapsulate_reencrypted(pub_key: Point, priv_key: CurveBN,
orig_pub_key: Point, capsule: Capsule,
def _decapsulate_reencrypted(receiving_privkey: UmbralPrivateKey, capsule: Capsule,
key_length = DEM_KEYSIZE) -> bytes:
"""Derive the same symmetric key"""
params = capsule._umbral_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)
@ -428,6 +432,7 @@ def _decapsulate_reencrypted(pub_key: Point, priv_key: CurveBN,
s = capsule._bn_sig
h = CurveBN.hash(e, v, params=params)
inv_d = ~d
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()
@ -441,9 +446,7 @@ def encrypt(alice_pubkey: UmbralPublicKey, plaintext: bytes) -> Tuple[bytes, Cap
Returns the ciphertext and the KEM Capsule.
"""
params = alice_pubkey.params
key, capsule = _encapsulate(alice_pubkey.point_key, params, DEM_KEYSIZE)
key, capsule = _encapsulate(alice_pubkey, DEM_KEYSIZE)
capsule_bytes = bytes(capsule)
@ -455,8 +458,6 @@ def encrypt(alice_pubkey: UmbralPublicKey, plaintext: bytes) -> Tuple[bytes, Cap
def _open_capsule(capsule: Capsule,
receiving_privkey: UmbralPrivateKey,
delegating_pubkey: UmbralPublicKey,
signing_pubkey: UmbralPublicKey,
check_proof=True) -> bytes:
"""
Activates the Capsule from the attached CFrags,
@ -466,33 +467,26 @@ def _open_capsule(capsule: Capsule,
"""
receiving_pubkey = receiving_privkey.get_pubkey()
priv_b = receiving_privkey.bn_key
if check_proof:
offending_cfrags = []
for cfrag in capsule._attached_cfrags:
if not cfrag.verify_correctness(capsule=capsule,
delegating_pubkey=delegating_pubkey,
signing_pubkey=signing_pubkey,
receiving_pubkey=receiving_pubkey):
if not cfrag.verify_correctness(capsule):
offending_cfrags.append(cfrag)
if offending_cfrags:
error_msg = "Decryption error: Some CFrags are not correct"
raise UmbralCorrectnessError(error_msg, offending_cfrags)
capsule._reconstruct_shamirs_secret(priv_b)
capsule._reconstruct_shamirs_secret(receiving_privkey)
key = _decapsulate_reencrypted(receiving_pubkey.point_key, priv_b,
delegating_pubkey.point_key, capsule)
key = _decapsulate_reencrypted(receiving_privkey, capsule)
return key
def decrypt(ciphertext: bytes,
capsule: Capsule,
decrypting_key: UmbralPrivateKey,
delegating_pubkey: UmbralPublicKey = None,
verifying_key = None,
check_proof=True) -> bytes:
"""
Opens the capsule and gets what's inside.
@ -505,9 +499,7 @@ def decrypt(ciphertext: bytes,
# Since there are cfrags attached, we assume this is Bob opening the Capsule.
# (i.e., this is a re-encrypted capsule)
encapsulated_key = _open_capsule(capsule, decrypting_key,
delegating_pubkey, verifying_key,
check_proof=check_proof)
encapsulated_key = _open_capsule(capsule, decrypting_key, check_proof=check_proof)
dem = UmbralDEM(encapsulated_key)
original_capsule_bytes = capsule._original_to_bytes()
@ -515,7 +507,7 @@ def decrypt(ciphertext: bytes,
else:
# Since there aren't cfrags attached, we assume this is Alice opening the Capsule.
# (i.e., this is an original capsule)
decapsulated_key = _decapsulate_original(decrypting_key.bn_key, capsule)
decapsulated_key = _decapsulate_original(decrypting_key, capsule)
dem = UmbralDEM(decapsulated_key)
capsule_bytes = bytes(capsule)