Ejemplo n.º 1
0
    def __init__(self, runtime, security_parameter, p, random):
        assert p > 1
        self.random = random
        # TODO: Generate Paillier cipher with N_i sufficiently larger than p
        self.runtime = runtime
        self.p = p
        self.Zp = GF(p)
        self.k = self._bit_length_of(p)
        self.security_parameter = security_parameter
        self.u_bound = 2**(self.security_parameter + 4 * self.k)
        self.zk_random = Random(self.random.getrandbits(128))

        paillier_random = Random(self.random.getrandbits(128))
        alpha_random = Random(self.random.getrandbits(128))
        self.paillier = ModifiedPaillier(runtime, paillier_random)

        # Debug output.
        #print "n_%d**2:%d" % (runtime.id, self.paillier.pubkey['n_square'])
        #print "n_%d:%d" % (runtime.id, self.paillier.pubkey['n'])
        #print "n_%d bitlength: %d" % \
        #    (runtime.id, self._bit_length_of(self.paillier.pubkey['n']))

        #self.Zp = GF(p)
        #self.Zn2 = GF(self.paillier.pubkey['n_square'])
        #self.alpha = self.Zp(self.random.randint(0, p - 1))
        self.alpha = alpha_random.randint(0, p - 1)
        self.n2 = runtime.players[runtime.id].pubkey['n_square']
Ejemplo n.º 2
0
 def _generate_test_ciphertexts(self, random, runtime, k, s, prover_id):
     paillier = ModifiedPaillier(runtime, random)
     xs, rs, cs = [], [], []
     for i in range(s):
         x = rand_int_signed(random, 2 ** k)
         r, c = paillier.encrypt_r(x, player_id=prover_id)
         xs.append(mpz(x))
         rs.append(mpz(r))
         cs.append(mpz(c))
     return xs, rs, cs
Ejemplo n.º 3
0
 def _generate_test_ciphertexts(self, random, runtime, k, s, prover_id):
     paillier = ModifiedPaillier(runtime, random)
     xs, rs, cs = [], [], []
     for i in range(s):
         x = rand_int_signed(random, 2**k)
         r, c = paillier.encrypt_r(x, player_id=prover_id)
         xs.append(mpz(x))
         rs.append(mpz(r))
         cs.append(mpz(c))
     return xs, rs, cs
Ejemplo n.º 4
0
    def __init__(self, runtime, security_parameter, p, random):
        assert p > 1
        self.random = random
        # TODO: Generate Paillier cipher with N_i sufficiently larger than p
        self.runtime = runtime
        self.p = p
        self.Zp = GF(p)
        self.k = self._bit_length_of(p)
        self.security_parameter = security_parameter
        self.u_bound = 2**(self.security_parameter + 4 * self.k)
        self.zk_random = Random(self.random.getrandbits(128))

        paillier_random = Random(self.random.getrandbits(128))
        alpha_random = Random(self.random.getrandbits(128))
        self.paillier = ModifiedPaillier(runtime, paillier_random)
        
        # Debug output.
        #print "n_%d**2:%d" % (runtime.id, self.paillier.pubkey['n_square'])
        #print "n_%d:%d" % (runtime.id, self.paillier.pubkey['n'])
        #print "n_%d bitlength: %d" % \
        #    (runtime.id, self._bit_length_of(self.paillier.pubkey['n']))

        #self.Zp = GF(p)
        #self.Zn2 = GF(self.paillier.pubkey['n_square'])
        #self.alpha = self.Zp(self.random.randint(0, p - 1))
        self.alpha = alpha_random.randint(0, p - 1)
        self.n2 = runtime.players[runtime.id].pubkey['n_square']
Ejemplo n.º 5
0
        def do_stuff(triple, alpha):
            random = Random(3423993)
            share_random = Random(random.getrandbits(128))

            paillier = ModifiedPaillier(runtime,
                                        Random(random.getrandbits(128)))
            gen = TestShareGenerator(self.Zp, runtime, share_random, paillier,
                                     self.u_bound, alpha)

            x2 = gen.generate_share(x1)
            y2 = gen.generate_share(y1)
            z2 = runtime._basic_multiplication(self.Zp(x1), y2, triple.a,
                                               triple.b, triple.c)
            d = runtime.open(z2)
            d.addCallback(check)
            return d
