Esempio n. 1
0
def _decapsulate_reencrypted(receiving_privkey: UmbralPrivateKey,
                             capsule: Capsule,
                             key_length: int = DEM_KEYSIZE) -> bytes:
    """Derive the same symmetric key"""
    params = capsule._umbral_params

    pub_key = receiving_privkey.get_pubkey().point_key
    priv_key = receiving_privkey.bn_key

    ni = capsule._point_noninteractive
    d = CurveBN.hash(ni, pub_key, priv_key * ni, params=params)

    e_prime = capsule._point_e_prime
    v_prime = capsule._point_v_prime

    shared_key = d * (e_prime + v_prime)

    key = kdf(shared_key, key_length)

    e = capsule._point_e
    v = capsule._point_v
    s = capsule._bn_sig
    h = CurveBN.hash(e, v, params=params)
    inv_d = ~d
    orig_pub_key = capsule.get_correctness_keys()['delegating'].point_key

    if not (s * inv_d) * orig_pub_key == (h * e_prime) + v_prime:
        raise GenericUmbralError()
    return key
Esempio n. 2
0
def _open_capsule(capsule: Capsule,
                  receiving_privkey: UmbralPrivateKey,
                  check_proof: bool = True) -> bytes:
    """
    Activates the Capsule from the attached CFrags,
    opens the Capsule and returns what is inside.

    This will often be a symmetric key.
    """

    receiving_pubkey = receiving_privkey.get_pubkey()

    if check_proof:
        offending_cfrags = []
        for cfrag in capsule._attached_cfrags:
            if not cfrag.verify_correctness(capsule):
                offending_cfrags.append(cfrag)

        if offending_cfrags:
            error_msg = "Decryption error: Some CFrags are not correct"
            raise UmbralCorrectnessError(error_msg, offending_cfrags)

    capsule._reconstruct_shamirs_secret(receiving_privkey)

    key = _decapsulate_reencrypted(receiving_privkey, capsule)
    return key
Esempio n. 3
0
def _open_capsule(capsule: Capsule, bob_privkey: UmbralPrivateKey,
                  alice_pubkey: UmbralPublicKey, params: UmbralParameters=None, 
                  check_proof=True) -> bytes:
    """
    Activates the Capsule from the attached CFrags,
    opens the Capsule and returns what is inside.

    This will often be a symmetric key.
    """
    params = params if params is not None else default_params()

    priv_b = bob_privkey.bn_key
    pub_b = bob_privkey.get_pubkey().point_key

    pub_a = alice_pubkey.point_key

    # TODO: Change dict for a list if issue #116 goes through
    if check_proof:
        offending_cfrags = []
        for cfrag in capsule._attached_cfrags:
            if not _verify_correctness(capsule, cfrag, pub_a, pub_b, params):
                offending_cfrags.append(cfrag)

        if offending_cfrags:
            error_msg = "Decryption error: Some CFrags are not correct"
            raise UmbralCorrectnessError(error_msg, offending_cfrags)

    capsule._reconstruct_shamirs_secret(pub_a, priv_b, params=params)

    key = _decapsulate_reencrypted(pub_b, priv_b, pub_a, capsule, params=params)
    return key
Esempio n. 4
0
def _decapsulate_reencrypted(receiving_privkey: UmbralPrivateKey,
                             capsule: Capsule,
                             key_length: int = DEM_KEYSIZE) -> bytes:
    """Derive the same symmetric encapsulated_key"""

    params = capsule.params

    pub_key = receiving_privkey.get_pubkey().point_key
    priv_key = receiving_privkey.bn_key

    precursor = capsule._attached_cfrags[0]._point_precursor
    dh_point = priv_key * precursor

    from constant_sorrow import constants

    # Combination of CFrags via Shamir's Secret Sharing reconstruction
    if len(capsule._attached_cfrags) > 1:
        xs = [
            CurveBN.hash(precursor,
                         pub_key,
                         dh_point,
                         bytes(constants.X_COORDINATE),
                         cfrag._kfrag_id,
                         params=params) for cfrag in capsule._attached_cfrags
        ]

        e_summands, v_summands = list(), list()
        for cfrag, x in zip(capsule._attached_cfrags, xs):
            if precursor != cfrag._point_precursor:
                raise ValueError("Attached CFrags are not pairwise consistent")

            lambda_i = lambda_coeff(x, xs)
            e_summands.append(lambda_i * cfrag._point_e1)
            v_summands.append(lambda_i * cfrag._point_v1)

        e_prime = sum(e_summands[1:], e_summands[0])
        v_prime = sum(v_summands[1:], v_summands[0])
    else:
        e_prime = capsule._attached_cfrags[0]._point_e1
        v_prime = capsule._attached_cfrags[0]._point_v1

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

    e, v, s = capsule.components()
    h = CurveBN.hash(e, v, params=params)

    orig_pub_key = capsule.get_correctness_keys(
    )['delegating'].point_key  # type: ignore

    if not (s / d) * orig_pub_key == (h * e_prime) + v_prime:
        raise GenericUmbralError()

    shared_key = d * (e_prime + v_prime)
    encapsulated_key = kdf(shared_key, key_length)
    return encapsulated_key
