mirror of
https://github.com/data61/MP-SPDZ.git
synced 2026-01-10 05:57:57 -05:00
Bristol Fashion.
This commit is contained in:
@@ -1,4 +1,32 @@
|
||||
The ExternalIO directory contains examples of managing I/O between external client processes and SPDZ parties running SPDZ engines. These instructions assume that SPDZ has been built as per the [project readme](../README.md).
|
||||
The ExternalIO directory contains an example of managing I/O between external client processes and SPDZ parties running SPDZ engines. These instructions assume that SPDZ has been built as per the [project readme](../README.md).
|
||||
|
||||
## Working Examples
|
||||
|
||||
[bankers-bonus-client.cpp](./bankers-bonus-client.cpp) acts as a
|
||||
client to [bankers_bonus.mpc](../Programs/Source/bankers_bonus.mpc)
|
||||
and demonstrates sending input and receiving output as described by
|
||||
[Damgård et al.](https://eprint.iacr.org/2015/1006) The computation
|
||||
allows up to eight clients to input a number and computes the client
|
||||
with the largest input. You can run it as follows from the main
|
||||
directory:
|
||||
```
|
||||
make bankers-bonus-client.x
|
||||
./compile.py bankers_bonus 1
|
||||
Scripts/setup-ssl.sh <nparties>
|
||||
Scripts/setup-clients.sh 3
|
||||
Scripts/<protocol>.sh &
|
||||
./bankers-bonus-client.x 0 <nparties 100 0 &
|
||||
./bankers-bonus-client.x 1 <nparties> 200 0 &
|
||||
./bankers-bonus-client.x 2 <nparties> 50 1
|
||||
```
|
||||
This should output that the winning id is 1. Note that the ids have to
|
||||
be incremental, and the client with the highest id has to input 1 as
|
||||
the last argument while the others have to input 0 there. Furthermore,
|
||||
`<nparties>` refers to the number of parties running the computation
|
||||
not the number of clients, and `<protocol>` can be the name of
|
||||
protocol script. The setup scripts generate the necessary SSL
|
||||
certificates and keys. Therefore, if you run the computation on
|
||||
different hosts, you will have to distribute the `*.pem` files.
|
||||
|
||||
## I/O MPC Instructions
|
||||
|
||||
@@ -55,49 +83,3 @@ Receive shares of private inputs from a client, blocking on client send. This is
|
||||
*message_type* - optional integer which will be sent in first 4 bytes of message, to indicate message type to client.
|
||||
|
||||
*[inputs]* - returned list of shares of private input.
|
||||
|
||||
|
||||
## Securing communications
|
||||
|
||||
Two cryptographic protocols have been implemented for use in particular applications and are included here for completeness:
|
||||
|
||||
1. Communication security using a Station to Station key agreement and libsodium Secret Box using a nonce counter for message ordering.
|
||||
2. Authenticated Diffie-Hellman without message ordering.
|
||||
|
||||
Please note these are **NOT** required to allow external client I/O. Your mileage may vary, for example in a web setting TLS may be sufficient to secure communications between processes.
|
||||
|
||||
[client-setup.cpp](../client-setup.cpp) is a utility which is run to generate the key material for both the external clients and SPDZ parties for both protocols.
|
||||
|
||||
#### MPC instructions
|
||||
|
||||
**regint.init_secure_socket**(*regint client_socket_id*, *[regint] public_signing_key*)
|
||||
|
||||
STS protocol initiator. Read a client public key for a specified client connection and negotiate a shared key via STS. All subsequent write_socket / read_socket instructions are encrypted / decrypted for replay resistant commsec.
|
||||
|
||||
*client_socket_id* - an identifier used to refer to the client socket.
|
||||
|
||||
*public_signing_key* - client public key supplied as list of 8 32-bit ints.
|
||||
|
||||
**regint.resp_secure_socket**(*regint client_socket_id*, *[regint] public_signing_key*)
|
||||
|
||||
STS protocol responder. Read a client public key for a specified client connection and negotiate a shared key via STS. All subsequent write_socket / read_socket instructions are encrypted / decrypted for replay resistant commsec.
|
||||
|
||||
*client_socket_id* - an identifier used to refer to the client socket.
|
||||
|
||||
*public_signing_key* - client public key supplied as list of 8 32-bit ints.
|
||||
|
||||
*[regint public_key]* **regint.read_client_public_key**(*regint client_socket_id*)
|
||||
|
||||
Instruction to read the client public key and run setup for the authenticated Diffie-Hellman encryption. All subsequent write_socket instructions are encrypted. Only the sint.read_from_socket instruction is encrypted.
|
||||
|
||||
*client_socket_id* - an identifier used to refer to the client socket.
|
||||
|
||||
*public_key* - client public key made available to mpc programs as list of 8 32-bit ints.
|
||||
|
||||
## Working Examples
|
||||
|
||||
See [bankers-bonus-client.cpp](./bankers-bonus-client.cpp) which acts as a client to [bankers_bonus.mpc](../Programs/Source/bankers_bonus.mpc) and demonstrates sending input and receiving output with no communications security.
|
||||
|
||||
See [bankers-bonus-commsec-client.cpp](./bankers-bonus-commsec-client.cpp) which acts as a client to [bankers_bonus_commsec.mpc](../Programs/Source/bankers_bonus_commsec.mpc) which runs the same algorithm but includes both the available crypto protocols.
|
||||
|
||||
More instructions on how to run these are provided in the *-client files.
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
* - share of random value [r]
|
||||
* - share of winning unique id * random value [w]
|
||||
* winning unique id is valid if ∑ [y] * ∑ [r] = ∑ [w]
|
||||
*
|
||||
* No communications security is used.
|
||||
*
|
||||
* To run with 2 parties / SPDZ engines:
|
||||
* ./Scripts/setup-online.sh to create triple shares for each party (spdz engine).
|
||||
* ./Scripts/setup-clients.sh to create SSL keys and certificates for clients
|
||||
* ./compile.py bankers_bonus
|
||||
* ./Scripts/run-online.sh bankers_bonus to run the engines.
|
||||
*
|
||||
@@ -34,6 +33,7 @@
|
||||
#include "Math/gfp.h"
|
||||
#include "Math/gf2n.h"
|
||||
#include "Networking/sockets.h"
|
||||
#include "Networking/ssl_sockets.h"
|
||||
#include "Tools/int.h"
|
||||
#include "Math/Setup.h"
|
||||
#include "Protocols/fake-stuff.h"
|
||||
@@ -46,12 +46,13 @@
|
||||
// Send the private inputs masked with a random value.
|
||||
// Receive shares of a preprocessed triple from each SPDZ engine, combine and check the triples are valid.
|
||||
// Add the private input value to triple[0] and send to each spdz engine.
|
||||
void send_private_inputs(const vector<gfp>& values, vector<int>& sockets, int nparties)
|
||||
template<class T>
|
||||
void send_private_inputs(const vector<T>& values, vector<ssl_socket*>& sockets, int nparties)
|
||||
{
|
||||
int num_inputs = values.size();
|
||||
octetStream os;
|
||||
vector< vector<gfp> > triples(num_inputs, vector<gfp>(3));
|
||||
vector<gfp> triple_shares(3);
|
||||
vector< vector<T> > triples(num_inputs, vector<T>(3));
|
||||
vector<T> triple_shares(3);
|
||||
|
||||
// Receive num_inputs triples from SPDZ
|
||||
for (int j = 0; j < nparties; j++)
|
||||
@@ -59,6 +60,10 @@ void send_private_inputs(const vector<gfp>& values, vector<int>& sockets, int np
|
||||
os.reset_write_head();
|
||||
os.Receive(sockets[j]);
|
||||
|
||||
#ifdef VERBOSE_COMM
|
||||
cerr << "received " << os.get_length() << " from " << j << endl;
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < num_inputs; j++)
|
||||
{
|
||||
for (int k = 0; k < 3; k++)
|
||||
@@ -72,49 +77,30 @@ void send_private_inputs(const vector<gfp>& values, vector<int>& sockets, int np
|
||||
// Check triple relations (is a party cheating?)
|
||||
for (int i = 0; i < num_inputs; i++)
|
||||
{
|
||||
if (triples[i][0] * triples[i][1] != triples[i][2])
|
||||
if (T(triples[i][0] * triples[i][1]) != triples[i][2])
|
||||
{
|
||||
cerr << triples[i][2] << " != " << triples[i][0] << " * " << triples[i][1] << endl;
|
||||
cerr << "Incorrect triple at " << i << ", aborting\n";
|
||||
exit(1);
|
||||
throw mac_fail();
|
||||
}
|
||||
}
|
||||
// Send inputs + triple[0], so SPDZ can compute shares of each value
|
||||
os.reset_write_head();
|
||||
for (int i = 0; i < num_inputs; i++)
|
||||
{
|
||||
gfp y = values[i] + triples[i][0];
|
||||
T y = values[i] + triples[i][0];
|
||||
y.pack(os);
|
||||
}
|
||||
for (int j = 0; j < nparties; j++)
|
||||
os.Send(sockets[j]);
|
||||
}
|
||||
|
||||
// Assumes that Scripts/setup-online.sh has been run to compute prime
|
||||
void initialise_fields(const string& dir_prefix)
|
||||
{
|
||||
int lg2;
|
||||
bigint p;
|
||||
|
||||
string filename = dir_prefix + "Params-Data";
|
||||
cout << "loading params from: " << filename << endl;
|
||||
|
||||
ifstream inpf(filename.c_str());
|
||||
if (inpf.fail()) { throw file_error(filename.c_str()); }
|
||||
inpf >> p;
|
||||
inpf >> lg2;
|
||||
|
||||
inpf.close();
|
||||
|
||||
gfp::init_field(p);
|
||||
gf2n::init_field(lg2);
|
||||
}
|
||||
|
||||
|
||||
// Receive shares of the result and sum together.
|
||||
// Also receive authenticating values.
|
||||
gfp receive_result(vector<int>& sockets, int nparties)
|
||||
template<class T>
|
||||
T receive_result(vector<ssl_socket*>& sockets, int nparties)
|
||||
{
|
||||
vector<gfp> output_values(3);
|
||||
vector<T> output_values(3);
|
||||
octetStream os;
|
||||
for (int i = 0; i < nparties; i++)
|
||||
{
|
||||
@@ -122,20 +108,32 @@ gfp receive_result(vector<int>& sockets, int nparties)
|
||||
os.Receive(sockets[i]);
|
||||
for (unsigned int j = 0; j < 3; j++)
|
||||
{
|
||||
gfp value;
|
||||
T value;
|
||||
value.unpack(os);
|
||||
output_values[j] += value;
|
||||
}
|
||||
}
|
||||
|
||||
if (output_values[0] * output_values[1] != output_values[2])
|
||||
if (T(output_values[0] * output_values[1]) != output_values[2])
|
||||
{
|
||||
cerr << "Unable to authenticate output value as correct, aborting." << endl;
|
||||
exit(1);
|
||||
throw mac_fail();
|
||||
}
|
||||
return output_values[0];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void run(int salary_value, vector<ssl_socket*>& sockets, int nparties)
|
||||
{
|
||||
// Run the computation
|
||||
send_private_inputs<T>({salary_value}, sockets, nparties);
|
||||
cout << "Sent private inputs to each SPDZ engine, waiting for result..." << endl;
|
||||
|
||||
// Get the result back (client_id of winning client)
|
||||
T result = receive_result<T>(sockets, nparties);
|
||||
|
||||
cout << "Winning client id is : " << result << endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
@@ -162,34 +160,65 @@ int main(int argc, char** argv)
|
||||
if (argc > 6)
|
||||
port_base = atoi(argv[6]);
|
||||
|
||||
// init static gfp
|
||||
string prep_data_prefix = get_prep_dir(nparties, 128, gf2n::default_degree());
|
||||
initialise_fields(prep_data_prefix);
|
||||
bigint::init_thread();
|
||||
|
||||
// Setup connections from this client to each party socket
|
||||
vector<int> sockets(nparties);
|
||||
vector<int> plain_sockets(nparties);
|
||||
vector<ssl_socket*> sockets(nparties);
|
||||
ssl_ctx ctx("C" + to_string(my_client_id));
|
||||
ssl_service io_service;
|
||||
octetStream specification;
|
||||
for (int i = 0; i < nparties; i++)
|
||||
{
|
||||
set_up_client_socket(sockets[i], host_name.c_str(), port_base + i);
|
||||
send(sockets[i], (octet*) &my_client_id, sizeof(int));
|
||||
set_up_client_socket(plain_sockets[i], host_name.c_str(), port_base + i);
|
||||
send(plain_sockets[i], (octet*) &my_client_id, sizeof(int));
|
||||
sockets[i] = new ssl_socket(io_service, ctx, plain_sockets[i],
|
||||
"P" + to_string(i), "C" + to_string(my_client_id), true);
|
||||
if (i == 0)
|
||||
specification.Receive(sockets[0]);
|
||||
octetStream os;
|
||||
os.store(finish);
|
||||
os.Send(sockets[i]);
|
||||
}
|
||||
cout << "Finish setup socket connections to SPDZ engines." << endl;
|
||||
|
||||
// Run the commputation
|
||||
send_private_inputs({salary_value}, sockets, nparties);
|
||||
cout << "Sent private inputs to each SPDZ engine, waiting for result..." << endl;
|
||||
int type = specification.get<int>();
|
||||
switch (type)
|
||||
{
|
||||
case 'p':
|
||||
{
|
||||
gfp::init_field(specification.get<bigint>());
|
||||
cerr << "using prime " << gfp::pr() << endl;
|
||||
run<gfp>(salary_value, sockets, nparties);
|
||||
break;
|
||||
}
|
||||
case 'R':
|
||||
{
|
||||
int R = specification.get<int>();
|
||||
switch (R)
|
||||
{
|
||||
case 64:
|
||||
run<Z2<64>>(salary_value, sockets, nparties);
|
||||
break;
|
||||
case 104:
|
||||
run<Z2<104>>(salary_value, sockets, nparties);
|
||||
break;
|
||||
case 128:
|
||||
run<Z2<128>>(salary_value, sockets, nparties);
|
||||
break;
|
||||
default:
|
||||
cerr << R << "-bit ring not implemented";
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
cerr << "Type " << type << " not implemented";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Get the result back (client_id of winning client)
|
||||
gfp result = receive_result(sockets, nparties);
|
||||
|
||||
cout << "Winning client id is : " << result << endl;
|
||||
|
||||
for (unsigned int i = 0; i < sockets.size(); i++)
|
||||
close_client_socket(sockets[i]);
|
||||
for (int i = 0; i < nparties; i++)
|
||||
delete sockets[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,407 +0,0 @@
|
||||
/*
|
||||
* Demonstrate external client inputing and receiving outputs from a SPDZ process,
|
||||
* following the protocol described in https://eprint.iacr.org/2015/1006.pdf.
|
||||
* Uses SPDZ implemented encryption for external client communication, see bankers-bonus-client.cpp
|
||||
* for a simpler client with no crypto.
|
||||
*
|
||||
* Provides a client to bankers_bonus_commsec.mpc program to calculate which banker pays for lunch based on
|
||||
* the private value annual bonus. Up to 8 clients can connect to the SPDZ engines running
|
||||
* the bankers_bonus.mpc program.
|
||||
*
|
||||
* Each connecting client:
|
||||
* - sends an increasing id to identify the client, starting with 0
|
||||
* - sends an integer (0 meaining more players will join this round or 1 meaning stop the round and calc the result).
|
||||
* - runs crypto setup to demonstrate both DH Auth Encryption and STS protocol for comms security.
|
||||
* - sends an integer input (bonus value to compare)
|
||||
*
|
||||
* The result is returned authenticated with a share of a random value:
|
||||
* - share of winning unique id [y]
|
||||
* - share of random value [r]
|
||||
* - share of winning unique id * random value [w]
|
||||
* winning unique id is valid if ∑ [y] * ∑ [r] = ∑ [w]
|
||||
*
|
||||
* To run with 2 parties (SPDZ engines) and 3 external clients:
|
||||
* ./Scripts/setup-online.sh to create triple shares for each party (spdz engine).
|
||||
* ./client-setup.x 2 -nc 3 to create the crypto key material for both parties and clients.
|
||||
* ./compile.py bankers_bonus_commsec
|
||||
* ./Scripts/run-online.sh bankers_bonus_commsec to run the engines.
|
||||
*
|
||||
* ./bankers-bonus-commsec-client.x 0 2 100 0
|
||||
* ./bankers-bonus-commsec-client.x 1 2 200 0
|
||||
* ./bankers-bonus-commsec-client.x 2 2 50 1
|
||||
*
|
||||
* Expect winner to be second client with id 1.
|
||||
* Note here client id must match id used in generating client key material, Client-Keys-C<client id>.
|
||||
*/
|
||||
|
||||
#include "Math/gfp.h"
|
||||
#include "Math/gf2n.h"
|
||||
#include "Networking/sockets.h"
|
||||
#include "Networking/STS.h"
|
||||
#include "Tools/int.h"
|
||||
#include "Math/Setup.h"
|
||||
#include "Protocols/fake-stuff.h"
|
||||
|
||||
#include <sodium.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
typedef pair< vector<octet>, vector<octet> > keypair_t; // A pair of send/recv keys for talking to SPDZ
|
||||
typedef vector< keypair_t > commsec_t; // A database of send/recv keys indexed by server
|
||||
typedef struct {
|
||||
unsigned char client_secretkey[crypto_sign_SECRETKEYBYTES];
|
||||
unsigned char client_publickey[crypto_sign_PUBLICKEYBYTES];
|
||||
vector<int> client_publickey_ints;
|
||||
vector< vector<unsigned char> >server_publickey;
|
||||
} sign_key_container_t;
|
||||
|
||||
keypair_t sts_response_role_exceptions(sign_key_container_t keys, vector<int>& sockets, int server_id)
|
||||
{
|
||||
STS ke(&keys.server_publickey[server_id][0], keys.client_publickey, keys.client_secretkey);
|
||||
sts_msg1_t m1;
|
||||
sts_msg2_t m2;
|
||||
sts_msg3_t m3;
|
||||
octetStream os;
|
||||
|
||||
os.Receive(sockets[server_id]);
|
||||
os.consume(m1.bytes, sizeof m1.bytes);
|
||||
m2 = ke.recv_msg1(m1);
|
||||
os.reset_write_head();
|
||||
os.append(m2.pubkey, sizeof m2.pubkey);
|
||||
os.append(m2.sig, sizeof m2.sig);
|
||||
os.Send(sockets[server_id]);
|
||||
os.Receive(sockets[server_id]);
|
||||
os.consume(m3.bytes, sizeof m3.bytes);
|
||||
ke.recv_msg3(m3);
|
||||
vector<unsigned char> recvKey = ke.derive_secret(crypto_secretbox_KEYBYTES);
|
||||
vector<unsigned char> sendKey = ke.derive_secret(crypto_secretbox_KEYBYTES);
|
||||
return make_pair(sendKey,recvKey);
|
||||
}
|
||||
|
||||
keypair_t sts_initiator_role_exceptions(sign_key_container_t keys, vector<int>& sockets, int server_id)
|
||||
{
|
||||
STS ke(&keys.server_publickey[server_id][0], keys.client_publickey, keys.client_secretkey);
|
||||
sts_msg1_t m1;
|
||||
sts_msg2_t m2;
|
||||
sts_msg3_t m3;
|
||||
octetStream os;
|
||||
|
||||
m1 = ke.send_msg1();
|
||||
cout << "m1: ";
|
||||
for (unsigned int j = 0; j < 32; j++)
|
||||
cout << setfill('0') << setw(2) << hex << (int) m1.bytes[j];
|
||||
cout << dec << endl;
|
||||
os.reset_write_head();
|
||||
os.append(m1.bytes, sizeof m1.bytes);
|
||||
os.Send(sockets[server_id]);
|
||||
|
||||
os.reset_write_head();
|
||||
os.Receive(sockets[server_id]);
|
||||
os.consume(m2.pubkey, sizeof m2.pubkey);
|
||||
os.consume(m2.sig, sizeof m2.sig);
|
||||
m3 = ke.recv_msg2(m2);
|
||||
|
||||
os.reset_write_head();
|
||||
os.append(m3.bytes, sizeof m3.bytes);
|
||||
os.Send(sockets[server_id]);
|
||||
|
||||
vector<unsigned char> sendKey = ke.derive_secret(crypto_secretbox_KEYBYTES);
|
||||
vector<unsigned char> recvKey = ke.derive_secret(crypto_secretbox_KEYBYTES);
|
||||
return make_pair(sendKey,recvKey);
|
||||
}
|
||||
|
||||
pair< vector<octet>, vector<octet> > sts_response_role(sign_key_container_t keys, vector<int>& sockets, int server_id)
|
||||
{
|
||||
pair< vector<octet>, vector<octet> > res;
|
||||
try {
|
||||
res = sts_response_role_exceptions(keys, sockets, server_id);
|
||||
} catch(char const *e) {
|
||||
cerr << "Error in STS: " << e << endl;
|
||||
exit(1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
pair< vector<octet>, vector<octet> > sts_initiator_role(sign_key_container_t keys, vector<int>& sockets, int server_id)
|
||||
{
|
||||
pair< vector<octet>, vector<octet> > res;
|
||||
try {
|
||||
res = sts_initiator_role_exceptions(keys, sockets, server_id);
|
||||
} catch(char const *e) {
|
||||
cerr << "Error in STS: " << e << endl;
|
||||
exit(1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Send the private inputs masked with a random value.
|
||||
// Receive shares of a preprocessed triple from each SPDZ engine, combine and check the triples are valid.
|
||||
// Add the private input value to triple[0] and send to each spdz engine.
|
||||
void send_private_inputs(const vector<gfp>& values, vector<int>& sockets, int nparties,
|
||||
commsec_t commsec, vector<octet*>& keys)
|
||||
{
|
||||
int num_inputs = values.size();
|
||||
octetStream os;
|
||||
vector< vector<gfp> > triples(num_inputs, vector<gfp>(3));
|
||||
vector<gfp> triple_shares(3);
|
||||
|
||||
// Receive num_inputs triples from SPDZ
|
||||
for (int j = 0; j < nparties; j++)
|
||||
{
|
||||
os.reset_write_head();
|
||||
os.Receive(sockets[j]);
|
||||
os.decrypt_sequence(&commsec[j].second[0],0);
|
||||
os.decrypt(keys[j]);
|
||||
|
||||
for (int j = 0; j < num_inputs; j++)
|
||||
{
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
triple_shares[k].unpack(os);
|
||||
triples[j][k] += triple_shares[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check triple relations
|
||||
for (int i = 0; i < num_inputs; i++)
|
||||
{
|
||||
if (triples[i][0] * triples[i][1] != triples[i][2])
|
||||
{
|
||||
cerr << "Incorrect triple at " << i << ", aborting\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// Send inputs + triple[0], so SPDZ can compute shares of each value
|
||||
os.reset_write_head();
|
||||
for (int i = 0; i < num_inputs; i++)
|
||||
{
|
||||
gfp y = values[i] + triples[i][0];
|
||||
y.pack(os);
|
||||
}
|
||||
for (int j = 0; j < nparties; j++)
|
||||
{
|
||||
octetStream temp = os;
|
||||
temp.encrypt_sequence(&commsec[j].first[0], 0);
|
||||
temp.Send(sockets[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Send public key in clear to each SPDZ engine.
|
||||
void send_public_key(vector<int>& pubkey, int socket)
|
||||
{
|
||||
octetStream os;
|
||||
os.reset_write_head();
|
||||
|
||||
for (unsigned int i = 0; i < pubkey.size(); i++)
|
||||
{
|
||||
os.store(pubkey[i]);
|
||||
}
|
||||
|
||||
os.Send(socket);
|
||||
}
|
||||
|
||||
// Assumes that Scripts/setup-online.sh has been run to compute prime
|
||||
void initialise_fields(const string& dir_prefix)
|
||||
{
|
||||
int lg2;
|
||||
bigint p;
|
||||
|
||||
string filename = dir_prefix + "Params-Data";
|
||||
cout << "loading params from: " << filename << endl;
|
||||
|
||||
ifstream inpf(filename.c_str());
|
||||
if (inpf.fail()) { throw file_error(filename.c_str()); }
|
||||
inpf >> p;
|
||||
inpf >> lg2;
|
||||
|
||||
inpf.close();
|
||||
|
||||
gfp::init_field(p);
|
||||
gf2n::init_field(lg2);
|
||||
}
|
||||
|
||||
// Assumes that client-setup has been run to create key pairs for clients and parties
|
||||
void generate_symmetric_keys(vector<octet*>& keys, vector<int>& client_public_key_ints,
|
||||
sign_key_container_t *sts_key, const string& dir_prefix, int client_no)
|
||||
{
|
||||
unsigned char client_publickey[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char client_secretkey[crypto_box_SECRETKEYBYTES];
|
||||
unsigned char server_publickey[crypto_box_PUBLICKEYBYTES];
|
||||
unsigned char scalarmult_q[crypto_scalarmult_BYTES];
|
||||
crypto_generichash_state h;
|
||||
|
||||
// read client public/secret keys + SPDZ server public keys
|
||||
ifstream keyfile;
|
||||
stringstream client_filename;
|
||||
client_filename << dir_prefix << "Client-Keys-C" << client_no;
|
||||
keyfile.open(client_filename.str().c_str());
|
||||
if (keyfile.fail())
|
||||
throw file_error(client_filename.str());
|
||||
keyfile.read((char*)client_publickey, sizeof client_publickey);
|
||||
if (keyfile.eof())
|
||||
throw end_of_file(client_filename.str(), "client public key" );
|
||||
|
||||
// Convert client public key unsigned char to int, reverse endianness
|
||||
for(unsigned int j = 0; j < client_public_key_ints.size(); j++) {
|
||||
int keybyte = 0;
|
||||
for(unsigned int k = 0; k < 4; k++) {
|
||||
keybyte = keybyte + (((int)client_publickey[j*4+k]) << ((3-k) * 8));
|
||||
}
|
||||
client_public_key_ints[j] = keybyte;
|
||||
}
|
||||
|
||||
keyfile.read((char*)client_secretkey, sizeof client_secretkey);
|
||||
if (keyfile.eof()) {
|
||||
throw end_of_file(client_filename.str(), "client private key" );
|
||||
}
|
||||
|
||||
keyfile.read((char*)sts_key->client_publickey, crypto_sign_PUBLICKEYBYTES);
|
||||
keyfile.read((char*)sts_key->client_secretkey, crypto_sign_SECRETKEYBYTES);
|
||||
// Convert client public key unsigned char to int, reverse endianness
|
||||
sts_key->client_publickey_ints.resize(8);
|
||||
for(unsigned int j = 0; j < sts_key->client_publickey_ints.size(); j++) {
|
||||
int keybyte = 0;
|
||||
for(unsigned int k = 0; k < 4; k++) {
|
||||
keybyte = keybyte + (((int)sts_key->client_publickey[j*4+k]) << ((3-k) * 8));
|
||||
}
|
||||
sts_key->client_publickey_ints[j] = keybyte;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < keys.size(); i++)
|
||||
{
|
||||
keys[i] = new octet[crypto_generichash_BYTES];
|
||||
keyfile.read((char*)server_publickey, crypto_box_PUBLICKEYBYTES);
|
||||
if (keyfile.eof())
|
||||
throw end_of_file(client_filename.str(), "server public key for party " + to_string(i));
|
||||
keyfile.read((char*)(&sts_key->server_publickey[i][0]), crypto_sign_PUBLICKEYBYTES);
|
||||
if (keyfile.eof())
|
||||
throw end_of_file(client_filename.str(), "server public signing key for party " + to_string(i));
|
||||
|
||||
// Derive a shared key from this server's secret key and the client's public key
|
||||
// shared key = h(q || client_secretkey || server_publickey)
|
||||
if (crypto_scalarmult(scalarmult_q, client_secretkey, server_publickey) != 0) {
|
||||
cerr << "Scalar mult failed\n";
|
||||
exit(1);
|
||||
}
|
||||
crypto_generichash_init(&h, NULL, 0U, crypto_generichash_BYTES);
|
||||
crypto_generichash_update(&h, scalarmult_q, sizeof scalarmult_q);
|
||||
crypto_generichash_update(&h, client_publickey, sizeof client_publickey);
|
||||
crypto_generichash_update(&h, server_publickey, sizeof server_publickey);
|
||||
crypto_generichash_final(&h, keys[i], crypto_generichash_BYTES);
|
||||
}
|
||||
keyfile.close();
|
||||
|
||||
cout << "My public key is: ";
|
||||
for (unsigned int j = 0; j < 32; j++)
|
||||
cout << setfill('0') << setw(2) << hex << (int) client_publickey[j];
|
||||
cout << dec << endl;
|
||||
}
|
||||
|
||||
|
||||
// Receive shares of the result and sum together.
|
||||
// Also receive authenticating values.
|
||||
gfp receive_result(vector<int>& sockets, int nparties, commsec_t commsec, vector<octet*>& keys)
|
||||
{
|
||||
vector<gfp> output_values(3);
|
||||
octetStream os;
|
||||
for (int i = 0; i < nparties; i++)
|
||||
{
|
||||
os.reset_write_head();
|
||||
os.Receive(sockets[i]);
|
||||
|
||||
os.decrypt_sequence(&commsec[i].second[0],1);
|
||||
os.decrypt(keys[i]);
|
||||
|
||||
for (unsigned int j = 0; j < 3; j++)
|
||||
{
|
||||
gfp value;
|
||||
value.unpack(os);
|
||||
output_values[j] += value;
|
||||
}
|
||||
}
|
||||
|
||||
if (output_values[0] * output_values[1] != output_values[2])
|
||||
{
|
||||
cerr << "Unable to authenticate output value as correct, aborting." << endl;
|
||||
exit(1);
|
||||
}
|
||||
return output_values[0];
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int my_client_id;
|
||||
int nparties;
|
||||
int salary_value;
|
||||
int finish;
|
||||
int port_base = 14000;
|
||||
sign_key_container_t sts_key;
|
||||
string host_name = "localhost";
|
||||
|
||||
if (argc < 5) {
|
||||
cout << "Usage is external-client <client identifier> <number of spdz parties> "
|
||||
<< "<salary to compare> <finish (0 false, 1 true)> <optional host name, default localhost> "
|
||||
<< "<optional spdz party port base number, default 14000>" << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
my_client_id = atoi(argv[1]);
|
||||
nparties = atoi(argv[2]);
|
||||
salary_value = atoi(argv[3]);
|
||||
finish = atoi(argv[4]);
|
||||
if (argc > 5)
|
||||
host_name = argv[5];
|
||||
if (argc > 6)
|
||||
port_base = atoi(argv[6]);
|
||||
|
||||
sts_key.server_publickey.resize(nparties);
|
||||
for(int i = 0 ; i < nparties; i++) {
|
||||
sts_key.server_publickey[i].resize(crypto_sign_PUBLICKEYBYTES);
|
||||
}
|
||||
|
||||
// init static gfp
|
||||
string prep_data_prefix = get_prep_dir(nparties, 128, gf2n::default_degree());
|
||||
initialise_fields(prep_data_prefix);
|
||||
bigint::init_thread();
|
||||
|
||||
// Generate session keys to decrypt data sent from each spdz engine (party)
|
||||
vector<octet*> session_keys(nparties);
|
||||
vector<int> client_public_key_ints(8);
|
||||
|
||||
generate_symmetric_keys(session_keys, client_public_key_ints, &sts_key, prep_data_prefix, my_client_id);
|
||||
|
||||
// Setup connections from this client to each party socket and send the client public keys
|
||||
vector<int> sockets(nparties);
|
||||
// vector< pair <vector<octet>, vector <octet> > > commseckey(nparties);
|
||||
commsec_t commseckey(nparties);
|
||||
for (int i = 0; i < nparties; i++)
|
||||
{
|
||||
set_up_client_socket(sockets[i], host_name.c_str(), port_base + i);
|
||||
send(sockets[i], (octet*) &my_client_id, sizeof(int));
|
||||
octetStream os;
|
||||
os.store(finish);
|
||||
os.Send(sockets[i]);
|
||||
|
||||
send_public_key(sts_key.client_publickey_ints, sockets[i]);
|
||||
send_public_key(client_public_key_ints, sockets[i]);
|
||||
commseckey[i] = sts_initiator_role(sts_key, sockets, i);
|
||||
}
|
||||
cout << "Finish setup socket connections to SPDZ engines." << endl;
|
||||
|
||||
// Send the inputs to the SPDZ Engines
|
||||
send_private_inputs({salary_value}, sockets, nparties, commseckey, session_keys);
|
||||
cout << "Sent private inputs to each SPDZ engine, waiting for result..." << endl;
|
||||
|
||||
// Get the result back
|
||||
gfp result = receive_result(sockets, nparties, commseckey, session_keys);
|
||||
|
||||
cout << "Winning client id is : " << result << endl;
|
||||
|
||||
for (unsigned int i = 0; i < sockets.size(); i++)
|
||||
close_client_socket(sockets[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user