Ejemplo n.º 6
0
        def do_mult(triples, alpha):
            runtime.triples = triples
            random = Random(3423993)
            share_random = Random(random.getrandbits(128))
            paillier = ModifiedPaillier(runtime,
                                        Random(random.getrandbits(128)))
            gen = TestShareGenerator(self.Zp, runtime, share_random, paillier,
                                     self.u_bound, alpha)

            x2 = gen.generate_share(x1)
            y2 = gen.generate_share(y1)

            z2 = x2 * y2
            d = runtime.open(z2)
            d.addCallback(check)
            return d
Ejemplo n.º 7
0
    def test_sum_plus(self, runtime):
        """Test addition of two numbers."""
        def check(v):
            self.assertEquals(v, 11)

        random = Random(3423993)
        share_random = Random(random.getrandbits(128))

        paillier = ModifiedPaillier(runtime, Random(random.getrandbits(128)))
        gen = TestShareGenerator(self.Zp, runtime, share_random, paillier,
                                 self.u_bound, self.alpha)

        x2 = gen.generate_share(2)
        y2 = gen.generate_share(9)
        z2 = x2 + y2
        d = runtime.open(z2)
        d.addCallback(check)
        return d
Ejemplo n.º 8
0
    def test_open_two_secret_share(self, runtime):
        """Test sharing and open of a number."""
        def check((a, b)):
            self.assertEquals(a, 6)
            self.assertEquals(b, 6)

        random = Random(3423993)
        share_random = Random(random.getrandbits(128))

        paillier = ModifiedPaillier(runtime, Random(random.getrandbits(128)))
        gen = TestShareGenerator(self.Zp, runtime, share_random, paillier,
                                 self.u_bound, self.alpha)

        x = gen.generate_share(6)
        y = gen.generate_share(6)
        d = runtime.open_two_values(x, y)
        d.addCallback(check)
        return d
Ejemplo n.º 9
0
    def test_sub_constant_left(self, runtime):
        """Test subtraction of a public number and secret shared number."""

        y = 8

        def check(v):
            self.assertEquals(v, 3)

        random = Random(3423993)
        share_random = Random(random.getrandbits(128))

        paillier = ModifiedPaillier(runtime, Random(random.getrandbits(128)))
        gen = TestShareGenerator(self.Zp, runtime, share_random, paillier,
                                 self.u_bound, self.alpha)

        x2 = gen.generate_share(5)
        z2 = y - x2
        d = runtime.open(x2)
        d.addCallback(check)
        return d
Ejemplo n.º 10
0
    def test_sum_constant_right(self, runtime):
        """Test addition of secret shared number and a public number."""

        y1 = 7

        def check(v):
            self.assertEquals(v, 15)

        random = Random(3423993)
        share_random = Random(random.getrandbits(128))

        paillier = ModifiedPaillier(runtime, Random(random.getrandbits(128)))
        gen = TestShareGenerator(self.Zp, runtime, share_random, paillier,
                                 self.u_bound, self.alpha)

        x2 = gen.generate_share(8)
        z2 = x2 + y1
        d = runtime.open(z2)
        d.addCallback(check)
        return d
Ejemplo n.º 11
0
    def test_constant_multiplication_constant_right(self, runtime):
        """Test multiplication of two numbers."""

        x1 = 6
        y1 = 7

        def check(v):
            self.assertEquals(v, self.Zp(x1 * y1))

        random = Random(3423993)
        share_random = Random(random.getrandbits(128))

        paillier = ModifiedPaillier(runtime, Random(random.getrandbits(128)))
        gen = TestShareGenerator(self.Zp, runtime, share_random, paillier,
                                 self.u_bound, self.alpha)

        x2 = gen.generate_share(x1)

        z2 = runtime._cmul(x2, self.Zp(y1), self.Zp)
        d = runtime.open(z2)
        d.addCallback(check)
        return d
