Esempio n. 1
0
    def reencrypt_via_rest(self, hrac_as_hex, request: http.Request):
        from nkms.policy.models import WorkOrder  # Avoid circular import
        hrac = binascii.unhexlify(hrac_as_hex)
        work_order = WorkOrder.from_rest_payload(hrac, request.body)
        with ThreadedSession(self.db_engine) as session:
            kfrag_bytes = self.datastore.get_policy_arrangement(hrac.hex().encode(),
                                                             session=session).k_frag  # Careful!  :-)
        # TODO: Push this to a lower level.
        kfrag = KFrag.from_bytes(kfrag_bytes)
        cfrag_byte_stream = b""

        for capsule in work_order.capsules:
            # TODO: Sign the result of this.  See #141.
            cfrag_byte_stream += VariableLengthBytestring(pre.reencrypt(kfrag, capsule))

        # TODO: Put this in Ursula's datastore
        self._work_orders.append(work_order)

        headers = {'Content-Type': 'application/octet-stream'}

        return Response(content=cfrag_byte_stream, headers=headers)
Esempio n. 2
0
    def reencrypt_via_rest(self, id_as_hex, request: Request):
        from nucypher.policy.models import WorkOrder  # Avoid circular import
        arrangement_id = binascii.unhexlify(id_as_hex)
        work_order = WorkOrder.from_rest_payload(arrangement_id, request.body)
        self.log.info("Work Order from {}, signed {}".format(
            work_order.bob, work_order.receipt_signature))
        with ThreadedSession(self.db_engine) as session:
            policy_arrangement = self.datastore.get_policy_arrangement(
                arrangement_id=id_as_hex.encode(), session=session)

        kfrag_bytes = policy_arrangement.kfrag  # Careful!  :-)
        verifying_key_bytes = policy_arrangement.alice_pubkey_sig.key_data

        # TODO: Push this to a lower level.
        kfrag = KFrag.from_bytes(kfrag_bytes)
        alices_verifying_key = UmbralPublicKey.from_bytes(verifying_key_bytes)
        cfrag_byte_stream = b""

        for capsule, capsule_signature in zip(work_order.capsules,
                                              work_order.capsule_signatures):
            # This is the capsule signed by Bob
            capsule_signature = bytes(capsule_signature)
            # Ursula signs on top of it. Now both are committed to the same capsule.
            capsule_signed_by_both = bytes(self._stamp(capsule_signature))

            capsule.set_correctness_keys(verifying=alices_verifying_key)
            cfrag = pre.reencrypt(kfrag,
                                  capsule,
                                  metadata=capsule_signed_by_both)
            self.log.info("Re-encrypting for {}, made {}.".format(
                capsule, cfrag))
            signature = self._stamp(bytes(cfrag) + bytes(capsule))
            cfrag_byte_stream += VariableLengthBytestring(cfrag) + signature

        # TODO: Put this in Ursula's datastore
        self._work_order_tracker.append(work_order)

        headers = {'Content-Type': 'application/octet-stream'}

        return Response(content=cfrag_byte_stream, headers=headers)
def test_grant(alice, bob, three_agents):
    # Monkey patch KFrag repr for better debugging.
    KFrag.__repr__ = lambda kfrag: "KFrag: {}".format(bytes(kfrag)[:10].hex())

    policy_end_datetime = maya.now() + datetime.timedelta(days=5)
    n = 3
    label = b"this_is_the_path_to_which_access_is_being_granted"
    _token_agent, _miner_agent, policy_agent = three_agents

    policy_agent.blockchain.wait_for_receipt = MockPolicyCreation.wait_for_receipt
    policy_agent.contract.functions.createPolicy = MockPolicyCreation

    policy = alice.grant(
        bob,
        label,
        m=2,
        n=n,
        expiration=policy_end_datetime,
    )

    # The number of accepted arrangements at least the number of Ursulas we're using (n)
    assert len(policy._accepted_arrangements) >= n

    # The number of actually enacted arrangements is exactly equal to n.
    assert len(policy._enacted_arrangements) == n

    # Let's look at the enacted arrangements.
    for kfrag in policy.kfrags:
        arrangement = policy._enacted_arrangements[kfrag]
        ursula = _ALL_URSULAS[arrangement.ursula.rest_interface.port]

        # Get the Arrangement from Ursula's datastore, looking up by hrac.
        # This will be changed in 180, when we use the Arrangement ID.
        proper_hrac = keccak_digest(
            bytes(alice.stamp) + bytes(bob.stamp) + label)
        retrieved_policy = ursula.datastore.get_policy_arrangement(
            arrangement.id.hex().encode())
        retrieved_kfrag = KFrag.from_bytes(retrieved_policy.k_frag)

        assert kfrag == retrieved_kfrag
