Skip to content

Latest commit

 

History

History
383 lines (302 loc) · 11.1 KB

low-level.rst

File metadata and controls

383 lines (302 loc) · 11.1 KB

Low-Level Interface

In the following we will explain the basic of the C++ interface by walking trough Utils/paper-example.cpp.

cpp

template<class T>
void run(char** argv, int prime_length);

MP-SPDZ heavily uses templating to allow to reuse code between different protocols. :cpprun is a simple example of this. The entire virtual machine in the Processor directory is built on the same principle. The central type is a type representing a share in a particular type.

// bit length of prime
const int prime_length = 128;

// compute number of 64-bit words needed
const int n_limbs = (prime_length + 63) / 64;

Computation modulo a prime requires to fix the number of limbs (64-bit words) at compile time. This allows for optimal memory usage and computation.

if (protocol == "MASCOT")
    run<Share<gfp_<0, n_limbs>>>(argv, prime_length);
else if (protocol == "CowGear")
    run<CowGearShare<gfp_<0, n_limbs>>>(argv, prime_length);

Share types for computation module a prime (and in GF(2n)) generally take one parameter for the computation domain. gfp_ in turn takes two parameters, a counter and the number of limbs. The counter allows to use several instances with different parameters. It can be chosen freely, but the convention is to use 0 for the online phase and 1 for the offline phase where required.

else if (protocol == "SPDZ2k")
    run<Spdz2kShare<64, 64>>(argv, 0);

Share types for computation modulo a power of two simply take the exponent as parameter, and some take an additional security parameter.

int my_number = atoi(argv[1]);
int n_parties = atoi(argv[2]);
int port_base = 9999;
Names N(my_number, n_parties, "localhost", port_base);

All implemented protocols require point-to-point connections between all parties. Names objects represent a setup of hostnames and IPs used to set up the actual connections. The chosen initialization provides a way where every party connects to party 0 on a specified location (localhost in this case), which then broadcasts the locations of all parties. The base port number is used to derive the port numbers for the parties to listen on (base + party number). See the the Names class for other possibilities such as a text file containing hostname and port number for each party.

CryptoPlayer P(N);

The networking setup is used to set up the actual connections. CryptoPlayer uses encrypted connection while PlainPlayer does not. If you use several instances (for several threads for example), you must use an integer identifier as the second parameter, which must differ from any other by at least the number of parties.

ProtocolSetup<T> setup(P, prime_length);

We have to use a specific prime for computation modulo a prime. This deterministically generates one of the desired length if necessary. For computation modulo a power of two, this does not do anything. Some protocols use an information-theoretic tag that is constant throughout the protocol. This code reads it from storage if available or generates a fresh one otherwise.

ProtocolSet<T> set(P, setup);
auto& input = set.input;
auto& protocol = set.protocol;
auto& output = set.output;

The ProtocolSet<T> contains one instance for every essential protocol step.

int n = 1000;
vector<T> a(n), b(n);
T c;
typename T::clear result;

Remember that T stands for a share in the protocol. The derived type T::clear stands for the cleartext domain. Share types support linear operations such as addition, subtraction, and multiplication with a constant. Use T::constant to convert a constant to a share type.

input.reset_all(P);
for (int i = 0; i < n; i++)
    input.add_from_all(i);
input.exchange();
for (int i = 0; i < n; i++)
{
    a[i] = input.finalize(0);
    b[i] = input.finalize(1);
}

The interface for all protocols proceeds in four stages:

  1. Initialization. This is required to initialize and reset data structures in consecutive use.
  2. Local data preparation
  3. Communication
  4. Output extraction

This blueprint allows for a minimal number of communication rounds.

protocol.init_dotprod(&processor);
for (int i = 0; i < n; i++)
    protocol.prepare_dotprod(a[i], b[i]);
protocol.next_dotprod();
protocol.exchange();
c = protocol.finalize_dotprod(n);

The initialization of the multiplication sets the preprocessing and output instances to use in Beaver multiplication. next_dotprod separates dot products in the data preparation phase.

set.check();

Some protocols require a check of all multiplications up to a certain point. To guarantee that outputs do not reveal secret information, it has to be run before using the output protocol.

output.init_open(P);
output.prepare_open(c);
output.exchange(P);
result = output.finalize_open();

cout << "result: " << result << endl;
output.Check(P);

The output protocol follows the same blueprint as the multiplication protocol.

set.check();

Some output protocols require an additional to guarantee the correctness of outputs.

Thread Safety

The low-level interface generally isn't thread-safe. In particular, you should only use one instance of ProtocolSetup in the whole program, and you should use only one instance of CryptoPlayer/PlainPlayer and ProtocolSet per thread.

Domain Types

Share Types

Protocol Setup

ProtocolSetup

ProtocolSet

BinaryProtocolSetup

BinaryProtocolSet

MixedProtocolSetup

MixedProtocolSet

Protocol Interfaces

ProtocolBase

InputBase

MAC_Check_Base

Preprocessing

BufferPrep

Domain Reference

gfp

Z2

SignedZ2