コード例 #1
0
    def _raw_mul(self, plaintext):
        """Returns the integer E(a * plaintext), where E(a) = ciphertext

        Args:
          plaintext (int): number by which to multiply the
            `EncryptedNumber`. *plaintext* is typically an encoding.
            0 <= *plaintext* < :attr:`~PaillierPublicKey.n`

        Returns:
          int: Encryption of the product of `self` and the scalar
            encoded in *plaintext*.

        Raises:
          TypeError: if *plaintext* is not an int.
          ValueError: if *plaintext* is not between 0 and
            :attr:`PaillierPublicKey.n`.
        """
        if not isinstance(plaintext, int):
            raise TypeError('Expected ciphertext to be int, not %s' %
                            type(plaintext))

        if plaintext < 0 or plaintext >= self.public_key.n:
            raise ValueError('Scalar out of bounds: %i' % plaintext)

        if self.public_key.n - self.public_key.max_int <= plaintext:
            # Very large plaintext, play a sneaky trick using inverses
            neg_c = invert(self.ciphertext(False), self.public_key.nsquare)
            neg_scalar = self.public_key.n - plaintext
            return powmod(neg_c, neg_scalar, self.public_key.nsquare)
        else:
            return powmod(self.ciphertext(False), plaintext,
                          self.public_key.nsquare)
コード例 #2
0
def prepare_ballot(pk, n_choices, n_candidates):
    n2 = pk.nsquare

    ballot = []
    for _ in range(n_candidates):
        # values provided (ciphertexts and corresponding proofs of validity)
        ciphertexts = []
        proofs = []

        # product of ciphertexts of the raw (ciphertext and randomization)
        row_c = 1
        row_r = 1

        plaintexts = [1] + [0] * (n_choices - 1)
        for plaintext in plaintexts:
            ciphertext, r, proof = encrypt_among(pk, plaintext, [0, 1])
            ciphertexts.append(ciphertext)
            proofs.append(proof)

            row_c = row_c * ciphertext.raw_value % n2
            row_r = row_r * r % pk.n

        # prove that row_c is an encryption of 1
        omega = random.SystemRandom().randrange(pk.n)
        a = powmod(omega, pk.n, n2)
        e = H(a)
        z = omega * powmod(row_r, e, pk.n)
        row_proof = a, z
        row_proof = None

        row = (ciphertexts, proofs, row_proof)
        ballot.append(row)
    return ballot
コード例 #3
0
    def raw_multiply(self, a, b, randomization=None):
        """Multiply a raw ciphertext with a plaintext

        Arguments:
            a (int): a ciphertext for this Paillier public key
            b (int): a plaintext
            randomization (int, optional): the randomization factor; if not
                provided, a secure-random value is chosen

        Returns:
            tuple: a pair of integers, corresponding to the raw ciphertext
            encrypting the product of the value encrypted by `a` and the value
            `b`, and the randomization used
        """
        n2 = self.nsquare

        # explicitely reduces to avoid large exponents
        b %= self.n
        if b > self.n // 2:
            b -= self.n

        # if a is of the form (1+n)^q, then we can avoid the exponentiation
        q, r = divmod(a - 1, self.n)
        if r == 0:
            raw_value = (1 + self.n * q * b) % n2
        else:
            raw_value = util.powmod(a, b, n2)

        # apply randomization
        if randomization is None:
            randomization = random.SystemRandom().randrange(self.n)
        raw_value = raw_value * util.powmod(randomization, self.n, n2) % n2

        return raw_value, randomization
コード例 #4
0
    def verify_decrypt(self, ciphertext, partial_decryption, proof):
        """Check the proof of decryption of the corresponding secret key

        Arguments:
            ciphertext (PaillierCiphertext): the ciphertext to be decrypted in
                a verifiable manner
            partial_decryption (int): the corresponding partial decryption
            proof (int): a proof that `partial_decryption` is indeed a partial
                decryption of `ciphertext` under the corresponding secret key
        """

        pk = self.public_key

        # run Chaum-Pedersen protocol in the Fiat-Shamir heuristic
        t1, t2, w = proof
        h = util.H([
            ciphertext.raw_value,
            partial_decryption,
            t1,
            self.verification_base,
            self.verification,
            t2,
        ])

        # verify proof
        # check that v^s = t_1 * v_i^c
        if util.powmod(self.verification_base, w, pk.n) != \
                t1 * util.powmod(self.verification, h, pk.n) % pk.n:
            raise InvalidProof
        # check that (x^2)^s = t_2 * (m^2)^c
        if util.powmod(ciphertext.raw_value, _CP*_QR*w, pk.n) != \
                t2 * util.powmod(partial_decryption, _CP*h, pk.n) % pk.n:
            raise InvalidProof
