diff --git a/tests/test_capsule/test_capsule_operations.py b/tests/test_capsule/test_capsule_operations.py index 8deb99c7..3c4627ef 100644 --- a/tests/test_capsule/test_capsule_operations.py +++ b/tests/test_capsule/test_capsule_operations.py @@ -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 diff --git a/tests/test_capsule/test_capsule_serializers.py b/tests/test_capsule/test_capsule_serializers.py index 008efe22..8b104d9d 100644 --- a/tests/test_capsule/test_capsule_serializers.py +++ b/tests/test_capsule/test_capsule_serializers.py @@ -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) diff --git a/tests/test_correctness.py b/tests/test_correctness.py index 7e985dbc..1d25bd47 100644 --- a/tests/test_correctness.py +++ b/tests/test_correctness.py @@ -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 - ) - - assert not cfrags[0].verify_correctness(capsule_alice1, - delegating_pubkey, - signing_privkey.get_pubkey(), - pub_key_bob, - ) + sym_key = pre._decapsulate_reencrypted(priv_key_bob, capsule_alice1) + + 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 diff --git a/tests/test_keys/test_key_fragments.py b/tests/test_keys/test_key_fragments.py index f25a6eab..f35481c4 100644 --- a/tests/test_keys/test_key_fragments.py +++ b/tests/test_keys/test_key_fragments.py @@ -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) diff --git a/tests/test_simple_api.py b/tests/test_simple_api.py index f4602daa..daf82bce 100644 --- a/tests/test_simple_api.py +++ b/tests/test_simple_api.py @@ -48,8 +48,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 @@ -164,8 +163,7 @@ def new_keypair_bytes(): cfrag = CapsuleFrag.from_bytes(cfrag_bytes, params.curve) 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 diff --git a/umbral/_pre.py b/umbral/_pre.py index cf3ac9a0..138b5ff4 100644 --- a/umbral/_pre.py +++ b/umbral/_pre.py @@ -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 #### diff --git a/umbral/fragments.py b/umbral/fragments.py index 53f9e09a..8a2ad3a7 100644 --- a/umbral/fragments.py +++ b/umbral/fragments.py @@ -222,19 +222,8 @@ def to_bytes(self): 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, diff --git a/umbral/pre.py b/umbral/pre.py index 444b9481..8ac8cdcb 100644 --- a/umbral/pre.py +++ b/umbral/pre.py @@ -142,6 +142,10 @@ def _set_cfrag_correctness_key(self, key_type, key: UmbralPublicKey): 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, @@ -188,17 +192,12 @@ def original_components(self) -> Tuple[Point, Point, CurveBN]: 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 @@ -365,10 +364,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) @@ -380,7 +380,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) @@ -388,10 +388,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) @@ -403,12 +405,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) @@ -424,6 +428,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() @@ -437,9 +442,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) @@ -451,8 +454,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, @@ -462,33 +463,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. @@ -501,9 +495,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() @@ -511,7 +503,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)