Ejemplo n.º 12
0
    def test_succeeding_proof(self, runtime):
        seed = 2348838
        k, s, prover_id = 5, 3, 1
        player_random = Random(seed + runtime.id)
        shared_random = Random(seed)
        paillier = ModifiedPaillier(runtime,
                                    Random(player_random.getrandbits(128)))
        x, r, c = self._generate_test_ciphertexts(shared_random, runtime, k, s,
                                                  prover_id)
        #print "Player", runtime.id, " x =", x
        #print "Player", runtime.id, " r =", r
        #print "Player", runtime.id, " c =", c
        if runtime.id == prover_id:
            zk = ZKProof(s,
                         prover_id,
                         k,
                         runtime,
                         c,
                         paillier=paillier,
                         random=player_random,
                         x=x,
                         r=r)
        else:
            zk = ZKProof(s,
                         prover_id,
                         k,
                         runtime,
                         c,
                         paillier=paillier,
                         random=player_random)

        deferred_proof = zk.start()

        def verify(result):
            self.assertTrue(result)

        runtime.schedule_callback(deferred_proof, verify)
        return deferred_proof
Ejemplo n.º 13
0
class TripleGenerator(object):
    def __init__(self, runtime, security_parameter, p, random):
        assert p > 1
        self.random = random
        # TODO: Generate Paillier cipher with N_i sufficiently larger than p
        self.runtime = runtime
        self.p = p
        self.Zp = GF(p)
        self.k = self._bit_length_of(p)
        self.security_parameter = security_parameter
        self.u_bound = 2**(self.security_parameter + 4 * self.k)
        self.zk_random = Random(self.random.getrandbits(128))

        paillier_random = Random(self.random.getrandbits(128))
        alpha_random = Random(self.random.getrandbits(128))
        self.paillier = ModifiedPaillier(runtime, paillier_random)

        # Debug output.
        #print "n_%d**2:%d" % (runtime.id, self.paillier.pubkey['n_square'])
        #print "n_%d:%d" % (runtime.id, self.paillier.pubkey['n'])
        #print "n_%d bitlength: %d" % \
        #    (runtime.id, self._bit_length_of(self.paillier.pubkey['n']))

        #self.Zp = GF(p)
        #self.Zn2 = GF(self.paillier.pubkey['n_square'])
        #self.alpha = self.Zp(self.random.randint(0, p - 1))
        self.alpha = alpha_random.randint(0, p - 1)
        self.n2 = runtime.players[runtime.id].pubkey['n_square']

    def generate_triples(self):
        """Generates triples for use in the BeDOZa protocol.

        Returns a deferred that will eventually yield a list
        containing *self.security_parameter* multiplicative
        triples. Each of these triples is an object of type
        viff.triple.Triple. The components of each triple t, e.g.

            t.a, t.b, and t.c,

        are each of type BeDOZaShare and guaranteed to satisfy the
        equation

            t.c = t.a * t.b.

        This method carries out the off-line work that is needed for
        the BeDOZa protocol to work.

        Data sent over the network is packaged in large hunks in order
        to optimize. TODO: Explain better.

        TODO: This method needs to have enough RAM to represent all n
        triples in memory at the same time. Is there a nice way to
        stream this, e.g. by using Python generators?

        """
        return self._generate_triples(self.security_parameter)

    def _generate_triples(self, number_of_triples):
        self.runtime.increment_pc()
        triples = self._generate_triple_candidates(2 * number_of_triples)
        return self._verify_triple_candidates(number_of_triples, triples)

    def _generate_triple_candidates(self, n):
        """Generates triple candidates for use in the BeDOZa protocol.

        Returns a deferred that will eventually yield a list of 3n
        shares of type viff.bedoza.shares.BeDOZaShare corresponding to
        n multiplicative tuples. The first n are the a's, then comes n
        b's followed by n c's.
        
        The triples are only candidates because consistency of the
        triples is only half-way guaranteed in the precense of active
        adversaries. More concretely, the triples returned by this
        method are guaranteed - even in the precense of an active
        adversary - to be of the right size. But they may not satisfy
        the equation

            c = a * b.

        """
        self.runtime.increment_pc()

        gen = PartialShareGenerator(self.Zp, self.runtime, self.random,
                                    self.paillier)
        partial_shares = []
        for _ in xrange(2 * n):
            partial_shares.append(
                gen.generate_share(self.random.randint(0,
                                                       self.Zp.modulus - 1)))
        partial_shares_c = self._full_mul(partial_shares[0:n],
                                          partial_shares[n:2 * n])
        full_shares = add_macs(self.runtime, self.Zp, self.u_bound, self.alpha,
                               self.random, self.paillier,
                               partial_shares + partial_shares_c)
        return full_shares

    def _verify_triple_candidates(self, n, triple_candidates):
        """Verify remaining consistency of triple candidates.

        Takes as input a deferred list of 2n triple candidates as
        generated by _generate_triple_candidates(2 * n) and returns
        another deferred that will eventually yield a list of
        viff.triple.Triple objects that are guaranteed to be fully
        consistent. That is, the shares of the triples are guaranteed
        to be of the right size and for each triple, the shares are
        quaranteed to satisfy c = a * b.

        The technique implemented in this method is essentially that
        listed in Figure 5.5 in the progress report "LEGO and Other
        Cryptographic Constructions" by Claudio Orlandi.

        However, since we only implement a protocol that is secure in
        the Random Oracle Model and not the Common Reference String
        Model, we can safely skip step 2 of Figure 5.5 and instead
        generate r by having all players P_i broadcast a random
        element r_i and then have each player compute r as the hash of
        the sum of all r_i's.

        """
        assert n == len(triple_candidates) / 6

        def verify(v, a, b, c):
            if v.value != 0:
                raise Exception(
                    "TripleTest failed - The two triple candidates were "
                    "inconsistent.")
            return Triple(a, b, c)

        def prepare_verification(rs_serialized, results):
            # Repr/eval deserialization.
            rs = [eval(rss) for rss in rs_serialized]

            for i in xrange(n):
                a = triple_candidates[i]
                b = triple_candidates[i + 2 * n]
                c = triple_candidates[i + 4 * n]
                x = triple_candidates[i + n]
                y = triple_candidates[i + 3 * n]
                z = triple_candidates[i + 5 * n]

                # Hash all received random values to agree on a single
                # random value for each triple.
                hash = hashlib.sha1()
                for rp in rs:
                    hash.update(str(rp[i]))
                # TODO: We should use a secure random generator here.
                rand = Random(hash.digest())
                r = self.Zp(rand.randint(0, self.p - 1))

                l = self.runtime._cmul(r, x, self.Zp)
                m = self.runtime._cmul(r, y, self.Zp)
                k = self.runtime._cmul(r * r, z, self.Zp)
                v = c - self.runtime._basic_multiplication(a, b, l, m, k)
                v = self.runtime.open(v)
                self.runtime.schedule_callback(v, verify, a, b, c)
                v.addCallbacks(results[i].callback, results[i].errback)

        # TODO: Handle errors better.
        def err_handler(err):
            print err

        results = [Deferred() for _ in xrange(n)]

        ri = [self.random.randint(0, self.p - 1) for _ in xrange(n)]
        # TODO: We use repr/eval as simple marshalling. Should be
        # factored out and maybe optimized by using better compression
        # here.
        ris = self.runtime.broadcast(self.runtime.players.keys(),
                                     self.runtime.players.keys(), repr(ri))
        ris = gatherResults(ris)
        self.runtime.schedule_callback(ris, prepare_verification, results)
        ris.addErrback(err_handler)

        # TODO: Which makes most sense?
        #
        # 1) Compute each triple separately and return list of
        #    deferred triples, or
        #
        # 2) Compute triples as a batch and return single deferred
        #    that evaluates to list of triples.
        #

        return results

    def _bit_length_of(self, i):
        bit_length = 0
        while i:
            i >>= 1
            bit_length += 1
        return bit_length

    def _mul(self, inx, jnx, n, ais=None, cjs=None):
        """Multiply each of the field elements in *ais* with the
        corresponding encrypted elements in *cjs*.
        
        Returns a deferred which will yield a list of field elements.
        """
        CKIND = 1
        """The transmission_restraint_constant is the number of
        encrypted shares we can safely transmit in one call to
        sendData. The sendData method can only transmit up to
        65536 bytes.
        The constant has been imperically determined by running
        TripleGenerator.generate_triples.
        TODO: How can we allow a user of the runtime to adjust this
        constraint at a higher level of abstraction?

        """
        transmission_restraint_constant = 425

        number_of_packets = n / transmission_restraint_constant
        if n % transmission_restraint_constant != 0:
            number_of_packets += 1

        self.runtime.increment_pc()

        pc = tuple(self.runtime.program_counter)

        deferred = []
        zis = []
        if self.runtime.id == inx:
            Nj_square = self.paillier.get_modulus_square(jnx)
            all_cs = []
            for iny, (ai, cj) in enumerate(zip(ais, cjs)):
                if iny % transmission_restraint_constant == 0:
                    cs = []
                    all_cs.append(cs)
                u = rand.randint(0, self.u_bound)
                Ej_u = self.paillier.encrypt(u, jnx)
                cs.append(
                    (fast_pow(cj, ai.value, Nj_square) * Ej_u) % Nj_square)
                zi = self.Zp(-u)
                zis.append(zi)

            for cs in all_cs:
                self.runtime.protocols[jnx].sendData(pc, CKIND, str(cs))

        if self.runtime.id == jnx:
            all_cs = []
            for _ in xrange(number_of_packets):
                cs = Deferred()
                self.runtime._expect_data(inx, CKIND, cs)
                all_cs.append(cs)

            def decrypt(all_cs, pc, zis):
                zjs = []
                cs = reduce(lambda x, y: x + eval(y), all_cs, [])
                for iny, c in enumerate(cs):
                    t = self.paillier.decrypt(c)
                    zj = self.Zp(t)
                    zjs.append(zj)
                if not zis == []:
                    return [x + y for x, y in zip(zis, zjs)]
                else:
                    return zjs

            all_cs_d = gatherResults(all_cs)
            all_cs_d.addCallback(decrypt, pc, zis)
            deferred = all_cs_d
        else:
            zis_deferred = Deferred()
            zis_deferred.callback(zis)
            deferred = zis_deferred

        return deferred

    def _full_mul(self, a, b):
        """Multiply each of the PartialShares in the list *a* with the
        corresponding PartialShare in the list *b*.
        
        Returns a deferred which will yield a list of PartialShares.
        """
        self.runtime.increment_pc()

        def do_full_mul(shareContents, result_shares):
            """Share content belonging to ai, bi are at:
            shareContents[i], shareContents[len(shareContents) + i].
            """
            deferreds = []
            len_shares = len(shareContents)

            ais = [
                shareContent.value
                for shareContent in shareContents[0:len_shares / 2]
            ]
            bis = [
                shareContent.value
                for shareContent in shareContents[len_shares / 2:]
            ]

            b_enc_shares = []
            for inx in self.runtime.players:
                b_enc_shares.append([
                    s.enc_shares[inx - 1]
                    for s in shareContents[len_shares / 2:]
                ])

            values = len(ais) * [0]

            for inx in xrange(0, len(self.runtime.players)):
                for jnx in xrange(0, len(self.runtime.players)):
                    deferreds.append(
                        self._mul(inx + 1, jnx + 1, len(ais), ais,
                                  b_enc_shares[jnx]))

            def compute_shares(zils, values, result_shares):
                for zil in zils:
                    for inx, zi in enumerate(zil):
                        values[inx] += zi

                return values

            d = gatherResults(deferreds)
            d.addCallback(compute_shares, values, result_shares)

            def callBackPartialShareContents(partialShareContents,
                                             result_shares):
                for v, s in zip(partialShareContents, result_shares):
                    s.callback(v)
                return None

            d.addCallback(lambda values: generate_partial_share_contents(
                values, self.runtime, self.paillier, self.k, self.zk_random))
            d.addCallback(callBackPartialShareContents, result_shares)
            return d

        result_shares = [PartialShare(self.runtime, self.Zp) for _ in a]
        self.runtime.schedule_callback(gatherResults(a + b), do_full_mul,
                                       result_shares)
        return result_shares
