diff --git a/Compiler/instructions.py b/Compiler/instructions.py index 5d6bf5fc..058b6ff4 100644 --- a/Compiler/instructions.py +++ b/Compiler/instructions.py @@ -2501,6 +2501,26 @@ class delshuffle(base.Instruction): code = base.opcodes['DELSHUFFLE'] arg_format = ['ci'] +class inverse_permutation(base.VectorInstruction, shuffle_base): + """ Calculate the inverse permutation of a secret permutation. + + :param: destination (sint) + :param: source (sint) + + """ + __slots__ = [] + code = base.opcodes['INVPERM'] + arg_format = ['sw', 's'] + + def __init__(self, *args, **kwargs): + super(inverse_permutation, self).__init__(*args, **kwargs) + assert len(args[0]) == len(args[1]) + + def add_usage(self, req_node): + self.add_gen_usage(req_node, len(self.args[0])) + self.add_apply_usage(req_node, len(self.args[0]), 1) + + class check(base.Instruction): """ Force MAC check in current thread and all idle thread if current diff --git a/Compiler/instructions_base.py b/Compiler/instructions_base.py index 3a56e604..f7aa48f9 100644 --- a/Compiler/instructions_base.py +++ b/Compiler/instructions_base.py @@ -111,6 +111,7 @@ opcodes = dict( GENSECSHUFFLE = 0xFB, APPLYSHUFFLE = 0xFC, DELSHUFFLE = 0xFD, + INVPERM = 0xFE, # Data access TRIPLE = 0x50, BIT = 0x51, diff --git a/Compiler/types.py b/Compiler/types.py index 75e1f58f..a69e5e52 100644 --- a/Compiler/types.py +++ b/Compiler/types.py @@ -2776,6 +2776,11 @@ class sint(_secret, _int): applyshuffle(res, self, unit_size, shuffle, reverse) return res + def inverse_permutation(self): + res = sint(size=self.size) + inverse_permutation(res, self) + return res + class sintbit(sint): """ :py:class:`sint` holding a bit, supporting binary operations (``&, |, ^``). """ diff --git a/Processor/Instruction.h b/Processor/Instruction.h index f3caf565..1de58c99 100644 --- a/Processor/Instruction.h +++ b/Processor/Instruction.h @@ -113,6 +113,7 @@ enum GENSECSHUFFLE = 0xFB, APPLYSHUFFLE = 0xFC, DELSHUFFLE = 0xFD, + INVPERM = 0xFE, // Data access TRIPLE = 0x50, BIT = 0x51, diff --git a/Processor/Instruction.hpp b/Processor/Instruction.hpp index 1d7c883d..7763c837 100644 --- a/Processor/Instruction.hpp +++ b/Processor/Instruction.hpp @@ -283,6 +283,10 @@ void BaseInstruction::parse_operands(istream& s, int pos, int file_pos) n = get_int(s); get_vector(2, start, s); break; + // instructions with 2 register operands + case INVPERM: + get_vector(2, start, s); + break; // open instructions + read/write instructions with variable length args case OPEN: case GOPEN: @@ -1076,6 +1080,9 @@ inline void Instruction::execute(Processor& Proc) const case DELSHUFFLE: Proc.Procp.delete_shuffle(Proc.read_Ci(r[0])); return; + case INVPERM: + Proc.Procp.inverse_permutation(*this); + return; case CHECK: { CheckJob job; diff --git a/Processor/Processor.h b/Processor/Processor.h index 927e9327..e29f6eb4 100644 --- a/Processor/Processor.h +++ b/Processor/Processor.h @@ -77,6 +77,7 @@ public: size_t generate_secure_shuffle(const Instruction& instruction); void apply_shuffle(const Instruction& instruction, int handle); void delete_shuffle(int handle); + void inverse_permutation(const Instruction& instruction); void input_personal(const vector& args); void send_personal(const vector& args); @@ -101,6 +102,8 @@ public: { return C[i]; } + + void inverse_permutation(const Instruction &instruction, int handle); }; class ArithmeticProcessor : public ProcessorBase diff --git a/Processor/Processor.hpp b/Processor/Processor.hpp index 861e8cfe..e80df0d0 100644 --- a/Processor/Processor.hpp +++ b/Processor/Processor.hpp @@ -668,6 +668,12 @@ void SubProcessor::delete_shuffle(int handle) shuffler.del(handle); } +template +void SubProcessor::inverse_permutation(const Instruction& instruction) { + shuffler.inverse_permutation(S, instruction.get_size(), instruction.get_start()[0], + instruction.get_start()[1]); +} + template void SubProcessor::input_personal(const vector& args) { @@ -686,6 +692,16 @@ void SubProcessor::input_personal(const vector& args) S[args[i + 2] + j] = input.finalize(args[i + 1]); } +/** + * + * @tparam T + * @param args Args contains four arguments + * a[0] = the size of the input (and output) vector + * a[1] = the player to which to reveal the output + * a[2] = the memory address of the input vector (sint) (i.e. the value to reveal) + * a[3] = the memory address of the output vector (cint) (i.e. the register to store the revealed value) + * // TODO: When would there be multiple sets of arguments? (for ... i < args.size(); i += 4 ... ) + */ template void SubProcessor::private_output(const vector& args) { diff --git a/Protocols/SecureShuffle.h b/Protocols/SecureShuffle.h index a90c6e64..c1c265ea 100644 --- a/Protocols/SecureShuffle.h +++ b/Protocols/SecureShuffle.h @@ -24,8 +24,28 @@ class SecureShuffle size_t n_shuffle; bool exact; + /** + * Generates and returns a newly generated random permutation. This permutation is generated locally. + * + * @param n The size of the permutation to generate. + * @return A vector representing a permutation, a shuffled array of integers 0 through n-1. + */ + vector generate_random_permutation(int n); + + /** + * Configure a shared waksman network from a permutation known only to config_player. + * Note that although the configuration bits of the waksman network are secret shared, + * the player that generated the permutation (config_player) knows the value of these bits. + * + * A permutation is a mapping represented as a vector. + * Each item in the vector represents the output of mapping(i) where i is the index of that item. + * e.g. [2, 4, 0, 3, 1] -> perm(1) = 4 + * + * @param config_player The player tasked with generating the random permutation from which to configure the waksman network. + * @param n_shuffle The size of the permutation to generate. + */ + void configure(int config_player, vector* perm, int n); void player_round(int config_player); - void generate(int config_player, int n_shuffle); void waksman(vector& a, int depth, int start); void cond_swap(T& x, T& y, const T& b); @@ -44,9 +64,37 @@ public: int generate(int n_shuffle); + /** + * + * @param a The vector of registers representing the stack // TODO: Is this correct? + * @param n The size of the input vector to shuffle + * @param unit_size Determines how many vector items constitute a single block with regards to permutation: + * i.e. input vector [1,2,3,4] with unit_size=2 under permutation map [1,0] + * would result in [3,4,1,2] + * @param output_base The starting address of the output vector (i.e. the location to write the inverted permutation to) + * @param input_base The starting address of the input vector (i.e. the location from which to read the permutation) + * @param handle The integer identifying the preconfigured waksman network (shuffle) to use. Such a handle can be obtained from calling + * @param reverse Boolean indicating whether to apply the inverse of the permutation + * @see SecureShuffle::generate for obtaining a shuffle handle + */ void apply(vector& a, size_t n, int unit_size, size_t output_base, size_t input_base, int handle, bool reverse); + /** + * Calculate the secret inverse permutation of stack given secret permutation. + * + * This method is given in [1], based on stack technique in [2]. It is used in the Compiler (high-level) implementation of Square-Root ORAM. + * + * [1] Samee Zahur, Xiao Wang, Mariana Raykova, Adrià Gascón, Jack Doerner, David Evans, and Jonathan Katz. 2016. Revisiting Square Root ORAM: Efficient Random Access in Multi-Party Computation. In IEEE S&P. + * [2] Ivan Damgård, Matthias Fitzi, Eike Kiltz, Jesper Buus Nielsen, and Tomas Toft. Unconditionally Secure Constant-rounds Multi-Party Computation for Equality, Comparison, Bits and Exponentiation. In Theory of Cryptography, 2006. + * + * @param stack The vector or registers representing the stack (?) + * @param n The size of the input vector for which to calculate the inverse permutation + * @param output_base The starting address of the output vector (i.e. the location to write the inverted permutation to) + * @param input_base The starting address of the input vector (i.e. the location from which to read the permutation) + */ + void inverse_permutation(vector& stack, size_t n, size_t output_base, size_t input_base); + void del(int handle); }; diff --git a/Protocols/SecureShuffle.hpp b/Protocols/SecureShuffle.hpp index d2b0676a..5d713066 100644 --- a/Protocols/SecureShuffle.hpp +++ b/Protocols/SecureShuffle.hpp @@ -58,6 +58,82 @@ void SecureShuffle::apply(vector& a, size_t n, int unit_size, size_t outpu post(a, n, output_base); } + +template +void SecureShuffle::inverse_permutation(vector &stack, size_t n, size_t output_base, + size_t input_base) { + int alice = 0; + int bob = 1; + + auto &P = proc.P; + auto &input = proc.input; + + // This method only supports two players + assert(proc.protocol.get_relevant_players().size() == 2); + // The current implementation assumes a semi-honest environment + assert(!T::malicious); + + // We are dealing directly with permutations, so the unit_size will always be 1. + this->unit_size = 1; + // We need to account for sizes which are not a power of 2 + size_t n_pow2 = (1u << int(ceil(log2(n)))); + + // Copy over the input registers + pre(stack, n, input_base); + // Alice generates stack local permutation and shares the waksman configuration bits secretly to Bob. + vector perm_alice(n_pow2); + if (P.my_num() == alice) + perm_alice = generate_random_permutation(n); + configure(alice, &perm_alice, n); + // Apply perm_alice to perm_alice to get perm_bob, + // stack permutation that we can reveal to Bob without Bob learning anything about perm_alice (since it is masked by perm_a) + iter_waksman(true); + // Store perm_bob at stack[output_base] + post(stack, n, output_base); + + // Reveal permutation perm_bob = perm_a * perm_alice + // Since this permutation is masked by perm_a, Bob learns nothing about perm + vector perm_bob(n_pow2); + typename T::PrivateOutput output(proc); + for (size_t i = 0; i < n; i++) + output.prepare_sending(stack[output_base + i], bob); + output.exchange(); + for (size_t i = 0; i < n_pow2; i++) { + // TODO: Is there a better way to convert a T::clear to int? + bigint val; + output.finalize(bob).to(val); + perm_bob[i] = (int) val.get_si(); + } + + vector perm_bob_inv(n_pow2); + if (P.my_num() == bob) { + for (int i = 0; i < (int) n; i++) + perm_bob_inv[perm_bob[i]] = i; + // Pad the permutation to n_pow2 + // Required when using waksman networks + for (int i = (int) n; i < (int) n_pow2; i++) + perm_bob_inv[i] = i; + } + + // Alice secret shares perm_a with bob + // perm_a is stored in the stack at output_base + input.reset_all(P); + if (P.my_num() == alice) { + for (int i = 0; i < (int) n; i++) + input.add_mine(perm_alice[i]); + } + input.exchange(); + for (int i = 0; i < (int) n; i++) + stack[output_base + i] = input.finalize(alice); + + // The two parties now jointly compute perm_a * perm_bob_inv to obtain perm_inv + pre(stack, n, output_base); + configure(bob, &perm_bob_inv, n); + iter_waksman(true); + // perm_inv is written back to stack[output_base] + post(stack, n, output_base); +} + template void SecureShuffle::del(int handle) { @@ -129,9 +205,27 @@ void SecureShuffle::post(vector& a, size_t n, size_t output_base) } template -void SecureShuffle::player_round(int config_player) -{ - generate(config_player, n_shuffle); +vector SecureShuffle::generate_random_permutation(int n) { + vector perm; + int n_pow2 = 1 << int(ceil(log2(n))); + int shuffle_size = n; + for (int j = 0; j < n_pow2; j++) + perm.push_back(j); + SeededPRNG G; + for (int i = 0; i < shuffle_size; i++) { + int j = G.get_uint(shuffle_size - i); + swap(perm[i], perm[i + j]); + } + + return perm; +} + +template +void SecureShuffle::player_round(int config_player) { + vector random_perm(n_shuffle); + if (proc.P.my_num() == config_player) + random_perm = generate_random_permutation(n_shuffle); + configure(config_player, &random_perm, n_shuffle); iter_waksman(); } @@ -142,9 +236,12 @@ int SecureShuffle::generate(int n_shuffle) shuffles.push_back({}); auto& shuffle = shuffles.back(); - for (auto i : proc.protocol.get_relevant_players()) - { - generate(i, n_shuffle); + for (auto i: proc.protocol.get_relevant_players()) { + vector perm; + if (proc.P.my_num() == i) + perm = generate_random_permutation(n_shuffle); + configure(i, &perm, n_shuffle); + shuffle.push_back(config); } @@ -152,39 +249,27 @@ int SecureShuffle::generate(int n_shuffle) } template -void SecureShuffle::generate(int config_player, int n) -{ - auto& P = proc.P; - auto& input = proc.input; +void SecureShuffle::configure(int config_player, vector *perm, int n) { + auto &P = proc.P; + auto &input = proc.input; input.reset_all(P); int n_pow2 = 1 << int(ceil(log2(n))); Waksman waksman(n_pow2); - if (P.my_num() == config_player) - { - vector perm; - int shuffle_size = n; - for (int j = 0; j < n_pow2; j++) - perm.push_back(j); - SeededPRNG G; - for (int i = 0; i < shuffle_size; i++) - { - int j = G.get_uint(shuffle_size - i); - swap(perm[i], perm[i + j]); - } - - auto config_bits = waksman.configure(perm); - for (size_t i = 0; i < config_bits.size(); i++) - { - auto& x = config_bits[i]; + // The player specified by config_player configures the shared waksman network + // using its personal permutation + if (P.my_num() == config_player) { + auto config_bits = waksman.configure(*perm); + for (size_t i = 0; i < config_bits.size(); i++) { + auto &x = config_bits[i]; for (size_t j = 0; j < x.size(); j++) if (waksman.matters(i, j)) input.add_mine(int(x[j])); else assert(x[j] == 0); } - } - else + // The other player waits for its share of the configured waksman network + } else for (size_t i = 0; i < waksman.n_bits(); i++) input.add_other(config_player);