Skip to content

Commit

Permalink
ECDSA, more replicated secret sharing.
Browse files Browse the repository at this point in the history
  • Loading branch information
mkskeller committed Aug 6, 2019
1 parent 5ef7058 commit bd60197
Show file tree
Hide file tree
Showing 143 changed files with 2,186 additions and 1,036 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
The changelog explains changes pulled through from the private development repository. Bug fixes and small enchancements are committed between releases and not documented here.
The changelog explains changes pulled through from the private development repository. Bug fixes and small enhancements are committed between releases and not documented here.

## 0.1.1 (Aug 6, 2019)

- ECDSA
- Loop unrolling with budget as in [HyCC](https://thomaschneider.de/papers/BDKKS18.pdf)
- Malicious replicated secret sharing for binary circuits
- New variants of malicious replicated secret over rings in [Use your Brain!](https://eprint.iacr.org/2019/164)
- MASCOT for any prime larger than 2^64
- Private fixed- and floating-point inputs

## 0.1.0 (Jun 7, 2019)

Expand Down
12 changes: 10 additions & 2 deletions CONFIG
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ USE_GF2N_LONG = 1

# set to -march=<architecture> for optimization
# SSE4.2 is required homomorphic encryption in GF(2^n) when compiling with clang
# AES-NI is required for BMR
# PCLMUL is required for GF(2^128) computation
# AES-NI and PCLMUL are not required
# AVX is required for oblivious transfer (OT)
# AVX2 support (Haswell or later) is used to optimize OT
# AVX/AVX2 is required for replicated binary secret sharing
# BMI2 is used to optimize multiplication modulo a prime
Expand Down Expand Up @@ -63,3 +63,11 @@ endif
CFLAGS += $(ARCH) $(MY_CFLAGS) $(GDEBUG) -Wextra -Wall $(OPTIM) -I$(ROOT) -pthread $(PROF) $(DEBUG) $(MOD) $(MEMPROTECT) $(GF2N_LONG) $(PREP_DIR) -std=c++11 -Werror
CPPFLAGS = $(CFLAGS)
LD = $(CXX)

ECLIB = -lcryptopp

ifeq ($(OS), Darwin)
ifeq ($(USE_NTL),1)
CFLAGS += -Wno-error=unused-parameter
endif
endif
2 changes: 1 addition & 1 deletion Check-Offline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ void check_bits(const typename T::mac_key_type& key,int N,vector<Sub_Data_Files<

cout << n << " bits of type " << T::type_string() << endl;
}
catch (bad_value& e)
catch (exception& e)
{
cout << "Error after " << n << " bits of type " << T::type_string() << endl;
}
Expand Down
21 changes: 19 additions & 2 deletions Compiler/allocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,22 +573,39 @@ def eliminate_dead_code(self):
merge_nodes = self.open_nodes
count = 0
open_count = 0
stats = defaultdict(lambda: 0)
for i,inst in zip(xrange(len(instructions) - 1, -1, -1), reversed(instructions)):
# remove if instruction has result that isn't used
unused_result = not G.degree(i) and len(list(inst.get_def())) \
and reduce(operator.and_, (reg.can_eliminate for reg in inst.get_def())) \
and not isinstance(inst, (DoNotEliminateInstruction))
stop_node = G.get_attr(i, 'stop')
unused_startopen = stop_node != -1 and instructions[stop_node] is None
if unused_result or unused_startopen:
def eliminate(i):
G.remove_node(i)
merge_nodes.discard(i)
stats[type(instructions[i]).__name__] += 1
instructions[i] = None
if unused_result or unused_startopen:
eliminate(i)
count += 1
if unused_startopen:
open_count += len(inst.args)
# remove unnecessary stack instructions
# left by optimization with budget
if isinstance(inst, popint_class) and \
(not G.degree(i) or (G.degree(i) == 1 and
isinstance(instructions[list(G[i])[0]], StackInstruction))) \
and \
inst.args[0].can_eliminate and \
len(G.pred[i]) == 1 and \
isinstance(instructions[list(G.pred[i])[0]], pushint_class):
eliminate(list(G.pred[i])[0])
eliminate(i)
count += 2
if count > 0:
print 'Eliminated %d dead instructions, among which %d opens' % (count, open_count)
print 'Eliminated %d dead instructions, among which %d opens: %s' \
% (count, open_count, dict(stats))

def print_graph(self, filename):
f = open(filename, 'w')
Expand Down
2 changes: 2 additions & 0 deletions Compiler/instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,7 @@ def add_usage(self, req_node):
def execute(self):
self.args[0].value = _python_input("Enter player %d's input:" % self.args[1]) % program.P

@base.vectorize
class inputfix(base.TextInputInstruction):
__slots__ = []
code = base.opcodes['INPUTFIX']
Expand All @@ -881,6 +882,7 @@ def add_usage(self, req_node):
req_node.increment((self.field_type, 'input', player), \
self.get_size())

@base.vectorize
class inputfloat(base.TextInputInstruction):
__slots__ = []
code = base.opcodes['INPUTFLOAT']
Expand Down
2 changes: 1 addition & 1 deletion Compiler/instructions_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,7 @@ class IntegerInstruction(Instruction):
__slots__ = []
arg_format = ['ciw', 'ci', 'ci']

class StackInstruction(Instruction):
class StackInstruction(DoNotEliminateInstruction):
""" Base class for thread-local stack instructions. """
__slots__ = []

Expand Down
95 changes: 74 additions & 21 deletions Compiler/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -804,14 +804,17 @@ def decorator(loop_body):
return decorator

def for_range_parallel(n_parallel, n_loops):
return map_reduce_single(n_parallel, n_loops, \
lambda *x: [], lambda *x: [])
return map_reduce_single(n_parallel, n_loops)

def map_reduce_single(n_parallel, n_loops, initializer, reducer, mem_state=None):
if not isinstance(n_parallel, int):
def for_range_opt(n_loops):
return map_reduce_single(None, n_loops)

def map_reduce_single(n_parallel, n_loops, initializer=lambda *x: [],
reducer=lambda *x: [], mem_state=None):
if not (isinstance(n_parallel, int) or n_parallel is None):
raise CompilerException('Number of parallel executions' \
'must be constant')
n_parallel = n_parallel or 1
n_parallel = 1 if n_parallel is 0 else n_parallel
if mem_state is None:
# default to list of MemValues to allow varying types
mem_state = [MemValue(x) for x in initializer()]
Expand All @@ -820,33 +823,83 @@ def map_reduce_single(n_parallel, n_loops, initializer, reducer, mem_state=None)
# use Arrays for multithread version
use_array = True
def decorator(loop_body):
if isinstance(n_loops, int):
loop_rounds = n_loops / n_parallel \
if n_parallel < n_loops else 0
else:
loop_rounds = n_loops / n_parallel
my_n_parallel = n_parallel
if isinstance(n_parallel, int):
if isinstance(n_loops, int):
loop_rounds = n_loops / n_parallel \
if n_parallel < n_loops else 0
else:
loop_rounds = n_loops / n_parallel
def write_state_to_memory(r):
if use_array:
mem_state.assign(r)
else:
# cannot do mem_state = [...] due to scope issue
for j,x in enumerate(r):
mem_state[j].write(x)
# will be optimized out if n_loops <= n_parallel
@for_range(loop_rounds)
def f(i):
state = tuplify(initializer())
for k in range(n_parallel):
j = i * n_parallel + k
state = reducer(tuplify(loop_body(j)), state)
r = reducer(mem_state, state)
write_state_to_memory(r)
if n_parallel is not None:
# will be optimized out if n_loops <= n_parallel
@for_range(loop_rounds)
def f(i):
state = tuplify(initializer())
for k in range(n_parallel):
j = i * n_parallel + k
state = reducer(tuplify(loop_body(j)), state)
r = reducer(mem_state, state)
write_state_to_memory(r)
else:
n_parallel_reg = MemValue(regint(0))
parent_block = get_block()
@while_do(lambda x: x + n_parallel_reg <= n_loops, regint(0))
def _(i):
state = tuplify(initializer())
k = 0
block = get_block()
while k < n_loops and (len(get_block()) < get_program().budget \
or k == 0) \
and block is get_block():
j = i + k
state = reducer(tuplify(loop_body(j)), state)
k += 1
r = reducer(mem_state, state)
write_state_to_memory(r)
global n_opt_loops
n_opt_loops = k
n_parallel_reg.write(k)
return i + k
my_n_parallel = n_opt_loops
loop_rounds = n_loops / my_n_parallel
blocks = get_tape().basicblocks
n_to_merge = 5
if loop_rounds == 1 and parent_block is blocks[-n_to_merge]:
# merge blocks started by if and do_while
def exit_elimination(block):
if block.exit_condition is not None:
for reg in block.exit_condition.get_used():
reg.can_eliminate = True
exit_elimination(parent_block)
merged = parent_block
merged.exit_condition = blocks[-1].exit_condition
merged.exit_block = blocks[-1].exit_block
assert parent_block is blocks[-n_to_merge]
assert blocks[-n_to_merge + 1] is \
get_tape().req_node.children[-1].nodes[0].blocks[0]
for block in blocks[-n_to_merge + 1:]:
merged.instructions += block.instructions
exit_elimination(block)
del blocks[-n_to_merge + 1:]
del get_tape().req_node.children[-1]
merged.children = []
get_tape().active_basicblock = merged
else:
req_node = get_tape().req_node.children[-1].nodes[0]
req_node.children[0].aggregator = lambda x: loop_rounds * x[0]
if isinstance(n_loops, int):
state = mem_state
for j in range(loop_rounds * n_parallel, n_loops):
for j in range(loop_rounds * my_n_parallel, n_loops):
state = reducer(tuplify(loop_body(j)), state)
else:
@for_range(loop_rounds * n_parallel, n_loops)
@for_range(loop_rounds * my_n_parallel, n_loops)
def f(j):
r = reducer(tuplify(loop_body(j)), mem_state)
write_state_to_memory(r)
Expand Down
13 changes: 9 additions & 4 deletions Compiler/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def __init__(self, args, options, param=-1, assemblymode=False):
self.free_threads = set()
self.public_input_file = open(self.programs_dir + '/Public-Input/%s' % self.name, 'w')
self.types = {}
self.budget = int(self.options.budget)
self.to_merge = [Compiler.instructions.asm_open_class, \
Compiler.instructions.gasm_open_class, \
Compiler.instructions.muls_class, \
Expand All @@ -80,8 +81,8 @@ def __init__(self, args, options, param=-1, assemblymode=False):
Compiler.instructions.gdotprods_class, \
Compiler.instructions.asm_input_class, \
Compiler.instructions.gasm_input_class,
Compiler.instructions.inputfix,
Compiler.instructions.inputfloat]
Compiler.instructions.inputfix_class,
Compiler.instructions.inputfloat_class]
import Compiler.GC.instructions as gc
self.to_merge += [gc.ldmsdi, gc.stmsdi, gc.ldmsd, gc.stmsd, \
gc.stmsdci, gc.xors, gc.andrs, gc.ands, gc.inputb]
Expand Down Expand Up @@ -423,6 +424,7 @@ def __init__(self, name, program):
self.req_node = self.req_tree
self.basicblocks = []
self.purged = False
self.block_counter = 0
self.active_basicblock = None
self.start_new_basicblock()
self._is_empty = False
Expand All @@ -438,7 +440,6 @@ def __init__(self, parent, name, scope, exit_condition=None):
self.parent = parent
self.instructions = []
self.name = name
self.index = len(parent.basicblocks)
self.open_queue = []
self.exit_condition = exit_condition
self.exit_block = None
Expand All @@ -452,6 +453,9 @@ def __init__(self, parent, name, scope, exit_condition=None):
self.alloc_pool = defaultdict(set)
self.purged = False

def __len__(self):
return len(self.instructions)

def new_reg(self, reg_type, size=None):
return self.parent.new_reg(reg_type, size=size)

Expand Down Expand Up @@ -520,7 +524,8 @@ def start_new_basicblock(self, scope=False, name=''):
# use False because None means no scope
if scope is False:
scope = self.active_basicblock
suffix = '%s-%d' % (name, len(self.basicblocks))
suffix = '%s-%d' % (name, self.block_counter)
self.block_counter += 1
sub = self.BasicBlock(self, self.name + '-' + suffix, scope)
self.basicblocks.append(sub)
self.active_basicblock = sub
Expand Down
11 changes: 9 additions & 2 deletions Compiler/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ def pop(cls):

@vectorized_classmethod
def get_random(cls, bit_length):
""" Public insecure randomness """
if isinstance(bit_length, int):
bit_length = regint(bit_length)
res = cls()
Expand Down Expand Up @@ -914,34 +915,39 @@ def protect_memory(cls, start, end):
@vectorized_classmethod
@set_instruction_type
def get_input_from(cls, player):
""" Secret input """
res = cls()
asm_input(res, player)
return res

@vectorized_classmethod
@set_instruction_type
def get_random_triple(cls):
""" Secret random triple according to security model """
res = (cls(), cls(), cls())
triple(*res)
return res

@vectorized_classmethod
@set_instruction_type
def get_random_bit(cls):
""" Secret random bit according to security model """
res = cls()
bit(res)
return res

@vectorized_classmethod
@set_instruction_type
def get_random_square(cls):
""" Secret random square according to security model """
res = (cls(), cls())
square(*res)
return res

@vectorized_classmethod
@set_instruction_type
def get_random_inverse(cls):
""" Secret random inverse according to security model """
res = (cls(), cls())
inverse(*res)
return res
Expand Down Expand Up @@ -1110,6 +1116,7 @@ class sint(_secret, _int):

@vectorized_classmethod
def get_random_int(cls, bits):
""" Secret random n-bit number according to security model """
res = sint()
comparison.PRandInt(res, bits)
return res
Expand Down Expand Up @@ -2408,7 +2415,7 @@ class sfix(_fix):
int_type = sint
clear_type = cfix

@classmethod
@vectorized_classmethod
def get_input_from(cls, player):
v = cls.int_type()
inputfix(v, cls.f, player)
Expand Down Expand Up @@ -2734,7 +2741,7 @@ def convert_float(v, vlen, plen):
'with %d exponent bits' % (vv, plen))
return v, p, z, s

@classmethod
@vectorized_classmethod
def get_input_from(cls, player):
v = sint()
p = sint()
Expand Down
25 changes: 25 additions & 0 deletions ECDSA/Fake-ECDSA.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Fake-ECDSA.cpp
*
*/

#include "ECDSA/P256Element.h"
#include "Tools/mkpath.h"

#include "Protocols/fake-stuff.hpp"
#include "Protocols/Share.hpp"
#include "Processor/Data_Files.hpp"

int main()
{
P256Element::init();
P256Element::Scalar key;
gf2n key2;
string prefix = PREP_DIR "ECDSA/";
mkdir_p(prefix.c_str());
ofstream outf;
write_online_setup(outf, prefix, P256Element::Scalar::pr(), 0, false);
generate_mac_keys<Share<P256Element::Scalar>>(key, key2, 2, prefix);
make_mult_triples<Share<P256Element::Scalar>>(key, 2, 1000, false, prefix);
make_inverse<Share<P256Element::Scalar>>(key, 2, 1000, false, prefix);
}
Loading

0 comments on commit bd60197

Please sign in to comment.