Esempio n. 4
0
    def set_policy(self, hrac_as_hex, request: http.Request):
        """
        REST endpoint for setting a kFrag.
        TODO: Instead of taking a Request, use the apistar typing system to type
            a payload and validate / split it.
        TODO: Validate that the kfrag being saved is pursuant to an approved
            Policy (see #121).
        """
        hrac = binascii.unhexlify(hrac_as_hex)
        policy_message_kit = MessageKit.from_bytes(request.body)
        # group_payload_splitter = BytestringSplitter(PublicKey)
        # policy_payload_splitter = BytestringSplitter((KFrag, KFRAG_LENGTH))

        alice = self._alice_class.from_public_keys(
            {SigningPower: policy_message_kit.alice_pubkey})

        verified, cleartext = self.verify_from(alice,
                                               policy_message_kit,
                                               decrypt=True,
                                               signature_is_on_cleartext=True)

        if not verified:
            # TODO: What do we do if the Policy isn't signed properly?
            pass
        #
        # alices_signature, policy_payload =\
        #     BytestringSplitter(Signature)(cleartext, return_remainder=True)

        # TODO: If we're not adding anything else in the payload, stop using the
        # splitter here.
        # kfrag = policy_payload_splitter(policy_payload)[0]
        kfrag = KFrag.from_bytes(cleartext)

        with ThreadedSession(self.db_engine) as session:
            self.datastore.attach_kfrag_to_saved_contract(alice,
                                                          hrac_as_hex,
                                                          kfrag,
                                                          session=session)

        return  # TODO: Return A 200, with whatever policy metadata.
Esempio n. 5
0
def test_kfrags():

    vector_file = os.path.join('vectors', 'vectors_kfrags.json')
    try:
        with open(vector_file) as f:
            vector_suite = json.load(f)
    except OSError:
        raise

    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']))

    for json_kfrag in vector_suite['vectors']:
        kfrag = KFrag.from_bytes(bytes.fromhex(json_kfrag['kfrag']))
        assert kfrag.verify(signing_pubkey=verifying_key,
                            delegating_pubkey=delegating_key,
                            receiving_pubkey=receiving_key), \
            'Invalid KFrag {}'.format(kfrag.to_bytes().hex())
Esempio n. 6
0
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_federated_grant(federated_alice, federated_bob):

    # Monkey patch KFrag repr for better debugging.
    KFrag.__repr__ = lambda kfrag: "KFrag: {}".format(bytes(kfrag)[:10].hex())

    # Setup the policy details
    n = 3
    policy_end_datetime = maya.now() + datetime.timedelta(days=5)
    label = b"this_is_the_path_to_which_access_is_being_granted"

    # Create the Policy, Grating access to Bob
    policy = federated_alice.grant(federated_bob,
                                   label,
                                   m=2,
                                   n=n,
                                   expiration=policy_end_datetime)

    # The number of accepted arrangements at least the number of Ursulas we're using (n)
    assert len(policy._accepted_arrangements) >= n

    # The number of actually enacted arrangements is exactly equal to n.
    assert len(policy._enacted_arrangements) == n

    # Let's look at the enacted arrangements.
    for kfrag in policy.kfrags:
        arrangement = policy._enacted_arrangements[kfrag]

        # Get the Arrangement from Ursula's datastore, looking up by hrac.
        # This will be changed in 180, when we use the Arrangement ID.
        proper_hrac = keccak_digest(
            bytes(federated_alice.stamp) + bytes(federated_bob.stamp) + label)
        retrieved_policy = arrangement.ursula.datastore.get_policy_arrangement(
            arrangement.id.hex().encode())
        retrieved_kfrag = KFrag.from_bytes(retrieved_policy.k_frag)

        assert kfrag == retrieved_kfrag
