#!/usr/bin/python # Copyright 2007, 2008 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 . # This example is a tribute to the original example of secure # multi-party computation by Yao in 1982. In his example two # millionaires meet in the street and they want to securely compare # their fortunes. They want to do so without revealing how much they # own to each other. This problem would be easy to solve if both # millionaires trust a common third party, but we want to solve it # without access to a third party. # # In this example the protocol is run between three millionaires and # uses a protocol for secure integer comparison by Toft from 2005. # # Give a player configuration file as a command line argument or run # the example with '--help' for help with the command line options. from pprint import pprint from optparse import OptionParser from twisted.internet import reactor from viff.field import GF from viff.runtime import Runtime, create_runtime, gather_shares from viff.comparison import Toft05Runtime from viff.config import load_config from viff.util import rand, find_prime, dprint # We start by defining the protocol, it will be started at the bottom # of the file. class Protocol: def __init__(self, runtime): # Save the Runtime for later use self.runtime = runtime # This is the value we will use in the protocol. self.millions = rand.randint(1, 200) print "I am Millionaire %d and I am worth %d millions." \ % (runtime.id, self.millions) # For the comparison protocol to work, we need a field modulus # bigger than 2**(l+1) + 2**(l+k+1), where the bit length of # the input numbers is l and k is the security parameter. # Further more, the prime must be a Blum prime (a prime p such # that p % 4 == 3 holds). The find_prime function lets us find # a suitable prime. l = runtime.options.bit_length k = runtime.options.security_parameter Zp = GF(find_prime(2**(l + 1) + 2**(l + k + 1), blum=True)) # We must secret share our input with the other parties. They # will do the same and we end up with three variables m1, m2, m3, m4, m5 = runtime.shamir_share([1, 2, 3, 4, 5], Zp, self.millions) # Now that everybody has secret shared their inputs we can # compare them. We compare the worth of the first millionaire # with the two others, and compare those two millionaires with # each other. m1_gt_m2 = m1 >= m2 m1_gt_m3 = m1 >= m3 m1_gt_m4 = m1 >= m4 m1_gt_m5 = m1 >= m5 m2_gt_m3 = m2 >= m3 m2_gt_m4 = m2 >= m4 m2_gt_m5 = m2 >= m5 m3_gt_m4 = m3 >= m4 m3_gt_m5 = m3 >= m5 m4_gt_m5 = m4 >= m5 # The results are secret shared, so we must open them before # we can do anything usefull with them. open_m1_gt_m2 = runtime.open(m1_gt_m2) open_m1_gt_m3 = runtime.open(m1_gt_m3) open_m1_gt_m4 = runtime.open(m1_gt_m4) open_m1_gt_m5 = runtime.open(m1_gt_m5) open_m2_gt_m3 = runtime.open(m2_gt_m3) open_m2_gt_m4 = runtime.open(m2_gt_m4) open_m2_gt_m5 = runtime.open(m2_gt_m5) open_m3_gt_m4 = runtime.open(m3_gt_m4) open_m3_gt_m5 = runtime.open(m3_gt_m5) open_m4_gt_m5 = runtime.open(m4_gt_m5) # We will now gather the results and call the # self.results_ready method when they have all been received. results = gather_shares([open_m1_gt_m2, open_m1_gt_m3, open_m1_gt_m4, open_m1_gt_m5, open_m2_gt_m3, open_m2_gt_m4, open_m2_gt_m5, open_m3_gt_m4, open_m3_gt_m5, open_m4_gt_m5]) results.addCallback(self.results_ready) # We can add more callbacks to the callback chain in results. # These are called in sequence when self.results_ready is # finished. The first callback acts like a barrier and makes # all players wait on each other. results.addCallback(lambda _: runtime.synchronize()) # The next callback shuts the runtime down, killing the # connections between the players. results.addCallback(lambda _: runtime.shutdown()) def results_ready(self, results): # Since this method is called as a callback above, the results # variable will contain actual field elements, not just # Shares. That makes it very easy to work on them. # Let us start by unpacking the list. m1_gt_m2 = results[0] m1_gt_m3 = results[1] m1_gt_m4 = results[2] m1_gt_m5 = results[3] m2_gt_m3 = results[4] m2_gt_m4 = results[5] m2_gt_m5 = results[6] m3_gt_m4 = results[7] m3_gt_m5 = results[8] m4_gt_m5 = results[9] dprint("m1 > m2?: %s", m1_gt_m2) dprint("m1 > m3?: %s", m1_gt_m3) dprint("m1 > m4?: %s", m1_gt_m4) dprint("m1 > m5?: %s", m1_gt_m5) dprint("m2 > m3?: %s", m2_gt_m3) dprint("m2 > m4?: %s", m2_gt_m4) dprint("m2 > m5?: %s", m2_gt_m5) dprint("m3 > m4?: %s", m3_gt_m4) dprint("m3 > m5?: %s", m3_gt_m5) dprint("m4 > m5?: %s", m4_gt_m5) # We can establish the correct order of Millionaires 2 and 3. comparison = [5] if m1_gt_m2 and m1_gt_m3 and m1_gt_m4 and m1_gt_m5: comparison = [1] elif not m1_gt_m2 and m2_gt_m3 and m2_gt_m4 and m2_gt_m5: comparison = [2] elif not m1_gt_m3 and not m2_gt_m3 and m3_gt_m4 and m3_gt_m5: comparison = [3] elif not m1_gt_m4 and not m2_gt_m4 and not m3_gt_m4 and m4_gt_m5: comparison = [4] print "Richest:" for id in comparison: if id == self.runtime.id: print " Millionaire %d (%d millions)" % (id, self.millions) else: print " Millionaire %d" % id # Parse command line arguments. parser = OptionParser() Runtime.add_options(parser) options, args = parser.parse_args() if len(args) == 0: parser.error("you must specify a config file") else: id, players = load_config(args[0]) # Create a deferred Runtime and ask it to run our protocol when ready. pre_runtime = create_runtime(id, players, 3, options, Toft05Runtime) pre_runtime.addCallback(Protocol) # Start the Twisted event loop. reactor.run()