From 0864e0421732e2a7cf665788a1225e750ea1e350 Mon Sep 17 00:00:00 2001 From: Michael Zohner Date: Fri, 15 May 2015 11:03:23 +0200 Subject: [PATCH] Initial file upload --- .gitmodules | 3 + Makefile | 52 ++ Makefile~ | 52 ++ src/bench_psi.cpp | 425 +++++++++++++ src/bench_psi.h | 61 ++ src/externals/Miracl | 1 + src/hashing/cuckoo.cpp | 196 ++++++ src/hashing/cuckoo.h | 60 ++ src/hashing/hashing_util.h | 216 +++++++ src/hashing/kcuckoo3a.C | 154 +++++ src/hashing/mt-real.c | 90 +++ src/hashing/simple_hashing.cpp | 178 ++++++ src/hashing/simple_hashing.h | 54 ++ src/hashing/util.h | 103 ++++ src/naive-hashing/naive-psi.cpp | 218 +++++++ src/naive-hashing/naive-psi.h | 60 ++ src/ot-based/ot-psi.cpp | 855 ++++++++++++++++++++++++++ src/ot-based/ot-psi.h | 93 +++ src/pk-based/dh-psi.cpp | 325 ++++++++++ src/pk-based/dh-psi.h | 69 +++ src/thirdparty/shpsi.cpp | 329 ++++++++++ src/thirdparty/shpsi.exe | Bin 0 -> 20100 bytes src/thirdparty/shpsi.h | 55 ++ src/util/cbitvector.cpp | 680 ++++++++++++++++++++ src/util/cbitvector.h | 305 +++++++++ src/util/codewords.cpp | 25 + src/util/codewords.h | 283 +++++++++ src/util/connection.cpp | 78 +++ src/util/connection.h | 22 + src/util/crypto/crypto.cpp | 409 ++++++++++++ src/util/crypto/crypto.h | 139 +++++ src/util/crypto/ecc-pk-crypto.cpp | 289 +++++++++ src/util/crypto/ecc-pk-crypto.h | 143 +++++ src/util/crypto/gmp-pk-crypto.cpp | 280 +++++++++ src/util/crypto/gmp-pk-crypto.h | 118 ++++ src/util/crypto/pk-crypto.h | 88 +++ src/util/miracl.a | Bin 0 -> 529540 bytes src/util/ot/baseOT.h | 53 ++ src/util/ot/maskingfunction.h | 33 + src/util/ot/naor-pinkas.cpp | 204 ++++++ src/util/ot/naor-pinkas.h | 26 + src/util/ot/opemasking.h | 236 +++++++ src/util/ot/ot-extension-1oon-ecc.cpp | 624 +++++++++++++++++++ src/util/ot/ot-extension-1oon-ecc.h | 129 ++++ src/util/ot/ot-extension.cpp | 786 +++++++++++++++++++++++ src/util/ot/ot-extension.h | 284 +++++++++ src/util/ot/xormasking.h | 155 +++++ src/util/ot/xorrgbfmasking.h | 97 +++ src/util/socket.h | 274 +++++++++ src/util/thread.h | 255 ++++++++ src/util/typedefs.h | 136 ++++ 51 files changed, 9800 insertions(+) create mode 100644 .gitmodules create mode 100644 Makefile create mode 100644 Makefile~ create mode 100644 src/bench_psi.cpp create mode 100644 src/bench_psi.h create mode 160000 src/externals/Miracl create mode 100644 src/hashing/cuckoo.cpp create mode 100644 src/hashing/cuckoo.h create mode 100644 src/hashing/hashing_util.h create mode 100644 src/hashing/kcuckoo3a.C create mode 100644 src/hashing/mt-real.c create mode 100644 src/hashing/simple_hashing.cpp create mode 100644 src/hashing/simple_hashing.h create mode 100644 src/hashing/util.h create mode 100644 src/naive-hashing/naive-psi.cpp create mode 100644 src/naive-hashing/naive-psi.h create mode 100644 src/ot-based/ot-psi.cpp create mode 100644 src/ot-based/ot-psi.h create mode 100644 src/pk-based/dh-psi.cpp create mode 100644 src/pk-based/dh-psi.h create mode 100644 src/thirdparty/shpsi.cpp create mode 100755 src/thirdparty/shpsi.exe create mode 100644 src/thirdparty/shpsi.h create mode 100644 src/util/cbitvector.cpp create mode 100644 src/util/cbitvector.h create mode 100644 src/util/codewords.cpp create mode 100644 src/util/codewords.h create mode 100644 src/util/connection.cpp create mode 100644 src/util/connection.h create mode 100644 src/util/crypto/crypto.cpp create mode 100644 src/util/crypto/crypto.h create mode 100644 src/util/crypto/ecc-pk-crypto.cpp create mode 100644 src/util/crypto/ecc-pk-crypto.h create mode 100644 src/util/crypto/gmp-pk-crypto.cpp create mode 100644 src/util/crypto/gmp-pk-crypto.h create mode 100644 src/util/crypto/pk-crypto.h create mode 100644 src/util/miracl.a create mode 100644 src/util/ot/baseOT.h create mode 100644 src/util/ot/maskingfunction.h create mode 100644 src/util/ot/naor-pinkas.cpp create mode 100644 src/util/ot/naor-pinkas.h create mode 100644 src/util/ot/opemasking.h create mode 100644 src/util/ot/ot-extension-1oon-ecc.cpp create mode 100644 src/util/ot/ot-extension-1oon-ecc.h create mode 100644 src/util/ot/ot-extension.cpp create mode 100644 src/util/ot/ot-extension.h create mode 100644 src/util/ot/xormasking.h create mode 100644 src/util/ot/xorrgbfmasking.h create mode 100644 src/util/socket.h create mode 100644 src/util/thread.h create mode 100644 src/util/typedefs.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..91b9bf3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/externals/Miracl"] + path = src/externals/Miracl + url = git://github.com/CertiVox/Miracl.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3d3d2d3 --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ + +BIN=bin +SRC=src +EXT=${SRC}/externals + +# compiler settings +CC=g++ +COMPILER_OPTIONS=-O2 +DEBUG_OPTIONS=-g3 -Wall +BATCH= + +ARCHITECTURE = $(shell uname -m) +ifeq (${ARCHITECTURE},x86_64) +MIRACL_MAKE:=linux64 +GNU_LIB_PATH:=x86_64 +else +MIRACL_MAKE:=linux +GNU_LIB_PATH:=i386 +endif + +INCLUDE=-I.. -I/usr/include/glib-2.0/ -I/usr/lib/${GNU_LIB_PATH}-linux-gnu/glib-2.0/include + + +LIBRARIES=-lgmp -lgmpxx -lpthread src/miracl_lib/miracl.a -L /usr/lib -lssl -lcrypto -lglib-2.0 +CFLAGS= + +# directory for the Miracl submodule and library +MIRACL_LIB_DIR=${EXT}/miracl_lib +SOURCES_MIRACL=${EXT}/Miracl/* +OBJECTS_MIRACL=${MIRACL_LIB_DIR}/*.o + +all: miracl + @echo "make all done." + +# this will create a copy of the files in ${SOURCES_MIRACL} and its sub-directories and put them into ${MIRACL_LIB_DIR} without sub-directories, then compile it +miracl: ${MIRACL_LIB_DIR}/miracl.a + +# copy Miracl files to a new directory (${CORE}/util/miracl_lib/), call the build script and delete everything except the archive, header and object files. +${MIRACL_LIB_DIR}/miracl.a: ${SOURCES_MIRACL} + @find ${EXT}/Miracl/ -type f -exec cp '{}' ${EXT}/miracl_lib \; + @cd ${EXT}/miracl_lib/; bash ${MIRACL_MAKE}; find . -type f -not -name '*.a' -not -name '*.h' -not -name '*.o' -not -name '.git*'| xargs rm + + +.PHONY: clean cleanall examples ${EXAMPLE_SUBDIRS} all ${TEST} miracl runtest core + +# only clean example objects, test object and binaries +clean: + rm -f ${OBJECTS_EXAMPLE} ${OBJECTS_TEST} ${BIN}/*.exe + +# this will clean everything: example objects, test object and binaries and the Miracl library +cleanall: cleanmore + rm -f ${OBJECTS_MIRACL} ${MIRACL_LIB_DIR}/*.a diff --git a/Makefile~ b/Makefile~ new file mode 100644 index 0000000..3d3d2d3 --- /dev/null +++ b/Makefile~ @@ -0,0 +1,52 @@ + +BIN=bin +SRC=src +EXT=${SRC}/externals + +# compiler settings +CC=g++ +COMPILER_OPTIONS=-O2 +DEBUG_OPTIONS=-g3 -Wall +BATCH= + +ARCHITECTURE = $(shell uname -m) +ifeq (${ARCHITECTURE},x86_64) +MIRACL_MAKE:=linux64 +GNU_LIB_PATH:=x86_64 +else +MIRACL_MAKE:=linux +GNU_LIB_PATH:=i386 +endif + +INCLUDE=-I.. -I/usr/include/glib-2.0/ -I/usr/lib/${GNU_LIB_PATH}-linux-gnu/glib-2.0/include + + +LIBRARIES=-lgmp -lgmpxx -lpthread src/miracl_lib/miracl.a -L /usr/lib -lssl -lcrypto -lglib-2.0 +CFLAGS= + +# directory for the Miracl submodule and library +MIRACL_LIB_DIR=${EXT}/miracl_lib +SOURCES_MIRACL=${EXT}/Miracl/* +OBJECTS_MIRACL=${MIRACL_LIB_DIR}/*.o + +all: miracl + @echo "make all done." + +# this will create a copy of the files in ${SOURCES_MIRACL} and its sub-directories and put them into ${MIRACL_LIB_DIR} without sub-directories, then compile it +miracl: ${MIRACL_LIB_DIR}/miracl.a + +# copy Miracl files to a new directory (${CORE}/util/miracl_lib/), call the build script and delete everything except the archive, header and object files. +${MIRACL_LIB_DIR}/miracl.a: ${SOURCES_MIRACL} + @find ${EXT}/Miracl/ -type f -exec cp '{}' ${EXT}/miracl_lib \; + @cd ${EXT}/miracl_lib/; bash ${MIRACL_MAKE}; find . -type f -not -name '*.a' -not -name '*.h' -not -name '*.o' -not -name '.git*'| xargs rm + + +.PHONY: clean cleanall examples ${EXAMPLE_SUBDIRS} all ${TEST} miracl runtest core + +# only clean example objects, test object and binaries +clean: + rm -f ${OBJECTS_EXAMPLE} ${OBJECTS_TEST} ${BIN}/*.exe + +# this will clean everything: example objects, test object and binaries and the Miracl library +cleanall: cleanmore + rm -f ${OBJECTS_MIRACL} ${MIRACL_LIB_DIR}/*.a diff --git a/src/bench_psi.cpp b/src/bench_psi.cpp new file mode 100644 index 0000000..100e818 --- /dev/null +++ b/src/bench_psi.cpp @@ -0,0 +1,425 @@ +/* + * bench_psi.cpp + * + * Created on: Nov 10, 2014 + * Author: mzohner + */ + +#include "bench_psi.h" + + +int32_t main(int32_t argc, char** argv) { + psi_demonstrator(argc, argv); + //benchroutine(argc, argv); +} + + + +int32_t benchroutine(int32_t argc, char** argv) { + uint32_t nelements=0, elebytelen=4, symsecbits=128, + intersect_size, i, ntasks=1, runs=1, j, protocol, nclients=2, pnelements; + uint8_t *elements, *intersection; + string address = "127.0.0.1"; + uint16_t port=7766; + timeval begin, end; + vector sockfd(ntasks); + field_type ftype = ECC_FIELD; + role_type role = (role_type) 0; + uint64_t bytes_sent=0, bytes_received=0, mbfac; + double epsilon=1.2; + bool cardinality=false; + bool detailed_timings = false; + + mbfac=1024*1024; + + read_bench_options(&argc, &argv, &role, &nelements, &elebytelen, &symsecbits, + &address, &port, &ntasks, &protocol, &nclients, &epsilon, &cardinality, &ftype, + &detailed_timings); + + if(role == SERVER) { + if(protocol == TTP) { + ntasks = nclients; + sockfd.resize(ntasks); + } + listen(address.c_str(), port, sockfd.data(), ntasks); + } else { + for(i = 0; i < ntasks; i++) + connect(address.c_str(), port, sockfd[i]); + } + + crypto crypto(symsecbits, (uint8_t*) const_seed); + + //exchange number of items, bit-length of items, symmetric security parameter and protocol to make sure the parameters are correct + if(protocol != TTP) { + pnelements = exchange_information(nelements, elebytelen, symsecbits, ntasks, protocol, sockfd[0]); + } + + if(protocol != TTP || role != SERVER) { + elements = (uint8_t*) calloc(nelements * elebytelen, sizeof(uint8_t)); + crypto.gen_rnd(elements, elebytelen * nelements); + } + + +#ifdef PRINT_INPUT_ELEMENTS + for(i = 0; i < nelements; i++) { + cout << "Element " << i << ": " << (hex); + for(j = 0; j < elebytelen; j++) + cout << (uint32_t) elements[i*elebytelen + j]; + cout << (dec) << endl; + } +#endif + +#ifndef BATCH + cout << "Benchmarking protocol " << protocol << " on " << runs << " runs" << endl; +#endif + gettimeofday(&begin, NULL); + for(i = 0; i < runs; i++) { + switch(protocol) { + case NAIVE: + naivepsi(role, nelements, pnelements, elebytelen, elements, &intersection, &crypto, sockfd.data(), ntasks); + break; + case TTP: + ttppsi(role, nelements, elebytelen, elements, &intersection, &crypto, sockfd.data(), nclients, cardinality); break; + case DH_ECC: + intersect_size = dhpsi(role, nelements, pnelements, elebytelen, elements, &intersection, &crypto, sockfd.data(), + ntasks, cardinality, ftype); + break; + case OT_PSI: + intersect_size = otpsi(role, nelements, pnelements, elebytelen*8, elements, &intersection, &crypto, sockfd.data(), + ntasks, epsilon, detailed_timings); + break; + default:break; + } + } + gettimeofday(&end, NULL); + + for(i = 0; i < sockfd.size(); i++) { + bytes_sent += sockfd[i].get_bytes_sent(); + bytes_received += sockfd[i].get_bytes_received(); + } +#ifdef BATCH + cout << getMillies(begin, end) << "\t" << ((double) bytes_sent + bytes_received)/mbfac << endl; + + +#else + cout << "Required time:\t" << fixed << std::setprecision(1) << getMillies(t_start, t_end)/1000 << " s" << endl; + cout << "Data sent:\t" << ((double)bytes_sent)/mbfac << " MB" << endl; + cout << "Data received:\t" << ((double)bytes_received)/mbfac << " MB" << endl; +#endif + +#ifdef PRINT_INTERSECTION + + cout << "Found " << intersect_size << " intersecting elements" << endl; + if(!cardinality) { + for(i = 0; i < intersect_size; i++) { + for(j = 0; j < elebytelen; j++) { + cout << (hex) << (uint32_t) intersection[i * elebytelen + j] << (dec); + } + cout << endl; + } + } +#endif + if(protocol != TTP || role != SERVER) { + free(elements); + } + return 0; +} + + + +int32_t psi_demonstrator(int32_t argc, char** argv) { + uint32_t nelements=0, elebytelen=16, symsecbits=128, intersect_size, i, j, ntasks=1, protocol=3, + pnelements, *elebytelens, *res_bytelens; + bool detailed_timings=false; + uint8_t **elements, **intersection; + string address="127.0.0.1"; + uint16_t port=7766; + timeval t_start, t_end; + vector sockfd(ntasks); + string filename; + uint64_t bytes_sent=0, bytes_received=0, mbfac; + role_type role = (role_type) 0; + double epsilon=1.2; + + mbfac=1024*1024; + + read_psi_demo_options(&argc, &argv, &role, &filename, &address, &nelements, &detailed_timings); + + if(role == SERVER) { + listen(address.c_str(), port, sockfd.data(), ntasks); + } else { + for(i = 0; i < ntasks; i++) + connect(address.c_str(), port, sockfd[i]); + } + + gettimeofday(&t_start, NULL); + + //read in files and get elements and byte-length from there + + read_elements(&elements, &elebytelens, &nelements, filename); + if(detailed_timings) { + gettimeofday(&t_end, NULL); + } + + pnelements = exchange_information(nelements, elebytelen, symsecbits, ntasks, protocol, sockfd[0]); + //cout << "Performing private set-intersection between " << nelements << " and " << pnelements << " element sets" << endl; + + if(detailed_timings) { + cout << "Time for reading elements:\t" << fixed << std::setprecision(2) << getMillies(t_start, t_end)/1000 << " s" << endl; + } + + crypto crypto(symsecbits, (uint8_t*) const_seed); + +#ifndef BATCH + cout << "Benchmarking protocol " << protocol << " on " << runs << " runs" << endl; +#endif + intersect_size = otpsi(role, nelements, pnelements, elebytelens, elements, &intersection, &res_bytelens, + &crypto, sockfd.data(), ntasks, epsilon, detailed_timings); + gettimeofday(&t_end, NULL); + + +#ifdef PRINT_INTERSECTION + if(role == CLIENT) { + //cout << "Computation finished. Found " << intersect_size << " intersecting elements:" << endl; + if(!detailed_timings) { + for(i = 0; i < intersect_size; i++) { + //cout << "\t"; + for(j = 0; j < res_bytelens[i]; j++) { + cout << intersection[i][j]; + } + cout << endl; + } + } + } +#endif + + for(i = 0; i < sockfd.size(); i++) { + bytes_sent += sockfd[i].get_bytes_sent(); + bytes_received += sockfd[i].get_bytes_received(); + } + + //cout << "Required time:\t" << fixed << std::setprecision(1) << getMillies(t_start, t_end)/1000 << " s" << endl; + //cout << "Data sent:\t" << ((double)bytes_sent)/mbfac << " MB" << endl; + //cout << "Data received:\t" << ((double)bytes_received)/mbfac << " MB" << endl; + + for(i = 0; i < nelements; i++) + free(elements[i]); + free(elements); + free(elebytelens); + return 1; +} + + +void read_elements(uint8_t*** elements, uint32_t** elebytelens, uint32_t* nelements, string filename) { + uint32_t i, j; + ifstream infile(filename.c_str()); + if(!infile.good()) { + cerr << "Input file " << filename << " does not exist, program exiting!" << endl; + exit(0); + } + string line; + if(*nelements == 0) { + while (std::getline(infile, line)) { + ++*nelements; + } + } + *elements=(uint8_t**) malloc(sizeof(uint8_t*)*(*nelements)); + *elebytelens = (uint32_t*) malloc(sizeof(uint32_t) * (*nelements)); + + infile.clear(); + infile.seekg(ios::beg); + for(i = 0; i < *nelements; i++) { + assert(std::getline(infile, line)); + (*elebytelens)[i] = line.length(); + (*elements)[i] = (uint8_t*) malloc((*elebytelens)[i]); + memcpy((*elements)[i], (uint8_t*) line.c_str(), (*elebytelens)[i]); + +#ifdef PRINT_INPUT_ELEMENTS + cout << "Element " << i << ": "; + for(j = 0; j < (*elebytelens)[i]; j++) + cout << (*elements)[i][j]; + cout << endl; +#endif + } +} + + + +uint32_t exchange_information(uint32_t myneles, uint32_t mybytelen, uint32_t mysecparam, uint32_t mynthreads, + uint32_t myprotocol, CSocket& sock) { + uint32_t pneles, pbytelen, psecparam, pnthreads, pprotocol; + //Send own values + sock.Send(&myneles, sizeof(uint32_t)); + sock.Send(&mybytelen, sizeof(uint32_t)); + sock.Send(&mysecparam, sizeof(uint32_t)); + sock.Send(&mynthreads, sizeof(uint32_t)); + sock.Send(&myprotocol, sizeof(uint32_t)); + + //Receive partner values + sock.Receive(&pneles, sizeof(uint32_t)); + sock.Receive(&pbytelen, sizeof(uint32_t)); + sock.Receive(&psecparam, sizeof(uint32_t)); + sock.Receive(&pnthreads, sizeof(uint32_t)); + sock.Receive(&pprotocol, sizeof(uint32_t)); + + //Assert + assert(mybytelen == pbytelen); + assert(mysecparam == psecparam); + assert(mynthreads == pnthreads); + assert(myprotocol == pprotocol); + + return pneles; +} + + +int32_t read_bench_options(int32_t* argcp, char*** argvp, role_type* role, uint32_t* nelements, uint32_t* bytelen, + uint32_t* secparam, string* address, uint16_t* port, uint32_t* ntasks, uint32_t* protocol, uint32_t* nclients, + double* epsilon, bool* cardinality, field_type* ftype, bool* detailed_timings) { + + uint32_t int_role=0, int_port=0; + bool useffc=false; + + parsing_ctx options[] = {{(void*) &int_role, T_NUM, 'r', "Role: 0/1", true, false}, + {(void*) protocol, T_NUM, 'p', "PSI protocol (0: Naive, 1: TTP, 2: DH, 3: OT)", true, false}, + {(void*) nelements, T_NUM, 'n', "Num elements", true, false}, + {(void*) bytelen, T_NUM, 'b', "Byte length", true, false}, + {(void*) secparam, T_NUM, 's', "Symmetric Security Bits", false, false}, + {(void*) address, T_STR, 'a', "IP-address", false, false}, + {(void*) &int_port, T_NUM, 'o', "Port", false, false}, + {(void*) ntasks, T_NUM, 't', "Number of threads", false, false}, + {(void*) nclients, T_NUM, 'c', "Number of clients for TTP based protocol", false, false}, + {(void*) epsilon, T_DOUBLE, 'e', "Epsilon in Cuckoo hashing", false, false}, + {(void*) cardinality, T_FLAG, 'y', "Compute cardinality (only for DH and TTP PSI)", false, false}, + {(void*) &useffc, T_FLAG, 'f', "Use finite-field cryptography", false, false}, + {(void*) detailed_timings, T_FLAG, 'd', "Enable Detailed Timings", false, false} + }; + + if(!parse_options(argcp, argvp, options, sizeof(options)/sizeof(parsing_ctx))) { + print_usage("PSI-Implementations", options, sizeof(options)/sizeof(parsing_ctx)); + cout << "Exiting" << endl; + exit(0); + } + + assert(int_role < 2); + *role = (role_type) int_role; + + assert(*protocol < PROT_LAST); + if(int_port != 0) { + assert(int_port < 1<<(sizeof(uint16_t)*8)); + *port = (uint16_t) int_port; + } + + if(useffc) { + *ftype = P_FIELD; + } + //delete options; + + return 1; +} + + +int32_t read_psi_demo_options(int32_t* argcp, char*** argvp, role_type* role, string* filename, string* address, + uint32_t* nelements, bool* detailed_timings) { + + uint32_t int_role; + //parsing_ctx *options = new parsing_ctx[5];//(parsing_ctx*) calloc(noptions, sizeof(parsing_ctx)); + + parsing_ctx options[] = {{(void*) &int_role, T_NUM, 'r', "Role: 0/1", true, false}, + {(void*) filename, T_STR, 'f', "Input file", true, false}, + {(void*) address, T_STR, 'a', "IP-address", false, false}, + {(void*) nelements, T_NUM, 'n', "Num elements", false, false}, + {(void*) detailed_timings, T_FLAG, 't', "Flag: Detailed timings", false, false} + }; + + if(!parse_options(argcp, argvp, options, sizeof(options)/sizeof(parsing_ctx))) { + print_usage("PSI_demo", options, sizeof(options)/sizeof(parsing_ctx)); + cout << "Exiting" << endl; + exit(0); + } + + assert(int_role < 2); + *role = (role_type) int_role; + + //delete options; + + return 1; +} + + + +int32_t parse_options(int32_t* argcp, char*** argvp, parsing_ctx* options, uint32_t nops) { + uint32_t result = 0; + bool skip; + uint32_t i; + if(*argcp < 2) + return -1; + + while((*argcp) > 1) + { + if ((*argvp)[1][0] != '-' || (*argvp)[1][1] == '\0' || (*argvp)[1][2] != '\0') + return result; + for(i = 0, skip=false; i < nops && !skip; i++) { + if( ((*argvp)[1][1]) == options[i].opt_name) { + switch(options[i].type) { + case T_NUM: + if (isdigit((*argvp)[2][0])) { + ++*argvp; + --*argcp; + *((uint32_t*) options[i].val) = atoi((*argvp)[1]); + } + break; + case T_DOUBLE: + ++*argvp; + --*argcp; + *((double*) options[i].val) = atof((*argvp)[1]); + break; + case T_STR: + ++*argvp; + --*argcp; + *((string*) options[i].val) = (*argvp)[1]; + break; + case T_FLAG: + *((bool*)options[i].val) = true; + break; + } + ++result; + ++*argvp; + --*argcp; + options[i].set=true; + skip = true; + } + } + } + + for(i = 0; i < nops; i++) { + if(options[i].required && !options[i].set) + return 0; + } + return 1; +} + + +void print_usage(string progname, parsing_ctx* options, uint32_t nops) { + uint32_t i; + cout << "Usage: ./"< +#include +#include + + +using namespace std; + +//#define PRINT_INPUT_ELEMENTS +//#define PRINT_INTERSECTION + +enum PSI_PROT {NAIVE=0, TTP=1, DH_ECC=2, OT_PSI=3, PROT_LAST=4}; +enum etype {T_NUM, T_STR, T_FLAG, T_DOUBLE}; + +typedef struct { + void* val; + etype type; + char opt_name; + string help_str; + bool required; + bool set; +} parsing_ctx; + + +int32_t benchroutine(int32_t argc, char** argv); +int32_t psi_demonstrator(int32_t argc, char** argv); + +void read_elements(uint8_t*** elements, uint32_t** elebytelens, uint32_t* nelements, string filename); + + +uint32_t exchange_information(uint32_t myneles, uint32_t mybytelen, uint32_t mysecparam, uint32_t mynthreads, + uint32_t myprotocol, CSocket& sock); + +void print_bench_usage(); +void print_demo_usage(); + +void print_usage(string progname, parsing_ctx* options, uint32_t nops); + +int32_t read_psi_demo_options(int32_t* argcp, char*** argvp, role_type* role, string* filename, string* address, + uint32_t* nelements, bool* detailed_timings); +int32_t read_bench_options(int32_t* argcp, char*** argvp, role_type* role, uint32_t* nelements, uint32_t* bytelen, + uint32_t* secparam, string* address, uint16_t* port, uint32_t* ntasks, uint32_t* protocol, uint32_t* nclients, + double* epsilon, bool* cardinality, field_type* ftype, bool* detailed_timings); + + +int32_t parse_options(int32_t* argcp, char*** argvp, parsing_ctx* options, uint32_t nops); + +#endif /* BENCH_PSI_H_ */ diff --git a/src/externals/Miracl b/src/externals/Miracl new file mode 160000 index 0000000..cff161b --- /dev/null +++ b/src/externals/Miracl @@ -0,0 +1 @@ +Subproject commit cff161bad6364548b361b63938a988db23f60c2a diff --git a/src/hashing/cuckoo.cpp b/src/hashing/cuckoo.cpp new file mode 100644 index 0000000..882628f --- /dev/null +++ b/src/hashing/cuckoo.cpp @@ -0,0 +1,196 @@ +/* + * cuckoo.cpp + * + * Created on: Oct 7, 2014 + * Author: mzohner + */ + +#include "cuckoo.h" + +//returns a cuckoo hash table with the first dimension being the bins and the second dimension being the pointer to the elements +uint8_t* cuckoo_hashing(uint8_t* elements, uint32_t neles, uint32_t nbins, uint32_t bitlen, uint32_t *outbitlen, uint32_t* nelesinbin, + uint32_t* perm, uint32_t ntasks, prf_state_ctx* prf_state) +{ + //The resulting hash table + uint8_t* hash_table; + cuckoo_entry_ctx** cuckoo_table; + cuckoo_entry_ctx* cuckoo_entries; + uint32_t i, j; + uint32_t *perm_ptr; + pthread_t* entry_gen_tasks; + cuckoo_entry_gen_ctx* ctx; + hs_t hs; + +#ifdef COUNT_FAILS + uint32_t fails = 0; +#endif + + + init_hashing_state(&hs, neles, bitlen, nbins, prf_state); + *outbitlen = hs.outbitlen; + + cuckoo_table = (cuckoo_entry_ctx**) calloc(nbins, sizeof(cuckoo_entry_ctx*)); + cuckoo_entries = (cuckoo_entry_ctx*) malloc(neles * sizeof(cuckoo_entry_ctx)); + entry_gen_tasks = (pthread_t*) malloc(sizeof(pthread_t) * ntasks); + ctx = (cuckoo_entry_gen_ctx*) malloc(sizeof(cuckoo_entry_gen_ctx) * ntasks); + + //use the number of bins as address bits + //addr_bits = ceil_log2(nbins); + //cout << "Using addr_bits: " << hs.addrbitlen << endl; + //cout << "number of bins: " << nbins << ", address bits = " << addr_bits << endl; + //the remaining bits form the size of the values + + //inbytelen = ceil_divide(bitlen, 8); + //outbytelen = ceil_divide(outbitlen, 8); + //dummy_ele = (uint8_t*) malloc(ceil_divide(bitlen, 8)); + + for(i = 0; i < ntasks; i++) { + ctx[i].elements = elements; + ctx[i].cuckoo_entries = cuckoo_entries; + //ctx[i].inbitlen = bitlen; + // ctx[i].addr_bitlen = addr_bits; + //ctx[i].outbitlen = outbitlen; + //ctx[i].nbins = nbins; + ctx[i].hs = &hs; + ctx[i].startpos = i * ceil_divide(neles, ntasks); + ctx[i].endpos = min(ctx[i].startpos + ceil_divide(neles, ntasks), neles); + //cout << "Thread " << i << " starting from " << ctx[i].startpos << " going to " << ctx[i].endpos << " for " << neles << " elements" << endl; + if(pthread_create(entry_gen_tasks+i, NULL, gen_cuckoo_entries, (void*) (ctx+i))) { + cerr << "Error in creating new pthread at cuckoo hashing!" << endl; + exit(0); + } + } + + //generate the cuckoo entries for all elements + //for(i = 0; i < neles; i++) { + // gen_cuckoo_entry(elements + inbytelen * i, bitlen, addr_bits, cuckoo_entries + i, outbitlen, nbins, i); + //} + + for(i = 0; i < ntasks; i++) { + if(pthread_join(entry_gen_tasks[i], NULL)) { + cerr << "Error in joining pthread at cuckoo hashing!" << endl; + exit(0); + } + } + + //for(i = 0; i < nbins; i++) { + // cout << "Address " << i << " mapped to " << hs.address_used[i] << " times" << endl; + //} + //insert all elements into the cuckoo hash table + for(i = 0; i < neles; i++) { + if(!(insert_element(cuckoo_table, cuckoo_entries + i))) { +#ifdef COUNT_FAILS + fails++; + /*cout << "insertion failed for element " << (hex) << (*(((uint32_t*) elements)+i)) << ", inserting to address: "; + for(uint32_t j = 0; j < NUM_HASH_FUNCTIONS; j++) { + cout << (cuckoo_entries + i)->address[j] << ", "; + } + cout << (dec) << endl;*/ +#else + //cerr << "Insertion not successful for element " < 0) + cout << "insertion performed with " << fails << " fails" << endl; +#endif + + //Copy the final state of the cuckoo table into the hash table + perm_ptr = perm; + hash_table = (uint8_t*) calloc(nbins, hs.outbytelen); + + for(i = 0; i < nbins; i++) { + if(cuckoo_table[i] != NULL) { + memcpy(hash_table + i * hs.outbytelen, cuckoo_table[i]->val, hs.outbytelen); + //cout << "copying value: " << (hex) << (unsigned int) cuckoo_table[i]->val[cuckoo_table[i]->pos][0] << (dec) << endl; + *perm_ptr = cuckoo_table[i]->eleid; + perm_ptr++; + nelesinbin[i] = 1; + } else { + memset(hash_table + i * hs.outbytelen, DUMMY_ENTRY_CLIENT, hs.outbytelen); + nelesinbin[i] = 0; + } + } + + //Cleanup +#ifndef TEST_UTILIZATION + for(i = 0; i < neles; i++) { + //for(j = 0; j < NUM_HASH_FUNCTIONS; j++) { + free(cuckoo_entries[i].val); + //} + } +#endif + free(cuckoo_entries); + free(cuckoo_table); + free(entry_gen_tasks); + free(ctx); + + free_hashing_state(&hs); + + return hash_table; +} + + +void *gen_cuckoo_entries(void *ctx_void) { + cuckoo_entry_gen_ctx* ctx = (cuckoo_entry_gen_ctx*) ctx_void; + hs_t* hs = ctx->hs; + uint32_t i, inbytelen = ceil_divide(hs->inbitlen, 8); + uint8_t* eleptr = ctx->elements + inbytelen * ctx->startpos; + + + //generate the cuckoo entries for all elements + for(i = ctx->startpos; i < ctx->endpos; i++, eleptr+=inbytelen) { + gen_cuckoo_entry(eleptr, ctx->cuckoo_entries + i, hs, i); + } +} + + +inline void gen_cuckoo_entry(uint8_t* in, cuckoo_entry_ctx* out, hs_t* hs, uint32_t ele_id) { + uint32_t i; + + out->pos = 0; + out->eleid = ele_id; + + //for(i = 0; i < NUM_HASH_FUNCTIONS; i++) { +#ifndef TEST_UTILIZATION + out->val = (uint8_t*) malloc(hs->outbytelen); +#endif + hashElement(in, out->address, out->val, hs); + //} +} + +inline bool insert_element(cuckoo_entry_ctx** ctable, cuckoo_entry_ctx* element) { + cuckoo_entry_ctx *evicted, *tmp_evicted; + uint32_t i, ev_pos, iter_cnt; +#ifdef DEBUG_CUCKOO + cout << "iter_cnt = " << iter_cnt << " for element " << (hex) << (*((uint32_t*) element->element)) << (dec) << ", inserting to address: " + << element->address[element->pos] << " or " << element->address[element->pos^1] << endl; +#endif + + for(iter_cnt = 0, evicted = element; iter_cnt < MAX_ITERATIONS; iter_cnt++) { + //TODO: assert(addr < MAX_TAB_ENTRIES) + for(i = 0; i < NUM_HASH_FUNCTIONS; i++) {//, ele_pos=(ele_pos+1)%NUM_HASH_FUNCTIONS) { + if(ctable[evicted->address[i]] == NULL) { + ctable[evicted->address[i]] = evicted; + evicted->pos = i; + return true; + } + } + + //choose random bin to evict other element + ev_pos = evicted->address[(evicted->pos^iter_cnt) % NUM_HASH_FUNCTIONS]; + + tmp_evicted = ctable[ev_pos]; + ctable[ev_pos] = evicted; + evicted = tmp_evicted; + + //change position - if the number of HF's is increased beyond 2 this should be replaced by a different strategy + evicted->pos = (evicted->pos+1) % NUM_HASH_FUNCTIONS; + } + + //the highest number of iterations has been reached + return false; +} diff --git a/src/hashing/cuckoo.h b/src/hashing/cuckoo.h new file mode 100644 index 0000000..cc0ddc3 --- /dev/null +++ b/src/hashing/cuckoo.h @@ -0,0 +1,60 @@ +/* + * cuckoo.h + * + * Created on: Oct 7, 2014 + * Author: mzohner + */ + +#ifndef CUCKOO_H_ +#define CUCKOO_H_ + +#include "hashing_util.h" + +#define MAX_ITERATIONS 1000 +//#define DEBUG_CUCKOO +//#define COUNT_FAILS + + +struct cuckoo_entry_ctx { + //id of the element in the source set + uint32_t eleid; + //addresses the bin of the cuckoo entry in the cuckoo table, will only work for up to 2^{32} bins + uint32_t address[NUM_HASH_FUNCTIONS]; + //the value of the entry + uint8_t* val; + //which position is the entry currently mapped to + uint32_t pos; +#ifdef DEBUG_CUCKOO + uint8_t* element; +#endif +}; + + +struct cuckoo_entry_gen_ctx { + //starting position in the generation process + uint32_t startpos; + //end position of entries that are generated by this thread + uint32_t endpos; + //input elements + uint8_t* elements; + //pointer to the cuckoo entries + cuckoo_entry_ctx* cuckoo_entries; + hs_t* hs; + //uint32_t inbitlen; + //uint32_t addr_bitlen; + //uint32_t outbitlen; + //uint32_t nbins; +}; + + +//returns a cuckoo hash table with the first dimension being the bins and the second dimension being the pointer to the elements +uint8_t* cuckoo_hashing(uint8_t* elements, uint32_t neles, uint32_t nbins, uint32_t bitlen, uint32_t* outbitlen, uint32_t* nelesinbin, + uint32_t* perm, uint32_t ntasks, prf_state_ctx* prf_state); +//routine for generating the entries, is invoked by the threads +void *gen_cuckoo_entries(void *ctx); +inline void gen_cuckoo_entry(uint8_t* in, cuckoo_entry_ctx* out, hs_t* hs, uint32_t ele_id); +inline bool insert_element(cuckoo_entry_ctx** ctable, cuckoo_entry_ctx* element); + + + +#endif /* CUCKOO_H_ */ diff --git a/src/hashing/hashing_util.h b/src/hashing/hashing_util.h new file mode 100644 index 0000000..34d0dbe --- /dev/null +++ b/src/hashing/hashing_util.h @@ -0,0 +1,216 @@ +/* + * hashing_util.h + * + * Created on: Oct 8, 2014 + * Author: mzohner + */ + +#ifndef HASHING_UTIL_H_ +#define HASHING_UTIL_H_ + +#include "../util/typedefs.h" +#include "../util/crypto/crypto.h" + +typedef uint16_t TABLEID_T; + +#define NUM_HASH_FUNCTIONS 2 +#define MAX_TABLE_SIZE_BYTES sizeof(TABLEID_T) +#define DUMMY_ENTRY_SERVER 0x00 +#define DUMMY_ENTRY_CLIENT 0xFF + +#define USE_LUBY_RACKOFF +//#define TEST_UTILIZATION + +typedef struct hashing_state_ctx { + uint32_t** hf_values[NUM_HASH_FUNCTIONS]; + uint32_t nhfvals; + uint32_t nelements; + uint32_t nbins; + uint32_t inbitlen; + uint32_t addrbitlen; + uint32_t outbitlen; + //the byte values, are stored separately since they are needed very often + uint32_t inbytelen; + uint32_t addrbytelen; + uint32_t outbytelen; + uint32_t* address_used; +} hs_t; + +//TODO: generate these randomly for each execution and communicate them between the parties +static const uint32_t HF_MASKS[3] = {0x00000000, 0x33333333, 0x14894568}; + +//use as mask to address the bits in a uint32_t vector +static const uint32_t SELECT_BITS[33] = {0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, \ + 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, \ + 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, \ + 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, \ + 0xFFFFFFFF }; + +//can also be computed as SELECT_BITS ^ 0xFFFFFFFF +static const uint32_t SELECT_BITS_INV[33] ={0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8, 0xFFFFFFF0, 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80, \ + 0xFFFFFF00, 0xFFFFFFE00, 0xFFFFFC00, 0xFFFFF800, 0xFFFFF000, 0xFFFFE000, 0xFFFFC000, 0xFFFF8000, \ + 0xFFFF0000, 0xFFFFE0000, 0xFFFC0000, 0xFFF80000, 0xFFF00000, 0xFFE00000, 0xFFC00000, 0xFF800000, \ + 0xFF000000, 0xFFE000000, 0xFC000000, 0xF8000000, 0xF0000000, 0xE0000000, 0xC0000000, 0x80000000, \ + 0x00000000 }; + + +//Init the values for the hash function +static void init_hashing_state(hs_t* hs, uint32_t nelements, uint32_t inbitlen, uint32_t nbins, + prf_state_ctx* prf_state) { + uint32_t i, j, nrndbytes; + hs->nelements = nelements; + hs->nbins = nbins; + + hs->inbitlen = inbitlen; + hs->addrbitlen = min((uint32_t) ceil_log2(nbins), inbitlen); + +#ifdef USE_LUBY_RACKOFF + hs->outbitlen = hs->inbitlen - hs->addrbitlen+1; +#else + hs->outbitlen = inbitlen; +#endif + //TODO prevent too much memory utilization + //assert(hs->outbitlen < 32); + //TODO: quickfix to enable hashing for large values + //hs->outbitlen = min((double) hs->outbitlen, (double) 24); + + hs->inbytelen = ceil_divide(hs->inbitlen, 8); + hs->addrbytelen = ceil_divide(hs->addrbitlen, 8); + hs->outbytelen = ceil_divide(hs->outbitlen, 8); + + hs->nhfvals = ceil_divide(hs->outbytelen, MAX_TABLE_SIZE_BYTES); + + + nrndbytes = (1<<(8*MAX_TABLE_SIZE_BYTES)) * sizeof(uint32_t); + + //cout << " random bytes: " << nrndbytes << endl; + //cout << "inbitlen = " << hs->inbitlen << ", outbitlen = " << hs->outbitlen << ", addrbitlen = " << hs->addrbitlen << + // ", nhfvals = " << hs->nhfvals << ", nrndbytes = " << nrndbytes << endl; + + for(i = 0; i < NUM_HASH_FUNCTIONS; i++) { + hs->hf_values[i] = (uint32_t**) malloc(sizeof(uint32_t*) * hs->nhfvals); + + for(j = 0; j < hs->nhfvals; j++) { + hs->hf_values[i][j] = (uint32_t*) malloc(nrndbytes); + assert(hs->hf_values[i][j]); + gen_rnd_bytes(prf_state, (uint8_t*) hs->hf_values[i][j], nrndbytes); + } + } + //cout << "nhfvals = " << hs->nhfvals << endl; + hs->address_used = (uint32_t*) calloc(nbins, sizeof(uint32_t)); +} + +static void free_hashing_state(hs_t* hs) { + uint32_t i; + for(i = 0; i < NUM_HASH_FUNCTIONS; i++) + free(hs->hf_values[i]); +} + +//reduce the bit-length of the elements if some bits are used to determine the bin and a permutation is used for hashing +//static uint32_t getOutBitLen(uint32_t inbitlen, uint32_t nelements) { +// return inbitlen - ceil_log2(nelements); +//}; + +//TODO: a generic place holder, can be replaced by any other hash function +//inline void hashElement(uint8_t* element, uint32_t* address, uint8_t* val, uint32_t hfid, hs_t* hs) { +inline void hashElement(uint8_t* element, uint32_t* address, uint8_t* val, hs_t* hs) { + +#ifdef USE_LUBY_RACKOFF + //TODO: the table-lookup hashing is only used for elements up to 32-bit length, since it gets very inefficient for larger values + uint64_t i, j, L, R; + TABLEID_T hfmaskaddr; + //Store the first hs->addrbitlen bits in L + L = *((uint32_t*) element) & SELECT_BITS[hs->addrbitlen]; + //Store the remaining hs->outbitlen bits in R and pad correspondingly + R = (*((uint32_t*) element) & SELECT_BITS_INV[hs->addrbitlen]) >> (hs->addrbitlen); + + + //assert(R < (1<outbitlen)); + //cout << "R = " << R << endl; + /*if(hfid == 0) { + *address = L % hs->nbins; + *((uint32_t*) val) = R; + } else if(hfid == 1) { + *address = R % hs->nbins; + *((uint32_t*) val) = L; + } else { + *address = (L ^ R) % hs->nbins; + *((uint32_t*) val) = R; + }*/ + hfmaskaddr = R * sizeof(uint32_t); + //cout << "L = " << L << ", R = " << R << " addresses: "; + + for(i = 0; i < NUM_HASH_FUNCTIONS; i++) { + //cout << "i = " << i << ", addrbytelen = " << hs->addrbytelen << ", R = " << R << ", nbins = " << + // hs->nbins << ", L = " << L << ", addr= " << endl; + //address[i] = (L ^ *(((uint32_t*) &(hs->hf_values[i][R*hs->addrbytelen])))) % hs->nbins; + for(j = 0; j < hs->nhfvals; j++) { + //assert(hfmaskaddr < (1<<(8*MAX_TABLE_SIZE_BYTES)) * hs->addrbytelen); + //cout << "i = " << i << ", j = " << j << ", Hfmaskaddr = " << hfmaskaddr << endl; + //cout << "Hfvalue: " << hs->hf_values[i][j][hfmaskaddr] << endl; + address[i] = (L ^ *((hs->hf_values[i][j]+hfmaskaddr))) % hs->nbins; + } + //cout << address[i] << ", "; + //hs->address_used[address[i]]++; + } + //cout << endl; + *((uint32_t*) val) = R; + //TODO copy remaining bits + + if(hs->outbytelen >= sizeof(uint32_t)) + memcpy(val + (sizeof(uint32_t) - hs->addrbytelen), element + sizeof(uint32_t), hs->outbytelen - sizeof(uint32_t)); + + //cout << "Address for hfid = " << hfid << ": " << *address << ", L = " << L << ", R = " << R << endl; + +#else + for(uint64_t i = 0; i < NUM_HASH_FUNCTIONS; i++) { + address[i] = ((*((uint32_t*) element+i) ^ HF_MASKS[i]) & SELECT_BITS[hs->addrbitlen]) % hs->nbins; + + #ifndef TEST_UTILIZATION + *((uint32_t*) val) = (*((uint32_t*) element) & SELECT_BITS_INV[hs->addrbitlen]) >> (hs->addrbitlen); + + //copy the remaining full bytes + if(hs->outbytelen >= sizeof(uint32_t)) + memcpy(val + (sizeof(uint32_t) - hs->addrbytelen), element + sizeof(uint32_t), hs->outbytelen - sizeof(uint32_t)); + #endif + } +#endif +} + +inline void domain_hashing(uint32_t nelements, uint8_t* elements, uint32_t elebytelen, uint8_t* result, + uint32_t resultbytelen, crypto* crypt) { + uint8_t *eleptr, *resultptr, *hash_buf; + uint32_t i; + + eleptr=elements; + resultptr = result; +#ifndef BATCH + cout << "Hashing " << nelements << " elements from " << elebytelen << " bytes into " << resultbytelen << " bytes" << endl; +#endif + hash_buf = (uint8_t*) calloc(crypt->get_hash_bytes(), sizeof(uint8_t)); + for(i = 0; i < nelements; i++, resultptr+=resultbytelen, eleptr+=elebytelen) { + memcpy(hash_buf, eleptr, elebytelen); + crypt->hash(resultptr, resultbytelen, hash_buf, elebytelen); + } + free(hash_buf); +} + +inline void domain_hashing(uint32_t nelements, uint8_t** elements, uint32_t* elebytelens, uint8_t* result, + uint32_t resultbytelen, crypto* crypt) { + uint8_t *resultptr;//, *hash_buf; + uint32_t i; + + //eleptr=elements; + resultptr = result; +#ifndef BATCH + cout << "Hashing " << nelements << " elements from " << elebytelen << " bytes into " << resultbytelen << " bytes" << endl; +#endif + //hash_buf = (uint8_t*) calloc(crypt->get_hash_bytes(), sizeof(uint8_t)); + for(i = 0; i < nelements; i++, resultptr+=resultbytelen) { + //memcpy(hash_buf, elements[i], elebytelens[i]); + crypt->hash(resultptr, resultbytelen, elements[i], elebytelens[i]); + } + //free(hash_buf); +} + +#endif /* HASHING_UTIL_H_ */ diff --git a/src/hashing/kcuckoo3a.C b/src/hashing/kcuckoo3a.C new file mode 100644 index 0000000..a2aaac3 --- /dev/null +++ b/src/hashing/kcuckoo3a.C @@ -0,0 +1,154 @@ +// K-ary cuckoo hashing (c) 2002-2003 by Peter Sanders +// this is a simple implementation for the purposes +// of measuring the performance of cuckoo hashing in abstract +// terms (number of probes per insert, storage efficiency) +// usage: compile using g++ or another ISO C++ compiler +// a.out +// there is also a constant tMax that defines the maximum number +// of trials for an insertion +// allocates space for n elements in K subtables, and repeats +// the follwing measurements repeat time: +// - find a hash function by filling full lookup tables +// with pseudo-random numbers. +// - insert elements i=0..n-1 into the table until this fails +// for the first time. (The cost of these insertions is not +// measured.) +// Every n/r successfully inserted elements, the follwing +// measurement is repeated n/r times: +// * a random element i2 is removed +// * the hash table entries for i2 are filled with new random values +// * i2 is reinserted. +// Note that this is equivalent to removing a random element +// inserting a new element that +// has never been in the table before. +// The output is a table that gives +// - x the number of elements in the table at each measuremnt interval +// - the average number of probes for an insertion during the measruements +// for the given number of inserted elements +// - K +// - n +// - repeat +// - seed +#define DEBUGLEVEL 1 +#include +#include +#include "util.h" +#include "mt-real.c" +#include + +#define split 1 + + +using namespace std; +const int tMax = 10000; // max number of random walk trials +int K; // number of probes +int n; // number of elements +int m; // number of elements per table +int **hash; // K times n array +int **table; // K times m array + + +// generate random int in 0..x-1 +inline int rand0K(int x) { return int(genrand()*x); } + + +// insert element i into table +// return value: +// -1 failure +// otherwise number of hash function evaluation +int insert(int i) { + int forbidden = -1; + int j = rand0K(K); + for (int t = 1; t <= tMax; t++) { + int p = hash [j][i]; + int newI = table[j][p]; + table[j][p] = i; // play the cuckoo + if (newI == -1) return t; // done + forbidden = j; + i = newI; // find new home for cuckoo victim + j = rand0K(K-1); + if (j == forbidden) j = K-1; + } + return tMax + 1; // insertion failed +} + +// remove element i from the table +void remove(int i) { + for (int j = 0; j < K; j++) { + int p = hash[j][i]; + if (table[j][p] == i) { + table[j][p] = -1; + return; + } + } +} + +/*int main(int argc, char **argv) { + int i, j; + assert(argc == 6); + K = atoi(argv[1]); // number of probes + n = atoi(argv[2]); // number of elements + // double eps = atof(argv[3]); // space slack + m = int(n/K + 0.5); + int r = atoi(argv[3]); // number of measured densities + int step = n/r; + int repeat = atoi(argv[4]); // how often to start from scratch + int seed = atoi(argv[5]); + sgenrand(seed); + cout << "# x tAvg(x) K N repeat seed" << endl; + + // allocate hash function and table + // and an empty table + hash = (int**) malloc(sizeof(int*) * K); + table = (int**) malloc(sizeof(int*) * K); + for (j = 0; j < K; j++) { + hash [j] = new int[n]; + table[j] = new int[m]; + } + + // initialize statistics + // sumT[i] is the average time for size i*step + double *sumT = new double[r+1]; + int *cf = new int[r+1]; + for (int i = 0; i < r; i++) { + sumT[i] = 0; + cf[i] = 0; + } + + // main loop + for (int rep = 0; rep < repeat; rep++) { + // init hash function and empty table + for (j = 0; j < K; j++) { + for (i = 0; i < n; i++) { hash [j][i] = rand0K(m); } + for (i = 0; i < m; i++) { table[j][i] = -1; } + } + + // fill table and keep measuring from time to time + for (i = 0; i < n; i++) { + if (insert(i) > tMax) break; // table is full + if (((i+1) % step) == 0) { // measure in detail here + for (int i1 = 0; i1 < step; i1++) { + // remove and reinsert a random element + int i2 = rand0K(i); + remove(i2); + for (j = 0; j < K; j++) hash[j][i2] = rand0K(m); + int t = insert(i2); + cf[i/step] += (t > tMax); + //cout << t << endl; + sumT[i/step] += t; + } + } + } + } + + for (int rep = 0; rep < r; rep++) { + cout << rep*step + step << " " + << sumT[rep]/step/repeat << " " + << K << " " + << n << " " + << repeat << " " + << seed << " " + << cf[rep] << endl; + } +} +*/ diff --git a/src/hashing/mt-real.c b/src/hashing/mt-real.c new file mode 100644 index 0000000..41bf1b1 --- /dev/null +++ b/src/hashing/mt-real.c @@ -0,0 +1,90 @@ +/* A C-program for MT19937B: Real number version */ +/* genrand() generate one pseudorandom number with double precision */ +/* which is uniformly distributed on [0,1]-interval for each call. */ +/* sgenrand(seed) set initial values to the working area of 624 words.*/ +/* sgenrand(seed) must be called once before calling genrand() */ +/* (seed is any integer except 0). */ + +/* + LICENCE CONDITIONS: + + Matsumoto and Nishimura consent to GNU General + Public Licence + + NOTE: + When you use it in your program, please let Matsumoto + know it. + + Because of a machine-trouble, Matsumoto lost emails + which arrived during May 28-29. +*/ + + +#include + +/* Period parameters */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0df /* constant vector a */ +#define UPPER_MASK 0x80000000 /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffff /* least significant r bits */ + +/* for tempering */ +#define TEMPERING_MASK_B 0x9d2c5680 +#define TEMPERING_MASK_C 0xefc60000 +#define TEMPERING_SHIFT_U(y) (y >> 11) +#define TEMPERING_SHIFT_S(y) (y << 7) +#define TEMPERING_SHIFT_T(y) (y << 15) +#define TEMPERING_SHIFT_L(y) (y >> 18) + +static unsigned long ptgfsr[N]; /* set initial seeds: N = 624 words */ + +void +sgenrand(unsigned long seed) /* seed should not be 0 */ +{ + int k; + + /* setting initial seeds to ptgfsr[N] using */ + /* the generator Line 25 of Table 1 in */ + /* [KNUTH 1981, The Art of Computer Programming */ + /* Vol. 2 (2nd Ed.), pp102] */ + + ptgfsr[0]= seed & 0xffffffff; + for (k=1; k> 1) ^ mag01[y & 0x1]; + } + for (;kk> 1) ^ mag01[y & 0x1]; + } + y = (ptgfsr[N-1]&UPPER_MASK)|(ptgfsr[0]&LOWER_MASK); + ptgfsr[N-1] = ptgfsr[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; + + k = 0; + } + + y = ptgfsr[k++]; + y ^= TEMPERING_SHIFT_U(y); + y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; + y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; + y &= 0xffffffff; /* you may delete this line if word size = 32 */ + y ^= TEMPERING_SHIFT_L(y); + + return ( (double)y / (unsigned long)0xffffffff ); +} + diff --git a/src/hashing/simple_hashing.cpp b/src/hashing/simple_hashing.cpp new file mode 100644 index 0000000..3d11f7c --- /dev/null +++ b/src/hashing/simple_hashing.cpp @@ -0,0 +1,178 @@ +/* + * simple_hashing.cpp + * + * Created on: Oct 8, 2014 + * Author: mzohner + */ + +#include "simple_hashing.h" + +uint8_t* simple_hashing(uint8_t* elements, uint32_t neles, uint32_t bitlen, uint32_t *outbitlen, uint32_t* nelesinbin, uint32_t nbins, + uint32_t ntasks, prf_state_ctx* prf_state) { + sht_ctx* table; + //uint8_t** bin_content; + uint8_t *eleptr, *bin_ptr, *result, *res_bins; + uint32_t i, j, tmpneles; + sheg_ctx* ctx; + pthread_t* entry_gen_tasks; + hs_t hs; + + init_hashing_state(&hs, neles, bitlen, nbins, prf_state); + //Set the output bit-length of the hashed elements + *outbitlen = hs.outbitlen; + + entry_gen_tasks = (pthread_t*) malloc(sizeof(pthread_t) * ntasks); + ctx = (sheg_ctx*) malloc(sizeof(sheg_ctx) * ntasks); + table = (sht_ctx*) malloc(sizeof(sht_ctx) * ntasks); + + + + for(i = 0; i < ntasks; i++) { + init_hash_table(table + i, ceil_divide(neles, ntasks), &hs); + } + + //for(i = 0; i < nbins; i++) + // pthread_mutex_init(locks+i, NULL); + + //tmpbuf = (uint8_t*) malloc(table->outbytelen); + + for(i = 0; i < ntasks; i++) { + ctx[i].elements = elements; + ctx[i].table = table + i; + ctx[i].startpos = i * ceil_divide(neles, ntasks); + ctx[i].endpos = min(ctx[i].startpos + ceil_divide(neles, ntasks), neles); + ctx[i].hs = &hs; + + //cout << "Thread " << i << " starting from " << ctx[i].startpos << " going to " << ctx[i].endpos << " for " << neles << " elements" << endl; + if(pthread_create(entry_gen_tasks+i, NULL, gen_entries, (void*) (ctx+i))) { + cerr << "Error in creating new pthread at simple hashing!" << endl; + exit(0); + } + } + + for(i = 0; i < ntasks; i++) { + if(pthread_join(entry_gen_tasks[i], NULL)) { + cerr << "Error in joining pthread at simple hashing!" << endl; + exit(0); + } + } + + //for(i = 0, eleptr=elements; i < neles; i++, eleptr+=inbytelen) { + // insert_element(table, eleptr, tmpbuf); + //} + + //malloc and copy simple hash table into hash table + //bin_content = (uint8_t**) malloc(sizeof(uint8_t*) * nbins); + //*nelesinbin = (uint32_t*) malloc(sizeof(uint32_t) * nbins); + + res_bins = (uint8_t*) malloc(neles * NUM_HASH_FUNCTIONS * hs.outbytelen); + bin_ptr = res_bins; + + + for(i = 0; i < hs.nbins; i++) { + nelesinbin[i] = 0; + for(j = 0; j < ntasks; j++) { + tmpneles = (table +j)->bins[i].nvals; + nelesinbin[i] += tmpneles; + //bin_content[i] = (uint8_t*) malloc(nelesinbin[i] * table->outbytelen); + memcpy(bin_ptr, (table + j)->bins[i].values, tmpneles * hs.outbytelen); + bin_ptr += (tmpneles * hs.outbytelen); + } + //right now only the number of elements in each bin is copied instead of the max bin size + } + + for(j = 0; j < ntasks; j++) + free_hash_table(table + j); + free(table); + free(entry_gen_tasks); + free(ctx); + + //for(i = 0; i < nbins; i++) + // pthread_mutex_destroy(locks+i); + //free(locks); + + free_hashing_state(&hs); + + return res_bins; +} + +void *gen_entries(void *ctx_tmp) { + //Insert elements in parallel, use lock to communicate + uint8_t *tmpbuf, *eleptr; + sheg_ctx* ctx = (sheg_ctx*) ctx_tmp; + uint32_t i, inbytelen, *address; + + address = (uint32_t*) malloc(NUM_HASH_FUNCTIONS * sizeof(uint32_t)); + tmpbuf = (uint8_t*) malloc(ceil_divide(ctx->hs->outbitlen, 8)); //for(i = 0; i < NUM_HASH_FUNCTIONS; i++) { + // tmpbuf[i] = (uint8_t*) malloc(ceil_divide(ctx->hs->outbitlen, 8)); + //} + + for(i = ctx->startpos, eleptr=ctx->elements, inbytelen=ctx->hs->inbytelen; i < ctx->endpos; i++, eleptr+=inbytelen) { + insert_element(ctx->table, eleptr, address, tmpbuf, ctx->hs); + } + free(tmpbuf); + free(address); +} + +inline void insert_element(sht_ctx* table, uint8_t* element, uint32_t* address, uint8_t* tmpbuf, hs_t* hs) { + uint32_t i, j; + bin_ctx* tmp_bin; + + hashElement(element, address, tmpbuf, hs); + + for(i = 0; i < NUM_HASH_FUNCTIONS; i++) { + + tmp_bin=table->bins + address[i]; + //pthread_mutex_lock(locks + address[i]); + memcpy(tmp_bin->values + tmp_bin->nvals * hs->outbytelen, tmpbuf, hs->outbytelen); + for(j = 0; j < i; j++) { + if(address[i] == address[j]) { + memset(tmp_bin->values + tmp_bin->nvals * hs->outbytelen, DUMMY_ENTRY_SERVER, hs->outbytelen); + } + } + tmp_bin->nvals++; + //TODO: or simply allocate a bigger block of memory: table->maxbinsize * 2, left out for efficiency reasons + assert(tmp_bin->nvals < table->maxbinsize); + /*cout << "Inserted into bin: " << address << ": " << (hex); + for(uint32_t j = 0; j < table->outbytelen; j++) { + cout << (unsigned int) tmpbuf[j]; + } + cout << (dec) << endl;*/ + //pthread_mutex_unlock(locks + address[i]); + } +} + + +void init_hash_table(sht_ctx* table, uint32_t nelements, hs_t* hs) { + uint32_t i; + + //table->addrbitlen = ceil_log2(nbins); + //table->addrbytelen = ceil_divide(table->addrbitlen, 8); + //table->inbytelen = ceil_divide(inbitlen, 8); + if(ceil_divide(nelements, hs->nbins) < 3) + table->maxbinsize = 3*max(ceil_log2(nelements),3); + else + table->maxbinsize = 6*max((int) ceil_divide(nelements, hs->nbins), 3); + //cout << "maxbinsize = " << table->maxbinsize << endl; + //table->outbytelen = ceil_divide(getOutBitLen(inbitlen, nbins), 8); + table->nbins = hs->nbins; + + table->bins = (bin_ctx*) calloc(hs->nbins, sizeof(bin_ctx)); + + for(i = 0; i < hs->nbins; i++) { + table->bins[i].values = (uint8_t*) malloc(table->maxbinsize * hs->outbytelen); + } +} + +void free_hash_table(sht_ctx* table) { + uint32_t i; + //1. free the byte-pointers for the values in the bints + for(i = 0; i < table->nbins; i++) { + //if(table->bins[i].nvals > 0) + free(table->bins[i].values); + } + //2. free the bins + free(table->bins); + //3. free the actual table + //free(table); +} diff --git a/src/hashing/simple_hashing.h b/src/hashing/simple_hashing.h new file mode 100644 index 0000000..7b7038e --- /dev/null +++ b/src/hashing/simple_hashing.h @@ -0,0 +1,54 @@ +/* + * simle_hashing.h + * + * Created on: Oct 8, 2014 + * Author: mzohner + */ + +#ifndef SIMLE_HASHING_H_ +#define SIMLE_HASHING_H_ + +#include "hashing_util.h" + +struct bin_ctx { + //hash-values of all elements mapped to this bin + uint8_t* values; + //number of elements stored in this bin + uint32_t nvals; +}; + +typedef struct simple_hash_table_ctx { + //pointer to the bins in the hash table + bin_ctx* bins; + //number bins in the hash table + uint32_t nbins; + //max bin size + uint32_t maxbinsize; + //uint32_t addrbitlen; + //uint32_t addrbytelen; + //uint32_t inbytelen; + //uint32_t outbytelen; +} sht_ctx; + +typedef struct simple_hash_entry_gen_ctx { + sht_ctx* table; + //input elements + uint8_t* elements; + uint32_t startpos; + uint32_t endpos; + //uint32_t inbytelen; + hs_t* hs; +} sheg_ctx; + + + +//returns a cuckoo hash table with the first dimension being the bins and the second dimension being the pointer to the elements +uint8_t* simple_hashing(uint8_t* elements, uint32_t neles, uint32_t bitlen, uint32_t* outbitlen, uint32_t* nelesinbin, uint32_t nbins, + uint32_t ntasks, prf_state_ctx* prf_state); +//routine for generating the entries, is invoked by the threads +void *gen_entries(void *ctx); +void init_hash_table(sht_ctx* table, uint32_t nelements, hs_t* hs); +void free_hash_table(sht_ctx* table); +inline void insert_element(sht_ctx* table, uint8_t* element, uint32_t* address, uint8_t* tmpbuf, hs_t* hs); + +#endif /* SIMLE_HASHING_H_ */ diff --git a/src/hashing/util.h b/src/hashing/util.h new file mode 100644 index 0000000..d069410 --- /dev/null +++ b/src/hashing/util.h @@ -0,0 +1,103 @@ +// this files contains all the application independent little +// functions and macros used for the optimizer. +// In particular Peters debug macros and Dags stuff +// from dbasic.h cdefs, random,... + +//////////////// stuff originally from debug.h /////////////////////////////// +// (c) 1997 Peter Sanders +// some little utilities for debugging adapted +// to the paros conventions + + +#ifndef UTIL +#define UTIL + +// default debug level. will be overidden e.g. if debug.h is included +#ifndef DEBUGLEVEL +#define DEBUGLEVEL 3 +#endif + +#if DEBUGLEVEL >= 0 +#define Debug0(A) A +#else +#define Debug0(A) +#endif +#if DEBUGLEVEL >= 1 +#define Debug1(A) A +#else +#define Debug1(A) +#endif +#if DEBUGLEVEL >= 2 +#define Debug2(A) A +#else +#define Debug2(A) +#endif +#if DEBUGLEVEL >= 3 +#define Debug3(A) A +#else +#define Debug3(A) +#endif +#if DEBUGLEVEL >= 4 +#define Debug4(A) A +#else +#define Debug4(A) +#endif +#if DEBUGLEVEL >= 5 +#define Debug5(A) A +#else +#define Debug5(A) +#endif +#if DEBUGLEVEL >= 6 +#define Debug6(A) A +#else +#define Debug6(A) +#endif + +#define Assert(c) if(!(c))\ + {cout << "\nAssertion violation " << __FILE__ << ":" << __LINE__ << endl;} +#define Assert0(C) Debug0(Assert(C)) +#define Assert1(C) Debug1(Assert(C)) +#define Assert2(C) Debug2(Assert(C)) +#define Assert3(C) Debug3(Assert(C)) +#define Assert4(C) Debug4(Assert(C)) +#define Assert5(C) Debug5(Assert(C)) + +#define Error(s) {cout << "\nError:" << s << " " << __FILE__ << ":" << __LINE__ << endl;} + +////////////// min, max etc. ////////////////////////////////////// + +#ifndef Max +#define Max(x,y) ((x)>=(y)?(x):(y)) +#endif + +#ifndef Min +#define Min(x,y) ((x)<=(y)?(x):(y)) +#endif + +#ifndef Abs +#define Abs(x) ((x) < 0 ? -(x) : (x)) +#endif + +#ifndef PI +#define PI 3.1415927 +#endif + +// is this the right definition of limit? +inline double limit(double x, double bound) +{ + if (x > bound) { return bound; } + else if (x < -bound) { return -bound; } + else return x; +} + +/////////////////////// timing ///////////////////// +#include + + +// elapsed CPU time see also /usr/include/sys/time.h +inline double cpuTime() +{ + return clock() * 1e-6; +} + +#endif diff --git a/src/naive-hashing/naive-psi.cpp b/src/naive-hashing/naive-psi.cpp new file mode 100644 index 0000000..ebdf05f --- /dev/null +++ b/src/naive-hashing/naive-psi.cpp @@ -0,0 +1,218 @@ +/* + * naive-psi.cpp + * + * Created on: Jul 9, 2014 + * Author: mzohner + */ +#include "naive-psi.h" + + +uint32_t naivepsi(role_type role, uint32_t neles, uint32_t pneles, uint32_t elebytelen, uint8_t* elements, + uint8_t** result, crypto* crypt_env, CSocket* sock, uint32_t ntasks) { + + uint32_t i, intersect_size, maskbytelen; + task_ctx_naive ectx; + CSocket* tmpsock = sock; + + uint32_t* perm; + uint8_t *permeles, *hashes, *phashes; + + maskbytelen = ceil_divide(crypt_env->get_seclvl().statbits + ceil_log2(neles) + ceil_log2(pneles), 8); + + permeles = (uint8_t*) malloc(sizeof(uint8_t) * neles * elebytelen); + hashes = (uint8_t*) malloc(sizeof(uint8_t) * neles * maskbytelen); + perm = (uint32_t*) malloc(sizeof(uint32_t) * neles); + + + /* Permute the elements */ + crypt_env->gen_rnd_perm(perm, neles); + for(i = 0; i < neles; i++) { + memcpy(permeles + perm[i] * elebytelen, elements + i * elebytelen, elebytelen); + } + + /* Hash elements */ +#ifdef DEBUG + cout << "Hashing my elements" << endl; +#endif + + ectx.eles.input = permeles; + ectx.eles.inbytelen = elebytelen; + ectx.eles.outbytelen = maskbytelen, + ectx.eles.nelements = neles; + ectx.eles.output = hashes; + ectx.hctx.symcrypt = crypt_env; + + run_task_naive(ntasks, ectx, hash_naive); + + phashes = (uint8_t*) malloc(sizeof(uint8_t) * pneles * maskbytelen); + + +#ifdef DEBUG + cout << "Exchanging hashes" << endl; +#endif + snd_and_rcv_naive(hashes, neles * maskbytelen, phashes, pneles * maskbytelen, tmpsock); + + /*cout << "Hashes of my elements: " << endl; + for(i = 0; i < neles; i++) { + for(uint32_t j = 0; j < maskbytelen; j++) { + cout << (hex) << (uint32_t) hashes[i * maskbytelen + j] << (dec); + } + cout << endl; + }*/ + + /*cout << "Hashes of partner elements: " << endl; + for(i = 0; i < npeles; i++) { + for(uint32_t j = 0; j < hash_bytes; j++) { + cout << (hex) << (uint32_t) phashes[i * hash_bytes + j] << (dec); + } + cout << endl; + }*/ +#ifdef DEBUG + cout << "Finding intersection" << endl; +#endif + intersect_size = find_intersection_naive(elements, result, elebytelen, hashes, + neles, phashes, pneles, maskbytelen, perm); + + +#ifdef DEBUG + cout << "Free-ing allocated memory" << endl; +#endif + free(perm); + free(hashes); + free(permeles); + free(phashes); + + return intersect_size; +} + + + +uint32_t find_intersection_naive(uint8_t* elements, uint8_t** result, uint32_t elebytelen, uint8_t* hashes, + uint32_t neles, uint8_t* phashes, uint32_t pneles, uint32_t hashbytelen, uint32_t* perm) { + + uint32_t* invperm = (uint32_t*) malloc(sizeof(uint32_t) * neles); + uint32_t* matches = (uint32_t*) malloc(sizeof(uint32_t) * neles); + uint64_t* tmpval; + + uint32_t size_intersect, i, intersect_ctr; + + for(i = 0; i < neles; i++) { + invperm[perm[i]] = i; + } + //cout << "My number of elements. " << neles << ", partner number of elements: " << pneles << ", maskbytelen: " << hashbytelen << endl; + + GHashTable *map= g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, NULL); + for(i = 0; i < neles; i++) { + g_hash_table_insert(map,(void*) ((uint64_t*) &(hashes[i*hashbytelen])), &(invperm[i])); + } + + //for(i = 0; i < pneles; i++) { + // ((uint64_t*) &(phashes[i*hashbytelen]))[0]++; + //} + + for(i = 0, intersect_ctr = 0; i < pneles; i++) { + + if(g_hash_table_lookup_extended(map, (void*) ((uint64_t*) &(phashes[i*hashbytelen])), + NULL, (void**) &tmpval)) { + matches[intersect_ctr] = tmpval[0]; + intersect_ctr++; + //assert(intersect_ctr <= min(neles, pneles)); + } + } + + size_intersect = intersect_ctr; + + //result = (uint8_t**) malloc(sizeof(uint8_t*)); + (*result) = (uint8_t*) malloc(sizeof(uint8_t) * size_intersect * elebytelen); + for(i = 0; i < size_intersect; i++) { + memcpy((*result) + i * elebytelen, elements + matches[i] * elebytelen, elebytelen); + } + + free(invperm); + free(matches); + return size_intersect; +} + +void snd_and_rcv_naive(uint8_t* snd_buf, uint32_t snd_bytes, uint8_t* rcv_buf, uint32_t rcv_bytes, CSocket* sock) { + pthread_t snd_task; + bool created, joined; + snd_ctx_naive ctx; + + //Start new sender thread + ctx.sock = sock; + ctx.snd_buf = snd_buf; + ctx.snd_bytes = snd_bytes; + created = !pthread_create(&snd_task, NULL, send_data_naive, (void*) &(ctx)); + + //receive + sock->Receive(rcv_buf, rcv_bytes); + assert(created); + + joined = !pthread_join(snd_task, NULL); + assert(joined); + +} + +void run_task_naive(uint32_t nthreads, task_ctx_naive context, void* (*func)(void*) ) { + task_ctx_naive* contexts = (task_ctx_naive*) malloc(sizeof(task_ctx_naive) * nthreads); + pthread_t* threads = (pthread_t*) malloc(sizeof(pthread_t) * nthreads); + uint32_t i, neles_thread, electr, neles_cur; + bool created, joined; + + neles_thread = ceil_divide(context.eles.nelements, nthreads); + for(i = 0, electr = 0; i < nthreads; i++) { + neles_cur = min(context.eles.nelements - electr, neles_thread); + memcpy(contexts + i, &context, sizeof(task_ctx_naive)); + contexts[i].eles.nelements = neles_cur; + contexts[i].eles.input = context.eles.input + (context.eles.inbytelen * electr); + contexts[i].eles.output = context.eles.output + (context.eles.outbytelen * electr); + electr += neles_cur; + } + + for(i = 0; i < nthreads; i++) { + created = !pthread_create(threads + i, NULL, func, (void*) &(contexts[i])); + } + + assert(created); + + for(i = 0; i < nthreads; i++) { + joined = !pthread_join(threads[i], NULL); + } + + assert(joined); + + free(threads); + free(contexts); +} + +void *hash_naive(void* context) { +#ifdef DEBUG + cout << "Hashing thread started" << endl; +#endif + crypto* crypt_env = ((task_ctx_naive*) context)->hctx.symcrypt; + element_ctx_naive electx = ((task_ctx_naive*) context)->eles; + + uint8_t *inptr=electx.input, *outptr=electx.output; + uint32_t i; + + for(i = 0; i < electx.nelements; i++, inptr+=electx.inbytelen, outptr+=electx.outbytelen) { + crypt_env->hash(outptr, electx.outbytelen, inptr, electx.inbytelen); + } + return 0; +} + +void *send_data_naive(void* context) { + snd_ctx_naive *ctx = (snd_ctx_naive*) context; + ctx->sock->Send(ctx->snd_buf, ctx->snd_bytes); + return 0; +} + + + + +void print_naive_psi_usage() { + cout << "Usage: ./naivepsi [0 (server)/1 (client)] [num_elements] " << + "[element_byte_length] [sym_security_bits] [server_ip] [server_port]" << endl; + cout << "Program exiting" << endl; + exit(0); +} diff --git a/src/naive-hashing/naive-psi.h b/src/naive-hashing/naive-psi.h new file mode 100644 index 0000000..a43fc55 --- /dev/null +++ b/src/naive-hashing/naive-psi.h @@ -0,0 +1,60 @@ +/* + * naive-psi.h + * + * Created on: Jul 9, 2014 + * Author: mzohner + */ + +#ifndef NAIVE_PSI_H_ +#define NAIVE_PSI_H_ + + +#include "../util/typedefs.h" +#include "../util/connection.h" +#include "../util/crypto/crypto.h" +#include "../util/crypto/pk-crypto.h" +#include + + + +struct element_ctx_naive { + uint32_t nelements; + uint32_t inbytelen; + uint8_t* input; + uint32_t outbytelen; + uint8_t* output; +}; + +struct hash_ctx_naive { + crypto* symcrypt; +}; + +struct task_ctx_naive { + element_ctx_naive eles; + hash_ctx_naive hctx; +}; + +struct snd_ctx_naive { + uint8_t* snd_buf; + uint32_t snd_bytes; + CSocket* sock; +}; + +//TODO merge with dhpsi + +void print_naive_psi_usage(); +uint32_t naivepsi(role_type role, uint32_t neles, uint32_t pneles, uint32_t elebytelen, uint8_t* elements, + uint8_t** result, crypto* crypt_env, CSocket* sock, uint32_t ntasks); +void run_task_naive(uint32_t nthreads, task_ctx_naive context, void* (*func)(void*) ); +void permute_naive(uint32_t nelements, uint32_t bytelen, uint8_t* elements, uint8_t* result, uint32_t* perm); +uint32_t find_intersection_naive(uint8_t* elements, uint8_t** result, uint32_t elebytelen, uint8_t* hashes, + uint32_t neles, uint8_t* phashes, uint32_t peles, uint32_t hashbytelen, uint32_t* perm); +void snd_and_rcv_naive(uint8_t* snd_buf, uint32_t snd_bytes, uint8_t* rcv_buf, uint32_t rcv_bytes, CSocket* sock); +void *hash_naive(void* context); +void *send_data_naive(void* context); + + + + + +#endif /* NAIVE_PSI_H_ */ diff --git a/src/ot-based/ot-psi.cpp b/src/ot-based/ot-psi.cpp new file mode 100644 index 0000000..32fa3e4 --- /dev/null +++ b/src/ot-based/ot-psi.cpp @@ -0,0 +1,855 @@ +/* + * ot-psi.cpp + * + * Created on: Jul 16, 2014 + * Author: mzohner + */ + +#include "ot-psi.h" + +/*int32_t main(int32_t argc, char** argv) { + uint32_t pid, nelements=10, elebytelen=4, symsecbits=80, + intersect_size, i, ntasks=1, runs=1, j; + int32_t nargs=8; + uint8_t *elements, *intersection; + const char* address; + uint16_t port; + timeval begin, end; + //CSocket* sockfd = (CSocket*) malloc(sizeof(CSocket) * ntasks); + vector sockfd(ntasks); + role_type role = (role_type) 0; + double epsilon=1.2; + + if(argc < nargs) { + print_ot_psi_usage(); + } + + pid = atoi(argv[1]); + nelements = atoi(argv[2]); + elebytelen = atoi(argv[3]); + symsecbits = atoi(argv[4]); + address = argv[5]; + port = (uint16_t) atoi(argv[6]); + epsilon = atof(argv[7]); + ntasks=atoi(argv[8]); + + if(pid == 0) { + role = SERVER; + listen(address, port, sockfd.data(), ntasks); + } else { + role = CLIENT; + for(i = 0; i < ntasks; i++) + connect(address, port, sockfd[i]); + } + + crypto crypto(symsecbits);//, const_ot_psi_seed); + elements = (uint8_t*) calloc(nelements * elebytelen, sizeof(uint8_t)); + +#ifdef SET_INPUT_ELEMENTS + for(i = 0; i < nelements; i++) { + //for(j = 0; j < elebytelen; j++) + elements[i*elebytelen] = i; + } +#endif +#ifdef PRINT_INPUT_ELEMENTS + for(i = 0; i < nelements; i++) { + cout << "Element " << i << ": " << (hex); + for(j = 0; j < elebytelen; j++) + cout << (unsigned int) elements[i*elebytelen + j]; + cout << (dec) << endl; + } +#endif + + + gettimeofday(&begin, NULL); + for(i = 0; i < runs; i++) { + crypto.gen_rnd(elements, elebytelen * nelements); + intersect_size = otpsi(nelements, elebytelen*8, elements, &intersection, &crypto, sockfd.data(), role, ntasks, epsilon); + } + gettimeofday(&end, NULL); + cout << "Computing the intersection took " << getMillies(begin, end) << " ms" << endl; + +#ifdef PRINT_INTERSECTION + cout << "Intersecting elements" << endl; + for(i = 0; i < intersect_size; i++) { + for(j = 0; j < elebytelen; j++) { + cout << (hex) << (uint32_t) intersection[i * elebytelen + j] << (dec); + } + cout << endl; + } +#endif + return 0; +}*/ + + +uint32_t otpsi(role_type role, uint32_t neles, uint32_t pneles, uint32_t* elebytelens, uint8_t** elements, + uint8_t*** result, uint32_t** res_bytelen, crypto* crypt_env, CSocket* sock, uint32_t ntasks, double epsilon, + bool detailed_timings) { + + prf_state_ctx prf_state; + uint32_t maskbytelen, nbins, intersect_size, internal_bitlen, maskbitlen, *res_pos, i, elebytelen; + uint8_t *eleptr; + timeval t_start, t_end; + + + DETAILED_TIMINGS = (detailed_timings>0); + + + maskbitlen = pad_to_multiple(crypt_env->get_seclvl().statbits + ceil_log2(neles) + ceil_log2(pneles), 8); + maskbytelen = ceil_divide(maskbitlen, 8); + + //Hash elements into a smaller domain + eleptr = (uint8_t*) malloc(maskbytelen * neles); + + //cout << "Hashing " << neles << " elements with arbitrary bit-length into " << + // maskbitlen << " bit representation " << endl; + + if(DETAILED_TIMINGS) { + gettimeofday(&t_start, NULL); + } + domain_hashing(neles, elements, elebytelens, eleptr, maskbytelen, crypt_env); + internal_bitlen = maskbitlen; + + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for domain hashing:\t" << fixed << std::setprecision(2) << getMillies(t_start, t_end) << " ms" << endl; + } + + crypt_env->gen_common_seed(&prf_state, sock[0]); + + if(role == SERVER) { + nbins = ceil(epsilon * pneles); + otpsi_server(eleptr, neles, nbins, pneles, internal_bitlen, maskbitlen, crypt_env, sock, ntasks, + &prf_state); + } else { //playing as client + nbins = ceil(epsilon * neles); + intersect_size = otpsi_client(eleptr, neles, nbins, pneles, internal_bitlen, maskbitlen, crypt_env, + sock, ntasks, &prf_state, &res_pos); + + std::sort(res_pos, res_pos+intersect_size); + + *result = (uint8_t**) malloc(intersect_size * sizeof(uint8_t*)); + *res_bytelen = (uint32_t*) malloc(intersect_size * sizeof(uint32_t)); + for(i = 0; i < intersect_size; i++) { + (*res_bytelen)[i] = elebytelens[res_pos[i]]; + (*result)[i] = (uint8_t*) malloc((*res_bytelen)[i]); + memcpy((*result)[i], elements[res_pos[i]], (*res_bytelen)[i]); + } + } + + free(eleptr); + + return intersect_size; +} + + + +uint32_t otpsi(role_type role, uint32_t neles, uint32_t pneles, uint32_t elebitlen, uint8_t* elements, + uint8_t** result, crypto* crypt_env, CSocket* sock, uint32_t ntasks, double epsilon, + bool detailed_timings) { + + prf_state_ctx prf_state; + uint32_t maskbytelen, nbins, intersect_size, internal_bitlen, maskbitlen, *res_pos, i, elebytelen; + uint8_t *eleptr; + timeval t_start, t_end; + + DETAILED_TIMINGS = detailed_timings; + + maskbitlen = pad_to_multiple(crypt_env->get_seclvl().statbits + ceil_log2(neles) + ceil_log2(pneles), 8); + maskbytelen = ceil_divide(maskbitlen, 8); + elebytelen = ceil_divide(elebitlen, 8); + + if(elebitlen > maskbitlen) { + //Hash elements into a smaller domain + eleptr = (uint8_t*) malloc(maskbytelen * neles); + domain_hashing(neles, elements, ceil_divide(elebitlen, 8), eleptr, maskbytelen, crypt_env); + internal_bitlen = maskbitlen; +#ifndef BATCH + cout << "Hashing " << neles << " elements with " << elebitlen << " bit-length into " << + maskbitlen << " representation " << endl; +#endif + } else { + eleptr = elements; + internal_bitlen = elebitlen; + } + + + crypt_env->gen_common_seed(&prf_state, sock[0]); + + if(role == SERVER) { + nbins = ceil(epsilon * pneles); + otpsi_server(eleptr, neles, nbins, pneles, internal_bitlen, maskbitlen, crypt_env, sock, ntasks, + &prf_state); + } else { //playing as client + nbins = ceil(epsilon * neles); + intersect_size = otpsi_client(eleptr, neles, nbins, pneles, internal_bitlen, maskbitlen, crypt_env, + sock, ntasks, &prf_state, &res_pos); + *result = (uint8_t*) malloc(intersect_size * elebytelen); + for(i = 0; i < intersect_size; i++) { + memcpy((*result) + i * elebytelen, elements + res_pos[i] * elebytelen, elebytelen); + } + } + + if(elebitlen > maskbitlen) + free(eleptr); + + return intersect_size; +} + + + +uint32_t otpsi_client(uint8_t* elements, uint32_t neles, uint32_t nbins, uint32_t pneles, + uint32_t elebitlen, uint32_t maskbitlen, crypto* crypt_env, CSocket* sock, uint32_t ntasks, + prf_state_ctx* prf_state, uint32_t** result) { + + uint32_t outbitlen, maskbytelen, intersect_size; + uint8_t *hash_table, *masks; + uint32_t* nelesinbin; + uint8_t *server_masks; + uint32_t* perm = (uint32_t*) calloc(neles, sizeof(uint32_t)); + pthread_t rcv_masks_thread; + pthread_t* query_map_thread = (pthread_t*) malloc(sizeof(pthread_t) * ntasks); + query_ctx* query_data = (query_ctx*) malloc(sizeof(query_ctx) * ntasks); + mask_rcv_ctx rcv_ctx; + timeval t_start, t_end; + + nelesinbin = (uint32_t*) calloc(nbins, sizeof(uint32_t)); + maskbytelen = ceil_divide(maskbitlen, 8); + intersect_size=0; + +#ifdef TIMING + gettimeofday(&t_start, NULL); +#endif + if(DETAILED_TIMINGS) { + gettimeofday(&t_start, NULL); + } + hash_table = cuckoo_hashing(elements, neles, nbins, elebitlen, &outbitlen, + nelesinbin, perm, ntasks, prf_state); + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for Cuckoo hashing:\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); + } + +#ifdef TIMING + gettimeofday(&t_end, NULL); + cout << "Client: time for cuckoo hashing: " << getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); +#endif +#ifdef PRINT_BIN_CONTENT + print_bin_content(hash_table, nbins, ceil_divide(outbitlen, 8), NULL, false); +#endif + + masks = (uint8_t*) malloc(neles * maskbytelen); + //Perform the OPRG execution + oprg_client(hash_table, nbins, neles, nelesinbin, outbitlen, maskbitlen, crypt_env, sock, ntasks, masks); + + if(DETAILED_TIMINGS) { + gettimeofday(&t_start, NULL); + } +#ifdef TIMING + gettimeofday(&t_end, NULL); + cout << "Client: time for OPRG evaluation: " << getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); + +#endif + //receive server masks + server_masks = (uint8_t*) malloc(NUM_HASH_FUNCTIONS * pneles * maskbytelen); + + //receive_masks(server_masks, NUM_HASH_FUNCTIONS * neles, maskbytelen, sock[0]); + //use a separate thread to receive the server's masks + rcv_ctx.rcv_buf = server_masks; + rcv_ctx.nmasks = NUM_HASH_FUNCTIONS * pneles; + rcv_ctx.maskbytelen = maskbytelen; + rcv_ctx.sock = sock; + if(pthread_create(&rcv_masks_thread, NULL, receive_masks, (void*) (&rcv_ctx))) { + cerr << "Error in creating new pthread at cuckoo hashing!" << endl; + exit(0); + } + //meanwhile generate the hash table + //GHashTable* map = otpsi_create_hash_table(ceil_divide(inbitlen,8), masks, neles, maskbytelen, perm); + //intersect_size = otpsi_find_intersection(eleptr, result, ceil_divide(inbitlen,8), masks, neles, server_masks, + // neles * NUM_HASH_FUNCTIONS, maskbytelen, perm); + //wait for receiving thread + if(pthread_join(rcv_masks_thread, NULL)) { + cerr << "Error in joining pthread at cuckoo hashing!" << endl; + exit(0); + } + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for receiving masks:\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); + } +#ifdef TIMING + gettimeofday(&t_end, NULL); + cout << "Client: time for receiving masks and generating hash table: " << getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); +#endif +#ifdef PRINT_RECEIVED_VALUES + cout << "Received server masks: " << endl; + print_bin_content(server_masks, NUM_HASH_FUNCTIONS*neles, maskbytelen, NULL, false); +#endif + + //query hash table using multiple threads + //TODO set the values in the struct correct + /*for(i = 0; i < ntasks; i++) { + neles_per_thread = i * ceil_divide(pneles, ntasks); + thread_startpos = i * neles_per_thread; + query_data[i].elebytelen = ceil_divide(inbitlen,8); + query_data[i].elements = eleptr; + query_data[i].hashbytelen = maskbytelen; + query_data[i].map = map; + query_data[i].qhashes = server_masks + (thread_startpos * NUM_HASH_FUNCTIONS * maskbytelen); + query_data[i].qneles = min(neles - thread_startpos, neles_per_thread) * NUM_HASH_FUNCTIONS;//neles * NUM_HASH_FUNCTIONS; + if(pthread_create(query_map_thread+i, NULL, otpsi_query_hash_table, (void*) (query_data+i))) { + cerr << "Error in creating new pthread at cuckoo hashing!" << endl; + exit(0); + } + }*/ +#ifdef TIMING + gettimeofday(&t_end, NULL); + cout << "Client: time for computing intersection: " << getMillies(t_start, t_end) << " ms" << endl; +#endif + + //compute intersection + intersect_size = otpsi_find_intersection(result, masks, neles, server_masks, + neles * NUM_HASH_FUNCTIONS, maskbytelen, perm); + + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for intersecting:\t\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + } + free(masks); + free(hash_table); + free(nelesinbin); + free(perm); + free(server_masks); + free(query_map_thread); + free(query_data); + //free(map); + + return intersect_size; +} + + + +void otpsi_server(uint8_t* elements, uint32_t neles, uint32_t nbins, uint32_t pneles, uint32_t elebitlen, uint32_t maskbitlen, + crypto* crypt_env, CSocket* sock, uint32_t ntasks, prf_state_ctx* prf_state) { + uint8_t *hash_table, *masks; + uint32_t* nelesinbin; + uint32_t outbitlen, maskbytelen; + timeval t_start, t_end; + + nelesinbin = (uint32_t*) malloc(sizeof(uint32_t) * nbins); + maskbytelen = ceil_divide(maskbitlen, 8); + + //outbitlen = getOutBitLen(inbitlen, nbins);//bitlen - addr_bits; + + //hash_table = (uint8_t*) malloc(nbins * NUM_HASH_FUNCTIONS * ceil_divide(outbitlen, 8)); + if(DETAILED_TIMINGS) { + gettimeofday(&t_start, NULL); + } +#ifdef TIMING + gettimeofday(&t_start, NULL); +#endif + hash_table = simple_hashing(elements, neles, elebitlen, &outbitlen, nelesinbin, nbins, ntasks, prf_state); + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for simple hashing:\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); + } +#ifdef TIMING + gettimeofday(&t_end, NULL); + cout << "Server: time for simple hashing: " << getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); +#endif +#ifdef PRINT_BIN_CONTENT + print_bin_content(hash_table, nbins, ceil_divide(outbitlen, 8), nelesinbin, true); +#endif + masks = (uint8_t*) malloc(NUM_HASH_FUNCTIONS * neles * maskbytelen); + oprg_server(hash_table, nbins, neles * NUM_HASH_FUNCTIONS, nelesinbin, outbitlen, maskbitlen, crypt_env, sock, ntasks, masks); + if(DETAILED_TIMINGS) { + gettimeofday(&t_start, NULL); + } +#ifdef TIMING + gettimeofday(&t_end, NULL); + cout << "Server: time for OPRG evaluation: " << getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); +#endif + //send the masks to the receiver + send_masks(masks, neles * NUM_HASH_FUNCTIONS, maskbytelen, sock[0]); + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for sending masks:\t\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + } +#ifdef TIMING + gettimeofday(&t_end, NULL); + cout << "Server: time for sending masks: " << getMillies(t_start, t_end) << " ms" << endl; +#endif + + free(masks); + free(hash_table); + free(nelesinbin); +} + + + + + +void print_ot_psi_usage() { + cout << "Usage: ./otpsi [0 (server)/1 (client)] [num_elements] [element_byte_length] " + << "[sym_security_bits] [server_ip] [server_port] [CH_epsilon] [nthreads]" << endl; + cout << "Program exiting" << endl; + exit(0); +} + + + +void oprg_client(uint8_t* hash_table, uint32_t nbins, uint32_t neles, uint32_t* nelesinbin, uint32_t elebitlen, + uint32_t maskbitlen, crypto* crypt, CSocket* sock, uint32_t nthreads, uint8_t* res_buf) { + CBitVector choices; + CBitVector resulting_masks; + uint32_t OTsPerElement, numOTs, i, u, maskbytelen, ctr; + uint8_t *keyMtx; + OTExtension1ooNECCReceiver* receiver; + timeval t_start, t_end; + + maskbytelen = ceil_divide(maskbitlen, 8); + + OTsPerElement = ceil_divide(elebitlen, 8); + numOTs = nbins * OTsPerElement; + +#ifndef BATCH + cout << "Client: bins = " << nbins << ", elebitlen = " << elebitlen << " and maskbitlen = " << + maskbitlen << " and performs " << numOTs << " OTs" << endl; +#endif + + keyMtx = (uint8_t*) malloc(crypt->get_aes_key_bytes()*m_nCodeWordBits* 2); + if(DETAILED_TIMINGS) { + gettimeofday(&t_start, NULL); + } + + InitOTReceiver(keyMtx, sock[0], crypt); + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for base-OTs:\t\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); + } + + receiver = new OTExtension1ooNECCReceiver(m_nCodeWordBits, crypt, sock, keyMtx); + + //TODO recheck size + choices.AttachBuf(hash_table, ceil_divide(numOTs * 8, 8)); + + //for(uint32_t i = 0; i < nbins; i++) { + resulting_masks.AttachBuf(res_buf, neles * maskbytelen);//[i].Create(maskbitlen); + resulting_masks.Reset(); + //} + + //cout << "Choices: "; + //choices.PrintHex(); + + CBitVector response; + response.Create(numOTs, AES_BITS); + + //uint32_t itembitlen, uint32_t maskbitlen, CBitVector* results, crypto* crypt + //m_fMaskFct = new XORMasking(bitlength, m_cCrypto); + OPEMasking* mskfct = new OPEMasking(elebitlen, maskbitlen, nbins, nelesinbin, resulting_masks, crypt); + //choices.Reset(); + +// ObliviouslyReceive(choices, response, numOTs, AES_BITS, RN_OT); + //cout << "Receiver performing " << numOTs << " ots" << endl; + receiver->receive(numOTs, AES_BITS, choices, response, RN_OT, nthreads, mskfct); + + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for OT extension:\t\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); + } + +#ifdef PRINT_OPRG_MASKS + for(i = 0, ctr=0; i < nbins; i++) { + if(nelesinbin[i] > 0) { + cout << "Result for element i = " << i << " and choice = " << (hex) << + choices.Get(i * OTsPerElement*8, OTsPerElement*8) << (dec) << ": "; + for(uint32_t j = 0; j < maskbytelen; j++) { + cout << (hex) << (uint32_t) res_buf[ctr * maskbytelen + j] << (dec); + } + cout << endl; + ctr++; + //resulting_masks[i].PrintHex(); + } + //memcpy(res_buf_ptr, resulting_masks[i].GetArr(), maskbytelen); + } +#endif + + + evaluate_crf(res_buf, res_buf, neles, maskbytelen, crypt); + + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for CRF evaluation:\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + } + + delete mskfct; +} + + + + +void oprg_server(uint8_t* hash_table, uint32_t nbins, uint32_t totaleles, uint32_t* nelesinbin, uint32_t elebitlen, + uint32_t maskbitlen, crypto* crypt, CSocket* sock, uint32_t nthreads, uint8_t* res_buf) { + CBitVector input, results; + CBitVector baseOTchoices; + uint8_t* keySeeds; + uint32_t numOTs, OTsPerElement, i, maskbytelen; + OTExtension1ooNECCSender* sender; + timeval t_start, t_end; + + maskbytelen = maskbitlen / 8; + + OTsPerElement = ceil_divide(elebitlen, 8); + numOTs = nbins * OTsPerElement; + +#ifndef BATCH + cout << "Server: bins = " << nbins << ", elebitlen = " << elebitlen << " and maskbitlen = " << + maskbitlen << " and performs " << numOTs << " OTs" << endl; +#endif + + baseOTchoices.Create(m_nCodeWordBits); + crypt->gen_rnd(baseOTchoices.GetArr(), ceil_divide(m_nCodeWordBits, 8)); + + keySeeds = (uint8_t*) malloc(crypt->get_aes_key_bytes()*m_nCodeWordBits); + + if(DETAILED_TIMINGS) { + gettimeofday(&t_start, NULL); + } + InitOTSender(keySeeds, baseOTchoices, sock[0], crypt); + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for base-OTs:\t\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); + } + sender = new OTExtension1ooNECCSender(m_nCodeWordBits, crypt, sock, baseOTchoices, keySeeds); + + //Check base-OT seeds + /*for(i = 0; i < m_nCodeWordBits; i++) { + cout << "i = " << i << ": " << (hex) << ((uint64_t*) keySeeds)[(i*2)] << ((uint64_t*) keySeeds)[(i*2)+1] << (dec) << endl; + }*/ + + //for(uint32_t i = 0; i < nbins; i++) { + input.AttachBuf(hash_table, totaleles * ceil_divide(elebitlen, 8)); + results.AttachBuf(res_buf, totaleles * maskbytelen); + //input.Reset(); + results.Reset(); + //} + + CBitVector values[2]; + values[0].Create(numOTs * AES_BITS); + values[1].Create(numOTs * AES_BITS); + + //m_fMaskFct = new XORMasking(bitlength, m_cCrypto); + OPEMasking* mskfct = new OPEMasking(elebitlen, maskbitlen, nbins, nelesinbin, input, results, crypt); + //cout << "Sender performing " << numOTs << " ots" << endl; + sender->send(numOTs, AES_BITS, values, RN_OT, nthreads, mskfct);//ObliviouslySend(values, numOTs, AES_BITS, RN_OT); + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for OT extension:\t\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + gettimeofday(&t_start, NULL); + } + +#ifdef PRINT_OPRG_MASKS + for(i = 0; i < totaleles; i++) { + cout << "Result for element i = " << i << ": "; + for(uint32_t j = 0; j < maskbytelen; j++) { + cout << (hex) << (uint32_t) res_buf[i * maskbytelen + j] << (dec); + } + cout << endl; + } +#endif + //memcpy(res_buf_ptr, results[i].GetArr(), maskbytelen * nelesinbin[i]); + //res_buf_ptr+=maskbytelen * nelesinbin[i]; + + //evaluate correlation robust function on the elements + evaluate_crf(res_buf, res_buf, totaleles, maskbytelen, crypt); + + if(DETAILED_TIMINGS) { + gettimeofday(&t_end, NULL); + cout << "Time for CRF evaluation:\t" << fixed << std::setprecision(2) << + getMillies(t_start, t_end) << " ms" << endl; + } + + delete mskfct; +} + + +void InitOTSender(uint8_t* keySeeds, CBitVector& choices, CSocket sock, crypto* crypt) +{ +#ifdef TIMING + timeval np_begin, np_end; +#endif + +// keySeeds = (uint8_t*) malloc(crypt->get_aes_key_bytes()*m_nCodeWordBits); + NaorPinkas* bot = new NaorPinkas(crypt, ECC_FIELD); + + +#ifdef TIMING + gettimeofday(&np_begin, NULL); +#endif + + uint32_t numbaseOTs = m_nCodeWordBits; + uint8_t* pBuf = (uint8_t*) malloc(numbaseOTs * crypt->get_hash_bytes()); + + bot->Receiver(2, numbaseOTs, choices, sock, pBuf); + + //Key expansion + uint8_t* pBufIdx = pBuf; + for(uint32_t i=0; iget_aes_key_bytes(), pBufIdx, crypt->get_aes_key_bytes()); + pBufIdx+=crypt->get_hash_bytes(); + //cout << i << ": " << (hex) << ((uint64_t*)keySeeds)[2*i] << ((uint64_t*)keySeeds)[2*i+1]<< (dec) << endl; + } + free(pBuf); + +#ifdef TIMING + gettimeofday(&np_end, NULL); + printf("Time for performing the NP base-OTs: %f seconds\n", getMillies(np_begin, np_end)); +#endif + +} + +void InitOTReceiver(uint8_t* keyMtx, CSocket sock, crypto* crypt) +{ +#ifdef TIMING + timeval np_begin, np_end; +#endif + + NaorPinkas* bot = new NaorPinkas(crypt, ECC_FIELD);//NaorPinkas(m_sSecLvl, m_aSeed); + +#ifdef TIMING + gettimeofday(&np_begin, NULL); +#endif + + uint32_t numbaseOTs = m_nCodeWordBits; + // Execute NP receiver routine and obtain the key + uint8_t* pBuf = (uint8_t*) malloc(crypt->get_hash_bytes() * numbaseOTs * 2); + bot->Sender(2, numbaseOTs, sock, pBuf); + + //Key expansion + uint8_t* pBufIdx = pBuf; + for(uint32_t i=0; iget_aes_key_bytes(), pBufIdx, crypt->get_aes_key_bytes()); + pBufIdx += crypt->get_hash_bytes(); + //cout << i/2 << ": " << (hex) << ((uint64_t*)keyMtx)[2*i] << ((uint64_t*)keyMtx)[2*i+1]<< (dec) << endl; + } + + free(pBuf); + + +#ifdef TIMING + gettimeofday(&np_end, NULL); + printf("Time for performing the NP base-OTs: %f seconds\n", getMillies(np_begin, np_end)); +#endif +} + + +void send_masks(uint8_t* masks, uint32_t nmasks, uint32_t maskbytelen, CSocket& sock) { + sock.Send(masks, nmasks*maskbytelen); +} + + +void receive_masks(uint8_t* masks, uint32_t nmasks, uint32_t maskbytelen, CSocket& sock) { + sock.Receive(masks, nmasks*maskbytelen); +} + + +GHashTable* otpsi_create_hash_table(uint32_t elebytelen, uint8_t* hashes, uint32_t neles, uint32_t + hashbytelen, uint32_t* perm) { + uint64_t tmpbuf; + uint32_t i, tmp_hashbytelen; + + tmp_hashbytelen = min((uint32_t) sizeof(uint64_t), hashbytelen); + + GHashTable *map= g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, NULL); + for(i = 0; i < neles; i++) { + memcpy((uint8_t*) &tmpbuf, hashes + i*tmp_hashbytelen, tmp_hashbytelen); + g_hash_table_insert(map,(void*) &tmpbuf, &(perm[i])); + } + return map; +} + + +void *otpsi_query_hash_table(void* ctx_tmp) {//GHashTable *map, uint8_t* elements, uint8_t** result, uint32_t elebytelen, + //uint8_t* qhashes, uint32_t qneles, uint32_t hashbytelen) { + + cout << "Starting to query hash table" << endl; + query_ctx* qctx = (query_ctx*) ctx_tmp; + + uint32_t* matches = (uint32_t*) malloc(sizeof(uint32_t) * qctx->qneles); + uint32_t* tmpval; + GHashTable *map = qctx->map; + uint8_t* qhashes = qctx->qhashes; + uint8_t* elements = qctx->elements; + uint64_t tmpbuf; + + uint32_t size_intersect, i, intersect_ctr, tmp_hashbytelen, elebytelen; + elebytelen = qctx->elebytelen; + + tmp_hashbytelen = min((uint32_t) sizeof(uint64_t), qctx->hashbytelen); + + for(i = 0, intersect_ctr = 0; i < qctx->qneles; i++) { + memcpy((uint8_t*) &tmpbuf, qhashes + i*tmp_hashbytelen, tmp_hashbytelen); + if(g_hash_table_lookup_extended(map, (void*) &tmpbuf, NULL, (void**) &tmpval)) { + matches[intersect_ctr] = tmpval[0]; + intersect_ctr++; + } + } + + size_intersect = intersect_ctr; + + qctx->result = (uint8_t*) malloc(sizeof(uint8_t) * size_intersect * elebytelen); + for(i = 0; i < size_intersect; i++) { + memcpy((qctx->result) + i * elebytelen, elements + matches[i] * elebytelen, elebytelen); + } + qctx->res_size = size_intersect; + + free(matches); + //return size_intersect; +} + +//TODO if this works correctly, combine with other find intersection methods and outsource to hashing_util.h +uint32_t otpsi_find_intersection(uint32_t** result, uint8_t* my_hashes, + uint32_t my_neles, uint8_t* pa_hashes, uint32_t pa_neles, uint32_t hashbytelen, uint32_t* perm) { + uint32_t keys_stored; + uint32_t* matches = (uint32_t*) malloc(sizeof(uint32_t) * my_neles); + uint32_t* tmpval; + uint64_t tmpbuf; + uint32_t* tmpkeys; + + uint32_t size_intersect, i, intersect_ctr, tmp_hashbytelen; + + //tmp_hashbytelen; //= min((uint32_t) sizeof(uint64_t), hashbytelen); + if(sizeof(uint64_t) < hashbytelen) { + keys_stored = 2; + tmp_hashbytelen = sizeof(uint64_t); + tmpkeys = (uint32_t*) calloc(my_neles * keys_stored, sizeof(uint32_t)); + for(i = 0; i < my_neles; i++) { + memcpy(tmpkeys + 2*i, my_hashes + i*hashbytelen + sizeof(uint64_t), hashbytelen-sizeof(uint64_t)); + memcpy(tmpkeys + 2*i + 1, perm + i, sizeof(uint32_t)); + } + } else { + keys_stored = 1; + tmp_hashbytelen = hashbytelen; + tmpkeys = (uint32_t*) malloc(my_neles * keys_stored * sizeof(uint32_t)); + memcpy(tmpkeys, perm, my_neles * sizeof(uint32_t)); + } + + GHashTable *map= g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, NULL); + for(i = 0; i < my_neles; i++) { + //tmpbuf=0; + memcpy((uint8_t*) &tmpbuf, my_hashes + i*hashbytelen, tmp_hashbytelen); + //cout << "Insertion, " << i << " = " <<(hex) << tmpbuf << endl; + //for(uint32_t j = 0; j < tmp_hashbytelen; j++) + + g_hash_table_insert(map,(void*) &tmpbuf, &(tmpkeys[i*keys_stored])); + } + + for(i = 0, intersect_ctr = 0; i < pa_neles; i++) { + //tmpbuf=0; + memcpy((uint8_t*) &tmpbuf, pa_hashes + i*hashbytelen, tmp_hashbytelen); + //cout << "Query, " << i << " = " <<(hex) << tmpbuf << endl; + if(g_hash_table_lookup_extended(map, (void*) &tmpbuf, NULL, (void**) &tmpval)) { + if(keys_stored > 1) { + tmpbuf = 0; + memcpy((uint8_t*) &tmpbuf, pa_hashes + i*hashbytelen+sizeof(uint64_t), hashbytelen-sizeof(uint64_t)); + if((uint32_t) tmpbuf == tmpval[0]) { + matches[intersect_ctr] = tmpval[1]; + if(intersect_ctrinit_aes_key(&aes_key, 128, (uint8_t*) const_seed); + for(i = 0; i < nelements; i++) { + //cout << "Input: " << ((uint64_t*)masks)[0] << endl; + crypt->fixed_key_aes_hash(&aes_key, result+i*elebytelen, elebytelen, masks+i*elebytelen, elebytelen); + //cout << "Input: " << ((uint64_t*)masks)[0] << endl; + } +} + + + +void print_bin_content(uint8_t* hash_table, uint32_t nbins, uint32_t elebytelen, uint32_t* nelesinbin, bool multi_values) { + uint32_t i, j, k, ctr; + if(multi_values) { + for(i = 0, ctr = 0; i < nbins; i++) { + cout << "(" << nelesinbin[i] << ") Bin " << i << ": "; + for(j = 0; j < nelesinbin[i]; j++) { + for(k = 0; k < elebytelen; k++, ctr++) { + cout << (hex) << (unsigned int) hash_table[ctr] << (dec); + } + cout << " "; + } + cout << endl; + } + } else { + for(i = 0, ctr = 0; i < nbins; i++) { + cout << "Bin " << i << ": "; + for(k = 0; k < elebytelen; k++, ctr++) { + cout << (hex) << (unsigned int) hash_table[ctr] << (dec); + } + cout << endl; + } + cout << endl; + } +} + +void *receive_masks(void *ctx_tmp) { + mask_rcv_ctx* ctx = (mask_rcv_ctx*) ctx_tmp; + ctx->sock->Receive(ctx->rcv_buf, ctx->maskbytelen * ctx->nmasks); +} + diff --git a/src/ot-based/ot-psi.h b/src/ot-based/ot-psi.h new file mode 100644 index 0000000..f6cc19c --- /dev/null +++ b/src/ot-based/ot-psi.h @@ -0,0 +1,93 @@ +/* + * ot-psi.h + * + * Created on: Jul 16, 2014 + * Author: mzohner + */ + +#ifndef OT_PSI_H_ +#define OT_PSI_H_ + +#include "../util/typedefs.h" +#include "../util/crypto/crypto.h" +#include "../util/cbitvector.h" +#include "../util/socket.h" +#include "../util/connection.h" +#include "../hashing/cuckoo.h" +#include "../hashing/simple_hashing.h" +#include "../util/ot/ot-extension-1oon-ecc.h" +#include "../util/ot/naor-pinkas.h" +#include "../util/ot/opemasking.h" +#include +#include + +static bool DETAILED_TIMINGS=0; + +//#define DEBUG +//#define PRINT_BIN_CONTENT +//#define PRINT_OPRG_MASKS +//#define PRINT_RECEIVED_VALUES + +struct mask_rcv_ctx { + uint8_t* rcv_buf; + uint32_t nmasks; + uint32_t maskbytelen; + CSocket* sock; +}; + +struct query_ctx { + GHashTable *map; + uint8_t* result; + uint32_t res_size; + + uint8_t* elements; + uint32_t elebytelen; + + uint8_t* qhashes; + uint32_t qneles; + uint32_t hashbytelen; +}; + + +uint32_t otpsi(role_type role, uint32_t neles, uint32_t pneles, uint32_t elebitlen, uint8_t* elements, + uint8_t** result, crypto* crypt_env, CSocket* sock, uint32_t ntasks, double epsilon=1.2, + bool detailed_timings=false); + +uint32_t otpsi(role_type role, uint32_t neles, uint32_t pneles, uint32_t* elebytelens, uint8_t** elements, + uint8_t*** result, uint32_t** res_bytelen, crypto* crypt_env, CSocket* sock, uint32_t ntasks, double epsilon=1.2, + bool detailed_timings=false); + + +uint32_t otpsi_client(uint8_t* elements, uint32_t neles, uint32_t nbins, uint32_t pneles, uint32_t elebitlen, uint32_t maskbitlen, + crypto* crypt_env, CSocket* sock, uint32_t ntasks, prf_state_ctx* prf_state, uint32_t** result); + +void otpsi_server(uint8_t* elements, uint32_t neles, uint32_t nbins, uint32_t pneles, uint32_t elebitlen, uint32_t maskbitlen, + crypto* crypt_env, CSocket* sock, uint32_t ntasks, prf_state_ctx* prf_state); + +void oprg_client(uint8_t* hash_table, uint32_t nbins, uint32_t neles, uint32_t* nelesinbin, uint32_t elebitlen, + uint32_t maskbitlen, crypto* crypt, CSocket* sock, uint32_t nthreads, uint8_t* res_buf); +void oprg_server(uint8_t* hash_table, uint32_t nbins, uint32_t totaleles, uint32_t* nelesinbin, uint32_t elebitlen, + uint32_t maskbitlen, crypto* crypt, CSocket* sock, uint32_t nthreads, uint8_t* res_buf); + +void send_masks(uint8_t* masks, uint32_t nmasks, uint32_t maskbytelen, CSocket& sock); +void *receive_masks(void *ctx_tmp); + + +void InitOTReceiver(uint8_t* keyMtx, CSocket sock, crypto* crypt); +void InitOTSender(uint8_t* keySeeds, CBitVector& choices, CSocket sock, crypto* crypt); + +void print_ot_psi_usage(); + +GHashTable* otpsi_create_hash_table(uint32_t elebytelen, uint8_t* hashes, uint32_t neles, uint32_t + hashbytelen, uint32_t* perm); +void *otpsi_query_hash_table(void* ctx_tmp); + +uint32_t otpsi_find_intersection(uint32_t** result, uint8_t* my_hashes, + uint32_t my_neles, uint8_t* pa_hashes, uint32_t pa_neles, uint32_t hashbytelen, uint32_t* perm); + +void print_bin_content(uint8_t* hash_table, uint32_t nbins, uint32_t elebytelen, uint32_t* nelesinbin, bool multi_values); + +void evaluate_crf(uint8_t* result, uint8_t* masks, uint32_t nelements, uint32_t elebytelen, crypto* crypt); + + +#endif /* OT_PSI_H_ */ diff --git a/src/pk-based/dh-psi.cpp b/src/pk-based/dh-psi.cpp new file mode 100644 index 0000000..bab62a5 --- /dev/null +++ b/src/pk-based/dh-psi.cpp @@ -0,0 +1,325 @@ +/* + * dh-psi.cpp + * + * Created on: Jul 9, 2014 + * Author: mzohner + */ +#include "dh-psi.h" + +uint32_t dhpsi(role_type role, uint32_t neles, uint32_t pneles, uint32_t elebytelen, uint8_t* elements, + uint8_t** result, crypto* crypt_env, CSocket* sock, uint32_t ntasks, bool cardinality, field_type ftype) { + + uint32_t i, hash_bytes = crypt_env->get_hash_bytes(), intersect_size, fe_bytes, sndbufsize, rcvbufsize; + task_ctx ectx; + pk_crypto* field = crypt_env->gen_field(ftype); + num* exponent = field->get_rnd_num(); + CSocket* tmpsock = sock; + + fe_bytes = field->fe_byte_size(); + + uint32_t* perm = (uint32_t*) malloc(sizeof(uint32_t) * neles); + uint32_t* cardinality_perm; + uint8_t* permeles = (uint8_t*) malloc(sizeof(uint8_t) * neles * elebytelen); + uint8_t* encrypted_eles = (uint8_t*) malloc(sizeof(uint8_t) * neles * fe_bytes); + uint8_t* hashes = (uint8_t*) malloc(sizeof(uint8_t) * neles * hash_bytes); + + //Partner's elements and hashes + uint8_t *peles, *phashes, *perm_peles; + + + /* Permute the elements */ + crypt_env->gen_rnd_perm(perm, neles); + for(i = 0; i < neles; i++) { + memcpy(permeles + perm[i] * elebytelen, elements + i * elebytelen, elebytelen); + } + + /* Hash elements */ + ectx.eles.input = permeles; + ectx.eles.output = hashes; + ectx.eles.nelements = neles; + ectx.eles.inbytelen = elebytelen; + ectx.eles.outbytelen = hash_bytes; + ectx.hctx.symcrypt = crypt_env; + +#ifdef DEBUG + cout << "Hashing elements" << endl; +#endif + run_task(ntasks, ectx, hash); + + /* Encrypt elements */ + ectx.eles.input = hashes; + ectx.eles.inbytelen = hash_bytes; + ectx.eles.nelements = neles; + ectx.eles.outbytelen = fe_bytes; + ectx.eles.output = encrypted_eles; + ectx.ectx.field = field; + ectx.ectx.exponent = exponent; + ectx.ectx.sample = true; + +#ifdef DEBUG + cout << "Hash and encrypting my elements" << endl; +#endif + run_task(ntasks, ectx, encrypt); + + + peles = (uint8_t*) malloc(sizeof(uint8_t) * pneles * fe_bytes); +#ifdef DEBUG + cout << "Exchanging ciphertexts" << endl; +#endif + snd_and_rcv(encrypted_eles, neles * fe_bytes, peles, pneles * fe_bytes, tmpsock); + + + if(cardinality) { + //samle permutation, permute elements, and copy back to original array + cardinality_perm = (uint32_t*) malloc(sizeof(uint32_t) * pneles); + crypt_env->gen_rnd_perm(cardinality_perm, pneles); + perm_peles = (uint8_t*) malloc(pneles * fe_bytes); + for(i = 0; i < pneles; i++) { + memcpy(perm_peles + cardinality_perm[i] * fe_bytes, peles + i * fe_bytes, fe_bytes); + } + memcpy(peles, perm_peles, fe_bytes * pneles); + free(cardinality_perm); + free(perm_peles); + } + + /* Import and Encrypt elements again */ + ectx.eles.input = peles; + ectx.eles.output = peles; + ectx.eles.nelements = pneles; + ectx.eles.inbytelen = fe_bytes; + ectx.eles.outbytelen = fe_bytes; + ectx.ectx.exponent = exponent; + ectx.ectx.sample = false; + +#ifdef DEBUG + cout << "Encrypting partners elements" << endl; +#endif + run_task(ntasks, ectx, encrypt); + + /* Hash elements */ + phashes = (uint8_t*) malloc(sizeof(uint8_t) * pneles * hash_bytes); + + ectx.eles.input = peles; + ectx.eles.output = phashes; + ectx.eles.nelements = pneles; + ectx.eles.inbytelen = fe_bytes; + ectx.eles.outbytelen = hash_bytes; + ectx.hctx.symcrypt = crypt_env; + +#ifdef DEBUG + cout << "Hashing elements" << endl; +#endif + run_task(ntasks, ectx, hash); + +#ifdef DEBUG + cout << "Exchanging hashes" << endl; +#endif + + if(role == SERVER) { + sndbufsize = pneles * hash_bytes; + rcvbufsize = 0; + } else { + sndbufsize = 0; + rcvbufsize = neles * hash_bytes; + } + + snd_and_rcv(phashes, sndbufsize, hashes, rcvbufsize, tmpsock); + +#ifdef DEBUG + cout << "Finding intersection" << endl; +#endif + if(role == SERVER) { + intersect_size = 0; + } else { + intersect_size = find_intersection(elements, result, elebytelen, hashes, + neles, phashes, pneles, hash_bytes, perm); + } + +#ifdef DEBUG + cout << "Free-ing allocated memory" << endl; +#endif + free(perm); + free(permeles); + free(encrypted_eles); + free(hashes); + free(peles); + free(phashes); + + return intersect_size; +} + + + +uint32_t find_intersection(uint8_t* elements, uint8_t** result, uint32_t elebytelen, uint8_t* hashes, + uint32_t neles, uint8_t* phashes, uint32_t npeles, uint32_t hashbytelen, uint32_t* perm) { + + uint32_t* invperm = (uint32_t*) malloc(sizeof(uint32_t) * neles); + uint32_t* matches = (uint32_t*) malloc(sizeof(uint32_t) * neles); + uint64_t* tmpinbuf; + uint64_t* tmpval; + uint32_t size_intersect, i, intersect_ctr, nextrakeysstored, j; + bool success; + + + nextrakeysstored = ceil_divide(hashbytelen, sizeof(uint64_t))-1; + cout << "hashbytelen = " << hashbytelen << ", nextrakeysstored = " << nextrakeysstored << endl; + + //store all the extra keys as well as the + tmpinbuf = (uint64_t*) malloc(neles * (nextrakeysstored+1) * sizeof(uint64_t)); + + for(i = 0; i < neles; i++) { + memcpy(tmpinbuf + i * (nextrakeysstored+1), hashes + i * hashbytelen + sizeof(uint64_t), + nextrakeysstored*sizeof(uint64_t)); + tmpinbuf[perm[i] * (nextrakeysstored+1) + nextrakeysstored] = (uint64_t) i; + //invperm[perm[i]] = i; + } + + + GHashTable *map= g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, NULL); + for(i = 0; i < neles; i++) { + // g_hash_table_insert(map,(void*) ((uint64_t*) &(hashes[i*hashbytelen])), &(invperm[i])); + g_hash_table_insert(map,(void*) ((uint64_t*) &(hashes[i*hashbytelen])), &(tmpinbuf[i*(nextrakeysstored+1)])); + } + + for(i = 0, intersect_ctr = 0; i < npeles; i++) { + success = true; + if(g_hash_table_lookup_extended(map, (void*) ((uint64_t*) &(phashes[i*hashbytelen])), + NULL, (void**) &tmpval)) { + for(j = 0; j < nextrakeysstored; j++) { + if(((uint64_t*) &(phashes[i*hashbytelen]))[j+1] != tmpval[j]) { + success = false; + } + } + if(success) { + matches[intersect_ctr] = tmpval[nextrakeysstored]; + intersect_ctr++; + } + } + } + + size_intersect = intersect_ctr; + + //result = (uint8_t**) malloc(sizeof(uint8_t*)); + (*result) = (uint8_t*) malloc(sizeof(uint8_t) * size_intersect * elebytelen); + for(i = 0; i < size_intersect; i++) { + memcpy((*result) + i * elebytelen, elements + matches[i] * elebytelen, elebytelen); + } + + free(invperm); + free(matches); + free(tmpinbuf); + return size_intersect; +} + +void snd_and_rcv(uint8_t* snd_buf, uint32_t snd_bytes, uint8_t* rcv_buf, uint32_t rcv_bytes, CSocket* sock) { + pthread_t snd_task; + bool created, joined; + snd_ctx ctx; + + //Start new sender thread + ctx.sock = sock; + ctx.snd_buf = snd_buf; + ctx.snd_bytes = snd_bytes; + created = !pthread_create(&snd_task, NULL, send_data, (void*) &(ctx)); + + //receive + sock->Receive(rcv_buf, rcv_bytes); + assert(created); + + joined = !pthread_join(snd_task, NULL); + assert(joined); + +} + +void run_task(uint32_t nthreads, task_ctx context, void* (*func)(void*) ) { + task_ctx* contexts = (task_ctx*) malloc(sizeof(task_ctx) * nthreads); + pthread_t* threads = (pthread_t*) malloc(sizeof(pthread_t) * nthreads); + uint32_t i, neles_thread, electr, neles_cur; + bool created, joined; + + neles_thread = ceil_divide(context.eles.nelements, nthreads); + for(i = 0, electr = 0; i < nthreads; i++) { + neles_cur = min(context.eles.nelements - electr, neles_thread); + memcpy(contexts + i, &context, sizeof(task_ctx)); + contexts[i].eles.nelements = neles_cur; + contexts[i].eles.input = context.eles.input + (context.eles.inbytelen * electr); + contexts[i].eles.output = context.eles.output + (context.eles.outbytelen * electr); + electr += neles_cur; + } + + for(i = 0; i < nthreads; i++) { + created = !pthread_create(threads + i, NULL, func, (void*) &(contexts[i])); + } + + assert(created); + + for(i = 0; i < nthreads; i++) { + joined = !pthread_join(threads[i], NULL); + } + + assert(joined); + + free(threads); + free(contexts); +} + + +void *encrypt(void* context) { +#ifdef DEBUG + cout << "Encryption task started" << endl; +#endif + pk_crypto* field = ((task_ctx*) context)->ectx.field; + element_ctx electx = ((task_ctx*) context)->eles; + num* e = ((task_ctx*) context)->ectx.exponent; + fe* tmpfe = field->get_fe(); + uint8_t *inptr=electx.input, *outptr=electx.output; + uint32_t i; + + + for(i = 0; i < electx.nelements; i++, inptr+=electx.inbytelen, outptr+=electx.outbytelen) { + if(((task_ctx*) context)->ectx.sample) { + tmpfe->sample_fe_from_bytes(inptr, electx.inbytelen); + //cout << "Mapped " << ((uint32_t*) inptr)[0] << " to "; + } else { + tmpfe->import_from_bytes(inptr); + } + tmpfe->set_pow(tmpfe, e); + //tmpfe->print(); + tmpfe->export_to_bytes(outptr); + } + + return 0; +} + +void *hash(void* context) { +#ifdef DEBUG + cout << "Hashing thread started" << endl; +#endif + crypto* crypt_env = ((task_ctx*) context)->hctx.symcrypt; + element_ctx electx = ((task_ctx*) context)->eles; + + uint8_t *inptr=electx.input, *outptr=electx.output; + uint32_t i; + + + for(i = 0; i < electx.nelements; i++, inptr+=electx.inbytelen, outptr+=electx.outbytelen) { + crypt_env->hash(outptr, electx.outbytelen, inptr, electx.inbytelen); + } + return 0; +} + +void *send_data(void* context) { + snd_ctx *ctx = (snd_ctx*) context; + ctx->sock->Send(ctx->snd_buf, ctx->snd_bytes); + return 0; +} + + + + +void print_dh_psi_usage() { + cout << "Usage: ./dhpsi [0 (server)/1 (client)] [num_elements] " << + "[element_byte_length] [sym_security_bits] [server_ip] [server_port]" << endl; + cout << "Program exiting" << endl; + exit(0); +} diff --git a/src/pk-based/dh-psi.h b/src/pk-based/dh-psi.h new file mode 100644 index 0000000..949171c --- /dev/null +++ b/src/pk-based/dh-psi.h @@ -0,0 +1,69 @@ +/* + * dh-psi.h + * + * Created on: Jul 9, 2014 + * Author: mzohner + */ + +#ifndef DH_PSI_H_ +#define DH_PSI_H_ + + +#include "../util/typedefs.h" +#include "../util/connection.h" +#include "../util/crypto/crypto.h" +#include "../util/crypto/pk-crypto.h" +#include + + +struct element_ctx { + uint32_t nelements; + uint32_t inbytelen; + uint8_t* input; + uint32_t outbytelen; + uint8_t* output; +}; + +struct encrypt_ctx { + num* exponent; + pk_crypto* field; + bool sample; +}; + +struct hash_ctx { + crypto* symcrypt; +}; + +struct task_ctx { + element_ctx eles; + union { + hash_ctx hctx; + encrypt_ctx ectx; + }; +}; + +struct snd_ctx { + uint8_t* snd_buf; + uint32_t snd_bytes; + CSocket* sock; +}; + + +void print_dh_psi_usage(); +uint32_t dhpsi(role_type role, uint32_t neles, uint32_t pneles, uint32_t elebytelen, uint8_t* elements, + uint8_t** result, crypto* crypt_env, CSocket* sock, uint32_t ntasks, bool cardinality=false, + field_type ftype=ECC_FIELD); +void run_task(uint32_t nthreads, task_ctx context, void* (*func)(void*) ); +void permute(uint32_t nelements, uint32_t bytelen, uint8_t* elements, uint8_t* result, uint32_t* perm); +uint32_t find_intersection(uint8_t* elements, uint8_t** result, uint32_t elebytelen, uint8_t* hashes, + uint32_t neles, uint8_t* phashes, uint32_t peles, uint32_t hashbytelen, uint32_t* perm); +void snd_and_rcv(uint8_t* snd_buf, uint32_t snd_bytes, uint8_t* rcv_buf, uint32_t rcv_bytes, CSocket* sock); +void *encrypt(void* context); +void *hash(void* context); +void *send_data(void* context); + + + + + +#endif /* DH_PSI_H_ */ diff --git a/src/thirdparty/shpsi.cpp b/src/thirdparty/shpsi.cpp new file mode 100644 index 0000000..a813444 --- /dev/null +++ b/src/thirdparty/shpsi.cpp @@ -0,0 +1,329 @@ +#include "shpsi.h" + + + +/*int32_t main(int32_t argc, char** argv) { + uint32_t pid, nclients, nelements, elebytelen, symsecbits; + uint8_t *elements, *intersection; + const char* address; + uint16_t port; + timeval begin, end; + + if(argc < 2) { + print_sh_psi_usage(); + } else { + pid = atoi(argv[1]); + if((pid == 0 && argc < 5) || (pid > 0 && argc < 6)) print_sh_psi_usage(); + } + + if(pid == 0) { // Play as server + nclients = atoi(argv[2]); + address = argv[3]; + port = (uint16_t) atoi(argv[4]); + server_routine(nclients, address, port); + } else { // Play as client + nelements = atoi(argv[2]); + elebytelen = atoi(argv[3]); + symsecbits = atoi(argv[4]); + address = argv[5]; + port = atoi(argv[6]); + elements = (uint8_t*) malloc(sizeof(uint8_t) * elebytelen * nelements); + crypto crypto(symsecbits); + crypto.gen_rnd(elements, elebytelen * nelements); + +#ifdef DEBUG + //Load some dummy-values + for(uint32_t i = 0; i < nelements; i++) { + ((uint32_t*) elements)[i] = i+(nelements/pid); + } +#endif + gettimeofday(&begin, NULL); + client_routine(nelements, elebytelen, elements, &intersection, symsecbits, address, port); + gettimeofday(&end, NULL); + cout << "Computing the intersection took " << getMillies(begin, end) << " ms" << endl; + } + cout << "Program execution finished" << endl; + return 0; +}*/ + +void server_routine(uint32_t nclients, CSocket* socket, bool cardinality) { + //cout << "Starting server for " << nclients << " clients on address " << address << ":" << port << endl; + + CSocket* sockfds = socket;//(CSocket*) malloc(sizeof(CSocket) * nclients); + uint32_t* neles = (uint32_t*) malloc(sizeof(uint32_t) * nclients); + uint8_t** csets = (uint8_t**) malloc(sizeof(uint8_t*) * nclients); + uint8_t* intersect; + uint32_t temp, maskbytelen, intersectsize, minset, i; + +#ifndef BATCH + cout << "Connections with all " << nclients << " clients established" << endl; +#endif + + /* Receive the input sizes and bit lengths for all clients */ + for(i = 0; i < nclients; i++) { + sockfds[i].Receive(neles+i, sizeof(uint32_t)); + sockfds[i].Receive(&temp, sizeof(uint32_t)); + if(i == 0) { maskbytelen = temp; minset = neles[i];} + if(neles[i] < minset) minset = neles[i]; + assert(maskbytelen == temp); +#ifndef BATCH + cout << "Client " << i << " holds " << neles[i] << " elements of length " << (temp * 8) << "-bit" << endl; +#endif + } +#ifndef BATCH + cout <<"Receiving the client's elements" << endl; +#endif + /* Allocate sufficient size for the intersecting elements */ + intersect = (uint8_t*) malloc(sizeof(uint8_t*) * minset * maskbytelen); + + /* Receive the permuted and masked sets of all clients */ + for(i = 0; i < nclients; i++) { + temp = sizeof(uint8_t) * neles[i] * maskbytelen; + csets[i] = (uint8_t*) malloc(temp); + sockfds[i].Receive(csets[i], temp); + } +#ifndef BATCH + cout << "Computing intersection for the clients" << endl; +#endif + /* Compute Intersection */ + intersectsize = compute_intersection(nclients, neles, csets, intersect, maskbytelen); +#ifndef BATCH + cout << "sending all " << intersectsize << " intersecting elements to the clients" << endl; +#endif + /* Send the intersection size and intersecting elements to all clients */ + for(i = 0; i < nclients; i++) { + sockfds[i].Send(&intersectsize, sizeof(uint32_t)); + if(!cardinality) + sockfds[i].Send(intersect, intersectsize * maskbytelen); + } + + /* Cleanup */ + free(neles); +} + +/* + * compute the intersection using a hash table - is optimized for the two-party case, + * for the n-party case a BF-based approach makes more sense. + */ +//TODO currently only works for 128 bit masks +uint32_t compute_intersection(uint32_t nclients, uint32_t* neles, uint8_t** csets, uint8_t* intersect, uint32_t entrybytelen) { + // Create the GHashTable + GHashTable *map = NULL, *tmpmap = NULL; + GHashTableIter iter; + timeval begin, end; + + map = g_hash_table_new_full( + g_int64_hash, g_int64_equal, + NULL, // no cleanup for key + NULL // cleanup value + ); + + uint32_t i, j, intersectsize, ctr = 0; + uint64_t* tmpval = (uint64_t*) malloc(sizeof(uint64_t)); + uint64_t* tmpkey = (uint64_t*) malloc(sizeof(uint64_t)); +#ifndef BATCH + cout << "Inserting the items into the hash table " << endl; +#endif + gettimeofday(&begin, NULL); + for(i=0;iSend((uint8_t*) &neles, sizeof(uint32_t)); + sockfd->Send((uint8_t*) &maskbytelen, sizeof(uint32_t)); + + + perm = mask_and_permute_elements(neles, elebytelen, elements, maskbytelen, masks, crypt->get_seclvl().symbits, crypt); + + sockfd->Send(masks, maskbytelen * neles); + + if(!cardinality) { + for(i = 0; i < neles; i++) { + invperm[perm[i]] = i; + } + + map= g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, NULL); + for(i = 0; i < neles; i++) { + g_hash_table_insert(map,(void*) &((uint64_t*)masks)[2*i], &(invperm[i])); + } + } + + + sockfd->Receive(&intersectsize, sizeof(uint32_t)); + if(!cardinality) { + sockfd->Receive(intersect, maskbytelen * intersectsize); + +#ifdef DEBUG + cout << "The intersection contains " << intersectsize << " elements: " << endl; + for(i = 0; i < intersectsize; i++) { + cout << (hex) << ((uint64_t*)intersect)[2*i] << " " << ((uint64_t*)intersect)[2*i+1] << (dec) << endl; + } +#endif + + *result = (uint8_t*) malloc(elebytelen * intersectsize); + //uint8_t* tmpbuf = (uint8_t*) malloc(maskbytelen); + for(i = 0; i < intersectsize; i++) { + g_hash_table_lookup_extended(map, (void*) &(((uint64_t*)intersect)[2*i]), NULL, (void**) &tmpval); + memcpy((*result) + i * elebytelen, elements + tmpval[0] * elebytelen, elebytelen); + //crypto.decrypt(tmpbuf, intersect+i*maskbytelen, maskbytelen); + //memcpy((*result) + i * elebytelen, tmpbuf, elebytelen); +#ifdef DEBUG + cout << ((uint32_t*) elements)[tmpval[0]] << ", "; +#endif + } +#ifdef DEBUG + cout << endl; +#endif + } + + free(perm); + free(invperm); + + return intersectsize; +} + + +void printKeyValue( gpointer key, gpointer value, gpointer userData ) { + uint64_t realKey = *((uint64_t*)key); + uint64_t realValue = *((uint64_t*)value); + + cout << (hex) << realKey << ": " << realValue << (dec) << endl; + return; +} + +uint32_t* mask_and_permute_elements(uint32_t neles, uint32_t elebytelen, uint8_t* + elements, uint32_t maskbytelen, uint8_t* masks, uint32_t symsecbits, crypto* crypto) { + uint32_t* perm = (uint32_t*) malloc(sizeof(uint32_t) * neles); + uint8_t* maskpermptr; + uint32_t i; + + //Get random permutation + crypto->gen_rnd_perm(perm, neles); + //crypto->seed_aes_enc(client_psk); + + //Hash and permute all elements + for(i = 0; i < neles; i++) { + //cout << "Performing encryption for " << i << "-th element " << ((uint32_t*) elements)[i] << ": "; + maskpermptr = masks + perm[i] * maskbytelen; + crypto->hash(maskpermptr, maskbytelen, elements + i * elebytelen, elebytelen); + //crypto->encrypt(maskpermptr, elements+i*elebytelen, elebytelen); + //cout <<(hex)<< ((uint64_t*) maskpermptr)[0] << ((uint64_t*) maskpermptr)[1] << (dec) << endl; +#ifdef DEBUG + cout << "Resulting hash for element " << ((uint32_t*)elements)[i] << ": " << (hex) << ((uint64_t*) maskpermptr)[0] << + " " << ((uint64_t*) maskpermptr)[1] << (dec) << endl; +#endif + } + + + //free(perm); + return perm; +} + +uint32_t ttppsi(role_type role, uint32_t neles, uint32_t elebytelen, uint8_t* elements, + uint8_t** intersection, crypto* crypt, CSocket* sockets, uint32_t nclients, bool cardinality) { + + if(role == 0) { //Start the server + //TODO maybe rerun infinitely + server_routine(nclients, sockets, cardinality); + return 0; + } else { //Start clients + return client_routine(neles, elebytelen, elements, intersection, crypt, sockets, cardinality); + } +} diff --git a/src/thirdparty/shpsi.exe b/src/thirdparty/shpsi.exe new file mode 100755 index 0000000000000000000000000000000000000000..267453980a12c43d2c5e3e2b71d1cf96ff802284 GIT binary patch literal 20100 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 WO8W2(eaZ#jV-Y +#include "../util/crypto/crypto.h" +#include "../util/socket.h" +#include "../util/typedefs.h" +#include "../util/connection.h" + + + +/* start both roles*/ +uint32_t ttppsi(role_type role, uint32_t neles, uint32_t elebytelen, uint8_t* elements, + uint8_t** intersection, crypto* crypt, CSocket* socket, uint32_t nclients = 0, bool cardinality=false); + +/* + * Params: + * neles: number of elements in the clients' set + * elebytelen: bytelength of the elements + * elements: byte pointer to the client's set + * intersection: a byte array that holds the intersection upon returning + * address: address of the server + * port: port that the server is listening on + * return: number of intersecting elements + */ +uint32_t client_routine(uint32_t neles, uint32_t elebytelen, uint8_t* elements, + uint8_t** intersection, crypto* crypt, CSocket* socket, bool cardinality); + +/* + * Mask and permute the elements using the pre-shared key + */ +uint32_t* mask_and_permute_elements(uint32_t neles, uint32_t elebytelen, uint8_t* + elements, uint32_t maskbytelen, uint8_t* masks, uint32_t symsecbits, crypto* crypto); + + +/* + * Params: + * nclients: number of participating clients + * address: address of the server + * port: port that the server is listening on + */ +void server_routine(uint32_t nclients, CSocket* socket, bool cardinality); + +uint32_t compute_intersection(uint32_t nclients, uint32_t* neles, uint8_t** csets, uint8_t* intersect, uint32_t entrybytelen); + +void printKeyValue( gpointer key, gpointer value, gpointer userData ); + +#endif /* SHPSI_H_ */ diff --git a/src/util/cbitvector.cpp b/src/util/cbitvector.cpp new file mode 100644 index 0000000..338954a --- /dev/null +++ b/src/util/cbitvector.cpp @@ -0,0 +1,680 @@ +/* +* BitVector.cpp +* +* Created on: May 6, 2013 +* Author: mzohner +*/ + +#include "cbitvector.h" + +/* Fill the bitvector with random values and pre-initialize the key to the seed-key*/ +void CBitVector::FillRand(int bits, crypto* crypt) +{ + InitRand(crypt); + FillRand(bits); +} + +/* Fill random values using the pre-defined AES key */ +void CBitVector::FillRand(int bits) +{ + if(bits > m_nByteSize<<3) + Create(bits); + + /*uint8_t buf[AES_BYTES]; + memset(buf, 0, AES_BYTES); + + for(int i = 0; i < (int) sizeof(int); i++) { + buf[i] = (cnt>>(8*i)) & 0xFF; + }*/ + + if(m_cCrypto== NULL) + { + cerr << "FillRand called without initializing AES key" << endl; + return; + } + + //int size = ceil_divide(bits, AES_BITS); + //unsigned long long* counter = (unsigned long long*) buf; + + m_cCrypto->gen_rnd(m_pBits, (bits>>3)); + + /*for(int i = 0; i < size; i++, counter[0]++, cnt++) + { + MPC_AES_ENCRYPT(m_nKey, m_pBits + i*AES_BYTES, buf); + }*/ + //cnt = (int) counter[0]; +} + +void CBitVector::Create(int numelements, int elementlength, crypto* crypt) +{ + CreateRand(numelements * elementlength, crypt); + m_nElementLength = elementlength; + m_nNumElements = numelements; + m_nNumElementsDimB = 1; +} + +void CBitVector::CreateRand(int bits, crypto* crypt) +{ + FillRand(bits, crypt); +} + +void CBitVector::Create(int bits) +{ + if(bits == 0) bits = DEFAULT_BITSIZE; + //cout << "creating " << bits << " sized vector" << endl; + if(m_nByteSize > 0 ) free(m_pBits); + m_nByteSize = ceil_divide(bits, 8); + m_pBits = (uint8_t*) calloc(m_nByteSize, sizeof(uint8_t));// * m_nSize); + //m_pBits = (uint8_t*) malloc(m_nSize * sizeof(uint8_t));// * m_nSize); + if(m_pBits==NULL) + { + cerr << "CBitVector: memory allocation not successful, requested " << m_nByteSize << " bytes" << endl; + exit(0); + } + m_nElementLength = 1; + m_nNumElements = m_nByteSize; + m_nNumElementsDimB = 1; + +} + +void CBitVector::Create(int numelements, int elementlength) +{ + Create(numelements * elementlength); + m_nElementLength = elementlength; + m_nNumElements = numelements; + m_nNumElementsDimB = 1; +} + +void CBitVector::Create(int numelementsDimA, int numelementsDimB, int elementlength) +{ + Create(numelementsDimA * numelementsDimB * elementlength); + m_nElementLength = elementlength; + m_nNumElements = numelementsDimA; + m_nNumElementsDimB = numelementsDimB; +} +void CBitVector::Create(int numelementsDimA, int numelementsDimB, int elementlength, crypto* crypt) +{ + CreateRand(numelementsDimA * numelementsDimB * elementlength, crypt); + m_nElementLength = elementlength; + m_nNumElements = numelementsDimA; + m_nNumElementsDimB = numelementsDimB; +} + + +void CBitVector::ResizeinBytes(int newSizeBytes) +{ + uint8_t* tBits = m_pBits; + int tSize = m_nByteSize; + + m_nByteSize = newSizeBytes; + m_pBits = new uint8_t[m_nByteSize]; + + memcpy(m_pBits, tBits, tSize); + + delete(tBits); +} + +void CBitVector::Copy(uint8_t* p, int pos, int len) +{ +//#ifdef ASSERT + if( pos+len > m_nByteSize) + { + if(m_pBits) + ResizeinBytes(pos+len); + else + { + CreateBytes(pos+len); + } + } +//#endif + memcpy (m_pBits+pos, p, len); +} + +//pos and len in bits +void CBitVector::SetBits(uint8_t* p, int pos, int len) +{ + + if(len < 1 || (pos+len) > (m_nByteSize << 3)) + return; + + if(len == 1) + { + SetBitNoMask(pos, *p); + return; + } + if(!((pos & 0x07) || (len & 0x07))) + { + + SetBytes(p, pos>>3, len>>3); + return; + } + int posctr = pos>>3; + int lowermask = pos & 7; + int uppermask = 8 - lowermask; + + int i; + uint8_t temp; + for(i = 0; i < len / (sizeof(uint8_t)*8); i++, posctr++) + { + temp = p[i]; + m_pBits[posctr] = (m_pBits[posctr] & RESET_BIT_POSITIONS[lowermask]) | ((temp << lowermask) & 0xFF); + m_pBits[posctr+1] = (m_pBits[posctr+1] & RESET_BIT_POSITIONS_INV[uppermask]) | (temp >> uppermask); + //cout << "Iteration for whole byte done" << endl; + } + int remlen = len & 0x07; + if(remlen) + { + temp = p[i] & RESET_BIT_POSITIONS[remlen]; + //cout << "temp = " << (unsigned int) temp << endl; + if(remlen <= uppermask) + { + //cout << "Setting " << remlen << " lower bits with lowermask = " << lowermask << ", and temp " << (unsigned int) temp << endl; + m_pBits[posctr] = (m_pBits[posctr] & (~(((1<> lowermask) & 0xFF; + //cout << "p[i] = " << (unsigned int) p[i] << endl; + } + else + { + //cout << "Getting upper" << endl; + //cout << "Getting " << remlen << " upper bits with lowermask = " << lowermask << ", " << (unsigned int) m_pBits[posctr] << ", and uppermask = " + // << uppermask << ", " << (unsigned int) m_pBits[posctr+1] << endl; + p[i] = ((m_pBits[posctr] & GET_BIT_POSITIONS[lowermask]) >> lowermask) & 0xFF; + p[i] |= (m_pBits[posctr+1] & (((1<<(remlen-uppermask))-1))) << uppermask; + //cout << "p[i] = " << (unsigned int) p[i] << endl; + } + } +} + +void CBitVector::XORBytesReverse(uint8_t* p, int pos, int len) +{ + uint8_t* src = p; + uint8_t* dst = m_pBits+pos; + uint8_t* lim = dst + len; + while(dst != lim) + { + *dst++ ^= REVERSE_BYTE_ORDER[*src++]; + } +} + +//XOR bits given an offset on the bits for p which is not necessarily divisible by 8 +void CBitVector::XORBitsPosOffset(uint8_t* p, int ppos, int pos, int len) +{ + for(int i = pos, j = ppos; j < ppos + len; i++, j++) + { + m_pBits[i/8] ^= (((p[j/8] & (1 << (j%8))) >> j % 8) << i % 8); + } +} + +void CBitVector::XORBits(uint8_t* p, int pos, int len) { + XORBits(p, (uint64_t) pos, (uint64_t) len); +} + +void CBitVector::XORBits(uint8_t* p, uint64_t pos, uint64_t len) +{ + if(len < 1 || (pos+len) > ((uint64_t) m_nByteSize) << 3) + { + //cout << "m_nSize = " << m_nSize << ", pos+len = " << (pos + len)/8 << endl; + return; + } + if(len == 1) + { + XORBitNoMask(pos, *p); + return; + } + if(!((pos & 0x07) || (len & 0x07))) + { + XORBytes(p, pos>>3, len>>3); + return; + } + uint64_t posctr = pos>>3; + uint64_t lowermask = pos & 7; + uint64_t uppermask = 8 - lowermask; + + uint64_t i; + uint8_t temp; + for(i = 0; i < len / (sizeof(uint8_t)*8); i++, posctr++) + { + temp = p[i]; + m_pBits[posctr] ^= ((temp << lowermask) & 0xFF); + m_pBits[posctr+1] ^= (temp >> uppermask); + //cout << "Iteration for whole byte done" << endl; + } + uint64_t remlen = len & 0x07; + if(remlen) + { + temp = p[i] & RESET_BIT_POSITIONS[remlen]; + //cout << "temp = " << (unsigned int) temp << endl; + if(remlen <= uppermask) + { + //cout << "Setting " << remlen << " lower bits with lowermask = " << lowermask << ", and temp " << (unsigned int) temp << endl; + m_pBits[posctr] ^= ((temp << lowermask) & 0xFF); + } + else + { + //cout << "Setting " << remlen << " bits with lowermask = " << lowermask << ", uppermask = " << uppermask << ", and temp = " << (unsigned int) temp << endl; + m_pBits[posctr] ^= ((temp << lowermask) & 0xFF); + m_pBits[posctr+1] ^= (temp >> uppermask); + } + } +} + +void CBitVector::ORByte(int pos, uint8_t p) +{ + m_pBits[pos] |= p; +} + + +//optimized bytewise for set operation +void CBitVector::GetBytes(uint8_t* p, uint64_t pos, uint64_t len) +{ + + uint8_t* src = m_pBits + pos; + uint8_t* dst = p; + //Do many operations on REGSIZE types first and then (if necessary) use bytewise operations + GetBytes((REGSIZE*) dst, (REGSIZE*) src, ((REGSIZE*) dst ) + (len>>SHIFTVAL)); + dst += ((len >> SHIFTVAL) << SHIFTVAL); + src += ((len >> SHIFTVAL) << SHIFTVAL); + GetBytes(dst, src, dst+(len & ((1< void CBitVector::GetBytes(T* dst, T* src, T* lim) +{ + while(dst != lim) + { + *dst++ = *src++; + } +} + +void CBitVector::XORBytes(uint8_t* p, uint64_t pos, uint64_t len) +{ + if((pos + len) > (uint64_t) m_nByteSize) { + cerr << "Error Trying to xor at byte position: " << pos << " with len = " << len << " with m_nSize = " << m_nByteSize << endl; + exit(0); + } + + uint8_t* dst = m_pBits + pos; + uint8_t* src = p; + //Do many operations on REGSIZE types first and then (if necessary) use bytewise operations + XORBytes((REGSIZE*) dst, (REGSIZE*) src, ((REGSIZE*) dst ) + (len>>SHIFTVAL)); + dst += ((len >> SHIFTVAL) << SHIFTVAL); + src += ((len >> SHIFTVAL) << SHIFTVAL); + XORBytes(dst, src, dst+(len & ((1< void CBitVector::XORBytes(T* dst, T* src, T* lim) +{ + while(dst != lim) + { + *dst++ ^= *src++; + } +} + +void CBitVector::XORRepeat(uint8_t* p, int pos, int len, int num) +{ + unsigned short* dst = (unsigned short*) (m_pBits + pos); + unsigned short* src = (unsigned short*) p; + unsigned short* lim = (unsigned short*) (m_pBits+pos+len); + for(int i = num; dst != lim; ) + { + *dst++ ^= *src++; + if(!(--i)) + { + src = (unsigned short*) p; + i = num; + } + } +} + +//optimized bytewise for set operation +void CBitVector::SetBytes(uint8_t* p, int pos, int len) +{ + + uint8_t* dst = m_pBits + pos; + uint8_t* src = p; + //REGSIZE rem = SHIFTVAL-1; + //Do many operations on REGSIZE types first and then (if necessary) use bytewise operations + SetBytes((REGSIZE*) dst, (REGSIZE*) src, ((REGSIZE*) dst ) + (len>>SHIFTVAL)); + dst += ((len >> SHIFTVAL) << SHIFTVAL); + src += ((len >> SHIFTVAL) << SHIFTVAL); + SetBytes(dst, src, dst+(len & ((1< void CBitVector::SetBytes(T* dst, T* src, T* lim) +{ + while(dst != lim) + { + *dst++ = *src++; + } +} + +//optimized bytewise for AND operation +void CBitVector::ANDBytes(uint8_t* p, int pos, int len) +{ + + uint8_t* dst = m_pBits + pos; + uint8_t* src = p; + //Do many operations on REGSIZE types first and then (if necessary) use bytewise operations + ANDBytes((REGSIZE*) dst, (REGSIZE*) src, ((REGSIZE*) dst ) + (len>>SHIFTVAL)); + dst += ((len >> SHIFTVAL) << SHIFTVAL); + src += ((len >> SHIFTVAL) << SHIFTVAL); + ANDBytes(dst, src, dst+(len & ((1< void CBitVector::ANDBytes(T* dst, T* src, T* lim) +{ + while(dst != lim) + { + *dst++ &= *src++; + } +} + +void CBitVector::SetXOR(uint8_t* p, uint8_t* q, int pos, int len) +{ + Copy(p, pos, len); + XORBytes(q, pos, len); +} + +void CBitVector::SetAND(uint8_t* p, uint8_t* q, int pos, int len) +{ + Copy(p, pos, len); + ANDBytes(q, pos, len); +} + +void CBitVector:: Print(int fromBit, int toBit) { + int to = toBit > (m_nByteSize << 3)? (m_nByteSize << 3) : toBit; + for (int i = fromBit; i < to; i++) { + cout << (unsigned int) GetBitNoMask(i); + } + cout << endl; +} + +void CBitVector::PrintHex(int fromByte, int toByte) { + for (int i = fromByte; i < toByte; i++) { + cout << setw(2) << setfill('0') << (hex) << ((unsigned int) m_pBits[i]); + } + cout << (dec) << endl; +} + +void CBitVector::PrintHex() +{ + for (int i = 0; i < m_nByteSize; i++) + { + cout << setw(2) << setfill('0') << (hex) << ((unsigned int) m_pBits[i]); + } + cout << (dec) << endl; +} + +void CBitVector::PrintBinaryMasked(int from, int to) +{ + for (int i = from; i < to; i++) + { + cout << (unsigned int) GetBit(i); + } + cout << endl; +} + +void CBitVector::PrintContent() +{ + if(m_nElementLength == 1) + { + PrintHex(); + return; + } + if(m_nNumElementsDimB == 1) + { + for(int i = 0; i < m_nNumElements; i++) + { + cout << Get(i) << ", "; + } + cout << endl; + } + else + { + for(int i = 0; i < m_nNumElements; i++) + { + cout << "("; + for(int j = 0; j < m_nNumElementsDimB-1; j++) + { + cout << Get2D(i, j) << ", "; + } + cout << Get2D(i, m_nNumElementsDimB-1); + cout << "), "; + } + cout << endl; + } +} + + +BOOL CBitVector::IsEqual(CBitVector& vec) +{ + if(vec.GetSize() != m_nByteSize) + return false; + + uint8_t* ptr = vec.GetArr(); + for(int i = 0; i < m_nByteSize; i++) + { + if(ptr[i] != m_pBits[i]) + return false; + } + return true; +} + +BOOL CBitVector::IsEqual(CBitVector& vec, int from, int to) +{ + if(vec.GetSize() * 8 < to || m_nByteSize * 8 < to || from > to) + return false; + + for(int i = from; i < to; i++) { + if(vec.GetBit(i) != GetBit(i) ) + return false; + } + return true; +} + +void CBitVector::XOR_no_mask(int p, int bitPos, int bitLen) +{ + if(!bitLen) + return; + + int i = bitPos>>3, j = 8-(bitPos&0x7), k; + + m_pBits[i++] ^= (GetIntBitsFromLen(p, 0, min(j, bitLen))<<(8-j)) & 0xFF; + + for (k=bitLen-j; k > 0; k-=8, i++, j+=8) + { + m_pBits[i] ^= GetIntBitsFromLen(p, j, min(8, k)); + } +} + + +unsigned int CBitVector::GetInt(int bitPos, int bitLen) +{ + int ret = 0, i = bitPos>>3, j = (bitPos&0x7), k; + ret = (m_pBits[i++] >> (j)) & (GetMask(min(8, bitLen))); + if (bitLen == 1) + return ret; + //cout << "MpBits: " << (unsigned int) m_pBits[i-1] << ", ret: " << ret << endl; + j = 8-j; + for(k = bitLen - j; i<(bitPos + bitLen+7)/8-1; i++, j+=8, k-=8) + { + //ret |= REVERSE_BYTE_ORDER[m_pBits[i+pos]] << (i*8); + ret |= m_pBits[i] << j; + //cout << "MpBits: " <<(unsigned int) m_pBits[i] << ", ret: " << ret << ", j: " << j << endl; + //m_pBits[i+pos] = ( ((REVERSE_NIBBLE_ORDER[(p>>((2*i)*4))&0xF] << 4) | REVERSE_NIBBLE_ORDER[(p>>((2*i+1)*4))&0xF]) & 0xFF);//((p>>((2*i+1)*4))&0xF) | ((p>>((2*i)*4))&0xF) & 0xFF; + } + ret |= (m_pBits[i] & SELECT_BIT_POSITIONS[k]) << j; //for the last execution 0<=k<=8 + //cout << "MpBits: " <<(unsigned int) m_pBits[i] << ", ret: " << ret << ", k = " << k << ", selects: " << (unsigned int) SELECT_BIT_POSITIONS[k] << ", j: " << j << endl; + return ret; +} + +void CBitVector::SimpleTranspose(int rows, int columns) +{ + CBitVector temp(rows * columns); + temp.Copy(m_pBits, 0, rows * columns / 8); + for(int i = 0; i < rows; i++) + { + for(int j = 0; j < columns; j++) + { + SetBit(j * rows + i, temp.GetBit(i * columns + j)); + } + } +} + + +//A transposition algorithm for bit-matrices of size 2^i x 2^i +void CBitVector::EklundhBitTranspose(int rows, int columns) +{ + REGISTER_SIZE* rowaptr;//ptr; + REGISTER_SIZE* rowbptr; + REGISTER_SIZE temp_row; + REGISTER_SIZE mask; + REGISTER_SIZE invmask; + REGISTER_SIZE* lim; + + lim = (REGISTER_SIZE*) m_pBits + ceil_divide(rows * columns, 8); + + int offset = (columns >> 3) / sizeof(REGISTER_SIZE); + int numiters = ceil_log2(rows); + int srcidx = 1, destidx; + int rounds; + int p; + + //If swapping is performed on bit-level + for(int i = 0, j; i < LOG2_REGISTER_SIZE; i++, srcidx*=2) + { + destidx = offset*srcidx; + rowaptr = (REGISTER_SIZE*) m_pBits; + rowbptr = rowaptr + destidx;//ptr = temp_mat; + + //cout << "numrounds = " << numrounds << " iterations: " << LOG2_REGISTER_SIZE << ", offset = " << offset << ", srcidx = " << srcidx << ", destidx = " << destidx << endl; + //Preset the masks that are required for bit-level swapping operations + mask = TRANSPOSITION_MASKS[i]; + invmask = ~mask; + + //If swapping is performed on byte-level reverse operations due to little-endian format. + rounds = rows / (srcidx * 2); + if(i > 2) { + for(int j = 0; j < rounds; j++) { + for(lim = rowbptr+destidx;rowbptr < lim; rowaptr++, rowbptr++) { + temp_row = *rowaptr; + *rowaptr = ((*rowaptr & mask) ^ ((*rowbptr & mask)<>srcidx)); + } + rowaptr+=destidx; + rowbptr+=destidx; + } + } + else { + for(int j = 0; j < rounds; j++) { + for(lim = rowbptr+destidx;rowbptr < lim; rowaptr++, rowbptr++) { + temp_row = *rowaptr; + *rowaptr = ((*rowaptr & invmask) ^ ((*rowbptr & invmask)>>srcidx)); + *rowbptr = ((*rowbptr & mask) ^ ((temp_row & mask)<= swapoffset)) { + temp_row = *rowaptr; + *rowaptr = *rowbptr; + *rowbptr = temp_row; + } + } + rowaptr+=destidx; + rowbptr+=destidx; + } + } + + if(columns > rows) { + uint8_t* tempvec = (uint8_t*) malloc((rows*columns)/8); + memcpy(tempvec, m_pBits, ((rows/8)*columns)); + + rowaptr = (REGISTER_SIZE*) m_pBits; + int rowbytesize = rows/8; + int rowregsize = rows/(sizeof(REGISTER_SIZE) * 8); + for(int i = 0; i +#include +#include + +#define DEFAULT_BITSIZE 128 + +static const uint8_t REVERSE_NIBBLE_ORDER[16] = + {0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF}; + +static const uint8_t REVERSE_BYTE_ORDER[256] = + {0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, \ + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, \ + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, \ + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, \ + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, \ + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, \ + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, \ + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, \ + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, \ + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, \ + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, \ + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, \ + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, \ + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, \ + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, \ + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF + }; + +static const uint8_t RESET_BIT_POSITIONS[9] = + {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}; + +static const uint8_t RESET_BIT_POSITIONS_INV[9] = + {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF}; + +static const uint8_t GET_BIT_POSITIONS[9] = + {0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00}; + +static const uint8_t GET_BIT_POSITIONS_INV[9] = + {0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00}; + +static const int INT_MASK[8] = + {0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01}; + +static const int FIRST_MASK_SHIFT[8] = + {0xFF00, 0x7F80, 0x3FC0, 0x1FE0, 0x0FF0, 0x07F8, 0x03FC, 0x01FE}; + +static const uint8_t MASK_BIT[8] = + {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}; + +static const uint8_t BIT[8] = + {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; + +static const uint8_t CMASK_BIT[8] = + {0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe}; + +static const uint8_t C_BIT[8] = + {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F}; + +static const uint8_t MASK_SET_BIT[2][8] = + {{0,0,0,0,0,0,0,0},{0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}}; + +static const uint8_t MASK_SET_BIT_C[2][8] = + {{0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1},{0,0,0,0,0,0,0,0}}; + +static const uint8_t SET_BIT_C[2][8] = + {{0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80},{0,0,0,0,0,0,0,0}}; + +const uint8_t SELECT_BIT_POSITIONS[9] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}; + + +#ifdef MACHINE_SIZE_32 +static const REGISTER_SIZE TRANSPOSITION_MASKS[6] = + {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF}; +static const REGISTER_SIZE TRANSPOSITION_MASKS_INV[6] = + {0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000}; +#else + #ifdef MACHINE_SIZE_64 +static const REGISTER_SIZE TRANSPOSITION_MASKS[6] = + {0x5555555555555555, 0x3333333333333333, 0x0F0F0F0F0F0F0F0F, 0x00FF00FF00FF00FF, 0x0000FFFF0000FFFF, 0x00000000FFFFFFFF}; +static const REGISTER_SIZE TRANSPOSITION_MASKS_INV[6] = + {0xAAAAAAAAAAAAAAAA, 0xCCCCCCCCCCCCCCCC, 0xF0F0F0F0F0F0F0F0, 0xFF00FF00FF00FF00, 0xFFFF0000FFFF0000, 0xFFFFFFFF00000000}; + #else + #endif +#endif + +static const size_t SHIFTVAL = 3;//sizeof(REGSIZE); + +class CBitVector +{ +public: + CBitVector(){ Init(); } + CBitVector(int bits){ Init(); Create(bits);} + CBitVector(int bits, crypto* crypt){ Init(); CreateRand(bits, crypt);} + + void Init() {m_pBits = NULL; m_nByteSize = 0; m_cCrypto = NULL;} + + //~CBitVector(){ if(m_pBits) free(m_pBits); } + void delCBitVector(){ if(m_nByteSize > 0) free(m_pBits); m_nByteSize = 0; m_pBits = NULL; } + + /* Use this function to initialize the AES round key which can then be used to generate further random bits */ + void InitRand(crypto* crypt) { m_cCrypto = crypt; } + + /* Fill the bitvector with random values and pre-initialize the key to the seed-key*/ + void FillRand(int bits, crypto* crypt); + + /* Fill random values using the pre-defined AES key */ + void FillRand(int bits); + + /** + * Create Operations + */ + + //Create in bits and bytes + void Create(int bits); + void CreateBytes(int bytes) { Create(bytes<<3); } + void CreateZeros(int bits) { Create(bits); memset(m_pBits, 0, m_nByteSize); } + //Create and fill with random values + void CreateRand(int bits, crypto* crypt); + //Create an abstract array of values + void Create(int numelements, int elementlength); + void Create(int numelements, int elementlength, crypto* crypt); + //Create an abstract two-dimensional array of values + void Create(int numelementsDimA, int numelementsDimB, int elementlength); + void Create(int numelementsDimA, int numelementsDimB, int elementlength, crypto* crypt); + + + /* + * Management operations + */ + void ResizeinBytes(int newSizeBytes); + void Reset() { memset(m_pBits, 0, m_nByteSize); } + void ResetFromTo(int frombyte, int tobyte) { memset(m_pBits+frombyte, 0, tobyte-frombyte); } + void SetToOne() { memset(m_pBits, 0xFF, m_nByteSize); } + int GetSize(){ return m_nByteSize; } + BOOL IsEqual(CBitVector& vec); + BOOL IsEqual(CBitVector& vec, int from, int to); + + void SetElementLength(int elelen) { m_nElementLength = elelen; } + + + /* + * Copy operations + */ + + void Copy(CBitVector& vec) { Copy(vec.GetArr(), 0, vec.GetSize()); } + void Copy(CBitVector& vec, int pos, int len) { Copy(vec.GetArr(), pos, len); } + void Copy(uint8_t* p, int pos, int len); + + + + void XOR_no_mask(int p, int bitPos, int bitLen); + unsigned int GetInt(int bitPos, int bitLen); + #define GetIntBitsFromLen(x, from, len) ( ( (x & ( ( (2<<(len))-1) << from )) >> from) & 0xFF) + #define GetMask(len) (( (1<<(len))-1)) + + void ORByte(int pos, uint8_t p); + + + /* + * Bitwise operations + */ + uint8_t GetBit(int idx) { return !!(m_pBits[idx>>3] & MASK_BIT[idx & 0x7]); } + void SetBit(int idx, uint8_t b) { m_pBits[idx>>3] = (m_pBits[idx>>3] & CMASK_BIT[idx & 0x7]) | MASK_SET_BIT_C[!(b&0x01)][idx & 0x7]; } + void XORBit(int idx, uint8_t b) { m_pBits[idx>>3] ^= MASK_SET_BIT_C[!(b&0x01)][idx & 0x7]; } + void ANDBit(int idx, uint8_t b) { if(!b) m_pBits[idx>>3] &= CMASK_BIT[idx & 0x7]; } + + //used to access bits in the regular order + uint8_t GetBitNoMask(uint64_t idx) { return !!(m_pBits[idx>>3] & BIT[idx & 0x7]); } + //uint8_t GetBitNoMask(int idx) { return GetBitNoMask((uint64_t) idx);}; + void SetBitNoMask(int idx, uint8_t b) { m_pBits[idx>>3] = (m_pBits[idx>>3] & C_BIT[idx & 0x7]) | SET_BIT_C[!(b&0x01)][idx & 0x7]; } + void XORBitNoMask(int idx, uint8_t b) { m_pBits[idx>>3] ^= SET_BIT_C[!(b&0x01)][idx & 0x7]; } + void ANDBitNoMask(int idx, uint8_t b) { if(!b) m_pBits[idx>>3] &= C_BIT[idx & 0x7]; } + + + /* + * Single byte operations + */ + void SetByte(int idx, uint8_t p) { m_pBits[idx] = p;} + uint8_t GetByte(int idx) { return m_pBits[idx]; } + void XORByte(int idx, uint8_t b) { m_pBits[idx] ^= b; } + void ANDByte(int idx, uint8_t b) { m_pBits[idx] &= b; } + + /* + * Get Operations + */ + void GetBits(uint8_t* p, uint64_t pos, uint64_t len); + //void GetBits(uint8_t* p, int pos, int len) {GetBits(p, (uint64_t) pos, (uint64_t) len);}; + //void GetBytes(uint8_t* p, int pos, int len) {GetBytes(p, (uint64_t) pos, (uint64_t) len); }; + void GetBytes(uint8_t* p, uint64_t pos, uint64_t len); + template void GetBytes(T* dst, T* src, T* lim); + //template T Get(int pos, int len); + template T Get(uint64_t pos, uint64_t len) + { + T val = 0; + GetBits((uint8_t*) &val, pos, len); + return val; + } + + /* + * Set Operations + */ + void SetBits(uint8_t* p, int pos, int len); + void SetBytes(uint8_t* p, int pos, int len); + template void SetBytes(T* dst, T* src, T* lim); + template void Set(T val, int pos, int len) { SetBits((uint8_t*) &val, pos, len); } + void SetBitsToZero(int bitpos, int bitlen); + + + /* + * XOR Operations + */ + void XORBytes(uint8_t* p, int pos, int len) {XORBytes(p, (uint64_t) pos, (uint64_t) len);}; + void XORBytes(uint8_t* p, uint32_t pos, uint32_t len) {XORBytes(p, (uint64_t) pos, (uint64_t) len);}; + void XORBytes(uint8_t* p, int len) { XORBytes(p, 0, len); } + void XORBytes(uint8_t* p, uint64_t pos, uint64_t len); + void XORVector(CBitVector &vec, int pos, int len) { XORBytes(vec.GetArr(), pos, len); } + template void XOR(T val, int pos, int len) { XORBits((uint8_t*) &val, pos, len); } + void XORBits(uint8_t* p, int pos, int len); + void XORBits(uint8_t* p, uint64_t pos, uint64_t len); + + void XORBitsPosOffset(uint8_t* p, int ppos, int pos, int len); + template void XORBytes(T* dst, T* src, T* lim); + void XORRepeat(uint8_t* p, int pos, int len, int num); + void XORBytesReverse(uint8_t* p, int pos, int len); + + + + /* + * AND Operations + */ + void ANDBytes(uint8_t* p, int pos, int len); + template void ANDBytes(T* dst, T* src, T* lim); + + + /* + * Set operations + */ + void SetXOR(uint8_t* p, uint8_t* q, int pos, int len); + void SetAND(uint8_t* p, uint8_t* q, int pos, int len); + + /* + * Buffer access operations + */ + uint8_t* GetArr(){ return m_pBits;} + void AttachBuf(uint8_t* p, int size=-1){ m_pBits = p; m_nByteSize = size;} + void DetachBuf(){ m_pBits = NULL; m_nByteSize = 0;} + + + /* + * Print Operations + */ + void Print(int fromBit, int toBit); + void PrintHex(); + void PrintHex(int fromByte, int toByte); + void PrintBinary() { Print(0, m_nByteSize<<3); } + void PrintContent(); + void PrintBinaryMasked(int from, int to); + + + /* + * If the cbitvector is abstracted to an array of elements with m_nElementLength bits size, these methods can be used for easier access + */ + template T Get(int i){ return Get(i*m_nElementLength, m_nElementLength);} + template void Set(T val, int i){ Set(val, i*m_nElementLength, m_nElementLength);} + /* + * The same as the above methods only for two-dimensional access + */ + template T Get2D(int i, int j){ return Get((i * m_nNumElementsDimB + j) * m_nElementLength, m_nElementLength);} + template void Set2D(T val, int i, int j){ Set(val, (i * m_nNumElementsDimB + j) * m_nElementLength, m_nElementLength);} + //useful when accessing elements using an index + + + //View the cbitvector as a rows x columns matrix and transpose + void EklundhBitTranspose(int rows, int columns); + void SimpleTranspose(int rows, int columns); + + + +private: + uint8_t* m_pBits; + int m_nByteSize; + crypto* m_cCrypto; + int m_nBits; //The exact number of bits + int m_nElementLength; + int m_nNumElements; + int m_nNumElementsDimB; +}; + + + + +#endif /* BITVECTOR_H_ */ diff --git a/src/util/codewords.cpp b/src/util/codewords.cpp new file mode 100644 index 0000000..842ee71 --- /dev/null +++ b/src/util/codewords.cpp @@ -0,0 +1,25 @@ +/* + * codewords.cpp + * + * Created on: Oct 10, 2014 + * Author: mzohner + */ + + +#include "codewords.h" + +/*void readCodeWords(uint64_t** codewords) { + uint32_t i, j, k; + for(i = 0; i < m_nCodewords; i++) { + for(j = 0; j < (m_nCWIntlen * sizeof(uint32_t)) / sizeof(uint64_t); j++) { + codewords[i][j] = 0; + for(k = 0; k < sizeof(uint64_t) / sizeof(uint32_t); k++) { + codewords[i][j] |= (((REGISTER_SIZE) CODE_MATRIX[i][j*sizeof(REGISTER_SIZE) / sizeof(uint32_t)+k]) << (k * 8 * sizeof(uint32_t))); + //cout << (hex) << CODE_MATRIX[i][j*2+k]; + } + // cout << (hex) << codewords[i][j] << ", "; + } + //cout << endl; + } +} +*/ diff --git a/src/util/codewords.h b/src/util/codewords.h new file mode 100644 index 0000000..1858fa7 --- /dev/null +++ b/src/util/codewords.h @@ -0,0 +1,283 @@ +#ifndef __CODEWORDS_H_ +#define __CODEWORDS_H_ + +#include "typedefs.h" + +static const uint32_t m_nCodewords = 256; +static const uint32_t m_nCWIntlen = 8; + +static const uint32_t CODE_MATRIX[m_nCodewords][m_nCWIntlen] = { \ + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, \ + {0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa}, \ + {0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc}, \ + {0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666, 0x66666666}, \ + {0xf0f0f0f0, 0xf0f0f0f0, 0xf0f0f0f0, 0xf0f0f0f0, 0xf0f0f0f0, 0xf0f0f0f0, 0xf0f0f0f0, 0xf0f0f0f0}, \ + {0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a}, \ + {0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c}, \ + {0x96969696, 0x96969696, 0x96969696, 0x96969696, 0x96969696, 0x96969696, 0x96969696, 0x96969696}, \ + {0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00}, \ + {0x55aa55aa, 0x55aa55aa, 0x55aa55aa, 0x55aa55aa, 0x55aa55aa, 0x55aa55aa, 0x55aa55aa, 0x55aa55aa}, \ + {0x33cc33cc, 0x33cc33cc, 0x33cc33cc, 0x33cc33cc, 0x33cc33cc, 0x33cc33cc, 0x33cc33cc, 0x33cc33cc}, \ + {0x99669966, 0x99669966, 0x99669966, 0x99669966, 0x99669966, 0x99669966, 0x99669966, 0x99669966}, \ + {0x0ff00ff0, 0x0ff00ff0, 0x0ff00ff0, 0x0ff00ff0, 0x0ff00ff0, 0x0ff00ff0, 0x0ff00ff0, 0x0ff00ff0}, \ + {0xa55aa55a, 0xa55aa55a, 0xa55aa55a, 0xa55aa55a, 0xa55aa55a, 0xa55aa55a, 0xa55aa55a, 0xa55aa55a}, \ + {0xc33cc33c, 0xc33cc33c, 0xc33cc33c, 0xc33cc33c, 0xc33cc33c, 0xc33cc33c, 0xc33cc33c, 0xc33cc33c}, \ + {0x69966996, 0x69966996, 0x69966996, 0x69966996, 0x69966996, 0x69966996, 0x69966996, 0x69966996}, \ + {0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000}, \ + {0x5555aaaa, 0x5555aaaa, 0x5555aaaa, 0x5555aaaa, 0x5555aaaa, 0x5555aaaa, 0x5555aaaa, 0x5555aaaa}, \ + {0x3333cccc, 0x3333cccc, 0x3333cccc, 0x3333cccc, 0x3333cccc, 0x3333cccc, 0x3333cccc, 0x3333cccc}, \ + {0x99996666, 0x99996666, 0x99996666, 0x99996666, 0x99996666, 0x99996666, 0x99996666, 0x99996666}, \ + {0x0f0ff0f0, 0x0f0ff0f0, 0x0f0ff0f0, 0x0f0ff0f0, 0x0f0ff0f0, 0x0f0ff0f0, 0x0f0ff0f0, 0x0f0ff0f0}, \ + {0xa5a55a5a, 0xa5a55a5a, 0xa5a55a5a, 0xa5a55a5a, 0xa5a55a5a, 0xa5a55a5a, 0xa5a55a5a, 0xa5a55a5a}, \ + {0xc3c33c3c, 0xc3c33c3c, 0xc3c33c3c, 0xc3c33c3c, 0xc3c33c3c, 0xc3c33c3c, 0xc3c33c3c, 0xc3c33c3c}, \ + {0x69699696, 0x69699696, 0x69699696, 0x69699696, 0x69699696, 0x69699696, 0x69699696, 0x69699696}, \ + {0x00ffff00, 0x00ffff00, 0x00ffff00, 0x00ffff00, 0x00ffff00, 0x00ffff00, 0x00ffff00, 0x00ffff00}, \ + {0xaa5555aa, 0xaa5555aa, 0xaa5555aa, 0xaa5555aa, 0xaa5555aa, 0xaa5555aa, 0xaa5555aa, 0xaa5555aa}, \ + {0xcc3333cc, 0xcc3333cc, 0xcc3333cc, 0xcc3333cc, 0xcc3333cc, 0xcc3333cc, 0xcc3333cc, 0xcc3333cc}, \ + {0x66999966, 0x66999966, 0x66999966, 0x66999966, 0x66999966, 0x66999966, 0x66999966, 0x66999966}, \ + {0xf00f0ff0, 0xf00f0ff0, 0xf00f0ff0, 0xf00f0ff0, 0xf00f0ff0, 0xf00f0ff0, 0xf00f0ff0, 0xf00f0ff0}, \ + {0x5aa5a55a, 0x5aa5a55a, 0x5aa5a55a, 0x5aa5a55a, 0x5aa5a55a, 0x5aa5a55a, 0x5aa5a55a, 0x5aa5a55a}, \ + {0x3cc3c33c, 0x3cc3c33c, 0x3cc3c33c, 0x3cc3c33c, 0x3cc3c33c, 0x3cc3c33c, 0x3cc3c33c, 0x3cc3c33c}, \ + {0x96696996, 0x96696996, 0x96696996, 0x96696996, 0x96696996, 0x96696996, 0x96696996, 0x96696996}, \ + {0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, \ + {0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa}, \ + {0x33333333, 0xcccccccc, 0x33333333, 0xcccccccc, 0x33333333, 0xcccccccc, 0x33333333, 0xcccccccc}, \ + {0x99999999, 0x66666666, 0x99999999, 0x66666666, 0x99999999, 0x66666666, 0x99999999, 0x66666666}, \ + {0x0f0f0f0f, 0xf0f0f0f0, 0x0f0f0f0f, 0xf0f0f0f0, 0x0f0f0f0f, 0xf0f0f0f0, 0x0f0f0f0f, 0xf0f0f0f0}, \ + {0xa5a5a5a5, 0x5a5a5a5a, 0xa5a5a5a5, 0x5a5a5a5a, 0xa5a5a5a5, 0x5a5a5a5a, 0xa5a5a5a5, 0x5a5a5a5a}, \ + {0xc3c3c3c3, 0x3c3c3c3c, 0xc3c3c3c3, 0x3c3c3c3c, 0xc3c3c3c3, 0x3c3c3c3c, 0xc3c3c3c3, 0x3c3c3c3c}, \ + {0x69696969, 0x96969696, 0x69696969, 0x96969696, 0x69696969, 0x96969696, 0x69696969, 0x96969696}, \ + {0x00ff00ff, 0xff00ff00, 0x00ff00ff, 0xff00ff00, 0x00ff00ff, 0xff00ff00, 0x00ff00ff, 0xff00ff00}, \ + {0xaa55aa55, 0x55aa55aa, 0xaa55aa55, 0x55aa55aa, 0xaa55aa55, 0x55aa55aa, 0xaa55aa55, 0x55aa55aa}, \ + {0xcc33cc33, 0x33cc33cc, 0xcc33cc33, 0x33cc33cc, 0xcc33cc33, 0x33cc33cc, 0xcc33cc33, 0x33cc33cc}, \ + {0x66996699, 0x99669966, 0x66996699, 0x99669966, 0x66996699, 0x99669966, 0x66996699, 0x99669966}, \ + {0xf00ff00f, 0x0ff00ff0, 0xf00ff00f, 0x0ff00ff0, 0xf00ff00f, 0x0ff00ff0, 0xf00ff00f, 0x0ff00ff0}, \ + {0x5aa55aa5, 0xa55aa55a, 0x5aa55aa5, 0xa55aa55a, 0x5aa55aa5, 0xa55aa55a, 0x5aa55aa5, 0xa55aa55a}, \ + {0x3cc33cc3, 0xc33cc33c, 0x3cc33cc3, 0xc33cc33c, 0x3cc33cc3, 0xc33cc33c, 0x3cc33cc3, 0xc33cc33c}, \ + {0x96699669, 0x69966996, 0x96699669, 0x69966996, 0x96699669, 0x69966996, 0x96699669, 0x69966996}, \ + {0x0000ffff, 0xffff0000, 0x0000ffff, 0xffff0000, 0x0000ffff, 0xffff0000, 0x0000ffff, 0xffff0000}, \ + {0xaaaa5555, 0x5555aaaa, 0xaaaa5555, 0x5555aaaa, 0xaaaa5555, 0x5555aaaa, 0xaaaa5555, 0x5555aaaa}, \ + {0xcccc3333, 0x3333cccc, 0xcccc3333, 0x3333cccc, 0xcccc3333, 0x3333cccc, 0xcccc3333, 0x3333cccc}, \ + {0x66669999, 0x99996666, 0x66669999, 0x99996666, 0x66669999, 0x99996666, 0x66669999, 0x99996666}, \ + {0xf0f00f0f, 0x0f0ff0f0, 0xf0f00f0f, 0x0f0ff0f0, 0xf0f00f0f, 0x0f0ff0f0, 0xf0f00f0f, 0x0f0ff0f0}, \ + {0x5a5aa5a5, 0xa5a55a5a, 0x5a5aa5a5, 0xa5a55a5a, 0x5a5aa5a5, 0xa5a55a5a, 0x5a5aa5a5, 0xa5a55a5a}, \ + {0x3c3cc3c3, 0xc3c33c3c, 0x3c3cc3c3, 0xc3c33c3c, 0x3c3cc3c3, 0xc3c33c3c, 0x3c3cc3c3, 0xc3c33c3c}, \ + {0x96966969, 0x69699696, 0x96966969, 0x69699696, 0x96966969, 0x69699696, 0x96966969, 0x69699696}, \ + {0xff0000ff, 0x00ffff00, 0xff0000ff, 0x00ffff00, 0xff0000ff, 0x00ffff00, 0xff0000ff, 0x00ffff00}, \ + {0x55aaaa55, 0xaa5555aa, 0x55aaaa55, 0xaa5555aa, 0x55aaaa55, 0xaa5555aa, 0x55aaaa55, 0xaa5555aa}, \ + {0x33cccc33, 0xcc3333cc, 0x33cccc33, 0xcc3333cc, 0x33cccc33, 0xcc3333cc, 0x33cccc33, 0xcc3333cc}, \ + {0x99666699, 0x66999966, 0x99666699, 0x66999966, 0x99666699, 0x66999966, 0x99666699, 0x66999966}, \ + {0x0ff0f00f, 0xf00f0ff0, 0x0ff0f00f, 0xf00f0ff0, 0x0ff0f00f, 0xf00f0ff0, 0x0ff0f00f, 0xf00f0ff0}, \ + {0xa55a5aa5, 0x5aa5a55a, 0xa55a5aa5, 0x5aa5a55a, 0xa55a5aa5, 0x5aa5a55a, 0xa55a5aa5, 0x5aa5a55a}, \ + {0xc33c3cc3, 0x3cc3c33c, 0xc33c3cc3, 0x3cc3c33c, 0xc33c3cc3, 0x3cc3c33c, 0xc33c3cc3, 0x3cc3c33c}, \ + {0x69969669, 0x96696996, 0x69969669, 0x96696996, 0x69969669, 0x96696996, 0x69969669, 0x96696996}, \ + {0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000}, \ + {0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa}, \ + {0x33333333, 0x33333333, 0xcccccccc, 0xcccccccc, 0x33333333, 0x33333333, 0xcccccccc, 0xcccccccc}, \ + {0x99999999, 0x99999999, 0x66666666, 0x66666666, 0x99999999, 0x99999999, 0x66666666, 0x66666666}, \ + {0x0f0f0f0f, 0x0f0f0f0f, 0xf0f0f0f0, 0xf0f0f0f0, 0x0f0f0f0f, 0x0f0f0f0f, 0xf0f0f0f0, 0xf0f0f0f0}, \ + {0xa5a5a5a5, 0xa5a5a5a5, 0x5a5a5a5a, 0x5a5a5a5a, 0xa5a5a5a5, 0xa5a5a5a5, 0x5a5a5a5a, 0x5a5a5a5a}, \ + {0xc3c3c3c3, 0xc3c3c3c3, 0x3c3c3c3c, 0x3c3c3c3c, 0xc3c3c3c3, 0xc3c3c3c3, 0x3c3c3c3c, 0x3c3c3c3c}, \ + {0x69696969, 0x69696969, 0x96969696, 0x96969696, 0x69696969, 0x69696969, 0x96969696, 0x96969696}, \ + {0x00ff00ff, 0x00ff00ff, 0xff00ff00, 0xff00ff00, 0x00ff00ff, 0x00ff00ff, 0xff00ff00, 0xff00ff00}, \ + {0xaa55aa55, 0xaa55aa55, 0x55aa55aa, 0x55aa55aa, 0xaa55aa55, 0xaa55aa55, 0x55aa55aa, 0x55aa55aa}, \ + {0xcc33cc33, 0xcc33cc33, 0x33cc33cc, 0x33cc33cc, 0xcc33cc33, 0xcc33cc33, 0x33cc33cc, 0x33cc33cc}, \ + {0x66996699, 0x66996699, 0x99669966, 0x99669966, 0x66996699, 0x66996699, 0x99669966, 0x99669966}, \ + {0xf00ff00f, 0xf00ff00f, 0x0ff00ff0, 0x0ff00ff0, 0xf00ff00f, 0xf00ff00f, 0x0ff00ff0, 0x0ff00ff0}, \ + {0x5aa55aa5, 0x5aa55aa5, 0xa55aa55a, 0xa55aa55a, 0x5aa55aa5, 0x5aa55aa5, 0xa55aa55a, 0xa55aa55a}, \ + {0x3cc33cc3, 0x3cc33cc3, 0xc33cc33c, 0xc33cc33c, 0x3cc33cc3, 0x3cc33cc3, 0xc33cc33c, 0xc33cc33c}, \ + {0x96699669, 0x96699669, 0x69966996, 0x69966996, 0x96699669, 0x96699669, 0x69966996, 0x69966996}, \ + {0x0000ffff, 0x0000ffff, 0xffff0000, 0xffff0000, 0x0000ffff, 0x0000ffff, 0xffff0000, 0xffff0000}, \ + {0xaaaa5555, 0xaaaa5555, 0x5555aaaa, 0x5555aaaa, 0xaaaa5555, 0xaaaa5555, 0x5555aaaa, 0x5555aaaa}, \ + {0xcccc3333, 0xcccc3333, 0x3333cccc, 0x3333cccc, 0xcccc3333, 0xcccc3333, 0x3333cccc, 0x3333cccc}, \ + {0x66669999, 0x66669999, 0x99996666, 0x99996666, 0x66669999, 0x66669999, 0x99996666, 0x99996666}, \ + {0xf0f00f0f, 0xf0f00f0f, 0x0f0ff0f0, 0x0f0ff0f0, 0xf0f00f0f, 0xf0f00f0f, 0x0f0ff0f0, 0x0f0ff0f0}, \ + {0x5a5aa5a5, 0x5a5aa5a5, 0xa5a55a5a, 0xa5a55a5a, 0x5a5aa5a5, 0x5a5aa5a5, 0xa5a55a5a, 0xa5a55a5a}, \ + {0x3c3cc3c3, 0x3c3cc3c3, 0xc3c33c3c, 0xc3c33c3c, 0x3c3cc3c3, 0x3c3cc3c3, 0xc3c33c3c, 0xc3c33c3c}, \ + {0x96966969, 0x96966969, 0x69699696, 0x69699696, 0x96966969, 0x96966969, 0x69699696, 0x69699696}, \ + {0xff0000ff, 0xff0000ff, 0x00ffff00, 0x00ffff00, 0xff0000ff, 0xff0000ff, 0x00ffff00, 0x00ffff00}, \ + {0x55aaaa55, 0x55aaaa55, 0xaa5555aa, 0xaa5555aa, 0x55aaaa55, 0x55aaaa55, 0xaa5555aa, 0xaa5555aa}, \ + {0x33cccc33, 0x33cccc33, 0xcc3333cc, 0xcc3333cc, 0x33cccc33, 0x33cccc33, 0xcc3333cc, 0xcc3333cc}, \ + {0x99666699, 0x99666699, 0x66999966, 0x66999966, 0x99666699, 0x99666699, 0x66999966, 0x66999966}, \ + {0x0ff0f00f, 0x0ff0f00f, 0xf00f0ff0, 0xf00f0ff0, 0x0ff0f00f, 0x0ff0f00f, 0xf00f0ff0, 0xf00f0ff0}, \ + {0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a, 0x5aa5a55a, 0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a, 0x5aa5a55a}, \ + {0xc33c3cc3, 0xc33c3cc3, 0x3cc3c33c, 0x3cc3c33c, 0xc33c3cc3, 0xc33c3cc3, 0x3cc3c33c, 0x3cc3c33c}, \ + {0x69969669, 0x69969669, 0x96696996, 0x96696996, 0x69969669, 0x69969669, 0x96696996, 0x96696996}, \ + {0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, \ + {0xaaaaaaaa, 0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555, 0xaaaaaaaa}, \ + {0xcccccccc, 0x33333333, 0x33333333, 0xcccccccc, 0xcccccccc, 0x33333333, 0x33333333, 0xcccccccc}, \ + {0x66666666, 0x99999999, 0x99999999, 0x66666666, 0x66666666, 0x99999999, 0x99999999, 0x66666666}, \ + {0xf0f0f0f0, 0x0f0f0f0f, 0x0f0f0f0f, 0xf0f0f0f0, 0xf0f0f0f0, 0x0f0f0f0f, 0x0f0f0f0f, 0xf0f0f0f0}, \ + {0x5a5a5a5a, 0xa5a5a5a5, 0xa5a5a5a5, 0x5a5a5a5a, 0x5a5a5a5a, 0xa5a5a5a5, 0xa5a5a5a5, 0x5a5a5a5a}, \ + {0x3c3c3c3c, 0xc3c3c3c3, 0xc3c3c3c3, 0x3c3c3c3c, 0x3c3c3c3c, 0xc3c3c3c3, 0xc3c3c3c3, 0x3c3c3c3c}, \ + {0x96969696, 0x69696969, 0x69696969, 0x96969696, 0x96969696, 0x69696969, 0x69696969, 0x96969696}, \ + {0xff00ff00, 0x00ff00ff, 0x00ff00ff, 0xff00ff00, 0xff00ff00, 0x00ff00ff, 0x00ff00ff, 0xff00ff00}, \ + {0x55aa55aa, 0xaa55aa55, 0xaa55aa55, 0x55aa55aa, 0x55aa55aa, 0xaa55aa55, 0xaa55aa55, 0x55aa55aa}, \ + {0x33cc33cc, 0xcc33cc33, 0xcc33cc33, 0x33cc33cc, 0x33cc33cc, 0xcc33cc33, 0xcc33cc33, 0x33cc33cc}, \ + {0x99669966, 0x66996699, 0x66996699, 0x99669966, 0x99669966, 0x66996699, 0x66996699, 0x99669966}, \ + {0x0ff00ff0, 0xf00ff00f, 0xf00ff00f, 0x0ff00ff0, 0x0ff00ff0, 0xf00ff00f, 0xf00ff00f, 0x0ff00ff0}, \ + {0xa55aa55a, 0x5aa55aa5, 0x5aa55aa5, 0xa55aa55a, 0xa55aa55a, 0x5aa55aa5, 0x5aa55aa5, 0xa55aa55a}, \ + {0xc33cc33c, 0x3cc33cc3, 0x3cc33cc3, 0xc33cc33c, 0xc33cc33c, 0x3cc33cc3, 0x3cc33cc3, 0xc33cc33c}, \ + {0x69966996, 0x96699669, 0x96699669, 0x69966996, 0x69966996, 0x96699669, 0x96699669, 0x69966996}, \ + {0xffff0000, 0x0000ffff, 0x0000ffff, 0xffff0000, 0xffff0000, 0x0000ffff, 0x0000ffff, 0xffff0000}, \ + {0x5555aaaa, 0xaaaa5555, 0xaaaa5555, 0x5555aaaa, 0x5555aaaa, 0xaaaa5555, 0xaaaa5555, 0x5555aaaa}, \ + {0x3333cccc, 0xcccc3333, 0xcccc3333, 0x3333cccc, 0x3333cccc, 0xcccc3333, 0xcccc3333, 0x3333cccc}, \ + {0x99996666, 0x66669999, 0x66669999, 0x99996666, 0x99996666, 0x66669999, 0x66669999, 0x99996666}, \ + {0x0f0ff0f0, 0xf0f00f0f, 0xf0f00f0f, 0x0f0ff0f0, 0x0f0ff0f0, 0xf0f00f0f, 0xf0f00f0f, 0x0f0ff0f0}, \ + {0xa5a55a5a, 0x5a5aa5a5, 0x5a5aa5a5, 0xa5a55a5a, 0xa5a55a5a, 0x5a5aa5a5, 0x5a5aa5a5, 0xa5a55a5a}, \ + {0xc3c33c3c, 0x3c3cc3c3, 0x3c3cc3c3, 0xc3c33c3c, 0xc3c33c3c, 0x3c3cc3c3, 0x3c3cc3c3, 0xc3c33c3c}, \ + {0x69699696, 0x96966969, 0x96966969, 0x69699696, 0x69699696, 0x96966969, 0x96966969, 0x69699696}, \ + {0x00ffff00, 0xff0000ff, 0xff0000ff, 0x00ffff00, 0x00ffff00, 0xff0000ff, 0xff0000ff, 0x00ffff00}, \ + {0xaa5555aa, 0x55aaaa55, 0x55aaaa55, 0xaa5555aa, 0xaa5555aa, 0x55aaaa55, 0x55aaaa55, 0xaa5555aa}, \ + {0xcc3333cc, 0x33cccc33, 0x33cccc33, 0xcc3333cc, 0xcc3333cc, 0x33cccc33, 0x33cccc33, 0xcc3333cc}, \ + {0x66999966, 0x99666699, 0x99666699, 0x66999966, 0x66999966, 0x99666699, 0x99666699, 0x66999966}, \ + {0xf00f0ff0, 0x0ff0f00f, 0x0ff0f00f, 0xf00f0ff0, 0xf00f0ff0, 0x0ff0f00f, 0x0ff0f00f, 0xf00f0ff0}, \ + {0x5aa5a55a, 0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a, 0x5aa5a55a, 0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a}, \ + {0x3cc3c33c, 0xc33c3cc3, 0xc33c3cc3, 0x3cc3c33c, 0x3cc3c33c, 0xc33c3cc3, 0xc33c3cc3, 0x3cc3c33c}, \ + {0x96696996, 0x69969669, 0x69969669, 0x96696996, 0x96696996, 0x69969669, 0x69969669, 0x96696996}, \ + {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, \ + {0x55555555, 0x55555555, 0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa}, \ + {0x33333333, 0x33333333, 0x33333333, 0x33333333, 0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc}, \ + {0x99999999, 0x99999999, 0x99999999, 0x99999999, 0x66666666, 0x66666666, 0x66666666, 0x66666666}, \ + {0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0xf0f0f0f0, 0xf0f0f0f0, 0xf0f0f0f0, 0xf0f0f0f0}, \ + {0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a}, \ + {0xc3c3c3c3, 0xc3c3c3c3, 0xc3c3c3c3, 0xc3c3c3c3, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c}, \ + {0x69696969, 0x69696969, 0x69696969, 0x69696969, 0x96969696, 0x96969696, 0x96969696, 0x96969696}, \ + {0x00ff00ff, 0x00ff00ff, 0x00ff00ff, 0x00ff00ff, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00}, \ + {0xaa55aa55, 0xaa55aa55, 0xaa55aa55, 0xaa55aa55, 0x55aa55aa, 0x55aa55aa, 0x55aa55aa, 0x55aa55aa}, \ + {0xcc33cc33, 0xcc33cc33, 0xcc33cc33, 0xcc33cc33, 0x33cc33cc, 0x33cc33cc, 0x33cc33cc, 0x33cc33cc}, \ + {0x66996699, 0x66996699, 0x66996699, 0x66996699, 0x99669966, 0x99669966, 0x99669966, 0x99669966}, \ + {0xf00ff00f, 0xf00ff00f, 0xf00ff00f, 0xf00ff00f, 0x0ff00ff0, 0x0ff00ff0, 0x0ff00ff0, 0x0ff00ff0}, \ + {0x5aa55aa5, 0x5aa55aa5, 0x5aa55aa5, 0x5aa55aa5, 0xa55aa55a, 0xa55aa55a, 0xa55aa55a, 0xa55aa55a}, \ + {0x3cc33cc3, 0x3cc33cc3, 0x3cc33cc3, 0x3cc33cc3, 0xc33cc33c, 0xc33cc33c, 0xc33cc33c, 0xc33cc33c}, \ + {0x96699669, 0x96699669, 0x96699669, 0x96699669, 0x69966996, 0x69966996, 0x69966996, 0x69966996}, \ + {0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000}, \ + {0xaaaa5555, 0xaaaa5555, 0xaaaa5555, 0xaaaa5555, 0x5555aaaa, 0x5555aaaa, 0x5555aaaa, 0x5555aaaa}, \ + {0xcccc3333, 0xcccc3333, 0xcccc3333, 0xcccc3333, 0x3333cccc, 0x3333cccc, 0x3333cccc, 0x3333cccc}, \ + {0x66669999, 0x66669999, 0x66669999, 0x66669999, 0x99996666, 0x99996666, 0x99996666, 0x99996666}, \ + {0xf0f00f0f, 0xf0f00f0f, 0xf0f00f0f, 0xf0f00f0f, 0x0f0ff0f0, 0x0f0ff0f0, 0x0f0ff0f0, 0x0f0ff0f0}, \ + {0x5a5aa5a5, 0x5a5aa5a5, 0x5a5aa5a5, 0x5a5aa5a5, 0xa5a55a5a, 0xa5a55a5a, 0xa5a55a5a, 0xa5a55a5a}, \ + {0x3c3cc3c3, 0x3c3cc3c3, 0x3c3cc3c3, 0x3c3cc3c3, 0xc3c33c3c, 0xc3c33c3c, 0xc3c33c3c, 0xc3c33c3c}, \ + {0x96966969, 0x96966969, 0x96966969, 0x96966969, 0x69699696, 0x69699696, 0x69699696, 0x69699696}, \ + {0xff0000ff, 0xff0000ff, 0xff0000ff, 0xff0000ff, 0x00ffff00, 0x00ffff00, 0x00ffff00, 0x00ffff00}, \ + {0x55aaaa55, 0x55aaaa55, 0x55aaaa55, 0x55aaaa55, 0xaa5555aa, 0xaa5555aa, 0xaa5555aa, 0xaa5555aa}, \ + {0x33cccc33, 0x33cccc33, 0x33cccc33, 0x33cccc33, 0xcc3333cc, 0xcc3333cc, 0xcc3333cc, 0xcc3333cc}, \ + {0x99666699, 0x99666699, 0x99666699, 0x99666699, 0x66999966, 0x66999966, 0x66999966, 0x66999966}, \ + {0x0ff0f00f, 0x0ff0f00f, 0x0ff0f00f, 0x0ff0f00f, 0xf00f0ff0, 0xf00f0ff0, 0xf00f0ff0, 0xf00f0ff0}, \ + {0xa55a5aa5, 0xa55a5aa5, 0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a, 0x5aa5a55a, 0x5aa5a55a, 0x5aa5a55a}, \ + {0xc33c3cc3, 0xc33c3cc3, 0xc33c3cc3, 0xc33c3cc3, 0x3cc3c33c, 0x3cc3c33c, 0x3cc3c33c, 0x3cc3c33c}, \ + {0x69969669, 0x69969669, 0x69969669, 0x69969669, 0x96696996, 0x96696996, 0x96696996, 0x96696996}, \ + {0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, \ + {0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555, 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa}, \ + {0xcccccccc, 0x33333333, 0xcccccccc, 0x33333333, 0x33333333, 0xcccccccc, 0x33333333, 0xcccccccc}, \ + {0x66666666, 0x99999999, 0x66666666, 0x99999999, 0x99999999, 0x66666666, 0x99999999, 0x66666666}, \ + {0xf0f0f0f0, 0x0f0f0f0f, 0xf0f0f0f0, 0x0f0f0f0f, 0x0f0f0f0f, 0xf0f0f0f0, 0x0f0f0f0f, 0xf0f0f0f0}, \ + {0x5a5a5a5a, 0xa5a5a5a5, 0x5a5a5a5a, 0xa5a5a5a5, 0xa5a5a5a5, 0x5a5a5a5a, 0xa5a5a5a5, 0x5a5a5a5a}, \ + {0x3c3c3c3c, 0xc3c3c3c3, 0x3c3c3c3c, 0xc3c3c3c3, 0xc3c3c3c3, 0x3c3c3c3c, 0xc3c3c3c3, 0x3c3c3c3c}, \ + {0x96969696, 0x69696969, 0x96969696, 0x69696969, 0x69696969, 0x96969696, 0x69696969, 0x96969696}, \ + {0xff00ff00, 0x00ff00ff, 0xff00ff00, 0x00ff00ff, 0x00ff00ff, 0xff00ff00, 0x00ff00ff, 0xff00ff00}, \ + {0x55aa55aa, 0xaa55aa55, 0x55aa55aa, 0xaa55aa55, 0xaa55aa55, 0x55aa55aa, 0xaa55aa55, 0x55aa55aa}, \ + {0x33cc33cc, 0xcc33cc33, 0x33cc33cc, 0xcc33cc33, 0xcc33cc33, 0x33cc33cc, 0xcc33cc33, 0x33cc33cc}, \ + {0x99669966, 0x66996699, 0x99669966, 0x66996699, 0x66996699, 0x99669966, 0x66996699, 0x99669966}, \ + {0x0ff00ff0, 0xf00ff00f, 0x0ff00ff0, 0xf00ff00f, 0xf00ff00f, 0x0ff00ff0, 0xf00ff00f, 0x0ff00ff0}, \ + {0xa55aa55a, 0x5aa55aa5, 0xa55aa55a, 0x5aa55aa5, 0x5aa55aa5, 0xa55aa55a, 0x5aa55aa5, 0xa55aa55a}, \ + {0xc33cc33c, 0x3cc33cc3, 0xc33cc33c, 0x3cc33cc3, 0x3cc33cc3, 0xc33cc33c, 0x3cc33cc3, 0xc33cc33c}, \ + {0x69966996, 0x96699669, 0x69966996, 0x96699669, 0x96699669, 0x69966996, 0x96699669, 0x69966996}, \ + {0xffff0000, 0x0000ffff, 0xffff0000, 0x0000ffff, 0x0000ffff, 0xffff0000, 0x0000ffff, 0xffff0000}, \ + {0x5555aaaa, 0xaaaa5555, 0x5555aaaa, 0xaaaa5555, 0xaaaa5555, 0x5555aaaa, 0xaaaa5555, 0x5555aaaa}, \ + {0x3333cccc, 0xcccc3333, 0x3333cccc, 0xcccc3333, 0xcccc3333, 0x3333cccc, 0xcccc3333, 0x3333cccc}, \ + {0x99996666, 0x66669999, 0x99996666, 0x66669999, 0x66669999, 0x99996666, 0x66669999, 0x99996666}, \ + {0x0f0ff0f0, 0xf0f00f0f, 0x0f0ff0f0, 0xf0f00f0f, 0xf0f00f0f, 0x0f0ff0f0, 0xf0f00f0f, 0x0f0ff0f0}, \ + {0xa5a55a5a, 0x5a5aa5a5, 0xa5a55a5a, 0x5a5aa5a5, 0x5a5aa5a5, 0xa5a55a5a, 0x5a5aa5a5, 0xa5a55a5a}, \ + {0xc3c33c3c, 0x3c3cc3c3, 0xc3c33c3c, 0x3c3cc3c3, 0x3c3cc3c3, 0xc3c33c3c, 0x3c3cc3c3, 0xc3c33c3c}, \ + {0x69699696, 0x96966969, 0x69699696, 0x96966969, 0x96966969, 0x69699696, 0x96966969, 0x69699696}, \ + {0x00ffff00, 0xff0000ff, 0x00ffff00, 0xff0000ff, 0xff0000ff, 0x00ffff00, 0xff0000ff, 0x00ffff00}, \ + {0xaa5555aa, 0x55aaaa55, 0xaa5555aa, 0x55aaaa55, 0x55aaaa55, 0xaa5555aa, 0x55aaaa55, 0xaa5555aa}, \ + {0xcc3333cc, 0x33cccc33, 0xcc3333cc, 0x33cccc33, 0x33cccc33, 0xcc3333cc, 0x33cccc33, 0xcc3333cc}, \ + {0x66999966, 0x99666699, 0x66999966, 0x99666699, 0x99666699, 0x66999966, 0x99666699, 0x66999966}, \ + {0xf00f0ff0, 0x0ff0f00f, 0xf00f0ff0, 0x0ff0f00f, 0x0ff0f00f, 0xf00f0ff0, 0x0ff0f00f, 0xf00f0ff0}, \ + {0x5aa5a55a, 0xa55a5aa5, 0x5aa5a55a, 0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a, 0xa55a5aa5, 0x5aa5a55a}, \ + {0x3cc3c33c, 0xc33c3cc3, 0x3cc3c33c, 0xc33c3cc3, 0xc33c3cc3, 0x3cc3c33c, 0xc33c3cc3, 0x3cc3c33c}, \ + {0x96696996, 0x69969669, 0x96696996, 0x69969669, 0x69969669, 0x96696996, 0x69969669, 0x96696996}, \ + {0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000}, \ + {0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555, 0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa}, \ + {0xcccccccc, 0xcccccccc, 0x33333333, 0x33333333, 0x33333333, 0x33333333, 0xcccccccc, 0xcccccccc}, \ + {0x66666666, 0x66666666, 0x99999999, 0x99999999, 0x99999999, 0x99999999, 0x66666666, 0x66666666}, \ + {0xf0f0f0f0, 0xf0f0f0f0, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0xf0f0f0f0, 0xf0f0f0f0}, \ + {0x5a5a5a5a, 0x5a5a5a5a, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0x5a5a5a5a, 0x5a5a5a5a}, \ + {0x3c3c3c3c, 0x3c3c3c3c, 0xc3c3c3c3, 0xc3c3c3c3, 0xc3c3c3c3, 0xc3c3c3c3, 0x3c3c3c3c, 0x3c3c3c3c}, \ + {0x96969696, 0x96969696, 0x69696969, 0x69696969, 0x69696969, 0x69696969, 0x96969696, 0x96969696}, \ + {0xff00ff00, 0xff00ff00, 0x00ff00ff, 0x00ff00ff, 0x00ff00ff, 0x00ff00ff, 0xff00ff00, 0xff00ff00}, \ + {0x55aa55aa, 0x55aa55aa, 0xaa55aa55, 0xaa55aa55, 0xaa55aa55, 0xaa55aa55, 0x55aa55aa, 0x55aa55aa}, \ + {0x33cc33cc, 0x33cc33cc, 0xcc33cc33, 0xcc33cc33, 0xcc33cc33, 0xcc33cc33, 0x33cc33cc, 0x33cc33cc}, \ + {0x99669966, 0x99669966, 0x66996699, 0x66996699, 0x66996699, 0x66996699, 0x99669966, 0x99669966}, \ + {0x0ff00ff0, 0x0ff00ff0, 0xf00ff00f, 0xf00ff00f, 0xf00ff00f, 0xf00ff00f, 0x0ff00ff0, 0x0ff00ff0}, \ + {0xa55aa55a, 0xa55aa55a, 0x5aa55aa5, 0x5aa55aa5, 0x5aa55aa5, 0x5aa55aa5, 0xa55aa55a, 0xa55aa55a}, \ + {0xc33cc33c, 0xc33cc33c, 0x3cc33cc3, 0x3cc33cc3, 0x3cc33cc3, 0x3cc33cc3, 0xc33cc33c, 0xc33cc33c}, \ + {0x69966996, 0x69966996, 0x96699669, 0x96699669, 0x96699669, 0x96699669, 0x69966996, 0x69966996}, \ + {0xffff0000, 0xffff0000, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0xffff0000, 0xffff0000}, \ + {0x5555aaaa, 0x5555aaaa, 0xaaaa5555, 0xaaaa5555, 0xaaaa5555, 0xaaaa5555, 0x5555aaaa, 0x5555aaaa}, \ + {0x3333cccc, 0x3333cccc, 0xcccc3333, 0xcccc3333, 0xcccc3333, 0xcccc3333, 0x3333cccc, 0x3333cccc}, \ + {0x99996666, 0x99996666, 0x66669999, 0x66669999, 0x66669999, 0x66669999, 0x99996666, 0x99996666}, \ + {0x0f0ff0f0, 0x0f0ff0f0, 0xf0f00f0f, 0xf0f00f0f, 0xf0f00f0f, 0xf0f00f0f, 0x0f0ff0f0, 0x0f0ff0f0}, \ + {0xa5a55a5a, 0xa5a55a5a, 0x5a5aa5a5, 0x5a5aa5a5, 0x5a5aa5a5, 0x5a5aa5a5, 0xa5a55a5a, 0xa5a55a5a}, \ + {0xc3c33c3c, 0xc3c33c3c, 0x3c3cc3c3, 0x3c3cc3c3, 0x3c3cc3c3, 0x3c3cc3c3, 0xc3c33c3c, 0xc3c33c3c}, \ + {0x69699696, 0x69699696, 0x96966969, 0x96966969, 0x96966969, 0x96966969, 0x69699696, 0x69699696}, \ + {0x00ffff00, 0x00ffff00, 0xff0000ff, 0xff0000ff, 0xff0000ff, 0xff0000ff, 0x00ffff00, 0x00ffff00}, \ + {0xaa5555aa, 0xaa5555aa, 0x55aaaa55, 0x55aaaa55, 0x55aaaa55, 0x55aaaa55, 0xaa5555aa, 0xaa5555aa}, \ + {0xcc3333cc, 0xcc3333cc, 0x33cccc33, 0x33cccc33, 0x33cccc33, 0x33cccc33, 0xcc3333cc, 0xcc3333cc}, \ + {0x66999966, 0x66999966, 0x99666699, 0x99666699, 0x99666699, 0x99666699, 0x66999966, 0x66999966}, \ + {0xf00f0ff0, 0xf00f0ff0, 0x0ff0f00f, 0x0ff0f00f, 0x0ff0f00f, 0x0ff0f00f, 0xf00f0ff0, 0xf00f0ff0}, \ + {0x5aa5a55a, 0x5aa5a55a, 0xa55a5aa5, 0xa55a5aa5, 0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a, 0x5aa5a55a}, \ + {0x3cc3c33c, 0x3cc3c33c, 0xc33c3cc3, 0xc33c3cc3, 0xc33c3cc3, 0xc33c3cc3, 0x3cc3c33c, 0x3cc3c33c}, \ + {0x96696996, 0x96696996, 0x69969669, 0x69969669, 0x69969669, 0x69969669, 0x96696996, 0x96696996}, \ + {0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, \ + {0x55555555, 0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555, 0x55555555, 0xaaaaaaaa}, \ + {0x33333333, 0xcccccccc, 0xcccccccc, 0x33333333, 0xcccccccc, 0x33333333, 0x33333333, 0xcccccccc}, \ + {0x99999999, 0x66666666, 0x66666666, 0x99999999, 0x66666666, 0x99999999, 0x99999999, 0x66666666}, \ + {0x0f0f0f0f, 0xf0f0f0f0, 0xf0f0f0f0, 0x0f0f0f0f, 0xf0f0f0f0, 0x0f0f0f0f, 0x0f0f0f0f, 0xf0f0f0f0}, \ + {0xa5a5a5a5, 0x5a5a5a5a, 0x5a5a5a5a, 0xa5a5a5a5, 0x5a5a5a5a, 0xa5a5a5a5, 0xa5a5a5a5, 0x5a5a5a5a}, \ + {0xc3c3c3c3, 0x3c3c3c3c, 0x3c3c3c3c, 0xc3c3c3c3, 0x3c3c3c3c, 0xc3c3c3c3, 0xc3c3c3c3, 0x3c3c3c3c}, \ + {0x69696969, 0x96969696, 0x96969696, 0x69696969, 0x96969696, 0x69696969, 0x69696969, 0x96969696}, \ + {0x00ff00ff, 0xff00ff00, 0xff00ff00, 0x00ff00ff, 0xff00ff00, 0x00ff00ff, 0x00ff00ff, 0xff00ff00}, \ + {0xaa55aa55, 0x55aa55aa, 0x55aa55aa, 0xaa55aa55, 0x55aa55aa, 0xaa55aa55, 0xaa55aa55, 0x55aa55aa}, \ + {0xcc33cc33, 0x33cc33cc, 0x33cc33cc, 0xcc33cc33, 0x33cc33cc, 0xcc33cc33, 0xcc33cc33, 0x33cc33cc}, \ + {0x66996699, 0x99669966, 0x99669966, 0x66996699, 0x99669966, 0x66996699, 0x66996699, 0x99669966}, \ + {0xf00ff00f, 0x0ff00ff0, 0x0ff00ff0, 0xf00ff00f, 0x0ff00ff0, 0xf00ff00f, 0xf00ff00f, 0x0ff00ff0}, \ + {0x5aa55aa5, 0xa55aa55a, 0xa55aa55a, 0x5aa55aa5, 0xa55aa55a, 0x5aa55aa5, 0x5aa55aa5, 0xa55aa55a}, \ + {0x3cc33cc3, 0xc33cc33c, 0xc33cc33c, 0x3cc33cc3, 0xc33cc33c, 0x3cc33cc3, 0x3cc33cc3, 0xc33cc33c}, \ + {0x96699669, 0x69966996, 0x69966996, 0x96699669, 0x69966996, 0x96699669, 0x96699669, 0x69966996}, \ + {0x0000ffff, 0xffff0000, 0xffff0000, 0x0000ffff, 0xffff0000, 0x0000ffff, 0x0000ffff, 0xffff0000}, \ + {0xaaaa5555, 0x5555aaaa, 0x5555aaaa, 0xaaaa5555, 0x5555aaaa, 0xaaaa5555, 0xaaaa5555, 0x5555aaaa}, \ + {0xcccc3333, 0x3333cccc, 0x3333cccc, 0xcccc3333, 0x3333cccc, 0xcccc3333, 0xcccc3333, 0x3333cccc}, \ + {0x66669999, 0x99996666, 0x99996666, 0x66669999, 0x99996666, 0x66669999, 0x66669999, 0x99996666}, \ + {0xf0f00f0f, 0x0f0ff0f0, 0x0f0ff0f0, 0xf0f00f0f, 0x0f0ff0f0, 0xf0f00f0f, 0xf0f00f0f, 0x0f0ff0f0}, \ + {0x5a5aa5a5, 0xa5a55a5a, 0xa5a55a5a, 0x5a5aa5a5, 0xa5a55a5a, 0x5a5aa5a5, 0x5a5aa5a5, 0xa5a55a5a}, \ + {0x3c3cc3c3, 0xc3c33c3c, 0xc3c33c3c, 0x3c3cc3c3, 0xc3c33c3c, 0x3c3cc3c3, 0x3c3cc3c3, 0xc3c33c3c}, \ + {0x96966969, 0x69699696, 0x69699696, 0x96966969, 0x69699696, 0x96966969, 0x96966969, 0x69699696}, \ + {0xff0000ff, 0x00ffff00, 0x00ffff00, 0xff0000ff, 0x00ffff00, 0xff0000ff, 0xff0000ff, 0x00ffff00}, \ + {0x55aaaa55, 0xaa5555aa, 0xaa5555aa, 0x55aaaa55, 0xaa5555aa, 0x55aaaa55, 0x55aaaa55, 0xaa5555aa}, \ + {0x33cccc33, 0xcc3333cc, 0xcc3333cc, 0x33cccc33, 0xcc3333cc, 0x33cccc33, 0x33cccc33, 0xcc3333cc}, \ + {0x99666699, 0x66999966, 0x66999966, 0x99666699, 0x66999966, 0x99666699, 0x99666699, 0x66999966}, \ + {0x0ff0f00f, 0xf00f0ff0, 0xf00f0ff0, 0x0ff0f00f, 0xf00f0ff0, 0x0ff0f00f, 0x0ff0f00f, 0xf00f0ff0}, \ + {0xa55a5aa5, 0x5aa5a55a, 0x5aa5a55a, 0xa55a5aa5, 0x5aa5a55a, 0xa55a5aa5, 0xa55a5aa5, 0x5aa5a55a}, \ + {0xc33c3cc3, 0x3cc3c33c, 0x3cc3c33c, 0xc33c3cc3, 0x3cc3c33c, 0xc33c3cc3, 0xc33c3cc3, 0x3cc3c33c}, \ + {0x69969669, 0x96696996, 0x96696996, 0x69969669, 0x96696996, 0x69969669, 0x69969669, 0x96696996} }; + + +static void readCodeWords(uint64_t** codewords) { + uint32_t i, j, k; + for(i = 0; i < m_nCodewords; i++) { + for(j = 0; j < (m_nCWIntlen * sizeof(uint32_t)) / sizeof(uint64_t); j++) { + codewords[i][j] = 0; + for(k = 0; k < sizeof(uint64_t) / sizeof(uint32_t); k++) { + codewords[i][j] |= (((REGISTER_SIZE) CODE_MATRIX[i][j*sizeof(REGISTER_SIZE) / sizeof(uint32_t)+k]) << (k * 8 * sizeof(uint32_t))); + //cout << (hex) << CODE_MATRIX[i][j*2+k]; + } + // cout << (hex) << codewords[i][j] << ", "; + } + //cout << endl; + } +} + +#endif //CODEWORDS_H_ diff --git a/src/util/connection.cpp b/src/util/connection.cpp new file mode 100644 index 0000000..d14649c --- /dev/null +++ b/src/util/connection.cpp @@ -0,0 +1,78 @@ +/* + * connection.cpp + * + * Created on: Mar 7, 2013 + * Author: mzohner + */ + +#include "connection.h" + +bool connect(const char* address, uint16_t port, CSocket& sockfd) { + uint64_t lTO = CONNECT_TIMEO_MILISEC; +#ifdef DEBUG + cout << "Connecting to " << address << ": " << port << endl; +#endif + for( uint32_t i=0; i + +bool connect(const char* address, uint16_t port, CSocket& sockfd); +bool listen(const char* address, uint16_t port, CSocket* sockfd, uint32_t nconnections); + +#define RETRY_CONNECT 1000 +#define CONNECT_TIMEO_MILISEC 10000 + + +#endif diff --git a/src/util/crypto/crypto.cpp b/src/util/crypto/crypto.cpp new file mode 100644 index 0000000..d3964db --- /dev/null +++ b/src/util/crypto/crypto.cpp @@ -0,0 +1,409 @@ +/* + * crypto.cpp + * + * Created on: Jul 9, 2014 + * Author: mzohner + */ + + +#include "crypto.h" + +crypto::crypto(uint32_t symsecbits, uint8_t* seed) { + init(symsecbits, seed); +} + +crypto::crypto(uint32_t symsecbits) { + uint8_t* seed = (uint8_t*) malloc(sizeof(uint8_t) * AES_BYTES); + gen_secure_random(seed, AES_BYTES); + + init(symsecbits, seed); + free(seed); +} + +crypto::~crypto() { + free_prf_state(&global_prf_state); + free(aes_hash_in_buf); + free(aes_hash_out_buf); + free(sha_hash_buf); + free(aes_hash_buf_y1); + free(aes_hash_buf_y2); + //TODO: securely delete the AES keys +} + + +void crypto::init(uint32_t symsecbits, uint8_t* seed) { + //seed_aes_key(&(global_prf_state.aes_key), seed); + secparam = get_sec_lvl(symsecbits); + //aes_hash_key = NULL; + //aes_enc_key = NULL; + + //rndctr = (uint64_t*) calloc(ceil_divide(AES_BYTES, sizeof(uint64_t)), sizeof(uint64_t)); + init_prf_state(&global_prf_state, seed); + + aes_hash_in_buf = (uint8_t*) malloc(AES_BYTES); + aes_hash_out_buf = (uint8_t*) malloc(AES_BYTES); + aes_hash_buf_y1 = (uint8_t*) malloc(AES_BYTES); + aes_hash_buf_y2 = (uint8_t*) malloc(AES_BYTES); + + sha_hash_buf = (uint8_t*) malloc((secparam.symbits >> 3 ) * 2); + + if(secparam.symbits == ST.symbits) { + hash_routine = &sha1_hash; + } else if(secparam.symbits == MT.symbits) { + hash_routine = &sha256_hash; + } else if(secparam.symbits == LT.symbits) { + hash_routine = &sha256_hash; + } else if(secparam.symbits == XLT.symbits) { + hash_routine = &sha512_hash; + } else if(secparam.symbits == XXLT.symbits) { + hash_routine = &sha512_hash; + } else { + hash_routine = &sha256_hash; + } +} + +pk_crypto* crypto::gen_field(field_type ftype) { + uint8_t* pkseed = (uint8_t*) malloc(sizeof(uint8_t) * (secparam.symbits >> 3)); + gen_rnd(pkseed, secparam.symbits>>3); + if(ftype == P_FIELD) return new prime_field(secparam, pkseed); + else return new ecc_field(secparam, pkseed); +} + +void gen_rnd_bytes(prf_state_ctx* prf_state, uint8_t* resbuf, uint32_t nbytes) { + AES_KEY_CTX* aes_key; + uint64_t* rndctr; + uint8_t* tmpbuf; + uint32_t i, size; + int32_t dummy; + + aes_key = &(prf_state->aes_key); + rndctr = prf_state->ctr; + size = ceil_divide(nbytes, AES_BYTES); + tmpbuf = (uint8_t*) malloc(sizeof(uint8_t) * size * AES_BYTES); + + //TODO it might be better to store the result directly in resbuf but this would require the invoking routine to pad it to a multiple of AES_BYTES + for(i = 0; i < size; i++, rndctr[0]++) { + EVP_EncryptUpdate(aes_key, tmpbuf + i*AES_BYTES, &dummy, (uint8_t*) rndctr, AES_BYTES); + } + + memcpy(resbuf, tmpbuf, nbytes); + + free(tmpbuf); +} + +void crypto::gen_rnd(uint8_t* resbuf, uint32_t nbytes) { + gen_rnd_bytes(&global_prf_state, resbuf, nbytes); + /*uint8_t* tmpbuf; + uint32_t i; + int32_t dummy; + + uint32_t size = ceil_divide(nbytes, AES_BYTES); + + //TODO it might be better to store the result directly in resbuf but this would require the invoking routine to pad it to a multiple of AES_BYTES + tmpbuf = (uint8_t*) malloc(sizeof(uint8_t) * size * AES_BYTES); + for(i = 0; i < size; i++, rndctr[0]++) { + EVP_EncryptUpdate(&aes_rnd_key, tmpbuf + i*AES_BYTES, &dummy, (uint8_t*) rndctr, AES_BYTES); + } + + memcpy(resbuf, tmpbuf, nbytes); + + free(tmpbuf);*/ +} + + + + +void crypto::gen_rnd_uniform(uint8_t* resbuf, uint64_t mod) { + //TODO: implement +} + +void crypto::encrypt(AES_KEY_CTX* enc_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) { + int32_t dummy; + EVP_EncryptUpdate(enc_key, resbuf, &dummy, inbuf, ninbytes); + //EVP_EncryptFinal_ex(enc_key, resbuf, &dummy); +} +void crypto::decrypt(AES_KEY_CTX* dec_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) { + int32_t dummy; + //cout << "inbuf = " << (hex) << ((uint64_t*) inbuf)[0] << ((uint64_t*) inbuf)[1] << (dec) << endl; + EVP_DecryptUpdate(dec_key, resbuf, &dummy, inbuf, ninbytes); + //EVP_DecryptFinal_ex(dec_key, resbuf, &dummy); + //cout << "outbuf = " << (hex) << ((uint64_t*) resbuf)[0] << ((uint64_t*) resbuf)[1] << (dec) << " (" << dummy << ")" << endl; +} + +void crypto::encrypt(uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) { + encrypt(&aes_enc_key, resbuf, inbuf, ninbytes); +} + + +void crypto::decrypt(uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) { + decrypt(&aes_dec_key, resbuf, inbuf, ninbytes); +} + +void crypto::seed_aes_hash(uint8_t* seed, bc_mode mode, const uint8_t* iv) { + seed_aes_key(&aes_hash_key, seed, mode, iv); +} + +void crypto::seed_aes_enc(uint8_t* seed, bc_mode mode, const uint8_t* iv) { + seed_aes_key(&aes_enc_key, seed, mode, iv, true); + seed_aes_key(&aes_dec_key, seed, mode, iv, false); +} + +void crypto::init_aes_key(AES_KEY_CTX* aes_key, uint8_t* seed, bc_mode mode, const uint8_t* iv) { + seed_aes_key(aes_key, seed, mode, iv); +} + +void crypto::init_aes_key(AES_KEY_CTX* aes_key, uint32_t symbits, uint8_t* seed, bc_mode mode, const uint8_t* iv) { + seed_aes_key(aes_key, symbits, seed, mode, iv); +} + +void crypto::seed_aes_key(AES_KEY_CTX* aeskey, uint8_t* seed, bc_mode mode, const uint8_t* iv, bool encrypt) { + seed_aes_key(aeskey, secparam.symbits, seed, mode, iv, encrypt); +} + + +void crypto::seed_aes_key(AES_KEY_CTX* aeskey, uint32_t symbits, uint8_t* seed, bc_mode mode, const uint8_t* iv, bool encrypt) { + //*aeskey = (AES_KEY_CTX*) malloc(sizeof(AES_KEY_CTX)); + EVP_CIPHER_CTX_init(aeskey); + int (*initfct)(EVP_CIPHER_CTX*,const EVP_CIPHER*, ENGINE*, + const unsigned char*, const unsigned char*); + + if(encrypt) + initfct = EVP_EncryptInit_ex; + else + initfct = EVP_DecryptInit_ex; + + switch (mode) { + case ECB: + if(symbits <= 128) { + initfct(aeskey, EVP_aes_128_ecb(), NULL, seed, iv); + } else { + initfct(aeskey, EVP_aes_256_ecb(), NULL, seed, iv); + } + break; + case CBC: //ECB_ENC + if(symbits <= 128) { + initfct(aeskey, EVP_aes_128_cbc(), NULL, seed, iv); + } else { + initfct(aeskey, EVP_aes_256_cbc(), NULL, seed, iv); + } + break; + default: + if(symbits <= 128) { + initfct(aeskey, EVP_aes_128_ecb(), NULL, seed, iv); + } else { + initfct(aeskey, EVP_aes_256_ecb(), NULL, seed, iv); + } + break; + } +} + + + +void crypto::hash_ctr(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint32_t ctr) { + uint8_t* tmpbuf = (uint8_t*) malloc(ninbytes + sizeof(uint32_t)); + memcpy(tmpbuf, &ctr, sizeof(uint32_t)); + memcpy(tmpbuf + sizeof(uint32_t), inbuf, ninbytes); + hash_routine(resbuf, noutbytes, inbuf, ninbytes, sha_hash_buf); + free(tmpbuf); +} + + +void crypto::hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes) { + hash_routine(resbuf, noutbytes, inbuf, ninbytes, sha_hash_buf); +} + + +//A fixed-key hashing scheme that uses AES, should not be used for real hashing, hashes to AES_BYTES bytes +void crypto::fixed_key_aes_hash(AES_KEY_CTX* aes_key, uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes) { + uint32_t i; + int32_t dummy; + + //assert(aes_hash_key != NULL); + + memset(aes_hash_in_buf, 0, AES_BYTES); + memcpy(aes_hash_in_buf, inbuf, ninbytes); + + //two encryption iterations TODO: not secure since both blocks are treated independently, implement DM or MMO + EVP_EncryptUpdate(aes_key, aes_hash_out_buf, &dummy, aes_hash_in_buf, AES_BYTES); + + ((uint64_t*) aes_hash_out_buf)[0] ^= ((uint64_t*) aes_hash_in_buf)[0]; + ((uint64_t*) aes_hash_out_buf)[1] ^= ((uint64_t*) aes_hash_in_buf)[1]; + + memcpy(resbuf, aes_hash_out_buf, noutbytes); +} + +//An aes hashing scheme that takes as input a counter and an aes-key-struct, should not be used for real hashing +void crypto::aes_cbc_hash(AES_KEY_CTX* aes_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) { + uint32_t i; + int32_t dummy; + + EVP_EncryptUpdate(aes_key, resbuf, &dummy, inbuf, ninbytes); + + //TODO: optimized for faster PSI, input is always size of 32-bytes + for(i = 0; i < ninbytes/AES_BYTES; i++) { + ((uint64_t*) resbuf)[0] ^= ((uint64_t*) inbuf)[2*i]; + ((uint64_t*) resbuf)[1] ^= ((uint64_t*) inbuf)[2*i+1]; + } + //for(i = 0; i < ninbytes; i++) { + // resbuf[i] ^= inbuf[i]; + //} +} + +//Generate a random permutation of neles elements using Knuths algorithm +void crypto::gen_rnd_perm(uint32_t* perm, uint32_t neles) { + uint32_t* rndbuf = (uint32_t*) malloc(sizeof(uint32_t) * neles); + uint32_t i, j; + //TODO Generate random numbers (CAREFUL: NOT UNIFORM) + gen_rnd((uint8_t*) rndbuf, sizeof(uint32_t) * neles); + for(i = 0; i < neles; i++) { + perm[i] = i; + } + for(i = 0; i < neles; i++) { + j = rndbuf[i] % neles; //NOT UNIFORM + swap(perm[i], perm[j]); + } + free(rndbuf); +} + +uint32_t crypto::get_aes_key_bytes() { + if(secparam.symbits == ST.symbits) return 16; + else if(secparam.symbits == MT.symbits) return 16; + else if(secparam.symbits == LT.symbits) return 16; + else if(secparam.symbits == XLT.symbits) return 24; + else if(secparam.symbits == XXLT.symbits) return 32; + else return 64; +} + +uint32_t crypto::get_hash_bytes() { + if(secparam.symbits == ST.symbits) return 20; + else if(secparam.symbits == MT.symbits) return 32; + else if(secparam.symbits == LT.symbits) return 32; + else if(secparam.symbits == XLT.symbits) return 64; + else if(secparam.symbits == XXLT.symbits) return 64; + else return 64; +} + +//Generate a common seed, is only secure in the semi-honest model +void crypto::gen_common_seed(prf_state_ctx* prf_state, CSocket& sock) { + uint8_t *seed_buf, *seed_rcv_buf; + uint32_t seed_bytes, i; + + seed_bytes = get_aes_key_bytes(); + seed_buf = (uint8_t*) malloc(seed_bytes); + seed_rcv_buf = (uint8_t*) malloc(seed_bytes); + + //randomly generate and exchange seed bytes: + gen_rnd(seed_buf, seed_bytes); + sock.Send(seed_buf, seed_bytes); + sock.Receive(seed_rcv_buf, seed_bytes); + + //xor both seeds + for(i = 0; i < seed_bytes; i++) { + seed_buf[i] ^= seed_rcv_buf[i]; + } + + init_prf_state(prf_state, seed_buf); + + free(seed_buf); + free(seed_rcv_buf); +} + +void crypto::init_prf_state(prf_state_ctx* prf_state, uint8_t* seed) { + seed_aes_key(&(prf_state->aes_key), seed); + prf_state->ctr = (uint64_t*) calloc(ceil_divide(secparam.symbits, 8*sizeof(uint64_t)), sizeof(uint64_t)); +} + +void crypto::free_prf_state(prf_state_ctx* prf_state) { + free(prf_state->ctr); + //TODO: delete the AES key +} + + +void sha1_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf) { + SHA_CTX sha; + SHA1_Init(&sha); + SHA1_Update(&sha, inbuf, ninbytes); + SHA1_Final(hash_buf, &sha); + memcpy(resbuf, hash_buf, noutbytes); +} + +void sha256_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf) { + SHA256_CTX sha; + SHA256_Init(&sha); + SHA256_Update(&sha, inbuf, ninbytes); + SHA256_Final(hash_buf, &sha); + memcpy(resbuf, hash_buf, noutbytes); +} + +void sha512_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf) { + SHA512_CTX sha; + SHA512_Init(&sha); + SHA512_Update(&sha, inbuf, ninbytes); + SHA512_Final(hash_buf, &sha); + memcpy(resbuf, hash_buf, noutbytes); +} + +//Read random bytes from /dev/random - copied from stackoverflow (post by zneak) +void gen_secure_random(uint8_t* dest, uint32_t nbytes) { + int32_t randomData = open("/dev/random", O_RDONLY); + uint32_t bytectr = 0; + while (bytectr < nbytes) { + uint32_t result = read(randomData, dest + bytectr, nbytes - bytectr); + if (result < 0) { + cerr << "Unable to read from /dev/random, exiting" << endl; + exit(0); + } + bytectr += result; + } + close(randomData); +} + +seclvl get_sec_lvl(uint32_t symsecbits) { + if(symsecbits == ST.symbits) return ST; + else if(symsecbits == MT.symbits) return MT; + else if(symsecbits == LT.symbits) return LT; + else if(symsecbits == XLT.symbits) return XLT; + else if(symsecbits == XXLT.symbits) return XXLT; + else return LT; +} + +void crypto::aes_compression_hash(AES_KEY_CTX* aes_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes) { + int32_t dummy; + + ((uint64_t*) aes_hash_in_buf)[0] = ((uint64_t*) inbuf)[0] ^ ((uint64_t*) inbuf)[2]; + ((uint64_t*) aes_hash_in_buf)[1] = ((uint64_t*) inbuf)[1] ^ ((uint64_t*) inbuf)[3]; + + EVP_EncryptUpdate(aes_key, aes_hash_buf_y1, &dummy, aes_hash_in_buf, AES_BYTES); + + //cout << (hex) << ((uint64_t*) aes_hash_buf_y1)[0] << ((uint64_t*) aes_hash_buf_y1)[1] << (dec) << endl; + + ((uint64_t*) aes_hash_in_buf)[0] = ((uint64_t*) inbuf)[0] ^ ((uint64_t*) inbuf)[2] ^ ((uint64_t*) aes_hash_buf_y1)[0]; + ((uint64_t*) aes_hash_in_buf)[1] = ((uint64_t*) inbuf)[1] ^ ((uint64_t*) inbuf)[3] ^ ((uint64_t*) aes_hash_buf_y1)[1]; + + EVP_EncryptUpdate(aes_key, aes_hash_buf_y2, &dummy, aes_hash_in_buf, AES_BYTES); + + //cout << (hex) << ((uint64_t*) aes_hash_buf_y2)[0] << ((uint64_t*) aes_hash_buf_y2)[1] << (dec) << endl; + + ((uint64_t*) aes_hash_in_buf)[0] = ((uint64_t*) inbuf)[0] ^ ((uint64_t*) inbuf)[2] ^ ((uint64_t*) aes_hash_buf_y2)[0]; + ((uint64_t*) aes_hash_in_buf)[1] = ((uint64_t*) inbuf)[1] ^ ((uint64_t*) inbuf)[3] ^ ((uint64_t*) aes_hash_buf_y2)[1]; + + EVP_EncryptUpdate(aes_key, resbuf, &dummy, aes_hash_in_buf, AES_BYTES); + + //cout << (hex) << ((uint64_t*) resbuf)[0] << ((uint64_t*) resbuf)[1] << (dec) << endl; + + ((uint64_t*) resbuf)[0] = ((uint64_t*) inbuf)[0] ^ ((uint64_t*) aes_hash_buf_y1)[0] ^ ((uint64_t*) aes_hash_buf_y2)[0] ^ ((uint64_t*) resbuf)[0]; + ((uint64_t*) resbuf)[1] = ((uint64_t*) inbuf)[1] ^ ((uint64_t*) aes_hash_buf_y1)[1] ^ ((uint64_t*) aes_hash_buf_y2)[1] ^ ((uint64_t*) resbuf)[1]; +} + + + + +/*static void InitAndReadCodeWord(REGISTER_SIZE*** codewords) { + uint32_t ncodewords = m_nCodeWordBits; + uint32_t ncwintlen = 8; + *codewords = (REGISTER_SIZE**) malloc(sizeof(REGISTER_SIZE*) * ncodewords); + for(uint32_t i = 0; i < m_nCodewords; i++) { + (*codewords)[i] = (REGISTER_SIZE*) malloc(sizeof(REGISTER_SIZE) * ((ncwintlen * sizeof(uint32_t)) / sizeof(REGISTER_SIZE))); + } + readCodeWords(*codewords); +}*/ diff --git a/src/util/crypto/crypto.h b/src/util/crypto/crypto.h new file mode 100644 index 0000000..ace7cb8 --- /dev/null +++ b/src/util/crypto/crypto.h @@ -0,0 +1,139 @@ +/* + * crypto.h + * + * Created on: Jul 9, 2014 + * Author: mzohner + */ + +#ifndef CRYPTO_H_ +#define CRYPTO_H_ + +#include +#include +#include +#include +#include + +#include "../typedefs.h" +#include "pk-crypto.h" +#include "gmp-pk-crypto.h" +#include "ecc-pk-crypto.h" +#include "../codewords.h" +#include "../socket.h" + + +#define AES_BYTES 16 +#define AES_BITS AES_BYTES*8 + +const uint8_t ZERO_IV[AES_BYTES]={0}; + +const uint8_t const_seed[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + + + +enum bc_mode {ECB, CBC}; + +typedef EVP_CIPHER_CTX AES_KEY_CTX; + +struct prf_state_ctx { + AES_KEY_CTX aes_key; + uint64_t* ctr; +}; + + +//TODO: not thread-secure when multiple threads generate random data +class crypto { + +public: + + crypto(uint32_t symsecbits, uint8_t* seed); + crypto(uint32_t symsecbits); + ~crypto(); + + //Randomness generation routines + void gen_rnd(uint8_t* resbuf, uint32_t numbytes); + //void gen_rnd(prf_state_ctx* prf_state, uint8_t* resbuf, uint32_t nbytes); + void gen_rnd_uniform(uint8_t* resbuf, uint64_t mod); + void gen_rnd_perm(uint32_t* perm, uint32_t neles); + + //Encryption routines + void encrypt(uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes); + void decrypt(uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes); + + //Hash routines + void hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes); + void hash_ctr(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint32_t ctr); + void fixed_key_aes_hash(AES_KEY_CTX* aes_key, uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes); + void fixed_key_aes_hash_ctr(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes); + + void aes_cbc_hash(AES_KEY_CTX* aes_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes); + void aes_compression_hash(AES_KEY_CTX* aes_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes); + + + //Key seed routines + void seed_aes_hash(uint8_t* seed, bc_mode mode=ECB, const uint8_t* iv=ZERO_IV); + void seed_aes_enc(uint8_t* seed, bc_mode mode=ECB, const uint8_t* iv=ZERO_IV); + + //External encryption routines + void init_aes_key(AES_KEY_CTX* aes_key, uint8_t* seed, bc_mode mode=ECB, const uint8_t* iv=ZERO_IV); + void init_aes_key(AES_KEY_CTX* aes_key, uint32_t symbits, uint8_t* seed, bc_mode mode=ECB, const uint8_t* iv=ZERO_IV); + uint32_t get_aes_key_bytes(); + void encrypt(AES_KEY_CTX* enc_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes); + void decrypt(AES_KEY_CTX* dec_key, uint8_t* resbuf, uint8_t* inbuf, uint32_t ninbytes); + + pk_crypto* gen_field(field_type ftype); + + seclvl get_seclvl() {return secparam;}; + uint32_t get_hash_bytes(); + + void gen_common_seed(prf_state_ctx* aes_key, CSocket& sock); +private: + void seed_aes_key(AES_KEY_CTX* aeskey, uint8_t* seed, bc_mode mode=ECB, const uint8_t* iv=ZERO_IV, bool encrypt=true); + void seed_aes_key(AES_KEY_CTX* aeskey, uint32_t symseclvl, uint8_t* seed, bc_mode mode=ECB, const uint8_t* iv=ZERO_IV, bool encrypt=true); + void init(uint32_t symsecbits, uint8_t* seed); + void init_prf_state(prf_state_ctx* prf_state, uint8_t* seed); + void free_prf_state(prf_state_ctx* prf_state); + + AES_KEY_CTX aes_hash_key; + AES_KEY_CTX aes_enc_key; + AES_KEY_CTX aes_dec_key; + prf_state_ctx global_prf_state; + //AES_KEY_CTX aes_rnd_key; + + seclvl secparam; + //uint64_t* rndctr; + uint8_t* aes_hash_in_buf; + uint8_t* aes_hash_out_buf; + uint8_t* aes_hash_buf_y1; + uint8_t* aes_hash_buf_y2; + + uint8_t* sha_hash_buf; + + void (*hash_routine)(uint8_t*, uint32_t, uint8_t*, uint32_t, uint8_t*); +}; + + +//Some functions that should be useable without the class +void sha1_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf); +void sha256_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf); +void sha512_hash(uint8_t* resbuf, uint32_t noutbytes, uint8_t* inbuf, uint32_t ninbytes, uint8_t* hash_buf); +void gen_secure_random(uint8_t* dest, uint32_t nbytes); +void gen_rnd_bytes(prf_state_ctx* prf_state, uint8_t* resbuf, uint32_t nbytes); + +seclvl get_sec_lvl(uint32_t symsecbits); + +static const uint32_t m_nCodeWordBits = 256; +static const uint32_t m_nCodeWordBytes = m_nCodeWordBits/8; + +static void InitAndReadCodeWord(REGISTER_SIZE*** codewords) { + uint32_t ncodewords = m_nCodeWordBits; + uint32_t ncwintlen = 8; + *codewords = (REGISTER_SIZE**) malloc(sizeof(REGISTER_SIZE*) * ncodewords); + for(uint32_t i = 0; i < m_nCodewords; i++) { + (*codewords)[i] = (REGISTER_SIZE*) malloc(sizeof(REGISTER_SIZE) * ((ncwintlen * sizeof(uint32_t)) / sizeof(REGISTER_SIZE))); + } + readCodeWords(*codewords); +} + + +#endif /* CRYPTO_H_ */ diff --git a/src/util/crypto/ecc-pk-crypto.cpp b/src/util/crypto/ecc-pk-crypto.cpp new file mode 100644 index 0000000..4ea7f12 --- /dev/null +++ b/src/util/crypto/ecc-pk-crypto.cpp @@ -0,0 +1,289 @@ +/* + * ecc-pk-crypto.cpp + * + * Created on: Jul 11, 2014 + * Author: mzohner + */ + +#include "ecc-pk-crypto.h" + + +char *ecx163 = (char *) "2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8"; +char *ecy163 = (char *) "289070fb05d38ff58321f2e800536d538ccdaa3d9"; + +char *ecx233 = (char *) "17232ba853a7e731af129f22ff4149563a419c26bf50a4c9d6eefad6126"; +char *ecy233 = (char *) "1db537dece819b7f70f555a67c427a8cd9bf18aeb9b56e0c11056fae6a3"; + +char *ecx283 = (char *) "503213f78ca44883f1a3b8162f188e553cd265f23c1567a16876913b0c2ac2458492836"; +char *ecy283 = (char *) "1ccda380f1c9e318d90f95d07e5426fe87e45c0e8184698e45962364e34116177dd2259"; + + + +void ecc_field::init(seclvl sp, uint8_t* seed) { + + miracl *mip = mirsys(sp.ecckcbits, 2); + fparams = (ecc_fparams*) malloc(sizeof(ecc_fparams)); + secparam = sp; + + //miracl *mip=mirsys(MR_ROUNDUP(abs(163),4),16); + char *ecp = NULL, *ecb = NULL, *ecx = ecx163, *ecy = ecy163; + fparams->BB = new Big(); + fparams->BA = new Big(); + fparams->BP = new Big(); + + + if(secparam.ecckcbits == ST.ecckcbits) { + ecx = ecx163; ecy = ecy163; fparams->m = 163; fparams->a = 7; + fparams->b = 6; fparams->c = 3; *fparams->BA = 1; fparams->secparam = ST.ecckcbits; + } else if(secparam.ecckcbits == MT.ecckcbits) { + ecx = ecx233; ecy = ecy233; fparams->m = 233; fparams->a = 74; + fparams->b = 0; fparams->c = 0; *fparams->BA = 0; fparams->secparam = MT.ecckcbits; + } else if(secparam.ecckcbits == LT.ecckcbits) { + ecx = ecx283; ecy = ecy283; fparams->m = 283; fparams->a = 12; + fparams->b = 7; fparams->c = 5; *fparams->BA = 0; fparams->secparam = LT.ecckcbits; + } else { //Long term security + ecx = ecx283; ecy = ecy283; fparams->m = 283; fparams->a = 12; + fparams->b = 7; fparams->c = 5; *fparams->BA = 0; fparams->secparam = LT.ecckcbits; + } + + //seed the miracl rnd generator + irand((long)(*seed)); + + //Change the base to read in the parameters + mip->IOBASE = 16; + *fparams->BB = 1; + + ecurve2_init(fparams->m, fparams->a, fparams->b, fparams->c, + fparams->BA->getbig(), fparams->BB->getbig(), false, MR_BEST); + + fparams->X = new Big(); + fparams->Y = new Big(); + *fparams->X = ecx; + *fparams->Y = ecy; + + //For ECC, a coordinate is transferred as well as a 1/-1 + fe_bytelen = ceil_divide(secparam.ecckcbits,8) + 1; + + mip->IOBASE = 16; +} + +ecc_field::~ecc_field(){ + delete fparams->Y; + delete fparams->X; + delete fparams->BA; + delete fparams->BB; + delete fparams->BP; + + free(fparams); + + mirexit(); +} + + +num* ecc_field::get_num() { + return new ecc_num(this); +} + +num* ecc_field::get_rnd_num(uint32_t bitlen) { + Big ele; + if(bitlen == 0) + bitlen = secparam.ecckcbits; + ele = rand(bitlen, 2); + return new ecc_num(this, &ele); +} +fe* ecc_field::get_fe() { + return new ecc_fe(this); +} + +fe* ecc_field::get_rnd_fe(uint32_t bitlen) { + return sample_random_point(); +} + +fe* ecc_field::get_generator() { + EC2 g = EC2(*fparams->X, *fparams->Y); + return new ecc_fe(this, &g); +} +fe* ecc_field::get_rnd_generator() { + return sample_random_point(); +} +brickexp* ecc_field::get_brick(fe* gen) { + return new ecc_brickexp(gen, fparams); +} +uint32_t ecc_field::get_size() { + return secparam.ecckcbits; +} + +fe* ecc_field::sample_random_point() { + Big bigtmp; + EC2 point; + uint32_t itmp = rand()%2; + do + { + bigtmp = rand(secparam.symbits, 2); + point = EC2(bigtmp, itmp); + } + while (point_at_infinity(point.get_point())); + return new ecc_fe(this, &point); +} + + + + +ecc_fe::ecc_fe(ecc_field* fld) { + field = fld; + init(); +} + +ecc_fe::ecc_fe(ecc_field* fld, EC2* src) { + field = fld; + init(); + *val = *src; +} +ecc_fe::~ecc_fe() { + delete val; +} + +void ecc_fe::set(fe* src) { + *val = *fe2ec2(src); +} +EC2* ecc_fe::get_val() { + return val; +} + +void ecc_fe::set_mul(fe* a, fe* b) { + set(a); + (*val)+=(*fe2ec2(b)); +} + +void ecc_fe::set_pow(fe* b, num* e) { + set(b); + (*val)*=(*num2Big(e)); +} + +void ecc_fe::set_div(fe* a, fe* b) { + set(a); + (*val)-=(*fe2ec2(b)); +} + +void ecc_fe::set_double_pow_mul(fe* b1, num* e1, fe* b2, num* e2) { + ecurve2_mult2(num2Big(e1)->getbig(), fe2ec2(b1)->get_point(), num2Big(e2)->getbig(), fe2ec2(b2)->get_point(), val->get_point()); +} + +void ecc_fe::import_from_bytes(uint8_t* buf) { + byte_to_point(val, field->fe_byte_size(), buf); +} +//export and pad all leading zeros +void ecc_fe::export_to_bytes(uint8_t* buf) { + point_to_byte(buf, field->fe_byte_size(), val); +} + +void ecc_fe::sample_fe_from_bytes(uint8_t* buf, uint32_t bytelen) { + EC2 point; + Big bigtmp; + uint8_t* tmpbuf = (uint8_t*) calloc(bytelen + 1, sizeof(uint8_t)); + memcpy(tmpbuf+1, buf, bytelen); + bytes_to_big (bytelen, (const char*) tmpbuf, bigtmp.getbig()); + premult(bigtmp.getbig(), MAXMSGSAMPLE, bigtmp.getbig()); + for(int i = 0; i < MAXMSGSAMPLE; i++) + { + point = EC2(bigtmp, 0); + if(!point_at_infinity(point.get_point())) { + *val = point; + return; + } + point = EC2(bigtmp, 1); + if(!point_at_infinity(point.get_point())) { + *val = point; + return; + } + incr(bigtmp.getbig(), 1, bigtmp.getbig()); + } + cerr << "Error while sampling point, exiting!" << endl; + exit(0); +} + + + + +ecc_num::ecc_num(ecc_field* fld) { + field = fld; + val = new Big(); +} +ecc_num::ecc_num(ecc_field* fld, Big* src) { + field = fld; + val = new Big(); + copy(src->getbig(), val->getbig()); +} + +ecc_num::~ecc_num() { + delete val; +} + +Big* ecc_num::get_val() { + return val; +} + +void ecc_num::set(num* src) { + copy(((ecc_num*) src)->get_val()->getbig(), val->getbig()); +} +void ecc_num::set_si(int32_t src) { + convert(src, val->getbig()); +} +void ecc_num::set_add(num* a, num* b) { + add(((ecc_num*) a)->get_val()->getbig(), ((ecc_num*) b)->get_val()->getbig(), val->getbig()); +} +void ecc_num::set_mul(num* a, num* b) { + multiply(((ecc_num*) a)->get_val()->getbig(), ((ecc_num*) b)->get_val()->getbig(), val->getbig()); +} + +void ecc_num::import_from_bytes(uint8_t* buf, uint32_t field_size_bytes) { + bytes_to_big (field_size_bytes, (const char*) buf, val->getbig()); +} + +//export and pad all leading zeros +void ecc_num::export_to_bytes(uint8_t* buf, uint32_t field_size_bytes) { + big_to_bytes ((int32_t) field_size_bytes, val->getbig(), (char*) buf, true); +} + + + +// ecc_brickexp methods +ecc_brickexp::ecc_brickexp(fe* point, ecc_fparams* fparams) { + Big x, y; + fe2ec2(point)->getxy(x, y); + ebrick2_init(&br, x.getbig(), y.getbig(), fparams->BA->getbig(), fparams->BB->getbig(), + fparams->m, fparams->a, fparams->b, fparams->c, 8, fparams->secparam); +} + +void ecc_brickexp::pow(fe* result, num* e) +{ + Big xtmp, ytmp; + mul2_brick(&br, num2Big(e)->getbig(), xtmp.getbig(), ytmp.getbig()); + *fe2ec2(result) = EC2(xtmp, ytmp); +} + + +// general methods + +void byte_to_point(EC2 *point, uint32_t field_size_bytes, uint8_t* pBufIdx) { + uint32_t itmp; + Big bigtmp; + itmp = (uint32_t) (pBufIdx[0]); + + bytes_to_big(field_size_bytes-1, (const char*) (pBufIdx + 1), bigtmp.getbig()); + *point = EC2(bigtmp, itmp); +} + +void point_to_byte(uint8_t* pBufIdx, uint32_t field_size_bytes, EC2* point) { + uint32_t itmp; + Big bigtmp; + //compress to x-point and y-bit and convert to byte array + itmp = point->get(bigtmp); + + //first store the y-bit + pBufIdx[0] = (uint8_t) (itmp & 0x01); + + //then store the x-coordinate (sec-param/8 byte size) + big_to_bytes(field_size_bytes-1, bigtmp.getbig(), (char*) pBufIdx+1, true); + +} diff --git a/src/util/crypto/ecc-pk-crypto.h b/src/util/crypto/ecc-pk-crypto.h new file mode 100644 index 0000000..2adfda9 --- /dev/null +++ b/src/util/crypto/ecc-pk-crypto.h @@ -0,0 +1,143 @@ +/* + * ecc-pk-crypto.h + * + * Created on: Jul 11, 2014 + * Author: mzohner + */ + +#ifndef ECC_PK_CRYPTO_H_ +#define ECC_PK_CRYPTO_H_ + +#include "pk-crypto.h" + +#include "../../Miracl/ecn.h" +#include "../../Miracl/big.h" +#include "../../Miracl/ec2.h" + + +#define fe2ec2(fieldele) (((ecc_fe*) (fieldele))->get_val()) +#define num2Big(number) (((ecc_num*) (number))->get_val()) + +//how many repetitions of random point samplings should be performed +#define MAXMSGSAMPLE 256 + + +struct ecc_fparams { + Big* BA; + Big* BB; + Big* X; + Big* Y; + Big* BP; + int32_t m; + int32_t a; + int32_t b; + int32_t c; + uint32_t secparam; +}; + +class ecc_num; +class ecc_fe; +class ecc_brickexp; + +class ecc_field : public pk_crypto { +public: + ecc_field(seclvl sp, uint8_t* seed) : pk_crypto(sp, seed) {init(sp, seed);}; + ~ecc_field(); + + num* get_num(); + num* get_rnd_num(uint32_t bitlen=0); + fe* get_fe(); + fe* get_rnd_fe(uint32_t bitlen); + fe* get_generator(); + fe* get_rnd_generator(); + uint32_t get_size(); + //fe* sample_fe_from_bytes(uint8_t* buf, uint32_t bytelen); + uint32_t num_byte_size() {return ceil_divide(secparam.ecckcbits, 8);} + uint32_t get_field_size() {return secparam.ecckcbits;}; + + brickexp* get_brick(fe* gen); + ecc_fparams* get_params() {return fparams;}; + + //#define SampleFieldElementFromBytes(ele, buf, bytelen) ByteToFieldElement(ele, bytelen, buf) + //#define FieldSampleRandomGenerator(g, div, params) SampleRandomGenerator(g, div, (¶ms)) +protected: + void init(seclvl sp, uint8_t* seed); +private: + fe* sample_random_point(); + /*Big* BA; + Big* BB; + Big* X; + Big* Y; + Big* BP; + int32_t m; + int32_t a; + int32_t b; + int32_t c;*/ + ecc_fparams* fparams; +}; + + + +class ecc_num : public num { + //This is a Big +public: + ecc_num(ecc_field* fld); + ecc_num(ecc_field* fld, Big* src); + ~ecc_num(); + void set(num* src); + void set_si(int32_t src); + void set_add(num* a, num* b); + void set_mul(num* a, num* b); + + Big* get_val(); + + void export_to_bytes(uint8_t* buf, uint32_t field_size_bytes); + void import_from_bytes(uint8_t* buf, uint32_t field_size_bytes); + void set_rnd(uint32_t bits); + void print() {cout << (*val) << endl;}; + +private: + Big* val; + ecc_field* field; +}; + + +class ecc_fe : public fe { +public: + ecc_fe(ecc_field* fld); + ecc_fe(ecc_field* fld, EC2* src); + ~ecc_fe(); + void set(fe* src); + EC2* get_val(); + void set_mul(fe* a, fe* b); + + void set_pow(fe* b, num* e); + void set_div(fe* a, fe* b); + void set_double_pow_mul(fe* b1, num* e1, fe* b2, num* e2); + void export_to_bytes(uint8_t* buf); + void import_from_bytes(uint8_t* buf); + void sample_fe_from_bytes(uint8_t* buf, uint32_t bytelen); + + void print() {cout << (*val) << endl;}; + + +private: + void init() {val = new EC2();}; + EC2* val; + ecc_field* field; +}; + +class ecc_brickexp : public brickexp { +public: + ecc_brickexp(fe* point, ecc_fparams* fparams); + ~ecc_brickexp() {ebrick2_end(&br);} + + void pow(fe* res, num* e); +private: + ebrick2 br; +}; + +void point_to_byte(uint8_t* pBufIdx, uint32_t field_size_bytes, EC2* to_export); +void byte_to_point(EC2* to_export, uint32_t field_size_bytes, uint8_t* pBufIdx); + +#endif /* ECC_PK_CRYPTO_H_ */ diff --git a/src/util/crypto/gmp-pk-crypto.cpp b/src/util/crypto/gmp-pk-crypto.cpp new file mode 100644 index 0000000..5329b93 --- /dev/null +++ b/src/util/crypto/gmp-pk-crypto.cpp @@ -0,0 +1,280 @@ +/* + * gmp-pk-crypto.cpp + * + * Created on: Jul 11, 2014 + * Author: mzohner + */ + +#include "gmp-pk-crypto.h" + + + +//Parameters for different security levels +const char* ifcp1024 = + "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371"; +const char* ifcg1024 = + "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5"; +const char* ifcq1024 = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353"; + +const char* ifcp2048 = + "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC2129037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708B3BF8A317091883681286130BC8985DB1602E714415D9330278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486DCDF93ACC44328387315D75E198C641A480CD86A1B9E587E8BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71CF9DE5384E71B81C0AC4DFFE0C10E64F"; +const char* ifcg2048 = + "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFAAB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7C17669101999024AF4D027275AC1348BB8A762D0521BC98AE247150422EA1ED409939D54DA7460CDB5F6C6B250717CBEF180EB34118E98D119529A45D6F834566E3025E316A330EFBB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC017981BC087F2A7065B384B890D3191F2BFA"; +const char* ifcq2048 = + "801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB"; + +const char* ifcp3072 = + "4660194093823565506151007332698542081380390944320667936220310340292682538415201463451360005469701273992420569531194415296871671272562243754789577412471203509686259933515539120145538889500684305065682267020422897056483203401642088590732633756278140548667640739272073464322452643609409839498807131787408915921523565001045685221279165409792825261753615641493423723165471868882028678262386826730035778207616806238910696112513243832793252430036079010833108716296401084350809152423357477416465451376967706115065572717893335336664895800189754170750266169252030669114411476002012410621336179123441424048589750501111541393610787337793314723136089502117079738181113934544472215273637670210480814609550715859453809706797176331069587697357167970759889883398852942449568449890603652456531060380065260476714266615239827983706919432589669744367350756821903843388105282430635020233707272521317674908786962912228887786913664926989228941514639"; +const char* ifcg3072 = + "326984479748743614358878489890111032378521682641889472728164592588245254735528952815040417677135099463681521117067228131302984716932197927691804537047698386112034189358693637764887258325546424576668654933254773228919028116187485325776123548207630122958160311311825230114818910264101591293903307807790394765896174615027850669640300925521032111542648598127663424462192520490917608209583615366128345913820058976254028107968965281721876376153097516948596625654797921929621363755081263164203185942482227411046415127689226121648774535224687708280963930985498313715804706762069594298539593719253724193098201932449349224692341850008449711165375995101343314201170357859203662648251088921851885444086613889195257606710405156897225917687758015354941738963422772322756212536951044725465040734436163477969317027796051497934165333064621979305683254912099909723895352817468375097484456065145582788954244042708099846989842764657922387568064"; +const char* ifcq3072 = + "95729504467608377623766753562217147614989054519467474668915026082895293552781"; + +gmp_num::gmp_num(prime_field* fld) { + field = fld; + mpz_init(val); +} +gmp_num::gmp_num(prime_field* fld, mpz_t src) { + field = fld; + mpz_init(val); + mpz_set(val, src); +} + +gmp_num::~gmp_num() { + mpz_clear(val); +} + +mpz_t* gmp_num::get_val() { + return &val; +} + +void gmp_num::set(num* src) { + mpz_set(val, *num2mpz(src)); +} +void gmp_num::set_si(int32_t src) { + mpz_set_si(val, src); +} +void gmp_num::set_add(num* a, num* b) { + mpz_add(val, *num2mpz(a), *num2mpz(b)); +} +void gmp_num::set_mul(num* a, num* b) { + mpz_mul(val, *num2mpz(a), *num2mpz(b)); +} + +void gmp_num::import_from_bytes(uint8_t* buf, uint32_t field_size) { + mpz_import(val, field_size, 1, sizeof((buf)[0]), 0, 0, (buf)); +} + +//export and pad all leading zeros +void gmp_num::export_to_bytes(uint8_t* buf, uint32_t field_size) { + mpz_export_padded(buf, ceil_divide(field_size, 8), val); +} + +num* prime_field::get_rnd_num(uint32_t bitlen) { + mpz_t val; + if(bitlen == 0) + bitlen = secparam.ifcbits; + mpz_init(val); + mpz_urandomm(val, rnd_state, q); //sample_rnd_mpz_t(val, bits, fp); + return new gmp_num(this, val); +} + +fe* prime_field::get_rnd_fe(uint32_t bitlen) { + mpz_t val; + mpz_init(val); + mpz_urandomm(val, rnd_state, q); //sample_rnd_mpz_t(val, bits, fp); + return new gmp_fe(this, val); +} + +gmp_fe::gmp_fe(prime_field* fld) { + field = fld; + init(); +} + +gmp_fe::gmp_fe(prime_field* fld, mpz_t src) { + field = fld; + init(); + mpz_set(val, src); +} +gmp_fe::~gmp_fe() { + mpz_clear(val); +} + +void gmp_fe::set(fe* src) { + mpz_set(val, *fe2mpz(src)); +} +mpz_t* gmp_fe::get_val() { + return &val; +} + +void gmp_fe::set_mul(fe* a, fe* b) { + mpz_mul(val, *fe2mpz(a), *fe2mpz(b)); + mpz_mod(val, val, *field->get_p()); +} + +void gmp_fe::set_pow(fe* b, num* e) { + mpz_powm(val, *fe2mpz(b), *num2mpz(e), *field->get_p()); +} + +void gmp_fe::set_div(fe* a, fe* b) { + mpz_invert(val, *fe2mpz(b), *field->get_p()); + mpz_mul(val, *fe2mpz(a), val); + mpz_mod(val, val, *field->get_p()); +} + +void gmp_fe::set_double_pow_mul(fe* b1, num* e1, fe* b2, num* e2) { + gmp_fe tmpa(field), tmpb(field); + tmpa.set_pow(b1, e1); //tmpa=b1^e1 + tmpb.set_pow(b2, e2); //tmpb=b2^e1 + set_mul(&tmpa, &tmpb); //val = b1^e1 * b2^e2 +} + +void gmp_fe::import_from_bytes(uint8_t* buf) { + mpz_import(val, field->fe_byte_size(), 1, sizeof((buf)[0]), 0, 0, (buf)); +} +//export and pad all leading zeros +void gmp_fe::export_to_bytes(uint8_t* buf) { + mpz_export_padded(buf, field->fe_byte_size(), val); +} + +void gmp_fe::sample_fe_from_bytes(uint8_t* buf, uint32_t bytelen) { + mpz_import(val, bytelen, 1, sizeof((buf)[0]), 0, 0, (buf)); + mpz_mod(val, val, *field->get_p()); +} + + +num* prime_field::get_num() { + return new gmp_num(this); +} +fe* prime_field::get_fe() { + return new gmp_fe(this); +} +mpz_t* prime_field::get_p() { + return &p; +} +fe* prime_field::get_generator() { + return new gmp_fe(this, g); +} + +fe* prime_field::get_rnd_generator() { + mpz_t tmp; + mpz_init(tmp); + //sample random hi -- sample random element x in Zp, and then compute x^{(p-1)/q} mod p + do { + mpz_urandomb(tmp, rnd_state, secparam.ifcbits); + mpz_mod(tmp, tmp, p); + mpz_powm(tmp, tmp, q, p); + } while(!(mpz_cmp_ui(tmp, (uint32_t) 1) ) ); + return new gmp_fe(this, tmp); +} + +brickexp* prime_field::get_brick(fe* gen) { + return new gmp_brickexp(gen, this); +} + +uint32_t prime_field::get_size() { + return secparam.ifcbits; +} + +void prime_field::init(seclvl sp, uint8_t* seed) { + mpz_t rnd_seed; + + //TODO: change to inits if working correctly + mpz_init(p); + mpz_init(g); + mpz_init(q); + mpz_init(rnd_seed); + secparam = sp; + + mpz_import(rnd_seed, secparam.symbits, 1, sizeof((seed)[0]), 0, 0, seed); + + if (secparam.ifcbits == ST.ifcbits) { + mpz_set_str(p, ifcp1024, 16); + mpz_set_str(g, ifcg1024, 16); + mpz_set_str(q, ifcq1024, 16); + } else if (secparam.ifcbits == MT.ifcbits) { + mpz_set_str(p, ifcp2048, 16); + mpz_set_str(g, ifcg2048, 16); + mpz_set_str(q, ifcq2048, 16); + } else if (secparam.ifcbits == LT.ifcbits) { + mpz_set_str(p, ifcp3072, 10); + mpz_set_str(g, ifcg3072, 10); + mpz_set_str(q, ifcq3072, 10); + } else //Long term security + { + mpz_set_str(p, ifcp3072, 10); + mpz_set_str(g, ifcg3072, 10); + mpz_set_str(q, ifcq1024, 10); + } + + //TODO it would be better not to GMP's internal random generator, since it is not secure + gmp_randinit_default(rnd_state); + gmp_randseed(rnd_state, rnd_seed); + fe_bytelen = ceil_divide(secparam.ifcbits, 8); +} + +prime_field::~prime_field(){ + gmp_randclear(rnd_state); + mpz_clear(p); + mpz_clear(g); + mpz_clear(q); +} + + +gmp_brickexp::~gmp_brickexp() { + for(uint32_t i = 0; i < m_numberOfElements; i++) + mpz_clear(m_table[i]); + free(m_table); +}; + +void gmp_brickexp::init(fe* g, prime_field* pfield) { + field = pfield; + + m_numberOfElements = field->get_field_size(); + + m_table = (mpz_t*) malloc(sizeof(mpz_t) * m_numberOfElements); + for(uint32_t i = 0; i < m_numberOfElements; i++) + { + mpz_init(m_table[i]); + } + + mpz_set(m_table[0], *((gmp_fe*) g)->get_val()); + for (unsigned u=1; uget_p()); + } +} + +void gmp_brickexp::pow(fe* result, num* e) { + mpz_t* res = ((gmp_fe*) result)->get_val();//->set_ui(1);//(result, 1); + mpz_t* exp = ((gmp_num*) e)->get_val(); + uint32_t u; + + + mpz_set_ui(*res, 1); + for ( u=0; uget_p()); + } + } +} + + + + +// mpz_export does not fill leading zeros, thus a prepending of leading 0s is required +void mpz_export_padded(uint8_t* pBufIdx, uint32_t field_size_bytes, mpz_t to_export) { + size_t size = 0; + mpz_export(pBufIdx, &size, 1, sizeof(pBufIdx[0]), 0, 0, to_export); + + if (size < field_size_bytes) { + for (int i = 0; i + size < field_size_bytes; i++) { + pBufIdx[i] = 0; + } + pBufIdx += (field_size_bytes - size); + mpz_export(pBufIdx, &size, 1, sizeof(pBufIdx[0]), 0, 0, to_export); + } +} diff --git a/src/util/crypto/gmp-pk-crypto.h b/src/util/crypto/gmp-pk-crypto.h new file mode 100644 index 0000000..d2b7f13 --- /dev/null +++ b/src/util/crypto/gmp-pk-crypto.h @@ -0,0 +1,118 @@ +/* + * gmp_pk-crypto.h + * + * Created on: Jul 11, 2014 + * Author: mzohner + */ + +#ifndef GMP_PK_CRYPTO_H_ +#define GMP_PK_CRYPTO_H_ + +#include "pk-crypto.h" +#include + +class prime_field; +class gmp_fe; +class gmp_num; +class gmp_brickexp; + +#define fe2mpz(fieldele) (((gmp_fe*) (fieldele))->get_val()) +#define num2mpz(number) (((gmp_num*) (number))->get_val()) + +class prime_field : public pk_crypto { +public: + prime_field(seclvl sp, uint8_t* seed) : pk_crypto(sp, seed) {init(sp, seed);}; + ~prime_field(); + + num* get_num(); + num* get_rnd_num(uint32_t bitlen=0); + fe* get_fe(); + fe* get_rnd_fe(uint32_t bitlen); + fe* get_generator(); + fe* get_rnd_generator(); + + mpz_t* get_p(); + uint32_t get_size(); + //fe* sample_fe_from_bytes(uint8_t* buf, uint32_t bytelen); + brickexp* get_brick(fe* gen); + + uint32_t num_byte_size() {return ceil_divide(secparam.ifcbits, 8);} + uint32_t get_field_size() {return secparam.ifcbits;}; + +protected: + void init(seclvl sp, uint8_t* seed); +private: + mpz_t p; + mpz_t g; + mpz_t q; + gmp_randstate_t rnd_state; +}; + + + +class gmp_fe : public fe { +public: + gmp_fe(prime_field* fld); + gmp_fe(prime_field* fld, mpz_t src); + ~gmp_fe(); + void set(fe* src); + mpz_t* get_val(); + + + void set_mul(fe* a, fe* b); + void set_pow(fe* b, num* e); + void set_div(fe* a, fe* b); + void set_double_pow_mul(fe* b1, num* e1, fe* b2, num* e2); + void export_to_bytes(uint8_t* buf); + void import_from_bytes(uint8_t* buf); + void sample_fe_from_bytes(uint8_t* buf, uint32_t bytelen); + void print() {cout << val << endl;}; + +private: + void init() {mpz_init(val); }; + mpz_t val; + prime_field* field; + +}; + +class gmp_num : public num { +public: + gmp_num(prime_field* fld); + gmp_num(prime_field* fld, mpz_t src); + ~gmp_num(); + void set(num* src); + void set_si(int32_t src); + void set_add(num* a, num* b); + void set_mul(num* a, num* b); + + mpz_t* get_val(); + + void export_to_bytes(uint8_t* buf, uint32_t field_size); + void import_from_bytes(uint8_t* buf, uint32_t field_size); + void set_rnd(uint32_t bits); + void print() {cout << val << endl;}; +private: + mpz_t val; + prime_field* field; +}; + + +class gmp_brickexp : public brickexp { +public: + gmp_brickexp(fe* g, prime_field* pfield){init(g, pfield);}; + ~gmp_brickexp(); + + void pow(fe* result, num* e); + void init(fe* g, prime_field* pfield); + +private: + uint32_t m_numberOfElements; + mpz_t* m_table; + prime_field* field; +}; + + +// mpz_export does not fill leading zeros, thus a prepending of leading 0s is required +void mpz_export_padded(uint8_t* pBufIdx, uint32_t field_size, mpz_t to_export); + +#endif /* GMP_PK_CRYPTO_H_ */ diff --git a/src/util/crypto/pk-crypto.h b/src/util/crypto/pk-crypto.h new file mode 100644 index 0000000..aa9ce39 --- /dev/null +++ b/src/util/crypto/pk-crypto.h @@ -0,0 +1,88 @@ +/* + * pk-crypto.h + * + * Created on: Jul 10, 2014 + * Author: mzohner + */ + +#ifndef PK_CRYPTO_H_ +#define PK_CRYPTO_H_ + +#include "../typedefs.h" + + +class num; +class fe; +class brickexp; + +class pk_crypto { +public: + pk_crypto(seclvl sp, uint8_t* seed){}; + ~pk_crypto(){}; + virtual num* get_num() = 0; + virtual num* get_rnd_num(uint32_t bitlen=0) = 0; + virtual fe* get_fe() = 0; + virtual fe* get_rnd_fe(uint32_t bitlen) = 0; + virtual fe* get_generator() = 0; + virtual fe* get_rnd_generator() = 0; + virtual uint32_t num_byte_size() = 0; + //virtual fe* sample_fe_from_bytes(uint8_t* buf, uint32_t bytelen) = 0; + uint32_t fe_byte_size() {return fe_bytelen;}; + virtual uint32_t get_field_size() =0; + virtual brickexp* get_brick(fe* gen) = 0; + + +protected: + virtual void init(seclvl secparam, uint8_t* seed) = 0; + uint32_t fe_bytelen; + seclvl secparam; +}; + + + + +//class number +class num { +public: + num(){}; + ~num(){}; + virtual void set(num* src) = 0; + virtual void set_si(int32_t src) = 0; + virtual void set_add(num* a, num* b) = 0; + virtual void set_mul(num* a, num* b) = 0; + virtual void export_to_bytes(uint8_t* buf, uint32_t field_size) = 0; + virtual void import_from_bytes(uint8_t* buf, uint32_t field_size) = 0; + virtual void print() = 0; +}; + + +//class field_element +class fe { +public: + fe(){}; + ~fe(){}; + virtual void set(fe* src) = 0; + virtual void set_mul(fe* a, fe* b) = 0; + virtual void set_pow(fe* b, num* e) = 0; + virtual void set_div(fe* a, fe* b) = 0; + virtual void set_double_pow_mul(fe* b1, num* e1, fe* b2, num* e2) = 0; + virtual void export_to_bytes(uint8_t* buf) = 0; + virtual void import_from_bytes(uint8_t* buf) = 0; + virtual void sample_fe_from_bytes(uint8_t* buf, uint32_t bytelen) = 0; + virtual void print() = 0; + +protected: + virtual void init() = 0; +}; + +class brickexp { +public: + brickexp(){}; + ~brickexp(){}; + + virtual void pow(fe* result, num* e) = 0; +}; + + + +#endif /* PK_CRYPTO_H_ */ diff --git a/src/util/miracl.a b/src/util/miracl.a new file mode 100644 index 0000000000000000000000000000000000000000..c4508d3a1bddf7ee62c116ef86cd698015e76d32 GIT binary patch literal 529540 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

fQK^;$qqv{&jgXs5 zwaNTNXhktNEROGqVI&7k2KNNr7JidL<%1XiVq}Ls2R4l0_`b*%iW%)#G-fvJQp8(# zxFVRH)Z98qKfZSfB>Fk$H1uJw=j^@tQM;j6muh=$*^9}xdlFZ@4+%@p*`6-*I-%-@ zR}Mq0uF9wIjc;`tKAQBH4eaqYrYkqNh%o@Iosjmk8gG3n(f=&#<@~psu0!9@>hF&H zvP!2Tdjsjgb_z93=t?6I26dv?VC#h2>oF&?wC5fw{FHPapG$+qt(@C}i?yT0Ehw6r zyX*rl50h;hQi-l@cn|Ns(b)5svIZvFWcK^uP^=x-(ilR_Qpv%uK=Y8QGT5ax#neM` z%sriRc0l&@ZGZCGc+hp^D)C$DNw)r7KalCzlGE|2`o)94DH#}z$e zw4>U@N`4K3^_9hWA?l&^m85lnOc9XEwNjJwqmIJVP@?0n)JI3<4pRxfH;d*dVBtOc zz%yK1$vJCQGmyxqf9R{7b1)VDT)73foOZq9p|F0&{DoFiyH@bG*}o<4e{x1Le+;_Y zFY^OboZf~Ut*6PbsMww*uF0|8Os(V=_v`#9%2;Wsn*+qU{i*D$=tY@sY*!TAv-0+3 z0auvU!!17}h3TNIp<++0e%&&7UF#qQ?k!w6-=4e)&+f-K0iGx8bYn#3w)?HqCc8!+j#k3lWh+ruIfYq?40vPx%#W=HsM_SMzj&@Cm8pRwpSDVZ7>X4 zCDuH(&|}a}k3l+>pZE#nJc}(Fhfi_L(mXkzx)|jQ+0L}LB(slZ?|^5F{pw_XCJYVP z?t06J1?6%C{i+TK`h~!3r%>G-s;bSAM^m#{E`jEz_HYapSDisM;&0r^ffA_a&t%9OUs|2CXvssrN>Ih zp1sf&Ku@6ih)_JQWqzURF-klf#!y_k@K(>S?KAtrPpxPdBt`I<>}&8v3ltZP{@VshPt%&t@FwpCU?uW;d`RW*2L#V)D3mduFGx=pp!*H!Lb*K$l< zb5i&uM}7j}=K z)p>QbxNO3oc_i@5x?A?Xg>+R#RaIAD8(VHoOJ1`) z4fjCA+(ykONU@^2Y7%I=MxJVMQl6Eb?xT*bdRkrMimEf}YU^?TZU2njP=-eQ*}xjV zoQlMma#a>L_^t-u6yoN4Tn)uA#{HN)+dP&-?Tayf^{Sea>!z=+TCq!JMD_7dI&-TL zj~e(K;Xu+)b7^Zw+I|TV9y2!{@I+7Z0ts!8V82IiOVtY5qjxE;kFouFlOxThy8U}| z_#Sj}0yUDt&b}Qky;u9^lldHhcF3ic3b8D@J5? zotCdzwaW>0YbsY)UsYATt_tLnN16^^^0cRUUMs4mxV?p2crn>T~J9;3Z6u&k~0*dHicv8+vv z${NbmiV?@KtWB-EuBLiC(lY@TIb2yIKRPuT9zbu$@#|pOMOpJ@*Z3tJzin03cT^8s zcA3>CUo-h2TpRJTy1@8#ur4ru#kyeW@bcLa<7x0&>iIoQrp5GH-D3QZwiP2%l<%0j zcADKVH4C^gt+#t#kI`0*S23+c**aZ4h4J;bVdoPj@1#3er%UT7YkIJ6aFiECe#Lsv zWSdfl{R`g9w8rpj1kbsiAKQ#jmRL{BhoLZ1%YRQp?^t;nEY6}nrqtmm+FvmZJLi8V z(?I<`?D-azFORpH}pRB_BET0Tq@>#As;`P({Kbk4`6a8N)b#eb(YvK1A*We>~acd_jJKk5E2_ZVkdsv;}!eO+MO zHN5Y_q&+@p9P4Ij#j}Lm>#wEZH06ttkj`8%{JiU!oK+k1{Nx}=sDp< z`hgZF=hN<|;vSFpwmQJZG`M`s!-T_@^9heSTj0O~7&dF!lnUi9?3R&0n&3_P7>zZ-+R{(s1G9b+Y62 zd5dxUvBf!mNzeK7yDuT1?!~j`oaYibe~BGJVG1y*?8-2EtN4_PaPNe(k9|@WLOb)^ zle6dHNM-607fUZGUb?GAOXfRKaH__+(ZfsK)#7uOxQiu$2QGZFypR@OP$)F(<09Q9 zo=Y*$95b&>lL?t(S?G_9c1kJgpI1hQ)1KW0b1}R*Q_xn{aYjFZC5omW9dGjZez>=j0jzbL-$@x{yYBOfxVqmuKnL;t26 z_r=%7A{=XkvoDTgSw@xLJkzdZ*3eGG2vzz_k` z%PwcgulEu_;19cy0k_o^!{F?9FqHH8R1D63M4(su$q3H&FT{JB;40_+g6}Exe-T{i zUyZ?cLwm)*$9mSv0fMU@^vo7^frFe!$Izc7xbmMZxbnY9aHU@ngI^=KruRF7vr8W2 z4DB(~tMm^Dy~^{n;F{hyV(?KlV2nWinlC2{uIcI%Tw`P?bE>hqBp{J9vs+Pgl=?RGE0mH*fnJSDiM>m@=<)T&}+IrE4b2M8-xEq zaOHDH4E|6IUJzX4eMNAs-}dm15T;A@Fj{cUm$5PUbitL+Ji#?x7sueMV({x?@E;4V z>Ah8OP4907S2-UQT+{nZ3|`|M4Ah&}tM3tfAF;RnV(_B`r|&k{=edGwyZ?mX+U`$h zrxStla84xf`J&*Qj}G|%2(Eg#O>oVx-^bw3#NgWnSNR)wgFrbIKV0zr@GQvrF~ODo za=}&qpNql27K7g<_<<7deKGhSWAHZw=Vg%pDDTf?dNp0s1=sfalY%RsO9a>S_6e@} z_yxf=AHNZUhckI8r_$df^eWE=!L?q!TX3bXW#L39&-YHj55n`Hw-aLUQv_H3=L+5= zeEu^A|B>LDFMkwV_55rM{)XTx&wOSE0@JH}elEDuKQFlI;Wfd>fLGAZF5c-y{))dx zaLvc}$KZzuuJUvWuKGDuaHaq67<>W~k3c!qPL38_`OFoZEJJ#~FE~{a@S6qK^llKG zSrq8+7F^?fLvW4vJ!FbdF8?8dYrGQ#*LXVxSA9+wT;-W7xV8)3f@}NI7lW^g!LN?N zzbQB`gWkR`xW@bA7<^E0)x#r#^D^-Nli-@(*92GkDmKyx%omkstl+B8ZwP)U-U;#E zAh_Dq&jr`|^V(?wqDM6t8%I84A)lQlPSNaoT@b3z)>HVSL;}Ca{ z^Ou5uSnv&kYrIbh-Xiob3a)zC5rdEL^H4kKLGThi2(I$X7F^?97=vFDgI^)I_LqJl zxW>DSpBJP22Z$a%Ex6K;RHRhQG$O2&qBU*2(Eli7hLW3V!<`v2V(HA3$FFaje<9d zJhut1?d+X`tDG+guK8QZMgpPS-s%Kb`uzo0Js&E#(tkv77X3({f@}VsD7f0&EWwrk zLc!GzFBe?pTq(Hn`I6vD|82ol&L0b|^gkC|_5Xz68t=<7`0gBhA~0VxU3&_y{7({G z>CY8hNDg&ryPF zdQTNx<(V(I^0_z$|E%Du=l>8~<-AUC<-bO7m2;ioD(8KIEB#*u*ZQZ1l^p{0JPv=M zJRU5#%6YWlD(CSr_)@`D4_}DEzbm-Ld#m6Y?>#a2lY%Rs(fc#CF1=g|5z;$WaOK}2 zxYnz41lMwRvEUyS{u#kp^@e!ADY)wArWkx(41TxZn%>QVYq|g(uKlqt!Il0h!L{D|y5LIxy5MS8BM&lh zOdiemy#!Z#K1^`6+bM!8|C3_yxq@rFO9WRw{er9AUM;xNe?xG!!=DJQ@vaqI`TS0B zrGH#-mFF43mHth^mH)fmU(~ny!t4v#JQ??mofW1b<9$rQa&}1fhRkaJBzWu#g~B;g3@dA>O%y(-j@?OJneC z4F1&^{0A}kI>A-W7X_!u1bJQ;{3yZeKY$wqrc3qHEcnrQ7Wf=4c)Q@o3*IRB9Klu2 z&kLRu`dww{2;;S3$AirDmYy%fzQ2ytNr{@ zaEwgWnc|-yehj zB?f;j22V6oYff*fho%_3Ee1a>2A>myFO9+TG59xQ@S9@r4T7s3zAU)fZ51bDydJdt zCInC6d9bU!1=o7uV8NCDc)^u_QgG$pDY)`qAb5w!^BKX95j-z=yWndC|Cr!63*IRB zYl3Sz9d!tZ5LoV1&xZ)E^~^^EpMrOSK2M0j&y2yB#Na;?T;;q|aLt!{1XuaD2(IrM=Yjt^!L>ad{b6Hc@+h7WT;=&q41Q!wk&nugiNS9WT=npT;41&qf|t}Yt;Kj1 z?-HC{y$&nNuY5ifgHMgYFBDwUwMOu% zBL4=#j~4vV7kz?Jo+Aa<`sXCURsWwCT=oBT!BuZRiNWuT z!5@pk>o}-JV0xAR2V(G#$Kaoe!LJZp^|MZJ&98?A*Zg`e2H%B)d<4p`^!vu(X9=$P z+atK&ELBOS3Z9eT;r`d!jfg_((=BS;L4{}aE*7d;A&UP1=oDP zItJemgFhC7zZ`?Vn@&%Ja(inNT+`Jf_%ys1%Ht}*HQ&D#gFhp<=KHIHtNz~-T;)ly z(np~D@5Nt`r&Vy(|K|l)KHCJR2?Rc`3a<2>Y@84n@9y{u^k)e^UGNKI@XKQGRf20j z_G>Zt|BJ!z5c~v@=eL4ux*ii;;xlFKgwr+!N=lR(Ax(DSNfJ1{Ky#m zm>B%j7<_IFenAX=Sq%Oq!L@$4L2#-nq<6F6n%*a4@E2n6H)8O4MR;nsE%@Ge z7Ua1m2LEOZenSlYJHfSFte;3}oID!u-7)w>G5B9%@Hb-c>W>%WRXaaeaLuoYf~($6 z5PUzd40=07aOJ;1aIH_42(I*B5&T5q|IHZu|Ha@>3$EjW*JJSgIT%Kuov1u#2(I$a z648mO$j6p;q7K(`F}dAf)7uPO z2Kq&UpDg(582pA9{MRw~OM1Xub$3a)nbatyxvBnXGVbSeD-G5CZSe0mIim*A?;*9F&j>n58xJIV7g!8xQ0 z`b-JlF8CROHwr#iaMjyq1lM|FrQn)hR|&3so)i24iTAziY#~(PPwSZz1Xn-X62Vn( zR|&51{zP!4-!8b?PZgb#2$ViWALBF;CII0kH+91qf-@u z=~6v>L2#Ax+cEg>1y_4}Qt)=f5$y0S!5anNg_(*_uIJH$tGzV~uJ(4A;M&ib7=uq1 zT=TbAaJA2G39k0JU2tB8^uCXSD}-`Aj}u(uJwMnHipb6n=v|?`<;J^g!1|z?)+58>PRb`Pk^#7$531ghl>> z82`e)W4|rOsceKT_P?Vg{Vb2aQHBqCyrI%P2>ipoQSIe83k|~DGJLSk;j7BXtG*><_i244>}vgZm*d1UY*>KAM2wVIQdWGCb@9wX_`H%e{Y186Ng;x}gjY z`#1fz3=jJ^Z7;*8H#+`})$W&%`M0Lg-A^dPw|RVS8D8)0X=xcA_FY<4hPQb7N6YZA z|IyeQ7k`jv+oA6MvNAmELv&{u9`+$>*rk+D*oWwxGCb@Mv9e;Q_cXNbpoZJXvJAB4d6;JUQZqxYs)7HDa;=yDSSmS6Wdk=h=dqY19 zXRqNjJ$JrBNC55&XWKa@y%u9>ezf6uf;W-mZXaoW3AA&$(|oR@5eEm9=`YIvNQpiv z!48W2O_4Y@^MQ2sL6=DC-k+ecv7^z%u{@7*=d1zh-xGU{l=8Ry$zS9ojF>rXLP!7H z`^EfCIVCIu!WMqfinE$UxQvWO6xARQHkPEmP z`bn&P;fy327!O98P!j(~qxf;Oxz`z0p3c7I4aV$KrRk+YQ@NEQ(EkF#5qjM}E|=&V z!CuPkU60NU2SffDT!NMN9Dy@IoXDGd4v2*O`;wvFQUOeI9*6j3bdZU zX*jGX?7SNs2GU-lp^kL+5jmVPojLr@2HL*vliDF&eA4mR@j2{`5-zJP8E0*S#*C9glN9N~{Kl zST_xQ@9*jd%7Ka`LrOJt?7u`q{)~!}!@Qm{x$o=#y3^E`OYB?q6%I`))7F@Oq%B0+ zM|Fjg#dyMz7k@`vID6)8*wX^d!R7mum_h*&@_5Kql);}bf{dDuvWQciL-pg%9YN)B zGR;q|(KR1x1v}dw?F+npF73><`}2!1+YDUdiiR?|_HUE`(EtQn@p5y6}1R zJgKSwf0&t8EBa*nn?DuXlyDjj&xHaTMCQ(bN@t(8JEt!H-Xm#$hWT(z@$d;#1P?*; zMa0Ejd_tzrg*+&2QkzdZV?FExq7?~BWhi}_cTYyFf?Q0%Q%;Jqmmvh6c?n(Cpy<)O zb89w1kM5=L0L-7)2ZiHlg9A!b_#{rOv7;G_^&h*sIFWpA(^eP)kKozH8tLKA?8`j+ znT^{iKGmIBm}+QE68$rn={PE>kGs~D7|Fh+ZN`w<++d>rRA(mFn+Z@6UFctR=FdPT znz^j;wh@_&9rl%-6*b?-NgT35|L9`u^h*97Y2?)l+M!fjcYE_@3rFPYoeF{a|q|II4h+~Kj zOXASGM(77c!-}3O&Hl`(2|RNO2Tv_%N_XJ^qv<@0X?-%+(}*tH2740dXCTF zIXZ!f{#$SniZ=?eGg+UZA>){#RyhNg>g}P?l@3uQQzR063v8O=d)Cqb(YX@H5aWT8Yo!~OAE^$ zxhup&_eS-#)W=YptB*_ixFxOrbBMG5uAYy1wP)6chQ3&F&@iFcl4`{F!fT-qvzwt? zi}8E=Kw%p$aveQ9&&XftIYYBQ)bK3c%wL?kHOd`KSInTIG*b7KWK;tL)ArQMGW12n z_%_bh0(C`j3sheEiHgdl^_BbXv1{!B%FSpz;~&FV{%7YJoq$tejv09(PFUfyiNq03 z=6}cWP^e7xS)kw!w=TZkd-%m3{C;+a(s6GN~mQ9EA?ha@d<=WD&>9u50mJ?jjF{rR8q zJRPyQeQn12XXWuV;(6LXy9n3Cv?N_x{JB*NJk81yn&TY}PKId&PTe!~^1nzYrs|~h&2LE zc^dY5RGu$_Plu;DCx+(Bpuy2s#%GnM`8xT?nPs#Y#?=g%jUdAdlpXS{s7kr>vD&L! zaO_Y6ARN0jX=D=~KN{jB?X*=jD|VTd8IiB10$YE&$H^K zh-GqQS9FcDokoLxqJQsfzMSScZB-|MDb3$xJ>5UQn9o(#MxMjvMMYs?x>kBxOS{o} z8Ct5KZBNV7gT^hnWMTL0Gx6KK*!`Xpt`^T*Fne70yrtb073VLW zx1_?(8wrPExN}N8pGB91!$aKF_?cy=lo(w&809dqqTtbe+3h){_l|WSmS;G|LZ11% z@iX<1mg4nzoZo?yO)TQ*2pE4ce&&+mRXFQ~hl&{WlHwK55&;$+4Kse`f(TmTub}t{ z3s;CBoQ)El2NR+^Y-TZnA`2WYV-fL`8Ixk}{EH5AP`DaDbI}DCU+A!6+Hfw5uMR3$ zt+5D&H81<&nxPy|I@oX)i9o+laOLwE!S@jQ?+8v44}9*5!G{Fbct3>l!$3J%E&`wN zf-C)G!P&* zKyao1jo_*`o}tCSA6+Z=ir~uUO~IAVp}u|QW6iH)1y}lof^&INkYCsS94L67&?}x3 zT>1Y>aFu7X;7b2)-!Gv2D$o9cYrG#2T=^U&xW?NdxYEy$!M`YYljz~QG58MzSN?iN zhsyJM4E=t-;-elk-X_77|7^jPe^zj%ze(_eC0%#L;F|#G3a<3W3a;^f zLU5(ON^nir&jnX{Ju5@&`C8wIGN&_X~cA=;si@HQrAPuJmUKuKbq>uJT+V zxTg1ef@{1_3$FZM6I}UM`F<<)p!9nQ&aq^u2Sy97@g66*(w{82@}DEP%5$OMn%-*! z*Ld{|4Z13VoDT@S;*Schd>SYmLb=_3Sa7BPy5L$b-6FWQJ9;*Smfr`3z6J3I`JWVA z>0c6D?MlzAP<+qbEm=FYUk3_)u*7?);406Nf-8Ne;3{WWD@=W=-t_E?LnPj%F??1C zuJl(6uJT+fxXS-s!8KogD!B4lFSw@b*Me)h9u=Ivz>tssE4b3XEV!=muOtydxgFLD zu6!`X5Ya3C7r`~Xe~rQILku79A@~jQ*RvChKzS73PjKabnBc0n_85G+;L88gf@{8< zAA?^axXP0eTc-HL7$@qSN?i-hw9-+La+EQ z1=n=_MsU^J{epi)o+BOW{Bdudp`&Dqk7v@aOHoX;405V!Ii#WaJB!u z;A$t=2(I+siow4xxW;>%;40^xf-8L^($7FW@GpdRe-UuX$}r3RFVYYA``$8qi@*PN z89rF)fWb06ME+fgC1X0h6lgv)n$0_uhKur z5aJE~)kn+l;72t=p$~r4CLe#`6a1y=GCcT87nI?_FUmD641rJZf8JPz2S4YQa-2qi zz_l|Bflu&H27LrP_#tPN(FcFyPT!z-`&+np@$B>G?(~htN7_C*{5KYdxA02~hJXGW zi|f5iOh-Ha!~guoA~^%g#gZp=X5YZ-qRcbVx)uK|f?HaoYi1IwIURye1DG4F&-!mA zh7Jg8mU5r0wN+F#hhXihTbSNIc*(J0k<|K1tR&>C+3@LsRo3ZTYdZIH;%Tgq`bFz~ z>D=+P=~rJdR-?uUtEE5nth@+YnHCRWg%)3MvXwq~ZWy$x1M|4=)SW<~T5&eNCyO)$Q-|UyXzn zjs1g5zvSPgQcgP~dAk1wVd5l37uctZUBflB#Rr>WAiJT_aW^fMODt{#5YYQ}D;IyMK38)v6Sr&L2m`_H}~w{{`Ce`pl5yI>j^!f)K#g`3Ls=H4p47w(VuyvoUg z-a?xn=!5>k{Zw3(>x9ql^leP&OHlv2;xD`(^dGl&A?+1%C8E zyMKY=Fz)jBX(r`)2dLsYPq=_L63XM3Mrq-qJ;!8^yFBsQC2Ed5afcuox1! zv!D1&Tc^#2G3{i62*g>&0zM1Z48&>s0Y8r*LOGwy9Bep;+krkKxW@bc1XuoS@fphb zuXC{B>=_0AcL}ciHw(_;UZDSz;7b33;Ou?|`t5=%{UP2iC@1YL(6)ya`G15T3>xq?`0@b-Q$yOkEF7jE^qG0;_I15vacq`Y@3p=Nk{AG zE4Oc`!lLQFCi8n#wEf|-ZOP0Xm7ST}DwCOY)t#C3)#=QJs#M!=mjAFT_W|&(=*->H zRF7EF{X4q%*h<7dNaioETFX}7z|L%+BXeDPbkY4wiN5uq*|;n7-Gpx){5eGrV}Rbv zFp<^0TYAn55F$J3CMG$vt_rFCOM1>-8*8otEx+EfSfm@zk51W@4`ZAB%*uOTFMJPU zmtx#rVEW8o{ME&O;1 z-!0jPb=&yy62DgpU$%6#{u!S%h2cu~{;BMi;;<6~DEI6rQMiOf&|2+^a(4os=8jIP`TlhUAbcc?)yA@E+)BpyV@= z`4s4`MBm5o#P}3iB0ZLj{5<#1F;$a}tT}MmT*NV^Gv85z5q0OZ>@%I&I|^gS-^RV} zU8Ff^FPXUd_QDsCxdXRu1&d*1EFnNgYWp&g{Um!=#Ct2o1Umjt+ z-FG0MVjd*=DoNso%~qAbLPL^=J{ZcEk1?6K9htXgrM*(jQvY-UueW&CX{7=Odh5K+jIiLDaMRwh7uO0B}-%|N= zsya}utxiFWNfUEuH|izxdgbz@vB=`1?dL&!+tXF~(e+pSFLLqdG50&}yk7qMQ1nor z-IjgW_-5}1gJrjR0pUK#K8?*Vax3W;^Xc@NZ1dznKTy{xps+_n7ut8jp~70j2hagI z*mEf0g3S6##M3?6$D@yt6N$dnNN%$2(L`TA735fTW>NGDp0qRjHz+iG+||Fi=c*W1 z{Y#M6=vjMX8qtr~u?hcwD4E$%+0ph~V)flHLQfLa=d^b-#X|p^{I1eJC)_$1Sz__K zN=sVdo1`^+J`v4M*r~(pI+gnmXUS;sxOg@44AHM(fEgJQ+`Ugd|I2$W<^q|(sKGM_ebH{xOHuM2sk3JIK(2PvyOf|*t z*r1@_OPWm;s5+y4KB&STc-+hkoK}XOK>J$4eCS7!K9P)DVF96vh+ZR4V}2Sw)hpRw zpiqUJi{7WZ@wcsmGf*c4wX&;Qk^2#~irTmTQz!|!WKe3-l=_vR)GJ!f1+jhV$S;!c z-6x!Wrhv7zcz`c8?S`KuAB5*t%5Hml|H@67E!AT-vH$P`z|j3#N}gE#CEShKV7|kr zei^maULB@)utcf2CRn|Hlzq$;bnlz6V?GPDLa;BN58b;{zlpxznQZtfJO~)r(1WM% zBQ=2pIrn2v77@8UIq>9~(kCv_gWXpYUrzL0SS&G7DHyQ4r;E?~Y*Bm@eQO|I|FhjQ zGTV1q%iLJLi>W6l#<>5I`Yrq^)bl=mGA2t`mXP)MYTU@vIb0P^2l-mMGy*H*;t9-l zhSPE3%X|$vQ&E`3EA~oU7LD2PlNx(_Xt4j;p2o~}*X>(&-CFk7BN`{d(6!8GcEA%? zwL%x!#|wwx2lxL1Uu75A1YhNmED+|aoJ3zGJd=^Hvc3A7_@1lGS$Sa@623bOew58j z`m_}_nO%;nYspuws$T3qMq``<_+$9Bk8^d^adi#(>PdBttLPtHLH}rG#55NJ#BmPN zj*->Ns>-lqM~(%bEo6o;g>mBhu1r-YzIFVa`;czR$SQnXR)389G_s-{B=w|2xYEbJ zs_Gc}SSQoRdQ4qQ^%5kPj#;pRt2I5cY6|WrhkK;--`v-_jt&F~9LH&h^R=GC0VqPP zf1f{8B})}U@b!HV*TiY_8fs|Z!vxk@8knc#`A*-D^R?BxY7MY|CgyF!pkp6_wV3qrC#Wh48iJF!-n@^#`#(g!aL<< zlROWJ!4HqYKMve{q(0xQZU)A>csTwi#Na2#;Aa3Q|3+`LR<8q_7ejw;48AA^zbFR3 zJO*DGgRcTkIk)*a18XmUT^&RJl^FaRo=>|sd}~L5aiBh&KH=hZ&aOEXXWIpRzVspE zXZC^x7tG${3i*n^j{HN`Cliv(%&Pv z(myD;(!VS?tI!~StuIgfVV5)D3Bi@meu8Vf2Mexz#tE)`J{^PW_Y&$G|5^;7Wg~;F>Q3f@{2A7F_wE( zDL9LI(9g4iDUl3gB zcd=29E7zLu^@1z?p@Qq2!uf*JuMp(a?0>`Mjya|KuWMS?5;Ucr_ATQT_h82pcd zYx#OpaOMATc197XAGNCv!Bx%~f-9f-f~&n7738-go+ zHHi?IE~VdBaLupx3$FB&1y}x41y}mh1XudA1lM}maO`@$krshXCx~Q|&UD^Ll zSD#TI7{MRfn9f$Ga~<_la}!^inj62}C_6B5d`S-cyXGut8r!*!VJsD|5D{vzWo^z%^)c6B`f#=1A9LQSYcdEuyZr z7khq+h{i&a=6tHY65G6u?aXYf%pmg0R2x*XEC~*^g|SE}^8r&IncF9XNs{Eu^?n|| z6KRd+@%vW7MQafhmf;E!jLvq1#R1+uJi*Nj*mQLA(`1zMdw&l*TiiN0v?8Lwg`bbQ zKj=I8NoQSQ;c`P}c>^X>)HYH@)KOy?$;T&?#Z+gKMM!+QDC2_b7c@)tv2 z6Wzvp9c|rB4a+{o2TDA=xF@9ff1qwZX>VF8Pj9pb zB-5KldBSdtCp9%BR_}${B9w`nZ5{prtufq;I@)tm-qcD>{5i{T|mN-1D@0{~cI=2U4nE&`q zB=5}LNj;~tf5i--(Pf|(OhsJIQiuqr&3QI?OD!`CnrE3{V;!wCXM7WzY2uxk+ff?A zO6SSQJ&OmA5S_)-i3})^;t|hLsh9Li_*DcYm+wLJRVxI z30F+;Oh-N_)2{5(*yWm|peU;bKVR{X2~dro$px%lMeA#%D_1tm+S8hGiI8u{qwq`JBf+eoW`p zv6G)JZq8#99Wn@^55uk#D~?)%2*Kz5L0l8}t^q!>5Wdw9_u;oDT=-u$$>jM?-*3gc zjK2kc8bbVsDu`>+GKAlc$+bQ3{~hVadW!Knych7}Jw`Lr(C*(4b8g{T&|kP`RbH+W zKHJ6t1wz{Wn7@%-w(x$?Kgr5<$5*EN{@pvB^H}5i%3M{$K))U1E{~u6?&0E};O*!0 zc&?#bfBH3buybSRm&D-Q^J_T%m&M?HF?c=($9w?? zU2`h%|IGR4&+q000_Q4h;sMuw!eaj7i)M2I0n6L5#ni$J=GtsVG7Y!MOy3fqd965AlY2t{flZ3p{c4@pHt+P)`3Jf~&9MMZwjV zHi8U1AJvb}EzoX)oUKC7x-;Mt1Xn(tf-9d-#Nau>)%T!#G%3D5hMpq<2L34hKE50g zS9#tagO82Db-d|3+pC0`0+@ zjNSLy|3!S3fB4%nJk-kz7cXJ`SAJgHN7^RDe9Qa}d`{uaJBrY5as;1~^9f+EH6#0S zI4q(66?2y)Rv&?)k2S_`VT8$usX!yYGo_@+9KsuO)&Yv|v!#>~A|o9Lr^jzeRNKquEHjU){A@oR&jbZ*adwjSa% zWY$%-J&h6lC=7!UogXt&PP~i}?O`Aq)qmgA?D!TOGBXDb8>F`Lwx_Cxc0+oMq~-Yh z*okmTGPujB*@$h<7XJ$h!yh#lu0`#oRp4Z@( zrNbxDj3xLbqsSEfD}NsP^TsI+NS{l&d@M|l+T{A~J1pVJ8{raQ3WxCVwm(y0==;%S zI(MYWIP~Sx^BYdgO~H|FulElwI{*&AbZ&Wlw{w=@O_!?h=Elj^kM-xI8M6o~uImjx zBl~t`LOPe|%AH-?mAkk;w;b_wH(?xY$I^5);eZc#Z$>9p^8*zevzO@O*C}b73$Rik$VFS*d73m%2;?$hD8Zah&}eP<|kLSp#UxauW}b z9`E)2*P!ooHmu&?N$`?b?Q?n)yTlb*u&U=@u#PkhM!_etj)f`KUon~f3i=tQ&?f>H zMw##9n33djjtS=XxwiG3SZ#_A!EV zZ|>^|eGadtoyH4szK-ea5F^}7y&|yx!R)N%4MG@e$N4&L^nDITUHrcsj`MZ=v+uWp zHRWn4MqyXs{del?c%ocT&!2H=i1T%9@$YX{Vq6FP1(;)&;i9T#M;{^We%`jP<4)f< zdZ)gQW_G|3?(_+MyS|QXG#CVqfi;xtPoL>E>HZyj9ZaVBI;_qEMxQaqBISW|@x$Rw zF*wTy`ET-yJH|8oh{qd!`Ly~E7=3rc@sInKeo72I?Rq#q^mPn}!!=-?Jedpr6YHS0BM@!IjTf1y}kX2(J8pD!9_$E4b1>EV$A?Be<^9&@<3g z4@daQm-Fu$?ZKfkZmK$DLgKkateUQq`ti#m`up8~72t|A9F(b;svofYTL9(Laf zmQCC0Ny{f79)41|4ZrA6GXiTe`IXr%#CSGa_}JOnG z;Z`;RPC~~)D7YR$9~E|Bjxw4L_6fz2c_FPvTR4d0n1LCsgWe&4aTpy*I4|*YbdK>z z)!i|Gpa?t*KKBdNXaDc1Gbc$&jvRn}zU+fUw_n(FFyx2LZdgh_(I=i&iPifSnLry$ z5vwKx&|nez5M?tbL12;XzYzsAowFyIU-4$w#EK!3u~rXcIUM?aY;F1%NG`+i+5VgD zHPcJxxyp3gs24W&+HMGV>*C$rH8TXJu?Yff>}B2)u;Gx3n7i4`Vn?E{&(SjtUhZ`6 z5LG8JX!|}2bqbqS8VLR0otHq3L5~ySK{rZ#8^;* z`s?sGJk1%q!ef{LFSj$iXGsa=DmN8|Ri8c-ZjJ%Upy`38tnB3mX5mS(Naa3SM&+`B zCC=0~%=znHqGL^>6qs4MAo7mFbMAv>=1{d?YaHDr^kxu(ePNt%KedkzHp?f2QSA`&D#RcR-TvCisFThUH3J z(bHLtWfiU{W5pCBYU$ZPhY5&!@W#iu6&`Zx~;2nJ69TI;l0>9-L`4*ZgBi0 zZ^GgnY#MS$?X7>WdqILmsq_Na{LpT4RUhPQtRfai6270L78bd z$0oQv3h3^dU>fbrckPGspHFIPfm-0}YM>@96R1Pzt>=C&E^6q?zSdQ_HC?$a4J~K! z4@=s*@KUxmeI$Mtf1tDNp=9FA8#@v|+u;?M=}?Fs3CkzZCgJ2^cS9E z;e~X;J?IKKWUMW)$Q0Y(B=)GVhn?Bqag`!YNJf+V@`>)CSU4TA8u3x&vX+L#>IZ?K zF!#cci>27o*uQz%aj@0M9zFMCbK{?xn!D`T3Zx`>x3qA0W|3pdEn-Tw4J}_A%4lwl zuWVU&l)f9DP@b}ew=4UK=dG{MFp3RvFb}qSsgi=e6zSxa6nzQkT^SVKvOH)9wERVBxn(LwzbX}~df2BeXee0|NH2yC_Qj}|BS|U){q1PKM@n{KFT5hjlwWaLb zwg|FX3rXHD$}P7+Na$>ZMQIkPzF6h~b^WCBn0z_LMhycoMtuhI$9%&7Abr|>Pnh zr4Qxz9i)$`{Kx4d)xSP{_p5#S9kqJSE3wqQQ@LxTJ^qTNbaT%quti9zJnYp(^#vM@ zeL^;6eg8Om=pPhKEwq#L?icd}SE$pQhQ1iu1(g4-EdQtkY!4?_4|rq9zKQxYD*nfo zq@lF<|0j}w;=hO{;w$m~K~(BX#|fe`=0>Ccu%HR^Hglue0R5ny56bo}L+<7%sY5pv z?c3Bfl#S|Zlpoc9DrXxYqnPZR1KRKZl;yx+&-7t)Kt>Wf=RovOQ-^IdIElsjb$I=M zS@ge&`hOULcV*``ZQ&kTTRXFNccD{<4Ye+_ni)MCE4bKysWbc1wCsjy*?U~lIDq_| z>+ThsGR!_^UfVl6eeCjm{5tP1BZ1LI$o(tuLYSi|W|)toEBl<=C}mMug$bkOumk{g^c8 z_?OmYu7xa*F{I9|xz(z`^GxLiTw?B%#EdZUOnekPOH+AdZw;5clIFPBo)vdE>td&{ z3;mZC&T*|TvtKV#VZJrWy~rvm(R5kdP$>}o3Pi>Ck1L z$*Ncmhfdb~b>)5zRzIMpWWF5D#n8V01Fwy}lFYnO*}crm9J*uZNnvF>EwvG**^El7 z7t&O``Zy;03!~93@$CpYX^xcpGzDtwtXaP$`^t_cuB!;^`cZkgzB>%RwlJ#5hsktf z{la3PUK*?Rhfkj?jXRYCRMe&sn@P6hnY0E+Tglm7F~Zl?zI}a4TCdlk|H4`lvW?y^ z++}ZAyQFv|HF*!0E1`Lgs-?nm<5Ur~PuTdb?;hwYw=3O!ci;*;<|q1oOK1e4bpg7| zEpvkmnss^^|_%Pmm*Rh&-X2(rJ|-Ys(vmKYZ@YnaNsw6?RR zE^2?%ZBKMR!<6(N!8{*Gw>{qdsNJ`@dyqqCr+LfSseN?0!hs&UDFyZ$OX?f$&F@`+ z{2m8G7UuzzT5fl^8HgZvC3|!jL##?i#?Mi8ZGj{8@cobH0QV%TZ88rV=7lFTs2{tg z=V=oYgYDX(eqSzc-4#)(^eo5lkGV}b+o8{Yv?r%^A%2ez20Wk6UQij`%LK%qZ}3Mv z=1z?qQsvgL-Nas$#XbNRf%+Cks0gwS0ae*rWyKUaPcud3hf4ch1s;hkbl-2W&g-h zQs}j;W&PkfH9M!lRWB|L=IT@xkUT`_Vq=nv^^em^h|Mc>c&!wwDJJT#V*WqC_IRf~ zSuw`{|EPNt_^7J0e|)ltQ80s*Dk?2eM-76QC@K-u3{G@{gQB3~ipU};n~(@7OE3v? z9fEN!+P+qQF16NLT#MFfP!O#f1$RU#Zj8~o3u2Z3_j%4acfNBo*J^+7`~Q93`+>}z z@BQBAJm)#jS?4ThLL&Wh(i^oD_bsnNl4#$ui)paURGUciEJuAB1eYR9>oLC{Y2Gp0 z+&N#cSuJ-!6edt^-p;nMpSFcMW?i+9^pWI!)=XMG9VZ`&Q!GP-kXmO+X2v)~z%yF& zZit9QjL%#D;6O6(*wzsJ9uz;daho2njT2eo!_8qLGKFz%w5cm}T#AN6iP+LuDVPLY zOkb=?fMPl$Ng$il9)}mD4v#j;Fnuz82#@biM||w+N^lD64vAv`^*an@xdB&Gj}J}{ z?~VC~bq8X(fzYlb2EGfh1{x!sggNynPm1pNu_13hf8(+xi9W5mAX;-lvsM_xe=E z@~fjwvgRK3=WCKdT#X<#s{ZI^y+`GE%?&C>{&p1eyb-fO#h5Y=Gdi)nRJ|tJBu8r} zygO&&!_+i9nxSh&rN`%5)TkfOOU*d8%tYs+7Ljp`m5~DVX8f0{lZ^Q0 zn~%zKL>mf{m9e@_%%8U9OWU9NTVN9Vr6M1>EUy-WvnR*rbMT)CY44F@5X z;n~eUg0sSMelNoOuo~RHsVZlaGGvyD8} zs{(p`2sz2lubQ|1x3~mzghHzi9+#V8Va2LqFu#H87aaz}G z-`TMJtAy_sdwwrm-siSx2Z@TG?&actxMcH>T;4vyLyDpbz<;;?MAt#7e+|y#UqyQa zcfhUfJU-xZagEE(E?i@u?HW6sT3WHVu%&2GaZyhR*&S;8ljk~58#{4S-)U3EjH(+I z>5HueCym8fHhm{gsT) zeo@cd2T~64wI{w5GhpwR_O&;Ana?ixkZ?*Y{BP$x2g0?lbqK4Em1&d3(IOB0$oCJ! z8={wWQUCpA_I>%qlP{;g!zyu;H~uFY#&{8qOX4pzuzTOd|0rNWapgE}^{OZ{pS{h8 z|JRDscLY2R{~hL&aC2rEf6Lwnx$-lsV8QsGTV~#QI$&6*zj+#ooQ9ix@$Y_;VQjGh zO8j@gfA_wN|IWYyLFkL+S{Pt8mVV3r3F@CucVTxeH}7n)%I$=&`N9`0U-+MjW8wNs zAi%&^L(214c>nWruC-0qA@m~O%DIn<)B?Gfd@Iu3j&pG-KY1>;Yjf-hJXi+Eo4*Lb z_XaL<%J3+SW5{1+7SG|Lh#!|@IL~VaBtooH@LK2hR;XvKVN*G48flT zE^;>546p4oV6R%dRWcqfZENB4ANZe7{wCleXR2KT;rO3h+^wx~_`fV(Aq5Vu-SX|pccqXee1z#!!1TM$#Yw>eS4X*7g-1fJ4nG{^OoE$l$MdY}BHMISN=O+Qr z7q8PTe`q%q6LEXOK7nnS4>~z{b*&fh`)dthYPZ+1$ z9N5hY0GWLZWEH~HX=A63nl{$TvzsHkl?$}; z7mqnOBC3oWGo@}Sno$Cp-45Ks2Yotu?D?bW##-itiw}0E1)5hUTzrWC=ujuF{=A8z z=NM7jhTtFxTp@luanvQVeAH(k!lIFwPFH2P*A#SLBzAf)(VjSVW=?o!<%iW>V~3?7 zvKVA+WbEkrX%~+*D@FYMA2i)v-6qM*Ycvkhr>5R#d@A9RCES{YM#3daIQ$C2S>S7n}O@yG)Mu$*{Bq)R{ym6r<|hf4s@my!BH_%xB?R9_IP2NT8aJ4Zjf6{;>B4=G za1M6`;mm)FaOS)Hx+Gk-L(+D<1(&|G3wIddoL*xI??L=&gmZjn5zhLQ*!G=-D{Xfd z?li)={G3lXmk$dGXZg2>;2#mr|4G8xKF<-({EdXOeKLeIe+%KP=QhHb zzpL%1iv8I>zaX6X148g=gqJ~<^YdARvzbG>s7;mp6Ca4t{oCYwE8o@gmC5$AbdZ(=j=0xaORI9 zyqx&s31|K#gfo9W;mrRP;ro;Pn+RwA9|`CB{Q<(6{{rD`pO*<|ez7zbafv^$KAj0? z{vL$0K6??){85CnKa3}w`3nf=^lBlT`416ZhB&zN{VU z*p2f=72zyr2H{-Jc!aZ@R>E1%p9yFFV}vt*HQ~(v2jQG9d&w7E5-*lBm2k!o^jc{D%o={?mlBJ}(o_`~#(u#wFFEbfK#8U#=r9F7cIDUAU(a zz8B%631|8BgmXDCn{ehYCY<@#5zhSI5zhQaLhz?U@O6Z<{H=tu{GZwJW3dn8yA#fG zdK1q4g9>f^g>lj&SCGPB@ou-w@9H5@~GV68*WniW1KJ3kYX_xQKA(-xh*D7lOY= zIP3E^;Vl0v!g>6slO3m*c!^ItzulQ|mU9H*9Is)7Gk+Z6(sXk2ClSv4d4wNG{Obvq zBFD-3FTz>Q3nBR75_w!fKRk+XmNS&_1Mr>;_cX$pKaFtKe-`1)zmjm~Ur#vm|42Cd z`2&PA|8>GSy*3ce{O<`rfa1G{R5rMRb~}vlGCX(oIg)UeKa6nZpFueDClb!}&LxDi zoP~t5J+CC3<=;*?^IssG`9G7)hD+?g{M`v>{=S6ogm;{M`Vr3jV+fa9#~(~M^G_$7 z`R5YO{EG>fq;~S>5YGH-2IkCzapIZcM;C^Tt_(bKOvm?UlY#!9i?!= z6}11(gfqVv;Vl19!kK?O;mkjoaOPh`IQ#7a!rA{@2xmFBhT!)R&h}qUcwgAV`N`9S zv;6ghvp(+<&iwsk5FeM=neiEfOB2k6JD+goFCcs;;kWhw8)aZrue&XXTf2Q3z}@_`eqvx;PQIIOc2)p)^UWFpxSLmYTL5?S z%G{p4PQII0_ECVpK?)~aU2WtY-_0xQ7r@=TvU3Btn^)Epz}>vEy92nJKPC&5`R5!TsbY(Xt=EmuB_R zP~2YC`f1K}z}ju>+5VkwapZlE#oKmwFT3R4tw@q$E0VrjWO}-_n2m9II<{osRe!4Psv>b5f3ctJL(?Scx`kz0 z^0xm0YlXAR=PTP1>B~OFdo|5%^>cDHO5dkj6)jnnxUAwDh`}};V-W?MbvoQ%eVFHD z!=>k{%DS9-Qvud|`Q&BtWRqy*Z#z}r+t*hX)<$o2*K zvdwOi>19H&uWEc{$-Znk3Q7)B(IgOY|C9L9rs?giG_1Xu_Id2dp2QCx7Cdk zo`vc;HrfN5kY#x$oClk!!b?wXOk;vaaFVGh{Wi|J-E`@j`1=3;h@m zY`@8@W%Uy&>i`}O7O|2fR{wsFws_Ubx;^}NG;~=i=2l0_u9jVOnWIV-r^4n4duqls znrALF%MaDRvTNnP&2<190@k!5^@D5W-JVKU!j{=Rl}->fOboK?vKt2!+PzkrKd$Sd z$?W}8EeKaopevTDvq`bg8iE?PVwLv;kl(zfe*YnOcke`cPuWn@?q#71MX`eqziAet zh9+e-v{R!{2=|!~4uX@`Mg77rtKhEo6|Ba*rM%Ek+S{bt*?$E7>TFBlKEdyToQ zWZri>QUuBfEomev1FOSb@~4+_=zdo7vuB6bRO4XDuu3QJ`h~hE==9d|II~4e9H<{@ zFDcEBx|dYr(S@1^Uy3%ZQ`_nLQenA$pKUS>>VsWZ8@KG{4eAqJyilkx8a`0VM4OiB ztB8B9zrh7_ik(!|dvzgBZ!N)_KiY)wE0|=Omx0@gtQvcA>eeu(W8Z#N8h<#b?`87n`1HV!Rz>ipE7;I%vlXM7(Y!tr z6{~&SuA|S{6G1k1b)IiCoV;hYd7TO0$oFod>f`8@l79BF@8-6io4-O5!R*mMd%d9v zk!>VArhc#{+H@5%g7JgPaVP1mJ5->2#9qkn;NY(UeVV9xZ{9zH;k92I@DC)X>riQaMr>~Qq_ zZ=n0U=z`Nl=l~RvawdS3?{?#m4lQcC<4dbH5<6@KqG)=lX zJ8I_|j_!$?|6x&A&5tIMzY7SGqvJkmcK@MtYeT*$>K@66W#0JPt&%#%P&T7+vCK8f zFez?L@@h-!v0!dq3)W~N%? z2mCF)RNOghESvE)jfdtnjiNu@-Hd+NfUW6f?01owi$Av9kbTi!{Y(mh29ypiGuD0J zyOb)E$3b+bUf*|IhS548{fKs?zghlGPw(l2JlH?)f7|8M{42Pv~$`HSttr$9@jd&OR}z>ypqqq3P$S69$A%FF6zMiYC^2$S2uC$(X}GWRYE( zk|j3|Cf|m%0_{Ur#AY7cN_)A;-ecGq$n7OA2X=mkU4!a(%Wk)2+e9`!sy5fXn@nGf z!eXB5=SxiIm~#ixHE6lj?co&#UZ?s^{A5md)gNrHsQsm!@fK=NcG%k(l`PcbJ@8~n zSh8v}_PvZYRpCCp8yYv!rekpna^q3Bm({1~g;HlWegoFGxREw}XMvM_8kZL|ezTjG z#IC#PWm?mZer8o-VZ{V^x^x!)CG<76CXF`jgs(`u)y|9L;15k97|zru(Wcimmu5MZ zAfEO;i^=Qy1Zi}tRZH4I z$iE41L!OrKxK?Auc=Xy8?QfcP%cUmmGW#M6N`K3yrEXg!-_;x%SyyL>r@PL+eQqP+ zvXDC%$7Q=Y@Teb%tUUcGkrnkbU97(3x@a^t%NBexZX+%Xt*v?V4e3$p_w*^Sy2dN> zsg!IqK8Ukz-0-OGEvDr+k2^gnewNeCd02c%_J%8iNF8iAtr(n|NaGf`tMF|n*6+KX zkLObUHa(^f=O71Nt?>_fP`2XhxV;`W7sGpYceg-UrOl;a>7v#4Z?bL5@Y)-;6Yo^N zrGO!T$9+OW=hT*H?yi7 zCfbbhwX$2!iX0slbq*g)aJD(9euVvOX7;+X*`>qVYBs+H_Cs%+BO@liNA!_|&T>u5 z@f~`YhM@S8EeZUvh5>W6sj%{)P6HfPY>sadIVMZW4-F zG^gjur9R~UK45bv_VQ)6@L!MrEr+>`AX zMBPP|>u^-f+;ZOgyly?V8y)zj94W0Pj90Cnm$4?a#UVCfj-M4jJAO`lBzlPrk^^!N z*SG|T99i%y_P=%y?G!zoEn%I)eW~?UDKY!~Df1omCR#S1$SG3pcgtcNVPU<*wNLyk zBggJ6-W4@o<+}C@O#Z4(7r)uqANBGvTJ@&ZM;fc}Z9Dxo6Nig;!*0MA8R3ZbTNacoub5{NZ&Arrx?OcT7cW`<~M2BCHcQJx-zr#3<|Vrq4fCu897wm(%Ilf%Lh z5;V(Z3++UxZkcLGq4vDbmu|mrTjI-aJNftsGB1Avs!-x1`Zz8Esc4TC%Nz#9=(#2) zOb;WQE{^QIjg81(Q*E;fnPM$xP^3291xaGP;i4bRzqVL6mPL95swh`2nEbD-nXY?e z<>&7#+NAQeuswO#V|3v;C9@@y`j#6?)ff*nG4{PsY+`I+oNex~Bo2T}7vG4&w{Cwl zHtXJR+)@;6ei-(~NoFS7>I?19lYM8ek8JvS14giysVkbaP?{-E2gaV=vgoLbtCUtFSXu_wcg7^ycI*d7qa8GSHsh}0QFCJ zX_W`Mfwb)H-yNP}OV(&pqvQ@fT*aE#y3x{BiH>Rb@{^SJJ$|}NrW@or3KEwp_O%9a z|5~q;O$;rF^K7UcJoBVg`zLa^tUtITg`1p0tCeumqx#79{^ijh0L0C+3vr7LpG}D; z&(6?iGLT)PkLG0`^#hQsYOg!MobqRfKcr~7M4qJU(LuALO9GBn(8f(`P|e=C?Z5wE zgzY_*``H)W!3V>5N~!dw9lZfK=4zEyPPwatGIPwvo2OUIAH#GP;eZN_$u9I1SO6sTBu=V%b-q~#VGQn1^>_+ zTBRr8=c|fi5`9x#u{>W8mrfGp8=hbV7*! z_@4eg>CT9%MkP@Yk5|C!hI(6OY`~1AJ!t z3-+WMG_dReGWLwPocRIcAr$ii>i#Prv3*f1#S-H{U9}H}==x`6YXA3D-=)Mc@q4gL z7BKcO{j*x{E9sy8>y*lm|7BW3IuCqlD^Z=CM`2j{QR@>W6n-KZE2p` z#myAqN15kfG?yNJSk+}oqnFrbQo3llZ*|2P!w;>eBHO(>1 zC8|f4&yCz%eVnbDN`>u0%KPi!r&{uVwsiyL4Rj_#-{8~=yE&)f# z3QJ`ZM^<{|PV%4qBWmOv}+2sNz74R zNX!u`g8jPw7mYX}w*av$feLblx7FLs`x=5nAt9~|Cl!5V?9u*W_Wg0K?aOoLSH@4P z^|r-NwI`*YGA!jind7ip3J#BzWe?4bHp@Pg?6>m*$621QnxYr|B8aZ6mqx&q`2IL* zYNMFeyfSp!UE?NNcGv15X$x+Hpd!M+f+@-k0x3XN=v$@?kCht{TBciRw(x`La#|a% zP|R&&{1^HfB0O8HV-9xCEM&w3a_)pSx$>9!kTg~1h#V9R`@Z0(i+|K^#zAyp zGRspzXvLtlqqptHyYccWP5HMl4-;&idVUga;*HV+e)%LB#CyXzK==!${4`L2*xy5dpHO~j9`M%$x(n1Km*lnYM6Y`S58*?oM`Z-WZ_fD)JdQ`N zTV-8HW9B_!-!^i^JMg-xB;*Ghi^*Mu$O{FSA3tt&j zNTrua&O_N_`c7JiVNgZNeOK7l;|G|nuAa~Vmz?f_d5AnT0?DH>Ax^FSe3d38@WVx{ z9M!!1NHf(~Oa?x)4qlJ@&s`Hw+xgiqNFr74RSidL1oxu8^tGebN}a8hkNGJ@z9V(* zsH+5z;Z{(xKMaW9Dan=%%F%&mANX(AQf(nx!V+#=LoxF^`&5?#u$cT@XJ~G$R6&gu zp7;ftqB1TO0OJ^VlS0@1WTYODsl@seEEIB0ELk(;_x0uUbN-TjqVGzXmH7+@G}>~; z*5`p%xarGfsyp(uIJT{I7f$9}$3r&HY(Xi8Oy`%$Qf6dUp*=4J&41IGZ{-kWw|j_u zCUW)ctmDktS!IRy;_R&B^k_Dm1tz?taB#^?*hsRpYhz*I6r4y^E9>Fq`FO!`eJ_6! zIR5Nt&*L~0N;)Oa{Uq+Gw0l` z!qwo^C}*`YB9T!d$LMjZJ}mqVJ`&1t>$wjQ4xNAYS0$RW;kwH+T)RnFIJ>K*5OH5r z1kD!|A5&W1xZU8=rNu?fP_8^)S{5(u4$dHR+?k%IBza!0^JREd^(cA*=X~LauEKZm zk?LI7j*lUoiw=_bouqm#vhQe(Vb7Dp*?BPH#rQi|IZG|)He&;Kc3aU55pzuENCLsV zWH}E?*x57B@(@vL&Ct-Q(PGFyRvXlY4^oN zgG$R#>py zDLj7Da%+B2|6@e|!KE7til)N^{g3Go19-Txhg-M)e?)G;9)oi2act?-q6c8>JO+t( zJ{Pagl+FOIpYMP_Dyv3pTO+nrS=qA;kAtjNi}K1!cFu3~<2pxv@)>adza9Ig@h-z( zaZ~;EK@sz(+TLGx1cA8K_A^1U<&U%$gV(vo8)V6`=5t4UNH`_tYs79f5ZTY-HW}>G zNaVFZxb8tPtUh=kGXK-}EAg)ARe}F}x$t{4hLGFq6eVavm zVu^kKwLrw2{Ty*HZh|(EXIri^47YcM*k7`|d*9h#_7w^Qp)YPYNcA^4OK zd?s)?Bg^geqU{i1^F#OxLhy75-U?jhTc^}^46t8^@b3u0WfglqyFDC&F9)vS+RUhJ zBVaFv@L#h0r8a+PI|7^>!hh8A-Ieg+j}GBa3BfN7!53Kh4V6Z|w)=27fqWor7mL6*ORm7@ zJVGQ!O`JFdrvXlwcJZib@_pR2vHlr&CcKeZAsT*GE0bXM7(gprl`==@X&Sk789RFF z$Vp=-nc%FZ6DCin6EHGy%K7I(y_r+-7XrjsmM;e-M*R1Tn)(V%Z5jCA$#VW@hAJ+ z$R#-Q*C}dnwnGuhSGj_GKFd$aFQ>oMZ*m3sXA#bPso(PP=MyefsFS~taMu4L5?S&BjTZA+JJ;FKMBHNA)+9yi5^dX)8-3ezoeD)uwJD>f>{A!XTeJLkD zML6?E621rVClk*6`Gm87-a|O^KOvmUr5$uQ+1Q`swMPiP7vU^_IN_WwXA;hQKBJKF zyNJ(n9wMCiD+y=*8p359(AocW!dcE&gfstNgfqXp?eB^-w9{_KL}@gZX%rd1-8E|_GiB; zA)NWi5WJ4?Uy%J563%{pCE+apw}dnQ4}>%S5yCmX%L!-x8p63;eVuUT?_$Rdg7%3K z&iSQJ2tI&tmOm*3e=r39nK%J1(PwY`cYZ#AaF%}@;cTC?2xtCYT zng0agEdLq8ng2H7Z0Gk0Xa2tkXMT}%GI0g%R!KOQs{;vV`6m+2`kzWT^DiKr?Q;>~ z%)fzfiHfu5&4e@mPlU^@<3B_=^PeM}?X#9}=5HdL_5Xx$=5HsB09;~cZa-HN&h3N~ z2^W!0|0yB()r7PDw-V0!JWM#JR~zBXf0=NO@0)}(|8v4Q-+eHbng4U?MCGyPz9IM_gtMF!;jHH{!kIsTaOO`XocWg%&iuuMGyitN znSVFo%zvD4w%h9=_-7$_f&JZ9{8O@s^Z(9-bGVfu_{A19poPZ7@i z*9m9-2Ev)YnQ-QBFYAPGi5-}~2jR@$i*V-mC!F~S!kK>>;mkjWaOT$&&ivVgGyiJB zxxBrBaOVGsaOOWmIP;$%T(Ye5pJxeY{(8ch|1shFken|GXF0va>2U@9b^zhbKZ|fK zS4R`h{ON=<|7yayKDvQ$mUBPh%zuP%=C3B4`L7br{Lcty{&sdESkRw)63+g!AK@%N z>`c-`2tVvh(orG&8HBSwa|vgCt|grLzb2gd_Y%(hWrQ>TMZ($tUm=|N8N!+WDdEg7 zl0iD_x6I#>aOTGdXZ?E<&ipvx%uf=|{PPHBeI^jj{8@yTBOWflrwM2Nts(eBgmeB{ zMfkoX{{_NXenfs8!XI^Uorj z`O^t!KA*M9d_Gf^?Z#)SGM~>>W%>6~xXfQpINNh8;mkiu2H$aso%hFom%j!R&in?# zx&3xI;mp5*@B>Kx??dp1L-1!q@K*@ua5IFn9m-|!8dos?RuaznZW!Tg|MLm&4SCLP z(?ak$A^7hIm#WptSs8-AO*rdWD48EuFu#-%&UQGMaJJh~gfpMdJZ1jm5dLDq_l90B zUbhm?avmj|^Tm^dGyiqMng1@~%;z&qIUNh7aKsg~+a827e=ovW{*i<;KS4P2&mf%j zzkqP&&mx@VdxSIpI>I>}?;*So;_cGq&mnkQ2>u!2TyJ(S6KfiKa=PzZrm)(X!#yYj zKRg6KIs`wKaJIup!r9JK3FmRJ8HBT(UlY#bmj8!v=HE*=kNYhnocYfZ&h6@d5YBpj zLpbw$?-kK2=+8}rb2*oe5`u>rOcH z`x4Inb131=A51vwe-`0v&x=Cv2EtkXHH5Q0ZzP=g_YltdJV-e6*Ave6e3x+Me?d6g zb1UJ@?<$2dE=d=*=Wc{EzYpQ8{~?4kKSg+7gyG8VVT3b(0^w}Wsf06s0pZMVA)NWQ z63+a)2xtCs!sXWK|1{yuUq?9eHxkbLZwP08L@Fm-!Tho-;jI5J2xs{fgtPp^31|Ls zgmd|M0^!WRkZ{sW_M2xom-31|MTgfssx!kNFEaOOWvIP>2kob`W?aOQ6zoaJvL zocYoHvgye7+?8OKBV3~B{Qo|}S)YdqXE|#^ z@J|S5`8$YET;l&+K6fXa<@X_+)8!DtnSUJN%s-uQ4tEUUEN3R+EN33!%)f?k=HExS z+&ViyOgPJVHU$5Wa4yfclR+q4!E$~V!dd>wgmZi5Y{Hp;5#j7tjf6A*dctLh#l`oJ zgfo95;Rg}_6T;bU-xALJ!d_y9j^%1U!dd@g2xomR48eaJf^Q(4?fD7eEdP7Lx!l;{ zfUN$UU%C>`a(WQX;r1q+`G*tE{5avvA4&MZP|c;|IKr7fhwwv)e+A+F2)~YS8A5RK zZzi1W@H@ge+(!sMl;o@;{4l~_Ae`mAOE~*&JK@aVM)=_*f0y3)f-6|R??<>a*<5@N zB%I|}6VBz;V8WSyHsQ>#Bb@nH5YF-aE#b_6kZ|TdN;vaZ6VCQ|m2l?2Lpb~AH-z^m zyG8nll{?z`zz{r6IF|z_6V84+lJF{oKO1>x)`uM^I4 z+6m`!vfx0mjl)}at;&{E{U%c zIWAr^2tS(e#e}n;v=Pqymk4M6hlI0zb`|5|5`EYo4kw)TKY?%=qI3FRLU=XdmlH0o z?f8ocXZgzs=X||mC0@oQddBhJ$vJ><_Mc-3XZhz4&UUzfaF%l;;VkDp!nu6gOgNW6 zoutymCHiwY(>(+~AOs&6g4Ytx_3v=P*&og%oZ~f-@UGC+*?BtQ%%4X%+uGOTlQ=gnu_x!FZiF*` zf5O>5M-a~Zp@eh0WhCLuuP2=4HxSPJ8wh9lHxthMhX`l+%L!-x8-%m`e-O_6F9~P) z1>zjI#Lmp$jc}G6@)W?E#X|>y+t_l zHxthM$l+?94t}x=;mq$rILkkXaOR&+INMapYV82cfz~jnUmj>aF$<5c!Kx?36Bzf0^vImK8kSGb5aOCn{d|OBb?>j zK=_Un?j3|P{{h08zk+ZMcMahz=Uu{?|25$(e|zbS;R>e9E`+oEy$NS~9z;0vk0zYM zO%cxgGYDt?WWqUK4TQ6t>j-E59|&jpZG>~Y-XNU)@I%769rYFAobL*c$l96tQNk-g zasIz6;T&%75c~+jxqmc>aOr|L`NIfj`R5VNaxNg8wIdmtcZ>d#bc^!iE0o?6hc5MK6 z`;vsLgfEZfzuSjvb^v$#XHA+mU3Qo1JLRAeB=5NW*1ac|NfqyIR}G{ z5$4y?=BrgQPO7$N4A@U>hK}Pt{V&CJN4kC9aqtiAH!i%B9nVr^d+N%z%y8_+a8>p868@xR)Fl`K!byk-p^tBvCSbmDwl#VcBpm;cZyHrRVZZ zzl;~Gh&R4lghO#~`cGlPd*1|Mcc+)F z#sd}DejM+$ojqT)K;F!^b3Jy9SK0P>!mS-nVeEJqk?wzQPK&U6iX*vOe7nB^Gh1C% zrOXtyRf8A`Ct=9;$9ayS)2-p5p*+s+bFOX&w!_`V=8D_&^&h$uarL1!sXORV#InV+ z?+$fO5>*>)9P0YJ6MpLZ;yj|p?+T*LgAfV4WYjY$(0*;JiQpLjECj^wT#10uS8bFV zsvdeAw6XU(j&Fc)<2TuJkrA-dD1pZ}L$bfyy0w6A&EG+uO#*p=gU`C}xu~r4Y+jsQ zyGhQj{j{qs zMf%n>KU06Ebq!;r=C=C1UB08e<8?>*4^%lY`nw|=a&l%e-2*3a=LknM7=uJ--$IcU~?4jzn~V4UPxt8MeRc!~YD$N!-HrO6ny z9iYfe8~*9?g|49f`E(chYc`-|He!+&1PX?~1F(GIw`%5zMDE9PzJlTNGrkwz|Ky(3 z64rmm9@7#{-5XZ~;?gn)pE6Dm%xm&orf%oM_X)wvL+}Gba2aCGCtrpn^5OEkO+Gvk zg4c%NLqqVhLvX2s@`WpXmwfm|A^0UBcykDT6>v$fHtV!n?gLvA!oNKP#}1;VF+bXD z)oM46#lE7lTdi)@I(@<=W3iL$n9&j4byl~H&DpJX!sOA@+*YV=OWU0NRIx|ugfU|y zW5$9fJDEHa3MvX`n=!0fC#VfqBwL>Bw9P8~IE?8r$|CfChEL~z++ zLCY)2yRI*6r;sSlV**mX$|c2s_`2gCBOorpSx&v82A3+t@#hfEa;^x$uP2=4OM1v9 z;c}n)K1B^)ihCFC!-TV(*9qsbvUdp=KXh{131>OHCo1CuZ9yk`N*V3s978zsPa|B4 ze#bwLaOTe-ob{AFzvT+1OAFzgzP~1%w zoPRGRocUJ}&iY(MIP-5Me0S34F2b3=E(HIjZQO}HrLFDkc>v+EjL+eR63%*76E1Bq z$LGCXx!fB@eAe?K!a02}C!EXe#e}n*GUy3!>lUFy1d zo04gqkt@Ae(}B&NdszKoK|KZ>E;Z-dVFU$d?P@jJxa@AEI!-NIhrn`1#`aB)*5hlR zl?ScT^TrsOI{;4K)jmCBVjM!oH39MkZ zr@*L-c^G`f4dD3WDRvB@@q#`D7=5dbUb`~$aqhW;12oWP94q@$_JGFB4mw;^ErTc~ z4#6?0OdJe3CsPM%hgG)e7=?~K%E06HXJXVb;YL2}(4ar&SpN?j$sEh(jyXODQ^=U( z0b;^z3fUKu}!5f z6<**~eG*KoWsHizrJVSOKpi3El@v@2u@AzLN#AiMUe^aHB=^XoHO4VJR zzh$1Iu|OBmx_PIh&|VD z90tV_XbkqjbD6Iw*958e^sK;>@$}fvMOVn7emF7kF?$I(Y#nGK)3JXV zHikA{DpFi!GH`=S+F3AfhcOgc)*`rVLg3C^ebkNJx9n#?9ZioCJpNDLmp-0^Uy%($ z?_Bu31GsSIa-U&iXs3nf@7^;~B9X8$v=R3G@s4OeC9Zr8#+RT?%FeD?|ACkSVI4kMi9PbQr8uP2=OjUo6g zg!7ow?+NF0{|n(PznyT_|4YJ|zlW_C31|HaZDlNv zng4Ubc?_%v;mjXEIO~5R;XDR&8sR(!b`Ieze+JB8x|GvQpW#t7&9$mc0>xF-;w{oxeCnLnOz zw$Bv8nLm$kw$BxWGyg`yIlgy<;D08Z{pv}=IouZsXZ{AlIbLP3yIf*_n|1Viwf@cG zuDvs9+SDmC#!d^4`GwARt315lp@+$k-%l9xyVG@4p@Y#QJLYHR%Xr`U^JQEQ+}oV= z)~1(!ibynnUDqwo^cD2!b%bY=M9sdIGY-`=y-+9Am~p;S!D@b{Zbzk?d0lpFqWNR{ zc0K01=`fjcFqLLkUz?nHVHTCajLc{qv1l+l=+v>K_ZF*^E7FCui@v;{jJ@^s=gZ{; z9$hGBU0c3Ch3e^KdT94#Ro7@!sXkS8Q|X&!=uty$nYj(~c=R^&0JP$K-VW`0xJ&6o>V&W{$x z)tdXkT24KDU-)NH+KeM*{-|Ru*~CLkyielZ3L`HYEtv>7_Ys*j*uEqF7U27~y1T5D zWV*U6i9yf8ZL9Md0}~B%4N>>#k26I3wU0~V#H30uRaN_~8cNR8%1rO9KgCarpBg_U zJ{(iXP8o(_SxhH$161DZSeK-iigisG&%kuDX!A691ZRNg;weC)52a#VRwZKE(tE4~ zYOEa->rqqrbxq~R>DjR!s#GGK5P>Vf>yCFY^{g$r_zv9Y82D(Q@y7*Ec%Q}Bet<7vF7uiz*C8yIKWfth-&A4os+EaoYE!cD$%3l0 zKtrk+bEfO?%K%N_^l_VKnh=G@rCFe28WH<~xqT4z9+m4FHx;H@Vk=8D&cYMln*ECs z1Y&+vNL9U4kFotvY;-h0$;6mSS1z-2<7dauiI03TkyGeQg&0iZi#A=1MAtVn6LA-B z{J3CTYjoiyf&A~g9}=cZ^R~HlNT$kl#`NP_k53Q$GU5GG#SCicHUMc<^F~dQh_o~vG*VXSXfyEoY!*3v+B?B2#wO@@f(b4M+KV7#T32EMFUmcXu{#dkU zgV;m7vDSMzK75F`;*_NK)iCc{xFig&wn&)5x<^-%W_zsd63h5vq@iH}H~t}b=l{OLq` z3DpZ1i6+hKqD^OO@sf&}m#GB?Euf1kqFS_H=j;J%Ja4k6&h6YhEag3!od1!` zQ}KNW%K;6#C`77j!(FVGJ)1m%?f7xPEgR)Xmik|C=cpv=$-0WQ3V8N|Lsxf$5 z-DUPA&YR>JP{U>_-4K>n;}7YH%a6 z1lLp9VqA*(?X>=}89Y0mR~~(cGRX=`FVnhgTeNwlf|}#4eej#kR8xsbeG+YY1B~2= zEEU~y%XVMyJ7>yE3;&1~7c)@N!Y5QrMrO{8GxL|5+w6`?o+s>M)oTqDm#!#uUWhLY1T@pUxH zr8OQ=?|WCmNy40zbt}J)^Zm{jkk0~{UGXZWBq#8zptP@^LQllzH>yLoM_|(a8=x<~(D-q+Aw@O2E=E+^UVo_AmIn|a60fB#(($y&=B$15U7;)0o=-BSUToAa= z#8`J((NLDcU3m%(zzS6P{ZYMEV-1l`>#*}YPK1KeOl~?A7E5^vThFJ^{42@Gf|ORa z$FppYube&LmTHf<_phY)vg@5jo9}~cHpjf(b6SDN;W5&xdCzRk6m60=l8Y9mbj8qyIDW) zj@_xB55$(@6ODKgqWLE$E3%LI#jdq@PSLPeZm~2R(oihr8<%pPnf34gV0>yHb>0Sq zg1G(E@u~N7?Hlm_ffxg}F+{J|)C8!suV0imQ%9RF zLKLhEp`GkHeZDt$td+I@U`sXQqZrh=%Dtd9s`X*xM$!#yV7~xT51ZASC@z|h)t#YtY~j`HYQKWmUOzf)^~&KdQ3DPkjooCF3k0d zj^&*74{2bThSs&(42mz4qSyAt66q!bdMh!AHFtkBxZL-?2hd+FNLN zWt&{4RcD)Ajawr1yET6t^|ak(43$}}eU!-DmGRp9$32jpQ@-RjJ><7iX?n=0 zo}V#If6sJB(Y%N*z7ecs`k0XBNB!JDJrFh?f!gCkq>-P*=pVZ>%sPfv`~DqX0A;)^T{$Lu8*~X9aQmNG`$ee~EYlD`}fuu&JPD@7kyA}fWN#@m@`ufMU zJEBc7Q$+d7NgudOn+NNoO}#oc52DSVBOLOQT5pBRJH~s+jP1irf7lWd={rqX`*F1S zDowbY@scJPjIh*N5AFNctr2RyZ~iZePd9*6>t(D|e^AN|_h-&ji()A6U#Qx5)Ci)L zNNoy}FH8z$GC5T{#(y#|;FWCc-kMp3?}*G#nJ+1H`8`ao;GD@aVD6`=-R1Yol&mAJ zl?BBU+)PL*+pFT*spi{iGf8+MYm7ZI4 zJ!Vt(kC#@&OUpq46|Z3Ja=ddjUYD#dm(IVygT*4NrKkph7AH!}7l^FJPREv37v8d? ziognJUqrHVq(9zWtr8-UzjVS4WV964ly+ZKlqfA*P<&izdE<72OWO+0*s-)6@5Wv5 zm;@RFy$gI#uBdztS+HGWry(sxixBDR#f62-5bK)GIybSO<>~Ln%&P$JY2_6*;Uk3~ zX+^HIedT(*7=M(1I+R#kD8dVCj9lR@vON7=e#nC2#_diht!OD+R8;sbNM`x=|Mt13 z`^pdIbakbBxec3px^Q1Z-1H0cDL)a3xk}8}f4hErd>~x=T8DX-eXLCTlf9!we(d{U zb5Gs-y?u%1#wNMOMUW%#-(iMf{$3NJe~>FZcflk(^0maizclcIv!CM*#*MgX@S1vz zLE7vCvA^^!-22@vE_2R;wv$ImTbTT(b5HM-jfHSESmk8?gM=LnU*;R+3%^`47_M^y z@eh18q&$Cx_rG!Osg~W)UFJgs^O{8Sr>@_Y{E<(N_JDsgBUtx)|6?A^eRY_y<-_o7BU&v_8h?m-wI04$^kfxy3RO5SP}$_}m^DPw-Zm zz=X@q)sh(<`Q+>pf|rHhy+iOrL-48)JPus+94eg%Tw2%Tb4UnZ)<+A!t7J@EZZ00? z9Ggi-b{#&xOswkMRYM^Bj~i{@qVyh3&n zK4uKd`ujS!U_WOizXUOMdfj;w>SUe%d1I$dn=&mTvzS#lbi#b($?|B_#EDZzMsHfy}xq^DGGjIp~M#5R2 ze-h66Y$lxfIs*pJg8J_d|CTGL&q;*yoJ9Hk*YTscadx|qa1M7)2z~?MQa(62Hxtfs z?k8N@E{?x~aLLjRf0l5T!}|j8oWyrS_*)5=KA4j)+oj1Bw3{sUbU4qQ>_Ir|Q%*Ri zWB(95Y|ga|m$`5`y~YwQlZ2f9Qwf)%&EYMCvz~Vl&gu0>!kPal;gXk~{O1Yh^m>VK zmh(R0oL-+3&U1>tC!FPUwe_<+mL|5-=WxQAe>vgOBzAnBQ_SVhCgMxi$MJWx?O+L) z?Z)fvIlYDupY=J1a1M7Y;mn^+INRqg!lg~=^yE3b(u8-o?vjV^qCd<3jO1{++TXTE zgwN?dl5naMu4D!kHhHFE(7R_je_n`F#jy{vm`j|2V?Aop1u-%s+#0POs5~Gyl>M z{8xmt9d06=<=jg++xZc~nZK5B=5HjN`Ck&w`g}_`^LLg^m?vEhBb@8I69{MdBSP?z zgmbv{gtMI4gfstY!kK>q;mp6EaOOWkIP+g3ocZerXZ}}&Gyh+NGk;ep9Ic(1{|myI ze>maHKZxV&0GIPa05zgiC9fY&~FB8t~t~Uv1 zemmjZ9{Zed<`;-^xFo*JFCm=yF~ax3JI-zg5xzI!hY`+l<`XWi;p99-IP*J+v2cl= z5*5eam2l?oMfm>2?@KuIk0e~OvXfs!xVVhNPa&M;Tt#>}@qa@&^Y0{_ zmnzrk^LN78{;v?;i}>paXMJ`Sr^h9B=5*;!IP)tB=YDa2!kM2SoZ~x$aOPhaf?q;7 z*N;~a&URZuINR-3!r5-i2xmQ46V7^WCj0={&ZSF%D2Gez$^34Fi_1HHPr@aM9DWGl zET<*}pGbIbk~5ufma~9xxpm<#A)Ln{a{(FQoKPpP&3g*)u zgmZZnBb@DXAmJ>30O8C(mT=~uLOAPx7U9gllyK%>MmX~q6VCelig4yHBb@oG2xtBm zgtI_AQ`{y3Qxqf_*aF(-zaJhB% zd6sbIuP6LK;%_2+Z^AzzoaO9pf4CC+a676u;mkjRaQ2fp;mjXOIP*sk&gJJw!dcFR zgmb!FOgQsB!nu9bN;va>Pq?^}v;R`Ung1x^tj|iqng1%`%>RUN=9h@G;*xYZ0RNpn zKO>y^F~X&Yb^JpJXa0$Vvp(k#&iq+~b33PjaOPhfg5N>76gf`MdkJSb&k@eyendF8 zH?|VacG$trQwo*?dxqfKNg;wu_-vmt0dWZ~|J{|aHx766D_R4%oB!}q0C)2lx)zuR zPELaaj;ntFck>p;2XHq}Ab4h&n-4H)+PG2Evqx){`DbfY(WzRrzwp6_S5*Dfy+&pp zVRV+i)p~2s);hInbFKFZwo~xFiVr_!*t~&Qyzaf6U5ht*8+H>I(h~bVnVu6#Hm)qG zP4|nrWx3dSAht=Q#jDnPZ_ioduPFE5#|90JA7TFVk-|!(FaHf}Bbz8RHQ;;t&r9$X z%m0q8TqoNZAii zGI`5Fs>Lc*0>MYpS6(aOW3^xjKGVNaZPv_98f`>F(PtT+<=3bCzt^WM8@T?8ggM9Z zb8SGyijL5o2sOZx-~y277Jy{dUjULH#4ThkATgC|)#YLVbLW08PG9~SFw6??j1_Su zXP#}=^x!r*e^ncvoOyOeUNCd7buW&`j>31@kwa74Esn%q9K}i5uI3eN$k9i(+bG7G z4EfP`RfEfdSSKstuZRhF63?2-ZK=_HjDA=+G0Viewq;@r^VHjGqkmd2`)#!EWN@rdNMfOBvSm7+zM;pw z)p}-)D!!+BeVD|;f_h9nH{RgA6>nUHbtD?1i>P~OP*t|MZJs;(4Erw>Ct%z10ShJV zj4j28^L9#9Et7Eeo{ zS3dU)u|U_#HAB43y85+!4_p4)SZqwD8!W$ns^vTs;RCUO3f8T>aY9RWnfltvBsWZmm@OwB+co&GtiD*o|Gk;u zEnW6TaabEo{ykaneQnj3Nv~pFr&KSjIH^b!ygu0L)|M(*9j|(E-qUcb7#2Exf^ZYw z)`Yi3_IIgXzO`p+bX}|~RO$vLBb%WP9w!@@!!zMZKOtRFIt#u^1FAOD^9~ zB-y3E(MRGXI|!A@t~nKJHzb!N(}^dx2%|*Ou@@dC2F#9in|qF~RZkX8jG^K-kgxNISQ$Ok#6`5!N5Rhv|(!s2{)|ppx$jHvIRCl8sNmA+SXS zELV>IWvZd?3Rt`J@BE;^_k()l2jT+i3<;E+-P+4t`Bt=xMrs!usa+C9IIa=NjYO}h z`e5$i$#gxO1>TTqNyJ8oKV;Msl4&@JY<|&eHEIPZerDaDD%d19?RSl9&Fdb=<#ms) zh+r%;!MMlx9QR=V@OOjK@?pGK6^?zVI5To}pY#H00wb9qts5fSt+GuvS^5%lbl`&B z66wdJuq$ZO^@kq=h2drLn0%H=UVK>oRJyhE1<#=UP_=yaW@O8cs13yyZ|5NQVvv51qmN}ehcg( z+-fNVhT8f9M`z+y)DPGfD!Kf=LW>I>lQOzd#RU z)tpDHoe@lV6{0hDP%{0P$dkAwQGS;9PQ+5FbdQ5j$Je%WTZ>3(?O)mEmp7}5!I$9L zN|IGi*JBao0+j&6R<6S~5r|f|a`h~!HmhB|haI_$>QGnuAjxzwOxW#!WWj66biX|+ zu{BEnTCdITxumMTs7HTAD_fBYTVSJ$1_{a!z{O^<)E@&k%t`3HWzVGQp5L$b`* zsy0+^d=d#N3Y(Z#eQV=c|DtT7hD=i;MQ4c=ozW||p#GG^Ov;kg5it1(Xt-*DYvU)= z*UywRI9z2(i>#Zp$QC~$W)}C2Up-TM z&+U~=|5+N7$@Gkvz#?-6G!y&J~ferPLb&1sKj+R5{t7~dE)K0NT}6{_mAKb>Z`WcoK5Q`a zz9dpJIaz5z_J^y_^`8JqO$~xu~lZ~`Lr1=CJ z8fhjOc);wQekMV|X7kDSE}x+FCq;1cy1D%;vE_@7Kd3Czo{(m(&63E?d9uY9$PXqn zVEZOCEz0Lct&QOnnKD!rViptx!(;tntv+I-P2NAnd#wNIPBKU{$syTpv7c9f%^M0}NT`PvA8to1Yy_k2EgR)*e^uJX zC}&r2Rf;?lljz_xr_#&x{>JPz!+U?4trlB~_rwsBbd#csS6~J;i71y49+5wl&)9ZE z!5X+h)#`bh$r*7|JInagRd}n$RL1b?xg+K1>Wvr+KyTh#?>(9F zupiVba(+&O6!)l{u*+xv+_FW-s|2oXT;(=T)MT$(B6a+{Cq3*k@!k;GyWpwX^kX6? zS3q{?%IYUsD_*rg>VxQ_d%=goA_~>5_*S2|cayX(sDBEu)>|N=6|{0Rn3hWm@JKRY zmfyS%Sz_ivx&vhcEP#fFa*y|lH_JAgNo4R6>}9H;*A3Z>S1#W=@XX@(*Zv*tt@D-rVve#5-q-=d3UH5UMph<$R( zuvLZjjHRCZ|JZvEs3@|v3zVdyB8`fQVYF3LR8$aDFw)RSqo|m5bWDg56%iC20}3c@ zi=ty3a~k6~I-{5sFksF(XF&{@v%dYEs%k!4`p&)gzxQANTkoB^fC_ zju(2ABUyEbzgi|XtRPb>y2-%U{ZfH~UX{3cUz5EwL$@DU8k2EWvOso49cY$rQ-QLQ zE(-L%7Ihy?P==U5JS^ijYAKq?9tYfAe;q>z?Dfhl(rbD3qjXde9klEDy>6w{?sTVF4 zZ+@{A3^2B|kpnEG_MZm5EH!@gi6VokWA;vx8rDP`)Y}Bnt{|LAu%JFeu(ScY;J`td z-47i(9)K`DDRu1B$LkUuMaNE<8`T?wBZ}GCnR<7Wg&jRAqrVqJ1SDUKAtTG9?8;IN zJPK=ZQM!$Ivm)r(J1goY?&pGD3j;P_p~uwhoK%TJkV;($#uupTqCp53&D)C*2G^-N z=&TdSlh*QsPL?n0={Djk6!App{epC}xF_nmE&Mo&sz9?TD>YKgq&zbx)zHB1 zXroW%@=#KaOpKu$L+P}%=1tH{qALqMgqxE*qQoCWbF6^aeUtJlC=M5hS20vYGG>gA zNUcaWCD*W#uGc~^ySSeLg;#EtX!qHcVw+gzrmTy^R})ftk5GDrQlRwWIRPogeWiv? z%^b;yqhz4qgbt!P$c)bml%p7yQ{!oe9uyRv;l|{5lrmPWh+BblJXZF&%gMbu>XJhoFM5S@x+v)g=qde`8h%4ZI0|gB3y|? za~KrQf#0{4%zGkl#DbSpodjbld`Uzp=W1qGOk2svAO1W1NTq~oD)m$nWdiD_KvKO? zO--XxLbpR|ItKgHf7~F^(A-!-*_=d*pYNtZ!+VxeITz54akC!E&CtX#FZbU%rO5Hi zzIAGP7JOpqeO)!Jxp=k{Wv0yBCrrPcxi6fj_6zM@F%i^x)zfjg}M_O1(v7%Qku^;lm!vdxBUx^0|e!-A3Rae%tE5f|9Xonjxks8!-ay(~%Xc}?uSUN8{`pndQvnhmi3BN`bR;K} zljIc|k`z;5oj#ETVFHt~=dTaEy`po}9|20@}bQ0qdF}WbG1i6ujczq+D>vtmS|3Rnh(YV33qFy9=Hf=+ieEMQ}j%vly%1MjKMOG1$_;d>RH9 z4~%&Ons>kzcFKLRt2{t7B05(dAj**TF9V^kn(Br0YZ5zHD%4L;#E$Ucgl@sXEBIbKGT`Y3PYA=GM!-4#kI^@zA`%vr?Fi5?Ti`k@SoL@2tt5DZe%n4z}ni`Yka zV=j$ox}OP5v^$ieN8rr#xj@p%Sja=@L1i=@gzSKEmPRD()FdAyJ!h;6m&A~q=qx0q z9b|2TuEk54_B03&G#F$Tl~}F=C>nY6PI=l@8owfvgAA@!jg(_2M6w;{8BFvktz^LF zC5A|j#E{{e=nh&*A8YCOyZ0C~D5EgQA+lI}lJb-?RA+5dO?eq0>L%sz5psco6_J+| zvYj&?Ps*sKeG+5qs3ypICX?N-<_s2Mxf*F`9d%fhum~kb8o3O02`>LGf8;W5t_%Vw z(fmh4=lDjftW;Dvidf_QM3p!wM_uJ8VyIy+tv=%$L9FOrJ5$}Oj0I(`q&AK5fI$68 zc{o6$|E#gZGA#0odG8Y#5pY5Ca-~_#b*i|7&=SZaG;PtHVp~Qu@gG2AWvG&4{OOza zU547GQm3d9$O8nlsG6~TH>CF6P_*x6l8kc!;A$M`62o2~4SKKE&e;?9p*M71Ce7VP z<`tim7v4KxQIwEeK;DIt@8&D#M06eRMGEp5N(|@YepTZ4e@{OJD}fDpBYDl~i_N4Uuh%B$JZM>?FwCGj-t2*t%g#DzY* zJB2fZNR6A2Jp!SdlcAxmp$TFVHvR#6gK=Q7e&Zj~-uN$HaeJKU0W4TGH3;)sjQ>iQ zYN`+R<+=tJ!O|B~7@q3^7T3bNg`t@XuIlZ5a%`C4S9ans?gL+R%#NeTyHS7R?#b1b_{QywccOGz4o3!!^sp4 z)s&UUB+J8E@As-HVWJC^-ntvsN|cK)R$z(*LIi~4&j$mO>z!i=LSNx#m`tAN6^Ra` zhAt2m)q|Y1RGQ&By)>;ia5oYhIHbg@;wwW;AY#GP4|xHxczcQ-kP$iZ6AgRm;|!b? zuR4&cUM=YhGQ$Khf|z<2U-yhgypXHy@D!Rtm#HJs{Q@NAEx!a(2!PJwTA_NVklit` zZG-Y2VN2yj7vnpvlq7&Bpb_7`3Di3t!uMpB_y{da>kX4JHRyG8uvmA57U>9bj;5E; zy42hF0D#7qkvt?&c}_*fx1(l2?zM8kPzA&Kcvu87A5t$DH&!$7!lm@M6;~G7h-C&E zieoH!LNg2E?Vf6?ALShw+SdI^pdJN|ajVEDG34}`7)GL8ghJ36+SrQmY0Hbz_>f;Z zYdp8{7Vd)-=?u+m-OmX3nN%Nh_=AeqiONotTMu4d?T#b&MS(q{0_!ox6LIQ6h1NsV zt-!=~=b?C&FKXm=lv}w`kzwgL>Heef%8B1!q;rggjuI#<;@s2e@DI|Nfpi8X#)-T^ zV)31;xS7r%We9Rl$w;1w<%}8C2F_zdeak%`vqe54f=1Vw&h%lJsE{c0oJxt$1H4e) zXkFHkP%aP|a*GKvtUpG#$Q&v?oW{}ZluAp*;6fswsw(dF4fWtAy>;nv&P(V51keUo zlX#^>l2H|3LuA5cv2F*w)=bQ z7!`p_CD_Ci)FPYo7xh+h(Tm#?`Df<)pe#D+htCRhSogD2<|$x7iBe|K8lXAZh1MJ{ z@+`}d-uKI?*eoTM7#pv5sCgpKME>avj^&hG(^+*&i)F z;R|0<=8#u8+oiWE@-^azM?sd^E%fh1#!+@{r|i-h@=GR^A;>mWd|Tv6=0ns;Vk6tk z;<9RRA%a0Hz|h}iOEx~d(iwK7pm_K>*lLLpHqr^5p{Z?pUhJT&d7(?k7JKPFwupmt zQS3YG$WnW4l|wmwBHNRM{~yF92J!O)skifz?xv3=c9MULpfegc;6pUUfSPq~2GR2S zPkk&kOru3X#Y_C*D10DFJ*$^4(LnW2Ap|{M9Mcuztrk6VX(1%EdeL=fHWLvRmjxxp zet%h3dJ?UTg=&fA!K9_aXjKYiuT)9Ik6*AV1!zTwd*(c{^Xx^L=bS`bq-?|zy|Nd4 zSyA}`e06)L_YM1uCq6SAs z)e7oAxM#!=wP($`H9cy$M+vY_Rq5FLS^*>_#FshPb|jx-?*0Jd-2IsBe_|d?`X8Hj zoS1jtIA=dhJ@MCxu0f!sCM`7f4il{~Gk+}2*>AlQ>jgLg($M7lT6-s**oB!6f6B*% z8F=)jd&+AM=!|O*;GXi@16uoV>mD{{)*c{V*YHQ-HN`>r!ny%`9Ruzw$YZ8pZkw{Fl+e3x(g-?Hee+L$)89g_KQ>P7XF%2TCrwBqRn^l zgTFt`6@u)qH{}^ZDPwy7vA=G4b4@Q+gOJK%v+VDLF}k>4n--{+lJ$btL?Q=AZn&<|;ID~p`64Y1F)w62`{;u!&yRyF>X z$o|$~pRLzn^^3hNAr}8{UppdGyt|T;LjN6dcEw(4{3j|f4X4DzAl3${D4n6-w^Or{ zb4AQ2N@ps|xSWjyiQrmS+52{DdM{U;CZGTA_3pTq;^l@v9+dl6=OFfFYA|l=GmM*d zG!uX2dd0Ste7)Rc6xgDZe0_>Z!j$||44YxcG*9T>qwytLU)HZ`RGK2e@!Og7^(Z-B@XES&&GkAaCl&q|xYjAM;P&4*n z4CnUI4CnsGF`U~Q7|!iyFr3>Hf8>^4;tvJq_L~^a{cmG9w^yEpP3r?+cch3Qn3nrwh7->!^>#YLxt~=GCrz!`Z)Z4lp$fi_;oQ#~hI4yG zB-3ebU(Ff-0^xj4r@jn##5v`@qZrQpBr&`gv!BCoZoi!2{Jt9)&h4)U!@2!>hI9K(4CnR-7|!e4F@|$L4dh1^%75;s1;e?0SB7)@ISl9Z^DM(@T0W)R z?=YPEFDgH3nbrp{hSRgI;-@*oxt~ymmt^)~4CnTl4Cncl&2VmCQhqL=c$Q-RD=?h5 z3oXOBpR@7&w;#lC zzUIybhI9Lu3|F)Ge_%MbcTm9yf%2g|{*?5VU^usTWq1W<-;m+l{x385Wrp)|&t^FH z<1asyP&|3L^EtG+{Q_ps^LaJHxqS-5dAX-EoZEk3I4}32)JY?l)+;ZD^Z2x5IQKuw z3_g$Hygr;@IQLVONJNg08e64&OEH|=yD^;Chk6X>_Bw|1`tTFOE3*3zVmS9xK>XV7 z`+RG~aB5OY{5vq5`x(G+Za?`!k%| zPh>c6@5>p^?T<5@c9nd%%W!W0k>T8bRZ0v3#go^EZVc!46Btg^rQBD~aDHDKHYYI0 z4>O#{{{q9geSaby1d1n*PZYzs{Y*3X9)|P#9%VT9)15>Ng6VySF`U~kVL0c#^$h3s z`x(x8_$b4<{TGH)l$HFqB{76R@#OX%X7El7=ke^zaPDUz!#NLMU^urgO@)L&@loSX ziDw0db9*m_a~^KOaBkm@;T4$w-VEpVvlvdhN__rgIIqvU8P5Gk(|CNJ&u)~72$Wtv zo>PzE+>al_xqTSJxqURltKuF?{Kqkz+s|NlHDB!#RJZGMw8VXE=|~Ifirl zhYaWOdBJdQpNAR)0_7)8YubW!EkQhis8K6e`GkfAINYTT2}lIXE?XN z#&B0=UzlDzAyB%g>r?!cVmSBX#&Dhw^%&0W+cKQ<+Yp9x`}JmUOKRi@6i?n>+!@Z} zU!URJe`|*G_;+GBw~uBxkN+%&bNhW}@Ed0EDr!pA_v!Lb3s|I=`|rVUs;WwPg)yAl z$1$9$fMRc8IJe))aL(rk7|!iqn!%ljG!dx0cs#2xocrm(@EW*AiRVuY=k`MxUX$7X z#&B+*$#6BZ&t^Ecx2ymo1d2cB2Rnvydo{yr;Tk3WH5l&3@XluN9t`LHM>Cw)!|4p? z{^v5B``N~D?&mnex&3X1^LFP?4;2Vj_~Z6>8P5GbXE?WycNT7hAMSrS!@2zdhVy!L zn&I3&li|GnXEU7JS0oXSK4tBPGvavbBN(Y6-xbiz;JF~lgJB! z;;F`;Vqb^h+`c)(smd$%Z5Yn&`!JlUfMP$K;oN>L!+HO;h2h-3QWZEwFs(li7|!d@ zONMhl<6Uz6@cNU;aBjbk;oSczhI9K+5)%j%PwqdK;oN>B!+E*yVmP-y$8cUhuQHt5 z+Y=cfP<(j(bYeKS_cMbJVmOcIScY>yI~dOE=M9E)`^qFr5GbB%{3-RP2E)0%AH#Y5 zY{qbI-;LoFnE$~H=l07O&gvZa;=PMFhGpkLMDGb3cz5&i#C5IJftw%7I{-uC5H{ z=^D;(?&lXX_!@@u^4-XA?#H@zPJB3Chv9W$qSTW?4CnTv8P4q|F`V1aV>q|J&+vNe zzK9$UR+=4Cmz)%W&?0HN(069}MUAM;XrTKQY{a#nYM^KLXVU?#GGY zyd29hoZGixxC*|M^6JWP-mXI!&ixECgHL2Q4dE#MXEL1oS;cT}zlGu4{xHM2{Y8dz z`;QFg^(Q|i62UY-YcrgeODBeN|05VqMW>{9EW^3|OorEI_KO(K?Kd-=_m4Xn&h3vd zoFtMGpSqM#1lri+Pr?0)6Dr|_uvKt1fe2(TUn-8fivKxy5BYee3GO3bbjt)+zIUYW z^(ZL*-SSJv{CgETr+n9V$i!axzL0*mNVK{6<=S>z)x9n_^QwJM~~$t@|y@LxHjHf};-k>mTS-{oPWs zRMKxtB@ajRsT%z;eYPfbe;)T!scB^c5?54Snss?_Q|sROqGeb9`lnjUTf)s@js5^R zDGDcXBGrh-5v@McUwx zrXaQ4FMRGMhs70tFjX9C^skU8{RyN|>;6=edZ4T(F)r_g)Lut?t>f}Jp;X*2kVngd z!p{M%OuKb@MHC-%0Ami;hjr`e|7vdw=m(brZVIn6dc1rv)Gj}5S~ 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 + +#include +#include +#include +#include +#include "../crypto/crypto.h" + +class BaseOT +{ + public: + BaseOT(crypto* crypt, field_type ftype){m_cCrypto = crypt; m_cPKCrypto = crypt->gen_field(ftype); }; + ~BaseOT(){delete m_cPKCrypto; }; + + virtual void Sender(uint32_t nSndVals, uint32_t nOTs, CSocket& sock, uint8_t* ret) = 0; + virtual void Receiver(uint32_t nSndVals, uint32_t uint32_t, CBitVector& choices, CSocket& sock, uint8_t* ret) = 0; + +protected: + + crypto* m_cCrypto; + pk_crypto* m_cPKCrypto; + //int m_nSecParam; + //fparams m_fParams; + //int m_nFEByteLen; + + //Big *m_BA, *m_BB, *m_BP; + //Big *m_X, *m_Y; + + //int m_nM, m_nA, m_nB, m_nC; + + void hashReturn(uint8_t* ret, uint32_t ret_len, uint8_t* val, uint32_t val_len, uint32_t ctr) { + m_cCrypto->hash_ctr(ret, ret_len, val, val_len, ctr); + } + + + + +}; + +#endif /* BASEOT_H_ */ diff --git a/src/util/ot/maskingfunction.h b/src/util/ot/maskingfunction.h new file mode 100644 index 0000000..66599fb --- /dev/null +++ b/src/util/ot/maskingfunction.h @@ -0,0 +1,33 @@ +/* + * MaskingFunction.h + * + * Created on: May 13, 2013 + * Author: mzohner + */ + +#ifndef MASKINGFUNCTION_H_ +#define MASKINGFUNCTION_H_ + +#include "../cbitvector.h" +#include "../typedefs.h" +#include "../crypto/crypto.h" + +class MaskingFunction +{ + +public: + MaskingFunction(){}; + ~MaskingFunction(){}; + + virtual void Mask(uint32_t progress, uint32_t len, CBitVector* values, CBitVector* snd_buf, uint8_t protocol) = 0; + virtual void UnMask(uint32_t progress, uint32_t len, CBitVector& choices, CBitVector& output, CBitVector& rcv_buf,CBitVector& tmpmask, uint8_t version) = 0; + virtual void expandMask(CBitVector& out, uint8_t* sbp, uint32_t offset, uint32_t processedOTs, uint32_t bitlength) = 0; + + +protected: + + +}; + + +#endif /* MASKINGFUNCTION_H_ */ diff --git a/src/util/ot/naor-pinkas.cpp b/src/util/ot/naor-pinkas.cpp new file mode 100644 index 0000000..c8a9431 --- /dev/null +++ b/src/util/ot/naor-pinkas.cpp @@ -0,0 +1,204 @@ +#include "naor-pinkas.h" + + + +void NaorPinkas::Receiver(uint32_t nSndVals, uint32_t nOTs, CBitVector& choices, + CSocket& socket, uint8_t* ret) { + + fe* PK0 = m_cPKCrypto->get_fe(); + fe** PK_sigma = (fe**) malloc(sizeof(fe*) * nOTs); + fe** pDec = (fe**) malloc(sizeof(fe*) * nOTs); + fe** pC = (fe**) malloc(sizeof(fe*) * nSndVals); + fe* g = m_cPKCrypto->get_generator(); + + num** pK = (num**) malloc(sizeof(num*) * nOTs); + + uint8_t* retPtr; + uint32_t u, k, choice, hash_bytes, fe_bytes; + hash_bytes = m_cCrypto->get_hash_bytes(); + fe_bytes = m_cPKCrypto->fe_byte_size(); + + + brickexp *bg, *bc; + bg = m_cPKCrypto->get_brick(g); //BrickInit(&bg, g, m_fParams); + + uint8_t* pBuf = (uint8_t*) malloc(sizeof(uint8_t) * nOTs * fe_bytes); + uint32_t nBufSize = nSndVals * fe_bytes; + + + //calculate the generator of the group + for (k = 0; k < nOTs; k++) + { + PK_sigma[k] = m_cPKCrypto->get_fe();// FieldElementInit(PK_sigma[k]); + pK[k] = m_cPKCrypto->get_rnd_num(); //FieldElementInit(pK[k]); + + //pK[k]->//GetRandomNumber(pK[k], m_fParams.secparam, m_fParams);/ + bg->pow(PK_sigma[k], pK[k]);//BrickPowerMod(&bg, PK_sigma[k], pK[k]); + } + + socket.Receive(pBuf, nBufSize); + uint8_t* pBufIdx = pBuf; + + for (u = 0; u < nSndVals; u++) { + pC[u] = m_cPKCrypto->get_fe();//FieldElementInit(pC[u]); + pC[u]->import_from_bytes(pBufIdx);//ByteToFieldElement(pC + u, m_fParams.elebytelen, pBufIdx); + pBufIdx += fe_bytes; + } + + bc = m_cPKCrypto->get_brick(pC[0]);//BrickInit(&bc, pC[0], m_fParams); + + //==================================================== + // N-P receiver: send pk0 + pBufIdx = pBuf; + for (k = 0; k < nOTs; k++) + { + choice = choices.GetBit((int32_t) k); + if (choice != 0) { + PK0->set_div(pC[choice], PK_sigma[k]);//FieldElementDiv(PK0, pC[choice], PK_sigma[k], m_fParams);//PK0 = pC[choice]; + } else { + PK0->set(PK_sigma[k]);//FieldElementSet(PK0, PK_sigma[k]);//PK0 = PK_sigma[k]; + } + //cout << "PK0: " << PK0 << ", PK_sigma: " << PK_sigma[k] << ", choice: " << choice << ", pC[choice: " << pC[choice] << endl; + PK0->export_to_bytes(pBufIdx);//FieldElementToByte(pBufIdx, m_fParams.elebytelen, PK0); + pBufIdx += fe_bytes;//m_fParams.elebytelen; + } + + socket.Send(pBuf, nOTs * m_cPKCrypto->fe_byte_size()); + + free(pBuf); + pBuf = (uint8_t*) malloc(sizeof(uint8_t) * fe_bytes);//new uint8_t[m_fParams.elebytelen]; + retPtr = ret; + + for (k = 0; k < nOTs; k++) { + pDec[k] = m_cPKCrypto->get_fe();//FieldElementInit(pDec[k]); + bc->pow(pDec[k], pK[k]);//BrickPowerMod(&bc, pDec[k], pK[k]); + pDec[k]->export_to_bytes(pBuf);//FieldElementToByte(pBuf, m_fParams.elebytelen, pDec[k]); + + hashReturn(retPtr, hash_bytes, pBuf, fe_bytes, k); + retPtr += hash_bytes;//SHA1_BYTES; + } + + delete bc;//BrickDelete(&bc); + delete bg;//BrickDelete(&bg); + + delete [] pBuf; + //TODO delete all field elements and numbers + free(PK_sigma); + free(pDec); + free(pC); + free(pK); +} + + + + +void NaorPinkas::Sender(uint32_t nSndVals, uint32_t nOTs, CSocket& socket, uint8_t* ret) +{ + num *alpha, *PKr, *tmp; + fe **pCr, **pC, *fetmp, *PK0r, *g, **pPK0; + uint8_t* pBuf, *pBufIdx; + uint32_t hash_bytes, fe_bytes, nBufSize, u, k; + + hash_bytes = m_cCrypto->get_hash_bytes(); + fe_bytes = m_cPKCrypto->fe_byte_size(); + + alpha = m_cPKCrypto->get_rnd_num(); + PKr = m_cPKCrypto->get_num(); + + pCr = (fe**) malloc(sizeof(fe*) * nSndVals); + pC = (fe**) malloc(sizeof(fe*) * nSndVals); + + fetmp = m_cPKCrypto->get_fe(); + PK0r = m_cPKCrypto->get_fe(); + pC[0] = m_cPKCrypto->get_fe(); + g = m_cPKCrypto->get_generator(); + + + + /*FieldElementInit(alpha); + FieldElementInit(fetmp); + FieldElementInit(pC[0]); + FieldElementInit(PK0r); + FieldElementInit(tmp);*/ + + //random C1 + //GetRandomNumber(alpha, m_fParams.secparam, m_fParams);//alpha = rand(m_nSecParam, 2);//TODO + pC[0]->set_pow(g, alpha);//FieldElementPow(pC[0], g, alpha, m_fParams); + + //random C(i+1) + for (u = 1; u < nSndVals; u++) { + pC[u] = m_cPKCrypto->get_fe();//FieldElementInit(pC[u]); + tmp = m_cPKCrypto->get_rnd_num(); + //GetRandomNumber(tmp, m_fParams.secparam, m_fParams);//alpha = rand(m_nSecParam, 2); //TODO + pC[u]->set_pow(g, tmp);//FieldElementPow(pC[u], g, tmp, m_fParams); + } + + //==================================================== + // Export the generated C_1-C_nSndVals to a uint8_t vector and send them to the receiver + nBufSize = nSndVals * fe_bytes; + pBuf = (uint8_t*) malloc(nBufSize); + pBufIdx = pBuf; + for (u = 0; u < nSndVals; u++) { + pC[u]->export_to_bytes(pBufIdx);//FieldElementToByte(pBufIdx, m_fParams.elebytelen, pC[u]); + pBufIdx += fe_bytes;//m_fParams.elebytelen; + } + socket.Send(pBuf, nBufSize); + + //==================================================== + // compute C^R + for (u = 1; u < nSndVals; u++) { + pCr[u] = m_cPKCrypto->get_fe();//FieldElementInit(pCr[u]); + pCr[u]->set_pow(pC[u], alpha);//FieldElementPow(pCr[u], pC[u], alpha, m_fParams); + } + //==================================================== + + free(pBuf); + // N-P sender: receive pk0 + nBufSize = fe_bytes * nOTs; + pBuf = (uint8_t*) malloc(nBufSize); + socket.Receive(pBuf, nBufSize); + + pBufIdx = pBuf; + + pPK0 = (fe**) malloc(sizeof(fe*) * nOTs); + for (k = 0; k < nOTs; k++) { + pPK0[k] = m_cPKCrypto->get_fe(); + //FieldElementInit(pPK0[k]); + pPK0[k]->import_from_bytes(pBufIdx); + //ByteToFieldElement(pPK0 + k, m_fParams.elebytelen, pBufIdx); + pBufIdx += fe_bytes; + } + + //==================================================== + // Write all nOTs * nSndVals possible values to ret + //free(pBuf); TODO fix and uncomment + pBuf = (uint8_t*) malloc(sizeof(uint8_t) * fe_bytes * nSndVals); + uint8_t* retPtr = ret; + fetmp = m_cPKCrypto->get_fe(); + + for (k = 0; k < nOTs; k++) + { + pBufIdx = pBuf; + for (u = 0; u < nSndVals; u++) { + + if (u == 0) { + // pk0^r + PK0r->set_pow(pPK0[k], alpha);//FieldElementPow(PK0r, pPK0[k], alpha, m_fParams); + PK0r->export_to_bytes(pBufIdx);//FieldElementToByte(pBufIdx, m_fParams.elebytelen, PK0r); + + } else { + // pk^r + fetmp->set_div(pCr[u], PK0r);//FieldElementDiv(fetmp, pCr[u], PK0r, m_fParams); + fetmp->export_to_bytes(pBufIdx);//FieldElementToByte(pBufIdx, m_fParams.elebytelen, fetmp); + } + hashReturn(retPtr, hash_bytes, pBufIdx, fe_bytes, k); + pBufIdx += fe_bytes; + retPtr += hash_bytes; + } + + } + + //free(pBuf); + free(pCr); + free(pC); +} diff --git a/src/util/ot/naor-pinkas.h b/src/util/ot/naor-pinkas.h new file mode 100644 index 0000000..ce960a8 --- /dev/null +++ b/src/util/ot/naor-pinkas.h @@ -0,0 +1,26 @@ +/* + * Compute the Naor-Pinkas Base OTs + */ + +#ifndef __Naor_Pinkas_H_ +#define __Naor_Pinkas_H_ + +#include "baseOT.h" + +class NaorPinkas : public BaseOT +{ + + public: + + NaorPinkas(crypto* crypto, field_type ftype) : BaseOT(crypto, ftype) {}; + ~NaorPinkas(){}; + + void Receiver(uint32_t nSndVals, uint32_t nOTs, CBitVector& choices, CSocket& sock, uint8_t* ret); + void Sender(uint32_t nSndVals, uint32_t nOTs, CSocket& sock, uint8_t* ret); + + +}; + + + +#endif diff --git a/src/util/ot/opemasking.h b/src/util/ot/opemasking.h new file mode 100644 index 0000000..51d148f --- /dev/null +++ b/src/util/ot/opemasking.h @@ -0,0 +1,236 @@ +/* + * XORMasking.h + * + * Created on: May 13, 2013 + * Author: mzohner + */ + +#ifndef OPEMASKING_H_ +#define OPEMASKING_H_ + +#include "maskingfunction.h" + +//#define DEBUG_HASH_INPUT +//#define DEBUG_HASH_OUTPUT +//#define FIXED_KEY_AES_HASH_OPRG + +#ifdef FIXED_KEY_AES_HASH_OPRG +static const uint8_t fixedkeyseed[AES_BYTES] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; +#endif + +class OPEMasking : public MaskingFunction +{ +public: + //Constructor that is called by the server + OPEMasking(uint32_t itembitlen, uint32_t maskbitlen, uint32_t nbins, uint32_t* nelements, CBitVector& server_choices, CBitVector& results, crypto* crypt) { + m_vServerChoices = server_choices; + m_vNumEleInBin = nelements; + init(itembitlen, maskbitlen, results, crypt, true); + InitAndReadCodeWord(&m_vCodeWords); + m_vStartingPosForBin = (uint32_t*) malloc(sizeof(uint32_t) * nbins); + assert(nbins > 0); + m_vStartingPosForBin[0] = 0; + m_nExpansionFactor = 1; + for(uint32_t i = 1; i < nbins; i++) { + m_vStartingPosForBin[i] = m_vStartingPosForBin[i-1] + m_vNumEleInBin[i-1]; + if(m_vNumEleInBin[i] > m_nCodeWordBits) { + m_nExpansionFactor = m_nCodeWordBits; + } + } + if(m_vNumEleInBin[0] > m_nCodeWordBits) { + m_nExpansionFactor = m_nCodeWordBits; + } + }; + + //Constructor that is called by the client + OPEMasking(uint32_t itembitlen, uint32_t maskbitlen, uint32_t nbins, uint32_t* nelements, CBitVector& results, crypto* crypt) + { + init(itembitlen, maskbitlen, results, crypt, false); + + m_vNumEleInBin = nelements; + m_vBinToResult = (uint32_t*) calloc(nbins, sizeof(uint32_t)); + + for(uint32_t i = 1; i < nbins; i++) { + m_vBinToResult[i] = m_vBinToResult[i-1] + m_vNumEleInBin[i-1]; + } + }; + + ~OPEMasking(){ + //TODO id whether client or server routine is used + }; + + void init(uint32_t itembitlen, uint32_t maskbitlen, CBitVector& results, crypto* crypt, bool server) + { + m_nItemBitLen = itembitlen; + m_nMaskBitLen = maskbitlen; + m_vResults = results; + m_cCrypto = crypt; + m_bServer = server; + m_nOTsPerElement = ceil_divide(m_nItemBitLen, 8); +#ifdef FIXED_KEY_AES_HASH_OPRG + m_kCRFKey = (AES_KEY_CTX*) malloc(sizeof(AES_KEY_CTX)); + m_cCrypto->init_aes_key(m_kCRFKey, (uint8_t*) fixedkeyseed, ECB); + //MPC_AES_KEY_INIT(m_kCRFKey); + //MPC_AES_KEY_EXPAND(m_kCRFKey, fixedkeyseed); +#endif + } + + //Expansion routine for the server + void ServerExpand(CBitVector& matrix, uint8_t* uptr, uint32_t ot_begin_id, uint32_t processedOTs) { + uint64_t ot_id, bin_id, bit_id, u, binoffset; + uint8_t *Mptr = matrix.GetArr(); + CBitVector mask(m_nCodeWordBits * m_nExpansionFactor); + uint8_t* hash_buf = (uint8_t*) malloc(m_nCodeWordBytes * m_nExpansionFactor); + uint8_t* mask_ptr = mask.GetArr(); + uint8_t* hash_ptr; + + //m_vServerChoices.PrintHex(); + for(ot_id = ot_begin_id; ot_id < ot_begin_id+processedOTs; ot_id++, Mptr+=m_nCodeWordBytes) + { + bin_id = ot_id/m_nOTsPerElement; + bit_id = ot_id%m_nOTsPerElement; + + binoffset = m_vStartingPosForBin[bin_id] * m_nOTsPerElement; + + if(m_vNumEleInBin[bin_id] < m_nCodeWordBits) { + //cout << "Choice for ot_id = " << ot_id << ": "; + for(u = 0; u < m_vNumEleInBin[bin_id]; u++) + { + //cout << "Server expanding for bin_id : " << bin_id << " and element id " << u << endl; + mask.Copy(uptr, 0, m_nCodeWordBytes); + //mask.ANDBytes((uint8_t*) m_vCodeWords[m_vServerChoices[bin_id].Get((bit_id + u* m_nOTsPerElement) * 8, 8)], 0, m_nCodeWordBytes); + mask.ANDBytes((uint8_t*) m_vCodeWords[m_vServerChoices.Get((binoffset + bit_id + u* m_nOTsPerElement) * 8, 8)], 0, m_nCodeWordBytes); + //cout << (hex) << m_vServerChoices[bin_id].Get((bit_id + u* m_nOTsPerElement) * 8, 8) << (dec) << " "; + mask.XORBytes(Mptr, m_nCodeWordBytes); + #ifdef DEBUG_HASH_INPUT + cout << "hash input for ot_id = " << ot_id <<" and choice = " << (hex) << + m_vServerChoices.Get((binoffset + bit_id + u* m_nOTsPerElement) * 8, 8) << (dec) <<": "; + mask.PrintHex(); + #endif + #ifdef FIXED_KEY_AES_HASH_OPRG + ((uint64_t*) mask_ptr)[0] ^= ot_id; + m_cCrypto->aes_compression_hash(m_kCRFKey, hash_buf, mask_ptr, m_nCodeWordBytes); + #else + m_cCrypto->hash_ctr(hash_buf, AES_BYTES, mask_ptr, m_nCodeWordBytes, ot_id); + #endif + // cout << "MaskBitLen: " << m_nMaskBitLen << ", results size = " << m_vResults[bin_id].GetSize() << endl; + //cout << "(" << (hex) << ((uint64_t*) hash_buf)[0] << ") " << (dec); + #ifdef DEBUG_HASH_OUTPUT + cout << "hash output for ot_id = " << ot_id << " and element_id = " << u << ": "; + for(uint32_t j = 0; j < AES_BYTES; j++) + cout << (hex) << (unsigned int) hash_buf[j]; + cout << (dec) << endl; + #endif + //TODO: permute the values at this point - write into a permuted index + m_vResults.XORBits(hash_buf, ((uint64_t) m_vStartingPosForBin[bin_id] + u) * m_nMaskBitLen, (uint64_t) m_nMaskBitLen); + //m_vResults[bin_id].PrintHex(); + } + } else { + for(u = 0; u < m_nCodeWordBits; u++) { + mask.Copy(uptr, u*m_nCodeWordBytes, m_nCodeWordBytes); + mask.ANDBytes((uint8_t*) m_vCodeWords[u], u*m_nCodeWordBytes, m_nCodeWordBytes); + mask.XORBytes(Mptr, (uint64_t) u*m_nCodeWordBytes, (uint64_t) m_nCodeWordBytes); + } + + for(u = 0, hash_ptr=hash_buf, mask_ptr=mask.GetArr(); u < m_nCodeWordBits; u++, mask_ptr+=m_nCodeWordBytes, hash_ptr+=AES_BYTES) { +#ifdef FIXED_KEY_AES_HASH_OPRG + ((uint32_t*) mask_ptr)[0] ^= ot_id; + m_cCrypto->aes_compression_hash(m_kCRFKey, hash_ptr, mask_ptr, m_nCodeWordBytes); +#else + m_cCrypto->hash_ctr(hash_buf, AES_BYTES, mask_ptr, m_nCodeWordBytes, ot_id); +#endif + } + uint64_t mask_id; + for(u = 0; u < m_vNumEleInBin[bin_id]; u++) { + mask_id = m_vServerChoices.Get((binoffset + bit_id + u* m_nOTsPerElement) * 8, 8); + m_vResults.XORBits(hash_buf+mask_id*AES_BYTES, ((uint64_t) m_vStartingPosForBin[bin_id] + u) * m_nMaskBitLen, (uint64_t) m_nMaskBitLen); + } + } + + //cout << endl; + } + mask.delCBitVector(); + free(hash_buf); + } + + //Expansion routine for the client + void ClientExpand(CBitVector& matrix, uint32_t ot_begin_id, uint32_t processedOTs) { + //uint8_t hash_buf[m_cCrypto->get_hash_bytes()]; + uint64_t ot_id, bin_id; + uint8_t *Mptr = matrix.GetArr(); + uint8_t* hash_buf = (uint8_t*) malloc(m_nCodeWordBytes); + + for(ot_id = ot_begin_id; ot_id < ot_begin_id+processedOTs; ot_id++, Mptr+=m_nCodeWordBytes) + { + + bin_id = ot_id/m_nOTsPerElement; + if(m_vNumEleInBin[bin_id] > 0) { +#ifdef DEBUG_HASH_INPUT + cout << "hash input for ot_id = " << ot_id << ": "; + for(uint32_t j = 0; j < m_nCodeWordBytes; j++) + cout << (hex) << (unsigned int) Mptr[j]; + cout << endl; +#endif + +#ifdef FIXED_KEY_AES_HASH_OPRG + ((uint64_t*) Mptr)[0] ^= ot_id; + m_cCrypto->aes_compression_hash(m_kCRFKey, hash_buf, Mptr, m_nCodeWordBytes); +#else + m_cCrypto->hash_ctr(hash_buf, AES_BYTES, Mptr, m_nCodeWordBytes, ot_id); +#endif + + // cout << "Client expanding for bin_id : " << bin_id << endl; + // cout << "MaskBitLen: " << m_nMaskBitLen << endl; + m_vResults.XORBits(hash_buf, ((uint64_t) m_vBinToResult[bin_id]) * m_nMaskBitLen, (uint64_t) m_nMaskBitLen); + //m_vResults[bin_id].PrintHex(); + //cout << "otid = " << ot_id << ": " << (hex) << ((uint64_t*) hash_buf)[0] << (dec) << endl; +#ifdef DEBUG_HASH_OUTPUT + cout << "hash output for ot_id = " << ot_id << ": "; + for(uint32_t j = 0; j < AES_BYTES; j++) + cout << (hex) << (unsigned int) hash_buf[j]; + cout << (dec) << endl; +#endif + } + } + free(hash_buf); + } + + //the out vector contains the matrix with the data that needs to be hashed, uptr is a pointer to the choice-bits of the server in the base-OTs + void expandMask(CBitVector& matrix, uint8_t* uptr, uint32_t ot_begin_id, uint32_t processedOTs, uint32_t bitlength) { + if(m_bServer) { + ServerExpand(matrix, uptr, ot_begin_id, processedOTs); + } else { + ClientExpand(matrix, ot_begin_id, processedOTs); + } + } + + //Do nothing, only dummy function to implement virtual function + void Mask(uint32_t progress, uint32_t processedOTs, CBitVector* values, CBitVector* snd_buf, + uint8_t protocol) {}; + + + //TODO: update, not working any more since unmask was changed + void UnMask(uint32_t progress, uint32_t processedOTs, CBitVector& choices, CBitVector& output, + CBitVector& rcv_buf, CBitVector& tempmasks, uint8_t protocol) { }; + +private: + uint32_t m_nItemBitLen; + uint32_t m_nMaskBitLen; + uint32_t m_nOTsPerElement; + uint8_t m_bBuf[AES_BYTES]; + uint8_t ctr_buf[AES_BYTES]; + uint32_t* m_vBinToResult; + uint32_t* m_vNumEleInBin; + uint32_t* m_vStartingPosForBin; + CBitVector m_vServerChoices; + CBitVector m_vResults; + crypto* m_cCrypto; + uint32_t m_nExpansionFactor; + bool m_bServer; + REGISTER_SIZE** m_vCodeWords; +#ifdef FIXED_KEY_AES_HASH_OPRG + AES_KEY_CTX* m_kCRFKey; +#endif +}; + +#endif /* OPEMASKING_H */ diff --git a/src/util/ot/ot-extension-1oon-ecc.cpp b/src/util/ot/ot-extension-1oon-ecc.cpp new file mode 100644 index 0000000..47ea3ff --- /dev/null +++ b/src/util/ot/ot-extension-1oon-ecc.cpp @@ -0,0 +1,624 @@ +#include "ot-extension-1oon-ecc.h" + +bool OTExtension1ooNECCReceiver::receive(uint32_t numOTs, uint32_t bitlength, CBitVector& choices, CBitVector& ret, uint8_t type, uint32_t numThreads, MaskingFunction* unmaskfct) { + m_nOTs = numOTs; + m_nBitLength = bitlength; + m_nChoices = choices; + m_nRet = ret; + m_bProtocol = type; + m_fMaskFct = unmaskfct; + return receive(numThreads); +}; + +//Initialize and start numThreads OTSenderThread +bool OTExtension1ooNECCReceiver::receive(uint32_t numThreads) +{ + if(m_nOTs == 0) + return true; + + if(m_bProtocol != R_OT && m_bProtocol != RN_OT) { + cerr << "Only working with R_OT or RN_OT right now, sorry!" << endl; + return false; + } + + //The total number of OTs that is performed has to be a multiple of numThreads*Z_REGISTER_BITS + uint32_t internal_numOTs = ceil_divide(pad_to_multiple(m_nOTs, REGISTER_BITS), numThreads); + //cout << "Internal num OTs: " << internal_numOTs << endl; + //uint8_t go; + //Wait for the signal of the corresponding sender thread + //sock.Receive(&go, 1); + + vector rThreads(numThreads); + for(uint32_t i = 0; i < numThreads; i++) + { + rThreads[i] = new OTReceiverThread(i, internal_numOTs, this); + rThreads[i]->Start(); + } + + for(uint32_t i = 0; i < numThreads; i++) + { + rThreads[i]->Wait(); + } + m_nCounter += m_nOTs; + + for(uint32_t i = 0; i < numThreads; i++) + delete rThreads[i]; + +#ifdef VERIFY_OT + //Wait for the signal of the corresponding sender thread + uint8_t finished = 0x01; + m_nSockets[0].Send(&finished, 1); + + verifyOT(m_nOTs); +#endif + + return true; +} + + + +bool OTExtension1ooNECCReceiver::OTReceiverRoutine(uint32_t id, uint32_t myNumOTs) +{ + //cout << "Thread " << id << " started" << endl; + uint32_t myStartPos = id * myNumOTs; + uint32_t i = myStartPos; + + myNumOTs = min(myNumOTs + myStartPos, m_nOTs) - myStartPos; + uint32_t lim = myStartPos+myNumOTs; + + //How many batches of OTEXT_BLOCK_SIZE_BITS OTs should be performed? + uint32_t processedOTBlocks = min((uint32_t) NUMOTBLOCKS, ceil_divide(myNumOTs, m_nCodeWordBits)); + //How many OTs should be performed per iteration + uint32_t OTsPerIteration = processedOTBlocks * m_nCodeWordBits; + uint32_t OTwindow = NUMOTBLOCKS*m_nCodeWordBits; + CSocket* sock = m_nSockets+id; + + //counter variables + uint32_t nSize; + + // A temporary part of the T matrix + CBitVector T(m_nCodeWordBits * OTsPerIteration); + T.Reset(); + // The send buffer + CBitVector vSnd(m_nCodeWordBits * OTsPerIteration); + // Stores the codes for the choice bits + CBitVector choicecodes(m_nCodeWordBits * m_nCodeWordBits); + choicecodes.Reset(); + // A temporary buffer that stores the resulting seeds from the hash buffer + CBitVector seedbuf(OTwindow*AES_BITS);// = new CBitVector[RoundWindow]; + + uint8_t ctr_buf[AES_BYTES] = {0}; + uint32_t* counter = (uint32_t*) ctr_buf; + (*counter) = myStartPos + m_nCounter; + +#ifdef TIMING + double totalMtxTime = 0, totalTnsTime = 0, totalHshTime = 0, totalRcvTime = 0, totalSndTime = 0, totalChcTime = 0; + timeval tempStart, tempEnd; +#endif + + while( i < lim ) + { + processedOTBlocks = min((uint32_t) NUMOTBLOCKS, ceil_divide(lim-i, m_nCodeWordBits)); + OTsPerIteration = processedOTBlocks * m_nCodeWordBits; + +#ifdef TIMING + gettimeofday(&tempStart, NULL); +#endif +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalChcTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + BuildMatrices(T, vSnd, processedOTBlocks, i, ctr_buf); + GenerateChoiceCodes(choicecodes, vSnd, i, min(lim-i, OTsPerIteration)); + +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalMtxTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + T.EklundhBitTranspose(m_nCodeWordBits, OTsPerIteration); +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalTnsTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + //cout << "offset: " << (AES_KEY_uint8_tS * (i-nProgress))<< ", i = " << i << ", nprogress = " << nProgress << ", otwindow = " << OTwindow << endl; + HashValues(T, seedbuf, i, min(lim-i, OTsPerIteration)); +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalHshTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + + nSize = m_nCodeWordBytes * OTsPerIteration; + //cout << "Sending " << nSize << " Bytes " << endl; + //m_lRcvLock->Lock(); + //cout << "(" << id << ") Sending " << nSize << " bytes on OT " << i << endl; + sock->Send( vSnd.GetArr(), nSize ); + //cout << "(" << id << ") sent " << nSize << " bytes for OT " << i << endl; + //vSnd.PrintHex(0,1024); + //m_lRcvLock->Unlock(); +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalSndTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + i+=min(lim-i, OTsPerIteration); + //cout << "Performing next OTs " << i << ", OTsPerItation = " << OTsPerIteration << endl; + +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalRcvTime += getMillies(tempStart, tempEnd); +#endif + vSnd.Reset(); + } + + T.delCBitVector(); + vSnd.delCBitVector(); + seedbuf.delCBitVector(); + choicecodes.delCBitVector(); + +#ifdef TIMING + cout << "Receiver time benchmark for performing " << myNumOTs << " OTs on " << m_nBitLength << " bit strings" << endl; + cout << "Time needed for: " << endl; + cout << "\t Generating Choice-Code:t" << totalChcTime << " ms" << endl; + cout << "\t Matrix Generation:\t" << totalMtxTime << " ms" << endl; + cout << "\t Sending Matrix:\t" << totalSndTime << " ms" << endl; + cout << "\t Transposing Matrix:\t" << totalTnsTime << " ms" << endl; + cout << "\t Hashing Matrix:\t" << totalHshTime << " ms" << endl; + cout << "\t Receiving Values:\t" << totalRcvTime << " ms" << endl; +#endif + + //sleep(1); + return TRUE; +} + +void OTExtension1ooNECCReceiver::GenerateChoiceCodes(CBitVector& choicecodes, CBitVector& vSnd, uint32_t startpos, uint32_t len) { + uint32_t tmpchoice; + uint32_t otid = startpos; + uint32_t ncolumnsbyte = ceil_divide(len, m_nCodeWordBits) * m_nCodeWordBytes; + for(uint32_t pos = 0; pos < len; pos+=m_nCodeWordBits) { + choicecodes.Reset(); + for(uint32_t j = 0; j < min(len - pos, m_nCodeWordBits); j++, otid++) { + tmpchoice = m_nChoices.Get(otid * 8, 8); + //cout << "otid = " << otid << ", choice = " << tmpchoice << endl; +#ifdef ZDEBUG + cout << "my choice : " << tmpchoice << endl; +#endif + choicecodes.SetBytes((uint8_t*) m_vCodeWords[tmpchoice], j*m_nCodeWordBytes, m_nCodeWordBytes); + } + choicecodes.EklundhBitTranspose(m_nCodeWordBits, m_nCodeWordBits); + for(uint32_t j = 0; j < m_nCodeWordBits; j++) { + vSnd.XORBytes(choicecodes.GetArr() + j * m_nCodeWordBytes, (pos >> 3) + j * ncolumnsbyte, m_nCodeWordBytes); + // cout << "accessing byte: " << (pos >> 3) + j * ncolumnsbyte << endl; + /*cout << "S: "; + for(uint32_t i = 0; i < m_nCodeWordBytes; i++) { + cout << (hex) << (unsigned int) *(vSnd.GetArr() + j * m_nCodeWordBytes + i); + } + cout << (dec) << endl;*/ + } + } +} + + + +void OTExtension1ooNECCReceiver::BuildMatrices(CBitVector& T, CBitVector& SndBuf, uint32_t numblocks, uint32_t ctr, uint8_t* ctr_buf) +{ + uint32_t* counter = (uint32_t*) ctr_buf; + uint32_t tempctr = (*counter); + + uint8_t* Tptr = T.GetArr(); + uint8_t* sndbufptr = SndBuf.GetArr(); + + //cout << "Numblocks = " << numblocks << endl; + for(uint32_t k = 0; k < m_nCodeWordBits; k++) + { + (*counter) = tempctr; + for(uint32_t b = 0; b < 2*numblocks; b++, (*counter)++) + { + m_cCrypto->encrypt(m_vKeySeedMtx + 2*k, Tptr, ctr_buf, AES_BYTES);//MPC_AES_ENCRYPT(m_vKeySeedMtx + 2*k, Tptr, ctr_buf); + Tptr+=OTEXT_BLOCK_SIZE_BYTES; + + m_cCrypto->encrypt(m_vKeySeedMtx + 2*k + 1, sndbufptr, ctr_buf, AES_BYTES);//MPC_AES_ENCRYPT(m_vKeySeedMtx + (2*k) + 1, sndbufptr, ctr_buf); + sndbufptr+=OTEXT_BLOCK_SIZE_BYTES; + } + +#ifdef DEBUG_PRG_OUTPUT + cout << "I: "; + for(uint32_t i = 0; i < AES_BYTES; i++) { + cout << (hex) << (unsigned int) *(ctr_buf + i); + } + cout << endl << "T: "; + for(uint32_t i = 0; i < OTEXT_BLOCK_SIZE_BYTES*2; i++) { + cout << (hex) << (unsigned int) *(Tptr-(2*OTEXT_BLOCK_SIZE_BYTES) + i); + } + cout << endl << "S: "; + for(uint32_t i = 0; i < OTEXT_BLOCK_SIZE_BYTES*2; i++) { + cout << (hex) << (unsigned int) *(sndbufptr-(2*OTEXT_BLOCK_SIZE_BYTES) + i); + } + cout << endl; +#endif + } + SndBuf.XORBytes(T.GetArr(), (uint32_t) 0, m_nCodeWordBytes*numblocks*m_nCodeWordBits); +} + + +void OTExtension1ooNECCReceiver::HashValues(CBitVector& T, CBitVector& seedbuf, uint32_t ctr, uint32_t processedOTs) +{ + //If OT-based PSI is performed, the hashing is skipped and the masking is called directly + if(m_bProtocol == RN_OT) { + m_fMaskFct->expandMask(T, seedbuf.GetArr(), ctr, processedOTs, m_nBitLength); //m_sSecParam.statbits + return; + } + + uint8_t* Tptr = T.GetArr(); + uint8_t* bufptr = seedbuf.GetArr();//m_vSeedbuf.GetArr() + ctr * AES_KEY_uint8_tS;//seedbuf.GetArr(); + + //HASH_CTX sha; + uint8_t hash_buf[m_cCrypto->get_hash_bytes()]; + + for(uint32_t i = ctr; i < ctr+processedOTs; i++, Tptr+=m_nCodeWordBytes, bufptr+=AES_BYTES) + { +#ifdef DEBUG_HASH_INPUT + cout << "hash input for i = " << i << " and choice = " << (uint32_t) m_nChoices.Get(i) << ": "; + T.PrintHex((i-ctr) * m_nCodeWordBytes, (i-ctr+1) * m_nCodeWordBytes); +#endif + m_cCrypto->hash_ctr(bufptr, AES_BYTES, Tptr, m_nCodeWordBytes, i); + //MPC_HASH_INIT(&sha); + //MPC_HASH_UPDATE(&sha, (uint8_t*) &i, sizeof(i)); + //MPC_HASH_UPDATE(&sha, Tptr, m_nCodeWordBytes); + //MPC_HASH_FINAL(&sha, hash_buf); + //memcpy(bufptr, hash_buf, AES_BYTES); +#ifdef DEBUG_HASH_OUTPUT + cout << "hash output for i = " << i << " and choice = " << (uint32_t) m_nChoices.Get(i) << ": "; + for(uint32_t j = 0; j < AES_BYTES; j++) + cout << (hex) << (unsigned int) bufptr[j]; + cout << (dec) << endl; +#endif + } + + m_fMaskFct->expandMask(m_nRet, seedbuf.GetArr(), ctr, processedOTs, m_nBitLength); + + +} + + +bool OTExtension1ooNECCReceiver::verifyOT(uint32_t NumOTs) +{ + CSocket sock = m_nSockets[0]; + CBitVector vRcvX[m_nSndVals]; + for(uint32_t u = 0; u < m_nSndVals; u++) { + vRcvX[u].Create(NUMOTBLOCKS*m_nCodeWordBits*m_nBitLength); + } + CBitVector* Xc; + uint64_t processedOTBlocks, OTsPerIteration; + uint64_t bytelen = ceil_divide(m_nBitLength, 8); + uint8_t* tempXc = new uint8_t[bytelen]; + uint8_t* tempRet = new uint8_t[bytelen]; + uint8_t resp; + for(uint64_t i = 0; i < NumOTs;) { + processedOTBlocks = min((uint64_t) NUMOTBLOCKS, ceil_divide(NumOTs-i, m_nCodeWordBits)); + //OTsPerIteration = processedOTBlocks * Z_REGISTER_BITS; + OTsPerIteration = min(processedOTBlocks * m_nCodeWordBits, NumOTs-i); + for(uint32_t u = 0; u < m_nSndVals; u++) { + sock.Receive(vRcvX[u].GetArr(), ceil_divide(m_nBitLength * OTsPerIteration, 8)); + } + for(uint32_t j = 0; j < OTsPerIteration && i < NumOTs; j++, i++) + { + Xc = &(vRcvX[m_nChoices.Get(i)]); + + Xc->GetBits(tempXc, j*m_nBitLength, m_nBitLength); + m_nRet.GetBits(tempRet, i*m_nBitLength, m_nBitLength); + for(uint32_t k = 0; k < bytelen; k++) + { + if(tempXc[k] != tempRet[k]) + { + cout << "Error at position i = " << i << ", k = " << k << ", with X" << (hex) << (uint32_t) m_nChoices.GetBitNoMask((uint64_t) i) + << " = " << (uint32_t) tempXc[k] << " and res = " << (uint32_t) tempRet[k] << (dec) << endl; + //<< " = " << ((uint32_t*) (tempXc + k))[0] << " and res = " << ((uint32_t*) (tempRet + k))[0] << (dec) << endl; + resp = 0x00; + sock.Send(&resp, 1); + return false; + } + } + } + resp = 0x01; + sock.Send(&resp, 1); + } + delete[] tempXc; + delete[] tempRet; + + for(uint32_t u = 0; u < m_nSndVals; u++) + vRcvX[u].delCBitVector(); + cout << "OT Verification successful" << endl; + return true; +} + + + +bool OTExtension1ooNECCSender::send(uint32_t numOTs, uint32_t bitlength, CBitVector* values, uint8_t type, + uint32_t numThreads, MaskingFunction* maskfct) +{ + m_nOTs = numOTs; + m_nBitLength = bitlength; + m_vValues = values; + m_bProtocol = type; + m_fMaskFct = maskfct; + return send(numThreads); +} + + +//Initialize and start numThreads OTSenderThread +bool OTExtension1ooNECCSender::send(uint32_t numThreads) +{ + if(m_nOTs == 0) + return true; + + if(m_bProtocol != R_OT && m_bProtocol != RN_OT) { + cerr << "Only working with R_OT or RN_OT right now, sorry!" << endl; + return false; + } + + + //The total number of OTs that is performed has to be a multiple of numThreads*Z_REGISTER_BITS + uint32_t numOTs = ceil_divide(pad_to_multiple(((uint64_t) m_nOTs), REGISTER_BITS), numThreads); + //cout << "numOTs: " << numOTs << endl; + m_nBlocks = 0; + m_lSendLock = new CLock; + + vector sThreads(numThreads); + + //uint8_t go; + //sock.Send(&go, 1); + + + for(uint32_t i = 0; i < numThreads; i++) + { + sThreads[i] = new OTSenderThread(i, numOTs, this); + sThreads[i]->Start(); + } + + for(uint32_t i = 0; i < numThreads; i++) + { + sThreads[i]->Wait(); + } + m_nCounter += m_nOTs; + + for(uint32_t i = 0; i < numThreads; i++) + delete sThreads[i]; + +#ifdef VERIFY_OT + uint8_t finished; + m_nSockets[0].Receive(&finished, 1); + + verifyOT(m_nOTs); +#endif + + + return true; +} + + +//bool OTsender(uint32_t nSndVals, uint32_t nOTs, uint32_t startpos, CSocket& sock, CBitVector& U, AES_KEY* vKeySeeds, CBitVector* values, uint8_t* seed) +bool OTExtension1ooNECCSender::OTSenderRoutine(uint32_t id, uint32_t myNumOTs) +{ + CSocket* sock = m_nSockets + id; + + uint32_t nProgress; + uint32_t myStartPos = id * myNumOTs; + uint32_t processedOTBlocks = min((uint32_t) NUMOTBLOCKS, ceil_divide(myNumOTs, m_nCodeWordBits)); + uint32_t OTsPerIteration = processedOTBlocks * m_nCodeWordBits; + + myNumOTs = min(myNumOTs + myStartPos, m_nOTs) - myStartPos; + uint32_t lim = myStartPos+myNumOTs; + + // The vector with the received bits + CBitVector vRcv(m_nCodeWordBits * OTsPerIteration); + + CBitVector* seedbuf = new CBitVector[m_nSndVals]; + for(uint32_t u = 0; u < m_nSndVals; u++) + seedbuf[u].Create(OTsPerIteration* AES_BITS); +#ifdef ZDEBUG + cout << "seedbuf size = " <Lock(); + //cout << "(" << id << ") Waiting for " << OTsPerIteration * m_nCodeWordBytes << " bytes on OT " << nProgress << endl; + sock->Receive(vRcv.GetArr(), OTsPerIteration * m_nCodeWordBytes); + //cout << "(" << id << ") received " << OTsPerIteration * m_nCodeWordBytes << " bytes for OT " << nProgress << endl; + //vRcv.PrintHex(0,1024); + //m_lSendLock->Unlock(); +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalRcvTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + BuildQMatrix(Q, vRcv, processedOTBlocks, ctr_buf); +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalMtxTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + Q.EklundhBitTranspose(m_nCodeWordBits, OTsPerIteration); +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalTnsTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + MaskInputs(Q, seedbuf, vSnd, nProgress, min(lim-nProgress, OTsPerIteration)); +#ifdef TIMING + gettimeofday(&tempEnd, NULL); + totalHshTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + nProgress += min(lim-nProgress, OTsPerIteration); + } + + vRcv.delCBitVector(); + Q.delCBitVector(); + for(uint32_t u = 0; u < m_nSndVals; u++) + seedbuf[u].delCBitVector(); + +#ifdef TIMING + cout << "Sender time benchmark for performing " << myNumOTs << " OTs on " << m_nBitLength << " bit strings" << endl; + cout << "Time needed for: " << endl; + cout << "\t Matrix Generation:\t" << totalMtxTime << " ms" << endl; + cout << "\t Sending Matrix:\t" << totalSndTime << " ms" << endl; + cout << "\t Transposing Matrix:\t" << totalTnsTime << " ms" << endl; + cout << "\t Hashing Matrix:\t" << totalHshTime << " ms" << endl; + cout << "\t Receiving Values:\t" << totalRcvTime << " ms" << endl; +#endif + + return TRUE; +} + +void OTExtension1ooNECCSender::BuildQMatrix(CBitVector& T, CBitVector& RcvBuf, uint32_t numblocks, uint8_t* ctr_buf) +{ + uint8_t* rcvbufptr = RcvBuf.GetArr(); + uint8_t* Tptr = T.GetArr(); + uint32_t* counter = (uint32_t*) ctr_buf; + uint32_t tempctr = *counter; + for (uint32_t k = 0; k < m_nCodeWordBits; k++, rcvbufptr += (m_nCodeWordBytes * numblocks)) + { + *counter = tempctr; + //one m_nCodeWordBytes / OTEXT_BLOCK_SIZE_uint8_tS = 2, thus 2 times the number of blocks + for(uint32_t b = 0; b < 2*numblocks; b++, (*counter)++, Tptr += OTEXT_BLOCK_SIZE_BYTES) { + m_cCrypto->encrypt(m_vKeySeeds + k, Tptr, ctr_buf, AES_BYTES);//MPC_AES_ENCRYPT(m_vKeySeeds + k, Tptr, ctr_buf); + } + if(m_nU.GetBit(k)) + { + T.XORBytes(rcvbufptr, k*m_nCodeWordBytes * numblocks, m_nCodeWordBytes * numblocks); + } + } +} + +void OTExtension1ooNECCSender::MaskInputs(CBitVector& Q, CBitVector* seedbuf, CBitVector* snd_buf, uint32_t ctr, uint32_t processedOTs) +{ + + //If OT-based PSI is performed, the hashing is skipped and the masking is called directly + if(m_bProtocol == RN_OT) { + m_fMaskFct->expandMask(Q, m_nU.GetArr(), ctr, processedOTs, m_nBitLength); //m_sSecParam.statbits + return; + //ncrfevals = 1; + } + + //HASH_CTX sha, shatmp; + uint8_t hash_buf[m_cCrypto->get_hash_bytes()]; + //SHA_BUFFER sha_buf; + + uint8_t** sbp = new uint8_t*[m_nSndVals]; + CBitVector mask(m_nCodeWordBits); + + uint32_t ncrfevals = m_nSndVals; + + + + for(uint32_t u = 0; u < m_nSndVals; u++) + sbp[u] = seedbuf[u].GetArr(); + + for(uint32_t i = ctr, j = 0; j(i)], 0, m_nCodeWordBytes); + else + mask.ANDBytes((uint8_t*) m_vCodeWords[u], 0, m_nCodeWordBytes); + + mask.XORBytes(Q.GetArr() + j * m_nCodeWordBytes, m_nCodeWordBytes); +#ifdef DEBUG_HASH_INPUT + cout << "hash input for i = " << i << " and u = " << u << ": "; + mask.PrintHex(); +#endif + //Q.XORBytes(mask.GetArr(), j * m_nCodeWordBytes, m_nCodeWordBytes); + m_cCrypto->hash_ctr(sbp[u], AES_BYTES, mask.GetArr(), m_nCodeWordBytes, i); + //sha = shatmp; + //MPC_HASH_UPDATE(&sha, mask.GetArr(), m_nCodeWordBytes); + //MPC_HASH_FINAL(&sha, hash_buf); +#ifdef DEBUG_HASH_OUTPUT + cout << "hash output for i = " << i << " and u = " << u << ": "; + for(uint32_t j = 0; j < AES_BYTES; j++) + cout << (hex) << (unsigned int) sbp[u][j]; + cout << (dec) << endl; +#endif + //memcpy(sbp[u], hash_buf, AES_BYTES); + sbp[u] += AES_BYTES; + } + } + + // Call expandMask to write data uint32_to snd_buf + cout << "Number of crf evals = " << ncrfevals << endl; + for(uint32_t u = 0; u < ncrfevals; u++) { + m_fMaskFct->expandMask(m_vValues[u], seedbuf[u].GetArr(), ctr, processedOTs, m_nBitLength); //m_sSecParam.statbits + } + + free(sbp); +} + + +bool OTExtension1ooNECCSender::verifyOT(uint32_t NumOTs) +{ + CSocket sock = m_nSockets[0]; + CBitVector vSnd(NUMOTBLOCKS*OTEXT_BLOCK_SIZE_BITS*m_nBitLength); + uint32_t processedOTBlocks, OTsPerIteration, nSnd; + uint8_t resp; + for(uint32_t i = 0, offset = 0; i < NumOTs;i+=OTsPerIteration, offset+=ceil_divide(m_nBitLength, 8)) + { + processedOTBlocks = min((uint32_t) NUMOTBLOCKS, ceil_divide(NumOTs-i, m_nCodeWordBits)); + OTsPerIteration = min(processedOTBlocks * m_nCodeWordBits, NumOTs-i); + nSnd = ceil_divide(OTsPerIteration * m_nBitLength, 8); + //cout << "copying " << nSnd << " bytes from " << CEIL_DIVIDE(i*m_nBitLength, 8) << ", for i = " << i << endl; + for(uint32_t u = 0; u < m_nSndVals; u++) { + vSnd.Copy(m_vValues[u].GetArr() + offset, 0, nSnd); + sock.Send(vSnd.GetArr(), nSnd); + } + sock.Receive(&resp, 1); + if(resp == 0x00) + { + cout << "OT verification unsuccessful" << endl; + return false; + } + } + vSnd.delCBitVector(); + cout << "OT Verification successful" << endl; + return true; +} + + diff --git a/src/util/ot/ot-extension-1oon-ecc.h b/src/util/ot/ot-extension-1oon-ecc.h new file mode 100644 index 0000000..707dac5 --- /dev/null +++ b/src/util/ot/ot-extension-1oon-ecc.h @@ -0,0 +1,129 @@ +/* + * Methods for the OT Extension routine + */ + +#ifndef __OT_EXTENSION_1N_H_ +#define __OT_EXTENSION_1N_H_ + +#include "../typedefs.h" +#include "../socket.h" +#include "../thread.h" +#include "../cbitvector.h" +#include "../crypto/crypto.h" +#include "maskingfunction.h" +#include "ot-extension.h" +#include "../crypto/crypto.h" + + + + +//TODO verification not working, fix +//#define ZDEBUG +//#define DEBUG_HASH_INPUT +//#define DEBUG_PRG_OUTPUT +//#define DEBUG_HASH_OUTPUT + + +class OTExtension1ooNECCSender : public OTExtSnd { +/* + * OT sender part + * Input: + * ret: returns the resulting bit representations. Has to initialized to a byte size of: nOTs * nSndVals * state.field_size + * + * CBitVector* values: holds the values to be transferred. If C_OT is enabled, the first dimension holds the value while the delta is written into the second dimension + * Output: was the execution successful? + */ + public: + OTExtension1ooNECCSender(uint32_t sndvals, uint32_t nOTs, uint32_t bitlength, crypto* crypt, CSocket* sock, CBitVector& U, uint8_t* keybytes, + CBitVector& x0, CBitVector& x1, uint8_t type) : OTExtSnd(sndvals, nOTs, bitlength, crypt, sock, U, keybytes, x0, x1, type, m_nCodeWordBits){ + InitAndReadCodeWord(&m_vCodeWords); + }; + + OTExtension1ooNECCSender(uint32_t nsndvals, crypto* crypt, CSocket* sock, CBitVector& U, uint8_t* keybytes) : + OTExtSnd(nsndvals, crypt, sock, U, keybytes, m_nCodeWordBits) { + InitAndReadCodeWord(&m_vCodeWords); + }; + + bool send(uint32_t numOTs, uint32_t bitlength, CBitVector* values, uint8_t type, uint32_t numThreads, MaskingFunction* maskfct); + bool send(uint32_t numThreads); + + bool OTSenderRoutine(uint32_t id, uint32_t myNumOTs); + void BuildQMatrix(CBitVector& T, CBitVector& RcvBuf, uint32_t blocksize, uint8_t* ctr); + void MaskInputs(CBitVector& Q, CBitVector* seedbuf, CBitVector* snd_buf, uint32_t ctr, uint32_t processedOTs); + bool verifyOT(uint32_t myNumOTs); + + private: + REGISTER_SIZE** m_vCodeWords; + + + class OTSenderThread : public CThread { + public: + OTSenderThread(uint32_t id, uint32_t nOTs, OTExtension1ooNECCSender* ext) {senderID = id; numOTs = nOTs; callback = ext; success = false;}; + ~OTSenderThread(){}; + void ThreadMain() {success = callback->OTSenderRoutine(senderID, numOTs);}; + private: + uint32_t senderID; + uint32_t numOTs; + OTExtension1ooNECCSender* callback; + bool success; + }; + +}; + + + +class OTExtension1ooNECCReceiver : public OTExtRec { +/* + * OT receiver part + * Input: + * nSndVals: perform a 1-out-of-nSndVals OT + * nOTs: the number of OTs that shall be performed + * choices: a vector containing nBaseOTs choices in the domain 0-(SndVals-1) + * ret: returns the resulting bit representations, Has to initialized to a byte size of: nOTs * state.field_size + * + * Output: was the execution successful? + */ + + public: + OTExtension1ooNECCReceiver(uint32_t nsndvals, uint32_t nOTs, uint32_t bitlength, crypto* crypt, CSocket* sock, + uint8_t* keybytes, CBitVector& choices, CBitVector& ret, uint8_t protocol) : + OTExtRec(nsndvals, nOTs, bitlength, crypt, sock, keybytes, choices, ret, protocol, m_nCodeWordBits) { + InitAndReadCodeWord(&m_vCodeWords); + }; + OTExtension1ooNECCReceiver(uint32_t nsndvals, crypto* crypt, CSocket* sock, uint8_t* keybytes) : + OTExtRec(nsndvals, crypt, sock, keybytes, m_nCodeWordBits) { + InitAndReadCodeWord(&m_vCodeWords); + }; + + + + bool receive(uint32_t numOTs, uint32_t bitlength, CBitVector& choices, CBitVector& ret, uint8_t type, + uint32_t numThreads, MaskingFunction* maskfct); + + bool receive(uint32_t numThreads); + bool OTReceiverRoutine(uint32_t id, uint32_t myNumOTs); + //void ReceiveAndProcess(CBitVector& vRcv, CBitVector& seedbuf, uint32_t id, uint32_t ctr, uint32_t lim); + void GenerateChoiceCodes(CBitVector& choicecodes, CBitVector& vSnd, uint32_t ctr, uint32_t lim); + void BuildMatrices(CBitVector& T, CBitVector& SndBuf, uint32_t numblocks, uint32_t ctr, uint8_t* ctr_buf); + void HashValues(CBitVector& T, CBitVector& seedbuf, uint32_t ctr, uint32_t lim); + bool verifyOT(uint32_t myNumOTs); + + + private: + REGISTER_SIZE** m_vCodeWords; + + class OTReceiverThread : public CThread { + public: + OTReceiverThread(uint32_t id, uint32_t nOTs, OTExtension1ooNECCReceiver* ext) {receiverID = id; numOTs = nOTs; callback = ext; success = false;}; + ~OTReceiverThread(){}; + void ThreadMain() {success = callback->OTReceiverRoutine(receiverID, numOTs);}; + private: + uint32_t receiverID; + uint32_t numOTs; + OTExtension1ooNECCReceiver* callback; + bool success; + }; + +}; + +#endif diff --git a/src/util/ot/ot-extension.cpp b/src/util/ot/ot-extension.cpp new file mode 100644 index 0000000..ae27fb6 --- /dev/null +++ b/src/util/ot/ot-extension.cpp @@ -0,0 +1,786 @@ +#include "ot-extension.h" + + + + +BOOL OTExtensionReceiver::receive(int numOTs, int bitlength, CBitVector& choices, CBitVector& ret, BYTE type, int numThreads, MaskingFunction* unmaskfct) +{ + m_nOTs = numOTs; + m_nBitLength = bitlength; + m_nChoices = choices; + m_nRet = ret; + m_bProtocol = type; + m_fMaskFct = unmaskfct; + return receive(numThreads); +}; + +//Initialize and start numThreads OTSenderThread +BOOL OTExtensionReceiver::receive(int numThreads) +{ + if(m_nOTs == 0) + return true; + + //The total number of OTs that is performed has to be a multiple of numThreads*Z_REGISTER_BITS + int internal_numOTs = CEIL_DIVIDE(PadToRegisterSize(m_nOTs), numThreads); + + //BYTE go; + //Wait for the signal of the corresponding sender thread + //sock.Receive(&go, 1); + + //Create temporary result buf to which the threads write their temporary masks + m_vTempOTMasks.Create(internal_numOTs * numThreads * m_nBitLength); + + vector rThreads(numThreads); + for(int i = 0; i < numThreads; i++) + { + rThreads[i] = new OTReceiverThread(i, internal_numOTs, this); + rThreads[i]->Start(); + } + + if(m_bProtocol != R_OT && m_bProtocol != OCRS_OT) + { + ReceiveAndProcess(numThreads); + } + + for(int i = 0; i < numThreads; i++) + { + rThreads[i]->Wait(); + } + m_nCounter += m_nOTs; + + for(int i = 0; i < numThreads; i++) + delete rThreads[i]; + + if(m_bProtocol == R_OT || m_bProtocol == OCRS_OT) { + m_nRet.Copy(m_vTempOTMasks.GetArr(), 0, CEIL_DIVIDE(m_nOTs * m_nBitLength, 8)); + m_vTempOTMasks.delCBitVector(); + } + + +#ifdef VERIFY_OT + //Wait for the signal of the corresponding sender thread + BYTE finished = 0x01; + m_nSockets[0].Send(&finished, 1); + + verifyOT(m_nOTs); +#endif + + + return true; +} + + + +BOOL OTExtensionReceiver::OTReceiverRoutine(int id, int myNumOTs) +{ + //cout << "Thread " << id << " started" << endl; + int myStartPos = id * myNumOTs; + int i = myStartPos, nProgress = myStartPos; + int RoundWindow = 2; + int roundctr = 0; + + myNumOTs = min(myNumOTs + myStartPos, m_nOTs) - myStartPos; + int lim = myStartPos+myNumOTs; + + int processedOTBlocks = min(NUMOTBLOCKS, CEIL_DIVIDE(myNumOTs, OTEXT_BLOCK_SIZE_BITS)); + int OTsPerIteration = processedOTBlocks * OTEXT_BLOCK_SIZE_BITS; + int OTwindow = NUMOTBLOCKS*OTEXT_BLOCK_SIZE_BITS*RoundWindow; + CSocket sock = m_nSockets[id]; + + //counter variables + int numblocks = CEIL_DIVIDE(myNumOTs, OTsPerIteration); + int nSize; + + // The receive buffer + CBitVector vRcv; + if(m_bProtocol == G_OT) + vRcv.Create(OTsPerIteration * m_nBitLength * m_nSndVals); + else if(m_bProtocol == C_OT || m_bProtocol == S_OT) + vRcv.Create(OTsPerIteration * m_nBitLength); + + // A temporary part of the T matrix + CBitVector T(OTEXT_BLOCK_SIZE_BITS * OTsPerIteration); + + // The send buffer + CBitVector vSnd(m_nSymSecParam * OTsPerIteration); + + // A temporary buffer that stores the resulting seeds from the hash buffer + //TODO: Check for some maximum size + CBitVector seedbuf(OTwindow*AES_KEY_BITS);// = new CBitVector[RoundWindow]; + //for(int j = 0; j < RoundWindow; j++) + // seedbuf[j].Create(OTwindow * AES_KEY_BITS); + + + + BYTE ctr_buf[AES_BYTES] = {0}; + int* counter = (int*) ctr_buf; + (*counter) = myStartPos + m_nCounter; + +#ifdef OTTiming + double totalMtxTime = 0, totalTnsTime = 0, totalHshTime = 0, totalRcvTime = 0, totalSndTime = 0; + timeval tempStart, tempEnd; +#endif + + while( i < lim ) + { + processedOTBlocks = min(NUMOTBLOCKS, CEIL_DIVIDE(lim-i, OTEXT_BLOCK_SIZE_BITS)); + OTsPerIteration = processedOTBlocks * OTEXT_BLOCK_SIZE_BITS; + nSize = (m_nSymSecParam>>3) * OTsPerIteration; + +#ifdef OTTiming + gettimeofday(&tempStart, NULL); +#endif + BuildMatrices(T, vSnd, processedOTBlocks, i, ctr_buf); +#ifdef OTTiming + gettimeofday(&tempEnd, NULL); + totalMtxTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + T.EklundhBitTranspose(OTEXT_BLOCK_SIZE_BITS, OTsPerIteration); +#ifdef OTTiming + gettimeofday(&tempEnd, NULL); + totalTnsTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + //cout << "offset: " << (AES_KEY_BYTES * (i-nProgress))<< ", i = " << i << ", nprogress = " << nProgress << ", otwindow = " << OTwindow << endl; + HashValues(T, seedbuf, i, min(lim-i, OTsPerIteration)); +#ifdef OTTiming + gettimeofday(&tempEnd, NULL); + totalHshTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + + //cout << "Sending " << nSize << " Bytes " << endl; + sock.Send( vSnd.GetArr(), nSize ); +#ifdef OTTiming + gettimeofday(&tempEnd, NULL); + totalSndTime += getMillies(tempStart, tempEnd); + gettimeofday(&tempStart, NULL); +#endif + i+=min(lim-i, OTsPerIteration); + +#ifdef OTTiming + gettimeofday(&tempEnd, NULL); + totalRcvTime += getMillies(tempStart, tempEnd); +#endif + vSnd.Reset(); + } + + T.delCBitVector(); + vSnd.delCBitVector(); + vRcv.delCBitVector(); + seedbuf.delCBitVector(); + +#ifdef OTTiming + cout << "Receiver time benchmark for performing " << myNumOTs << " OTs on " << m_nBitLength << " bit strings" << endl; + cout << "Time needed for: " << endl; + cout << "\t Matrix Generation:\t" << totalMtxTime << " ms" << endl; + cout << "\t Sending Matrix:\t" << totalSndTime << " ms" << endl; + cout << "\t Transposing Matrix:\t" << totalTnsTime << " ms" << endl; + cout << "\t Hashing Matrix:\t" << totalHshTime << " ms" << endl; + cout << "\t Receiving Values:\t" << totalRcvTime << " ms" << endl; +#endif +#ifndef BATCH + cout << "Receiver finished successfully" << endl; +#endif + //sleep(1); + return TRUE; +} + + + +void OTExtensionReceiver::BuildMatrices(CBitVector& T, CBitVector& SndBuf, int numblocks, int ctr, BYTE* ctr_buf) +{ + int* counter = (int*) ctr_buf; + int tempctr = (*counter); + + BYTE* Tptr = T.GetArr(); + BYTE* sndbufptr = SndBuf.GetArr(); + int ctrbyte = ctr/8; + for(int k = 0; k < m_nSymSecParam; k++) + { + (*counter) = tempctr; + for(int b = 0; b < numblocks; b++, (*counter)++) + { + MPC_AES_ENCRYPT(m_vKeySeedMtx + 2*k, Tptr, ctr_buf); + Tptr+=OTEXT_BLOCK_SIZE_BYTES; + + MPC_AES_ENCRYPT(m_vKeySeedMtx + (2*k) + 1, sndbufptr, ctr_buf); + sndbufptr+=OTEXT_BLOCK_SIZE_BYTES; + } + SndBuf.XORBytesReverse(m_nChoices.GetArr()+ctrbyte, k*OTEXT_BLOCK_SIZE_BYTES * numblocks, OTEXT_BLOCK_SIZE_BYTES * numblocks); + } + SndBuf.XORBytes(T.GetArr(), 0, OTEXT_BLOCK_SIZE_BYTES*numblocks*m_nSymSecParam); +} + + + +void OTExtensionReceiver::HashValues(CBitVector& T, CBitVector& seedbuf, int ctr, int processedOTs) +{ + BYTE* Tptr = T.GetArr(); + BYTE* bufptr = seedbuf.GetArr();//m_vSeedbuf.GetArr() + ctr * AES_KEY_BYTES;//seedbuf.GetArr(); + + HASH_CTX sha; + BYTE hash_buf[SHA1_BYTES]; + + for(int i = ctr; i < ctr+processedOTs; i++, Tptr+=OTEXT_BLOCK_SIZE_BYTES, bufptr+=AES_KEY_BYTES) + { + if((m_bProtocol == S_OT || m_bProtocol == OCRS_OT) && m_nChoices.GetBitNoMask(i) == 0) + { + continue; + } + + //for(hash_ctr = 0; hash_ctr < numhashiters; hash_ctr++, sha_buf.data+=SHA1_BYTES) + //{ +#ifdef FIXED_KEY_AES_HASHING + FixedKeyHashing(m_kCRFKey, bufptr, Tptr, hash_buf, i, m_nSymSecParam>>3); +#else + MPC_HASH_INIT(&sha); + MPC_HASH_UPDATE(&sha, (BYTE*) &i, sizeof(i)); + //OTEXT_HASH_UPDATE(&sha, (BYTE*) &hash_ctr, sizeof(hash_ctr)); + MPC_HASH_UPDATE(&sha, Tptr, m_nSymSecParam>>3); + MPC_HASH_FINAL(&sha, hash_buf); + //} + memcpy(bufptr, hash_buf, AES_KEY_BYTES); +#endif + + + //m_nRet.SetBits(hash_buf, i * m_nBitLength, m_nBitLength); + } + + // + m_fMaskFct->expandMask(m_vTempOTMasks, seedbuf.GetArr(), ctr, processedOTs, m_nBitLength); +} + + +//void OTExtensionReceiver::ReceiveAndProcess(CBitVector& vRcv, CBitVector& seedbuf, int id, int ctr, int processedOTs) +void OTExtensionReceiver::ReceiveAndProcess(int numThreads) +{ + int progress = 0; + int threadOTs = CEIL_DIVIDE(PadToRegisterSize(m_nOTs), numThreads); + int processedOTBlocks = min(NUMOTBLOCKS, CEIL_DIVIDE(threadOTs, OTEXT_BLOCK_SIZE_BITS)); + int OTsPerIteration = processedOTBlocks * OTEXT_BLOCK_SIZE_BITS; + int processedOTs; + int OTid; + int rcvbytes; + CBitVector vRcv; + +#ifdef OTTiming + double totalRcvTime = 0; + timeval tempStart, tempEnd; +#endif + + if(m_bProtocol == G_OT) + vRcv.Create(OTsPerIteration * m_nBitLength * m_nSndVals); + else if(m_bProtocol == C_OT || m_bProtocol == S_OT) + vRcv.Create(OTsPerIteration * m_nBitLength); + + while(progress < m_nOTs) + { + //cout << "Waiting for block " << endl; + + + m_nSockets[0].Receive((BYTE*) &OTid, sizeof(int)); + //cout << "Processing blockid " << OTid; + m_nSockets[0].Receive((BYTE*) &processedOTs, sizeof(int)); + //cout << " with " << processedOTs << " OTs "; + rcvbytes = CEIL_DIVIDE(processedOTs * m_nBitLength, 8); + if(m_bProtocol == G_OT) + rcvbytes = rcvbytes*m_nSndVals; + //cout << "Receiving " << rcvbytes << " bytes" << endl; + rcvbytes = m_nSockets[0].Receive(vRcv.GetArr(), rcvbytes); + +#ifdef OTTiming + gettimeofday(&tempStart, NULL); +#endif + //cout << "unmask" << endl; + m_fMaskFct->UnMask(OTid, processedOTs, m_nChoices, m_nRet, vRcv, m_vTempOTMasks, m_bProtocol); +#ifdef OTTiming + gettimeofday(&tempEnd, NULL); + totalRcvTime += getMillies(tempStart, tempEnd); +#endif + progress += processedOTs; + } + +#ifdef OTTiming + cout << "Total time spent processing received data: " << totalRcvTime << endl; +#endif + + vRcv.delCBitVector(); +} + +BOOL OTExtensionReceiver::verifyOT(int NumOTs) +{ + CSocket sock = m_nSockets[0]; + CBitVector vRcvX0(NUMOTBLOCKS*OTEXT_BLOCK_SIZE_BITS*m_nBitLength); + CBitVector vRcvX1(NUMOTBLOCKS*OTEXT_BLOCK_SIZE_BITS*m_nBitLength); + CBitVector* Xc; + int processedOTBlocks, OTsPerIteration; + int bytelen = CEIL_DIVIDE(m_nBitLength, 8); + BYTE* tempXc = new BYTE[bytelen]; + BYTE* tempRet = new BYTE[bytelen]; + BYTE resp; + for(int i = 0; i < NumOTs;) + { + processedOTBlocks = min(NUMOTBLOCKS, CEIL_DIVIDE(NumOTs-i, OTEXT_BLOCK_SIZE_BITS)); + //OTsPerIteration = processedOTBlocks * Z_REGISTER_BITS; + OTsPerIteration = min(processedOTBlocks * OTEXT_BLOCK_SIZE_BITS, NumOTs-i); + sock.Receive(vRcvX0.GetArr(), CEIL_DIVIDE(m_nBitLength * OTsPerIteration, 8)); + sock.Receive(vRcvX1.GetArr(), CEIL_DIVIDE(m_nBitLength * OTsPerIteration, 8)); + for(int j = 0; j < OTsPerIteration && i < NumOTs; j++, i++) + { + if(m_nChoices.GetBitNoMask(i) == 0) Xc = &vRcvX0; + else Xc = &vRcvX1; + + Xc->GetBits(tempXc, j*m_nBitLength, m_nBitLength); + m_nRet.GetBits(tempRet, i*m_nBitLength, m_nBitLength); + for(int k = 0; k < bytelen; k++) + { + if(tempXc[k] != tempRet[k]) + { + cout << "Error at position i = " << i << ", k = " << k << ", with X" << (hex) << (unsigned int) m_nChoices.GetBitNoMask(i) + << " = " << (unsigned int) tempXc[k] << " and res = " << (unsigned int) tempRet[k] << (dec) << endl; + resp = 0x00; + sock.Send(&resp, 1); + return false; + } + } + } + resp = 0x01; + sock.Send(&resp, 1); + } + delete[] tempXc; + delete[] tempRet; + + vRcvX0.delCBitVector(); + vRcvX1.delCBitVector(); + + cout << "OT Verification successful" << endl; + return true; +} + + + +BOOL OTExtensionSender::send(int numOTs, int bitlength, CBitVector& x0, CBitVector& x1, BYTE type, + int numThreads, MaskingFunction* maskfct) +{ + m_nOTs = numOTs; + m_nBitLength = bitlength; + m_vValues[0] = x0; + m_vValues[1] = x1; + m_bProtocol = type; + m_fMaskFct = maskfct; + return send(numThreads); +} + + +//Initialize and start numThreads OTSenderThread +BOOL OTExtensionSender::send(int numThreads) +{ + if(m_nOTs == 0) + return true; + + //The total number of OTs that is performed has to be a multiple of numThreads*Z_REGISTER_BITS + int numOTs = CEIL_DIVIDE(PadToRegisterSize(m_nOTs), numThreads); + m_nBlocks = 0; + m_lSendLock = new CLock; + + + vector sThreads(numThreads); + + //BYTE go; + //sock.Send(&go, 1); + + for(int i = 0; i < numThreads; i++) + { + sThreads[i] = new OTSenderThread(i, numOTs, this); + sThreads[i]->Start(); + } + + if(m_bProtocol != R_OT && m_bProtocol != OCRS_OT) + { + SendBlocks(numThreads); + } + + for(int i = 0; i < numThreads; i++) + { + sThreads[i]->Wait(); + } + m_nCounter += m_nOTs; + + for(int i = 0; i < numThreads; i++) + delete sThreads[i]; + +#ifdef VERIFY_OT + BYTE finished; + m_nSockets[0].Receive(&finished, 1); + + verifyOT(m_nOTs); +#endif + +/* cout << "OT0 val= "; + m_vValues[0].PrintBinary(); + cout << "OT1 val= "; + m_vValues[1].PrintBinary();*/ + + return true; +} + + +//BOOL OTsender(int nSndVals, int nOTs, int startpos, CSocket& sock, CBitVector& U, AES_KEY* vKeySeeds, CBitVector* values, BYTE* seed) +BOOL OTExtensionSender::OTSenderRoutine(int id, int myNumOTs) +{ + CSocket sock = m_nSockets[id]; + + int nProgress; + int myStartPos = id * myNumOTs; + int processedOTBlocks = min(NUMOTBLOCKS, CEIL_DIVIDE(myNumOTs, OTEXT_BLOCK_SIZE_BITS)); + int OTsPerIteration = processedOTBlocks * OTEXT_BLOCK_SIZE_BITS; + + myNumOTs = min(myNumOTs + myStartPos, m_nOTs) - myStartPos; + int lim = myStartPos+myNumOTs; + + //TODO: Check if this works: + if(m_bProtocol == S_OT || m_bProtocol == OCRS_OT) + m_nSndVals = 1; + + // The vector with the received bits + CBitVector vRcv(m_nSymSecParam * OTsPerIteration); + + // Holds the reply that is sent back to the receiver + int numsndvals = 2; + CBitVector* vSnd; + + /*if(m_bProtocol == G_OT) numsndvals = 2; + else if (m_bProtocol == C_OT || m_bProtocol == S_OT) numsndvals = 1; + else numsndvals = 0;*/ + + CBitVector* seedbuf = new CBitVector[m_nSndVals]; + for(int u = 0; u < m_nSndVals; u++) + seedbuf[u].Create(OTsPerIteration* AES_KEY_BITS); +#ifdef ZDEBUG + cout << "seedbuf size = " < 0) free(vSnd); + +#ifdef OTTiming + cout << "Sender time benchmark for performing " << myNumOTs << " OTs on " << m_nBitLength << " bit strings" << endl; + cout << "Time needed for: " << endl; + cout << "\t Matrix Generation:\t" << totalMtxTime << " ms" << endl; + cout << "\t Sending Matrix:\t" << totalSndTime << " ms" << endl; + cout << "\t Transposing Matrix:\t" << totalTnsTime << " ms" << endl; + cout << "\t Hashing Matrix:\t" << totalHshTime << " ms" << endl; + cout << "\t Receiving Values:\t" << totalRcvTime << " ms" << endl; +#endif + +#ifndef BATCH + cout << "Sender finished successfully" << endl; +#endif + return TRUE; +} + +void OTExtensionSender::BuildQMatrix(CBitVector& T, CBitVector& RcvBuf, int numblocks, BYTE* ctr_buf) +{ + BYTE* rcvbufptr = RcvBuf.GetArr(); + BYTE* Tptr = T.GetArr(); + int dummy; + int* counter = (int*) ctr_buf; + int tempctr = *counter; + for (int k = 0; k < m_nSymSecParam; k++, rcvbufptr += (OTEXT_BLOCK_SIZE_BYTES * numblocks)) + { + *counter = tempctr; + for(int b = 0; b < numblocks; b++, (*counter)++, Tptr += OTEXT_BLOCK_SIZE_BYTES) + { + MPC_AES_ENCRYPT(m_vKeySeeds + k, Tptr, ctr_buf); + } + if(m_nU.GetBit(k)) + { + T.XORBytes(rcvbufptr, k*OTEXT_BLOCK_SIZE_BYTES * numblocks, OTEXT_BLOCK_SIZE_BYTES * numblocks); + } + } +} + +void OTExtensionSender::MaskInputs(CBitVector& Q, CBitVector* seedbuf, CBitVector* snd_buf, int ctr, int processedOTs) +{ + int numhashiters = CEIL_DIVIDE(m_nBitLength, SHA1_BITS); + HASH_CTX sha, shatmp; + + BYTE hash_buf[SHA1_BYTES]; + //SHA_BUFFER sha_buf; + BYTE* Qptr = Q.GetArr(); + + BYTE** sbp = new BYTE*[m_nSndVals]; + + for(int u = 0; u < m_nSndVals; u++) + sbp[u] = seedbuf[u].GetArr(); + + for(int i = ctr, j = 0; j>3); + + + if(u == 1 || m_bProtocol == S_OT || m_bProtocol == OCRS_OT) + Q.XORBytes(m_nU.GetArr(), j * OTEXT_BLOCK_SIZE_BYTES, m_nSymSecParam>>3); + +#ifdef FIXED_KEY_AES_HASHING + //AES_KEY_CTX* aeskey, BYTE* outbuf, BYTE* inbuf, BYTE* tmpbuf, int id, int bytessecparam + FixedKeyHashing(m_kCRFKey, sbp[u], Q.GetArr() + j * OTEXT_BLOCK_SIZE_BYTES, hash_buf, i, m_nSymSecParam>>3); +#else + sha = shatmp; + MPC_HASH_UPDATE(&sha, Q.GetArr()+j * OTEXT_BLOCK_SIZE_BYTES, m_nSymSecParam>>3); + MPC_HASH_FINAL(&sha, hash_buf); + + memcpy(sbp[u], hash_buf, AES_KEY_BYTES); +#endif + + //cout << ((unsigned int) sbp[u][0] & 0x01); + sbp[u] += AES_KEY_BYTES; + + if(m_bProtocol == S_OT || m_bProtocol == OCRS_OT) + { + u=m_nSndVals-1; + } + } + } + + if(m_bProtocol == S_OT || m_bProtocol == OCRS_OT) + { + m_fMaskFct->expandMask(snd_buf[0], seedbuf[0].GetArr(), 0, processedOTs, m_nBitLength); + return; + } + + //Two calls to expandMask, both writing into snd_buf + for(int u = 0; u < m_nSndVals; u++) + m_fMaskFct->expandMask(snd_buf[u], seedbuf[u].GetArr(), 0, processedOTs, m_nBitLength); +} + +void OTExtensionSender::ProcessAndEnqueue(CBitVector* snd_buf, int id, int progress, int processedOTs) +{ + //cout << "processed OTs: " << processedOTs << endl; + m_fMaskFct->Mask(progress, processedOTs, m_vValues, snd_buf, m_bProtocol); + + if(m_bProtocol == R_OT) + return; + + OTBlock* block = new OTBlock; + int bufsize = CEIL_DIVIDE(processedOTs * m_nBitLength, 8); + + block->blockid = progress; + block->processedOTs = processedOTs; + + + if(m_bProtocol == G_OT) + { + block->snd_buf = new BYTE[bufsize<<1]; + memcpy(block->snd_buf, snd_buf[0].GetArr(), bufsize); + memcpy(block->snd_buf+bufsize, snd_buf[1].GetArr(), bufsize); + } + else if(m_bProtocol == C_OT) + { + block->snd_buf = new BYTE[bufsize]; + memcpy(block->snd_buf, snd_buf[1].GetArr(), bufsize); + } + else if(m_bProtocol == S_OT) + { + block->snd_buf = new BYTE[bufsize]; + memcpy(block->snd_buf, snd_buf[0].GetArr(), bufsize); + } + + + m_lSendLock->Lock(); + //Lock this part if multiple threads are used! + if(m_nBlocks == 0) + { + m_sBlockHead = block; + m_sBlockTail = block; + } else { + m_sBlockTail->next = block; + m_sBlockTail = block; + } + m_nBlocks++; + m_lSendLock->Unlock(); +} + + +void OTExtensionSender::SendBlocks(int numThreads) +{ + int progress = 0; + OTBlock* tempBlock; + +#ifdef OTTiming + double totalTnsTime = 0; + timeval tempStart, tempEnd; +#endif + + while(progress < m_nOTs) + { + if(m_nBlocks > 0) + { +#ifdef OTTiming + gettimeofday(&tempStart, NULL); +#endif + tempBlock = m_sBlockHead; + if(m_bProtocol == G_OT) + { + m_nSockets[0].Send((BYTE*) &(tempBlock->blockid), sizeof(int)); + m_nSockets[0].Send((BYTE*) &(tempBlock->processedOTs), sizeof(int)); + m_nSockets[0].Send(tempBlock->snd_buf, 2*CEIL_DIVIDE((tempBlock->processedOTs) * m_nBitLength, 8)); + } + else if(m_bProtocol == C_OT) + { + m_nSockets[0].Send((BYTE*) &(tempBlock->blockid), sizeof(int)); + m_nSockets[0].Send((BYTE*) &(tempBlock->processedOTs), sizeof(int)); + m_nSockets[0].Send(tempBlock->snd_buf, CEIL_DIVIDE((tempBlock->processedOTs) * m_nBitLength, 8)); + } + else if(m_bProtocol == S_OT) + { + m_nSockets[0].Send((BYTE*) &(tempBlock->blockid), sizeof(int)); + m_nSockets[0].Send((BYTE*) &(tempBlock->processedOTs), sizeof(int)); + m_nSockets[0].Send(tempBlock->snd_buf, CEIL_DIVIDE((tempBlock->processedOTs) * m_nBitLength, 8)); + } + //Lock this part + m_sBlockHead = m_sBlockHead->next; + + m_lSendLock->Lock(); + m_nBlocks--; + m_lSendLock->Unlock(); + + progress += tempBlock->processedOTs; + + delete tempBlock->snd_buf; + delete tempBlock; + +#ifdef OTTiming + gettimeofday(&tempEnd, NULL); + totalTnsTime += getMillies(tempStart, tempEnd); +#endif + } + } +#ifdef OTTiming + cout << "Total time spent transmitting data: " << totalTnsTime << endl; +#endif +} + + + +BOOL OTExtensionSender::verifyOT(int NumOTs) +{ + CSocket sock = m_nSockets[0]; + CBitVector vSnd(NUMOTBLOCKS*OTEXT_BLOCK_SIZE_BITS*m_nBitLength); + int processedOTBlocks, OTsPerIteration; + int bytelen = CEIL_DIVIDE(m_nBitLength, 8); + int nSnd; + BYTE resp; + for(int i = 0; i < NumOTs;i+=OTsPerIteration) + { + processedOTBlocks = min(NUMOTBLOCKS, CEIL_DIVIDE(NumOTs-i, OTEXT_BLOCK_SIZE_BITS)); + OTsPerIteration = min(processedOTBlocks * OTEXT_BLOCK_SIZE_BITS, NumOTs-i); + nSnd = CEIL_DIVIDE(OTsPerIteration * m_nBitLength, 8); + //cout << "copying " << nSnd << " bytes from " << CEIL_DIVIDE(i*m_nBitLength, 8) << ", for i = " << i << endl; + vSnd.Copy(m_vValues[0].GetArr() + CEIL_DIVIDE(i*m_nBitLength, 8), 0, nSnd); + sock.Send(vSnd.GetArr(), nSnd); + vSnd.Copy(m_vValues[1].GetArr() + CEIL_DIVIDE(i*m_nBitLength, 8), 0, nSnd); + sock.Send(vSnd.GetArr(), nSnd); + sock.Receive(&resp, 1); + if(resp == 0x00) + { + cout << "OT verification unsuccessful" << endl; + return false; + } + } + vSnd.delCBitVector(); + cout << "OT Verification successful" << endl; + return true; +} + + diff --git a/src/util/ot/ot-extension.h b/src/util/ot/ot-extension.h new file mode 100644 index 0000000..2307175 --- /dev/null +++ b/src/util/ot/ot-extension.h @@ -0,0 +1,284 @@ +/* + * Methods for the OT Extension routine + */ + +#ifndef __OT_EXTENSION_H_ +#define __OT_EXTENSION_H_ + +#include "../typedefs.h" +#include "../socket.h" +#include "../thread.h" +#include "../cbitvector.h" +#include "../crypto/crypto.h" +#include "maskingfunction.h" + + +//#define DEBUG +//#define FIXED_KEY_AES_HASHING +//#define VERIFY_OT + +const uint8_t G_OT = 0x01; +const uint8_t C_OT = 0x02; +const uint8_t R_OT = 0x03; +const uint8_t S_OT = 0x04; +const uint8_t OCRS_OT = 0x05; +const uint8_t RN_OT = 0x06; + + +typedef struct OTBlock_t { + uint32_t blockid; + uint32_t processedOTs; + uint8_t* snd_buf; + OTBlock_t* next; +} OTBlock; + +#define NUMOTBLOCKS 256 +#define REGISTER_BITS AES_BITS +#define REGISTER_BYTES AES_BYTES + + +static void InitAESKey(AES_KEY_CTX* ctx, uint8_t* keybytes, uint32_t numkeys) +{ + uint8_t* pBufIdx = keybytes; + for(uint32_t i=0; iget_seclvl().symbits; + m_vValues = (CBitVector*) malloc(sizeof(CBitVector) * nSndVals); + m_vKeySeeds = (AES_KEY_CTX*) malloc(sizeof(AES_KEY_CTX) * nbaseOTs); + InitAESKey(m_vKeySeeds, keybytes, nbaseOTs); + + m_lSendLock = new CLock; + + +#ifdef FIXED_KEY_AES_HASHING + m_kCRFKey = (AES_KEY_CTX*) malloc(sizeof(AES_KEY_CTX)); + m_cCrypto->init_aes_key(m_kCRFKey, XXLT.symbits, (uint8_t*) fixedkeyseed); + //MPC_AES_KEY_INIT(m_kCRFKey); + //MPC_AES_KEY_EXPAND(m_kCRFKey, fixedkeyseed); +#endif + }; + + ~OTExtSnd(){free(m_vKeySeeds);}; + bool send(uint32_t numOTs, uint32_t bitlength, CBitVector& s0, CBitVector& s1, uint8_t type, uint32_t numThreads, MaskingFunction* maskfct); + bool send(uint32_t numThreads); + + bool OTSenderRoutine(uint32_t id, uint32_t myNumOTs); + void BuildQMatrix(CBitVector& T, CBitVector& RcvBuf, uint32_t blocksize, uint8_t* ctr); + void ProcessAndEnqueue(CBitVector* snd_buf, uint32_t id, uint32_t progress, uint32_t processedOTs); + void SendBlocks(uint32_t numThreads); + void HashValues(CBitVector& Q, CBitVector* seedbuf, CBitVector* snd_buf, uint32_t ctr, uint32_t processedOTs); + bool verifyOT(uint32_t myNumOTs); + + + + protected: + uint8_t m_bProtocol; + uint32_t m_nSndVals; + uint32_t m_nOTs; + uint32_t m_nBitLength; + uint32_t m_nCounter; + uint32_t m_nBlocks; + uint32_t m_nSymSecParam; + CSocket* m_nSockets; + CBitVector m_nU; + CBitVector* m_vValues; + MaskingFunction* m_fMaskFct; + AES_KEY_CTX* m_vKeySeeds; + OTBlock* m_sBlockHead; + OTBlock* m_sBlockTail; + CLock* m_lSendLock; + crypto* m_cCrypto; +#ifdef FIXED_KEY_AES_HASH_OPRG + AES_KEY_CTX* m_kCRFKey; +#endif + + class OTSenderThread : public CThread { + public: + OTSenderThread(uint32_t id, uint32_t nOTs, OTExtSnd* ext) {senderID = id; numOTs = nOTs; callback = ext; success = false;}; + ~OTSenderThread(){}; + void ThreadMain() {success = callback->OTSenderRoutine(senderID, numOTs);}; + private: + uint32_t senderID; + uint32_t numOTs; + OTExtSnd* callback; + bool success; + }; + +}; + + + +class OTExtRec { +/* + * OT receiver part + * Input: + * nSndVals: perform a 1-out-of-nSndVals OT + * nOTs: the number of OTs that shall be performed + * choices: a vector containing nBaseOTs choices in the domain 0-(SndVals-1) + * ret: returns the resulting bit representations, Has to initialized to a byte size of: nOTs * state.field_size + * + * Output: was the execution successful? + */ + public: + OTExtensionReceiver(uint32_t nSndVals, uint32_t nOTs, uint32_t bitlength, crypto* crypt, CSocket* sock, + uint8_t* keybytes, CBitVector& choices, CBitVector& ret, uint8_t protocol, uint32_t nbaseOTs) { + Init(nSndVals, crypt, sock, keybytes, nbaseOTs); + //m_nSndVals = nSndVals; + m_nOTs = nOTs; + //m_nSockets = sock; + m_nChoices = choices; + m_nRet = ret; + //m_nSeed = seed; + m_nBitLength = bitlength; + m_eOTFlav = protocol; + //m_nCounter = 0; + //m_nSymSecParam = symsecparam; + //m_vKeySeedMtx = (AES_KEY_CTX*) malloc(sizeof(AES_KEY_CTX) * m_nSymSecParam * nSndVals); + //InitAESKey(m_vKeySeedMtx, keybytes, m_nSymSecParam * nSndVals); + }; + OTExtensionReceiver(uint32_t nSndVals, crypto* crypt, CSocket* sock, uint8_t* keybytes, uint32_t nbaseOTs) { + Init(nSndVals, crypt, sock, keybytes, nbaseOTs); + }; + + void Init(uint32_t nSndVals, crypto* crypt, CSocket* sock, uint8_t* keybytes, uint32_t nbaseOTs) { + m_nSndVals = nSndVals; + m_nSockets = sock; + //m_nKeySeedMtx = vKeySeedMtx; + m_cCrypto = crypt; + m_nSymSecParam = m_cCrypto->get_seclvl().symbits; + + + m_nCounter = 0; + m_vKeySeedMtx = (AES_KEY_CTX*) malloc(sizeof(AES_KEY_CTX) * nbaseOTs * nSndVals); + InitAESKey(m_vKeySeedMtx, keybytes, nbaseOTs * nSndVals); + + m_nSeed = (uint8_t*) malloc(sizeof(AES_BYTES)); // + m_cCrypto->gen_rnd(m_nSeed, AES_BYTES);//seed; + m_lRcvLock = new CLock; + +#ifdef FIXED_KEY_AES_HASHING + m_kCRFKey = (AES_KEY_CTX*) malloc(sizeof(AES_KEY_CTX)); + m_cCrypto->init_aes_key(m_kCRFKey, XXLT.symbits, (uint8_t*) fixedkeyseed); + //m_kCRFKey = (AES_KEY_CTX*) malloc(sizeof(AES_KEY_CTX)); + //MPC_AES_KEY_INIT(m_kCRFKey); + //MPC_AES_KEY_EXPAND(m_kCRFKey, fixedkeyseed); +#endif + } + + ~OTExtRec(){free(m_vKeySeedMtx); }; + + bool receive(uint32_t numOTs, uint32_t bitlength, CBitVector& choices, CBitVector& ret, uint8_t type, + uint32_t numThreads, MaskingFunction* maskfct); + + bool receive(uint32_t numThreads); + bool OTReceiverRoutine(uint32_t id, uint32_t myNumOTs); + //void ReceiveAndProcess(CBitVector& vRcv, CBitVector& seedbuf, uint32_t id, uint32_t ctr, uint32_t lim); + void ReceiveAndProcess(uint32_t numThreads); + void BuildMatrices(CBitVector& T, CBitVector& SndBuf, uint32_t numblocks, uint32_t ctr, uint8_t* ctr_buf); + void HashValues(CBitVector& T, CBitVector& seedbuf, uint32_t ctr, uint32_t lim); + bool verifyOT(uint32_t myNumOTs); + + protected: + uint8_t m_eOTFlav; + uint32_t m_nSndVals; + uint32_t m_nOTs; + uint32_t m_nBitLength; + uint32_t m_nCounter; + uint32_t m_nSymSecParam; + CSocket* m_nSockets; + CBitVector m_nChoices; + CBitVector m_nRet; + CBitVector m_vTempOTMasks; + uint8_t* m_nSeed; + MaskingFunction* m_fMaskFct; + AES_KEY_CTX* m_vKeySeedMtx; + crypto* m_cCrypto; + CLock* m_lRcvLock; +#ifdef FIXED_KEY_AES_HASHING + AES_KEY_CTX* m_kCRFKey; +#endif + + + class OTReceiverThread : public CThread { + public: + OTReceiverThread(uint32_t id, uint32_t nOTs, OTExtRec* ext) {receiverID = id; numOTs = nOTs; callback = ext; success = false;}; + ~OTReceiverThread(){}; + void ThreadMain() {success = callback->OTReceiverRoutine(receiverID, numOTs);}; + private: + uint32_t receiverID; + uint32_t numOTs; + OTExtRec* callback; + bool success; + }; + +}; + +#ifdef FIXED_KEY_AES_HASHING +inline void FixedKeyHashing(AES_KEY_CTX* aeskey, uint8_t* outbuf, uint8_t* inbuf, uint8_t* tmpbuf, uint32_t id, uint32_t bytessecparam) { + memset(tmpbuf, 0, AES_BYTES); + memcpy(tmpbuf, (uint8_t*) (&id), sizeof(uint32_t)); + for(uint32_t i = 0; i < bytessecparam; i++) { + tmpbuf[i] = tmpbuf[i] ^ inbuf[i]; + } + + MPC_AES_ENCRYPT(aeskey, outbuf, tmpbuf); + + for(uint32_t i = 0; i < bytessecparam; i++) { + outbuf[i] = outbuf[i] ^ inbuf[i];//todo: optimize + } +} +#endif + +#endif /* __OT_EXTENSION_H_ */ diff --git a/src/util/ot/xormasking.h b/src/util/ot/xormasking.h new file mode 100644 index 0000000..b9bd771 --- /dev/null +++ b/src/util/ot/xormasking.h @@ -0,0 +1,155 @@ +/* + * XORMasking.h + * + * Created on: May 13, 2013 + * Author: mzohner + */ + +#ifndef XORMASKING_H_ +#define XORMASKING_H_ + +#include "maskingfunction.h" + +class XORMasking : public MaskingFunction +{ +public: + XORMasking(int bitlength, crypto* crypt){init(bitlength, crypt); }; + XORMasking(int bitlength, crypto* crypt, CBitVector& delta) { m_vDelta = δ init(bitlength, crypt);}; + ~XORMasking(){}; + + + void init(int bitlength, crypto* crypt) + { + m_nBitLength = bitlength; + m_cCrypto = crypt; + } + + void Mask(uint32_t progress, uint32_t processedOTs, CBitVector* values, CBitVector* snd_buf, uint8_t protocol) + { + uint32_t nsndvals = 2; + + if(protocol == G_OT) + { + snd_buf[0].XORBytes(values[0].GetArr() + ceil_divide(progress * m_nBitLength, 8), (uint64_t) 0, ceil_divide(((uint64_t) processedOTs) * m_nBitLength, 8)); + snd_buf[1].XORBytes(values[1].GetArr() + ceil_divide(progress * m_nBitLength, 8), (uint64_t) 0, ceil_divide(((uint64_t) processedOTs) * m_nBitLength, 8)); + } + else if(protocol == C_OT) + { + values[0].SetBytes(snd_buf[0].GetArr(), ceil_divide(progress * m_nBitLength, 8), ceil_divide(processedOTs * m_nBitLength, 8));//.SetBits(hash_buf, i*m_nBitLength, m_nBitLength); + uint64_t bitPos = ((uint64_t) progress) * m_nBitLength; + uint64_t length = ((uint64_t) processedOTs) * m_nBitLength; + uint64_t bytePos = ceil_divide(bitPos, 8); + + //cout << "Performing masking for " << bytePos << " and " << bitPos << " to " << length << "(" << m_nBitLength << ", " << processedOTs << ")"<< endl; + values[1].SetBits(values[0].GetArr() + bytePos, bitPos, length); + values[1].XORBits(m_vDelta->GetArr() + bytePos, bitPos, length); + snd_buf[1].XORBits(values[1].GetArr() + bytePos, (uint64_t) 0, length); + } + else if(protocol == R_OT) + { + values[0].SetBytes(snd_buf[0].GetArr(), ceil_divide(progress * m_nBitLength, 8), ceil_divide(processedOTs * m_nBitLength, 8)); + values[1].SetBytes(snd_buf[1].GetArr(), ceil_divide(progress * m_nBitLength, 8), ceil_divide(processedOTs * m_nBitLength, 8)); + } + /*int bitPos = progress * m_nBitLength; + int length = processedOTs * m_nBitLength; + int bytePos = CEIL_DIVIDE(bitPos, 8); + + //cout << "Performing masking for " << bytePos << " and " << bitPos << " to " << length << "(" << m_nBitLength << ", " << processedOTs << ")"<< endl; + values[1].SetBits(values[0].GetArr() + bytePos, bitPos, length); + values[1].XORBits(m_vDelta->GetArr() + bytePos, bitPos, length); + + snd_buf.XORBits(values[1].GetArr() + bytePos, 0, length);*/ + }; + + //output already has to contain the masks + void UnMask(uint32_t progress, uint32_t processedOTs, CBitVector& choices, CBitVector& output, CBitVector& rcv_buf, CBitVector& tmpmask, uint8_t protocol) + { + uint32_t bytelen = ceil_divide(m_nBitLength, 8); + uint32_t gprogress = progress * bytelen; + //int gprogress = progress * m_nBitLength; + uint32_t lim = progress + processedOTs; + + if(protocol == G_OT) + { + for(uint32_t u, i= progress, offset = processedOTs * bytelen, l = 0; i < lim; i++, gprogress+=bytelen, l+=bytelen) + { + //TODO make this working for single bits + u = (int) choices.GetBitNoMask(i); + output.SetXOR(rcv_buf.GetArr() + (u * offset) + l, tmpmask.GetArr() + gprogress, gprogress, bytelen); + //output.SetBit(gprogress, tmpmask.GetBit(l)); + //output.XORBit(gprogress, rcv_buf.GetBit(u * offset + l)); + } + + } + else if (protocol == C_OT || protocol == S_OT) + { + int gprogress = progress * bytelen; + output.Copy(tmpmask.GetArr() + gprogress, gprogress, bytelen * processedOTs); + for(uint32_t i = progress, l = 0; i < lim; i++, l+=bytelen, gprogress+=bytelen) + { + if(choices.GetBitNoMask(i)) + { + //TODO make this working for single bits + output.XORBytes(rcv_buf.GetArr() + l, gprogress, (int) bytelen); + //output.XORBitsPosOffset(rcv_buf.GetArr(), l, progress*m_nBitLength, m_nBitLength); + } + } + } + else if(protocol == R_OT) + { + //The seed expansion has already been performed, so do nothing + } + }; + + + void expandMask(CBitVector& out, uint8_t* sbp, uint32_t offset, uint32_t processedOTs, uint32_t bitlength) + { + + if(bitlength <= m_cCrypto->get_aes_key_bytes()) + { + for(uint32_t i = 0; i< processedOTs; i++, sbp+=m_cCrypto->get_aes_key_bytes()) + { + // cout << "Setting bits from " << (offset + i) * bitlength << " with " << bitlength << " len " << endl; + //cout << "Byte: " << ((unsigned int) sbp[0]) << ", bitlenh = " << bitlength << ", pos = " << (offset + i) * bitlength << ", "; + + out.SetBits(sbp, (offset + i) * bitlength, bitlength); + //out.PrintBinary(); + + } + //cout << "Out = "<< endl; + //out.PrintHex(); + } + else + { + uint8_t m_bBuf[AES_BYTES]; + uint8_t ctr_buf[AES_BYTES] = {0}; + uint32_t counter = *((uint32_t*) ctr_buf); + AES_KEY_CTX tkey; + //MPC_AES_KEY_INIT(&tkey); + for(uint32_t i = 0, rem; i< processedOTs; i++, sbp+=m_cCrypto->get_aes_key_bytes()) + { + //MPC_AES_KEY_EXPAND(&tkey, sbp); + m_cCrypto->init_aes_key(&tkey, sbp); + for(counter = 0; counter < bitlength/AES_BITS; counter++) + { + m_cCrypto->encrypt(&tkey, m_bBuf, ctr_buf, AES_BYTES);//MPC_AES_ENCRYPT(&tkey, m_bBuf, ctr_buf); + out.SetBits(m_bBuf, (offset+ i) * bitlength + (counter*AES_BITS), AES_BITS); + } + //the final bits + //cout << "bits: " << (counter*AES_BITS) << ", bitlength: " << m_nBitLength << endl; + if((rem = bitlength - (counter*AES_BITS)) > 0) + { + m_cCrypto->encrypt(&tkey, m_bBuf, ctr_buf, AES_BYTES);//MPC_AES_ENCRYPT(&tkey, m_bBuf, ctr_buf); + out.SetBits(m_bBuf, (offset + i) * bitlength + (counter*AES_BITS), rem); + } + } + } + } + +private: + CBitVector* m_vDelta; + int m_nBitLength; + crypto* m_cCrypto; +}; + +#endif /* XORMASKING_H_ */ diff --git a/src/util/ot/xorrgbfmasking.h b/src/util/ot/xorrgbfmasking.h new file mode 100644 index 0000000..460cbcf --- /dev/null +++ b/src/util/ot/xorrgbfmasking.h @@ -0,0 +1,97 @@ +/* + * XORBFMasking.h + * + * Created on: October 22, 2013 + * Author: mzohner + */ + + +#ifndef XORRGBFMASKING_H_ +#define XORRGBFMASKING_H_ + +#include "maskingfunction.h" + +#define RGBF_SERVER 0x00 +#define RGBF_CLIENT 0x01 + +inline bool GetBitRGBF(BYTE* data, int idx){ + // return (filter->data[(idx+filter->leadingZeroes) >> 3] & (1 << (7-((idx+filter->leadingZeroes) & 7))))==0?0:1; + return !!(data[idx >> 3] & (1 << (7-(idx & 7)))); +}; + +//A masking function for the random garbled Bloom filter protocol +class XORRGBFMasking : public MaskingFunction +{ +public: + XORRGBFMasking(int bitlength, uint8_t id, uint8_t* choices, int leadingZeros) + {init(bitlength); m_bID = id; m_vChoices.AttachBuf(choices, bitlength); m_nLeadingZeros = leadingZeros;}; + ~XORRGBFMasking(){}; + + + void init(int bitlength) + { + if(!(bitlength>>3)) + { + cerr << "BitLength must be a multiple of 8!" << endl; + exit(0); + } + m_nByteLength = bitlength/8; + } + + + void Mask(int progress, int processedOTs, CBitVector* values, CBitVector* snd_buf, BYTE protocol) + { + //A hack write data into a fixed-size 2 dimensional out-bitvector for the GBF PSI protocl + BYTE** outptr = ((BYTE**) (values[0].GetArr()))+progress; + BYTE** limptr = outptr+processedOTs; + BYTE* srcptr = snd_buf[0].GetArr(); + BYTE* data = m_vChoices.GetArr(); + for(int i = progress+m_nLeadingZeros, j; outptr>3; + //for(int i = offset; i < offset+processedOTs; srcptr++, i++) + for(int i = offset+m_nLeadingZeros; outptr>3); + } + } + +private: + uint8_t** m_vDelta; + int m_nByteLength; + uint8_t m_bID; + CBitVector m_vChoices; + int m_nLeadingZeros; +}; + +#endif /* XORRGBFMASKING_H_ */ diff --git a/src/util/socket.h b/src/util/socket.h new file mode 100644 index 0000000..cfcc907 --- /dev/null +++ b/src/util/socket.h @@ -0,0 +1,274 @@ +//socket.h + +#ifndef __SOCKET_H__BY_SGCHOI +#define __SOCKET_H__BY_SGCHOI + +#include "typedefs.h" + +#define TRACK_COMMUNICATION + +class CSocket +{ +public: + CSocket(){ + m_hSock = INVALID_SOCKET; +#ifdef TRACK_COMMUNICATION + bytes_sent = 0; + bytes_received = 0; +#endif + } + ~CSocket(){ } + //~CSocket(){ cout << "Closing Socket!" << endl; Close(); } + +#ifdef TRACK_COMMUNICATION + uint64_t get_bytes_sent() { return bytes_sent; }; + uint64_t get_bytes_received() { return bytes_received; }; +#endif + +public: + bool Socket() + { + bool success = false; + + #ifdef WIN32 + static bool s_bInit = FALSE; + + if (!s_bInit) { + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 0); + WSAStartup(wVersionRequested, &wsaData); + s_bInit = TRUE; + } + #endif + + Close(); + + success = (m_hSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != INVALID_SOCKET; + + return success; + + } + + void Close() + { + if( m_hSock == INVALID_SOCKET ) return; + + #ifdef WIN32 + shutdown(m_hSock, SD_SEND); + closesocket(m_hSock); + #else + shutdown(m_hSock, SHUT_WR); + close(m_hSock); + #endif + + m_hSock = INVALID_SOCKET; + } + + void AttachFrom(CSocket& s) + { + m_hSock = s.m_hSock; + } + + void Detach() + { + m_hSock = INVALID_SOCKET; + } + +public: + string GetIP() + { + sockaddr_in addr; + uint32_t addr_len = sizeof(addr); + + if (getsockname(m_hSock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) return ""; + return inet_ntoa(addr.sin_addr); + } + + + uint16_t GetPort() + { + sockaddr_in addr; + uint32_t addr_len = sizeof(addr); + + if (getsockname(m_hSock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) return 0; + return ntohs(addr.sin_port); + } + + bool Bind(uint16_t nPort=0, const char* ip = "") + { + // Bind the socket to its port + sockaddr_in sockAddr; + memset(&sockAddr,0,sizeof(sockAddr)); + sockAddr.sin_family = AF_INET; + + if( strcmp(ip, "") ) + { + int on = 1; + setsockopt(m_hSock, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)); + + sockAddr.sin_addr.s_addr = inet_addr(ip); + + if (sockAddr.sin_addr.s_addr == INADDR_NONE) + { + hostent* phost; + phost = gethostbyname(ip); + if (phost != NULL) + sockAddr.sin_addr.s_addr = ((in_addr*)phost->h_addr)->s_addr; + else + return false; + } + } + else + { + sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); + } + + sockAddr.sin_port = htons(nPort); + + return bind(m_hSock, (sockaddr *) &sockAddr, sizeof(sockaddr_in)) >= 0; + } + + bool Listen(int nQLen = 5) + { + return listen(m_hSock, nQLen) >= 0; + } + + bool Accept(CSocket& sock) + { + sock.m_hSock = accept(m_hSock, NULL, 0); + if( sock.m_hSock == INVALID_SOCKET ) return false; + + return true; + } + + bool Connect(const char* ip, uint16_t port, int64_t lTOSMilisec = -1) + { + //cout << "Socket " << m_hSock << " connected" << endl; + sockaddr_in sockAddr; + memset(&sockAddr,0,sizeof(sockAddr)); + sockAddr.sin_family = AF_INET; + sockAddr.sin_addr.s_addr = inet_addr(ip); + + if (sockAddr.sin_addr.s_addr == INADDR_NONE) + { + hostent* lphost; + lphost = gethostbyname(ip); + if (lphost != NULL) + sockAddr.sin_addr.s_addr = ((in_addr*)lphost->h_addr)->s_addr; + else + return false; + } + + sockAddr.sin_port = htons(port); + +#ifdef WIN32 + + DWORD dw = 100000; + + if( lTOSMilisec > 0 ) + { + setsockopt(m_hSock, SOL_SOCKET, SO_RCVTIMEO, (char*) &lTOSMilisec, sizeof(lTOSMilisec)); + } + + int ret = connect(m_hSock, (sockaddr*)&sockAddr, sizeof(sockAddr)); + + if( ret >= 0 && lTOSMilisec > 0 ) + setsockopt(m_hSock, SOL_SOCKET, SO_RCVTIMEO, (char*) &dw, sizeof(dw)); + +#else + + timeval tv; + + if( lTOSMilisec > 0 ) + { + tv.tv_sec = lTOSMilisec/1000; + tv.tv_usec = (lTOSMilisec%1000)*1000; + + setsockopt(m_hSock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + + int ret = connect(m_hSock, (sockaddr*)&sockAddr, sizeof(sockAddr)); + + if( ret >= 0 && lTOSMilisec > 0 ) + { + tv.tv_sec = 100000; + tv.tv_usec = 0; + + setsockopt(m_hSock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + +#endif + return ret >= 0; + } + + int Receive(void* pBuf, int nLen, int nFlags = 0) + { + //cout << "Socket " << m_hSock << " (" << (unsigned long long) this << ") receiving " << nLen << " bytes" << endl; + char* p = (char*) pBuf; + int n = nLen; + int ret = 0; + while( n > 0 ) + { + ret = recv(m_hSock, p, n, 0); +#ifdef WIN32 + if( ret <= 0 ) + { + return ret; + } +#else + if( ret < 0 ) + { + if( errno == EAGAIN ) + { + cerr << "socket recv eror: EAGAIN" << endl; + SleepMiliSec(200); + continue; + } + else + { + cerr << "socket recv error: " << errno << endl; + perror("Socket error "); + return ret; + } + } + else if (ret == 0) + { + return ret; + } +#endif + + p += ret; + n -= ret; + } + +#ifdef TRACK_COMMUNICATION + bytes_received += ((uint64_t) nLen); + //cout << "bytes_received = " << bytes_received << endl; +#endif + return nLen; + } + + int Send(const void* pBuf, int nLen, int nFlags = 0) + { + //cout << "Socket " << m_hSock << " (" << (unsigned long long) this << ") sending " << nLen << " bytes" << endl; +#ifdef TRACK_COMMUNICATION + bytes_sent+= ((uint64_t) nLen); + //cout << "bytes_sent = " << bytes_sent << endl; +#endif + return send(m_hSock, (char*)pBuf, nLen, nFlags); + } + +private: + SOCKET m_hSock; +#ifdef TRACK_COMMUNICATION + uint64_t bytes_sent; + uint64_t bytes_received; +#endif + +}; + + +#endif //SOCKET_H__BY_SGCHOI + diff --git a/src/util/thread.h b/src/util/thread.h new file mode 100644 index 0000000..3aedb26 --- /dev/null +++ b/src/util/thread.h @@ -0,0 +1,255 @@ +// thread.h by sgchoi@cs.umd.edu + +#ifndef __THREAD_H__BY_SGCHOI +#define __THREAD_H__BY_SGCHOI + +#include "typedefs.h" + +#ifdef WIN32 + +#include + +class CEvent +{ +// Constructor +public: + CEvent(bool bManualReset = FALSE, bool bInitialSet = FALSE) + { + m_hHandle = ::CreateEvent(0, bManualReset, bInitialSet, 0); + } + + ~CEvent() + { + CloseHandle(m_hHandle); + } + +// Operations +public: + bool Set(){ return SetEvent(m_hHandle); } + bool Reset(){ return ResetEvent(m_hHandle); } + bool Wait(){ return WaitForSingleObject(m_hHandle, INFINITE) == WAIT_OBJECT_0; } + +private: + HANDLE m_hHandle; +}; + + +///////////////////////////////////////////////////////////////////////////// +// CLock + + +// Operations +class CLock +{ +// Constructor +public: + CLock(){ InitializeCriticalSection(&m_cs); } + ~CLock(){ DeleteCriticalSection(&m_cs); } + +public: + void Lock(){ EnterCriticalSection(&m_cs); } + void Unlock(){ LeaveCriticalSection(&m_cs); } + +private: + CRITICAL_SECTION m_cs; +}; + +class CThread +{ +public: + CThread(){m_bRunning = FALSE; m_hHandle = NULL;} + virtual ~CThread(){ if(m_hHandle != NULL) CloseHandle(m_hHandle);} + +public: + bool Start() + { + m_bRunning = TRUE; + m_hHandle = CreateThread(0, 0, ThreadMainHandler, this,0,0); + if( m_hHandle == NULL ) + m_bRunning = FALSE; + + return m_bRunning; + } + + bool Wait() + { + if( !m_bRunning ) return TRUE; + return WaitForSingleObject(m_hHandle, INFINITE) == WAIT_OBJECT_0; + } + + bool Kill() + { + if( !m_bRunning) return TRUE; + + m_bRunning = !(TerminateThread(m_hHandle, 0)); + return !m_bRunning; + } + + bool IsRunning() + { + return m_bRunning; + } + + +protected: + virtual void ThreadMain() = 0; + + static DWORD __stdcall ThreadMainHandler( void* p ) + { + CThread* pThis = (CThread*) p; + pThis->ThreadMain(); + pThis->m_bRunning = FALSE; + return 0; + } + +protected: + bool m_bRunning; + HANDLE m_hHandle; +}; + + +#else // NOT WIN32 +#include +class CThread +{ +public: + CThread(){m_bRunning = false; } + virtual ~CThread(){} + +public: + bool Start() + { + m_bRunning = !pthread_create(&m_pThread, NULL, + ThreadMainHandler, (void*) this); + return m_bRunning; + } + + bool Wait() + { + if( !m_bRunning ) return true; + return pthread_join(m_pThread, NULL)==0; + } + + bool Kill() + { + if( !m_bRunning) return true; + pthread_exit(NULL); + return true; + } + + bool IsRunning() + { + return m_bRunning; + } + +protected: + virtual void ThreadMain() = 0; + static void* ThreadMainHandler( void* p ) + { + CThread* pThis = (CThread*) p; + pThis->ThreadMain(); + pThis->m_bRunning = false; + return 0; + } + +protected: + bool m_bRunning; + pthread_t m_pThread; +}; + + +class CLock +{ +// Constructor +public: + CLock(){ pthread_mutex_init(&m_mtx, NULL); } + ~CLock(){ pthread_mutex_destroy(&m_mtx); } + +public: + void Lock(){ pthread_mutex_lock(&m_mtx);} + void Unlock(){ pthread_mutex_unlock(&m_mtx); } + +private: + pthread_mutex_t m_mtx; +}; + + + +class CEvent +{ +// Constructor +public: + CEvent(bool bManualReset = false, bool bInitialSet = false) + { + pthread_mutex_init(&m_mtx, NULL); + pthread_cond_init(&m_cnd, NULL); + m_bManual = bManualReset; + m_bSet = bInitialSet; + } + + ~CEvent() + { + pthread_mutex_destroy(&m_mtx); + pthread_cond_destroy(&m_cnd); + } + +// Operations +public: + bool Set() + { + pthread_mutex_lock(&m_mtx); + if(!m_bSet) + { + m_bSet = true; + + pthread_cond_signal(&m_cnd); + } + + pthread_mutex_unlock(&m_mtx); + return true; + } + + bool Wait() + { + pthread_mutex_lock( &m_mtx ); + + while(!m_bSet) + { + pthread_cond_wait( &m_cnd, &m_mtx ); + } + + if ( !m_bManual ) m_bSet = false; + pthread_mutex_unlock( &m_mtx ); + return true; + } + + bool Reset() + { + pthread_mutex_lock( &m_mtx ); + m_bSet = false; + pthread_mutex_unlock( &m_mtx ); + return true; + } +private: + pthread_cond_t m_cnd; + pthread_mutex_t m_mtx; + bool m_bManual; + bool m_bSet; + }; + +#endif //WIN32 + + + +class CGrabLock +{ +public: + CGrabLock(CLock& l):lock(l){ lock.Lock(); } + ~CGrabLock(){ lock.Unlock(); } +public: + CLock& lock; +}; + +#endif //__THREAD_H__BY_SGCHOI + + diff --git a/src/util/typedefs.h b/src/util/typedefs.h new file mode 100644 index 0000000..3d54c6d --- /dev/null +++ b/src/util/typedefs.h @@ -0,0 +1,136 @@ +/* + * typedefs.h + * + * Created on: Jul 1, 2014 + * Author: mzohner + */ + +#ifndef TYPEDEFS_H_ +#define TYPEDEFS_H_ + +//#define DEBUG +#define BATCH +//#define TIMING + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; + + +enum field_type {P_FIELD, ECC_FIELD}; +enum role_type {SERVER, CLIENT}; + +#define MAX_REPLY_BITS 65536 //at most 2^16 bits may be sent in one go +#define ELEMENT_WINDOW 1024 +#define MAX_REPLY_BYTES MAX_REPLY_BITS/8 + +#define RETRY_CONNECT 1000 +#define CONNECT_TIMEO_MILISEC 10000 + + +#define OTEXT_BLOCK_SIZE_BITS 128 +#define OTEXT_BLOCK_SIZE_BYTES 16 + +#define VECTOR_INTERNAL_SIZE 8 + +#define MAX_INT (~0) +#if (MAX_INT == 0xFFFFFFFF) +#define MACHINE_SIZE_32 +typedef uint32_t REGISTER_SIZE; + +#elif (MAX_INT == 0xFFFFFFFFFFFFFFFF) +#define MACHINE_SIZE_64 +typedef unsigned long int REGISTER_SIZE; + +#else +#define MACHINE_SIZE_16 +typedef uint16_t REGISTER_SIZE; + +#endif + +#define LOG2_REGISTER_SIZE ceil_log2(sizeof(REGISTER_SIZE) << 3) + +#ifdef WIN32 +#include +#include + +typedef unsigned short USHORT; +typedef int socklen_t; +#pragma comment(lib, "wsock32.lib") + +#define SleepMiliSec(x) Sleep(x) + +#else //WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int SOCKET; +#define INVALID_SOCKET -1 +typedef REGISTER_SIZE REGSIZE; + +#define SleepMiliSec(x) usleep((x)<<10) +#endif// WIN32 + +#define ceil_divide(x, y) ((x) > 0? ( ((x) - 1) / (y) )+1 : 0) +#define pad_to_multiple(x, y) (ceil_divide(x, y) * (y)) + +typedef struct securitylevel +{ + uint32_t statbits; + uint32_t symbits; + uint32_t ifcbits; + uint32_t eccpfbits; + uint32_t ecckcbits; +} seclvl; + + +static const seclvl ST = {40, 80, 1024, 160, 163}; +static const seclvl MT = {40, 112, 2048, 192, 233}; +static const seclvl LT = {40, 128, 3072, 256, 283}; +static const seclvl XLT = {40, 192, 7680, 384, 409}; +static const seclvl XXLT = {40, 256, 15360, 512, 571}; + +static int ceil_log2(int bits) { + if(bits == 1) return 1; + int targetlevel = 0, bitstemp = bits; + while (bitstemp >>= 1) ++targetlevel; + return targetlevel + ((1<>= 1) ++targetlevel; + return targetlevel; +} + + +// Timing routines +static double getMillies(timeval timestart, timeval timeend) +{ + long time1 = (timestart.tv_sec * 1000000) + (timestart.tv_usec ); + long time2 = (timeend.tv_sec * 1000000) + (timeend.tv_usec ); + + return (double)(time2-time1)/1000; +} + +#endif /* TYPEDEFS_H_ */