Esempio n. 8
0
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
Esempio n. 9
0
def test_bob_can_issue_a_work_order_to_a_specific_ursula(
        enacted_policy, alice, bob, 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 context 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_policy.hrac(), enacted_policy.treasure_map
    bob.treasure_maps[hrac] = treasure_map
    bob.follow_treasure_map(hrac)
    assert len(bob.known_nodes) == len(ursulas)

    the_hrac = enacted_policy.hrac()

    # Bob has no saved work orders yet, ever.
    assert len(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 = bob.generate_work_orders(the_hrac,
                                           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(bob._saved_work_orders) == 1
    # And the Ursula.
    assert len(bob._saved_work_orders.ursulas) == 1

    networky_stuff = MockNetworkyStuff(ursulas)
    ursula_dht_key, work_order = list(work_orders.items())[0]

    # In the real world, we'll have a full Ursula node here.  But in this case, we need to fake it.
    work_order.ursula = ursulas[0]

    # **** RE-ENCRYPTION HAPPENS HERE! ****
    cfrags = bob.get_reencrypted_c_frags(networky_stuff, 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_side_channel[0].capsule.attach_cfrag(the_cfrag)

    # Having received the cFrag, Bob also saved the WorkOrder as complete.
    assert len(bob._saved_work_orders) == 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).
    ursula = work_order.ursula
    kfrag_bytes = ursula.datastore.get_policy_arrangement(
        work_order.kfrag_hrac.hex().encode()).k_frag
    the_kfrag = KFrag.from_bytes(kfrag_bytes)
    the_correct_cfrag = pre.reencrypt(the_kfrag,
                                      capsule_side_channel[0].capsule)
    assert bytes(the_cfrag) == bytes(
        the_correct_cfrag)  # It's the correct cfrag!

    # Now we'll show that Ursula saved the correct WorkOrder.
    work_orders_from_bob = ursula.work_orders(bob=bob)
    assert len(work_orders_from_bob) == 1
    assert work_orders_from_bob[0] == work_order
Esempio n. 10
0
def split_rekey(delegating_privkey: UmbralPrivateKey, signer: Signer,
                receiving_pubkey: UmbralPublicKey, threshold: int,
                N: int) -> List[KFrag]:
    """
    Creates a re-encryption key from Alice to Bob and splits it in KFrags,
    using Shamir's Secret Sharing. Requires a threshold number of KFrags
    out of N to guarantee correctness of re-encryption.

    Returns a list of KFrags.
    """

    if threshold <= 0 or threshold > N:
        raise ValueError(
            'Arguments threshold and N must satisfy 0 < threshold <= N')

    if delegating_privkey.params != receiving_pubkey.params:
        raise ValueError("Keys must have the same parameter set.")

    params = delegating_privkey.params

    g = params.g

    pubkey_a_point = delegating_privkey.get_pubkey().point_key
    privkey_a_bn = delegating_privkey.bn_key

    pubkey_b_point = receiving_pubkey.point_key

    # 'ni' stands for 'Non Interactive'.
    # This point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'd' allows to make Umbral non-interactive
    priv_ni = CurveBN.gen_rand(params.curve)
    ni = priv_ni * g
    d = CurveBN.hash(ni,
                     pubkey_b_point,
                     pubkey_b_point * priv_ni,
                     params=params)

    coeffs = [privkey_a_bn * (~d)]
    coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)]

    u = params.u

    # 'xcoord' stands for 'X coordinate'.
    # This point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'dh_xcoord' contributes to prevent
    # reconstruction of the re-encryption key without Bob's intervention
    priv_xcoord = CurveBN.gen_rand(params.curve)
    xcoord = priv_xcoord * g

    dh_xcoord = priv_xcoord * pubkey_b_point

    blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
    blake2b.update(xcoord.to_bytes())
    blake2b.update(pubkey_b_point.to_bytes())
    blake2b.update(dh_xcoord.to_bytes())
    hashed_dh_tuple = blake2b.finalize()

    bn_size = CurveBN.expected_bytes_length(params.curve)

    kfrags = []
    for _ in range(N):
        id = os.urandom(bn_size)

        share_x = CurveBN.hash(id, hashed_dh_tuple, params=params)

        rk = poly_eval(coeffs, share_x)

        u1 = rk * u

        kfrag_validity_message = bytes().join(
            bytes(material) for material in (id, pubkey_a_point,
                                             pubkey_b_point, u1, ni, xcoord))
        signature = signer(kfrag_validity_message)

        kfrag = KFrag(id=id,
                      bn_key=rk,
                      point_noninteractive=ni,
                      point_commitment=u1,
                      point_xcoord=xcoord,
                      signature=signature)

        kfrags.append(kfrag)

    return kfrags
Esempio n. 11
0
def split_rekey(privkey_a_bn: Union[UmbralPrivateKey, CurveBN],
                signer_a: Signer,
                pubkey_b_point: Union[UmbralPublicKey, Point],
                threshold: int,
                N: int,
                params: UmbralParameters = None) -> List[KFrag]:
    """
    Creates a re-encryption key from Alice to Bob and splits it in KFrags,
    using Shamir's Secret Sharing. Requires a threshold number of KFrags
    out of N to guarantee correctness of re-encryption.

    Returns a list of KFrags.
    """
    params = params if params is not None else default_params()

    g = params.g

    if isinstance(privkey_a_bn, UmbralPrivateKey):
        pubkey_a_point = privkey_a_bn.get_pubkey().point_key
        privkey_a_bn = privkey_a_bn.bn_key
    else:
        pubkey_a_point = privkey_a_bn * g

    if isinstance(pubkey_b_point, UmbralPublicKey):
        pubkey_b_point = pubkey_b_point.point_key

    # 'ni' stands for 'Non Interactive'.
    # This point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'd' allows to make Umbral non-interactive
    priv_ni = CurveBN.gen_rand(params.curve)
    ni = priv_ni * g
    d = CurveBN.hash(ni,
                     pubkey_b_point,
                     pubkey_b_point * priv_ni,
                     params=params)

    coeffs = [privkey_a_bn * (~d)]
    coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)]

    u = params.u

    # 'xcoord' stands for 'X coordinate'.
    # This point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'dh_xcoord' contributes to prevent
    # reconstruction of the re-encryption key without Bob's intervention
    priv_xcoord = CurveBN.gen_rand(params.curve)
    xcoord = priv_xcoord * g

    dh_xcoord = priv_xcoord * pubkey_b_point

    blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
    blake2b.update(xcoord.to_bytes())
    blake2b.update(pubkey_b_point.to_bytes())
    blake2b.update(dh_xcoord.to_bytes())
    hashed_dh_tuple = blake2b.finalize()

    bn_size = CurveBN.expected_bytes_length(params.curve)

    kfrags = []
    for _ in range(N):
        id = os.urandom(bn_size)

        share_x = CurveBN.hash(id, hashed_dh_tuple, params=params)

        rk = poly_eval(coeffs, share_x)

        u1 = rk * u

        kfrag_validity_message = bytes().join(
            bytes(material) for material in (id, pubkey_a_point,
                                             pubkey_b_point, u1, ni, xcoord))
        signature = signer_a(kfrag_validity_message)

        kfrag = KFrag(id=id,
                      bn_key=rk,
                      point_noninteractive=ni,
                      point_commitment=u1,
                      point_xcoord=xcoord,
                      signature=signature)
        kfrags.append(kfrag)

    return kfrags