コード例 #5
0
    def decrypt(self, ciphertext, relative=True):
        """Decrypt a ciphertext

        Arguments:
            ciphertext (int or PaillierCiphertext): the ciphertext
            relative (bool): whether the result should be interpreted as a
                relative integer (i.e. in [-n/2, n/2] rather than in [0, n])

        Returns:
            int: the message represented in the ciphertext

            If no transformation other than (re)randomization has been
            performed on the ciphertext, then the original message should be
            returned. If homomorphic operations have been performed, then the
            result of these operations on the original messages should be
            returned.

            If relative is set to `True`, then the returned value is a relative
            integer between `-n/2` and `n/2`. Otherwise, it is a non-negative
            integer lower than `n`.
        """
        pk = self.public_key
        p, q = self.p, self.q
        if isinstance(ciphertext, PaillierCiphertext):
            ciphertext = ciphertext.raw_value
        m_mod_p = pk.L(util.powmod(ciphertext, p - 1, p * p), p) * self.hp % p
        m_mod_q = pk.L(util.powmod(ciphertext, q - 1, q * q), q) * self.hq % q
        plaintext = util.crt([m_mod_p, m_mod_q], [p, q])
        if relative and plaintext >= pk.n // 2:
            plaintext -= pk.n
        return plaintext
コード例 #6
0
    def raw_decrypt(self, ciphertext):
        """Decrypt raw ciphertext and return raw plaintext.

        Args:
          ciphertext (int): (usually from :meth:`EncryptedNumber.ciphertext()`)
            that is to be Paillier decrypted.

        Returns:
          int: Paillier decryption of ciphertext. This is a positive
          integer < :attr:`public_key.n`.

        Raises:
          TypeError: if ciphertext is not an int.
        """
        if not isinstance(ciphertext, int):
            raise TypeError('Expected ciphertext to be an int, not: %s' %
                            type(ciphertext))

        decrypt_to_p = self.l_function(
            powmod(ciphertext, self.p - 1, self.psquare),
            self.p) * self.hp % self.p
        decrypt_to_q = self.l_function(
            powmod(ciphertext, self.q - 1, self.qsquare),
            self.q) * self.hq % self.q
        return self.crt(decrypt_to_p, decrypt_to_q)
コード例 #7
0
    def prove_decrypt_batched(self, ciphertext_batch):
        """Batched version of `prove_decrypt()`

        Arguments:
            ciphertext_batch (list): the ciphertexts (PaillierCiphertext) to be
                decrypted in a verifiable manner

        Returns:
            tuple: `partial_decryption_batch`, `proof` where
            `partial_decryption_batch` is a list of the partial decryptions
            (int) of the ciphertexts, and `proof` is a proof (int) that they
            are indeed so
        """
        pk = self.public_key
        partial_decryption_batch = [
            self.decrypt(ciphertext) for ciphertext in ciphertext_batch
        ]

        # run protocol in the Fiat-Shamir heuristic

        # to aggregate ZKPs, the verifier provides λ_i *after* the plaintexts
        # have been provided; then combined_ciphertext = ∏ ciphertext^{λ_i}
        # and combined_plaintext = ∏ m^{λ_i} (not needed for prover)
        lambda_batch = [
            util.H([ciphertext.raw_value, partial_decryption])
            for ciphertext, partial_decryption in zip(
                ciphertext_batch, partial_decryption_batch)
        ]
        combined_plaintext = util.prod(
            util.powmod(plaintext, lambda_, pk.n) for plaintext, lambda_ in
            zip(partial_decryption_batch, lambda_batch))
        combined_ciphertext = util.prod(
            util.powmod(ciphertext.raw_value, lambda_, pk.n)
            for ciphertext, lambda_ in zip(ciphertext_batch, lambda_batch))

        try:
            # raises IndexError if not enough precomputations were forecast
            r, t1 = self.precomputed_values.pop()
        except AttributeError:
            # no pre-computations
            r = random.SystemRandom().randrange(pk.n <<
                                                (2 * pk.security_parameter))
            t1 = util.powmod(self.verification_base, r, pk.n)

        # prove knowledge of key_share such that:
        #   * v_i = v**key_share
        #   * combined_plaintext**2 = (combined_ciphertext**2)**(2*key_share)
        t2 = util.powmod(combined_ciphertext, _CP * _QR * r, pk.n)
        h = util.H([
            combined_ciphertext,
            combined_plaintext,
            t1,
            self.verification_base,
            self.verification,
            t2,
        ])
        w = r + h * self.key_share
        proof = t1, t2, w
        return partial_decryption_batch, proof