Ejemplo n.º 14
0
class TripleGenerator(object):

    def __init__(self, runtime, security_parameter, p, random):
        assert p > 1
        self.random = random
        # TODO: Generate Paillier cipher with N_i sufficiently larger than p
        self.runtime = runtime
        self.p = p
        self.Zp = GF(p)
        self.k = self._bit_length_of(p)
        self.security_parameter = security_parameter
        self.u_bound = 2**(self.security_parameter + 4 * self.k)
        self.zk_random = Random(self.random.getrandbits(128))

        paillier_random = Random(self.random.getrandbits(128))
        alpha_random = Random(self.random.getrandbits(128))
        self.paillier = ModifiedPaillier(runtime, paillier_random)
        
        # Debug output.
        #print "n_%d**2:%d" % (runtime.id, self.paillier.pubkey['n_square'])
        #print "n_%d:%d" % (runtime.id, self.paillier.pubkey['n'])
        #print "n_%d bitlength: %d" % \
        #    (runtime.id, self._bit_length_of(self.paillier.pubkey['n']))

        #self.Zp = GF(p)
        #self.Zn2 = GF(self.paillier.pubkey['n_square'])
        #self.alpha = self.Zp(self.random.randint(0, p - 1))
        self.alpha = alpha_random.randint(0, p - 1)
        self.n2 = runtime.players[runtime.id].pubkey['n_square']


    def generate_triples(self):
        """Generates triples for use in the BeDOZa protocol.

        Returns a deferred that will eventually yield a list
        containing *self.security_parameter* multiplicative
        triples. Each of these triples is an object of type
        viff.triple.Triple. The components of each triple t, e.g.

            t.a, t.b, and t.c,

        are each of type BeDOZaShare and guaranteed to satisfy the
        equation

            t.c = t.a * t.b.

        This method carries out the off-line work that is needed for
        the BeDOZa protocol to work.

        Data sent over the network is packaged in large hunks in order
        to optimize. TODO: Explain better.

        TODO: This method needs to have enough RAM to represent all n
        triples in memory at the same time. Is there a nice way to
        stream this, e.g. by using Python generators?

        """
        return self._generate_triples(self.security_parameter)


    def _generate_triples(self, number_of_triples):
        self.runtime.increment_pc()
        triples = self._generate_triple_candidates(2 * number_of_triples)
        return self._verify_triple_candidates(number_of_triples, triples)


    def _generate_triple_candidates(self, n):
        """Generates triple candidates for use in the BeDOZa protocol.

        Returns a deferred that will eventually yield a list of 3n
        shares of type viff.bedoza.shares.BeDOZaShare corresponding to
        n multiplicative tuples. The first n are the a's, then comes n
        b's followed by n c's.
        
        The triples are only candidates because consistency of the
        triples is only half-way guaranteed in the precense of active
        adversaries. More concretely, the triples returned by this
        method are guaranteed - even in the precense of an active
        adversary - to be of the right size. But they may not satisfy
        the equation

            c = a * b.

        """
        self.runtime.increment_pc()
        
        gen = PartialShareGenerator(self.Zp, self.runtime, self.random,
                                    self.paillier)
        partial_shares = []
        for _ in xrange(2 * n):
             partial_shares.append(
                 gen.generate_share(
                     self.random.randint(0, self.Zp.modulus - 1)))
        partial_shares_c = self._full_mul(partial_shares[0: n],
                                          partial_shares[n: 2 * n])
        full_shares = add_macs(self.runtime, self.Zp, self.u_bound, self.alpha,
                               self.random, self.paillier,
                               partial_shares + partial_shares_c)
        return full_shares  


    def _verify_triple_candidates(self, n, triple_candidates):
        """Verify remaining consistency of triple candidates.

        Takes as input a deferred list of 2n triple candidates as
        generated by _generate_triple_candidates(2 * n) and returns
        another deferred that will eventually yield a list of
        viff.triple.Triple objects that are guaranteed to be fully
        consistent. That is, the shares of the triples are guaranteed
        to be of the right size and for each triple, the shares are
        quaranteed to satisfy c = a * b.

        The technique implemented in this method is essentially that
        listed in Figure 5.5 in the progress report "LEGO and Other
        Cryptographic Constructions" by Claudio Orlandi.

        However, since we only implement a protocol that is secure in
        the Random Oracle Model and not the Common Reference String
        Model, we can safely skip step 2 of Figure 5.5 and instead
        generate r by having all players P_i broadcast a random
        element r_i and then have each player compute r as the hash of
        the sum of all r_i's.

        """
        assert n == len(triple_candidates) / 6

        def verify(v, a, b, c):
            if v.value != 0:
                raise Exception(
                    "TripleTest failed - The two triple candidates were "
                    "inconsistent.")
            return Triple(a, b, c)

        def prepare_verification(rs_serialized, results):
            # Repr/eval deserialization.
            rs = [eval(rss) for rss in rs_serialized]

            for i in xrange(n):
                a = triple_candidates[i]
                b = triple_candidates[i + 2 * n]
                c = triple_candidates[i + 4 * n]
                x = triple_candidates[i + n]
                y = triple_candidates[i + 3 * n]
                z = triple_candidates[i + 5 * n]

                # Hash all received random values to agree on a single
                # random value for each triple.
                hash = hashlib.sha1()
                for rp in rs:
                    hash.update(str(rp[i]))
                # TODO: We should use a secure random generator here.
                rand = Random(hash.digest())
                r = self.Zp(rand.randint(0, self.p - 1))

                l = self.runtime._cmul(r, x, self.Zp)
                m = self.runtime._cmul(r, y, self.Zp)
                k = self.runtime._cmul(r * r, z, self.Zp)
                v = c - self.runtime._basic_multiplication(a, b, l, m, k)
                v = self.runtime.open(v)
                self.runtime.schedule_callback(v, verify, a, b, c)
                v.addCallbacks(results[i].callback, results[i].errback)

        # TODO: Handle errors better.
        def err_handler(err):
            print err

        results = [Deferred() for _ in xrange(n)]

        ri = [self.random.randint(0, self.p - 1) for _ in xrange(n)]
        # TODO: We use repr/eval as simple marshalling. Should be
        # factored out and maybe optimized by using better compression
        # here.
        ris = self.runtime.broadcast(
            self.runtime.players.keys(), self.runtime.players.keys(), repr(ri))
        ris = gatherResults(ris)
        self.runtime.schedule_callback(ris, prepare_verification, results)     
        ris.addErrback(err_handler)
        
        # TODO: Which makes most sense?
        # 
        # 1) Compute each triple separately and return list of
        #    deferred triples, or
        #
        # 2) Compute triples as a batch and return single deferred
        #    that evaluates to list of triples.
        #
        
        return results


    def _bit_length_of(self, i):
        bit_length = 0
        while i:
            i >>= 1
            bit_length += 1
        return bit_length


    def _mul(self, inx, jnx, n, ais=None, cjs=None):
        """Multiply each of the field elements in *ais* with the
        corresponding encrypted elements in *cjs*.
        
        Returns a deferred which will yield a list of field elements.
        """
        CKIND = 1
 
        """The transmission_restraint_constant is the number of
        encrypted shares we can safely transmit in one call to
        sendData. The sendData method can only transmit up to
        65536 bytes.
        The constant has been imperically determined by running
        TripleGenerator.generate_triples.
        TODO: How can we allow a user of the runtime to adjust this
        constraint at a higher level of abstraction?

        """
        transmission_restraint_constant = 425

        number_of_packets = n / transmission_restraint_constant
        if n % transmission_restraint_constant != 0:
            number_of_packets += 1
        
        self.runtime.increment_pc()

        pc = tuple(self.runtime.program_counter)

        deferred = []
        zis = []
        if self.runtime.id == inx:
            Nj_square = self.paillier.get_modulus_square(jnx)
            all_cs = []
            for iny, (ai, cj) in enumerate(zip(ais, cjs)):
                if iny % transmission_restraint_constant == 0:
                    cs = []
                    all_cs.append(cs)
                u = rand.randint(0, self.u_bound)
                Ej_u = self.paillier.encrypt(u, jnx)
                cs.append( (fast_pow(cj, ai.value, Nj_square) * Ej_u) % Nj_square )
                zi = self.Zp(-u)
                zis.append(zi)
                
            for cs in all_cs:
                self.runtime.protocols[jnx].sendData(pc, CKIND, str(cs))

        if self.runtime.id == jnx:
            all_cs = []
            for _ in xrange(number_of_packets):
                cs = Deferred()
                self.runtime._expect_data(inx, CKIND, cs)
                all_cs.append(cs)
                
            def decrypt(all_cs, pc, zis):
                zjs = []
                cs = reduce(lambda x, y: x + eval(y), all_cs, [])
                for iny, c in enumerate(cs):
                    t = self.paillier.decrypt(c)
                    zj = self.Zp(t)
                    zjs.append(zj)
                if not zis == []:
                    return [x + y for x, y in zip(zis, zjs)]
                else:
                    return zjs 
            all_cs_d = gatherResults(all_cs)
            all_cs_d.addCallback(decrypt, pc, zis)
            deferred = all_cs_d
        else:
            zis_deferred = Deferred()
            zis_deferred.callback(zis)
            deferred = zis_deferred

        return deferred


    def _full_mul(self, a, b):
        """Multiply each of the PartialShares in the list *a* with the
        corresponding PartialShare in the list *b*.
        
        Returns a deferred which will yield a list of PartialShares.
        """
        self.runtime.increment_pc()
        
        def do_full_mul(shareContents, result_shares):
            """Share content belonging to ai, bi are at:
            shareContents[i], shareContents[len(shareContents) + i].
            """
            deferreds = []
            len_shares = len(shareContents)

            ais = [shareContent.value for shareContent in shareContents[0:len_shares/2]]
            bis = [shareContent.value for shareContent in shareContents[len_shares/2:]]
            
            b_enc_shares = []
            for inx in self.runtime.players:
                b_enc_shares.append([s.enc_shares[inx - 1]
                                     for s in shareContents[len_shares/2:]])

            values = len(ais) * [0]

            for inx in xrange(0, len(self.runtime.players)):
                for jnx in xrange(0, len(self.runtime.players)):
                    deferreds.append(self._mul(inx + 1,
                                               jnx + 1,
                                               len(ais),
                                               ais,
                                               b_enc_shares[jnx]))
            
            def compute_shares(zils, values, result_shares):
                for zil in zils:
                    for inx, zi in enumerate(zil):
                        values[inx] += zi

                return values
            
            d = gatherResults(deferreds)
            d.addCallback(compute_shares, values, result_shares)
            
            def callBackPartialShareContents(partialShareContents, result_shares):
                for v, s in zip(partialShareContents, result_shares):
                    s.callback(v)
                return None
            
            d.addCallback(lambda values: generate_partial_share_contents(
                    values, self.runtime, self.paillier, self.k, self.zk_random))
            d.addCallback(callBackPartialShareContents, result_shares)
            return d
        result_shares = [PartialShare(self.runtime, self.Zp) for _ in a]
        self.runtime.schedule_callback(gatherResults(a + b),
                                       do_full_mul,
                                       result_shares)
        return result_shares