def invert_by_masking(self, byte): bits = bit_decompose(byte) for j in range(len(bits)): bits[j].addCallback(lambda x: 1 - x) # bits[j] = 1 - bits[j] while len(bits) > 1: bits.append(bits.pop(0) * bits.pop(0)) # b == 1 if byte is 0, b == 0 else b = bits[0] r = Share(self.runtime, GF256) c = Share(self.runtime, GF256) def get_masked_byte(c_opened, r_related, c, r, byte): if c_opened == 0: r_trial = self.runtime.prss_share_random(GF256) c_trial = self.runtime.open((byte + b) * r_trial) self.runtime.schedule_callback(c_trial, get_masked_byte, r_trial, c, r, byte) else: r_related.addCallback(r.callback) c.callback(~c_opened) get_masked_byte(0, None, c, r, byte) # necessary to avoid communication in multiplication # was: return c * r - b result = gather_shares([c, r, b]) result.addCallback(lambda (c, r, b): c * r - b) return result
def mul(self, share_a, share_b): """Multiplication of shares.""" field = getattr(share_a, "field", getattr(share_b, "field", None)) k = self.options.security_parameter n = min(self.player.pubkey['n'], self.peer.pubkey['n']) assert field.modulus ** 2 + 2 ** k < n, \ "Need bigger Paillier keys to multiply." if not isinstance(share_a, Share): share_a = Share(self, field, share_a) if not isinstance(share_b, Share): share_b = Share(self, field, share_b) def finish_mul((a, b)): pc = tuple(self.program_counter) send_data = self.protocols[self.peer.id].sendData if hash(pc) % 2 == self.id: # We play the role of P1. a1, b1 = a, b enc_a1 = encrypt(a1.value, self.player.pubkey) enc_b1 = encrypt(b1.value, self.player.pubkey) send_data(pc, PAILLIER, str(enc_a1)) send_data(pc, PAILLIER, str(enc_b1)) enc_c1 = Share(self, field) self._expect_data(self.peer.id, PAILLIER, enc_c1) c1 = enc_c1.addCallback(decrypt, self.player.seckey) c1.addCallback(lambda c: long(c) + a1 * b1) return c1 else: # We play the role of P2. a2, b2 = a, b enc_a1 = Deferred() self._expect_data(self.peer.id, PAILLIER, enc_a1) enc_a1.addCallback(long) enc_b1 = Deferred() self._expect_data(self.peer.id, PAILLIER, enc_b1) enc_b1.addCallback(long) nsq = self.peer.pubkey['n']**2 # Calculate a1 * b2 and b1 * a2 inside the encryption. enc_a1_b2 = enc_a1.addCallback(pow, b2.value, nsq) enc_b1_a2 = enc_b1.addCallback(pow, a2.value, nsq) # Chose and encrypt r. r = rand.randint(0, 2 * field.modulus**2 + 2**k) enc_r = encrypt(r, self.peer.pubkey) c1 = gatherResults([enc_a1_b2, enc_b1_a2]) c1.addCallback(lambda (a, b): a * b * enc_r) c1.addCallback(lambda c: send_data(pc, PAILLIER, str(c))) c2 = a2 * b2 - r return Share(self, field, c2) result = gather_shares([share_a, share_b]) result.addCallback(finish_mul) return result
def greater_than_equal(self, share_a, share_b): """Compute ``share_a >= share_b``. Both arguments must be from the same field. The result is a :class:`~viff.field.GF256` share. :warning: The result type (:class:`~viff.field.GF256`) is different from the argument types (general field elements). """ field = getattr(share_a, "field", getattr(share_b, "field", None)) if not isinstance(share_a, Share): share_a = Share(self, field, share_a) if not isinstance(share_b, Share): share_b = Share(self, field, share_b) l = self.options.bit_length m = l + self.options.security_parameter t = m + 1 assert 2**(l + 1) + 2**t < field.modulus, "2^(l+1) + 2^t < p must hold" assert self.num_players + 2 < 2**l a = share_a - share_b + 2**l b, bits = self.decomposed_random_sharing(field, m) T = self.open(2**t - b + a) result = gather_shares((T, ) + bits) self.schedule_callback(result, self._finish_greater_than_equal, l) return result
def _exchange_double(self, shares, rvec1, rvec2, T, field, d1, d2): """Exchange and (if possible) verify shares.""" svec1, svec2 = shares pc = tuple(self.program_counter) inputters = range(1, self.num_players + 1) # We send our shares to the verifying players. for offset, (s1, s2) in enumerate(zip(svec1, svec2)): if T + 1 + offset != self.id: self.protocols[T + 1 + offset].sendShare(pc, s1) self.protocols[T + 1 + offset].sendShare(pc, s2) if self.id > T: # The other players will send us their shares of si_1 # and si_2 and we will verify it. si_1 = [] si_2 = [] for peer_id in inputters: if self.id == peer_id: si_1.append(Share(self, field, svec1[peer_id - T - 1])) si_2.append(Share(self, field, svec2[peer_id - T - 1])) else: si_1.append(self._expect_share(peer_id, field)) si_2.append(self._expect_share(peer_id, field)) result = gatherResults([gatherResults(si_1), gatherResults(si_2)]) result.addCallback(self._verify_double, rvec1, rvec2, T, field, d1, d2) return result else: # We cannot verify anything. return (rvec1[:T], rvec2[:T]) # do actual communication self.activate_reactor()
def test_open_does_not_mutate_share(self, runtime): """Test that opening a share does not change it.""" # The parties have shares 43, 44, 45 respectively. share = Share(runtime, self.Zp, self.Zp(42 + runtime.id)) opened = runtime.open(share) opened.addCallback(self.assertEquals, 42) share.addCallback(self.assertEquals, 42 + runtime.id) return opened
def __init__(self, runtime, field, value=None, keyList=None, authentication_codes=None): if value == None and keyList == None and authentication_codes == None: Share.__init__(self, runtime, field) else: Share.__init__(self, runtime, field, BeDOZaShareContents(value, keyList, authentication_codes))
def __init__(self, runtime, field, value=None, enc_shares=None): if value == None and enc_shares == None: Share.__init__(self, runtime, field) else: N_squared_list = [ runtime.players[player_id].pubkey['n_square'] for player_id in runtime.players.keys()] partial_share_contents = PartialShareContents(value, enc_shares, N_squared_list) Share.__init__(self, runtime, field, partial_share_contents)
def _test_binop(self, runtime, op): a, b = 12345, 34567 share_a = Share(runtime, self.Zp, self.Zp(a + runtime.id)) share_b = Share(runtime, self.Zp, self.Zp(b - runtime.id)) expected = self.Zp(op(a, b)) result = op(share_a, share_b) opened = runtime.open(result) opened.addCallback(self.assertEquals, expected) return opened
def check(triple): a, b, c = triple self.assert_type(a, self.Zp) self.assert_type(b, self.Zp) self.assert_type(c, self.Zp) open_a = runtime.open(Share(self, self.Zp, a)) open_b = runtime.open(Share(self, self.Zp, b)) open_c = runtime.open(Share(self, self.Zp, c)) result = gatherResults([open_a, open_b, open_c]) result.addCallback(verify) return result
def __init__(self, runtime, field, value=None, enc_shares=None): if value == None and enc_shares == None: Share.__init__(self, runtime, field) else: N_squared_list = [ runtime.players[player_id].pubkey['n_square'] for player_id in runtime.players.keys() ] partial_share_contents = PartialShareContents( value, enc_shares, N_squared_list) Share.__init__(self, runtime, field, partial_share_contents)
def __init__(self, runtime, field, value=None, keyList=None, authentication_codes=None): if value == None and keyList == None and authentication_codes == None: Share.__init__(self, runtime, field) else: Share.__init__( self, runtime, field, BeDOZaShareContents(value, keyList, authentication_codes))
def sub(self, a, b): """Subtraction of a and b. Communication cost: none. """ if not isinstance(a, Share): a = Share(self, b.field, a) if not isinstance(b, Share): b = Share(self, a.field, b) yield declareReturn(self, a.field) a, b = yield a, b returnValue(a - b)
def sub(self, share_a, share_b): """Subtraction of shares. Communication cost: none. """ field = getattr(share_a, "field", getattr(share_b, "field", None)) if not isinstance(share_a, Share): share_a = Share(self, field, share_a) if not isinstance(share_b, Share): share_b = Share(self, field, share_b) result = gather_shares([share_a, share_b]) result.addCallback(lambda (a, b): a - b) return result
def _finish_greater_than_equal(self, results, l): """Finish the calculation.""" T = results[0] bit_bits = results[1:] vec = [(GF256(0), GF256(0))] # Calculate the vector, using only the first l bits for i, bi in enumerate(bit_bits[:l]): Ti = GF256(T.bit(i)) ci = Share(self, GF256, bi ^ Ti) vec.append((ci, Ti)) # Reduce using the diamond operator. We want to do as much # as possible in parallel while being careful not to # switch the order of elements since the diamond operator # is non-commutative. while len(vec) > 1: tmp = [] while len(vec) > 1: tmp.append(self._diamond(vec.pop(0), vec.pop(0))) if len(vec) == 1: tmp.append(vec[0]) vec = tmp return GF256(T.bit(l)) ^ (bit_bits[l] ^ vec[0][1])
def share(self, inputters, field, number=None): """Share *number* additively.""" assert number is None or self.id in inputters results = [] for peer_id in inputters: # Unique program counter per input. self.increment_pc() if peer_id == self.id: a = field(rand.randint(0, field.modulus - 1)) b = number - a results.append(Share(self, a.field, a)) pc = tuple(self.program_counter) self.protocols[self.peer.id].sendShare(pc, b) else: share = self._expect_share(peer_id, field) results.append(share) # Unpack a singleton list. if len(results) == 1: return results[0] else: return results
def prss_share_random(self, field): """Generate a share of a uniformly random element.""" prfs = self.players[self.id].prfs(field.modulus) # There can only be one PRF in the dictionary. prf = prfs.values()[0] share = field(prf(tuple(self.program_counter))) return Share(self, field, share)
def argmax(xs, gte_max): n = len(xs) if n==1: return (xs[0], Share(tv, Zp, Zp(0))) max0, i0 = argmax(xs[:n/2], gte_max) max1, i1 = argmax(xs[n/2:], gte_max) b, max = gte_max(max0, max1) return max, i0 + b * (n/2 + i1 - i0)
def test_key_expansion(self, runtime): aes = AES(runtime, 256, quiet=True) key = [] ascii_key = [] for i in xrange(8): key.append([]) for j in xrange(4): b = 15 * i + j key[i].append(Share(runtime, GF256, GF256(b))) ascii_key.append(chr(b)) result = aes.key_expansion(key) r = rijndael(ascii_key) expected_result = [] for round_key in r.Ke: for word in round_key: split_word = [] expected_result.append(split_word) for j in xrange(4): split_word.insert(0, word % 256) word /= 256 self.verify(runtime, result, expected_result)
def __init__(self, runtime): self.k = 4 self.runtime = runtime self.ramdom_shares = [ 0 for i in range(self.k * int(math.log(self.k, 2))) ] self.triggers = [ Share(self.runtime, Zp) for i in range(self.k * int(math.log(self.k, 2))) ] self.p = find_prime(2**256, blum=True) print self.p for i in range(self.k * int(math.log(self.k, 2))): r = runtime.prss_share_random(Zp) u = r * r open_u = runtime.open(u) open_u.addCallback(self.calculate_share, r, i) list = [ self.triggers[i] for i in range(self.k * int(math.log(self.k, 2))) ] result = gather_shares(list) result.addCallback(self.preprocess_ready)
def bit_decompose(share, use_lin_comb=True): """Bit decomposition for GF256 shares.""" assert isinstance(share, Share) and share.field == GF256, \ "Parameter must be GF256 share." r_bits = share.runtime.prss_share_random_multi(GF256, 8, binary=True) if use_lin_comb: r = share.runtime.lin_comb([2 ** i for i in range(8)], r_bits) else: r = sum([r_bits[i] * 2 ** i for i in range(8)]) c = share.runtime.open(share + r) c_bits = [Share(share.runtime, GF256) for i in range(8)] def decompose(byte, bits): value = byte.value for i in range(8): c_bits[i].callback(GF256(value & 1)) value >>= 1 c.addCallback(decompose, c_bits) return [c_bits[i] + r_bits[i] for i in range(8)]
def _exchange_single(self, svec, rvec, T, field, degree): """Exchange and (if possible) verify shares.""" pc = tuple(self.program_counter) inputters = range(1, self.num_players + 1) # We send our shares to the verifying players. for offset, share in enumerate(svec): if T + 1 + offset != self.id: self.protocols[T + 1 + offset].sendShare(pc, share) if self.id > T: # The other players will send us their shares of si_1 # and si_2 and we will verify it. si = [] for peer_id in inputters: if self.id == peer_id: si.append(Share(self, field, svec[peer_id - T - 1])) else: si.append(self._expect_share(peer_id, field)) result = gatherResults(si) result.addCallback(self._verify_single, rvec, T, field, degree) return result else: # We cannot verify anything. return rvec[:T] # do actual communication self.activate_reactor()
def shamir_share(self, inputters, field, number=None, threshold=None): """Secret share *number* over *field* using Shamir's method. The number is shared using polynomial of degree *threshold* (defaults to :attr:`threshold`). Returns a list of shares unless there is only one inputter in which case the share is returned directly. In code it is used like this:: a, b, c = runtime.shamir_share([1, 2, 3], Zp, x) where ``Zp`` is a field and ``x`` is a Python integer holding the input of each player (three inputs in total). If only a subset of the players provide input it looks like this:: if runtime.id == 1: a = runtime.shamir_share([1], Zp, x) else: a = runtime.shamir_share([1], Zp) Instead of branching when calling :meth:`shamir_share`, one can give ``None`` as input:: if runtime.id == 1: x = int(raw_input("Input x: ")) else: x = None a = runtime.shamir_share([1], Zp, x) which might be practical in some cases. Communication cost: n elements transmitted. """ assert number is None or self.id in inputters if threshold is None: threshold = self.threshold results = [] for peer_id in inputters: # Unique program counter per input. self.increment_pc() if peer_id == self.id: shares = shamir.share(field(number), threshold, len(self.players)) for other_id, share in shares: if other_id.value == self.id: results.append(Share(self, field, share)) else: self._send_share(other_id.value, share) else: results.append(self._expect_share(peer_id, field)) # Unpack a singleton list. if len(results) == 1: return results[0] else: return results
def _get_triple(self, field): results = [Share(self, field) for i in range(3)] def chain(triple, results): for i, result in zip(triple, results): result.callback(i) self.random_triple(field, 1)[0].addCallbacks(chain, self.error_handler, (results,)) return results
def exchange(share): # Send share to all receivers. for peer_id in receivers: if peer_id != self.id: pc = tuple(self.program_counter) self.protocols[peer_id].sendShare(pc, share) # Receive and recombine shares if this player is a receiver. if self.id in receivers: deferreds = [] for peer_id in self.players: if peer_id == self.id: d = Share(self, share.field, (share.field(peer_id), share)) else: d = self._expect_share(peer_id, share.field) d.addCallback(lambda s, peer_id: (s.field(peer_id), s), peer_id) deferreds.append(d) return recombine(deferreds)
def test_all_players_receive_implicit(self, runtime): """Shamir share and open Zp(42).""" # The parties have shares 43, 44, 45 respectively. share = Share(runtime, self.Zp, self.Zp(42 + runtime.id)) opened = runtime.open(share) self.assert_type(opened, Share) opened.addCallback(self.assertEquals, 42) return opened
def vc_read_additive_shares(runtime, Zp, blockname, inputters): allvals = map(vc_read_values, [ vc_directory + "geppblk." + blockname + "." + str(curid) + my_suffix for curid in inputters ]) vals = map(lambda x: Share(runtime, Zp, Zp(sum(x) % vc_modulus)), zip(*allvals)) #print "vals", vals return vals
def add_and_multiply(masked_byte, random_powers, prep): masked_powers = self.runtime.powerchain(masked_byte, 7) byte_powers = map(operator.add, masked_powers, random_powers)[1:] if prep: byte_powers = [Share(self.runtime, GF256, value) for value in byte_powers] while len(byte_powers) > 1: byte_powers.append(byte_powers.pop(0) * byte_powers.pop(0)) return byte_powers[0]
def preprocess(self, input): if isinstance(input, str): return [Share(self.runtime, GF256, GF256(ord(c))) for c in input] else: for byte in input: assert byte.field == GF256, \ "Input must be a list of GF256 elements " \ "or of shares thereof." return input
def vc_input_predist(runtime, blockname): """ Import inputs that were pre-distributed via vc_share_offline. Returns VcShares. """ vals = vc_read_values(vc_directory + "geppblk." + blockname + my_suffix) importer = lambda x: VcShare.from_share( runtime, Share(runtime, runtime.Zp, runtime.Zp(x))) vcs = map(importer, vals[:-1]) rnd1 = importer(vals[-1]) (qp, bn, _, _, _) = vc_declare_block(runtime, vcs, rnd1) print >> qap, "[input]", qp, bn, blockname return vcs
def greater_than_equal(self, share_a, share_b): """Compute ``share_a >= share_b``. Both arguments must be shares from the same field. The result is a new 0/1 share from the field. """ # TODO: Make all input-taking methods do coercion like this. field = getattr(share_a, "field", getattr(share_b, "field", None)) if not isinstance(share_a, Share): if not isinstance(share_a, FieldElement): share_a = field(share_a) share_a = Share(self, field, share_a) if not isinstance(share_b, Share): if not isinstance(share_b, FieldElement): share_b = field(share_b) share_b = Share(self, field, share_b) preproc = self.greater_than_equal_preproc(field) return self.greater_than_equal_online(share_a, share_b, preproc, field)
def finish(square, share, binary): if square == 0: # We were unlucky, try again... return self.prss_share_random(field, binary) else: # We can finish the calculation root = square.sqrt() # When the root is computed, we divide the share and # convert the resulting -1/1 share into a 0/1 share. return Share(self, field, (share / root + 1) / 2)
def finish_mul((a, b)): pc = tuple(self.program_counter) send_data = self.protocols[self.peer.id].sendData if hash(pc) % 2 == self.id: # We play the role of P1. a1, b1 = a, b enc_a1 = encrypt(a1.value, self.player.pubkey) enc_b1 = encrypt(b1.value, self.player.pubkey) send_data(pc, PAILLIER, str(enc_a1)) send_data(pc, PAILLIER, str(enc_b1)) enc_c1 = Share(self, field) self._expect_data(self.peer.id, PAILLIER, enc_c1) c1 = enc_c1.addCallback(decrypt, self.player.seckey) c1.addCallback(lambda c: long(c) + a1 * b1) return c1 else: # We play the role of P2. a2, b2 = a, b enc_a1 = Deferred() self._expect_data(self.peer.id, PAILLIER, enc_a1) enc_a1.addCallback(long) enc_b1 = Deferred() self._expect_data(self.peer.id, PAILLIER, enc_b1) enc_b1.addCallback(long) nsq = self.peer.pubkey['n']**2 # Calculate a1 * b2 and b1 * a2 inside the encryption. enc_a1_b2 = enc_a1.addCallback(pow, b2.value, nsq) enc_b1_a2 = enc_b1.addCallback(pow, a2.value, nsq) # Chose and encrypt r. r = rand.randint(0, 2 * field.modulus**2 + 2**k) enc_r = encrypt(r, self.peer.pubkey) c1 = gatherResults([enc_a1_b2, enc_b1_a2]) c1.addCallback(lambda (a,b): a * b * enc_r) c1.addCallback(lambda c: send_data(pc, PAILLIER, str(c))) c2 = a2 * b2 - r return Share(self, field, c2)
def finish_mul((a, b)): pc = tuple(self.program_counter) send_data = self.protocols[self.peer.id].sendData if hash(pc) % 2 == self.id: # We play the role of P1. a1, b1 = a, b enc_a1 = encrypt(a1.value, self.player.pubkey) enc_b1 = encrypt(b1.value, self.player.pubkey) send_data(pc, PAILLIER, str(enc_a1)) send_data(pc, PAILLIER, str(enc_b1)) enc_c1 = Share(self, field) self._expect_data(self.peer.id, PAILLIER, enc_c1) c1 = enc_c1.addCallback(decrypt, self.player.seckey) c1.addCallback(lambda c: long(c) + a1 * b1) return c1 else: # We play the role of P2. a2, b2 = a, b enc_a1 = Deferred() self._expect_data(self.peer.id, PAILLIER, enc_a1) enc_a1.addCallback(long) enc_b1 = Deferred() self._expect_data(self.peer.id, PAILLIER, enc_b1) enc_b1.addCallback(long) nsq = self.peer.pubkey['n']**2 # Calculate a1 * b2 and b1 * a2 inside the encryption. enc_a1_b2 = enc_a1.addCallback(pow, b2.value, nsq) enc_b1_a2 = enc_b1.addCallback(pow, a2.value, nsq) # Chose and encrypt r. r = rand.randint(0, 2 * field.modulus**2 + 2**k) enc_r = encrypt(r, self.peer.pubkey) c1 = gatherResults([enc_a1_b2, enc_b1_a2]) c1.addCallback(lambda (a, b): a * b * enc_r) c1.addCallback(lambda c: send_data(pc, PAILLIER, str(c))) c2 = a2 * b2 - r return Share(self, field, c2)
def prss_share(self, inputters, field, element=None): """Creates pseudo-random secret sharings. This protocol creates a secret sharing for each player in the subset of players specified in *inputters*. Each inputter provides an integer. The result is a list of shares, one for each inputter. The protocol uses the pseudo-random secret sharing technique described in the paper "Share Conversion, Pseudorandom Secret-Sharing and Applications to Secure Computation" by Ronald Cramer, Ivan Damgård, and Yuval Ishai in Proc. of TCC 2005, LNCS 3378. `Download <http://www.cs.technion.ac.il/~yuvali/pubs/CDI05.ps>`__ Communication cost: Each inputter does one broadcast. """ # Verifying parameters. if element is None: assert self.id not in inputters, "No element given." else: assert self.id in inputters, \ "Element given, but we are not sharing?" n = self.num_players # Key used for PRSS. key = self.prss_key() # The shares for which we have all the keys. all_shares = [] # Shares we calculate from doing PRSS with the other players. tmp_shares = {} prfs = self.players[self.id].dealer_prfs(field.modulus) # Compute and broadcast correction value. if self.id in inputters: for player in self.players: share = prss(n, player, field, prfs[self.id], key) all_shares.append((field(player), share)) shared = shamir.recombine(all_shares[:self.threshold+1]) correction = element - shared # if this player is inputter then broadcast correction value # TODO: more efficient broadcast? pc = tuple(self.program_counter) for peer_id in self.players: if self.id != peer_id: self.protocols[peer_id].sendShare(pc, correction) # Receive correction value from inputters and compute share. result = [] for player in inputters: tmp_shares[player] = prss(n, self.id, field, prfs[player], key) if player == self.id: d = Share(self, field, correction) else: d = self._expect_share(player, field) d.addCallback(lambda c, s: s + c, tmp_shares[player]) result.append(d) # Unpack a singleton list. if len(result) == 1: return result[0] else: return result
def __init__(self, runtime, field, value=None, rho=None, commitment=None): self.share = value self.rho = rho self.commitment = commitment Share.__init__(self, runtime, field, (value, rho, commitment))
def shift(self, inputters, field, number=None): """Shift of a share. Useful for input. Communication cost: ???. Assume the parties are given a random share ``[r]`` by a trusted dealer. Then we denote the following protocol but ``[x] = Shift(P_i, x, [r])``. 1. ``r = OpenTo(P_i, [r])`` 2. ``P_i broadcasts Delta = r - x`` 3. ``[x] = [r] - Delta`` """ # TODO: Communitcation costs? assert (self.id in inputters and number is not None) or (self.id not in inputters) self.increment_pc() results = [] def hack(_, peer_id): # Assume the parties are given a random share [r] by a # trusted dealer. share_r = self.random_share(field) # 1. r = OpenTo(P_i, [r]) open_r = self.open(share_r, [peer_id]) def subtract_delta(delta, share_r): delta = field(long(delta)) x = self.sub(share_r, delta) return x if peer_id == self.id: def g(r, x): delta = r - x delta = self.broadcast([peer_id], self.players.keys(), str(delta.value)) self.schedule_callback(delta, subtract_delta, share_r) delta.addErrback(self.error_handler) return delta self.schedule_callback(open_r, g, number) open_r.addErrback(self.error_handler) return open_r else: d = Deferred() def g(_, peer_id, share_r): delta = self.broadcast([peer_id], self.players.keys()) self.schedule_callback(delta, subtract_delta, share_r) delta.addErrback(self.error_handler) return delta self.schedule_callback(d, g, peer_id, share_r) d.addErrback(self.error_handler) d.callback(None) return d for peer_id in inputters: s = Share(self, field) self.schedule_callback(s, hack, peer_id) s.addErrback(self.error_handler) s.callback(None) results.append(s) # do actual communication self.activate_reactor() if len(results) == 1: return results[0] return results