コード例 #8
0
    def verify_knowledge(self, proof):
        """Check the proof of knowledge of the corresponding secret key

        Arguments:
            proof (int): a proof of knowledge of the secret key
        """
        pk = self.public_key

        # run Schnorr protocol in the Fiat-Shamir heuristic
        t, w = proof
        h = util.H([self.verification_base, self.verification, t])

        # verify proof
        if util.powmod(self.verification_base, w, pk.n) != \
                t * util.powmod(self.verification, h, pk.n) % pk.n:
            raise InvalidProof
コード例 #9
0
    def __init__(self, p, q, g):
        """Constructor

        Arguments:
            p (int): parameter from the Paillier cryptosystem
            q (int): parameter from the Paillier cryptosystem
            g (int): parameter from the Paillier cryptosystem
        """

        self.p = p
        self.q = q
        self.public_key = pk = PaillierPublicKey(p * q, g)

        # pre-computations
        self.hp = util.invert(pk.L(util.powmod(pk.g, p - 1, p * p), p), p)
        self.hq = util.invert(pk.L(util.powmod(pk.g, q - 1, q * q), q), q)
コード例 #10
0
    def prove_private_multiply(self, x, cy):
        """Multiply a ciphertext with a plaintext in a verifiable manner

        Arguments:
            x (int): the clear operand
            cy (PaillierCiphertext): the encrypted operand

        Returns:
            tuple: `cx`, `cz`, `proof` where `cx` is an encryption
            (PaillierCiphertext) of x, `cz` is an encryption
            (PaillierCiphertext) of x*y and `proof` is a proof (int)
            that z = x * y
        """
        n2 = self.nsquare

        # precomputable values
        try:
            # raises IndexError if not enough precomputations were forecast
            x_, (cx, rx), u, (cu, ru) = self.precomputed_values.pop()
        except AttributeError:
            # no pre-computations
            u = random.SystemRandom().randrange(self.n)
            cx, rx = self.raw_multiply(self.g, x)  # ⟦x⟧
            cu, ru = self.raw_multiply(self.g, u)  # ⟦u⟧
        else:
            # ensure consistency with arguments
            if x is None:
                x = x_
            elif x != x_:
                raise ValueError

        # other encrypted values
        cz, rz = self.raw_multiply(cy, x)  # ⟦z⟧ = ⟦xy⟧
        cyu, ryu = self.raw_multiply(cy, u)  # ⟦yu⟧

        # run protocol in the Fiat-Shamir heuristic
        h = util.H([cx, cy, cz, cu, cyu])
        rs = ru * util.powmod(rx, h, self.n) % self.n
        rys = ryu * util.powmod(rz, h, self.n) % self.n
        w = u + x * h
        proof = cu, cyu, w, rs, rys

        return cx, cz, proof
コード例 #11
0
    def __init__(self, public_key, verification_base, key_share):
        """Constructor

        Arguments:
            public_key (PaillierPublicKey): the non-shared Paillier public key
            key_share (int): parameter s_i in shared Paillier
        """
        self.public_key = public_key
        self.verification_base = verification_base
        self.key_share = key_share
        n2 = public_key.nsquare
        self.verification = util.powmod(verification_base, key_share, n2)
コード例 #12
0
def encrypt_among(pk, plaintext, values):
    n2 = pk.nsquare
    raw_ciphertext, r = pk.raw_multiply(pk.g, plaintext)
    ciphertext = paillier.PaillierCiphertext(pk, raw_ciphertext)

    omega = random.SystemRandom().randrange(pk.n)
    e = [random.SystemRandom().randrange(2 << (2 * 1023)) for _ in values]
    z = [random.SystemRandom().randrange(pk.n) for _ in values]
    a = [
        powmod(omega, pk.n, n2)
        if plaintext == value else powmod(z[i], pk.n, n2) *
        powmod(raw_ciphertext * powmod(pk.g, value, n2), -e[i], n2) % n2
        for i, value in enumerate(values)
    ]

    e_challenge = H(a)
    i = values.index(plaintext)
    e[i] = e_challenge - (sum(e) + e[i])
    z[i] = omega * powmod(r, e[i], pk.n) % pk.n
    proof = a, e, z
    return ciphertext, r, proof