Esempio n. 12
0
class KeyStore(object):
    """
    A storage class of cryptographic keys.
    """
    kfrag_splitter = BytestringSplitter(Signature,
                                        (KFrag, KFrag.expected_bytes_length()))

    def __init__(self, sqlalchemy_engine=None):
        """
        Initalizes a KeyStore object.

        :param sqlalchemy_engine: SQLAlchemy engine object to create session
        """
        self.engine = sqlalchemy_engine
        Session = sessionmaker(bind=sqlalchemy_engine)

        # This will probably be on the reactor thread for most production configs.
        # Best to treat like hot lava.
        self._session_on_init_thread = Session()

    def add_key(self, key, is_signing=True, session=None) -> Key:
        """
        :param key: Keypair object to store in the keystore.

        :return: The newly added key object.
        """
        session = session or self._session_on_init_thread
        fingerprint = fingerprint_from_key(key)
        key_data = bytes(key)
        new_key = Key(fingerprint, key_data, is_signing)

        session.add(new_key)
        session.commit()

        return new_key

    def get_key(
        self,
        fingerprint: bytes,
        session=None
    ) -> Union[keypairs.EncryptingKeypair, keypairs.SigningKeypair]:
        """
        Returns a key from the KeyStore.

        :param fingerprint: Fingerprint, in bytes, of key to return

        :return: Keypair of the returned key.
        """
        session = session or self._session_on_init_thread

        key = session.query(Key).filter_by(fingerprint=fingerprint).first()

        if not key:
            raise NotFound(
                "No key with fingerprint {} found.".format(fingerprint))

        pubkey = UmbralPublicKey.from_bytes(key.key_data)
        return pubkey

    def del_key(self, fingerprint: bytes, session=None):
        """
        Deletes a key from the KeyStore.

        :param fingerprint: Fingerprint of key to delete
        """
        session = session or self._session_on_init_thread

        session.query(Key).filter_by(fingerprint=fingerprint).delete()
        session.commit()

    def add_policy_arrangement(
            self,
            expiration,
            deposit,
            hrac,
            kfrag=None,
            alice_pubkey_sig=None,  # alice_pubkey_enc,
            alice_signature=None,
            session=None) -> PolicyArrangement:
        """
        Creates a PolicyArrangement to the Keystore.

        :return: The newly added PolicyArrangement object
        """
        session = session or self._session_on_init_thread

        alice_key_instance = session.query(Key).filter_by(
            key_data=bytes(alice_pubkey_sig)).first()
        if not alice_key_instance:
            alice_key_instance = Key.from_umbral_key(alice_pubkey_sig,
                                                     is_signing=True)
        # alice_pubkey_enc = self.add_key(alice_pubkey_enc)
        # bob_pubkey_sig = self.add_key(bob_pubkey_sig)

        new_policy_arrangement = PolicyArrangement(
            expiration,
            deposit,
            hrac,
            kfrag,
            alice_pubkey_sig=alice_key_instance,
            alice_signature=None,  # bob_pubkey_sig.id
        )

        session.add(new_policy_arrangement)
        session.commit()

        return new_policy_arrangement

    def get_policy_arrangement(self,
                               hrac: bytes,
                               session=None) -> PolicyArrangement:
        """
        Returns the PolicyArrangement by its HRAC.

        :return: The PolicyArrangement object
        """
        session = session or self._session_on_init_thread

        policy_arrangement = session.query(PolicyArrangement).filter_by(
            hrac=hrac).first()

        if not policy_arrangement:
            raise NotFound(
                "No PolicyArrangement with {} HRAC found.".format(hrac))
        return policy_arrangement

    def del_policy_arrangement(self, hrac: bytes, session=None):
        """
        Deletes a PolicyArrangement from the Keystore.
        """
        session = session or self._session_on_init_thread

        session.query(PolicyArrangement).filter_by(hrac=hrac).delete()
        session.commit()

    def attach_kfrag_to_saved_arrangement(self,
                                          alice,
                                          hrac_as_hex,
                                          kfrag,
                                          session=None):
        session = session or self._session_on_init_thread

        policy_arrangement = session.query(PolicyArrangement).filter_by(
            hrac=hrac_as_hex.encode()).first()

        if policy_arrangement is None:
            raise NotFound(
                "Can't attach a kfrag to non-existent Arrangement with hrac {}"
                .format(hrac_as_hex))

        if policy_arrangement.alice_pubkey_sig.key_data != alice.stamp:
            raise alice.SuspiciousActivity

        policy_arrangement.k_frag = bytes(kfrag)
        session.commit()

    def add_workorder(self,
                      bob_pubkey_sig,
                      bob_signature,
                      hrac,
                      session=None) -> Workorder:
        """
        Adds a Workorder to the keystore.
        """
        session = session or self._session_on_init_thread
        bob_pubkey_sig = self.add_key(bob_pubkey_sig)
        new_workorder = Workorder(bob_pubkey_sig.id, bob_signature, hrac)

        session.add(new_workorder)
        session.commit()

        return new_workorder

    def get_workorders(self, hrac: bytes, session=None) -> Workorder:
        """
        Returns a list of Workorders by HRAC.
        """
        session = session or self._session_on_init_thread

        workorders = session.query(Workorder).filter_by(hrac=hrac)

        if not workorders:
            raise NotFound("No Workorders with {} HRAC found.".format(hrac))
        return workorders

    def del_workorders(self, hrac: bytes, session=None):
        """
        Deletes a Workorder from the Keystore.
        """
        session = session or self._session_on_init_thread

        workorders = session.query(Workorder).filter_by(hrac=hrac)
        deleted = workorders.delete()
        session.commit()

        return deleted
