Semi-honest computation based on threshold semi-homomorphic encryption.

This commit is contained in:
Marcel Keller
2022-02-17 13:21:19 +11:00
parent 61d40b7d83
commit 0f7020d791
129 changed files with 1973 additions and 539 deletions

View File

@@ -25,7 +25,7 @@ void Binary_File_IO::write_to_file(const string filename,
if (start_pos != -1)
{
long write_pos = start_pos * T::size();
long write_pos = file_signature<T>().get_total_length() + start_pos * T::size();
// fill with zeros if needed
for (long i = outf.tellp(); i < write_pos; i++)
outf.put(0);
@@ -50,10 +50,13 @@ void Binary_File_IO::read_from_file(const string filename, vector< T >& buffer,
inf.open(filename, ios::in | ios::binary);
if (inf.fail()) { throw file_missing(filename, "Binary_File_IO.read_from_file expects this file to exist."); }
check_file_signature<T>(inf, filename).get_length();
auto data_start = inf.tellg();
int size_in_bytes = T::size() * buffer.size();
int n_read = 0;
char read_buffer[size_in_bytes];
inf.seekg(start_posn * T::size());
inf.seekg(start_posn * T::size(), iostream::cur);
do
{
inf.read(read_buffer + n_read, size_in_bytes - n_read);
@@ -62,7 +65,9 @@ void Binary_File_IO::read_from_file(const string filename, vector< T >& buffer,
if (inf.eof())
{
stringstream ss;
ss << "Got to EOF when reading from disk (expecting " << size_in_bytes << " bytes).";
ss << "Got to EOF when reading from disk (expecting " << size_in_bytes
<< " bytes from " << (long(data_start) + start_posn * T::size())
<< ").";
throw file_error(ss.str());
}
if (inf.fail())
@@ -74,7 +79,7 @@ void Binary_File_IO::read_from_file(const string filename, vector< T >& buffer,
}
while (n_read < size_in_bytes);
end_posn = inf.tellg() / T::size();
end_posn = (inf.tellg() - data_start) / T::size();
assert (end_posn == start_posn + int(buffer.size()));
//Check if at end of file by getting 1 more char.

View File

@@ -32,6 +32,15 @@ protected:
Buffer<typename T::clear, typename T::clear> buffer;
Timer timer;
// Send my inputs (not generally available)
virtual void send_mine() { throw not_implemented(); }
// Get share for next input of mine (not generally available)
virtual T finalize_mine() { throw not_implemented(); }
// Store share for next input from ``player`` from buffer ``o``
// in ``target`` (not generally available)
virtual void finalize_other(int, T&, octetStream&, int = -1)
{ throw not_implemented(); }
public:
vector<octetStream> os;
int values_input;
@@ -61,18 +70,12 @@ public:
/// Schedule input from other player
virtual void add_other(int player, int n_bits = -1) = 0;
/// Schedule input from all players
void add_from_all(const clear& input, int n_bits = -1);
void add_from_all(const typename T::open_type& input, int n_bits = -1);
/// Send my inputs
virtual void send_mine() = 0;
/// Run input protocol for all players
virtual void exchange();
/// Get share for next input of mine
virtual T finalize_mine() = 0;
/// Store share for next input from ``player`` from buffer ``o`` in ``target``
virtual void finalize_other(int player, T& target, octetStream& o, int n_bits = -1) = 0;
/// Get share for next input from ``player`
/// Get share for next input from ``player``
virtual T finalize(int player, int n_bits = -1);
void raw_input(SubProcessor<T>& proc, const vector<int>& args, int size);

View File

@@ -113,7 +113,7 @@ void Input<T>::add_other(int player, int)
}
template<class T>
void InputBase<T>::add_from_all(const clear& input, int n_bits)
void InputBase<T>::add_from_all(const typename T::open_type& input, int n_bits)
{
for (int i = 0; i < P->num_players(); i++)
if (i == P->my_num())

View File

@@ -106,6 +106,7 @@ enum
MATMULSM = 0xAB,
CONV2DS = 0xAC,
CHECK = 0xAF,
PRIVATEOUTPUT = 0xAD,
// Data access
TRIPLE = 0x50,
BIT = 0x51,
@@ -127,6 +128,7 @@ enum
INPUTMIXEDREG = 0xF3,
RAWINPUT = 0xF4,
INPUTPERSONAL = 0xF5,
SENDPERSONAL = 0xF6,
STARTINPUT = 0x61,
STOPINPUT = 0x62,
READSOCKETC = 0x63,

View File

@@ -200,14 +200,17 @@ void BaseInstruction::parse_operands(istream& s, int pos, int file_pos)
case USE:
case USE_INP:
case USE_EDABIT:
case DIGESTC:
case INPUTMASK:
case GINPUTMASK:
get_ints(r, s, 2);
n = get_int(s);
break;
case STARTPRIVATEOUTPUT:
case GSTARTPRIVATEOUTPUT:
case STOPPRIVATEOUTPUT:
case GSTOPPRIVATEOUTPUT:
case DIGESTC:
get_ints(r, s, 2);
n = get_int(s);
break;
throw runtime_error("two-stage private output not supported any more");
case USE_MATMUL:
get_ints(r, s, 3);
n = get_int(s);
@@ -237,8 +240,6 @@ void BaseInstruction::parse_operands(istream& s, int pos, int file_pos)
case PRINTREGB:
case GPRINTREG:
case LDINT:
case INPUTMASK:
case GINPUTMASK:
case INV2M:
case CONDPRINTSTR:
case CONDPRINTSTRB:
@@ -290,6 +291,8 @@ void BaseInstruction::parse_operands(istream& s, int pos, int file_pos)
case RAWINPUT:
case GRAWINPUT:
case INPUTPERSONAL:
case SENDPERSONAL:
case PRIVATEOUTPUT:
case TRUNC_PR:
case RUN_TAPE:
num_var_args = get_int(s);
@@ -599,6 +602,7 @@ int BaseInstruction::get_reg_type() const
case PUBINPUT:
case FLOATOUTPUT:
case READSOCKETC:
case PRIVATEOUTPUT:
return CINT;
default:
if (is_gf2n_instruction())
@@ -738,10 +742,16 @@ unsigned BaseInstruction::get_max_reg(int reg_type) const
skip = 1;
break;
case INPUTPERSONAL:
case PRIVATEOUTPUT:
size_offset = -2;
offset = 2;
skip = 4;
break;
case SENDPERSONAL:
size_offset = -2;
offset = 2;
skip = 5;
break;
case READSOCKETS:
case READSOCKETC:
case READSOCKETINT:
@@ -939,13 +949,11 @@ inline void Instruction::execute(Processor<sint, sgf2n>& Proc) const
break;
case INPUTMASK:
Procp.DataF.get_input(Proc.get_Sp_ref(r[0]), Proc.temp.rrp, n);
if (n == Proc.P.my_num())
Proc.temp.rrp.output(Proc.private_output, false);
Proc.write_Cp(r[1], Proc.temp.rrp);
break;
case GINPUTMASK:
Proc2.DataF.get_input(Proc.get_S2_ref(r[0]), Proc.temp.ans2, n);
if (n == Proc.P.my_num())
Proc.temp.ans2.output(Proc.private_output, false);
Proc.write_C2(r[1], Proc.temp.ans2);
break;
case INPUT:
sint::Input::template input<IntInput<typename sint::clear>>(Proc.Procp, start, size);
@@ -974,6 +982,12 @@ inline void Instruction::execute(Processor<sint, sgf2n>& Proc) const
case INPUTPERSONAL:
Proc.Procp.input_personal(start);
return;
case SENDPERSONAL:
Proc.Procp.send_personal(start);
return;
case PRIVATEOUTPUT:
Proc.Procp.private_output(start);
return;
// Note: Fp version has different semantics for NOTC than GNOTC
case NOTC:
to_bigint(Proc.temp.aa, Proc.read_Cp(r[1]));
@@ -1202,18 +1216,6 @@ inline void Instruction::execute(Processor<sint, sgf2n>& Proc) const
Proc.binary_output.write((char*) &tmp, sizeof(double));
}
break;
case STARTPRIVATEOUTPUT:
Proc.privateOutputp.start(n,r[0],r[1]);
break;
case GSTARTPRIVATEOUTPUT:
Proc.privateOutput2.start(n,r[0],r[1]);
break;
case STOPPRIVATEOUTPUT:
Proc.privateOutputp.stop(n,r[0],r[1]);
break;
case GSTOPPRIVATEOUTPUT:
Proc.privateOutput2.stop(n,r[0],r[1]);
break;
case PREP:
Procp.DataF.get(Proc.Procp.get_S(), r, start, size);
return;

View File

@@ -97,12 +97,19 @@ Machine<sint, sgf2n>::Machine(int my_number, Names& playerNames,
// initialize persistence if necessary
for (auto& prog : progs)
{
if (prog.writes_persistance)
if (prog.writes_persistence)
{
string filename = Binary_File_IO::filename(my_number);
ifstream pers(filename);
if (pers.fail())
ofstream pers(filename, ios::binary);
try
{
check_file_signature<sint>(pers, filename);
}
catch (signature_mismatch&)
{
ofstream pers(filename, ios::binary);
file_signature<sint>().output(pers);
}
break;
}
}
@@ -418,12 +425,14 @@ void Machine<sint, sgf2n>::run()
cerr << "Full broadcast" << endl;
#endif
#ifdef CHOP_MEMORY
// Reduce memory size to speed up
unsigned max_size = 1 << 20;
if (M2.size_s() > max_size)
M2.resize_s(max_size);
if (Mp.size_s() > max_size)
Mp.resize_s(max_size);
#endif
// Write out the memory to use next time
ofstream outf(memory_filename(), ios::out | ios::binary);

View File

@@ -44,9 +44,9 @@ class Memory
static void check_index(const vector<U>& M, size_t i)
{
(void) M, (void) i;
#ifdef NO_CHECK_INDEX
#ifndef NO_CHECK_INDEX
if (i >= M.size())
throw overflow("memory", i, M.size());
throw overflow(U::type_string() + " memory", i, M.size());
#endif
}

View File

@@ -19,6 +19,9 @@ void MemoryPart<T>::minimum_size(size_t size)
{
if (size > this->size())
this->resize(size);
#ifdef DEBUG_MEMORY_SIZE
cerr << T::type_string() << " memory has now size " << this->size() << endl;
#endif
}
catch (bad_alloc&)
{
@@ -58,9 +61,9 @@ istream& operator>>(istream& s,Memory<T>& M)
int len;
s >> len;
M.resize_s(len);
M.MS.minimum_size(len);
s >> len;
M.resize_c(len);
M.MC.minimum_size(len);
s.seekg(1, istream::cur);
for (unsigned int i=0; i<M.MS.size(); i++)

View File

@@ -17,16 +17,16 @@ class PrivateOutput
typedef typename T::open_type open_type;
SubProcessor<T>& proc;
typename T::MAC_Check MC;
deque<open_type> masks;
public:
PrivateOutput(SubProcessor<T>& proc) : proc(proc) { };
PrivateOutput(SubProcessor<T>& proc);
~PrivateOutput();
void start(int player, int target, int source);
void stop(int player, int dest, int source);
T start(int player, const T& source);
typename T::clear stop(int player, const typename T::clear& masked);
void prepare_sending(const T& source, int player);
void exchange();
typename T::clear finalize(int player);
};
#endif /* PROCESSOR_PRIVATEOUTPUT_H_ */

View File

@@ -7,13 +7,21 @@
#include "Processor.h"
template<class T>
void PrivateOutput<T>::start(int player, int target, int source)
PrivateOutput<T>::PrivateOutput(SubProcessor<T>& proc) :
proc(proc), MC(proc.MC.get_alphai())
{
proc.get_S_ref(target) = start(player, proc.get_S_ref(source));
MC.init_open(proc.P);
MC.set_prep(proc.DataF);
}
template<class T>
T PrivateOutput<T>::start(int player, const T& source)
PrivateOutput<T>::~PrivateOutput()
{
MC.Check(proc.P);
}
template<class T>
void PrivateOutput<T>::prepare_sending(const T& source, int player)
{
assert (player < proc.P.num_players());
open_type mask;
@@ -24,26 +32,25 @@ T PrivateOutput<T>::start(int player, const T& source)
if (player == proc.P.my_num())
masks.push_back(mask);
return res;
MC.prepare_open(res);
}
template<class T>
void PrivateOutput<T>::stop(int player, int dest, int source)
void PrivateOutput<T>::exchange()
{
auto& value = proc.get_C_ref(dest);
value = stop(player, proc.get_C_ref(source));
if (proc.Proc)
value.output(proc.Proc->private_output, false);
MC.exchange(proc.P);
}
template<class T>
typename T::clear PrivateOutput<T>::stop(int player, const typename T::clear& source)
typename T::clear PrivateOutput<T>::finalize(int player)
{
typename T::clear value;
auto res = MC.finalize_open();
if (player == proc.P.my_num())
{
value = source - masks.front();
res -= masks.front();
masks.pop_front();
}
return value;
return res;
}

View File

@@ -71,6 +71,8 @@ public:
void conv2ds(const Instruction& instruction);
void input_personal(const vector<int>& args);
void send_personal(const vector<int>& args);
void private_output(const vector<int>& args);
CheckVector<T>& get_S()
{
@@ -110,7 +112,6 @@ public:
ifstream private_input;
ifstream public_input;
ofstream public_output;
ofstream private_output;
ofstream binary_output;
int sent, rounds;
@@ -172,9 +173,6 @@ class Processor : public ArithmeticProcessor
SubProcessor<sgf2n> Proc2;
SubProcessor<sint> Procp;
typename sgf2n::PrivateOutput privateOutput2;
typename sint::PrivateOutput privateOutputp;
unsigned int PC;
TempVars<sint, sgf2n> temp;

View File

@@ -4,9 +4,8 @@
#include "Processor/Processor.h"
#include "Processor/Program.h"
#include "GC/square64.h"
#include "SpecificPrivateOutput.h"
#include "Protocols/ReplicatedInput.hpp"
#include "Protocols/ReplicatedPrivateOutput.hpp"
#include "Processor/ProcessorBase.hpp"
#include "GC/Processor.hpp"
#include "GC/ShareThread.hpp"
@@ -63,7 +62,6 @@ Processor<sint, sgf2n>::Processor(int thread_num,Player& P,
share_thread(DataF.DataFb, P, machine.get_bit_mac_key()),
Procb(machine.bit_memories),
Proc2(*this,MC2,DataF.DataF2,P),Procp(*this,MCp,DataF.DataFp,P),
privateOutput2(Proc2),privateOutputp(Procp),
external_clients(P.my_num()),
binary_file_io(Binary_File_IO())
{
@@ -74,7 +72,6 @@ Processor<sint, sgf2n>::Processor(int thread_num,Player& P,
private_input_filename = (get_filename(PREP_DIR "Private-Input-",true));
private_input.open(private_input_filename.c_str());
public_output.open(get_filename(PREP_DIR "Public-Output-",true).c_str(), ios_base::out);
private_output.open(get_filename(PREP_DIR "Private-Output-",true).c_str(), ios_base::out);
binary_output.open(
get_parameterized_filename(P.my_num(), thread_num,
PREP_DIR "Binary-Output"), ios_base::out);
@@ -654,6 +651,37 @@ void SubProcessor<T>::input_personal(const vector<int>& args)
S[args[i + 2] + j] = input.finalize(args[i + 1]);
}
template<class T>
void SubProcessor<T>::private_output(const vector<int>& args)
{
typename T::PrivateOutput output(*this);
for (size_t i = 0; i < args.size(); i += 4)
for (int j = 0; j < args[i]; j++)
{
int player = args[i + 1];
output.prepare_sending(S.at(args[i + 3] + j), player);
}
output.exchange();
for (size_t i = 0; i < args.size(); i += 4)
for (int j = 0; j < args[i]; j++)
C.at(args[i + 2] + j) = output.finalize(args[i + 1]);
}
template<class T>
void SubProcessor<T>::send_personal(const vector<int>& args)
{
octetStreams to_send(P), to_receive(P);
for (size_t i = 0; i < args.size(); i += 5)
if (args[i + 3] == P.my_num())
for (int j = 0; j < args[i]; j++)
C[args[i + 4] + j].pack(to_send[args[i + 1]]);
P.send_receive_all(to_send, to_receive);
for (size_t i = 0; i < args.size(); i += 5)
if (args[i + 1] == P.my_num())
for (int j = 0; j < args[i]; j++)
C[args[i + 2] + j].unpack(to_receive[args[i + 3]]);
}
template<class sint, class sgf2n>
typename sint::clear Processor<sint, sgf2n>::get_inverse2(unsigned m)
{

View File

@@ -23,7 +23,7 @@ void Program::compute_constants()
max_mem[reg_type] = max(max_mem[reg_type],
p[i].get_mem(RegType(reg_type)));
}
writes_persistance |= p[i].opcode == WRITEFILESHARE;
writes_persistence |= p[i].opcode == WRITEFILESHARE;
}
}

View File

@@ -30,10 +30,10 @@ class Program
public:
bool writes_persistance;
bool writes_persistence;
Program(int nplayers) : offline_data_used(nplayers),
unknown_usage(false), writes_persistance(false)
unknown_usage(false), writes_persistence(false)
{ compute_constants(); }
// Read in a program

View File

@@ -0,0 +1,65 @@
/*
* SpecificPrivateOutput.h
*
*/
#ifndef PROCESSOR_SPECIFICPRIVATEOUTPUT_H_
#define PROCESSOR_SPECIFICPRIVATEOUTPUT_H_
template<class T>
class SpecificPrivateOutput
{
deque<T> secrets;
vector<typename T::PO*> pos;
Player& P;
vector<bool> active;
public:
SpecificPrivateOutput(SubProcessor<T>& proc) :
P(proc.P)
{
for (int i = 0; i < P.num_players(); i++)
pos.push_back(new typename T::PO(proc.P));
active.resize(P.num_players());
}
~SpecificPrivateOutput()
{
for (auto& x : pos)
delete x;
}
void prepare_sending(const T& secret, int player)
{
pos[player]->prepare_sending(secret, player);
if (P.my_num() == player)
secrets.push_back(secret);
active[player] = true;
}
void exchange()
{
for (int i = 0; i < this->P.num_players(); i++)
if (active[i])
{
if (i == this->P.my_num())
pos[i]->receive();
else
pos[i]->send(i);
}
}
typename T::clear finalize(int player)
{
if (player == this->P.my_num())
{
T secret = secrets.front();
secrets.pop_front();
return pos[player]->finalize(secret);
}
else
return {};
}
};
#endif /* PROCESSOR_SPECIFICPRIVATEOUTPUT_H_ */