From 84883b85da36ad9bba81f52c89acea48d208d68c Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Mon, 19 Apr 2021 18:53:16 -0700 Subject: [PATCH] Enforce CapsuleFrag verification before decryption --- docs/examples/umbral_simple_api.py | 21 +-- docs/notebooks/pyUmbral Simple API.ipynb | 27 ++-- docs/source/api.rst | 3 + docs/source/using_pyumbral.rst | 19 ++- tests/test_capsule.py | 4 +- tests/test_capsule_frag.py | 186 ++++++++++++----------- tests/test_compatibility.py | 22 +-- tests/test_pre.py | 43 +++--- tests/test_vectors.py | 12 +- umbral/__init__.py | 3 +- umbral/capsule_frag.py | 35 ++++- umbral/pre.py | 14 +- vectors/generate_test_vectors.py | 4 +- 13 files changed, 226 insertions(+), 167 deletions(-) diff --git a/docs/examples/umbral_simple_api.py b/docs/examples/umbral_simple_api.py index 55dfdda..bb31b49 100644 --- a/docs/examples/umbral_simple_api.py +++ b/docs/examples/umbral_simple_api.py @@ -1,6 +1,6 @@ import random from umbral import ( - SecretKey, PublicKey, Signer, GenericError, + SecretKey, PublicKey, Signer, GenericError, CapsuleFrag, encrypt, generate_kfrags, reencrypt, decrypt_original, decrypt_reencrypted) # Generate an Umbral key pair @@ -86,15 +86,18 @@ assert len(cfrags) == 10 # Bob checks the capsule fragments # -------------------------------- -# Bob can verify that the capsule fragments are valid and really originate from Alice, +# If Bob received the capsule fragments in serialized form, +# he can verify that they are valid and really originate from Alice, # using Alice's public keys. -assert all(cfrag.verify(capsule, - verifying_pk=alices_verifying_key, - delegating_pk=alices_public_key, - receiving_pk=bobs_public_key, - ) - for cfrag in cfrags) +suspicious_cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags] + +cfrags = [cfrag.verify(capsule, + verifying_pk=alices_verifying_key, + delegating_pk=alices_public_key, + receiving_pk=bobs_public_key, + ) + for cfrag in suspicious_cfrags] # Bob opens the capsule # ------------------------------------ @@ -103,7 +106,7 @@ assert all(cfrag.verify(capsule, bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key, delegating_pk=alices_public_key, capsule=bob_capsule, - cfrags=cfrags, + verified_cfrags=cfrags, ciphertext=ciphertext) print(bob_cleartext) assert bob_cleartext == plaintext diff --git a/docs/notebooks/pyUmbral Simple API.ipynb b/docs/notebooks/pyUmbral Simple API.ipynb index 6c361be..fbd2055 100644 --- a/docs/notebooks/pyUmbral Simple API.ipynb +++ b/docs/notebooks/pyUmbral Simple API.ipynb @@ -56,7 +56,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "b'\\x1c\\xa0\\xa83\\x0cv\\x97\\x02d\\xe9\\xe9\\xc5_\\x9d5NRGRx\\xd4\\xc9\\x17%\\x9b\\xb4\\x05\\xd1\\xc2\\x1e\\x9d\\x0b\\xbf\\xb4g\\xf0n\\xfe\\x9eM\\x93\\xe0\\xbf#l\\xf9\\x033\\xb00\\xf5\\r\\xff\\xc9\\x133C\\xf0\\xa3\\xc0\\xd1e\\xdb~.E$%'\n" + "b'\\xfb\\xc3T\\xb2\\x89=\\x08X\\xb1<\\xd0G/\\xab\\x8c\\xac\\x7f\\xd4)\\xcbB\\xcb^\\x99;P\\x9c\\xbf\\xaaf\\x03\\xdd\\n\\x1f$\\x1b\\xfb\\x88\\xfa\\xcd\\xe2\\x11\\x8d\\xcf\\xe5\\x88\\xaf\\x00\\xfe\\xcb\\x9d\\xf83\\x17\\x9b\\xdd\\xba\\xab\\x8b\\x08\\xbe\\xb1M\\x80\\xf1>> all(cfrag.verify(capsule, - ... verifying_pk=alices_verifying_key, - ... delegating_pk=alices_public_key, - ... receiving_pk=bobs_public_key) - ... for cfrag in cfrags) - True + >>> from umbral import CapsuleFrag + >>> suspicious_cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags] + >>> cfrags = [cfrag.verify(capsule, + ... verifying_pk=alices_verifying_key, + ... delegating_pk=alices_public_key, + ... receiving_pk=bobs_public_key, + ... ) + ... for cfrag in suspicious_cfrags] Bob opens the capsule @@ -196,7 +199,7 @@ Finally, Bob decrypts the re-encrypted ciphertext using his key. >>> cleartext = decrypt_reencrypted(decrypting_sk=bobs_secret_key, ... delegating_pk=alices_public_key, ... capsule=capsule, - ... cfrags=cfrags, + ... verified_cfrags=cfrags, ... ciphertext=ciphertext) diff --git a/tests/test_capsule.py b/tests/test_capsule.py index d485eb6..5ee03e6 100644 --- a/tests/test_capsule.py +++ b/tests/test_capsule.py @@ -77,7 +77,7 @@ def test_open_reencrypted(alices_keys, bobs_keys): threshold=threshold, num_kfrags=num_kfrags) - cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags] + cfrags = [reencrypt(capsule, kfrag).cfrag for kfrag in kfrags] key_back = capsule.open_reencrypted(receiving_sk, delegating_pk, cfrags[:threshold]) assert key_back == key @@ -99,7 +99,7 @@ def test_open_reencrypted(alices_keys, bobs_keys): receiving_pk=receiving_pk, threshold=threshold, num_kfrags=num_kfrags) - cfrags2 = [reencrypt(capsule, kfrag) for kfrag in kfrags2] + cfrags2 = [reencrypt(capsule, kfrag).cfrag for kfrag in kfrags2] with pytest.raises(ValueError, match="CapsuleFrags are not pairwise consistent"): capsule.open_reencrypted(receiving_sk, delegating_pk, [cfrags2[0]] + cfrags[:threshold-1]) diff --git a/tests/test_capsule_frag.py b/tests/test_capsule_frag.py index ed79a0f..e05a484 100644 --- a/tests/test_capsule_frag.py +++ b/tests/test_capsule_frag.py @@ -1,14 +1,12 @@ -from umbral import reencrypt, CapsuleFrag, PublicKey, Capsule +import pytest + +from umbral import reencrypt, CapsuleFrag, PublicKey, Capsule, VerificationError from umbral.curve_point import CurvePoint -def test_cfrag_serialization(alices_keys, bobs_keys, capsule, kfrags): +def test_cfrag_serialization(verification_keys, capsule, kfrags): - delegating_sk, signing_sk = alices_keys - _receiving_sk, receiving_pk = bobs_keys - - verifying_pk = PublicKey.from_secret_key(signing_sk) - delegating_pk = PublicKey.from_secret_key(delegating_sk) + verifying_pk, delegating_pk, receiving_pk = verification_keys metadata = b'This is an example of metadata for re-encryption request' for kfrag in kfrags: @@ -16,62 +14,63 @@ def test_cfrag_serialization(alices_keys, bobs_keys, capsule, kfrags): cfrag_bytes = bytes(cfrag) new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes) - assert new_cfrag == cfrag - assert new_cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - metadata=metadata, - ) + verified_cfrag = new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=metadata, + ) + assert verified_cfrag == cfrag # No metadata - assert not new_cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - ) + with pytest.raises(VerificationError): + new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + ) # Wrong metadata - assert not new_cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - metadata=b'Not the same metadata', - ) + with pytest.raises(VerificationError): + new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=b'Not the same metadata', + ) # Wrong delegating key - assert not new_cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=receiving_pk, - receiving_pk=receiving_pk, - metadata=metadata, - ) + with pytest.raises(VerificationError): + new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=receiving_pk, + receiving_pk=receiving_pk, + metadata=metadata, + ) # Wrong receiving key - assert not new_cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=delegating_pk, - metadata=metadata, - ) + with pytest.raises(VerificationError): + new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=delegating_pk, + metadata=metadata, + ) # Wrong signing key - assert not new_cfrag.verify(capsule, - verifying_pk=receiving_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - metadata=metadata, - ) + with pytest.raises(VerificationError): + new_cfrag.verify(capsule, + verifying_pk=receiving_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=metadata, + ) -def test_cfrag_serialization_no_metadata(alices_keys, bobs_keys, capsule, kfrags): +def test_cfrag_serialization_no_metadata(verification_keys, capsule, kfrags): - delegating_sk, signing_sk = alices_keys - _receiving_sk, receiving_pk = bobs_keys - - verifying_pk = PublicKey.from_secret_key(signing_sk) - delegating_pk = PublicKey.from_secret_key(delegating_sk) + verifying_pk, delegating_pk, receiving_pk = verification_keys for kfrag in kfrags: @@ -80,69 +79,68 @@ def test_cfrag_serialization_no_metadata(alices_keys, bobs_keys, capsule, kfrags cfrag_bytes = bytes(cfrag) new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes) - assert new_cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - ) + verified_cfrag = new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + ) + assert verified_cfrag == cfrag - assert not new_cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - metadata=b'some metadata', - ) + with pytest.raises(VerificationError): + new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=b'some metadata', + ) -def test_cfrag_with_wrong_capsule(alices_keys, bobs_keys, - kfrags, capsule_and_ciphertext, message): +def test_cfrag_with_wrong_capsule(verification_keys, kfrags, capsule_and_ciphertext, message): capsule, ciphertext = capsule_and_ciphertext - - delegating_sk, signing_sk = alices_keys - delegating_pk = PublicKey.from_secret_key(delegating_sk) - - _receiving_sk, receiving_pk = bobs_keys + verifying_pk, delegating_pk, receiving_pk = verification_keys capsule_alice1 = capsule capsule_alice2, _unused_key2 = Capsule.from_public_key(delegating_pk) metadata = b"some metadata" cfrag = reencrypt(capsule_alice2, kfrags[0], metadata=metadata) + cfrag = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify - assert not cfrag.verify(capsule_alice1, - verifying_pk=PublicKey.from_secret_key(signing_sk), - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - metadata=metadata, - ) + with pytest.raises(VerificationError): + cfrag.verify(capsule_alice1, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=metadata, + ) -def test_cfrag_with_wrong_data(kfrags, alices_keys, bobs_keys, capsule_and_ciphertext, message): +def test_cfrag_with_wrong_data(verification_keys, kfrags, capsule_and_ciphertext, message): capsule, ciphertext = capsule_and_ciphertext - - delegating_sk, signing_sk = alices_keys - delegating_pk = PublicKey.from_secret_key(delegating_sk) - - _receiving_sk, receiving_pk = bobs_keys + verifying_pk, delegating_pk, receiving_pk = verification_keys metadata = b"some metadata" cfrag = reencrypt(capsule, kfrags[0], metadata=metadata) # Let's put random garbage in one of the cfrags + cfrag = CapsuleFrag.from_bytes(bytes(cfrag)) # de-verify cfrag.point_e1 = CurvePoint.random() cfrag.point_v1 = CurvePoint.random() - assert not cfrag.verify(capsule, - verifying_pk=PublicKey.from_secret_key(signing_sk), - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - metadata=metadata, - ) + with pytest.raises(VerificationError): + cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=metadata, + ) -def test_cfrag_is_hashable(capsule, kfrags): +def test_cfrag_is_hashable(verification_keys, capsule, kfrags): + + verifying_pk, delegating_pk, receiving_pk = verification_keys cfrag0 = reencrypt(capsule, kfrags[0], metadata=b'abcdef') cfrag1 = reencrypt(capsule, kfrags[1], metadata=b'abcdef') @@ -150,10 +148,22 @@ def test_cfrag_is_hashable(capsule, kfrags): assert hash(cfrag0) != hash(cfrag1) new_cfrag = CapsuleFrag.from_bytes(bytes(cfrag0)) - assert hash(new_cfrag) == hash(cfrag0) + assert hash(new_cfrag) != hash(cfrag0) + + verified_cfrag = new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=b'abcdef') + + assert hash(verified_cfrag) == hash(cfrag0) def test_cfrag_str(capsule, kfrags): cfrag0 = reencrypt(capsule, kfrags[0], metadata=b'abcdef') s = str(cfrag0) - assert 'CapsuleFrag' in s + assert 'VerifiedCapsuleFrag' in s + + s = str(CapsuleFrag.from_bytes(bytes(cfrag0))) + assert "VerifiedCapsuleFrag" not in s + assert "CapsuleFrag" in s diff --git a/tests/test_compatibility.py b/tests/test_compatibility.py index 73aa784..99bd763 100644 --- a/tests/test_compatibility.py +++ b/tests/test_compatibility.py @@ -162,19 +162,19 @@ def _decrypt_reencrypted(umbral, receiving_sk_bytes, delegating_pk_bytes, verify capsule = umbral.Capsule.from_bytes(bytes(capsule_bytes)) cfrags = [umbral.CapsuleFrag.from_bytes(cfrag_bytes) for cfrag_bytes in cfrags_bytes] - assert all(cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - metadata=metadata) - for cfrag in cfrags) + verified_cfrags = [cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=metadata) + for cfrag in cfrags] # Decryption by Bob - plaintext = umbral.decrypt_reencrypted(receiving_sk, - delegating_pk, - capsule, - cfrags, - ciphertext, + plaintext = umbral.decrypt_reencrypted(decrypting_sk=receiving_sk, + delegating_pk=delegating_pk, + capsule=capsule, + verified_cfrags=verified_cfrags, + ciphertext=ciphertext, ) return plaintext diff --git a/tests/test_pre.py b/tests/test_pre.py index 2310699..194b988 100644 --- a/tests/test_pre.py +++ b/tests/test_pre.py @@ -6,6 +6,7 @@ from umbral import ( Signer, GenericError, KeyFrag, + CapsuleFrag, encrypt, generate_kfrags, decrypt_original, @@ -77,28 +78,14 @@ def test_simple_api(num_kfrags, threshold): num_kfrags=num_kfrags) # Bob requests re-encryption to some set of M ursulas - cfrags = list() - for kfrag in kfrags[:threshold]: - - # Re-encryption by an Ursula - cfrag = reencrypt(capsule, kfrag) - - # Bob collects the result - cfrags.append(cfrag) - - # Bob checks that the received cfrags are valid - assert all(cfrag.verify(capsule=capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - ) for cfrag in cfrags) + cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags] # Decryption by Bob - plaintext_reenc = decrypt_reencrypted(receiving_sk, - delegating_pk, - capsule, - cfrags[:threshold], - ciphertext, + plaintext_reenc = decrypt_reencrypted(decrypting_sk=receiving_sk, + delegating_pk=delegating_pk, + capsule=capsule, + verified_cfrags=cfrags[:threshold], + ciphertext=ciphertext, ) assert plaintext_reenc == plaintext @@ -108,3 +95,19 @@ def test_reencrypt_unverified_kfrag(capsule, kfrags): kfrag = KeyFrag.from_bytes(bytes(kfrags[0])) with pytest.raises(TypeError): reencrypt(capsule, kfrag) + + +def test_decrypt_unverified_cfrag(verification_keys, bobs_keys, capsule_and_ciphertext, kfrags): + verifying_pk, delegating_pk, receiving_pk = verification_keys + receiving_sk, _receiving_pk = bobs_keys + capsule, ciphertext = capsule_and_ciphertext + + cfrags = [reencrypt(capsule, kfrag) for kfrag in kfrags] + cfrags[0] = CapsuleFrag.from_bytes(bytes(cfrags[0])) + with pytest.raises(TypeError): + plaintext_reenc = decrypt_reencrypted(decrypting_sk=receiving_sk, + delegating_pk=delegating_pk, + capsule=capsule, + verified_cfrags=cfrags, + ciphertext=ciphertext, + ) diff --git a/tests/test_vectors.py b/tests/test_vectors.py index 8824fa4..9b9c17a 100644 --- a/tests/test_vectors.py +++ b/tests/test_vectors.py @@ -154,13 +154,13 @@ def test_cfrags(): delegating_pk=delegating_pk, receiving_pk=receiving_pk) - new_cfrag = reencrypt(capsule, verified_kfrag, metadata=metadata) + new_cfrag = reencrypt(capsule, verified_kfrag, metadata=metadata).cfrag 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.precursor == cfrag.precursor - assert new_cfrag.verify(capsule, - verifying_pk=verifying_pk, - delegating_pk=delegating_pk, - receiving_pk=receiving_pk, - metadata=metadata) + new_cfrag.verify(capsule, + verifying_pk=verifying_pk, + delegating_pk=delegating_pk, + receiving_pk=receiving_pk, + metadata=metadata) diff --git a/umbral/__init__.py b/umbral/__init__.py index bc3eb2d..b31f6ce 100644 --- a/umbral/__init__.py +++ b/umbral/__init__.py @@ -3,7 +3,7 @@ from .__about__ import ( ) from .capsule import Capsule -from .capsule_frag import CapsuleFrag +from .capsule_frag import CapsuleFrag, VerifiedCapsuleFrag from .errors import GenericError, VerificationError from .key_frag import KeyFrag, VerifiedKeyFrag, generate_kfrags from .keys import SecretKey, PublicKey, SecretKeyFactory @@ -28,6 +28,7 @@ __all__ = [ "KeyFrag", "VerifiedKeyFrag", "CapsuleFrag", + "VerifiedCapsuleFrag", "GenericError", "VerificationError", "encrypt", diff --git a/umbral/capsule_frag.py b/umbral/capsule_frag.py index 1a7e453..42af963 100644 --- a/umbral/capsule_frag.py +++ b/umbral/capsule_frag.py @@ -3,6 +3,7 @@ from typing import Sequence, Optional, Tuple from .capsule import Capsule from .curve_point import CurvePoint from .curve_scalar import CurveScalar +from .errors import VerificationError from .hashing import Hash, hash_to_cfrag_verification, kfrag_signature_message from .keys import PublicKey, SecretKey from .key_frag import KeyFrag, KeyFragID @@ -163,7 +164,7 @@ class CapsuleFrag(Serializable): delegating_pk: PublicKey, receiving_pk: PublicKey, metadata: Optional[bytes] = None, - ) -> bool: + ) -> 'VerifiedCapsuleFrag': """ Verifies the validity of this fragment. @@ -203,7 +204,7 @@ class CapsuleFrag(Serializable): maybe_receiving_pk=receiving_pk) if not self.proof.kfrag_signature.verify(verifying_pk, kfrag_message): - return False + raise VerificationError("Invalid KeyFrag signature") z = self.proof.signature @@ -215,6 +216,30 @@ class CapsuleFrag(Serializable): correct_reencryption_of_v = v * z == v2 + v1 * h correct_rk_commitment = u * z == u2 + u1 * h - return (correct_reencryption_of_e - and correct_reencryption_of_v - and correct_rk_commitment) + if not (correct_reencryption_of_e and correct_reencryption_of_v and correct_rk_commitment): + raise VerificationError("Failed to verify reencryption proof") + + return VerifiedCapsuleFrag(self) + + +class VerifiedCapsuleFrag: + """ + Verified capsule frag, good for decryption. + Can be cast to ``bytes``, but cannot be deserialized from bytes directly. + It can only be obtained from :py:meth:`CapsuleFrag.verify`. + """ + + def __init__(self, cfrag: CapsuleFrag): + self.cfrag = cfrag + + def __bytes__(self): + return bytes(self.cfrag) + + def __eq__(self, other): + return self.cfrag == other.cfrag + + def __hash__(self): + return hash((self.__class__, bytes(self))) + + def __str__(self): + return f"{self.__class__.__name__}:{bytes(self).hex()[:16]}" diff --git a/umbral/pre.py b/umbral/pre.py index 55f6bc7..25f41cb 100644 --- a/umbral/pre.py +++ b/umbral/pre.py @@ -1,7 +1,7 @@ from typing import Tuple, Optional, Sequence from .capsule import Capsule -from .capsule_frag import CapsuleFrag +from .capsule_frag import VerifiedCapsuleFrag, CapsuleFrag from .dem import DEM from .keys import PublicKey, SecretKey from .key_frag import VerifiedKeyFrag, KeyFrag @@ -33,7 +33,7 @@ def decrypt_original(sk: SecretKey, capsule: Capsule, ciphertext: bytes) -> byte def reencrypt(capsule: Capsule, kfrag: VerifiedKeyFrag, metadata: Optional[bytes] = None - ) -> CapsuleFrag: + ) -> VerifiedCapsuleFrag: """ Creates a capsule fragment using the given key fragment. Capsule fragments can later be used to decrypt the ciphertext. @@ -46,19 +46,25 @@ def reencrypt(capsule: Capsule, if isinstance(kfrag, KeyFrag) and not isinstance(kfrag, VerifiedKeyFrag): raise TypeError("KeyFrag must be verified before reencryption") - return CapsuleFrag.reencrypted(capsule, kfrag.kfrag, metadata) + return VerifiedCapsuleFrag(CapsuleFrag.reencrypted(capsule, kfrag.kfrag, metadata)) def decrypt_reencrypted(decrypting_sk: SecretKey, delegating_pk: PublicKey, capsule: Capsule, - cfrags: Sequence[CapsuleFrag], + verified_cfrags: Sequence[VerifiedCapsuleFrag], ciphertext: bytes, ) -> bytes: """ Decrypts the ciphertext using the original capsule and the reencrypted capsule fragments. """ + # We could let duck typing do its work, + # but it's better to make a common error more understandable. + for cfrag in verified_cfrags: + if isinstance(cfrag, CapsuleFrag) and not isinstance(cfrag, VerifiedCapsuleFrag): + raise TypeError("All CapsuleFrags must be verified before decryption") + cfrags = [vcfrag.cfrag for vcfrag in verified_cfrags] key_seed = capsule.open_reencrypted(decrypting_sk, delegating_pk, cfrags) dem = DEM(bytes(key_seed)) return dem.decrypt(ciphertext, authenticated_data=bytes(capsule)) diff --git a/vectors/generate_test_vectors.py b/vectors/generate_test_vectors.py index a81e4c9..9a5e405 100644 --- a/vectors/generate_test_vectors.py +++ b/vectors/generate_test_vectors.py @@ -1,7 +1,7 @@ import json import os -from umbral import SecretKey, PublicKey, Signer, KeyFrag, encrypt, generate_kfrags, reencrypt +from umbral import SecretKey, PublicKey, Signer, KeyFrag, CapsuleFrag, encrypt, generate_kfrags, reencrypt from umbral.curve_scalar import CurveScalar from umbral.curve_point import CurvePoint from umbral.hashing import Hash, unsafe_hash_to_point @@ -59,7 +59,7 @@ plain_data = b'peace at dawn' capsule, ciphertext = encrypt(delegating_pk, plain_data) -cfrag = reencrypt(capsule, kfrags[0]) +cfrag = CapsuleFrag.from_bytes(bytes(reencrypt(capsule, kfrags[0]))) points = [capsule.point_e, cfrag.point_e1, cfrag.proof.point_e2, capsule.point_v, cfrag.point_v1, cfrag.proof.point_v2, cfrag.proof.kfrag_commitment, cfrag.proof.kfrag_pok]