Esempio n. 13
0
    def test_recepient_add_records_set(self):
        #Generate keys
        config.set_default_curve(SECP256K1)

        patient_private_key = keys.UmbralPrivateKey.gen_key()
        patient_public_key = patient_private_key.get_pubkey()
        recepient_private_key = keys.UmbralPrivateKey.gen_key()
        recepient_public_key = recepient_private_key.get_pubkey()

        recepient_signing_key = keys.UmbralPrivateKey.gen_key()
        recepient_verifying_key = recepient_signing_key.get_pubkey()
        recepient_signer = signing.Signer(private_key=recepient_signing_key)

        #Convert keys to hex
        hexed_patient_public_key = patient_public_key.to_bytes().hex()
        hexed_recepient_public_key = recepient_public_key.to_bytes().hex()
        hexed_recepient_verifying_key = recepient_verifying_key.to_bytes().hex()

        #Signup patient
        data = {
            'first_name': 'Gordo',
            'last_name': 'Freema',
            'email': '*****@*****.**',
            'password': '******',
            'eth_address': '0x73015966604928A312F79F7E69291a656Cb88603',
            'pub_key': hexed_patient_public_key
        }

        response = self.client.post('/patients/signup/', data)

        #Signup recepient
        data = {
            'organisation_id': 'someseriousorganisationidentifier',
            'first_name': 'Gordon',
            'last_name': 'Freeman',
            'email': '*****@*****.**',
            'password': '******',
            'eth_address': '0x73015966604928A312F79F7E69291a656Cb88603',
            'pub_key': hexed_recepient_public_key
        }

        response = self.client.post('/recepients/signup/', data)

        #Log in as patient
        self.client.login(username='******', password='******')

        data = {
            'patient_id': 2,
            'recepient_id': 2
        }

        #Delegate add records permission to recepient
        response = self.client.post('/patients/delegations/make/', data)

        #Log in as recepient
        self.client.login(username='******', password='******')

        #Encrypt message
        plaintext = b'Proxy Re-encryption is cool!'
        ciphertext, capsule = pre.encrypt(recepient_public_key, plaintext)
        kfrags = pre.generate_kfrags(delegating_privkey=recepient_private_key,
                                     signer=recepient_signer,
                                     receiving_pubkey=patient_public_key,
                                     threshold=10,
                                     N=20)
        #Add encrypted record
        data = {
            'patient_id': 2,
            'type': 'type',
            'data': ciphertext.hex(),
            'capsule': capsule.to_bytes().hex()
        }

        response = self.client.post('/records/add/', data)

        self.assertEqual(response.status_code, 201)

        #Add kfrags 
        for kfrag in kfrags:
            data = {
                'delegation_id': 1,
                'bytes': kfrag.to_bytes().hex()
            }
            self.client.post('/patients/kfrags/', data)
        
        #Add verifying_key
        data = {
            'records_set_id': 1,
            'recepient_id': 3,
            'verifying_key': hexed_recepient_verifying_key
        }

        response = self.client.post('/re_encryptions/', data)
        
        #Login as patient
        self.client.login(username='******', password='******')

        response = self.client.get('/me/records/')

        from_proxy_capsule = bytes.fromhex(response.data[0]['capsule'])
        from_proxy_data = bytes.fromhex(response.data[0]['data'])

        umbral_parameteres = UmbralParameters(SECP256K1)

        from_proxy_capsule = pre.Capsule.from_bytes(
            from_proxy_capsule,
            umbral_parameteres
        )

        response = self.client.get('/patients/kfrags/')
        """
        Patient can receive kfrags for each re_encryption
        """

        from_proxy_kfrags = [KFrag.from_bytes(bytes.fromhex(kfrag['bytes'])) for kfrag in response.data]

        response = self.client.get('/re_encryptions/1/')
        from_proxy_verifying_key = bytes.fromhex(response.data['verifying_key'])

        """
        Patient gets verifying_key
        """

        from_proxy_verifying_key = keys.UmbralPublicKey.from_bytes(
            from_proxy_verifying_key,
            params=umbral_parameteres
        )

        from_proxy_capsule.set_correctness_keys(
            delegating=recepient_public_key,
            receiving=patient_public_key,
            verifying=from_proxy_verifying_key
        )


        for kfrag in from_proxy_kfrags:
            cfrag = pre.reencrypt(kfrag, from_proxy_capsule)
            from_proxy_capsule.attach_cfrag(cfrag)

        cleartext = pre.decrypt(
            ciphertext=from_proxy_data,
            capsule=from_proxy_capsule,
            decrypting_key=patient_private_key
        )

        """
        Patient opens the capsule to decrypt records
        organisation added. Then patient can reencrypt whole
        records to delegate editing and reading permissions
        to other organisations
        """

        self.assertEqual(cleartext, plaintext)
