/rev/ed2d02202af0
changeset: 1261:ed2d02202af0
user:      Marcel Keller <[email protected]>
date:      Thu Oct 08 14:27:37 2009 +0200
summary:   Merged with Janus.

diffstat:

 apps/benchmark.py                 |   213 +++---
 viff/active.py                    |     8 +-
 viff/constants.py                 |    34 +
 viff/hash_broadcast.py            |   167 +++++
 viff/orlandi.py                   |  1314 
++++++++++++++++++++++++++++++++++++++++++
 viff/paillier.py                  |     2 +-
 viff/passive.py                   |     1 +
 viff/runtime.py                   |    82 ++-
 viff/test/test_hash_broadcast.py  |   181 +++++
 viff/test/test_orlandi_runtime.py |   825 ++++++++++++++++++++++++++
 viff/test/test_runtime.py         |    46 +-
 viff/test/util.py                 |    10 +-
 12 files changed, 2765 insertions(+), 118 deletions(-)

diffs (truncated from 3167 to 800 lines):

diff -r 824ae3770456 -r ed2d02202af0 apps/benchmark.py
--- a/apps/benchmark.py Wed Oct 07 15:23:12 2009 +0200
+++ b/apps/benchmark.py Thu Oct 08 14:27:37 2009 +0200
@@ -63,6 +63,7 @@
 import viff.reactor
 viff.reactor.install()
 from twisted.internet import reactor
+from twisted.internet.defer import Deferred
 
 from viff.field import GF, GF256, FakeGF
 from viff.runtime import Runtime, create_runtime, gather_shares, \
@@ -87,25 +88,38 @@
     print "Started", what
 
 
-def record_stop(_, what):
+def record_stop(x, what):
     stop = time.time()
     print
     print "Total time used: %.3f sec" % (stop-start)
     print "Time per %s operation: %.0f ms" % (what, 1000*(stop-start) / count)
     print "*" * 6
+    return x
 
+operations = {"mul": (operator.mul,[]),
+              "compToft05": (operator.ge, [ComparisonToft05Mixin]),
+              "compToft07": (operator.ge, [ComparisonToft07Mixin]),
+              "eq": (operator.eq, [ProbabilisticEqualityMixin])}
 
-operations = ["mul", "compToft05", "compToft07", "eq"]
+runtimes = {"PassiveRuntime": PassiveRuntime,
+            "PaillierRuntime": PaillierRuntime, 
+            "BasicActiveRuntime": BasicActiveRuntime}
+
+mixins = {"TriplesHyperinvertibleMatricesMixin" : 
TriplesHyperinvertibleMatricesMixin, 
+          "TriplesPRSSMixin": TriplesPRSSMixin, 
+          "ComparisonToft05Mixin": ComparisonToft05Mixin,
+          "ComparisonToft07Mixin": ComparisonToft07Mixin, 
+          "ProbabilisticEqualityMixin": ProbabilisticEqualityMixin}
 
 parser = OptionParser(usage="Usage: %prog [options] config_file")
 parser.add_option("-m", "--modulus",
                   help="lower limit for modulus (can be an expression)")
-parser.add_option("-a", "--active", action="store_true",
-                  help="use actively secure runtime")
-parser.add_option("--passive", action="store_false", dest="active",
-                  help="use passively secure runtime")
-parser.add_option("-2", "--twoplayer", action="store_true",
-                  help="use twoplayer runtime")
+parser.add_option("-r", "--runtime", type="choice", choices=runtimes.keys(),
+                  help="the name of the basic runtime to test")
+parser.add_option("-n", "--num_players", action="store_true", 
dest="num_players",
+                  help="number of players")
+parser.add_option("--mixins", type="string",
+                  help="operation to benchmark")
 parser.add_option("--prss", action="store_true",
                   help="use PRSS for preprocessing")
 parser.add_option("--hyper", action="store_false", dest="prss",
@@ -114,7 +128,7 @@
                   help="corruption threshold")
 parser.add_option("-c", "--count", type="int",
                   help="number of operations")
