def test_cfrag_serialization_no_proof_no_metadata(alices_keys, bobs_keys): delegating_privkey, signing_privkey = alices_keys delegating_pubkey = delegating_privkey.get_pubkey() _receiving_privkey, receiving_pubkey = bobs_keys signer_alice = Signer(signing_privkey) _unused_key, capsule = pre._encapsulate(delegating_pubkey.point_key) kfrags = pre.split_rekey(delegating_privkey, signer_alice, receiving_pubkey, 1, 2) cfrag = pre.reencrypt(kfrags[0], capsule, provide_proof=False) cfrag_bytes = cfrag.to_bytes() proof = cfrag.proof assert proof is None curve = default_curve() assert len(cfrag_bytes) == CapsuleFrag.expected_bytes_length(curve) new_cfrag = pre.CapsuleFrag.from_bytes(cfrag_bytes) 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._point_noninteractive == cfrag._point_noninteractive new_proof = new_cfrag.proof assert new_proof is None
def test_cannot_attach_cfrag_without_proof(): """ However, even when properly attaching keys, we can't attach the CFrag if it is unproven. """ params = default_params() capsule = Capsule(params, point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) cfrag = CapsuleFrag( point_e1=Point.gen_rand(), point_v1=Point.gen_rand(), kfrag_id=os.urandom(10), point_noninteractive=Point.gen_rand(), point_xcoord=Point.gen_rand(), ) key_details = capsule.set_correctness_keys( UmbralPrivateKey.gen_key().get_pubkey(), UmbralPrivateKey.gen_key().get_pubkey(), UmbralPrivateKey.gen_key().get_pubkey()) delegating_details, receiving_details, verifying_details = key_details assert all((delegating_details, receiving_details, verifying_details)) with pytest.raises(cfrag.NoProofProvided): capsule.attach_cfrag(cfrag)
def reencrypt(kfrag: KFrag, capsule: Capsule, provide_proof: bool = True, metadata: Optional[bytes] = None) -> CapsuleFrag: if not capsule.verify(): raise Capsule.NotValid if not kfrag.verify_for_capsule(capsule): raise KFrag.NotValid rk = kfrag._bn_key e1 = rk * capsule._point_e v1 = rk * capsule._point_v cfrag = CapsuleFrag(point_e1=e1, point_v1=v1, kfrag_id=kfrag._id, point_noninteractive=kfrag._point_noninteractive, point_xcoord=kfrag._point_xcoord) if provide_proof: prove_cfrag_correctness(cfrag, kfrag, capsule, metadata) return cfrag
def reencrypt(kfrag: KFrag, capsule: Capsule, params: UmbralParameters = None, provide_proof=True, metadata: bytes = None) -> CapsuleFrag: if params is None: params = default_params() if not capsule.verify(): raise capsule.NotValid rk = kfrag._bn_key e1 = rk * capsule._point_e v1 = rk * capsule._point_v cfrag = CapsuleFrag(point_e1=e1, point_v1=v1, kfrag_id=kfrag._id, point_noninteractive=kfrag._point_noninteractive, point_xcoord=kfrag._point_xcoord) if provide_proof: prove_cfrag_correctness(cfrag, kfrag, capsule, metadata, params) return cfrag
def test_cfrag_serialization_with_proof_but_no_metadata( prepared_capsule, kfrags): for kfrag in kfrags: cfrag = pre.reencrypt(kfrag, prepared_capsule, provide_proof=True) cfrag_bytes = cfrag.to_bytes() proof = cfrag.proof assert proof is not None assert proof.metadata is None # A CFrag can be represented as the 131 total bytes of three Points (33 each) and a CurveBN (32). # TODO: Figure out final size for CFrags with proofs # assert len(cfrag_bytes) == 33 + 33 + 33 + 32 == 131 new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes) 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._point_precursor == cfrag._point_precursor new_proof = new_cfrag.proof assert new_proof is not None assert new_proof._point_e2 == proof._point_e2 assert new_proof._point_v2 == proof._point_v2 assert new_proof._point_kfrag_commitment == proof._point_kfrag_commitment assert new_proof._point_kfrag_pok == proof._point_kfrag_pok assert new_proof.bn_sig == proof.bn_sig assert new_proof.metadata is None
def test_cfrag_serialization_with_proof_and_metadata(prepared_capsule, kfrags): # Example of potential metadata to describe the re-encryption request metadata = b'This is an example of metadata for re-encryption request' for kfrag in kfrags: cfrag = pre.reencrypt(kfrag, prepared_capsule, provide_proof=True, metadata=metadata) cfrag_bytes = cfrag.to_bytes() proof = cfrag.proof assert proof is not None assert proof.metadata is not None new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes) 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._point_precursor == cfrag._point_precursor new_proof = new_cfrag.proof assert new_proof is not None assert new_proof._point_e2 == proof._point_e2 assert new_proof._point_v2 == proof._point_v2 assert new_proof._point_kfrag_commitment == proof._point_kfrag_commitment assert new_proof._point_kfrag_pok == proof._point_kfrag_pok assert new_proof.bn_sig == proof.bn_sig assert new_proof.metadata == metadata assert new_proof.metadata == proof.metadata
def test_cfrag_serialization_no_proof_no_metadata(prepared_capsule, kfrags): for kfrag in kfrags: cfrag = pre.reencrypt(kfrag, prepared_capsule, provide_proof=False) cfrag_bytes = cfrag.to_bytes() proof = cfrag.proof assert proof is None assert len(cfrag_bytes) == CapsuleFrag.expected_bytes_length() new_cfrag = CapsuleFrag.from_bytes(cfrag_bytes) 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._point_precursor == cfrag._point_precursor new_proof = new_cfrag.proof assert new_proof is None
def reencrypt(kfrag: KFrag, capsule: Capsule, params: UmbralParameters = None) -> CapsuleFrag: params = params if params is not None else default_params() if not capsule.verify(params): raise capsule.NotValid e1 = kfrag.bn_key * capsule._point_eph_e v1 = kfrag.bn_key * capsule._point_eph_v cfrag = CapsuleFrag(e1=e1, v1=v1, id_=kfrag.bn_id, x=kfrag.point_eph_ni) return cfrag
def test_cfrags(): vector_file = os.path.join('vectors', 'vectors_cfrags.json') try: with open(vector_file) as f: vector_suite = json.load(f) except OSError: raise params = default_params() capsule = pre.Capsule.from_bytes(bytes.fromhex(vector_suite['capsule']), params=params) verifying_key = UmbralPublicKey.from_bytes( bytes.fromhex(vector_suite['verifying_key'])) delegating_key = UmbralPublicKey.from_bytes( bytes.fromhex(vector_suite['delegating_key'])) receiving_key = UmbralPublicKey.from_bytes( bytes.fromhex(vector_suite['receiving_key'])) kfrags_n_cfrags = [ (KFrag.from_bytes(bytes.fromhex(json_kfrag['kfrag'])), CapsuleFrag.from_bytes(bytes.fromhex(json_kfrag['cfrag']))) for json_kfrag in vector_suite['vectors'] ] capsule.set_correctness_keys(delegating=delegating_key, receiving=receiving_key, verifying=verifying_key) for kfrag, cfrag in kfrags_n_cfrags: assert kfrag.verify(signing_pubkey=verifying_key, delegating_pubkey=delegating_key, receiving_pubkey=receiving_key), \ 'Invalid KFrag {}'.format(kfrag.to_bytes().hex()) new_cfrag = pre.reencrypt(kfrag, capsule, provide_proof=False) 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._point_noninteractive == cfrag._point_noninteractive assert new_cfrag._point_xcoord == cfrag._point_xcoord assert new_cfrag.proof is None assert cfrag.to_bytes() == new_cfrag.to_bytes()
def test_cannot_attach_cfrag_without_keys(): """ We need the proper keys to verify the correctness of CFrags in order to attach them to a Capsule. """ capsule = Capsule(point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) cfrag = CapsuleFrag( point_e1=Point.gen_rand(), point_v1=Point.gen_rand(), kfrag_id=os.urandom(10), point_noninteractive=Point.gen_rand(), point_xcoord=Point.gen_rand(), ) with pytest.raises(TypeError): capsule.attach_cfrag(cfrag)
def _prove_correctness(cfrag: CapsuleFrag, kfrag: KFrag, capsule: Capsule, metadata: bytes=None, params: UmbralParameters=None ) -> CorrectnessProof: params = params if params is not None else default_params() e1 = cfrag._point_e1 v1 = cfrag._point_v1 e = capsule._point_e v = capsule._point_v u = params.u u1 = kfrag._point_commitment rk = kfrag._bn_key t = CurveBN.gen_rand(params.curve) e2 = t * e v2 = t * v u2 = t * u hash_input = [e, e1, e2, v, v1, v2, u, u1, u2] if metadata is not None: hash_input.append(metadata) h = CurveBN.hash_to_bn(*hash_input, params=params) z3 = t + h * rk cfrag.proof = CorrectnessProof(point_e2=e2, point_v2=v2, point_kfrag_commitment=u1, point_kfrag_pok=u2, bn_kfrag_sig1=kfrag._bn_sig1, bn_kfrag_sig2=kfrag._bn_sig2, bn_sig=z3, metadata=metadata) # Check correctness of original ciphertext (check nº 2) at the end # to avoid timing oracles if not capsule.verify(params): raise capsule.NotValid("Capsule verification failed.")
def reencrypt(kfrag: KFrag, capsule: Capsule, provide_proof: bool = True, metadata: Optional[bytes] = None) -> CapsuleFrag: if not isinstance(capsule, Capsule) or not capsule.verify(): raise Capsule.NotValid elif not isinstance(kfrag, KFrag) or not kfrag.verify_for_capsule(capsule): raise KFrag.NotValid rk = kfrag._bn_key e1 = rk * capsule._point_e # type: Any v1 = rk * capsule._point_v # type: Any cfrag = CapsuleFrag(point_e1=e1, point_v1=v1, kfrag_id=kfrag.id, point_precursor=kfrag._point_precursor) if provide_proof: prove_cfrag_correctness(cfrag, kfrag, capsule, metadata) return cfrag
def test_lifecycle_with_serialization(N, M, signing_mode, curve=default_curve()): """ This test is a variant of test_simple_api, but with intermediate serialization/deserialization steps, modeling how pyUmbral artifacts (such as keys, ciphertexts, etc) will actually be used. These intermediate steps are in between the different 'usage domains' in NuCypher, namely, key generation, delegation, encryption, decryption by Alice, re-encryption by Ursula, and decryption by Bob. Manually injects UmbralParameters for multi-curve testing. """ # Convenience method to avoid replicating key generation code def new_keypair_bytes(): privkey = UmbralPrivateKey.gen_key(params=params) return privkey.to_bytes(), privkey.get_pubkey().to_bytes() ## SETUP params = UmbralParameters(curve=curve) delegating_privkey_bytes, delegating_pubkey_bytes = new_keypair_bytes() signing_privkey_bytes, signing_pubkey_bytes = new_keypair_bytes() receiving_privkey_bytes, receiving_pubkey_bytes = new_keypair_bytes() ## DELEGATION DOMAIN: ## Alice delegates decryption rights to some Bob by generating a set of ## KFrags, using her delegating private key and Bob's receiving public key delegating_privkey = UmbralPrivateKey.from_bytes(delegating_privkey_bytes, params) signing_privkey = UmbralPrivateKey.from_bytes(signing_privkey_bytes, params) receiving_pubkey = UmbralPublicKey.from_bytes(receiving_pubkey_bytes, params) signer = Signer(signing_privkey) sign_delegating_key, sign_receiving_key = signing_mode kfrags = pre.generate_kfrags(delegating_privkey=delegating_privkey, receiving_pubkey=receiving_pubkey, threshold=M, N=N, signer=signer, sign_delegating_key=sign_delegating_key, sign_receiving_key=sign_receiving_key) kfrags_bytes = tuple(map(bytes, kfrags)) del kfrags del signer del delegating_privkey del signing_privkey del receiving_pubkey del params ## ENCRYPTION DOMAIN ## params = UmbralParameters(curve=curve) delegating_pubkey = UmbralPublicKey.from_bytes(delegating_pubkey_bytes, params) plain_data = b'peace at dawn' ciphertext, capsule = pre.encrypt(delegating_pubkey, plain_data) capsule_bytes = bytes(capsule) del capsule del delegating_pubkey del params ## DECRYPTION BY ALICE ## params = UmbralParameters(curve=curve) delegating_privkey = UmbralPrivateKey.from_bytes(delegating_privkey_bytes, params) capsule = pre.Capsule.from_bytes(capsule_bytes, params) cleartext = pre.decrypt(ciphertext, capsule, delegating_privkey) assert cleartext == plain_data del delegating_privkey del capsule del params ## RE-ENCRYPTION DOMAIN (i.e., Ursula's side) cfrags_bytes = list() for kfrag_bytes in kfrags_bytes: params = UmbralParameters(curve=curve) delegating_pubkey = UmbralPublicKey.from_bytes(delegating_pubkey_bytes, params) signing_pubkey = UmbralPublicKey.from_bytes(signing_pubkey_bytes, params) receiving_pubkey = UmbralPublicKey.from_bytes(receiving_pubkey_bytes, params) capsule = pre.Capsule.from_bytes(capsule_bytes, params) capsule.set_correctness_keys(delegating=delegating_pubkey, receiving=receiving_pubkey, verifying=signing_pubkey) # TODO: use params instead of curve? kfrag = KFrag.from_bytes(kfrag_bytes, params.curve) assert kfrag.verify(signing_pubkey, delegating_pubkey, receiving_pubkey, params) cfrag_bytes = bytes(pre.reencrypt(kfrag, capsule)) cfrags_bytes.append(cfrag_bytes) del capsule del kfrag del params del delegating_pubkey del signing_pubkey del receiving_pubkey ## DECRYPTION DOMAIN (i.e., Bob's side) params = UmbralParameters(curve=curve) capsule = pre.Capsule.from_bytes(capsule_bytes, params) delegating_pubkey = UmbralPublicKey.from_bytes(delegating_pubkey_bytes, params) signing_pubkey = UmbralPublicKey.from_bytes(signing_pubkey_bytes, params) receiving_privkey = UmbralPrivateKey.from_bytes(receiving_privkey_bytes, params) receiving_pubkey = receiving_privkey.get_pubkey() capsule.set_correctness_keys(delegating=delegating_pubkey, receiving=receiving_pubkey, verifying=signing_pubkey) for cfrag_bytes in cfrags_bytes: # TODO: use params instead of curve? cfrag = CapsuleFrag.from_bytes(cfrag_bytes, params.curve) capsule.attach_cfrag(cfrag) reenc_cleartext = pre.decrypt(ciphertext, capsule, receiving_privkey) assert reenc_cleartext == plain_data
def test_bob_can_issue_a_work_order_to_a_specific_ursula( enacted_federated_policy, federated_bob, federated_alice, federated_ursulas, capsule_side_channel): """ Now that Bob has his list of Ursulas, he can issue a WorkOrder to one. Upon receiving the WorkOrder, Ursula saves it and responds by re-encrypting and giving Bob a cFrag. This is a multipart test; it shows proper relations between the Characters Ursula and Bob and also proper interchange between a KFrag, Capsule, and CFrag object in the cont ext of REST-driven proxy re-encryption. """ # We pick up our story with Bob already having followed the treasure map above, ie: hrac, treasure_map = enacted_federated_policy.hrac( ), enacted_federated_policy.treasure_map map_id = treasure_map.public_id() federated_bob.treasure_maps[map_id] = treasure_map d = federated_bob.start_learning_loop() federated_bob.follow_treasure_map(map_id=map_id, block=True, timeout=1) assert len(federated_bob.known_nodes) == len(federated_ursulas) the_hrac = enacted_federated_policy.hrac() # Bob has no saved work orders yet, ever. assert len(federated_bob._saved_work_orders) == 0 # We'll test against just a single Ursula - here, we make a WorkOrder for just one. # We can pass any number of capsules as args; here we pass just one. work_orders = federated_bob.generate_work_orders( map_id, capsule_side_channel[0].capsule, num_ursulas=1) # Again: one Ursula, one work_order. assert len(work_orders) == 1 # Bob saved the WorkOrder. assert len(federated_bob._saved_work_orders) == 1 # And the Ursula. assert len(federated_bob._saved_work_orders.ursulas) == 1 ursula_id, work_order = list(work_orders.items())[0] # **** RE-ENCRYPTION HAPPENS HERE! **** cfrags = federated_bob.get_reencrypted_cfrags(work_order) # We only gave one Capsule, so we only got one cFrag. assert len(cfrags) == 1 the_cfrag = cfrags[0] # Attach the CFrag to the Capsule. capsule = capsule_side_channel[0].capsule capsule.set_correctness_keys( delegating=enacted_federated_policy.public_key, receiving=federated_bob.public_keys(EncryptingPower), verifying=federated_alice.stamp.as_umbral_pubkey()) capsule.attach_cfrag(the_cfrag) # Having received the cFrag, Bob also saved the WorkOrder as complete. assert len(federated_bob._saved_work_orders.by_ursula[ursula_id]) == 1 # OK, so cool - Bob has his cFrag! Let's make sure everything went properly. First, we'll show that it is in fact # the correct cFrag (ie, that Ursula performed reencryption properly). for u in federated_ursulas: if u.rest_information()[0].port == work_order.ursula.rest_information( )[0].port: ursula = u break else: raise RuntimeError( "We've lost track of the Ursula that has the WorkOrder. Can't really proceed." ) kfrag_bytes = ursula.datastore.get_policy_arrangement( work_order.arrangement_id.hex().encode()).k_frag the_kfrag = KFrag.from_bytes(kfrag_bytes) the_correct_cfrag = pre.reencrypt(the_kfrag, capsule) # The first CFRAG_LENGTH_WITHOUT_PROOF bytes (ie, the cfrag proper, not the proof material), are the same: assert bytes(the_cfrag)[:CapsuleFrag.expected_bytes_length()] == bytes( the_correct_cfrag)[:CapsuleFrag.expected_bytes_length( )] # It's the correct cfrag! assert the_correct_cfrag.verify_correctness(capsule) # Now we'll show that Ursula saved the correct WorkOrder. work_orders_from_bob = ursula.work_orders(bob=federated_bob) assert len(work_orders_from_bob) == 1 assert work_orders_from_bob[0] == work_order
def attach_cfrag(self, cfrag: CapsuleFrag) -> None: if cfrag.verify_correctness(self): self._attached_cfrags.append(cfrag) else: error_msg = "CFrag is not correct and cannot be attached to the Capsule" raise UmbralCorrectnessError(error_msg, [cfrag])