Esempio n. 14
0
def split_rekey(priv_a: Union[UmbralPrivateKey, BigNum],
                pub_b: Union[UmbralPublicKey, Point],
                threshold: int,
                N: int,
                params: UmbralParameters = None) -> List[KFrag]:
    """
    Creates a re-encryption key from Alice to Bob and splits it in KFrags,
    using Shamir's Secret Sharing. Requires a threshold number of KFrags 
    out of N to guarantee correctness of re-encryption.

    Returns a list of KFrags.
    """
    params = params if params is not None else default_params()

    if isinstance(priv_a, UmbralPrivateKey):
        priv_a = priv_a.bn_key

    if isinstance(pub_b, UmbralPublicKey):
        pub_b = pub_b.point_key

    g = params.g
    pub_a = priv_a * g

    x = BigNum.gen_rand(params.curve)
    xcomp = x * g
    d = BigNum.hash_to_bn(xcomp, pub_b, pub_b * x, params=params)

    coeffs = [priv_a * (~d)]
    coeffs += [BigNum.gen_rand(params.curve) for _ in range(threshold - 1)]

    u = params.u

    g_ab = priv_a * pub_b

    blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
    blake2b.update(pub_a.to_bytes())
    blake2b.update(pub_b.to_bytes())
    blake2b.update(g_ab.to_bytes())
    hashed_dh_tuple = blake2b.finalize()

    kfrags = []
    for _ in range(N):
        id_kfrag = BigNum.gen_rand(params.curve)

        share_x = BigNum.hash_to_bn(id_kfrag, hashed_dh_tuple, params=params)

        rk = poly_eval(coeffs, share_x)

        u1 = rk * u
        y = BigNum.gen_rand(params.curve)

        z1 = BigNum.hash_to_bn(y * g,
                               id_kfrag,
                               pub_a,
                               pub_b,
                               u1,
                               xcomp,
                               params=params)
        z2 = y - priv_a * z1

        kfrag = KFrag(id_=id_kfrag, key=rk, x=xcomp, u1=u1, z1=z1, z2=z2)
        kfrags.append(kfrag)

    return kfrags