コード例 #13
0
    def decrypt(self, ciphertext):
        """(Partially) decrypt a ciphertext

        Arguments:
            ciphertext (PaillierCiphertext): the ciphertext to be decrypted

        Returns:
            int: the decryption share of the ciphertext corresponding to this
                secret key share
        """
        pk = self.public_key
        return util.powmod(ciphertext.raw_value, _QR * self.key_share, pk.n)
コード例 #14
0
    def prove_decrypt(self, ciphertext):
        """(Partially) decrypt a ciphertext in a verifiable manner

        Arguments:
            ciphertext (PaillierCiphertext): the ciphertext to be decrypted

        Returns:
            tuple: `partial_decryption`, `proof` where `partial_decryption` is
            the partial decryption (int) of the ciphertext, and `proof` is a
            proof (int) that it is indeed so
        """
        pk = self.public_key
        partial_decryption = self.decrypt(ciphertext)

        try:
            # raises IndexError if not enough precomputations were forecast
            r, t1 = self.precomputed_values.pop()
        except AttributeError:
            # no pre-computations
            r = random.SystemRandom().randrange(pk.n <<
                                                (2 * pk.security_parameter))
            t1 = util.powmod(self.verification_base, r, pk.n)

        # prove knowledge of key_share such that:
        #   * v_i = v**key_share
        #   * (partial_decryption**2) = (ciphertext**2)**(2*key_share)
        t2 = util.powmod(ciphertext.raw_value, _CP * _QR * r, pk.n)

        # run Chaum-Pedersen protocol in the Fiat-Shamir heuristic
        h = util.H([
            ciphertext.raw_value,
            partial_decryption,
            t1,
            self.verification_base,
            self.verification,
            t2,
        ])
        w = r + h * self.key_share
        proof = t1, t2, w
        return partial_decryption, proof
コード例 #15
0
def share_paillier_keypair(pk, sk, n_shares):
    """Share an existing keypair for the Paillier cryptosystem

    Arguments:
        pk (PaillierPublicKey): public part of the keypair to be shared
        sk (PaillierSecretKey): secret part of the keypair to be shared
        n_shares (int): the number of shares into which to split the keypair

    Returns:
        tuple: pair of two elements, usually named respectively `pk_shares`
            (`list` of `PaillierPublicKeyShare`) and `sk_shares` (`list` of
            `PaillierSecretKeyShare`).

        When used together, the secret key shares (`sk_shares`) allow to
        decrypt ciphertexts generated using the given public key (but not using
        another), using the method `assemble_decryption_shares()` from
        `PaillierPublicKeyShare`. The public key shares (`pk_shares`) can be
        used to verify that each secret key share was used correctly (usually
        one share would be given to each party, and decryption would imply that
        each party correctly processes the ciphertext using their secret key
        share).
    """
    m = (sk.p - 1) * (sk.q - 1)
    exponent = util.invert(pk.n, m)

    # the verification base must generate the quadratic residues; which happens
    # with overwhelming probability for a random square
    verification_base = random.SystemRandom().randrange(pk.nsquare)**2 % pk.n

    # split the secret exponent into required number of shares
    key_shares = [
        random.SystemRandom().randrange(m) for _ in range(n_shares - 1)
    ]
    key_shares.append((exponent - sum(key_shares)) % (m))

    # compute corresponding verification elements
    verifications = [
        util.powmod(verification_base, key_share, pk.nsquare)
        for key_share in key_shares
    ]

    # create public and private key shares
    pk_shares = [
        PaillierPublicKeyShare(pk, verification_base, verification)
        for verification in verifications
    ]
    sk_shares = [
        PaillierSecretKeyShare(pk, verification_base, key_share)
        for key_share in key_shares
    ]

    return pk_shares, sk_shares