-parser.add_option("-o", "--operation", type="choice", choices=operations,
+parser.add_option("-o", "--operation", type="choice", 
choices=operations.keys(),
                   help="operation to benchmark")
 parser.add_option("-p", "--parallel", action="store_true",
                   help="execute operations in parallel")
@@ -122,10 +136,12 @@
                   help="execute operations in sequence")
 parser.add_option("-f", "--fake", action="store_true",
                   help="skip local computations using fake field elements")
+parser.add_option("--args", type="string",
+                  help="additional arguments to the runtime, the format is a 
comma separated list of id=value pairs e.g. --args s=1,d=0,lambda=1")
 
 parser.set_defaults(modulus=2**65, threshold=1, count=10,
-                    active=False, twoplayer=False, prss=True,
-                    operation=operations[0], parallel=True, fake=False)
+                    runtime=runtimes.keys()[0], mixins="", num_players=2, 
prss=True,
+                    operation=operations.keys()[0], parallel=True, fake=False)
 
 # Add standard VIFF options.
 Runtime.add_options(parser)
@@ -158,55 +174,46 @@
 class Benchmark:
 
     def __init__(self, rt, operation):
+        print "init"
         self.rt = rt
         self.operation = operation
-        self.sync_preprocess()
-
-    def sync_preprocess(self):
-        print "Synchronizing preprocessing"
+        self.pc = None
         sys.stdout.flush()
         sync = self.rt.synchronize()
+        self.doTest(sync, lambda x: x)
         self.rt.schedule_callback(sync, self.preprocess)
+        self.doTest(sync, lambda x: self.rt.shutdown())
+        
+#     def sync_preprocess(self):
+#         print "Synchronizing preprocessing"
+#         sys.stdout.flush()
+#         sync = self.rt.synchronize()
+#         self.rt.schedule_callback(sync, self.preprocess)
 
-    def preprocess(self, _):
-        program_desc = {}
-
-        if isinstance(self.rt, BasicActiveRuntime):
-            # TODO: Make this optional and maybe automatic. The
-            # program descriptions below were found by carefully
-            # studying the output reported when the benchmarks were
-            # run with no preprocessing. So they are quite brittle.
-            if self.operation == operator.mul:
-                key = ("generate_triples", (Zp,))
-                desc = [(i, 1, 0) for i in range(3, 3 + count)]
-                program_desc.setdefault(key, []).extend(desc)
-            elif isinstance(self.rt, ComparisonToft05Mixin):
-                key = ("generate_triples", (GF256,))
-                desc = sum([[(c, 64, i, 1, 1, 0) for i in range(2, 33)] +
-                            [(c, 64, i, 3, 1, 0) for i in range(17, 33)]
-                            for c in range(3 + 2*count, 3 + 3*count)],
-                           [])
-                program_desc.setdefault(key, []).extend(desc)
-            elif isinstance(self.rt, ComparisonToft07Mixin):
-                key = ("generate_triples", (Zp,))
-                desc = sum([[(c, 2, 4, i, 2, 1, 0) for i in range(1, 33)] +
-                            [(c, 2, 4, 99, 2, 1, 0)] +
-                            [(c, 2, 4, i, 1, 0) for i in range(65, 98)]
-                            for c in range(3 + 2*count, 3 + 3*count)],
-                           [])
-                program_desc.setdefault(key, []).extend(desc)
-
-        if program_desc:
+    def preprocess(self, needed_data):
+        print "Preprocess", needed_data
+        if needed_data:
             print "Starting preprocessing"
             record_start("preprocessing")
-            preproc = self.rt.preprocess(program_desc)
+            preproc = self.rt.preprocess(needed_data)
             preproc.addCallback(record_stop, "preprocessing")
-            self.rt.schedule_callback(preproc, self.begin)
+            return preproc
         else:
             print "Need no preprocessing"
