A Rust implementation of Beaver's MPC protocol for secure evaluation of arithmetic circuits. A detailed description of the protocol and its security properties can be found in D. Boneh and V. Shoup's book A Graduate Course in Applied Cryptography (as of version 0.6, in section 23.2).
Important disclaimer: For many reasons (among others: parameter size, limited testing and lack of external revision) the project is completely unsuitable for use in real-world cryptographic applications. Its purposes are purely illustrative.
An arithmetic circuit consists of a (cycle-free) network of addition and multiplication gates.
In our case of interest, two parties
This implementation includes two versions of the protocol:
- The unauthenticated version is secure against honest-but-curious adversaries. This means each party obtains the outputs assigned to it but learns nothing about the inputs provided by the other party or the inputs assigned to the other party - except for the information intrinsically contained in the outputs it obtains. The dealer does not learn anything about any inputs or outputs either.
- The authenticated version is secure against malicious adversaries. That is, in addition to the above guarantees, each party can detect whether the dealer or the other party have tampered with their part of the data, in which case it will choose to abort. A consequence of this is soundness: if a party executes the protocol to the end and receives an output, that value is indeed correct with overwhelming probability.
The details of the protocol can be found in the aforementioned book.
The present implementation simulates the two parties and dealer as three separate threads which communicate only through messages as defined by the protocol - analogously to how they would operate if they were genuinely running in separate machines and communicating through some secure channel.
After compiling (for instance, with cargo build
), the executable can be run with
<exec_path> <input_path> <output_path>
where:
<exec_path>
is the path to the executable, usually./target/debug/beaver
from the main cargo directory.<input_path>
is the path containing the input file, which encodes the circuit and other data as specified in the section Input file structure below. A few example input files are included in theexample_inputs
folder.<output_path>
is a partial path for the output logs. The programme will create three files:<output_path>_dealer.txt
,<output_path>_p1.txt
and<output_path>_p2.txt
.
The aforementioned logs contain a record of the data received and sent by the corresponding party or the dealer.
One can thus get a feel for how the protocol provides secrecy.
If an error is encountered during the process, it will be printed to stderr
. Furthermore, if any party aborts (because it detects the other one has tampered with the data), it will record this in its own log as well as stderr
. This will usually cause the other party to abort too. Additionally, a corrupt party will inform whenever it tampers with the data, both in its own log and stdout
.
If the protocol is executed successfully (i.e. without either party aborting), the last lines of the protocol will contain whatever outputs it was assigned to receive.
Input files are plain text files encoding the circuit, input data and output assignment data.
Examples can be found in the example_inputs
folder. An input file must contain the following elements in order:
-
An encoding of the circuit, which can be freely split along any number of (non-blank) lines. Line breaks aside, the structure of this encoding is as follows:
<gates> & <outputs_for_p1> & <outputs_for_p2>
where
-
<gates>
is of the form<gate_1> | <gate_2> | ... | <gate_n>
Each gate
<gate_i>
, in turn, has the following structure:<id>, <input_1>, <op>, <input_2>
Here:
-
<id>
is au32
which uniquely identifies the gate in the circuit. These identifiers do not have to be in order, form a continuous numbering or describe in any way the evaluation order of the gates. -
<input_1>
must be eitherP1
,P2
or au32
value. The first two cases indicate that the input in question must be provided by$P_1$ or$P_2$ , respectively (see below). In the third case, the value value must be the identifier of another gate in the circuit. Note that the output of a single gate can act as input to more than one gate. -
<op>
is one ofadd
,mul
,addc
ormulc
. These refer to addition and multiplication by another input (first two, respectively) or a predefined constant (second two, respectively). -
<input_2>
is of the same form as<input_1>
, with the caveat that if<op>
is eitheraddc
ormulc
, then<input_2>
is actually of typei32
and it contains the value of the desired constant.
-
-
<outputs_for_p1>
is a list of comma-separatedu32
values indicating the identifiers of the gates whose outputs should be revealed to$P_1$ . If none should, this parameter should be the empty string. -
<outputs_for_p2>
is analogous to the previous point but refers to outputs for$P_2$ .
Example:
The lines
0, P1, addc, 0 | 1, 0, add, 5 | 2, 1, add, 6 | 3, 2, mul, 2 | 4, 3, add, 7 | 5, P2, mulc, -2 | 6, P2, addc, 0 | 7, 0, mul, 6 & 4 &
encode the circuit:
with the blue numbers indicating the identifier of each gate.
-
-
A blank line indicating the end of the circuit encoding.
-
A line containing a single
u32
value, which designatesq
. This is the prime modulo which the entire circuit arithmetic takes place. -
A line containing a comma-separated list of
i32
values. These are the actual input values that$P_1$ provides to all gates where it is supposed to provide the first input. In other words, these are the inputs for gates of the form<id>, P1, <op>, <input_2>
in the circuit encoding. Important: thesei32
values have to be in ascending order with respect to the identifier of the gate they correspond to. For instance, if$P_1$ wants to provide the value -5 to the first input of gate with identifier 10 and the value 3 to the first input of the gate with identifier 2, this line should be:3, -5
-
A line analogous to the previous one but referring to the gates where
$P_1$ provides the second input, i.e. gates of the form<id>, <input_1>, <op>, P1
-
A line analogous to point 4 above but corresponding to inputs provided by
$P_2$ . -
A line analogous to point 5 above but corresponding to inputs provided by
$P_2$ . -
A line containing
true
orfalse
indicating whether the protocol execution should be authenticated or not. Iftrue
, each party will verify that the other one has not tampered with the data using a key generated by the dealer. Note that, even if tampering occurs (which is registered in the log of the corrupt party), detection is not guaranteed - only very likely (and more so the higherq
becomes). -
A line containing
true
orfalse
indicating whether one of the parties should be corrupt. If so, the programme will randomly choose one of$P_1$ or$P_2$ to be the corrupt party. A corrupt party has a 20% chance of tampering with the data each time it sends data to the other party (this probability is hard-coded as the constantCORRUPTION_DEGREE
insrc/protocol.rs
, which can be tweaked to taste). Note that, even if a party is corrupt, the other one will not detect so unless the protocol is run in authenticated (see the previous point).
- Further testing
- Add verification that the dealer is not corrupt
- Add functionality to simulate corrupt dealer
The repository is currently meant to showcase the code only and is therefore not open to contributors. However, if you find it interesting or there is anything you would like to discuss, feel welcome to message me!