PULSAR-MPC is a special-purpose library for secure multiparty computation (MPC) that was written by Stealth Software Technologies, Inc. as part of the DARPA Brandeis project. The library was primarily intended to be used as part of the CoffeeBreak Android application. For additional inquiry in the use and deployment of our software, please reach out to us at contact@stealthsoftwareinc.com.
Essentially, CoffeeBreak is an application that securely computes the centroid of a set of private locations to find a single meeting place, such as a coffee shop. We model this computation as a circuit, which is securely evaluated by our core MPC engine. Each party’s private location is hidden.
For reference, a set of precompiled Android files,
pulsar-mpc-<version>-android.tar.xz
, is provided on the
Releases
page.
The archive includes the same content as the precompiled Android
library.
You can also build the Android native libraries yourself as follows
(this requires that you have docker
installed):
./configure
make parcels/pulsar-mpc-VERSION-aarch64-linux-android-api27.tar.xz
make parcels/pulsar-mpc-VERSION-x86_64-linux-android-api27.tar.xz
This section walks through building and installing PULSAR-MPC on an Ubuntu machine.
Run the following commands to build and install the bmc
native library
and the circuit_builder_main
utility:
sudo apt-get install g++ make openjdk-11-jdk libgmp-dev nettle-dev
./configure \
--disable-static \
CPPFLAGS="-I/usr/lib/jvm/java-11-openjdk-amd64/include \
-I/usr/lib/jvm/java-11-openjdk-amd64/include/linux" \
CFLAGS="-O2 -march=native" \
CXXFLAGS="-O2 -march=native" \
;
make
sudo make install
After installing, you can test the MpcTask
class using the
MpcTaskTest
program by following the comments inside the
MpcTaskTest
source code.
The MpcTaskTest
program includes an implementation of the
MpcTask.Channel
interface for
RabbitMQ.
As this is a push channel implementation, note how the basicConsume()
handlers call the MpcTask.recv()
method from elsewhere to feed it
bytes.
Also note how this code calls the System.loadLibrary()
method
similarly to as described previously.
After building and installing PULSAR-MPC on a Linux machine, the
installed circuit_builder_main
utility can be used to compile
circuits.
See CircuitBuilderREADME.adoc
for more
information on using this utility.
The main API is the MpcTask
Java class, which can be used to
securely evaluate an arbitrary circuit.
Underneath this class lies the core MPC engine, which is a native
library written in C++ called bmc
.
The MpcTask
class calls the bmc
native library using JNI.
All parties run the circuit evaluation at the same time and communicate
with each other using the MpcTask.Channel
interface, which you must
implement for each type of network channel you wish to use:
-
The
void send(byte[] buf)
method should send allbuf.length
bytes inbuf
to the other party, or throw an exception upon error.The ongoing computation may call the
send()
method from multiple threads at the same time. However, it is guaranteed that, for each channel, only onesend()
call will be occurring on that channel at any given time. -
For receiving bytes, there are two options:
-
If you want a push channel, meaning you’ll call the
MpcTask.recv()
method to feed bytes into the ongoing computation as you receive them (in another thread), then you need not implement anything but thesend()
method.You can call the
MpcTask.recv()
method from multiple threads at the same time. However, you must guarantee that, for each channel, only one call will be occurring on that channel at any given time. -
If you want a pull channel, meaning the ongoing computation will call back to you (from an unspecified thread) to receive bytes as it needs them, then you need to implement the
recv()
andisPush()
methods. Thevoid recv(byte[] buf)
method should fillbuf
with the nextbuf.length
bytes received from the other party, or throw an exception upon error. Theboolean isPush()
method should returnfalse
to indicate that this is a pull channel.The ongoing computation may call the
recv()
method from multiple threads at the same time. However, it is guaranteed that, for each channel, only onerecv()
call will be occurring on that channel at any given time.
-
Before you can use the MpcTask
class, you must load the bmc
native
library along with its prerequisite libraries,
Nettle
and
GMP.
This is typically done with the System.loadLibrary()
method.
Be sure to do this in the following order:
System.loadLibrary("c++_shared");
System.loadLibrary("gmp");
System.loadLibrary("nettle");
System.loadLibrary("bmc");
You may need to use the System.load()
method for any .so
library
files that do not match the typical libfoo.so
naming form, such as
libnettle.so.6
.
This method takes an absolute path to the library file to load, so
something like
System.load(getApplicationInfo().nativeLibraryDir + "/libnettle.so.6")
should work.
After you have the MpcTask.Channel
interface implemented and the
native libraries loaded, you’re ready to create an MpcTask
.
First, you need to pick a circuit for the parties to evaluate.
Various circuit files are provided in the
src/circuits
directory.
Each circuit is designed for a specific number of parties n
, and
each party must provide an appropriate input string to the circuit.
The circuit produces an output string, which is a comma separated list
of numbers.
All parties receive the same output string.
For the coffeeshop_*.cbg
circuits, n
is indicated in the
filename, and the input string for party i
(where 0 ≤ i < n
)
is of the form
xi_lat=a,xi_lng=b
,
where a
and b
are integers.
The output string is a list of two integers that are the sum of all
parties' a
values and the sum of all parties' b
values,
respectively.
For example, with two parties with input strings x0_lat=1,x0_lng=-1
and x1_lat=1,x1_lng=-1
, the output string will be 2,-2
.
You’ll need to make the circuit files be stored on disk in your Android
project so the native code can access them, which unfortunately takes
some work.
You can do this by adding the files to the assets
folder of your
Android project and adding code to write them to disk as necessary.
You can open an asset as an InputStream
with the
Context.getAssets()
and AssetManager.open()
methods, and you can
write the asset to disk somewhere in the directory returned by the
Context.getFilesDir()
method.
Next, you need to create a list of n
channels for the MpcTask
to use to communicate with the other n-1
parties.
The list should have a null
at the index of this party itself (which
is why the list has n
entries, not n-1
).
Now you’re ready to create an MpcTask
:
Constructing the MpcTask
merely prepares it to run.
To actually run it, the MpcTask
class implements the
Callable<String>
interface, which you can use in any normal way.
The simplest way is to call the call()
method in the current thread:
String result = mpcTask.call();
You can also call the MpcTask.getLog()
method to retrieve the log
after the call()
method returns or throws an exception.
If logging was enabled when the MpcTask
was created and the log was
successfully captured, this method returns the log as a String
.
Otherwise, it returns null
.
The log can be particularly helpful for diagnosing errors.
This work was supported by DARPA and NIWC Pacific under contract N66001-15-C-4065. The U.S. Government is authorized to reproduce and distribute reprints for Governmental purposes not withstanding any copyright notation thereon. The views, opinions, and/or findings expressed are those of the author(s) and should not be interpreted as representing the official views or policies of the Department of Defense or the U.S. Government.
-
Paul Bunn <paul@stealthsoftwareinc.com>
-
Quinn Grier <quinn@stealthsoftwareinc.com>
-
Steve Lu <steve@stealthsoftwareinc.com>
See the ATTRIBUTIONS.adoc file.