-            self.begin(None)
+            return None
+
+    def doTest(self, d, termination_function):
+        print "doTest", self.rt.program_counter
+        self.rt.schedule_callback(d, self.begin)
+        self.rt.schedule_callback(d, self.sync_test)
+#         self.rt.schedule_callback(d, self.countdown, 3)
+        self.rt.schedule_callback(d, self.run_test)
+        self.rt.schedule_callback(d, self.sync_test)
+        self.rt.schedule_callback(d, self.finished, termination_function)
+        return d
 
     def begin(self, _):
+        print "begin", self.rt.program_counter
         print "Runtime ready, generating shares"
         self.a_shares = []
         self.b_shares = []
@@ -220,43 +227,49 @@
             self.a_shares.append(self.rt.input([inputter], Zp, a))
             self.b_shares.append(self.rt.input([inputter], Zp, b))
         shares_ready = gather_shares(self.a_shares + self.b_shares)
-        self.rt.schedule_callback(shares_ready, self.sync_test)
+        return shares_ready
 
-    def sync_test(self, _):
+    def sync_test(self, x):
         print "Synchronizing test start."
         sys.stdout.flush()
         sync = self.rt.synchronize()
-        self.rt.schedule_callback(sync, self.countdown, 3)
+        self.rt.schedule_callback(sync, lambda y: x)
+        return sync
 
-    def countdown(self, _, seconds):
-        if seconds > 0:
-            print "Starting test in %d" % seconds
-            sys.stdout.flush()
-            reactor.callLater(1, self.countdown, None, seconds - 1)
-        else:
-            print "Starting test now"
-            sys.stdout.flush()
-            self.run_test(None)
+#     def countdown(self, _, seconds):
+#         if seconds > 0:
+#             print "Starting test in %d" % seconds
+#             sys.stdout.flush()
+#             reactor.callLater(1, self.countdown, None, seconds - 1)
+#         else:
+#             print "Starting test now"
+#             sys.stdout.flush()
+#             self.run_test(None)
 
     def run_test(self, _):
         raise NotImplemented("Override this abstract method in a sub class.")
 
-    def finished(self, _):
+    def finished(self, needed_data, termination_function):
         sys.stdout.flush()
 
         if self.rt._needed_data:
             print "Missing pre-processed data:"
-            for (func, args), pcs in self.rt._needed_data.iteritems():
+            for (func, args), pcs in needed_data.iteritems():
                 print "* %s%s:" % (func, args)
                 print "  " + pformat(pcs).replace("\n", "\n  ")
 
-        self.rt.shutdown()
+        return termination_function(needed_data)
 
 # This class implements a benchmark where run_test executes all
 # operations in parallel.
 class ParallelBenchmark(Benchmark):
 
-    def run_test(self, _):
+    def run_test(self, shares):
+        print "rt", self.rt.program_counter, self.pc
+        if self.pc != None:
+            self.rt.program_counter = self.pc
+        else:
+            self.pc = list(self.rt.program_counter)
         c_shares = []
         record_start("parallel test")
         while self.a_shares and self.b_shares:
@@ -266,60 +279,51 @@
 
         done = gather_shares(c_shares)
         done.addCallback(record_stop, "parallel test")
-        self.rt.schedule_callback(done, self.finished)
+        def f(x):
+            needed_data = self.rt._needed_data
+            self.rt._needed_data = {}
+            return needed_data
+        done.addCallback(f)
+        return done
+
 
 # A benchmark where the operations are executed one after each other.
 class SequentialBenchmark(Benchmark):
 
-    def run_test(self, _):
+    def run_test(self, _, termination_function, d):
         record_start("sequential test")
-        self.single_operation(None)
+        self.single_operation(None, termination_function)
 
-    def single_operation(self, _):
+    def single_operation(self, _, termination_function):
         if self.a_shares and self.b_shares:
             a = self.a_shares.pop()
             b = self.b_shares.pop()
             c = self.operation(a, b)
