/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