Esempio n. 15
0
def test_kfrag_roundtrip(d, b0, p0, p1, p2, sig):
    k = KFrag(d, b0, p0, p1, p2, sig)
    assert_kfrag_eq(k, KFrag.from_bytes(k.to_bytes()))
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
Esempio n. 17
0
def test_bob_can_issue_a_work_order_to_a_specific_ursula(
        enacted_policy, bob, alice, 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 context 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_policy.hrac(), enacted_policy.treasure_map
    bob.treasure_maps[hrac] = treasure_map
    bob.follow_treasure_map(hrac)
    assert len(bob.known_nodes) == len(ursulas)

    the_hrac = enacted_policy.hrac()

    # Bob has no saved work orders yet, ever.
    assert len(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 = bob.generate_work_orders(the_hrac,
                                           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(bob._saved_work_orders) == 1
    # And the Ursula.
    assert len(bob._saved_work_orders.ursulas) == 1

    ursula_dht_key, work_order = list(work_orders.items())[0]

    # **** RE-ENCRYPTION HAPPENS HERE! ****
    cfrags = bob.get_reencrypted_c_frags(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.attach_cfrag(the_cfrag)

    # Having received the cFrag, Bob also saved the WorkOrder as complete.
    assert len(bob._saved_work_orders) == 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 ursulas:  # ...and to do that, we need to address the right ursula.
        if u.rest_port == work_order.ursula.rest_port:
            ursula = u
            break
    else:
        raise RuntimeError(
            "Somehow we don't know about this Ursula.  Major malfunction.")

    kfrag_bytes = ursula.datastore.get_policy_arrangement(
        work_order.kfrag_hrac.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)[:CFRAG_LENGTH_WITHOUT_PROOF] == bytes(
        the_correct_cfrag
    )[:CFRAG_LENGTH_WITHOUT_PROOF]  # It's the correct cfrag!

    assert the_correct_cfrag.verify_correctness(
        capsule,
        pubkey_a=enacted_policy.public_key,
        pubkey_b=bob.public_key(EncryptingPower))

    # Now we'll show that Ursula saved the correct WorkOrder.
    work_orders_from_bob = ursula.work_orders(bob=bob)
    assert len(work_orders_from_bob) == 1
    assert work_orders_from_bob[0] == work_order
Esempio n. 18
0
def generate_kfrags(
    delegating_privkey: UmbralPrivateKey,
    receiving_pubkey: UmbralPublicKey,
    threshold: int,
    N: int,
    signer: Signer,
    sign_delegating_key: Optional[bool] = True,
    sign_receiving_key: Optional[bool] = True,
) -> List[KFrag]:
    """
    Creates a re-encryption key from Alice's delegating public key to Bob's
    receiving public key, and splits it in KFrags, using Shamir's Secret Sharing.
    Requires a threshold number of KFrags out of N.

    Returns a list of N KFrags
    """

    if threshold <= 0 or threshold > N:
        raise ValueError(
            'Arguments threshold and N must satisfy 0 < threshold <= N')

    if delegating_privkey.params != receiving_pubkey.params:
        raise ValueError("Keys must have the same parameter set.")

    params = delegating_privkey.params

    g = params.g

    delegating_pubkey = delegating_privkey.get_pubkey()

    bob_pubkey_point = receiving_pubkey.point_key

    # The precursor point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'dh_point' is used to derive other secret values
    private_precursor = CurveBN.gen_rand(params.curve)
    precursor = private_precursor * g  # type: Any

    dh_point = private_precursor * bob_pubkey_point

    from constant_sorrow import constants

    # Secret value 'd' allows to make Umbral non-interactive
    d = CurveBN.hash(precursor,
                     bob_pubkey_point,
                     dh_point,
                     bytes(constants.NON_INTERACTIVE),
                     params=params)

    # Coefficients of the generating polynomial
    coefficients = [delegating_privkey.bn_key * (~d)]
    coefficients += [
        CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)
    ]

    bn_size = CurveBN.expected_bytes_length(params.curve)

    kfrags = list()
    for _ in range(N):
        kfrag_id = os.urandom(bn_size)

        # The index of the re-encryption key share (which in Shamir's Secret
        # Sharing corresponds to x in the tuple (x, f(x)), with f being the
        # generating polynomial), is used to prevent reconstruction of the
        # re-encryption key without Bob's intervention
        share_index = CurveBN.hash(precursor,
                                   bob_pubkey_point,
                                   dh_point,
                                   bytes(constants.X_COORDINATE),
                                   kfrag_id,
                                   params=params)

        # The re-encryption key share is the result of evaluating the generating
        # polynomial for the index value
        rk = poly_eval(coefficients, share_index)

        commitment = rk * params.u  # type: Any

        validity_message_for_bob = (
            kfrag_id,
            delegating_pubkey,
            receiving_pubkey,
            commitment,
            precursor,
        )  # type: Any
        validity_message_for_bob = bytes().join(
            bytes(item) for item in validity_message_for_bob)
        signature_for_bob = signer(validity_message_for_bob)

        if sign_delegating_key and sign_receiving_key:
            mode = DELEGATING_AND_RECEIVING
        elif sign_delegating_key:
            mode = DELEGATING_ONLY
        elif sign_receiving_key:
            mode = RECEIVING_ONLY
        else:
            mode = NO_KEY

        validity_message_for_proxy = [kfrag_id, commitment, precursor,
                                      mode]  # type: Any

        if sign_delegating_key:
            validity_message_for_proxy.append(delegating_pubkey)
        if sign_receiving_key:
            validity_message_for_proxy.append(receiving_pubkey)

        validity_message_for_proxy = bytes().join(
            bytes(item) for item in validity_message_for_proxy)
        signature_for_proxy = signer(validity_message_for_proxy)

        kfrag = KFrag(
            identifier=kfrag_id,
            bn_key=rk,
            point_commitment=commitment,
            point_precursor=precursor,
            signature_for_proxy=signature_for_proxy,
            signature_for_bob=signature_for_bob,
            keys_in_signature=mode,
        )

        kfrags.append(kfrag)

    return kfrags