-            self.rt.schedule_callback(c, self.single_operation)
+            self.rt.schedule_callback(c, self.single_operation, 
termination_function)
         else:
             record_stop(None, "sequential test")
-            self.finished(None)
+            self.finished(None, termination_function)
 
-mixins = []
-if options.twoplayer:
-    # Then there is just one possible runtime:
-    operation = operator.mul
-    base_runtime_class = PaillierRuntime
-else:
-    # There are several options for a multiplayer runtime:
-    if options.active:
-        base_runtime_class = BasicActiveRuntime
-        if options.prss:
-            mixins.append(TriplesPRSSMixin)
-        else:
-            mixins.append(TriplesHyperinvertibleMatricesMixin)
-    else:
-        base_runtime_class = PassiveRuntime
+# Identify the base runtime class.
+base_runtime_class = runtimes[options.runtime]
 
-    if options.operation == "mul":
-        operation = operator.mul
-    elif options.operation == "compToft05":
-        operation = operator.ge
-        mixins.append(ComparisonToft05Mixin)
-    elif options.operation == "compToft07":
-        operation = operator.ge
-        mixins.append(ComparisonToft07Mixin)
-    elif options.operation == "eq":
-        operation = operator.eq
-        mixins.append(ProbabilisticEqualityMixin)
+# Identify the additional mixins.
+actual_mixins = []
+if options.mixins != "":
+    actual_mixins = [mixins[mixin] for mixin in options.mixins.split(',')]
+
+
+# Identify the operation and it mixin dependencies.
+operation = operations[options.operation][0]
+actual_mixins += operations[options.operation][1]
 
 print "Using the base runtime: %s." % base_runtime_class
-if mixins:
+if actual_mixins:
     print "With the following mixins:"
-    for mixin in mixins:
+    for mixin in actual_mixins:
         print "- %s" % mixin
 
-runtime_class = make_runtime_class(base_runtime_class, mixins)
+runtime_class = make_runtime_class(base_runtime_class, actual_mixins)
 
 if options.parallel:
     benchmark = ParallelBenchmark
@@ -328,6 +332,19 @@
 
 pre_runtime = create_runtime(id, players, options.threshold,
                              options, runtime_class)
+
+def update_args(runtime, options):
+    args = {}
+    if options.args != "":
+        for arg in options.args.split(','):
+            id, value = arg.split('=')
+            args[id] = long(value)
+        runtime.setArgs(args)
+    return runtime
+
+
+pre_runtime.addCallback(update_args, options)
+
 pre_runtime.addCallback(benchmark, operation)
 
 print "#### Starting reactor ###"
diff -r 824ae3770456 -r ed2d02202af0 viff/active.py
--- a/viff/active.py    Wed Oct 07 15:23:12 2009 +0200
+++ b/viff/active.py    Thu Oct 08 14:27:37 2009 +0200
@@ -28,7 +28,7 @@
 from viff.matrix import Matrix, hyper
 from viff.passive import PassiveRuntime
 from viff.runtime import Share, preprocess, gather_shares
-from viff.runtime import ECHO, READY, SEND
+from viff.constants import ECHO, READY, SEND
 
 
 class BrachaBroadcastMixin:
@@ -378,11 +378,11 @@
     def get_triple(self, field):
         # This is a waste, but this function is only called if there
         # are no pre-processed triples left.
-        count, result = self.generate_triples(field)
+        count, result = self.generate_triples(field, quantity=1)
         result.addCallback(lambda triples: triples[0])
         return result
 
-    def generate_triples(self, field):
+    def generate_triples(self, field, quantity):
         """Generate multiplication triples.
 
         These are random numbers *a*, *b*, and *c* such that ``c =
@@ -436,6 +436,8 @@
         Returns a tuple with the number of triples generated and a
         Deferred which will yield a singleton-list with a 3-tuple.
         """
+        quantity = min(quantity, 20)
+
         a_t = self.prss_share_random_multi(field, quantity)
         b_t = self.prss_share_random_multi(field, quantity)
         r_t, r_2t = self.prss_double_share(field, quantity)