コード例 #16
0
    def verify_private_multiply_batched(self, cx, cy_list, cz_list, proof):
        """Check the proof of multiplication of a ciphertext with a plaintext

        Arguments:
            cx (PaillierCiphertext): the encrypted left operand
            cy_list (list): the encrypted right operands (PaillierCiphertext)
            cz_list (list): the encrypted products (PaillierCiphertext) of x*y
                for each y in `cy_list`
            proof (int): a proof that z = x*y for each y, z in cy_list, cz_list
        """
        n2 = self.nsquare

        # generate random λ_i *after* ciphertexts have been provided
        cu, cyu, w, rs, rys = proof
        lambda_list = [
            util.H([cx, cy, cz]) for cy, cz in zip(cy_list, cz_list)
        ]

        # compute combined ciphertexts
        cy = util.prod(
            util.powmod(cy, lambda_, self.nsquare)
            for cy, lambda_ in zip(cy_list, lambda_list))
        cz = util.prod(
            util.powmod(cz, lambda_, self.nsquare)
            for cz, lambda_ in zip(cz_list, lambda_list))

        # run private multiply protocol in the Fiat-Shamir heuristic
        h = util.H([cx, cy, cz, cu, cyu])

        # verify proofs
        cs, _ = self.raw_multiply(self.g, w, rs)  # ⟦s⟧ = ⟦u + xe⟧
        cys, _ = self.raw_multiply(cy, w, rys)  # ⟦ys⟧ = ⟦y(u + xe)⟧
        # ⟦u⟧ * ⟦x⟧**e = ⟦u + xe⟧ = ⟦s⟧
        if cs != cu * util.powmod(cx, h, n2) % n2:
            raise InvalidProof
        # ⟦yu⟧ * ⟦z⟧**e = ⟦yu + yxe⟧ = ⟦ys⟧
        if cys != cyu * util.powmod(cz, h, n2) % n2:
            raise InvalidProof
コード例 #17
0
    def verify_private_multiply(self, cx, cy, cz, proof):
        """Check the proof of multiplication of a ciphertext with a plaintext

        Arguments:
            cx (int): the encrypted left operand
            cy (int): the encrypted right operand
            cz (int): the encrypted result
            proof (int): a proof that z = x*y
        """
        n2 = self.nsquare

        # run protocol in the Fiat-Shamir heuristic
        cu, cyu, w, rs, rys = proof
        h = util.H([cx, cy, cz, cu, cyu])

        # verify proofs
        cs, _ = self.raw_multiply(self.g, w, rs)  # ⟦s⟧ = ⟦u + xe⟧
        cys, _ = self.raw_multiply(cy, w, rys)  # ⟦ys⟧ = ⟦y(u + xe)⟧
        # ⟦u⟧ * ⟦x⟧**e = ⟦u + xe⟧ = ⟦s⟧
        if cs != cu * util.powmod(cx, h, n2) % n2:
            raise InvalidProof
        # ⟦yu⟧ * ⟦z⟧**e = ⟦yu + yxe⟧ = ⟦ys⟧
        if cys != cyu * util.powmod(cz, h, n2) % n2:
            raise InvalidProof
コード例 #18
0
    def verify_decrypt_batched(self, ciphertext_batch,
                               partial_decryption_batch, proof):
        """Batched version of `verify_decrypt()`

        Arguments:
            ciphertext_batch (list): the ciphertexts (PaillierCiphertext) to be
                decrypted in a verifiable manner
            partial_decryption_batch (list): the corresponding partial
                decryptions (int)
            proof: a proof that each element of `partial_decryption_batch` is
                indeed a partial decryption of the corresponding element in
                `ciphertext_batch` under the secret key corresponding to self
        """
        pk = self.public_key

        # run protocol in the Fiat-Shamir heuristic
        t1, t2, w = proof

        # generate random λ_i *after* decryption shares have been provided
        lambda_batch = [
            util.H([ciphertext.raw_value, partial_decryption])
            for ciphertext, partial_decryption in zip(
                ciphertext_batch, partial_decryption_batch)
        ]

        # compute combined plaintext and ciphertext for verification
        combined_plaintext = util.prod(
            util.powmod(plaintext, lambda_, pk.n) for plaintext, lambda_ in
            zip(partial_decryption_batch, lambda_batch))
        combined_ciphertext = util.prod(
            util.powmod(ciphertext.raw_value, lambda_, pk.n)
            for ciphertext, lambda_ in zip(ciphertext_batch, lambda_batch))
        h = util.H([
            combined_ciphertext,
            combined_plaintext,
            t1,
            self.verification_base,
            self.verification,
            t2,
        ])

        # verify proof
        # check that v^s = t_1 * v_i^c
        if util.powmod(self.verification_base, w, pk.n) != \
                t1 * util.powmod(self.verification, h, pk.n) % pk.n:
            raise InvalidProof
        # check that (x^2)^s = t_2 * (m^2)^c
        if util.powmod(combined_ciphertext, _CP*_QR*w, pk.n) != \
                t2 * util.powmod(combined_plaintext, _CP*h, pk.n) % pk.n:
            raise InvalidProof
