mirror of https://github.com/nucypher/pyUmbral.git
Enforce CapsuleFrag verification before decryption
parent
65d32fd63e
commit
84883b85da
|
@ -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
|
||||
|
|
|
@ -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<S#'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -170,7 +170,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
|
@ -196,7 +196,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
|
@ -219,22 +219,27 @@
|
|||
"metadata": {},
|
||||
"source": [
|
||||
"## Bob checks the capsule fragments\n",
|
||||
"Bob can verify that the capsule fragments are valid and really originate from Alice, using Alice's public keys."
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"assert all(cfrag.verify(capsule,\n",
|
||||
" verifying_pk=alices_verifying_key,\n",
|
||||
" delegating_pk=alices_public_key,\n",
|
||||
" receiving_pk=bobs_public_key)\n",
|
||||
" for cfrag in cfrags)"
|
||||
"from umbral import CapsuleFrag\n",
|
||||
"\n",
|
||||
"suspicious_cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags]\n",
|
||||
"\n",
|
||||
"cfrags = [cfrag.verify(capsule,\n",
|
||||
" verifying_pk=alices_verifying_key,\n",
|
||||
" delegating_pk=alices_public_key,\n",
|
||||
" receiving_pk=bobs_public_key,\n",
|
||||
" )\n",
|
||||
" for cfrag in suspicious_cfrags]"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -264,7 +269,7 @@
|
|||
"bob_cleartext = decrypt_reencrypted(decrypting_sk=bobs_private_key,\n",
|
||||
" delegating_pk=alices_public_key,\n",
|
||||
" capsule=capsule,\n",
|
||||
" cfrags=cfrags,\n",
|
||||
" verified_cfrags=cfrags,\n",
|
||||
" ciphertext=ciphertext)\n",
|
||||
"\n",
|
||||
"print(bob_cleartext)\n",
|
||||
|
|
|
@ -47,6 +47,9 @@ Intermediate objects
|
|||
:special-members: __eq__, __hash__
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: VerifiedCapsuleFrag()
|
||||
:special-members: __eq__, __hash__
|
||||
|
||||
Encryption, re-encryption and decryption
|
||||
----------------------------------------
|
||||
|
||||
|
|
|
@ -173,17 +173,20 @@ Decryption
|
|||
|
||||
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.
|
||||
|
||||
.. doctest:: capsule_story
|
||||
|
||||
>>> 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)
|
||||
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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]}"
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue