mirror of
https://github.com/data61/MP-SPDZ.git
synced 2026-01-09 13:37:58 -05:00
Add the INVPERM instruction
The IVMPERM instruction takes in a secret shared vector representing a permutation, and returns the corresponding secret shared inverse permutation.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -111,6 +111,7 @@ opcodes = dict(
|
||||
GENSECSHUFFLE = 0xFB,
|
||||
APPLYSHUFFLE = 0xFC,
|
||||
DELSHUFFLE = 0xFD,
|
||||
INVPERM = 0xFE,
|
||||
# Data access
|
||||
TRIPLE = 0x50,
|
||||
BIT = 0x51,
|
||||
|
||||
@@ -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
|
||||
(``&, |, ^``). """
|
||||
|
||||
@@ -113,6 +113,7 @@ enum
|
||||
GENSECSHUFFLE = 0xFB,
|
||||
APPLYSHUFFLE = 0xFC,
|
||||
DELSHUFFLE = 0xFD,
|
||||
INVPERM = 0xFE,
|
||||
// Data access
|
||||
TRIPLE = 0x50,
|
||||
BIT = 0x51,
|
||||
|
||||
@@ -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<sint, sgf2n>& 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;
|
||||
|
||||
@@ -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<int>& args);
|
||||
void send_personal(const vector<int>& args);
|
||||
@@ -101,6 +102,8 @@ public:
|
||||
{
|
||||
return C[i];
|
||||
}
|
||||
|
||||
void inverse_permutation(const Instruction &instruction, int handle);
|
||||
};
|
||||
|
||||
class ArithmeticProcessor : public ProcessorBase
|
||||
|
||||
@@ -668,6 +668,12 @@ void SubProcessor<T>::delete_shuffle(int handle)
|
||||
shuffler.del(handle);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SubProcessor<T>::inverse_permutation(const Instruction& instruction) {
|
||||
shuffler.inverse_permutation(S, instruction.get_size(), instruction.get_start()[0],
|
||||
instruction.get_start()[1]);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SubProcessor<T>::input_personal(const vector<int>& args)
|
||||
{
|
||||
@@ -686,6 +692,16 @@ void SubProcessor<T>::input_personal(const vector<int>& 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<class T>
|
||||
void SubProcessor<T>::private_output(const vector<int>& args)
|
||||
{
|
||||
|
||||
@@ -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<int> 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<int>* perm, int n);
|
||||
void player_round(int config_player);
|
||||
void generate(int config_player, int n_shuffle);
|
||||
|
||||
void waksman(vector<T>& 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 <code>unit_size=2</code> 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<T>& 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<T>& stack, size_t n, size_t output_base, size_t input_base);
|
||||
|
||||
void del(int handle);
|
||||
};
|
||||
|
||||
|
||||
@@ -58,6 +58,82 @@ void SecureShuffle<T>::apply(vector<T>& a, size_t n, int unit_size, size_t outpu
|
||||
post(a, n, output_base);
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
void SecureShuffle<T>::inverse_permutation(vector<T> &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<int> 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<int> 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<int> 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<class T>
|
||||
void SecureShuffle<T>::del(int handle)
|
||||
{
|
||||
@@ -129,9 +205,27 @@ void SecureShuffle<T>::post(vector<T>& a, size_t n, size_t output_base)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SecureShuffle<T>::player_round(int config_player)
|
||||
{
|
||||
generate(config_player, n_shuffle);
|
||||
vector<int> SecureShuffle<T>::generate_random_permutation(int n) {
|
||||
vector<int> 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<class T>
|
||||
void SecureShuffle<T>::player_round(int config_player) {
|
||||
vector<int> 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<T>::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<int> 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<T>::generate(int n_shuffle)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void SecureShuffle<T>::generate(int config_player, int n)
|
||||
{
|
||||
auto& P = proc.P;
|
||||
auto& input = proc.input;
|
||||
void SecureShuffle<T>::configure(int config_player, vector<int> *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<int> 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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user