Esempio n. 5
0
    def create_policy(self, label: bytes, alice_privkey: UmbralPrivateKey,
                      bob_pubkey: UmbralPublicKey, policy_expiration, m: int,
                      n: int):
        """
        Create a Policy with Alice granting Bob access to `label` DataSource

        :param label: A label to represent the policies data
        :param alice_privkey: Alice's private key
        :param bob_pubkey: Bob's public key
        :param policy_expiration: Datetime of policy expiration duration
        :param m: Minimum number of KFrags needed to rebuild ciphertext
        :param n: Total number of rekey shares to generate

        :return: The policy granted to Bob
        """
        # This is not how this should be implemented, but I am still figuring out
        # the keying material and why it is randomly generated when a character is
        # initialized, instead of being derived from the keys like the other powers
        # or explained how it should be stored.
        d = DelegatingPower()
        d.umbral_keying_material = UmbralKeyingMaterial.from_bytes(
            alice_privkey.to_bytes() + alice_privkey.get_pubkey().to_bytes())

        # Initialize Alice
        ALICE = Alice(
            crypto_power_ups=[
                SigningPower(keypair=SigningKeypair(alice_privkey)),
                EncryptingPower(keypair=EncryptingKeypair(alice_privkey)),
                # DelegatingPower
                d
            ],
            network_middleware=RestMiddleware(),
            known_nodes=(self.ursula, ),
            federated_only=True,
            always_be_learning=True)

        # Initialize Bob
        BOB = Bob(crypto_power_ups=[
            SigningPower(pubkey=bob_pubkey),
            EncryptingPower(pubkey=bob_pubkey)
        ],
                  known_nodes=(self.ursula, ),
                  federated_only=True,
                  always_be_learning=True)

        # Alice grants a policy for Bob
        policy = ALICE.grant(BOB,
                             label,
                             m=m,
                             n=n,
                             expiration=policy_expiration)

        return policy
Esempio n. 6
0
    def _reconstruct_shamirs_secret(self, priv_b: UmbralPrivateKey) -> None:
        params = self._umbral_params
        g = params.g

        pub_b = priv_b.get_pubkey()
        priv_b = priv_b.bn_key

        cfrag_0 = self._attached_cfrags[0]
        id_0 = cfrag_0._kfrag_id
        ni = cfrag_0._point_noninteractive
        xcoord = cfrag_0._point_xcoord

        dh_xcoord = priv_b * xcoord

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

        if len(self._attached_cfrags) > 1:
            xs = [
                CurveBN.hash(cfrag._kfrag_id, hashed_dh_tuple, params=params)
                for cfrag in self._attached_cfrags
            ]
            x_0 = CurveBN.hash(id_0, hashed_dh_tuple, params=params)
            lambda_0 = lambda_coeff(x_0, xs)
            e = lambda_0 * cfrag_0._point_e1
            v = lambda_0 * cfrag_0._point_v1

            for cfrag in self._attached_cfrags[1:]:
                if (ni, xcoord) != (cfrag._point_noninteractive,
                                    cfrag._point_xcoord):
                    raise ValueError(
                        "Attached CFrags are not pairwise consistent")

                x_i = CurveBN.hash(cfrag._kfrag_id,
                                   hashed_dh_tuple,
                                   params=params)
                lambda_i = lambda_coeff(x_i, xs)
                e = e + (lambda_i * cfrag._point_e1)
                v = v + (lambda_i * cfrag._point_v1)
        else:
            e = cfrag_0._point_e1
            v = cfrag_0._point_v1

        self._point_e_prime = e
        self._point_v_prime = v
        self._point_noninteractive = ni
Esempio n. 7
0
def _open_capsule(capsule: Capsule, bob_private_key: UmbralPrivateKey,
                  alice_pub_key: UmbralPublicKey) -> bytes:
    """
    Activates the Capsule from the attached CFrags,
    opens the Capsule and returns what is inside.

    This will often be a symmetric key.
    """
    recp_pub_key = bob_private_key.get_pubkey()
    capsule._reconstruct_shamirs_secret()

    key = decapsulate_reencrypted(recp_pub_key.point_key,
                                  bob_private_key.bn_key,
                                  alice_pub_key.point_key, capsule)
    return key
Esempio n. 8
0
    def decrypt_by_sk(cls, sk: keys.UmbralPrivateKey,
                      d_pk: keys.UmbralPublicKey, v_pk: keys.UmbralPublicKey,
                      encrypt_text: bytes, capsule: pre.Capsule, k_frags):
        capsule.set_correctness_keys(delegating=d_pk,
                                     receiving=sk.get_pubkey(),
                                     verifying=v_pk)
        c_frags = list()
        for kFrag in k_frags:
            c_frags.append(pre.reencrypt(kfrag=kFrag, capsule=capsule))
        for cfrag in c_frags:
            capsule.attach_cfrag(cfrag)

        decrypt_text = pre.decrypt(ciphertext=encrypt_text,
                                   capsule=capsule,
                                   decrypting_key=sk)
        return decrypt_text.decode("utf-8")
Esempio n. 9
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. 10
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

    # Secret value 'd' allows to make Umbral non-interactive
    d = hash_to_curvebn(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 = hash_to_curvebn(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