diff --git a/Circuits/README.txt b/Circuits/README.txt index 55fdf5c5..245890b5 100644 --- a/Circuits/README.txt +++ b/Circuits/README.txt @@ -94,3 +94,47 @@ The list of all main files (excluding test ones) to convert (and how to do so) are given in ./convert.sh +------------------------------------------------------------- + +There is a program to utilize YoSys synthesis tools to produce +a Bristol Fashion circuit representation. This is untested by the +SCALE maintainers, but has been provided by Mark Will. + + +./convert_yosys.py --help +usage: convert_yosys.py [-h] [-t TOP_MODULE] [-sy] [-a ADD_FILE] + [-l {verilog,vhdl}] [-v] + input_file output_file + +positional arguments: + input_file File to be converted + output_file Bristol output file + +optional arguments: + -h, --help show this help message and exit + -t TOP_MODULE, --top_module TOP_MODULE + Top module of the design + -sy, --skip_yosys Input file is now assumed to have been synthesised. + This allows you to run Yosys manually + -a ADD_FILE, --add_file ADD_FILE + Add more files to the design + -l {verilog,vhdl}, --lang {verilog,vhdl} + Input langauge. Note vhdl requires Yosys to be built + with Verific support, probably better to convert to + Verilog for this converter + -v, --verbose + + +So a basic use could be the following: + +> cat mult64_2.v +module mult64_2 (a,b,res); + input signed [63:0] a; + input signed [63:0] b; + output [127:0] res; + assign res = (a * b); +endmodule + +> ./convert_yosys.py mult64_2.v mult64_2.txt + + diff --git a/Circuits/convert_yosys.py b/Circuits/convert_yosys.py new file mode 100644 index 00000000..72540df3 --- /dev/null +++ b/Circuits/convert_yosys.py @@ -0,0 +1,408 @@ +#!/usr/bin/python3 + +# Author: Mark Will +# Copyright (c) 2019 Acronis Asia R&D Pte Ltd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import argparse +import logging +import re +import subprocess +from functools import reduce + +# Handle arguments +parser = argparse.ArgumentParser() +parser.add_argument( + "input_file", + help = "File to be converted", + type = str +) +parser.add_argument( + "output_file", + help = "Bristol output file", + type = str +) +parser.add_argument( + "-t", + "--top_module", + help = "Top module of the design", + type = str +) +parser.add_argument( + "-sy", + "--skip_yosys", + help = "Input file is now assumed to have been synthesised. This allows you to run Yosys manually", + action = "store_true" +) +parser.add_argument( + "-a", + "--add_file", + help = "Add more files to the design", + action = 'append', + type = str +) +parser.add_argument( + "-l", + "--lang", + help = """ + Input langauge. Note vhdl requires Yosys to be built with Verific support, + probably better to convert to Verilog for this converter + """, + type = str, + choices = ["verilog", "vhdl"], + default = "verilog" +) +parser.add_argument( + "-v", + "--verbose", + action = "store_true" +) +args = parser.parse_args() + +if args.verbose: + logging.basicConfig(level=logging.INFO) +else: + logging.basicConfig(level=logging.ERROR) + +if args.top_module == None: + tm = re.findall(r"([^/.]+)\.[^/]*$", args.input_file) + if len(tm) != 1: + tm = [''] + args.top_module = tm[0] + logging.info("Missing top module, assuming input filename: %s" % args.top_module) + + +if not args.skip_yosys: + # Make sure yosys is installed and in the path + try: + with subprocess.Popen(["yosys", "-V"], stdout=subprocess.PIPE) as proc: + proc.wait() + version = re.findall(r"Yosys (\d+\.\d+)", str(proc.stdout.read())) + if len(version) == 1: + logging.info("Using Yosys version %s" % version[0]) + else: + raise Exception("Yosys version not found.") + except Exception as e: + logging.error(e) + logging.error("Yosys not found. Please install it (http://www.clifford.at/yosys/download.html)") + exit() + # Process the design + try: + pipe_stdout = subprocess.PIPE if not args.verbose else None + with subprocess.Popen(["yosys"], stdin = subprocess.PIPE, stdout = pipe_stdout) as proc: + # Please feel free to change the commands or use -sy to manually run Yosys + if args.lang == 'verilog': + read_option = "-sv" + else: + read_option = "-vhdl" + input_files = [args.input_file] + input_files += args.add_file if args.add_file else [] + for f in input_files: + proc.stdin.write(("read %s %s\n" % (read_option, f)).encode()) + proc.stdin.write(("synth -top %s\n" % args.top_module).encode()) + # Needed to reduce the gates to ones supported by the Bristol format + proc.stdin.write(b"proc\n") + proc.stdin.write(b"opt\n") + proc.stdin.write(b"fsm\n") + proc.stdin.write(b"flatten\n") + proc.stdin.write(b"memory\n") + proc.stdin.write(b"opt\n") + proc.stdin.write(b"techmap\n") + proc.stdin.write(b"opt\n") + proc.stdin.write(b"wreduce\n") + # This can be slow + #proc.stdin.write(b"freduce -vv\n") + proc.stdin.write(b"clean\n") + proc.stdin.write(b"abc -g AND\n") + proc.stdin.write(b"opt_reduce -fine\n") + for i in range(5): + proc.stdin.write(b"abc -g XOR,AND\n") + proc.stdin.write(b"opt -fine\n") + proc.stdin.write(b"clean\n") + proc.stdin.write(b"flatten\n") + proc.stdin.write(b"torder\n") + # Note that -nohex and -noattr are required + proc.stdin.write(("write_verilog -nohex -noattr %s.yosys.v\n" % args.input_file).encode()) + out = proc.communicate(input=b"exit") + if not args.verbose: + pstdout = out[0].decode("utf-8") + logging.info(pstdout) + errors = re.findall(r"\nERROR: ([^\n]*)\n", pstdout) + for error in errors: + logging.error(error) + if args.lang == 'vhdl' and 'Verific' in error: + logging.error("Note that Yosys needs to be built with Verific support. You could also try https://github.com/pliu6/vhd2vl.git to convert your VHDL code to Verilog.") + except Exception as e: + logging.error(e) + exit() + +# Convert yosys verilog output to Bristol format +# Note only one module should be here. +wires = [] +inputs = [] +outputs = [] +assigns = [] +logging.info("Loading design from Yosys") +filename = "%s.yosys.v" % args.input_file if not args.skip_yosys else args.input_file +with open(filename, 'r') as f: + modules = 0 + while True: + line = f.readline() + if not line: + break + line = line.strip() + if line.startswith('wire'): + wires.append(line[5:-1]) + elif line.startswith('input'): + inputs.append(line[6:-1]) + elif line.startswith('output'): + outputs.append(line[7:-1]) + elif line.startswith('assign'): + assigns.append(line[7:-1]) + elif line.startswith('module'): + modules += 1 + assert modules == 1 + + + +num_input_wires = 0 +num_output_wires = 0 +wire_mapping = {} +input_wire = 0 +input_lens = [] +output_lens = [] +prop_lens = {} + +# Inputs are first wires +logging.info("Processing input wires") + +def getIOProp(line): + line = line.strip() + arr = re.findall(r"\[([0-9]+)\:([0-9]+)\]", line) + c = 1 + if arr: + c += (reduce(lambda a,b: a + int(b), [0] + list(arr[0]))) + name = re.findall(r"[^ ]*$", line)[0] + return (name, c) + +for ip in inputs: + name, l = getIOProp(ip) + num_input_wires += l + input_lens.append(str(l)) + prop_lens[name] = l + if l == 1: + wire_mapping[name] = input_wire + input_wire += 1 + else: + for i in range(l): + wire_mapping["%s[%s]" % (name, i)] = input_wire + input_wire += 1 + +# Fix wires to offset with input_wires +# Sort, in string mode should be fine +logging.info("Processing internal wires") +wires.sort() +num_wires = 2 +for i in range(len(wires)): + name, l = getIOProp(wires[i]) + prop_lens[name] = l + if l == 1: + wire_mapping[name] = num_wires + num_input_wires + num_wires += 1 + else: + for j in range(l): + wire_mapping["%s[%s]" % (name, j)] = num_wires + num_input_wires + num_wires += 1 + +# Outputs are last wires, note gnd and high require a wire +logging.info("Processing output wires") +output_wire = num_input_wires + num_wires +output_wire_names = [] +for op in outputs: + name, l = getIOProp(op) + num_output_wires += l + output_lens.append(str(l)) + prop_lens[name] = l + if l == 1: + wire_mapping[name] = output_wire + output_wire_names.append(name) + output_wire += 1 + else: + for i in range(l): + wire_mapping["%s[%s]" % (name, i)] = output_wire + output_wire_names.append("%s[%s]" % (name, i)) + output_wire += 1 + +total_num_wires = sum([num_input_wires, num_output_wires, num_wires]) + +# Set gnd and high wire. +STR_GND_WIRE = "YOSYS_STR_GND_WIRE" +STR_HIGH_WIRE = "YOSYS_STR_HIGH_WIRE" +wire_mapping[STR_GND_WIRE] = num_input_wires +wire_mapping[STR_HIGH_WIRE] = num_input_wires + 1 + + +# Process assignments +logging.info("Processing assigments") +class Instruction(object): + def __init__(self, inputs, output, cmd): + self.inputs = list(map(lambda a: a.strip(), inputs)) + # Only a single output is supported + self.output = output.strip() + self.cmd = cmd + def __str__(self): + io = ' '.join(list(map(lambda a: str(wire_mapping[a]), self.inputs + [self.output]))) + return "%d 1 %s %s" % (len(self.inputs), io, self.cmd) + +code = [] +code.append(Instruction([STR_GND_WIRE], STR_HIGH_WIRE, "INV")) +try: + for assign in assigns: + result_wire = re.findall(r"([^=]+)=", assign)[0].strip() + operation_side = re.findall(r"=(.*)$", assign)[0].strip() + # AND + is_and = re.findall(r"(.*)\&(.*)", operation_side) + if len(is_and) > 0: + assert(len(is_and[0]) == 2) + code.append(Instruction([is_and[0][0], is_and[0][1]], result_wire, "AND")) + continue + # NOT (INV) + is_inv = re.findall(r"~(.*)$", operation_side) + if len(is_inv) > 0: + assert(len(is_inv) == 1) + code.append(Instruction([is_inv[0]], result_wire, "INV")) + continue + # XOR + is_xor = re.findall(r"(.*)\^(.*)$", operation_side) + if len(is_xor) > 0: + assert(len(is_xor[0]) == 2) + code.append(Instruction([is_xor[0][0], is_xor[0][1]], result_wire, "XOR")) + continue + + # ADD CUSTOM GATES HERE + + + + + # ASSIGNMENT + # Note last part handles assignments to wires/vars, + operation_side = "{%s}" % operation_side + result_var = re.findall(r"^([^[]+)", result_wire)[0] + result_arr1 = re.findall(r"\[(.+)\]", result_wire) + result_arr2 = re.findall(r"\[(.+):(.+)\]", result_wire) + # Part of variable is being assigned e.g. assign a[1] = + if len(result_arr2) > 0: + tmp_wire_start = int(result_arr2[0][1]) + tmp_wire_end = int(result_arr2[0][0]) + elif len(result_arr1) > 0: + tmp_wire_start = int(result_arr1[0][0]) + tmp_wire_end = tmp_wire_start + # All the variable is assigned e.g. assign a = + else: + tmp_wire_start = 0 + tmp_wire_end = prop_lens[result_var] - 1 + tmp_wires = [None for i in range(prop_lens[result_var])] + # Since re.findall is non-overlapping + operation_side = operation_side.replace(",", ",,") + parts = re.findall(r"[{,]([^{},]+)[},]", operation_side) + pos_i = tmp_wire_end + for part in parts: + part = part.strip() + result_arr = re.findall(r"(\d{1,})'b(.*)", part) + # Static assignment + if len(result_arr) == 1: + for i in range(int(result_arr[0][0])): + if result_arr[0][1][i] == '1': + tmp_wires[pos_i] = STR_HIGH_WIRE + elif result_arr[0][1][i] == '0': + tmp_wires[pos_i] = STR_GND_WIRE + # Note x is just skipped + pos_i -= 1 + else: + if part in wire_mapping: + tmp_wires[pos_i] = part + pos_i -= 1 + else: + a_var = re.findall(r"^([^[]+)", part)[0] + a_arr = re.findall(r"\[(.+):(.+)\]", part) + if len(a_arr) > 0: + for i in range(int(a_arr[0][0]), int(a_arr[0][1]) - 1, -1): + tmp_wires[pos_i] = "%s[%s]" % (a_var, i) + pos_i -= 1 + else: + for i in range(prop_lens[part] - 1, -1, -1): + tmp_wires[pos_i] = "%s[%s]" % (part, i) + pos_i -= 1 + assert pos_i + 1 == tmp_wire_start + if result_var in wire_mapping and tmp_wire_start == tmp_wire_end and tmp_wire_start == 0: + code.append(Instruction([tmp_wires[0]], "%s" % result_var, "EQW")) + else: + for i in range(tmp_wire_start, tmp_wire_end + 1): + if tmp_wires[i] == None: + continue + code.append(Instruction([tmp_wires[i]], "%s[%s]" % (result_var, i), "EQW")) + +except Exception as e: + logging.error(e) + exit() + +# Apply some quick optimisations +#- Remove output wires which are not used and not connected to an output port +while True: + output_wires = set(map(lambda a: a.output, code)) + input_wires = [] + remove_wires = [] + remove_wires_mappings = [] + for instr in code: + input_wires += instr.inputs + input_wires = set(input_wires) + for output_wire in output_wires: + if output_wire not in input_wires and output_wire not in output_wire_names and wire_mapping[output_wire] >= num_input_wires: + remove_wires.append(output_wire) + remove_wires_mappings.append(wire_mapping[output_wire]) + del wire_mapping[output_wire] + if len(remove_wires) == 0: + break + # Remove from code + i = len(code) - 1 + while i >= 0: + instr = code[i] + if instr.output in remove_wires: + del code[i] + i -= 1 + total_num_wires -= len(remove_wires_mappings) + # Adjust wire mappings + for wire in wire_mapping: + curr = wire_mapping[wire] + if curr < num_input_wires: + continue + num = reduce(lambda c, a: c + int(a < curr), [0] + remove_wires_mappings) + wire_mapping[wire] -= num + +# Output the Bristol Format +logging.info("Outputting the Bristol formatted netlist to %s" % args.output_file) +with open(args.output_file, 'w') as f: + f.write("%s %s\n" % (len(code), total_num_wires)) + f.write("%s %s\n" % (len(input_lens), ' '.join(input_lens))) + f.write("%s %s\n" % (len(output_lens), ' '.join(output_lens))) + f.write("\n") + for instr in code: + f.write("%s\n" % str(instr)) + f.write("\n\n") diff --git a/Compiler/floatingpoint.py b/Compiler/floatingpoint.py index 681a0cb0..6f320014 100644 --- a/Compiler/floatingpoint.py +++ b/Compiler/floatingpoint.py @@ -571,6 +571,22 @@ def SDiv_mono(a, b, l, kappa): y = TruncPr(y, 3 * l, 2 * l, kappa) return y +## +# SDiv as annotated in ABZS12. It perfroms +# division using rapson newton from approx on +# 2^l +def SDiv_ABZS12(a, b, l, kappa): + theta = int(ceil(log(l, 2))) + x = b + y = a + for i in range(theta -1): + y = y * ((2 ** (l + 1)) - x) + y = TruncPr(y, 2 * l + 1, l, kappa) + x = x * ((2 ** (l + 1)) - x) + x = TruncPr(x, 2 * l + 1, l, kappa) + y = y * ((2 ** (l + 1)) - x) + y = TruncPr(y, 2 * l + 1, l, kappa) + return y def AdditionKOp(a,b): return a + b diff --git a/Compiler/instructions.py b/Compiler/instructions.py index d1b0dc8f..3a828eba 100644 --- a/Compiler/instructions.py +++ b/Compiler/instructions.py @@ -31,6 +31,8 @@ LDARG= 0x11, REQBL= 0x12, STARG= 0x13, + CALL= 0x14, + RETURN= 0x15, RUN_TAPE= 0x19, JOIN_TAPE= 0x1A, CRASH= 0x1B, @@ -87,6 +89,7 @@ TRIPLE= 0x50, BIT= 0x51, SQUARE= 0x52, + DABIT= 0x53, # sregint/sbit instructions LDMSINT= 0x60, @@ -136,7 +139,6 @@ LTINT= 0x95, GTINT= 0x96, EQINT= 0x97, - JMPI= 0x98, # Integers LDINT= 0x9A, @@ -153,16 +155,15 @@ CONVSREGSINT= 0xC4, # Debug Printing - PRINTMEM= 0xB0, - PRINTREG= 0XB1, - PRINTREGPLAIN= 0xB2, - PRINTCHR= 0xB3, - PRINTSTR= 0xB4, - PRINTCHRINT= 0xB5, - PRINTSTRINT= 0xB6, - PRINTFLOATPLAIN= 0xB7, - PRINTFIXPLAIN= 0xB8, - PRINTINT= 0xB9, + PRINT_MEM= 0xB0, + PRINT_REG= 0xB2, + PRINT_CHAR= 0xB3, + PRINT_CHAR4= 0xB4, + PRINT_CHAR_REGINT= 0xB5, + PRINT_CHAR4_REGINT= 0xB6, + PRINT_FLOAT= 0xB7, + PRINT_FIX= 0xB8, + PRINT_INT= 0xB9, # Comparison of sregints EQZSINT = 0xD0, @@ -187,8 +188,8 @@ # Others RAND= 0xE0, - START_TIMER= 0xE1, - STOP_TIMER= 0xE2, + START_CLOCK= 0xE1, + STOP_CLOCK= 0xE2, # Local functions LF_CINT= 0xEA, @@ -305,8 +306,9 @@ class movsint(base.Instruction): @base.vectorize class opensint(base.Instruction): - """ Open the sregint in sr_j and assign it to r_i. - This instruction is vectorizable + """ OPENSINT i j + Open the sregint in sr_j and assign it to r_i. + This instruction is vectorizable """ __slots__ = [] code = base.opcodes['OPENSINT'] @@ -315,8 +317,9 @@ class opensint(base.Instruction): @base.vectorize class opensbit(base.Instruction): - """ Open the sbit in sb_j and assign it to r_i. - This instruction is vectorizable + """ OPENSBIT i j + Open the sbit in sb_j and assign it to r_i. + This instruction is vectorizable """ __slots__ = [] code = base.opcodes['OPENSBIT'] @@ -368,7 +371,7 @@ class mulsintc(base.Instruction): @base.vectorize class mul2sint(base.Instruction): - r""" MUL2SINTC i j u v + r""" MUL2SINT i j u v Full multiplication of secret registers (sr_i || sr_j )=sr_u \cdot sr_v. Where sr_i is the most significant word and sr_j is the least significant word of the output. @@ -395,7 +398,7 @@ class LF_CINT(base.VarArgsInstruction): r""" LF_CINT i0, i1, i2, i3, i4, i5 [outputs], [inputs] This calls the Local Function with index i0, which produces i1 cints as output, and takes i2 rints, - i3 srints, i4 cints and i5 srints as input. + i3 srints, i4 cints and i5 sints as input. """ code = base.opcodes['LF_CINT'] def __init__(self, *args): @@ -413,7 +416,7 @@ class LF_SINT(base.VarArgsInstruction): r""" LF_SINT i0, i1, i2, i3, i4, i5 [outputs], [inputs] This calls the Local Function with index i0, which produces i1 sints as output, and takes i2 rints, - i3 srints, i4 cints and i5 srints as input. + i3 srints, i4 cints and i5 sints as input. """ code = base.opcodes['LF_SINT'] def __init__(self, *args): @@ -430,7 +433,7 @@ class LF_REGINT(base.VarArgsInstruction): r""" LF_REGINT i0, i1, i2, i3, i4, i5 [outputs], [inputs] This calls the Local Function with index i0, which produces i1 regints as output, and takes i2 rints, - i3 srints, i4 cints and i5 srints as input. + i3 srints, i4 cints and i5 sints as input. """ code = base.opcodes['LF_REGINT'] def __init__(self, *args): @@ -447,7 +450,7 @@ class LF_SREGINT(base.VarArgsInstruction): r""" LF_SREGINT i0, i1, i2, i3, i4, i5 [outputs], [inputs] This calls the Local Function with index i0, which produces i1 sregints as output, and takes i2 rints, - i3 srints, i4 cints and i5 srints as input. + i3 srints, i4 cints and i5 sints as input. """ code = base.opcodes['LF_SREGINT'] def __init__(self, *args): @@ -464,7 +467,7 @@ def __init__(self, *args): @base.vectorize class subsintc(base.Instruction): - r""" SUBC i j k + r""" SUBSINTC i j k Subtracts secret and clear registers sr_i=sr_j-r_k. This instruction is vectorizable """ @@ -475,7 +478,7 @@ class subsintc(base.Instruction): @base.vectorize class subsint(base.Instruction): - r""" SUBS i j k + r""" SUBSINT i j k Subtracts secret registers sr_i=sr_j-sr_k. This instruction is vectorizable """ @@ -486,7 +489,7 @@ class subsint(base.Instruction): @base.vectorize class subcints(base.Instruction): - r""" SUBC i j k + r""" SUBCINTS i j k Subtracts clear and secret registers sr_i=r_j-sr_k. This instruction is vectorizable """ @@ -529,7 +532,7 @@ class shrsint(base.Instruction): @base.vectorize class neg(base.Instruction): r""" NEG i j - Negation of a a secret register s_i=-s_j . + Negation of a regint sr_i=-sr_j . This instruction is vectorizable """ __slots__ = [] @@ -700,7 +703,7 @@ class bitsint(base.Instruction): @base.vectorize class sintbit(base.Instruction): r""" SINTBIT i j k n - Assigns si to sj, and then sets the n-th bit to be sb_k + Assigns sri to srj, and then sets the n-th bit to be sb_k This instruction is vectorizable """ __slots__ = ["code"] @@ -986,6 +989,7 @@ class ldtn(base.Instruction): class ldarg(base.Instruction): r""" LDARG i Assigns the argument passed to the current thread to the regint register r_i. + This is also used to pass variables to functions. This instruction is vectorizable """ code = base.opcodes['LDARG'] @@ -996,6 +1000,7 @@ class ldarg(base.Instruction): class starg(base.Instruction): r""" STARG i Assigns register r_i to variable in the thread argument. + This is also used to pass variables to functions. This instruction is vectorizable """ code = base.opcodes['STARG'] @@ -1034,6 +1039,25 @@ class crash(base.IOInstruction): code = base.opcodes['CRASH'] arg_format = [] +class CALL(base.JumpInstruction): + r""" CALL n + Pushes the current PC onto the stack, and then performs an unconditional relative jump of n+1 instructions. + """ + __slots__ = [] + code = base.opcodes['CALL'] + arg_format = ['int'] + jump_arg = 0 + +class RETURN(base.JumpInstruction): + r""" RETURN + Pops an integer off the stack, and sets the program counter + to this value. Used to return from sub-routines executed + by CALL + """ + code = base.opcodes['RETURN'] + arg_format = [] + jump_arg = -1 + class restart(base.IOInstruction): r""" RESTART Restart the runtime by reloading the schedule file. @@ -1351,7 +1375,7 @@ class mulci(base.Instruction): @base.vectorize class mulsi(base.Instruction): - r""" MULCI i j n + r""" MULSI i j n Multiplication of secret register by immediate value s_i=s_j \cdot n. This instruction is vectorizable """ @@ -1374,7 +1398,7 @@ class divci(base.Instruction): @base.vectorize class modci(base.Instruction): - r""" MODC i j n + r""" MODCI i j n Clear division with remainder c_i=c_j%n (after lifting to the integers) by an immediate This instruction is vectorizable """ @@ -1496,6 +1520,16 @@ class bit(base.DataInstruction): arg_format = ['sw'] data_type = 'bit' +@base.vectorize +class dabit(base.DataInstruction): + r""" DABIT i j + Load sint, sbit registers s_i and sb_j with the next secret dabit. + This instruction is vectorizable + """ + __slots__ = [] + code = base.opcodes['DABIT'] + arg_format = ['sw', 'sbw'] + data_type = 'dabit' @base.vectorize class square(base.DataInstruction): @@ -1525,83 +1559,68 @@ class private_input(base.IOInstruction): @base.vectorize class print_mem(base.IOInstruction): - r""" PRINTMEM i + r""" PRINT_MEM i Print value in clear memory C[i] to debug IO channel. Can only be executed in thread zero. This instruction is vectorizable """ __slots__ = [] - code = base.opcodes['PRINTMEM'] + code = base.opcodes['PRINT_MEM'] arg_format = ['int'] @base.vectorize class print_reg(base.IOInstruction): - r""" PRINTREG i j - Print value of cint register c_i to debug IO channel and 4-char comment j - Can only be executed in thread zero. - This instruction is vectorizable - """ - __slots__ = [] - code = base.opcodes['PRINTREG'] - arg_format = ['c', 'i'] - - def __init__(self, reg, comment=''): - super(print_reg_class, self).__init__(reg, self.str_to_int(comment)) - - -@base.vectorize -class print_reg_plain(base.IOInstruction): - r""" PRINTREGPLAIN i + r""" PRINT_REG i As above but skips the comment j Can only be executed in thread zero. This instruction is vectorizable """ __slots__ = [] - code = base.opcodes['PRINTREGPLAIN'] + code = base.opcodes['PRINT_REG'] arg_format = ['c'] @base.vectorize -class print_fix_plain(base.IOInstruction): - r""" PRINTFIXPLAIN i f k +class print_fix(base.IOInstruction): + r""" PRINT_FIX i f k Prints the fixed point number in cint register c_i using parameters f and k Can only be executed in thread zero. This instruction is vectorizable """ __slots__ = [] - code = base.opcodes['PRINTFIXPLAIN'] + code = base.opcodes['PRINT_FIX'] arg_format = ['c', 'i', 'i'] @base.vectorize -class print_float_plain(base.IOInstruction): - r""" PRINTFLOATPLAIN i j k l +class print_float(base.IOInstruction): + r""" PRINT_FLOAT i j k l Prints the floating point number in cint registers (c_i, c_j, c_k, c_l) assuming they map to the representation (v,p,z,s) Can only be executed in thread zero. This instruction is vectorizable """ __slots__ = [] - code = base.opcodes['PRINTFLOATPLAIN'] + code = base.opcodes['PRINT_FLOAT'] arg_format = ['c', 'c', 'c', 'c'] - +@base.vectorize class print_int(base.IOInstruction): - r""" PRINTINT i + r""" PRINT_INT i Prints the value of register r_i to debug IO channel. Can only be executed in thread zero. This instruction is vectorizable """ __slots__ = [] - code = base.opcodes['PRINTINT'] + code = base.opcodes['PRINT_INT'] arg_format = ['r'] class print_char(base.IOInstruction): - r""" PRINTCHAR i + r""" PRINT_CHAR i Prints a single character i to debug IO channel. Can only be executed in thread zero. This instruction is vectorizable """ - code = base.opcodes['PRINTCHR'] + code = base.opcodes['PRINT_CHAR'] arg_format = ['int'] def __init__(self, ch): @@ -1609,12 +1628,12 @@ def __init__(self, ch): class print_char4(base.IOInstruction): - r""" PRINTSTR i + r""" PRINT_CHAR4 i Print a 4 character string i to debug IO channel. Can only be executed in thread zero. This instruction is vectorizable """ - code = base.opcodes['PRINTSTR'] + code = base.opcodes['PRINT_CHAR4'] arg_format = ['int'] def __init__(self, val): @@ -1623,22 +1642,22 @@ def __init__(self, val): @base.vectorize class print_char_regint(base.IOInstruction): - r""" PRINTCHRINT i + r""" PRINT_CHAR_REGINT i Print regint register r_i as a single character to debug IO channel. Can only be executed in thread zero. This instruction is vectorizable """ - code = base.opcodes['PRINTCHRINT'] + code = base.opcodes['PRINT_CHAR_REGINT'] arg_format = ['r'] @base.vectorize class print_char4_regint(base.IOInstruction): - r""" PRINTSTRINT i + r""" PRINTi_CHAR4_REGINT i Print regint register r_i as a four character string to debug IO channel. Can only be executed in thread zero. This instruction is vectorizable """ - code = base.opcodes['PRINTSTRINT'] + code = base.opcodes['PRINT_CHAR4_REGINT'] arg_format = ['r'] @base.vectorize @@ -1667,11 +1686,11 @@ class input_int(base.IOInstruction): arg_format = ['rw','i'] class open_chan(base.IOInstruction): - r""" OPEN_CHAN n + r""" OPEN_CHAN i n Opens channel number n for reading/writing on the IO class. Channels are assumed to be bi-directional, i.e. can read and write. This is provided as some IO classes may require this to be called explicitly, the default one does not need this. - The return value *can* be some error code which the IO class may want to return. + The return value r_i *can* be some error code which the IO class may want to return. Can only be executed in thread zero. """ __slots__ = [] @@ -1720,7 +1739,7 @@ def has_var_args(self): @base.vectorize class output_clear(base.IOInstruction): - r""" OUTPUT i n + r""" OUTPUT_CLEAR i n Public output of cint register c_i to IO class on channel n. This instruction is vectorizable Can only be executed in thread zero. @@ -1829,7 +1848,7 @@ class divint(base.Instruction): # @base.vectorize -class eqzc(base.Instruction): +class eqzint(base.Instruction): r""" EQZINT i j Clear comparison to zero test of regint registers r_i = (r_j == 0). This instruction is vectorizable @@ -1840,7 +1859,7 @@ class eqzc(base.Instruction): @base.vectorize -class ltzc(base.Instruction): +class ltzint(base.Instruction): r""" LTZINT i j Clear comparison of regint registers r_i = (r_j < 0). This instruction is vectorizable @@ -1851,7 +1870,7 @@ class ltzc(base.Instruction): @base.vectorize -class ltc(base.Instruction): +class ltint(base.Instruction): r""" LTINT i j k Clear comparison of regint registers r_i = (r_j < r_k). This instruction is vectorizable @@ -1862,7 +1881,7 @@ class ltc(base.Instruction): @base.vectorize -class gtc(base.Instruction): +class gtint(base.Instruction): r""" GTINT i j k Clear comparison of regint registers r_i = (r_j > r_k). This instruction is vectorizable @@ -1873,7 +1892,7 @@ class gtc(base.Instruction): @base.vectorize -class eqc(base.Instruction): +class eqint(base.Instruction): r""" EQINT i j k Clear comparison of regint registers r_i = (r_j == r_k). This instruction is vectorizable @@ -1897,16 +1916,6 @@ class jmp(base.JumpInstruction): jump_arg = 0 -class jmpi(base.JumpInstruction): - r""" JMPI i - Unconditional relative jump of r_i+1 instructions. - """ - __slots__ = [] - code = base.opcodes['JMPI'] - arg_format = ['r'] - jump_arg = 0 - - class jmpnz(base.JumpInstruction): r""" JMPNZ i n Jump of n+1 instructions if regint register r_i \neq 0. @@ -1948,8 +1957,9 @@ class convint(base.Instruction): @base.vectorize class convmodp(base.Instruction): - """ CONVMODP i j - Convert from cint register c_j to regint register r_i. + """ CONVMODP i j n + Convert from cint register c_j to regint register r_i with + bitlength of c_j equal to n This instruction is vectorizable """ __slots__ = [] @@ -1988,17 +1998,17 @@ class stopopen(base.VarArgsInstruction): class start_clock(base.Instruction): - r""" START_TIMER n + r""" START_CLOCK n Re-initializes the specified timer n """ - code = base.opcodes['START_TIMER'] + code = base.opcodes['START_CLOCK'] arg_format = ['i'] class stop_clock(base.Instruction): - r""" STOP_TIMER n + r""" STOP_CLOCK n Prints the time since the last initialization of timer n """ - code = base.opcodes['STOP_TIMER'] + code = base.opcodes['STOP_CLOCK'] arg_format = ['i'] diff --git a/Compiler/instructions_base.py b/Compiler/instructions_base.py index c6e75688..0f42096a 100644 --- a/Compiler/instructions_base.py +++ b/Compiler/instructions_base.py @@ -41,6 +41,8 @@ LDARG= 0x11, REQBL= 0x12, STARG= 0x13, + CALL= 0x14, + RETURN=0x15, RUN_TAPE= 0x19, JOIN_TAPE= 0x1A, CRASH= 0x1B, @@ -97,6 +99,7 @@ TRIPLE= 0x50, BIT= 0x51, SQUARE= 0x52, + DABIT= 0x53, # sregint/sbit instructions LDMSINT= 0x60, @@ -146,7 +149,6 @@ LTINT= 0x95, GTINT= 0x96, EQINT= 0x97, - JMPI= 0x98, # Integers LDINT= 0x9A, @@ -163,16 +165,15 @@ CONVSREGSINT= 0xC4, # Debug Printing - PRINTMEM= 0xB0, - PRINTREG= 0XB1, - PRINTREGPLAIN= 0xB2, - PRINTCHR= 0xB3, - PRINTSTR= 0xB4, - PRINTCHRINT= 0xB5, - PRINTSTRINT= 0xB6, - PRINTFLOATPLAIN= 0xB7, - PRINTFIXPLAIN= 0xB8, - PRINTINT= 0xB9, + PRINT_MEM= 0xB0, + PRINT_REG= 0xB2, + PRINT_CHAR= 0xB3, + PRINT_CHAR4= 0xB4, + PRINT_CHAR_REGINT= 0xB5, + PRINT_CHAR4_REGINT= 0xB6, + PRINT_FLOAT= 0xB7, + PRINT_FIX= 0xB8, + PRINT_INT= 0xB9, # Comparison of sregints EQZSINT = 0xD0, @@ -197,8 +198,8 @@ # Others RAND= 0xE0, - START_TIMER= 0xE1, - STOP_TIMER= 0xE2, + START_CLOCK= 0xE1, + STOP_CLOCK= 0xE2, # Local functions LF_CINT= 0xEA, @@ -631,6 +632,8 @@ def set_relative_jump(self, value): self.args[self.jump_arg] = value def get_relative_jump(self): + if self.jump_arg == -1: + return -1 return self.args[self.jump_arg] diff --git a/Compiler/library.py b/Compiler/library.py index 65296046..ddd930e1 100644 --- a/Compiler/library.py +++ b/Compiler/library.py @@ -68,15 +68,15 @@ def print_plain_str(ss): val = args[i] if isinstance(val, program.Tape.Register): if val.is_clear: - val.print_reg_plain() + val.print_reg() else: raise CompilerError('Cannot print secret value:', args[i]) elif isinstance(val, sfix) or isinstance(val, sfloat): raise CompilerError('Cannot print secret value:', args[i]) elif isinstance(val, cfloat): - val.print_float_plain() + val.print_float() elif isinstance(val, cfix): - val.print_fix_plain() + val.print_fix() elif isinstance(val, list): print_str('[' + ', '.join('%s' for i in range(len(val))) + ']', *val) else: @@ -322,8 +322,7 @@ def on_first_call(self, wrapped_function): print 'Done compiling function', self.name p_return_address = get_tape().program.malloc(1, 'r') get_tape().function_basicblocks[block] = p_return_address - return_address = regint.load_mem(p_return_address) - get_tape().active_basicblock.set_exit(instructions.jmpi(return_address, add_to_prog=False)) + get_tape().active_basicblock.set_exit(instructions.RETURN(add_to_prog=False)) self.last_sub_block = get_tape().active_basicblock get_tape().close_scope(old_block, parent_node, 'end-' + self.name) old_block.set_exit(instructions.jmp(0, add_to_prog=False), get_tape().active_basicblock) @@ -336,11 +335,9 @@ def on_call(self, base, bases): if block not in get_tape().function_basicblocks: raise CompilerError('unknown function') old_block = get_tape().active_basicblock - old_block.set_exit(instructions.jmp(0, add_to_prog=False), block) + old_block.set_exit(instructions.CALL(0, add_to_prog=False), block) p_return_address = get_tape().function_basicblocks[block] return_address = get_tape().new_reg('r') - old_block.return_address_store = instructions.ldint(return_address, 0) - instructions.stmint(return_address, p_return_address) get_tape().start_new_basicblock(name='call-' + self.name) get_tape().active_basicblock.set_return(old_block, self.last_sub_block) get_tape().req_node.children.append(self.node) diff --git a/Compiler/oram.py b/Compiler/oram.py index 0cbeec27..093e26b8 100644 --- a/Compiler/oram.py +++ b/Compiler/oram.py @@ -430,7 +430,7 @@ def __repr__(self): class RAM(RefRAM): """ List of entries in memory. """ def __init__(self, size, entry_type, index=0): - #print_reg(cint(0), 'r in') + #print_reg_char4(cint(0), 'r in') self.size = size self.entry_type = entry_type self.l = [t.dynamic_array(self.size, t) for t in entry_type] @@ -885,16 +885,16 @@ def __repr__(self, depth=0): self.ref_children(1).__repr__(depth + 1) return result def output(self): - print_reg(cint(self.depth), 'buck') + print_reg_char4(cint(self.depth), 'buck') Program.prog.curr_tape.start_new_basicblock() self.bucket.output() - print_reg(cint(self.depth), 'dep') + print_reg_char4(cint(self.depth), 'dep') Program.prog.curr_tape.start_new_basicblock() @if_(self.p_children(1) < oram.n_buckets()) def f(): for i in (0,1): child = self.ref_children(i) - print_reg(cint(i), 'chil') + print_reg_char4(cint(i), 'chil') Program.prog.curr_tape.start_new_basicblock() child.output() @@ -954,7 +954,7 @@ def update(self, index, value): return self.value_type(read_value) def output(self): for i,v in enumerate(self): - print_reg(v.reveal(), 'i %d' % i) + print_reg_char4(v.reveal(), 'i %d' % i) __getitem__ = lambda self,index: List.__getitem__(self, index)[0] def get_n_threads_for_tree(size): @@ -1013,7 +1013,7 @@ def add_to_root(self, state, is_empty, v, *x): l = state self.root.bucket.add(Entry(v, (l,) + x, is_empty)) def evict_bucket(self, bucket, d): - #print_reg(cint(0), 'evb') + #print_reg_char4(cint(0), 'evb') #print 'pre', bucket entry = bucket.bucket.pop() #print 'evict', entry @@ -1107,7 +1107,7 @@ def read_and_remove(self, u): def add(self, entry, state=None): if state is None: state = self.state.read() - #print_reg(cint(0), 'add') + #print_reg_char4(cint(0), 'add') #print 'add', id(self) #print 'pre-add', self maybe_start_timer(4) @@ -1122,21 +1122,21 @@ def add(self, entry, state=None): #print 'post-evict', self def evict(self): #print 'evict root', id(self) - #print_reg(cint(0), 'ev_r') + #print_reg_char4(cint(0), 'ev_r') self.evict_bucket(self.root, 0) self.check() if self.D > 1: #print 'evict 1', id(self) - #print_reg(cint(0), 'ev1') + #print_reg_char4(cint(0), 'ev1') self.evict2(self.root.p_children(0), self.root.p_children(1), 1) self.check() if self.D > 2: - #print_reg(cint(self.D), 'D') + #print_reg_char4(cint(self.D), 'D') @for_range(2, self.D) def f(d): - #print_reg(d, 'ev2') + #print_reg_char4(d, 'ev2') #print 'evict 2', id(self) - #print_reg(d, 'evl2') + #print_reg_char4(d, 'evl2') s1 = regint.get_random(d) s2 = MemValue(regint(0)) @do_while @@ -1537,8 +1537,8 @@ def __repr__(self): return repr(self.l) def output(self): if self.small: - print_reg(self.l[0].reveal(), 'i0') - print_reg(self.l[1].reveal(), 'i1') + print_reg_char4(self.l[0].reveal(), 'i0') + print_reg_char4(self.l[1].reveal(), 'i1') class PackedORAMWithEmpty(AbstractORAM, PackedIndexStructure): def __init__(self, size, entry_size=None, value_type=sint, init_rounds=-1): @@ -1657,7 +1657,7 @@ def test_oram_access(oram_type, N, value_type=sint, index_size=None, iterations= oram = oram_type(N, value_type=value_type, entry_size=32, \ init_rounds=0) print 'initialized' - print_reg(cint(0), 'init') + print_reg_char4(cint(0), 'init') stop_timer() # synchronize Program.prog.curr_tape.start_new_basicblock(name='sync') @@ -1686,9 +1686,9 @@ def test_batch_init(oram_type, N): value_type = sint oram = oram_type(N, value_type) print 'initialized' - print_reg(cint(0), 'init') + print_reg_char4(cint(0), 'init') oram.batch_init([value_type(i) for i in range(N)]) - print_reg(cint(0), 'done') + print_reg_char4(cint(0), 'done') @for_range(N) def f(i): x = oram[value_type(i)] diff --git a/Compiler/program.py b/Compiler/program.py index b1a92552..c7a71547 100644 --- a/Compiler/program.py +++ b/Compiler/program.py @@ -22,6 +22,7 @@ bit = 2, inverse = 3, bittriple = 4, + dabit = 5, ) field_types = dict( @@ -279,9 +280,9 @@ def finalize_tape(self, tape): if not tape.purged: tape.optimize(self.options) tape.write_bytes() + if self.options.asmoutfile: + tape.write_str(self.options.asmoutfile + '-' + tape.name+'.asm') tape.purge() - if self.options.asmoutfile: - tape.write_str(self.options.asmoutfile + '-' + tape.name) def restart_main_thread(self): @@ -349,6 +350,7 @@ def finalize_memory(self): self.curr_tape.start_new_basicblock(None, 'memory-usage') # reset register counter to 0 self.curr_tape.init_registers() + library.jmp(0); # Create a new basic block for the set memory instructions for mem_type,size in self.allocated_mem.items(): if size: print "Memory of type '%s' of size %d" % (mem_type, size) @@ -417,10 +419,6 @@ def set_return(self, previous_block, sub_block): self.previous_block = previous_block self.sub_block = sub_block - def adjust_return(self): - offset = self.sub_block.get_offset(self) - self.previous_block.return_address_store.args[1] = offset - def set_exit(self, condition, exit_true=None): """ Sets the block which we start from next, depending on the condition. @@ -554,8 +552,6 @@ def optimize(self, options): for block in self.basicblocks: if block.exit_block is not None: block.adjust_jump() - if block.previous_block is not None: - block.adjust_return() # now remove any empty blocks (must be done after setting jumps) self.basicblocks = filter(lambda x: len(x.instructions) != 0, self.basicblocks) @@ -579,7 +575,9 @@ def alloc_loop(block): (block.name, i, len(self.basicblocks)) if block.exit_condition is not None: jump = block.exit_condition.get_relative_jump() - if isinstance(jump, (int,long)) and jump < 0 and \ + if jump != -1 and \ + isinstance(jump, (int,long)) and \ + jump < 0 and \ block.exit_block.scope is not None: alloc_loop(block.exit_block.scope) allocator.process(block.instructions, block.alloc_pool) diff --git a/Compiler/types.py b/Compiler/types.py index 1525bbcc..ede0c09c 100644 --- a/Compiler/types.py +++ b/Compiler/types.py @@ -254,12 +254,15 @@ def convert_from(self, val): @set_instruction_type @vectorize def print_reg(self, comment=''): - print_reg(self, comment) + print_reg(self) + print_char4(" # ") + print_char4(comment) + print_char("\n") @set_instruction_type @vectorize - def print_reg_plain(self): - print_reg_plain(self) + def print_reg_pl(self): + print_reg(self) @set_instruction_type @vectorize @@ -652,17 +655,17 @@ def __rpow__(self, other): return other ** cint(self) def __eq__(self, other): - return self.int_op(other, eqc) + return self.int_op(other, eqint) def __ne__(self, other): if isinstance(other, _secretMod2): return NotImplemented return 1 - (self == other) def __lt__(self, other): - return self.int_op(other, ltc) + return self.int_op(other, ltint) def __gt__(self, other): - return self.int_op(other, gtc) + return self.int_op(other, gtint) def __le__(self, other): if isinstance(other, _secretMod2): return NotImplemented @@ -731,7 +734,8 @@ def bit_compose(bits): def reveal(self): return self - def print_reg_plain(self): + @vectorize + def print_reg(self): print_int(self) @@ -1145,6 +1149,12 @@ def __or__(self, other): orsb(res, self, other) return res + # There are two ways of doing negation, neg and invert + def __invert__(self): + res = sbit() + negb(res, self) + return res + def __neg__(self): res = sbit() negb(res, self) @@ -1203,7 +1213,7 @@ def eqz(self): def ltz(self): res = sbit() - ltzint(res, self) + ltzsint(res, self) return res @@ -1596,6 +1606,7 @@ def __init__(self, v, size=None): self.v = v else: raise NotImplementedError + program.curr_tape.require_bit_length(2*self.k+1) @vectorize def load_int(self, v): @@ -1785,9 +1796,9 @@ def __div__(self, other): raise TypeError('Incompatible fixed point types in division') @vectorize - def print_fix_plain(self): + def print_fix(self): r"""Prints the cfix in as a vector (y,f,k)""" - print_fix_plain(self.v, self.f, self.k) + print_fix(self.v, self.f, self.k) ## @@ -1870,6 +1881,7 @@ def __init__(self, _v=None, size=None): elif isinstance(_v, regint): self.v = sint(_v, size=self.size) * 2 ** f self.kappa = sfix.kappa + program.curr_tape.require_bit_length(2*self.k+self.kappa+1) @vectorize def load_int(self, v): @@ -2107,7 +2119,7 @@ def __init__(self, v, p=None, z=None, s=None, err=None, size=None): else: v, p, z, s, err = self.convert_float(v, self.vlen, self.plen) - if isinstance(v, int): + if isinstance(v, (int,long)): if not ((v >= 2 ** (self.vlen - 1) and v < 2 ** (self.vlen)) or v == 0): raise CompilerError('Floating point number malformed: significand') self.v = library.load_int_to_secret(v) @@ -2140,6 +2152,7 @@ def __init__(self, v, p=None, z=None, s=None, err=None, size=None): self.err = library.load_int_to_secret(err) else: self.err = err + program.curr_tape.require_bit_length(2*self.vlen+self.kappa+1) def __iter__(self): yield self.v @@ -2289,18 +2302,37 @@ def mul(self, other): other_parse = parse_float(other) return self * other_parse # self.mul(scalar_float) - def __sub__(self, other): - return (self + -other) + ## + # float division (FLDiv) as described in ABZS12. + # Additional conditions for err added in algiment with + # the way we work with floating point numbers. + def local_division_ABZS12(self, other): + if isinstance(other, (cfloat, sfloat)): + l = self.vlen + v = floatingpoint.SDiv_ABZS12(self.v, other.v + other.z, l, self.kappa) + b = v.less_than(2 ** l, l + 1, self.kappa) + v = floatingpoint.Trunc(v * b + v, l + 1, 1, self.kappa) + p = (1 - self.z) * (self.p - other.p - l + 1 - b) + z = self.z + + #simple xor of sign + s = self.s + other.s - 2*self.s * other.s + if isinstance(other, sfloat): + err = other.err + err = err + self.err + err = err + self.__flow_detect__(p) + err = err + other.z + return sfloat(v, p, z, s, err) + else: - def __rsub__(self, other): - return -1 * self + other + other_parse = parse_float(other) + return self / other_parse ## # realizes the division protocol for several different types. # @param other: value dividing self, could be any type - # @return sloat: new sfloat instance - def __div__(self, other): - + # @return sloat: new sfloat instance + def local_division(self, other): if isinstance(other, (cfloat, sfloat)): v = floatingpoint.SDiv(self.v, other.v + other.z * (2 ** self.vlen - 1), self.vlen, self.kappa) @@ -2328,6 +2360,16 @@ def __div__(self, other): other_parse = parse_float(other) return self / other_parse + + def __sub__(self, other): + return (self + -other) + + def __rsub__(self, other): + return -1 * self + other + + def __div__(self, other): + return self.local_division_ABZS12(other) + @vectorize def __neg__(self): return sfloat(self.v, self.p, self.z, (1 - self.s) * (1 - self.z), self.err) @@ -2503,6 +2545,7 @@ def __init__(self, v, p=None, z=None, s=None, size=None): self.v = v else: # missmatch of types validation raise CompilerError('Missmatching input type ') + program.curr_tape.require_bit_length(2*self.vlen+1) # validation of p if isinstance(p, int): @@ -2554,8 +2597,8 @@ def set_zero(self, flag): # facade method that evokes low level instructions # to print float number. # No params, uses instance records. - def print_float_plain(self): - print_float_plain(self.v, self.p, self.z, self.s) + def print_float(self): + print_float(self.v, self.p, self.z, self.s) ## # computes the product times -1 of the cfloat diff --git a/Documentation/ByteCodes.tex b/Documentation/ByteCodes.tex index ac8e09eb..519cf4e1 100644 --- a/Documentation/ByteCodes.tex +++ b/Documentation/ByteCodes.tex @@ -61,67 +61,66 @@ \subsection{Overview} \begin{figure}[htb!] \begin{center} -\begin{picture}{(370,280)} - -\put(0,25){\framebox(50,30){FHE Fact 1}} -\put(0,75){\framebox(50,30){FHE Fact 2}} -\put(50,90){\line(1,-1){25}} -\put(50,40){\line(1,1){25}} -\put(75,65){\line(1,0){25}} -\put(100,15){\line(0,1){220}} +\begin{picture}{(370,360)} + +\put(0,65){\framebox(50,30){FHE Fact 1}} +\put(0,115){\framebox(50,30){FHE Fact 2}} +\put(50,130){\line(1,-1){25}} +\put(50,80){\line(1,1){25}} +\put(75,105){\line(1,0){25}} +\put(100,15){\line(0,1){300}} +\put(100,315){\vector(1,0){20}} +\put(100,275){\vector(1,0){20}} \put(100,235){\vector(1,0){20}} \put(100,195){\vector(1,0){20}} -\put(100,155){\vector(1,0){20}} +\put(100,135){\vector(1,0){20}} \put(100,95){\vector(1,0){20}} \put(100,55){\vector(1,0){20}} \put(100,15){\vector(1,0){20}} -\put(100,125){\line(1,0){130}} -\put(230,125){\vector(0,1){55}} -\put(230,125){\vector(0,-1){55}} -\put(40,195){\framebox(50,30){aAND}} -\put(0,150){\framebox(50,30){aBit}} +\put(40,235){\framebox(50,30){aAND}} +\put(0,190){\framebox(50,30){aBit}} %aBit->aAND -\put(25,180){\vector(1,1){15}} +\put(25,220){\vector(1,1){15}} %aBit->Online -\put(25,180){\line(0,1){100}} -\put(25,280){\line(1,0){345}} -\put(370,280){\line(0,-1){235}} -\put(370,45){\vector(-1,0){20}} -\put(370,185){\vector(-1,0){20}} +\put(25,220){\line(0,1){140}} +\put(25,360){\line(1,0){345}} +\put(370,360){\line(0,-1){275}} +\put(370,85){\vector(-1,0){57}} +\put(370,225){\vector(-1,0){57}} %aAND->Online -\put(65,225){\line(0,1){40}} -\put(65,265){\line(1,0){295}} -\put(360,265){\line(0,-1){210}} -\put(360,55){\vector(-1,0){10}} -\put(360,195){\vector(-1,0){10}} - -\put(120,220){\framebox(50,30){Mult Triples}} -\put(170,235){\vector(1,-1){27}} -\put(120,180){\framebox(50,30){Square Pairs}} -\put(170,195){\vector(1,0){27}} -\put(120,140){\framebox(50,30){Bits Pairs}} -\put(170,155){\vector(1,1){27}} - -\put(200,180){\framebox(70,30){Sacrifice/Inputs}} -\put(270,195){\vector(1,0){30}} -\put(300,180){\framebox(50,30){Online Two}} - - -\put(120,80){\framebox(50,30){Mult Triples}} -\put(170,95){\vector(1,-1){27}} -\put(120,40){\framebox(50,30){Square Pairs}} -\put(170,55){\vector(1,0){27}} -\put(120,0){\framebox(50,30){Bits Pairs}} -\put(170,15){\vector(1,1){27}} - -\put(200,40){\framebox(70,30){Sacrifice/Inputs}} -\put(270,55){\vector(1,0){30}} -\put(300,40){\framebox(50,30){Online One}} +\put(65,265){\line(0,1){80}} +\put(65,345){\line(1,0){295}} +\put(360,345){\line(0,-1){250}} +\put(360,95){\vector(-1,0){47}} +\put(360,235){\vector(-1,0){47}} + +\put(120,300){\framebox(50,30){Mult Triples}} +\put(170,315){\vector(3,-2){92}} +\put(120,260){\framebox(50,30){Square Pairs}} +\put(170,275){\vector(3,-1){83}} +\put(120,220){\framebox(50,30){Bits Pairs}} +\put(170,235){\vector(1,0){87}} +\put(120,180){\framebox(70,30){Sacrifice/Inputs}} +\put(190,185){\vector(2,1){65}} + +\put(260,220){\framebox(50,30){Online Two}} + + +\put(120,120){\framebox(50,30){Mult Triples}} +\put(170,135){\vector(3,-1){84}} +\put(120,80){\framebox(50,30){Square Pairs}} +\put(170,95){\vector(1,0){83}} +\put(120,40){\framebox(50,30){Bits Pairs}} +\put(170,55){\vector(3,1){88}} +\put(120,0){\framebox(70,30){Sacrifice/Inputs}} +\put(190,15){\vector(1,1){67}} + +\put(260,80){\framebox(50,30){Online One}} \end{picture} \end{center} @@ -224,6 +223,7 @@ \subsection{Overview} \end{center} \caption{Pictorial Representation of Memory and Registers: With Two Online Threads} +\label{fig:memory} \end{figure} \subsection{Byte-code instructions} @@ -341,6 +341,8 @@ \subsection{Preprocessing loading instructions} respectively. The associated data is loaded from the concurrently running offline threads and loaded into the registers given as arguments. +There is also an instruction \verb+DABIT+ to load a doubly authenticated +bit into a \verb|sint| and an \verb|sbit| register. \subsection{Open instructions} There are tailor-made approaches to open registers depending on whether they are $\modp$ or $\modn$. We detail both in this section. @@ -502,9 +504,9 @@ \subsection{Debuging Output} debugging information to the \verb+Input_Output+ class. These byte-codes are \begin{verbatim} - PRINTINT, PRINTMEM, PRINTREG, PRINTREGPLAIN, - PRINTCHR, PRINTSTR, PRINTCHRINT, PRINTSTRINT, - PRINTFLOATPLAIN, PRINTFIXPLAIN. + PRINT_INT, PRINT_MEM, PRINT_REG, PRINT_CHAR, + PRINT_CHAR4, PRINT_CHAR_REGINT, PRINT_CHAR4_REGINT, PRINT_FLOAT, + PRINT_FIX. \end{verbatim} \subsection{Data input and output} @@ -518,14 +520,16 @@ \subsection{Data input and output} OPEN_CHAN, CLOSE_CHAN \end{verbatim} -\subsection{Branching on clear registers} -Branching on clear registers is supported by the following -instructions +\subsection{Branching} +Branching is supported by the following instructions \verb+JMP+, \verb+JMPNZ+, \verb+JMPEQZ+, - and - \verb+JMPI+. + +\subsection{Call/Return} +Call and return to subroutines is supported by the following +instructions +\verb+CALL+ and \verb+RETURN+. \subsection{Comparison Tests for $\modn$} We support comparison on $\modn$ clear registers via the instructions @@ -563,11 +567,11 @@ \subsection{Other Commands} See Section \ref{sec:restart} for more details on how this is used. \item \verb+CLEAR_REGISTERS+ which clears the registers of this processor core (i.e. thread). See Section \ref{sec:restart} for more details on how this is used. -\item \verb+START_TIMER+ and \verb+STOP_TIMER+ are used to time different +\item \verb+START_CLOCK+ and \verb+STOP_CLOCK+ are used to time different parts of the code. There are 100 times available in the system; each is initialized to zero at the start of the machine running. -The operation \verb+START_TIMER+ re-initializes a specified timer, -whereas \verb+STOP_TIMER+ prints the elapsed time since the last +The operation \verb+START_CLOCK+ re-initializes a specified timer, +whereas \verb+STOP_CLOCK+ prints the elapsed time since the last initialization (it does not actually reinitialise/stop the timer itself). These are accessed from MAMBA via the functions \verb+start_timer(n)+ and \verb+stop_timer(n)+. diff --git a/Documentation/Changes.tex b/Documentation/Changes.tex index 2dd66fd6..8427b77a 100644 --- a/Documentation/Changes.tex +++ b/Documentation/Changes.tex @@ -6,7 +6,7 @@ \vspace{5mm} \noindent -This is currently {\bf version 1.5} of the SCALE and MAMBA software. +This is currently {\bf version 1.6} of the SCALE and MAMBA software. There are two main components to the system, a run time system called SCALE, \begin{center} @@ -26,6 +26,55 @@ You {\em should not} assume it works the same so please read the documentation fully before proceeding. + +\subsection{Changes in version 1.6 from 1.5} +Apart from the usual minor bug fixes... +\begin{enumerate} +\item Call/Return byte-codes have been added, removing the need for the \verb+JMPI+ +instruction. This makes things much simplier going forward we hope. +\item Some byte-code names have been changed, and made more consistent. This +is purely cosmetic, unless you look at the assembler output from the compiler. +\item \verb|sfloat|, \verb|sfix| are now a little more robust. A program will +now definitely abort if the desired security bounds are not met. +We also removed a limitation on the mantissa size in \verb|sfloat|. +\item Fake offline mode is now depreciated and you {\em cannot} select it +from the Setup menu. This is due to a bug, and the fact we never actually +use/test it ourselves. +\item Re the change in version 1.4 for Full Threshold input production. +Turns out the method in SPDZ-2 and in Overdrive are both insecure. +One needs to execute a ZKPoK to prove that party $P_i$'s input is correct; +which is basically the TopGear proof done without a summation. Then +one operates as in SPDZ-2. This has now been alterred. +\item Compiler now outputs assembler for all tapes which are compiled, if +directed to. +\item Direct access to the DABITs via a DABIT opcode. +\item Upgrade to the DABIT production for dishonest majority. +\item Change to the threading to place sacrificing into the production +threads. This makes the configuration in \verb+config.h+ a little +simpler to understand, and also removes a potential security hole +we discussed in earlier documentation. +This means some of the data structures previously defined in +the RunTime are no longer needed. +\item The Discrete Gaussians can now be selected using different +bounds on the NewHope loop. This bound is defined in \verb|config.h|. +The previous value was hardwired to 20, we now allow the user +to compile the system with any value. The default is now one. +See Section \ref{sec:fhe} for details of this. +\item We have extended the range of ring dimensions available, +so bigger parameters can be utilized. Note, this is untested +in terms of memory usage, so could result in huge memory +and/or network problems. +\item You can also, by editing \verb|config.h| use discrete Gaussian +secret keys instead of short Hamming weight keys if you so desire. +This makes the FHE parameters a little bigger though. +\item We fixed some bugs in the \verb|sfloat| class in relation to +some division operations. +\item We have added a program provided by Mark Will which allows +people using YoSys synthesis tools to produce circuits in Bristol +Fashion. +\end{enumerate} + + \subsection{Changes in version 1.5 from 1.4.1} Apart from the usual minor bug fixes... \begin{enumerate} diff --git a/Documentation/Compiler.tex b/Documentation/Compiler.tex index a7bdbe28..967bb817 100644 --- a/Documentation/Compiler.tex +++ b/Documentation/Compiler.tex @@ -811,7 +811,6 @@ \subsubsection{Operations on Data Types} \paragraph{Exponentiation:} We also provide a built-in exponentiation operator for exponentiation over integer and fractional $\modp$ data types ($**$), when the base is secret shared. -For the fractional case, This overload provides a simple implementation for successive multiplications. We provide more comprehensive protocols for exponentiation of fractional inputs in the following sections. @@ -832,6 +831,13 @@ \subsubsection{Operations on Data Types} This is not the case for integer types. We invite the reader to revise the Advance Protocols section for a revision of alternatives to compute the exponent on fractional data types. +One can also raise a \verb|sfix| value to another \verb|sfix| value using the +function \verb|mpc_math.pow_fx|. However, when doing this the routine uses +\verb|sfloat| variables internally, thus you need to ensure the two types +are parametrized correctly. +In particular you must have \verb|k| parameter for \verb|sfix| being greater than +the \verb|vlen| for \verb|sfloat|. + \paragraph{Comparisons (Inequality Tests):} We have in-built operators for comparisons as well. Indeed, comparison of secret values is supported, @@ -987,10 +993,10 @@ \subsubsection{Printing} \noindent There are other member functions which perform printing as well, these are -\func{cfix.print_fix_plain()\\ -regint.print_reg_plain()\\ -cfloat.print_float_plain()\\ -cint.print_reg_plain()} +\func{cfix.print_fix()\\ +regint.print_reg()\\ +cfloat.print_float()\\ +cint.print_reg()} \subsubsection{How to print Vectorized data} @@ -1028,7 +1034,7 @@ \subsubsection{class sfix} \func{v} \textbf{Accessed by:} \verb|Default|.\\ \textbf{Type:} \verb|sint|.\\ - S Stores a register of type \verb|sint| on the $\{-2^k-1, 2^k-1\}$ interval, encoding of the rational original value. + Stores a register of type \verb|sint| on the $\{-2^k-1, 2^k-1\}$ interval, encoding of the rational original value. \func{f} \textbf{Accessed by:} \verb|Default|.\\ \textbf{Type:} \verb|int|.\\ @@ -1078,7 +1084,7 @@ \subsubsection{class sfix} a = sfix() a.load_int(5) b = a*3.0 -print_ln(b.reveal(b)) #the output is 15 +print_ln("the answer is %s", b.reveal()) #the output is 15 \end{lstlisting} \func{sfix.conv()} \textbf{Accessed by:} \verb|Default|. \\ @@ -1092,7 +1098,7 @@ \subsubsection{class sfix} a =sfix() a.load_int(4.5) v = a.conv() -print_ln(v.reveal()) # the output is 4718592 +print_ln("the answer is %s", v.reveal()) # the output is 4718592 \end{lstlisting} %\func{sfix.store_in_mem} @@ -1110,7 +1116,7 @@ \subsubsection{class sfix} a =sfix() a.load_int(4.5) r = a.sizeof() -print_ln(r) # the output is 1. +print_ln("the answer is %s", r) # the output is 1. # By Default the global_vector_size is set to 1. \end{lstlisting} \func{sfix.compute_reciprocal()} @@ -1126,7 +1132,7 @@ \subsubsection{class sfix} a =sfix() a.load_int(4.5) r = a.compute_reciprocal() -print_ln(r.reveal()) # the output is 0.222222 +print_ln("the answer is %s", r.reveal()) # the output is 0.222222 \end{lstlisting} \paragraph{Observations:} \begin{description} @@ -1203,7 +1209,7 @@ \subsubsection{class cfix} a = cfix() a.load_int(5) b = a*3.0 -print_ln(b) #the output is 15 +print_ln("the answer is %s", b) #the output is 15 \end{lstlisting} \func{cfix.conv()} \textbf{Accessed by:} \verb|Default|. \\ @@ -1217,7 +1223,7 @@ \subsubsection{class cfix} a =cfix() a.load_int(4.5) v = a.conv() -print_ln(v) # the output is 4718592 +print_ln("the answer is %s", v) # the output is 4718592 \end{lstlisting} %\func{sfix.store_in_mem} @@ -1235,7 +1241,7 @@ \subsubsection{class cfix} a =cfix() a.load_int(4.5) r = a.sizeof() -print_ln(r) # the output is 4. +print_ln("the answer is %s", r) # the output is 4. # By Default the global_vector_size is set to 1. \end{lstlisting} \func{cfix.compute_reciprocal()} @@ -1250,7 +1256,7 @@ \subsubsection{class cfix} a =sfix() a.load_int(4.5) r = a.compute_reciprocal() -print_ln(r) # the output is 0.222222 +print_ln("the answer is %s", r) # the output is 0.222222 \end{lstlisting} \paragraph{Observations:} \begin{description} @@ -1414,7 +1420,7 @@ \subsubsection{class sfloat} \begin{lstlisting} a = sfloat(4.5) r = a.sizeof() -print_ln(r) # the output is 5. +print_ln("the answer is %s", r) # the output is 5. # By Default the global_vector_size is set to 1. \end{lstlisting} @@ -1473,7 +1479,7 @@ \subsubsection{class cfloat} \begin{lstlisting} a =cfloat(4.5) r = a.sizeof() -print_ln(r) # the output is 4. +print_ln("the answer is %s", r) # the output is 4. # By Default the global_vector_size is set to 1. \end{lstlisting} @@ -1612,7 +1618,7 @@ \subsubsection{Arrays} @while_do(lambda x: x < 5, 0) def while_body(i): - print_ln("%s",new_array[i].reveal()) + print_ln("%s", new_array[i].reveal()) return i+1 \end{lstlisting} Declares new array of size 10, then fills it with values from 1 to 10, at the end prints first five values, so it prints numbers from 1 to 5. Note that the values of array can be modified inside the function, they are exacly like MemValue. \\ diff --git a/Documentation/Documentation.tex b/Documentation/Documentation.tex index 48b9845f..5bf2d7d0 100644 --- a/Documentation/Documentation.tex +++ b/Documentation/Documentation.tex @@ -3,6 +3,7 @@ \usepackage[nottoc]{tocbibind} +\usepackage{placeins} \usepackage{verbatim} \usepackage{fullpage} \usepackage{times} @@ -32,7 +33,7 @@ \newcommand{\msubsection}[1]{\newpage \subsection{#1}} \newcommand{\msubsubsection}[1]{\subsubsection{#1}} -\title{SCALE--MAMBA v1.5 : Documentation} +\title{SCALE--MAMBA v1.6 : Documentation} \author{ A. Aly \and D. Cozzo diff --git a/Documentation/FHE.tex b/Documentation/FHE.tex index ee9d237d..cdb5df88 100644 --- a/Documentation/FHE.tex +++ b/Documentation/FHE.tex @@ -44,25 +44,27 @@ In fact this is an under-estimate of the probability. From $\epsilon$ we define $e_i$ such that $\mathsf{erfc}(e_i)^i \approx 2^{-\epsilon}$ and then we set $\gc_i = e_i^i$. +\item \verb+NewHopeB+ (default $1$): +This defines how Gaussians are selected in the FHE system for +Full Threshold. We use the NewHope approximation of +$\sum b_i - b_i'$, where $b_i, b_i' \in \{0,1\}$, +with the sum being over \verb+NewHopeB+ values of $i$. +This gives an approximation to a discrete Gaussian with +standard deviation $\sigma = \sqrt{\texttt{NewHopeB}/2}$. +\item \verb+HwtSK+ (default $64$): +The Hamming weight of the secret key. If this is a negative +number then the discrete Gaussian is used for the secret key. \end{itemize} All of these parameters can be tweaked in the file \verb+config.h+. -Another two parameters related to the FHE scheme are -\verb+hwt+, which is defined in \verb+Setup.cpp+ to -be equal to $h=64$. -This defines the number of non-zero coefficients in the -FHE secret key. -Another parameter is $\sigma$ which is the standard deviation -for our approximate discrete Gaussians, which we -hardwire to be $3.16 = \sqrt{10}$ (as we use the NewHope method -of producing approximate Gaussians). \subsection{Main Security Parameters} Our Ring-LWE samples are (essentially) always from an -approximate Gaussian distribution with standard deviation $3.16$ +approximate Gaussian distribution with standard deviation +$\sigma=\sqrt{\texttt{NewHopeB}/2}$ and from a ring with a two-power degree of $N$. This is not strictly true as some noise samples come -from small Hamming Weight distributions, and some come +from (possibly) small Hamming Weight distributions, and some come from distributions over $\{-1,0,1\}$. But the above are the main parameters, and have been used in prior works to estimate Ring-LWE security in SHE settings. @@ -80,32 +82,82 @@ \subsection{Main Security Parameters} This is done by running the following \verb+sage+ code \begin{verbatim} load("estimator.py") -n = 1024 -comp_sec = 128 -for i in range(10, 500, 5): - q= 2^i - costs = estimate_lwe(n, 3.16*sqrt(2*pi)/q, q, reduction_cost_model=BKZ.sieve, \ - skip=["arora-gb", "bkw", "dec", "mitm"]) - if any([cost.values()[0]<2^comp_sec for cost in costs.values()]): - break -print i-5 +import sys + +for n in range(10,17): + N = 2^n + #for B in [1,2,4,8,16,20]: + for B in [1]: + sigma=sqrt(B/2.0) + ans=[1,1,1] + cnt=0 + for sec in [80,128,256]: + bot=0 + top=40 + if N>5000: + top=100 + if N>20000: + top=256 + if N>40000: + top=900 + repeat=true + while repeat: + repeat=false + q=2^top + costs = estimate_lwe(N, RealField(512)(sigma*sqrt(2*pi)/q), q, \ + reduction_cost_model=BKZ.sieve, skip=["arora-gb", \ + "bkw", "dec", "mitm"]) + if not any([cost.values()[0]<2^sec for cost in costs.values()]): + bot=top + top=2*top + repeat=true + while top <> bot: + mid=round((top+bot)/2-.5) + if (mid==bot): + break + sys.stdout.flush() + q = 2^mid + costs = estimate_lwe(N, RealField(512)(sigma*sqrt(2*pi)/q), q, \ + reduction_cost_model=BKZ.sieve, skip=["arora-gb", \ + "bkw", "dec", "mitm"]) + if any([cost.values()[0]<2^sec for cost in costs.values()]): + top=mid + else: + bot=mid + sys.stdout.flush() + ans[cnt]=bot + cnt=cnt+1 + print N,"&",B,"&",sigma,"&",ans[0],"&",ans[1],"&",ans[2],"\\\\" + sys.stdout.flush() \end{verbatim} -This will print a bunch of rubbish and then the number $29$. -This means any $q < 2^{29}$ will be ``secure'' by the above definition of secure. +When run via +\begin{center} + \verb+sage < SCALE-Est.py > Res.txt+ +\end{center} +this will produce lines of the form +\begin{center} +\verb+1024 & 1 & 0.707106781186548 & 40 & 25 & 12 \\+ +\end{center} +This that for ring dimension $1024$, with \verb+NewHopeB+ equal to +one, and so $\sigma=0.707$, that at the 80-bit security level any $q < 2^{40}$ will +be ``secure'' by the above definition of secure. +Note that producing the table for \verb+NewHopeB+ equal to one produces +values which remain secure when a higher value of \verb+NewHopeB+ is selected. -We did this in Feb 2018 and obtained the following table of values, giving maximum +We did this in Oct 2019 and obtained the following table of values, giving maximum values of $q$ in the form of $2^x$ for the value $x$ from the following table. \begin{center} -\begin{tabular}{|c|c|c|c|} +\begin{tabular}{|c|c|c|c||c|c|} \hline -$N$ & \verb+comp_sec+=80 & \verb+comp_sec+=128 & \verb+comp_sec+=256 \\ +$N$ & \verb+NewHopeB+ & $\sigma$ & \verb+comp_sec+=80 & \verb+comp_sec+=128 & \verb+comp_sec+=256 \\ \hline -1024 & 44 & 29 & 16 \\ -2048 & 86 & 56 & 31 \\ -4096 & 171 & 111 & 60 \\ -8192 & 344 & 220 & 120 \\ -16384 & 690 & 440 & 239 \\ -32768 & 998 & 883 & 478 \\ +1024 & 1 & 0.707106781186548 & 40 & 25 & 12 \\ +2048 & 1 & 0.707106781186548 & 82 & 52 & 26 \\ +4096 & 1 & 0.707106781186548 & 167 & 106 & 56 \\ +8192 & 1 & 0.707106781186548 & 340 & 215 & 115 \\ +16384 & 1 & 0.707106781186548 & 686 & 436 & 235 \\ +32768 & 1 & 0.707106781186548 & 1392 & 879 & 473 \\ +65536 & 1 & 0.707106781186548 & 2830 & 1778 & 953 \\ \hline \end{tabular} \end{center} @@ -158,8 +210,8 @@ \subsection{Distributions and Norms} probability of coefficient is $p_{-1}=1/4$, $p_0=1/2$ and $p_1=1/4$. \item $\dN(\sigma^2,N)$: This generates a vector of - length $N$ with elements chosen according to an approximation to - the discrete Gaussian distribution with variance $\sigma^2$. + length $N$ with elements chosen according to the NewHope + approximation to the discrete Gaussian distribution with variance $\sigma^2$. \item $\RC(0.5,\sigma^2,N)$: This generates a triple of elements $(v,e_0,e_1)$ where $v$ is sampled from $\ZO_s(0.5,N)$ and $e_0$ and $e_1$ are sampled from $\dN_s(\sigma^2,N)$. @@ -173,6 +225,8 @@ \subsection{Distributions and Norms} when sampled from $\dN(\sigma^2,\phi(m))$ we obtain variance $\sigma^2 \cdot \phi(m)$ and when sampled from $\calU(q,\phi(m))$ we obtain variance $q^2 \cdot \phi(m)/12$. +We let in what follows $V_\sk$ denote $\sqrt{\texttt{HwtSK}}$ in the case +when \verb|HwtSK| is positive, and $\sigma \cdot \sqrt{\phi(m)}$ otherwise. By the law of large numbers we can use $\gc_1 \cdot \sqrt{V}$, where $V$ is the above variance, as a high probability bound on the size of $a(\zeta_m)$ (the probability is $1-2^{-\epsilon}$, @@ -181,12 +235,11 @@ \subsection{Distributions and Norms} If we take a product of $t$ such elements with variances $V_1, V_2, \ldots, V_t$ then we use $\gc_t \cdot \sqrt{V_1 \cdot V_2 \cdots V_t}$ -as the resulting bounds. +as the resulting high probability bounds. In our implementation we approximate $\dN(\sigma^2,n)$ using -the binomial method from the NewHope paper, with a standard -deviation of $3.16 = \sqrt{10}$. In particular this means +the above binomial method from the NewHope paper, this means any vector sampled from $\dN(\sigma^2,n)$ will have -infinity norm bounded by $20$. +infinity norm bounded by \verb+NewHopeB+. \subsection{The FHE Scheme and Noise Analysis} @@ -219,9 +272,10 @@ \subsubsection{Key Generation:} key generation protocol for the underlying threshold FHE keys was assumed. In SCALE we assume a `magic black box' which distributes these keys, which we leave to the application developer to create. -The secret key $\sk$ is selected from a distribution with -Hamming weight $h$, i.e. $\HWT(h,\phi(m))$, -and then it is distributed amongst the $n$ parties by simply producing a random +The secret key $\sk$ is either selected from a distribution with +Hamming weight $h$, i.e. $\HWT(h,\phi(m))$ or from +$\dN(\sigma^2,N)$ (depending on what was selected in \verb|config.h|). +Then the secret key is distributed amongst the $n$ parties by simply producing a random linear combination, and assigning each party one of the sums. The switching key data is produced in the standard way, i.e. in a non-distributed trusted manner. @@ -239,7 +293,7 @@ \subsubsection{Key Generation:} \[ a_{\sk,\sk^2} \asn \calU(q,\phi(m)) \quad \mbox{ and } \quad b_{\sk,\sk^2} = a_{\sk,\sk^2} \cdot \sk + p \cdot e_{\sk,\sk^2} - p_1 \cdot \sk^2 \] where $e_{\sk,\sk^2} \asn \dN(\sigma^2,\phi(m))$. -We take $\sigma=3.16$ as described above in what follows. + \subsubsection{Encryption:} To encrypt an element $m\in R$, we choose $v, e_0, e_1 \asn \RC(0.5,\sigma^2,n)$, i.e. @@ -248,7 +302,7 @@ \subsubsection{Encryption:} \] Then we set $c_0 = b \cdot v + p \cdot e_0+m$,~ $c_1=a\cdot v+p\cdot e_1$, and set the initial ciphertext as $\ct'=(c_0,c_1)$. -We calculate a bound (with high probability) on the output noise of +We calculate a bound (which holds with high probability) on the output noise of an honestly generated ciphertext to be \begin{align*} \norm{c_0 - \sk \cdot c_1}_\infty^\can @@ -264,7 +318,7 @@ \subsubsection{Encryption:} + p \cdot \sigma \cdot \left( \gc_2 \cdot \phi(m) / \sqrt{2} + \gc_1 \cdot \sqrt{\phi(m)} - + \gc_2 \cdot \sqrt{h \cdot \phi(m)} + + \gc_2 \cdot \sqrt{\phi(m)} \cdot V_\sk \right) = B_\clean. \end{align*} Note this is a probablistic bound and not an absolute bound. @@ -272,26 +326,29 @@ \subsubsection{Encryption:} \vspace{5mm} \noindent -However, below we will only be able to guarantee the -$m, v, e_0$ and $e_1$ values of a {\bf sum} of $n$ fresh ciphertexts +However, below (using the TopGear protocol) +we will only be able to guarantee the $m, v, e_0$ and $e_1$ +values of a {\bf sum} of $n$ fresh ciphertexts (one from each party) are selected subject to -\[ \norm{v}_\infty \le 2^{\ZKsecp + 3} \cdot n \quad +\[ \norm{2 \cdot v}_\infty \le 2^{\ZKsecp + 3} \cdot n \quad \mbox{ and } \quad - \norm{e_0}_\infty, \norm{e_1}_\infty \le 20 \cdot 2^{\ZKsecp + 2} \cdot n \quad + \norm{2 \cdot e_0}_\infty, \norm{2 \cdot e_1}_\infty \le \texttt{NewHopeB} \cdot 2^{\ZKsecp + 2} \cdot n \quad \mbox{ and } \quad - \norm{m}_\infty \le 2^{\ZKsecp + 1} \cdot n \cdot p, + \norm{2 \cdot m}_\infty \le 2^{\ZKsecp + 1} \cdot n \cdot p, \] where $\ZKsecp$ is our soundness parameter for the -zero-knowledge proofs -and $U$ is selected so that $(2 \cdot \phi(m))^U > 2^\Soundsecp$. -In this situation we obtain the bound, using the inequality above -between the infinity norm in the polynomial embedding -and the infinity norm in the canonical embedding, +zero-knowledge proofs and $U$ is selected so that +$(2 \cdot \phi(m))^U > 2^\Soundsecp$. +Thus in TopGear we double the generated ciphertext +(which is the sum of the input players ciphertext) +to obtain a ciphertext $(c_0,c_1)$ which even in the +case of dishonest players has a noise bound in the +infinity norm in the canonical embedding of, \begin{align*} \norm{c_0 - \sk \cdot c_1}_\infty^\can &\le \sum_{i=1}^n \norm{2 \cdot m_i}_\infty^\can - + p \cdot \Big(\norm{\epsilon}_\infty^\can \cdot \norm{2 \cdot e_{2,i}}_\infty^\can + + p \cdot \Big(\norm{\epsilon}_\infty^\can \cdot \norm{2 \cdot v_i}_\infty^\can + \norm{2 \cdot e_{0,i}}_\infty^\can \\ &\hspace{3.5cm} + \norm{\sk}_\infty^\can \cdot \norm{2 \cdot e_{1,i}}_\infty^\can \Big) \\ @@ -300,18 +357,18 @@ \subsubsection{Encryption:} + p \cdot \Big( \gc_1 \cdot \sigma \cdot \phi(m)^{3/2} \cdot 2 \cdot 2^{\ZKsecp+2} \cdot n\\ &\hspace{3cm} - + \phi(m) \cdot 2 \cdot 2^{\ZKsecp+2} \cdot n \cdot 20 \\ + + \phi(m) \cdot 2 \cdot 2^{\ZKsecp+2} \cdot n \cdot \texttt{NewHopeB} \\ &\hspace{3cm} - + \gc_1 \cdot \sqrt{h} \cdot \phi(m) \cdot 2 \cdot 2^{\ZKsecp+2} \cdot n \cdot 20 + + \gc_1 \cdot V_\sk \cdot \phi(m) \cdot 2 \cdot 2^{\ZKsecp+2} \cdot n \cdot \texttt{NewHopeB} \Big) \\ &=\phi(m) \cdot 2^{\ZKsecp+2} \cdot n \cdot p \cdot \Big( \frac{41}{2} + \gc_1 \cdot \sigma \cdot \phi(m)^{1/2} - + 20 \cdot \gc_1 \cdot \sqrt{h} + + \texttt{NewHopeB} \cdot \gc_1 \cdot V_\sk \Big) \\ & = B_\clean^\dishonest. \end{align*} Again this is a probabilistic bound (assuming validly -distributed key generation), but assumes the worst case +distributed key generation), but assumes the {\em worst case} ciphertext bounds. \subsubsection{$\SwitchModulus((c_0,c_1))$:} @@ -331,7 +388,7 @@ \subsubsection{$\SwitchModulus((c_0,c_1))$:} \begin{align*} B_\scale &= c_1 \cdot p \cdot \sqrt{ \phi(m)/12} - + c_2 \cdot p \cdot \sqrt{ \phi(m) \cdot h/12}, \\ + + c_2 \cdot p \cdot \sqrt{ \phi(m)/12} \cdot V_\sk, \\ \end{align*} This is again a probabilistic analysis, assuming validly generated public keys. @@ -368,7 +425,7 @@ \subsubsection{$\DistDec_{\{\sk_i\}}(\ct)$:} \item Player one computes $\vv_1 = e_{\vm+\vf}^{(0)}-\sk_1 \cdot e_{\vm+\vf}^{(1)}$ and player $i \ne 1$ computes. $\vv_i = -\sk_i \cdot e_{\vm+\vf}^{(1)}$. \item All players compute $\vt_i = \vv_i + p \cdot \vr_i$ for some random element -with infinity norm given by $2^\DDsecp \cdot B/p$, where $\DDsecp$ is the parameter defining statistical distance for the distributed decryption. +with infinity norm given by $2^\DDsecp \cdot B_\dec/p$, where $\DDsecp$ is the parameter defining statistical distance for the distributed decryption. \item The parties broadcast $\vt_i$. \item The parties compute $\vm+\vf = \sum \vt_i \pmod{p}$. \end{enumerate} @@ -383,15 +440,15 @@ \subsubsection{$\DistDec_{\{\sk_i\}}(\ct)$:} \paragraph{Reshare Version 1:} This is described in \figref{reshare}. -The value $B$ in the protocol is an upper bound on the noise in the canonical embedding +The value $B_\dec$ in the protocol is an upper bound on the noise in the canonical embedding $\nu$ associated with a ciphertext we will decrypt in our protocols. To ensure valid distributed decryption we require -\[ 2 \cdot (1+n \cdot 2^\DDsecp) \cdot B < q_{0}. \] +\[ 2 \cdot (1+n \cdot 2^\DDsecp) \cdot B_\dec < q_{0}. \] -Given a value of $B$, we therefore will obtain a lower bound +Given a value of $B_\dec$, we therefore will obtain a lower bound on $p_0$ by the above inequality. The addition of a random term with infinity norm bounded by -$2^\DDsecp \cdot B/p$ in the distributed decryption procedure +$2^\DDsecp \cdot B_\dec/p$ in the distributed decryption procedure ensures that the individual {\em coefficients} of the sum $\vt_1+\cdots+\vt_n$ are statistically indistinguishable from random, with probability $2^{-\DDsecp}$. @@ -414,9 +471,9 @@ \subsubsection{$\DistDec_{\{\sk_i\}}(\ct)$:} \begin{Boxfig}{Distributed decryption to secret sharing}{distdec}{$\mathsf{Reshare-2}$} -Furthermore, let $B$ denote a bound on the noise, that is $\norm{c_0 - s \cdot c_1}_\infty$. +Furthermore, let $B_\dec$ denote a bound on the noise, that is $\norm{c_0 - s \cdot c_1}_\infty$. \begin{enumerate} -\item Party $i$ samples $\vf_i \asn [0,B\cdot 2^\DDsecp]^N$. +\item Party $i$ samples $\vf_i \asn [0,B_\dec \cdot 2^\DDsecp]^N$. \item Party $1$ computes $\vv_i := (c_0 - s_1 \cdot c_1) - \vf_1 \bmod q$, and every other party $i$ computes $\vv_i := -s_i \cdot c_1- \vf_i$. @@ -444,12 +501,12 @@ \subsubsection{$\SwitchKey(d_0,d_1,d_2)$:} So we expect \begin{align*} \norm{ p \cdot d_2 \cdot e_{\sk,\sk^2}}_\infty^\can - &\le p \cdot c_2 \cdot \sqrt{q_0^2/12 \cdot \sigma^2 \cdot \phi(m)^2} \\ - & = p \cdot c_2 \cdot q_0 \cdot \sigma \cdot \phi(m)/\sqrt{12} \\ + &\le p \cdot \gc_2 \cdot \sqrt{q_0^2/12 \cdot \sigma^2 \cdot \phi(m)^2} \\ + & = p \cdot \gc_2 \cdot q_0 \cdot \sigma \cdot \phi(m)/\sqrt{12} \\ & = B_\KS \cdot q_0. \end{align*} Thus -\[ B_\KS = p \cdot c_2 \cdot \sigma \cdot \phi(m)/\sqrt{12}. \] +\[ B_\KS = p \cdot \gc_2 \cdot \sigma \cdot \phi(m)/\sqrt{12}. \] Then if the input to $\SwitchKey$ has noise bounded by $\nu$ then the output noise value in the canonical embedding will be bounded by \[ \nu+\frac{B_\KS \cdot p_0}{p_1} + B_\scale. \] @@ -484,7 +541,7 @@ \subsubsection{Application to the Offline Phase:} \[ U_2 = U_1 + \frac{B_\clean^\dishonest}{p_1} + B_\scale. \] To ensure valid (distributed) decryption, we require \[ 2 \cdot U_2 \cdot (1+n \cdot 2^\DDsecp) < p_0, \] -i.e. we take $B=U_2$ in our distributed decryption protocol. +i.e. we take $B_\dec=U_2$ in our distributed decryption protocol. Note, that here we take the worst case bound for the ciphertext noise, but probabilistic analysis everywhere else. Since the key generation is assumed to be honestly performed. diff --git a/Documentation/FigZKPoK.tex b/Documentation/FigZKPoK.tex index c7a5d546..c3c22434 100644 --- a/Documentation/FigZKPoK.tex +++ b/Documentation/FigZKPoK.tex @@ -1,6 +1,7 @@ \begin{Boxfig}{Protocol for global proof of knowledge of a set of ciphertexts}{PZK1}{Protocol $\Pi_{\gZKPoK}$} The protocol is parametrized by integer parameters $U,V$ and $\flag \in \left\{ \Diag, \perp \right\}$ as well as $\pk$ and further parameters of the encryption scheme. +Define $\rho_1=1$ and $\rho_2, \rho_3 = \texttt{NewHopeB}$. \vspace{3mm} diff --git a/Documentation/Installation.tex b/Documentation/Installation.tex index 6ab1eee7..e47ed1f8 100644 --- a/Documentation/Installation.tex +++ b/Documentation/Installation.tex @@ -10,6 +10,7 @@ \subsection{Installation} \item CPU supporting AES-NI and PCLMUL \item OpenSSL: Tested with version 1.1.0.b \item Crypto++: Tested with version 7.0 +\item CMake, only if you want to use it instead of make: Version 3.1 to 3.14 is required \end{itemize} Developers will also require \begin{itemize} @@ -103,7 +104,7 @@ \subsubsection{Change config.h} config.h \end{verbatim} in the sub-directory \verb+src+. -The main things to watch out for here are the various security parameters; +The main things to watch out for here are the various FHE security parameters; these are explained in more detail in Section \ref{sec:fhe}. Note, to configure the statistical security parameter for the number representations in the compiler (integer comparison, fixed point etc) from the default @@ -125,6 +126,37 @@ \subsubsection{Final Compilation} That's it! After make finishes then you should see a `Player.x` executable inside the SCALE-MAMBA directory. +\subsubsection{Compile with CMake (experimental)} +It is possible to build SCALE with CMake. +We introduced CMake because there are many development tools that work with CMake-based projects, +e.g., CLion, clangd and so on. + +You may install the dependencies the same way as above. +We explain how to use CMake with the example below. + +\begin{verbatim} + mkdir src/build + cd src/build + # create the cmake project + CC=gcc CXX=g++ cmake \ + -DOPENSSL_ROOT_DIR=$openssl_root \ + -DCRYPTOPP_ROOT_DIR=$cryptopp_root \ + -DMPIR_ROOT_DIR=$mpir_root .. + # build the project + make +\end{verbatim} + +The first step is to have CMake create a project using the \verb+cmake+ command. +The compiler can be changed using \verb+CC+ and \verb+CXX+. +Use the \verb+-D+ flag to configure the dependencies. +For example, if the MPIR library is in \verb+$HOME/mpir/lib+ and its include files are in \verb+$HOME/mpir/include+, +then \verb+-DMPIR_ROOT_DIR+ should be set to \verb+$HOME/mpir+, i.e., the parent directory of \verb+lib+ and \verb+include+. +If the dependencies are installed in the default directories, +e.g., \verb+/usr/{lib64,include}+ or \verb+/usr/local/{lib64,include}+, +then the \verb+-D+ flags are not needed. +Finally, run \verb+make+ to create the binaries. +The \verb+cmake+ command is not needed in subsequent compilations. +For more information on CMake, we recommend this excellent wiki\footnote{\url{https://gitlab.kitware.com/cmake/community/wikis/home}}. \subsection{Creating and Installing Certificates} @@ -237,8 +269,10 @@ \subsubsection{Data for networking} \item Which IP address is going to be used \item The name of the certificate for that player \end{itemize} +\iffalse XXXX \item Whether a fake offline phase is going to be used. \item Whether a fake sacrifice phase is going to be used. +\fi \end{itemize} \subsubsection{Data for secret sharing:} @@ -269,8 +303,10 @@ \subsubsection{Data for secret sharing:} research system. At this stage we also generate a set of keys for distributed decryption of a level-one FHE scheme if needed. +\iffalse XXXX For the case of fake offline we assume these keys are on {\em each} computer, but using fake offline is only for test purposes in any case. +\fi \paragraph{Shamir Secret Sharing:} Shamir secret sharing we assume is self-explanatory. diff --git a/Documentation/Introduction.tex b/Documentation/Introduction.tex index fc2370d6..4d2a86d2 100644 --- a/Documentation/Introduction.tex +++ b/Documentation/Introduction.tex @@ -72,28 +72,6 @@ In any real system this entire setup phase will need investigating, with perhaps using HSMs to construct and deploy keys if no actively secure key generation protocol is available. -\item -There is a security hole in how we have implemented things. -As the -\begin{center} - \verb+offline->sacrifice->online+ -\end{center} -pipeline is run continously in separate threads, each with their own channels etc., we -may {\bf use} a data item in the online phase {\bf before} the checking -of a data item has {\bf fully completed} (i.e. before in an associated -sacrifice etc. has been MAC-checked, for full-threshold, -or hash-checked, for other LSSS schemes). -In a real system you will want to address this by having some -other list of stuff (between sacrifice and online), which -ensures that all checks are complete before online is -allowed to use any data; or by ensuring MAC/hash-checking is -done before the sacrifice queues are passed to the online phase. -In our current system if something bad is introduced by an adversary -then the system {\bf will} halt. -But, before doing so there is a {\em small} chance that some data will -have leaked from the computation if the adversary can schedule the -error at exactly the right point (depending on what is happening in -other threads). \end{itemize} @@ -108,18 +86,13 @@ \subsection{Architecture} Using multiple threads enables you to get high throughput. Almost all of our experimental results are produced using multiple threads. \item Each online is associated with another four ``feeder'' threads. -One produces multiplication triples, one produces square pairs -and one produces shared bits. -The fourth thread performs the sacrificing step, as well as -the preprocessing data for player IO. -The chain of events is that the multiplication thread produces -an unchecked triple. This triple is added to a triple-list (which is -done in batches for efficiency). -At some point the sacrifice thread decides to take some -data off the triple-list and perform the triple checking via -sacrificing. -Once a triple passes sacrificing it is passed onto another -list, called the sacrificed-list, for consumption by the online phase. +One produces multiplication triples, one produces square pairs, +one produces shared bits and one produces data for input/output +of data items. +The chain of events is that the multiplication thread (say) produces +a fully checked triple. This triple is added to a triple-list (which is +done in batches for efficiency) for consumption by the online +phase. The sizes of these lists can be controlled (somewhat) by the values in the file \verb+config.h+. One can control the number of entries {\em ever} diff --git a/Documentation/symbols.tex b/Documentation/symbols.tex index 9044017e..ce4ba073 100644 --- a/Documentation/symbols.tex +++ b/Documentation/symbols.tex @@ -203,6 +203,7 @@ \newcommand{\clean}{\mathsf{clean}} \newcommand{\scale}{\mathsf{scale}} +\newcommand{\dec}{\mathsf{dec}} \newcommand{\dishonest}{\mathsf{dishonest}} \newcommand{\KS}{\mathsf{KS}} diff --git a/Programs/restart_1/restart.mpc b/Programs/restart_1/restart.mpc index 7921c6bf..712ea517 100644 --- a/Programs/restart_1/restart.mpc +++ b/Programs/restart_1/restart.mpc @@ -1,16 +1,17 @@ -sfloat.vlen = 8 # Length of mantissa in bits -sfloat.plen = 5 # Length of exponent in bits -sfloat.kappa = 4 # Statistical security parameter for floats -a = [sfloat(i) for i in [-.03,2]] -a[0], a[1] = cond_swap(a[0], a[1]) +# Write some data to file +inp = [sint(1), sint(2), sint(3), sint(4)] +output_shares(3000,*inp) -a = [sfloat(i) for i in [3,0]] -a[0], a[1] = cond_swap(a[0], a[1]) - -import random -random.seed(0) -a = [sfloat(random.uniform(-100,100)) for i in range(8)] -odd_even_merge_sort(a) +print_ln("Player zero enter a number") +a=sint.get_private_input_from(0) +print_ln("Player one enter a number") +b=sint.get_private_input_from(1) +c=a+b +print_ln("The SUM is being sent to player one") +c.reveal_to(1) +print_reg(reveal(a)) +print_reg(reveal(b)) +print_reg(reveal(c)) restart() diff --git a/Programs/test_dabit/test_dabit.mpc b/Programs/test_dabit/test_dabit.mpc new file mode 100644 index 00000000..fc7d3699 --- /dev/null +++ b/Programs/test_dabit/test_dabit.mpc @@ -0,0 +1,29 @@ +n_parallel = 8192 +n_threads = 8 + +n = 8192 * (2**12) + +def generate_bits_single(n): + for i in range(n / n_parallel): + bp = sint(size=n_parallel) + b2 = sbit(size=n_parallel) + vdabit(n_parallel, *(bp, b2)) + +def generate_bits_multithread(n): + def f(): + @for_range(n / n_parallel / n_threads) + def g(_): + bp = sint(size=n_parallel) + b2 = sbit(size=n_parallel) + vdabit(n_parallel, *(bp, b2)) + + t = MPCThread(f, 'f') + + for i in range(n_threads): + t.start() + for i in range(n_threads): + t.join() + +start_timer(1) +generate_bits_multithread(n) +stop_timer(1) diff --git a/README.md b/README.md index 10105db9..2dbb0176 100644 --- a/README.md +++ b/README.md @@ -7,26 +7,28 @@ First type -
- make doc -
+``` +make doc +``` + Then *read* the documentation! Note: For Leuven maintainers, if wishing to recompile the basic 64 bit circuits then call -- make circuits -
+``` +make circuits +``` + These are then compiled down from the netlist down to the Bristol fashion again, and then simplified. After doing this run -- ./Test-Convert.x -
+``` +./Test-Convert.x +``` to check all is OK. -If you want to recompile the .net circuits from the .vhd see the +If you want to recompile the `.net` circuits from the `.vhd` see the instructions in Circuits/README.txt diff --git a/Scripts/compile-scasm.sh b/Scripts/compile-scasm.sh new file mode 100755 index 00000000..2e98bb6b --- /dev/null +++ b/Scripts/compile-scasm.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# This file creates the test assembly files for scascm + +# Portably find the directory containing this script. +HERE=$(cd `dirname $0`; pwd) +# Assuming it lives in Scripts/, set ROOT to the root directory of the +# project. +ROOT=$HERE/.. +# Now set the PYTHONPATH so we don't have to worry about the CWD +# whenever we invoke python scripts. +export PYTHONPATH=$ROOT:$PYTHONPATH + +shift $[OPTIND-1] + +run_test() { + test=$1 + shift + printf "\n\n\n\n\n\n\n\n\n\n" + echo "$test" + $ROOT/compile.py -a scasm -n -r -M -u $compile_opts $* Programs/$test || exit 1 +} + +test_opts="-s" +compile_opts="--stop" +for test in test_all test_array test_branch test_branching test_comparison test_count test_empty_tape test_flex test_float test_floatingpoint test_float_sorting test_float_vector test_for_range_multithread test_function test_idle_threads test_lib test_loop test_map_reduce test_mem_order test_new_threads test_sregint test_threads test_vector test_sfix test_sqrt test_math test_custom_array test_fix_array do_nothing GC_test IO_demo Local_test mem_clear_demo mult_demo restart sregint_tests tutorial test_dabit; do + run_test $test +done diff --git a/Scripts/test-n-10.sh b/Scripts/test-n-10.sh index 2e7c05f4..1ba06dec 100755 --- a/Scripts/test-n-10.sh +++ b/Scripts/test-n-10.sh @@ -41,7 +41,7 @@ if test "$1"; then else test_opts="-s" compile_opts="--stop" - for test in test_all test_array test_branch test_branching test_comparison test_empty_tape test_flex test_float test_floatingpoint test_float_sorting test_float_vector test_function test_idle_threads test_lib test_loop test_mem_order test_sregint test_threads test_vector test_sfix test_sqrt test_math test_custom_array; do + for test in test_array test_branch test_branching test_comparison test_empty_tape test_flex test_float test_floatingpoint test_float_sorting test_float_vector test_function test_idle_threads test_lib test_loop test_mem_order test_sregint test_vector test_sfix test_sqrt test_math test_custom_array test_all; do run_test $test done fi diff --git a/Scripts/test.sh b/Scripts/test.sh index dd401867..5841b7c1 100755 --- a/Scripts/test.sh +++ b/Scripts/test.sh @@ -38,7 +38,7 @@ if test "$1"; then else test_opts="-s" compile_opts="--stop" - for test in test_all test_array test_branch test_branching test_comparison test_count test_empty_tape test_flex test_float test_floatingpoint test_float_sorting test_float_vector test_for_range_multithread test_function test_idle_threads test_lib test_loop test_map_reduce test_mem_order test_new_threads test_sregint test_threads test_vector test_sfix test_sqrt test_math test_custom_array test_fix_array; do + for test in test_array test_branch test_branching test_comparison test_count test_empty_tape test_flex test_float test_floatingpoint test_float_sorting test_float_vector test_for_range_multithread test_function test_idle_threads test_lib test_loop test_map_reduce test_mem_order test_new_threads test_sregint test_threads test_vector test_sfix test_sqrt test_math test_custom_array test_fix_array test_all; do run_test $test done fi diff --git a/Scripts/test_32.sh b/Scripts/test_32.sh index 21533966..1b22f8bb 100755 --- a/Scripts/test_32.sh +++ b/Scripts/test_32.sh @@ -41,7 +41,7 @@ else test_opts="-s" compile_opts="--stop" # We do not run sfix, float, math, sqrt and custom_array tests for 32 bit inputs - for test in test_all test_array test_branch test_branching test_comparison test_count test_empty_tape test_flex test_for_range_multithread test_function test_idle_threads test_lib test_loop test_map_reduce test_mem_order test_new_threads test_threads test_vector; do + for test in test_array test_branch test_branching test_comparison test_count test_empty_tape test_flex test_for_range_multithread test_function test_idle_threads test_lib test_loop test_map_reduce test_mem_order test_new_threads test_threads test_vector test_all; do run_test $test done fi diff --git a/Test/FHE-P.cpp b/Test/FHE-P.cpp index 38493180..9f1aaa05 100644 --- a/Test/FHE-P.cpp +++ b/Test/FHE-P.cpp @@ -17,6 +17,10 @@ int main() cout << "Enter plaintext modulus size " << endl; cin >> lg2p; + int newHB; + cout << "Enter the NewHope loop bound" << endl; + cin >> newHB; + vector