diff -r 824ae3770456 -r ed2d02202af0 viff/constants.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/viff/constants.py Thu Oct 08 14:27:37 2009 +0200
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2009 VIFF Development Team.
+#
+# This file is part of VIFF, the Virtual Ideal Functionality Framework.
+#
+# VIFF is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License (LGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# VIFF is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with VIFF. If not, see <http://www.gnu.org/licenses/>.
+
+__docformat__ = "restructuredtext"
+
+# Constants used for communication.
+SHARE    = 0
+ECHO     = 1
+READY    = 2
+SEND     = 3
+PAILLIER = 4
+TEXT     = 5
+
+# Used by the HashBroadcastMixin
+INCONSISTENTHASH = 6
+OK               = 7
+HASH             = 8
+SIGNAL           = 9
diff -r 824ae3770456 -r ed2d02202af0 viff/hash_broadcast.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/viff/hash_broadcast.py    Thu Oct 08 14:27:37 2009 +0200
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+
+# Copyright 2009 VIFF Development Team.
+#
+# This file is part of VIFF, the Virtual Ideal Functionality Framework.
+#
+# VIFF is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License (LGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# VIFF is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with VIFF. If not, see <http://www.gnu.org/licenses/>.
+
+from twisted.internet.defer import gatherResults, Deferred, DeferredList
+
+from hashlib import sha1
+from viff.constants import TEXT, INCONSISTENTHASH, OK, HASH, SIGNAL
+
+error_msg = "Player %i, has received an inconsistent hash %s."
+
+class InconsistentHashException(Exception):
+    pass
+
+class HashBroadcastMixin:
+    """A non-consistent broadcast scheme mainly useful for full threshold 
security.
+
+    A value is send using `send_value` and when received a hash is generated 
and
+    exchanged among the receivers. If a receiver receives a hash which is not 
equal
+    to the one he generated, then he sends an error signal to the others and 
+    they stop the computation. Else he sends an ok signal and the computation 
continues."""
+
+    def _send_message(self, pc, sender, receivers, message):
+        for peer_id in receivers:
+            self.protocols[peer_id].sendData(pc, TEXT, message)
+
+    def _receive_broadcast(self, pc, unique_pc, sender, receivers):
+        # The result.
+        result = Deferred()
+        # The message store.
+        message = []
+        # The hash store
+        g_hashes = {}
+        # The signal store
+        signals = {}
+
+        def signal_received(signal, peer_id, message, num_receivers, hashes, 
signals):
+            # Store the signal.
+            signals[peer_id] = long(signal)
+            # If all signals are received then check if they are OK or 
INCONSISTENTHASH.
+            if num_receivers == len(signals.keys()):
+                s = reduce(lambda x, y: OK if OK == y else INCONSISTENTHASH, 
signals.values())
+                if OK == s:
+                    # Make the result ready.
+                    result.callback(message[0])
+                else:
+                    raise InconsistentHashException(error_msg % (self.id, 
hashes))
+
+        def hash_received(h, unique_pc, peer_id, receivers, a_hashes):
+            # Store the hash.
+            a_hashes[peer_id] = h
+            # If we have received a hash from everybody, then compute the 
signal and send it.
+            if len(receivers) == len(a_hashes.keys()):
+                signal = OK
+                # First we check if the hashes we received are equal to the 
hash we computed ourselves.
+                for peer_id in receivers:
+                    signal = signal if a_hashes[peer_id] == a_hashes[self.id] 
else INCONSISTENTHASH
+                # Then we send the SAME signal to everybody. 
+                for peer_id in receivers:
+                    self.protocols[peer_id].sendData(unique_pc, SIGNAL, 
str(signal))           
+            # The return value does not matter.
+            return None
+
+        def message_received(m, unique_pc, message, receivers, hashes):
+            # Store the message.
+            message.append(m)
+            # Compute hash of message.
+            h = sha1(m).hexdigest()
+            # Store hash.
+            hashes[self.id] = h
+            # Send the hash to all receivers.
+            for peer_id in receivers:
+                self.protocols[peer_id].sendData(unique_pc, HASH, str(h))
+
+        # Set up receivers for hashes and signals.
+        # Note, we use the unique_pc to avoid data to cross method invocation 
boundaries.
+        for peer_id in receivers:
+            d_hash = Deferred().addCallbacks(hash_received,
+                                             self.error_handler, 
+                                             callbackArgs=(unique_pc, peer_id, 
receivers, g_hashes))
+            self._expect_data_with_pc(unique_pc, peer_id, HASH, d_hash)
+            d_signal = Deferred().addCallbacks(signal_received, 
+                                               self.error_handler, 
+                                               callbackArgs=(peer_id, message, 
len(receivers), 
+                                                             g_hashes, 
signals))
+            self._expect_data_with_pc(unique_pc, peer_id, SIGNAL, d_signal)
+
+        # Set up receiving of the message.
+        d_message = Deferred().addCallbacks(message_received, 
+                                            self.error_handler, 
+                                            callbackArgs=(unique_pc, message, 
receivers, g_hashes))
+        self._expect_data(sender, TEXT, d_message)
+        return result
+
+
+    def broadcast(self, senders, receivers, message=None):
+        """Broadcast the messeage from senders to receivers.
+
+        Returns a list of deferreds if the calling player is among 
+        the receivers and there are multiple senders.
+        Returns a single element if there is only on sender, or the 
+        calling player is among the senders only.
+
+        The order of the resulting list is guaranteed to be the same order 
+        as the list of senders.
+
+        Senders and receivers should be lists containing the id of the senders 
+        and receivers, respectively.
+
+        Note: You send implicitly to your self."""
+        assert message is None or self.id in senders
+
+        self.program_counter[-1] += 1
+
+        pc = tuple(self.program_counter)
+        if (self.id in receivers or self.id in senders):
+            results = [None] * len(senders)
+        else:
+            results = []
+
+        if self.id in senders:
+            self._send_message(pc, self.id, receivers, message)
+
+        if self.id in receivers:
+            for x in xrange(len(senders)):
+                sender = senders[x]
+                new_pc = list(self.program_counter)
+                new_pc.append(x)
+                results[x] = self._receive_broadcast(pc, tuple(new_pc), 
sender, receivers)
+
+        if self.id in senders and self.id not in receivers:
+            d = Deferred()
+            d.callback(message)
+            results = [d]
+
+        self.program_counter[-1] += 1
+
+        if len(results) == 1:
+            return results[0]
+
+        return results
+          
+    def list_str(self, s):
+        ls = []
+        for x in s[1:-1].split(','):
+            x = x.strip()
+            ls.append(str(x)[1:-1])
+        return ls
+
+    def error_handler(self, ex):
+        print "Error: ", ex
+        return ex
diff -r 824ae3770456 -r ed2d02202af0 viff/orlandi.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/viff/orlandi.py   Thu Oct 08 14:27:37 2009 +0200
@@ -0,0 +1,1314 @@
+# Copyright 2009 VIFF Development Team.
+#
+# This file is part of VIFF, the Virtual Ideal Functionality Framework.
+#
+# VIFF is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License (LGPL) as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# VIFF is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with VIFF. If not, see <http://www.gnu.org/licenses/>.
+
+from twisted.internet.defer import Deferred, DeferredList, gatherResults
+
+from viff.runtime import Runtime, Share, ShareList, gather_shares
+from viff.util import rand
+from viff.constants import TEXT, PAILLIER
+from viff.field import FieldElement
+from viff.paillier import encrypt_r, decrypt
+
+from hash_broadcast import HashBroadcastMixin
+
+import commitment
+commitment.set_reference_string(23434347834783478783478L, 
489237823478234783478020L)
+
+# import logging
+# LOG_FILENAME = 'logging_example.out'
+# logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)
+
+class OrlandiException(Exception):
+    pass
+
+class OrlandiShare(Share):
+    """A share in the Orlandi runtime.
+
+    A share in the Orlandi runtime is a 3-tuple ``(x_i, rho_i, Cr_i)`` of:
+    - A share of a number, ``x_i``
+    - A tuple of two random numbers, ``rho_i = (rho_i1, rho_i2)``
+    - A commitment to the number and the random numbers, ``Cr_i``
+
+    The :class:`Runtime` operates on shares, represented by this class.
+    Shares are asynchronous in the sense that they promise to attain a
+    value at some point in the future.
+
+    Shares overload the arithmetic operations so that ``x = a + b``
+    will create a new share *x*, which will eventually contain the
+    sum of *a* and *b*. Each share is associated with a
+    :class:`Runtime` and the arithmetic operations simply call back to
+    that runtime.
+    """
+
+    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))
+
+
+class OrlandiRuntime(Runtime, HashBroadcastMixin):
+    """The Orlandi runtime.
+
+    The runtime is used for sharing values (:meth:`secret_share` or
+    :meth:`shift`) into :class:`OrlandiShare` object and opening such
+    shares (:meth:`open`) again. Calculations on shares is normally
+    done through overloaded arithmetic operations, but it is also
+    possible to call :meth:`add`, :meth:`mul`, etc. directly if one
+    prefers.
+
+    Each player in the protocol uses a :class:`Runtime` object. To
+    create an instance and connect it correctly with the other
+    players, please use the :func:`create_runtime` function instead of
+    instantiating a Runtime directly. The :func:`create_runtime`
+    function will take care of setting up network connections and
+    return a :class:`Deferred` which triggers with the
+    :class:`Runtime` object when it is ready.
+    """
+
+    def __init__(self, player, threshold=None, options=None):
+        """Initialize runtime."""
+        Runtime.__init__(self, player, threshold, options)
+        self.threshold = self.num_players - 1
+        self.s = 1
+        self.d = 0
+        self.s_lambda = 1
+
+    def compute_delta(self, d):
+        def product(j):
+            pt = 1
+            pn = 1
+            for k in xrange(1, 2 * d + 2):
+                if k != j:
+                    pt *= k
+                    pn *= k - j
+            return pt // pn
+
+        delta = []
+        for j in xrange(1, 2 * d + 2):
+            delta.append(product(j))
+        return delta
+
+    def output(self, share, receivers=None, threshold=None):
+        return self.open(share, receivers, threshold)
+
+    def _send_orlandi_share(self, other_id, pc, xi, rhoi, Cx):
+        """Send the share *xi*, *rhoi*, and the commitment *Cx* to party 
*other_id*."""
+        self.protocols[other_id].sendShare(pc, xi)
+        self.protocols[other_id].sendShare(pc, rhoi[0])
+        self.protocols[other_id].sendShare(pc, rhoi[1])
+        self.protocols[other_id].sendData(pc, TEXT, repr(Cx))
+
+    def _expect_orlandi_share(self, peer_id, field):
+        """Waits for a number ``x``, ``rho``, and the commitment for ``x``."""
+        xi = self._expect_share(peer_id, field)
+        Cx = Deferred()        
+        rhoi1 = self._expect_share(peer_id, field)
+        rhoi2 = self._expect_share(peer_id, field)
+        self._expect_data(peer_id, TEXT, Cx)
+        sls = ShareList([xi, rhoi1, rhoi2, Cx])
+        def combine(ls):
+            expected_num = 4;
+            if len(ls) is not expected_num:
+                raise OrlandiException("Cannot share number, trying to create 
a share,"
+                                       " expected %s components got %s." % 
(expected_num, len(ls)))
+            s1, xi = ls[0]
+            s2, rhoi1 = ls[1]
+            s3, rhoi2 = ls[2]
+            s4, Cx = ls[3]
+            Cxx = commitment.deserialize(Cx)
+            if not (s1 and s2 and s3 and s4):
+                raise OrlandiException("Cannot share number, trying to create 
share,"
+                                       " but a component did arrive properly.")
+            return OrlandiShare(self, field, xi, (rhoi1, rhoi2), Cxx)
+        sls.addCallbacks(combine, self.error_handler)
+        return sls
+
+    def _expect_orlandi_share_xi_rhoi(self, peer_id, field):
+        xi = self._expect_share(peer_id, field)
+        rhoi1 = self._expect_share(peer_id, field)
+        rhoi2 = self._expect_share(peer_id, field)
+        sls = ShareList([xi, rhoi1, rhoi2])
+        def combine(ls):
+            expected_num = 3;
+            if len(ls) is not expected_num:
+                raise OrlandiException("Cannot share number, trying to create 
a share,"
+                                       " expected %s components got %s." % 
(expected_num, len(ls)))
+
+            s1, xi = ls[0]
+            s2, rhoi1 = ls[1]
+            s3, rhoi2 = ls[2]
+            if not (s1 and s2 and s3):
+                raise OrlandiException("Cannot share number, trying to create 
share "
+                                       "but a component did arrive properly.")
+            return OrlandiShare(self, field, xi, (rhoi1, rhoi2))
+        sls.addCallbacks(combine, self.error_handler)
+        return sls
+
+    def secret_share(self, inputters, field, number=None, threshold=None):
+        """Share the value, number, among all the parties using additive 
shareing.
+
+        To share an element ``x in Z_p``, choose random ``x_1, ..., x_n-1 in 
Z_p``, 
+        define ``x_n = x - SUM_i=1^n-1 x_i mod p``.
+
+        Choose random values ``rho_x1, ..., rho_xn in (Z_p)^2``, define 
+        ``rho_x = SUM_i=1^n rho_x,i`` and ``C_x = Com_ck(x, p_x)``.
+        
+        Send ``[x]_i = (x_i, rho_xi, C_x)`` to party ``P_i``.
+        """
+        assert number is None or self.id in inputters
+        self.threshold = self.num_players - 1
+
+        self.program_counter[-1] += 1
+
+        def additive_shares_with_rho(x):
+            """Returns a tuple of a list of tuples (player id, share, rho) and 
rho.
+
+            Chooses random elements ``x_1, ..., x_n-1`` in field and ``x_n`` 
st. 
+            ``x_n = x - Sum_i=1^n-1 x_i``.
+
+            Chooses random pair of elements ``rho_1, ..., rho_n in Z_p^2``
+            and define ``rho_n = Sum_i=1^n rho_i``.
+
+            Returns a pair of ``((player id, x_i, rho_i), rho)``.
+            """ 
+            shares = []
+            rhos = []
+            sum = 0
+            rho1 = 0
+            rho2 = 0
+            for i in xrange(1, self.num_players):
+                xi = field(rand.randint(0, field.modulus - 1))
+                rhoi1 = field(rand.randint(0, field.modulus - 1))
+                rhoi2 = field(rand.randint(0, field.modulus - 1))
+                sum += xi
+                rho1 += rhoi1
+                rho2 += rhoi2
+                shares.append((i, xi, (rhoi1, rhoi2)))    
+            xn = field(x) - sum
+            rhon1 = field(rand.randint(0, field.modulus - 1))
+            rhon2 = field(rand.randint(0, field.modulus - 1))
+            shares.append((self.num_players, xn, (rhon1, rhon2)))
+            rho1 += rhon1
+            rho2 += rhon2
+            return shares, (rho1, rho2)
+
+        # Send ``[x]_i = (x_i, rho_x,i, C_x)`` to party ``P_i``.
+        results = []
+        for peer_id in inputters:
+            if peer_id == self.id:
+                pc = tuple(self.program_counter)
_______________________________________________
viff-commits mailing list
[email protected]
http://lists.viff.dk/listinfo.cgi/viff-commits-viff.dk

Reply via email to