コード例 #19
0
    def raw_encrypt(self, plaintext, r_value=None):
        """Paillier encryption of a positive integer plaintext < :attr:`n`.

        You probably should be using :meth:`encrypt` instead, because it
        handles positive and negative ints and floats.

        Args:
          plaintext (int): a positive integer < :attr:`n` to be Paillier
            encrypted. Typically this is an encoding of the actual
            number you want to encrypt.
          r_value (int): obfuscator for the ciphertext; by default (i.e.
            r_value is None), a random value is used.

        Returns:
          int: Paillier encryption of plaintext.

        Raises:
          TypeError: if plaintext is not an int or mpz.
        """
        if not isinstance(plaintext, int) and not isinstance(
                plaintext, type(mpz(1))) and not isinstance(
                    plaintext, numpy.int64):
            raise TypeError('Expected int type plaintext but got: %s' %
                            type(plaintext))

        if self.n - self.max_int <= plaintext < self.n:
            # Very large plaintext, take a sneaky shortcut using inverses
            neg_plaintext = self.n - plaintext  # = abs(plaintext - nsquare)
            neg_ciphertext = (self.n * neg_plaintext + 1) % self.nsquare
            nude_ciphertext = invert(neg_ciphertext, self.nsquare)
        else:
            # we chose g = n + 1, so that we can exploit the fact that
            # (n+1)^plaintext = n*plaintext + 1 mod n^2
            nude_ciphertext = (self.n * plaintext + 1) % self.nsquare

        # r = r_value or self.get_random_lt_n()
        # obfuscator = powmod(r, self.n, self.nsquare)
        r = r_value or powmod(self.get_random_lt_n(), self.n,
                              self.nsquare)  # Pass the precomputed obfuscator
        obfuscator = r

        return (nude_ciphertext * obfuscator) % self.nsquare
コード例 #20
0
    def obfuscate(self):
        """Disguise ciphertext by multiplying by r ** n with random r.

        This operation must be performed for every `EncryptedNumber`
        that is sent to an untrusted party, otherwise eavesdroppers
        might deduce relationships between this and an antecedent
        `EncryptedNumber`.

        For example::

            enc = public_key.encrypt(1337)
            send_to_nsa(enc)       # NSA can't decrypt (we hope!)
            product = enc * 3.14
            send_to_nsa(product)   # NSA can deduce 3.14 by bruteforce attack
            product2 = enc * 2.718
            product2.obfuscate()
            send_to_nsa(product)   # NSA can't deduce 2.718 by bruteforce attack
        """
        r = self.public_key.get_random_lt_n()
        r_pow_n = powmod(r, self.public_key.n, self.public_key.nsquare)
        self.__ciphertext = self.__ciphertext * r_pow_n % self.public_key.nsquare
        self.__is_obfuscated = True
コード例 #21
0
    def precompute_proofs(self, n_uses):
        """Precompute and cache some values used in the proofs

        Using this function does not decreases (nor increases, assuming the
        exact value for `n_uses` is provided) the total execution time.
        However, it can be useful to perform some computation in advance, so
        that the actual use of the secret shares does not take as much time
        (i.e. when the inputs to the protocol are known).

        Arguments:
            n_uses (int): upper-bound on the number of proofs that will be
                performed (the larger the value, the longer this step takes)
        """
        pk = self.public_key
        randoms = [
            random.SystemRandom().randrange(pk.n << (2 *
                                                     pk.security_parameter))
            for _ in range(n_uses)
        ]
        self.precomputed_values = [
            (r, util.powmod(self.verification_base, r, pk.n)) for r in randoms
        ]
コード例 #22
0
    def prove_knowledge(self):
        """Proves knowldege of the secret key share

        Returns:
            int: the proof
        """
        pk = self.public_key

        try:
            # raises IndexError if not enough precomputations were forecast
            r, t = self.precomputed_values.pop()
        except AttributeError:
            # no pre-computations
            r = random.SystemRandom().randrange(pk.n <<
                                                (2 * pk.security_parameter))
            t = util.powmod(self.verification_base, r, pk.n)

        # run Schnorr protocol in the Fiat-Shamir heuristic
        h = util.H([self.verification_base, self.verification, t])
        w = r + h * self.key_share
        proof = t, w
        return proof
コード例 #23
0
    def assemble_decryption_shares(ciphertext,
                                   shares,
                                   decryption_shares,
                                   relative=True):
        """Assemble decryptions share in a complete decryption

        Arguments:
            shares (list): the public key shares (PaillierPublicKeyShare)
                corresponding to the secret key shares used to decrypt the
                ciphertext
            decryption_shares (list): the decryption shares (int) resulting
                from the (partial) decryptions by the secret key shares
            relative (bool): whether the result should be interpreted as a
                relative integer (i.e. in [-n/2, n/2] rather than in [0, n])

        Returns:
            int: the message represented in the ciphertext

            If no transformation other than (re)randomization has been
            performed on the ciphertext, then the original message should be
            returned. If homomorphic operations have been performed, then the
            result of these operations on the original messages should be
            returned.

            If relative is set to `True`, then the returned value is a relative
            integer between `-n/2` and `n/2`. Otherwise, it is a non-negative
            integer lower than `n`.
        """
        pk = shares[0].public_key
        C = ciphertext.raw_value
        R = util.prod(decryption_shares, pk.n)
        plaintext = (
            (C * util.powmod(R, -pk.n, pk.nsquare) % pk.nsquare) - 1) // pk.n
        # TODO:  _QR
        if relative and plaintext >= pk.n // 2:
            plaintext -= pk.n
        return plaintext
コード例 #24
0
 def h_function(self, x, xsquare):
     """Computes the h-function as defined in Paillier's paper page 12,
     'Decryption using Chinese-remaindering'.
     """
     return invert(
         self.l_function(powmod(self.public_key.g, x - 1, xsquare), x), x)
コード例 #25
0
    def prove_private_multiply_batched(self, x, cy_list):
        """Multiply a secret with several ciphertexts in a verifiable manner

        Arguments:
            x (int): the secret operand (plaintext)
            cy_list (list): the list of encrypted operands (PaillierCiphertext)

        Returns:
            tuple: `cx`, `cz_list`, `proof` where `cx` is an encryption
            (PaillierCiphertext) of x, `cz_list` is a list of encryptions
            (PaillierCiphertext) of x*y for each y in `cy_list`, and `proof`
            (int) is a proof that z = x*y for each y, z in cy_list, cz_list
        """
        n2 = self.nsquare

        # precomputable values
        try:
            # raises IndexError if not enough precomputations were forecast
            x_, (cx, rx), u, (cu, ru) = self.precomputed_values.pop()
        except AttributeError:
            # no pre-computations
            u = random.SystemRandom().randrange(self.n)
            cx, rx = self.raw_multiply(self.g, x)  # ⟦x⟧
            cu, ru = self.raw_multiply(self.g, u)  # ⟦u⟧
        else:
            # ensure consistency with arguments
            if x is None:
                x = x_
            elif x != x_:
                raise ValueError

        # encrypted result
        cz_rz_list = [self.raw_multiply(cy, x) for cy in cy_list]  # ⟦z⟧ = ⟦xy⟧
        cz_list = [cz for cz, rz in cz_rz_list]
        rz_list = [rz for cz, rz in cz_rz_list]

        lambda_list = [
            util.H([cx, cy, cz]) for cy, cz in zip(cy_list, cz_list)
        ]

        # compute combined ciphertexts
        cy = util.prod(
            util.powmod(cy, lambda_, self.nsquare)
            for cy, lambda_ in zip(cy_list, lambda_list))
        cz = util.prod(
            util.powmod(cz, lambda_, self.nsquare)
            for cz, lambda_ in zip(cz_list, lambda_list))
        rz = util.prod(
            util.powmod(rz, lambda_, self.n)
            for rz, lambda_ in zip(rz_list, lambda_list))

        # other encrypted values
        cyu, ryu = self.raw_multiply(cy, u)  # ⟦yu⟧

        # run private multiply protocol in the Fiat-Shamir heuristic
        h = util.H([cx, cy, cz, cu, cyu])
        rs = ru * util.powmod(rx, h, self.n) % self.n
        rys = ryu * util.powmod(rz, h, self.n) % self.n
        w = u + x * h
        proof = cu, cyu, w, rs, rys

        return cx, cz_list, proof