async def vector_bsgn_1(x): """Compute bsgn_1(a) for all elements a of x in parallel.""" stype = type(x[0]) n = len(x) await mpc.returnType(stype, n) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 3 * n, signed=True) # 3n random signs r = mpc._randoms(Zp, 3 * n) r = mpc.schur_prod(r, r) # 3n random squares modulo p x, s, r = await mpc.gather(x, s, r) y = [b + 2 * i for b in (2 * a + 1 for a in x) for i in (-1, 0, 1)] y.extend(s[:n]) s.extend(s[n:2 * n]) r.extend(s[-n:]) y = await mpc.schur_prod(y, s) y = await mpc.schur_prod(y, r) y = await mpc.output(y) h = [legendre_p(y[j]) for j in range(3 * n)] t = [s[j] * h[j] for j in range(3 * n)] z = [ h[3 * j] * h[3 * j + 1] * h[3 * j + 2] * y[3 * n + j] for j in range(n) ] q = (p + 1) >> 1 # q = 1/2 mod p return [ Zp((u.value + v.value + w.value - uvw.value) * q) for u, v, w, uvw in zip(*[iter(t)] * 3, z) ]
async def bsgn_1(a): """Compute binary sign of a securely. Binary sign of a (1 if a>=0 else -1) is obtained by securely computing (u+v+w - u*v*w)/2 with u=(2a-1 | p), v=(2a+1 | p), and w=(2a+3 | p). """ stype = type(a) await mpc.returnType(stype) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 3, signed=True) # 3 random signs r = mpc._randoms(Zp, 3) r = mpc.schur_prod(r, r) # 3 random squares modulo p a, s, r = await mpc.gather(a, s, r) y = [b + 2 * i for b in (2 * a + 1, ) for i in (-1, 0, 1)] y.append(s[0]) s.append(s[1]) r.append(s[2]) y = await mpc.schur_prod(y, s) y = await mpc.schur_prod(y, r) y = await mpc.output(y) h = [legendre_p(y[i]) for i in range(3)] u, v, w = [s[i] * h[i] for i in range(3)] uvw = h[0] * h[1] * h[2] * y[3] return (u + v + w - uvw) / 2
async def vector_bsgn_0(x): """Compute bsgn_0(a) for all elements a of x in parallel.""" stype = type(x[0]) n = len(x) await mpc.returnType(stype, n) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, n, signed=True) # n random signs r = mpc._randoms(Zp, n) r = mpc.schur_prod(r, r) # n random squares modulo p x, s, r = await mpc.gather(x, s, r) y = [2 * a + 1 for a in x] y = await mpc.schur_prod(y, s) y = await mpc.schur_prod(y, r) y = await mpc.output(y) return [s[j] * legendre_p(y[j]) for j in range(n)]
async def vector_bsgn_2(x): """Compute bsgn_2(a) for all elements a of x in parallel.""" stype = type(x[0]) n = len(x) await mpc.returnType(stype, n) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 6*n, signed=True) # 6n random signs r = mpc._randoms(Zp, 6*n) r = mpc.schur_prod(r, r) # 6n random squares modulo p x, s, r = await mpc.gather(x, s, r) y = [b + 2 * i for b in (2 * a + 1 for a in x) for i in (-2, -1, 0, 1, 2)] y = await mpc.schur_prod(y, s[:-n]) y.extend(s[-n:]) y = await mpc.schur_prod(y, r) y = await mpc.output(y) t = [sum(s[5*j + i] * legendre_p(y[5*j + i]) for i in range(5)) for j in range(n)] t = await mpc.output(await mpc.schur_prod(t, y[-n:])) return [c * legendre_p(d) for c, d in zip(s[-n:], t)]
async def bsgn_0(a): """Compute binary sign of a securely. Binary sign of a (1 if a>=0 else -1) is obtained by securely computing (2a+1 | p). Legendre symbols (a | p) for secret a are computed securely by evaluating (a s r^2 | p) in the clear for secret random sign s and secret random r modulo p, and outputting secret s * (a s r^2 | p). """ stype = type(a) await mpc.returnType(stype) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 1, signed=True) # random sign r = mpc._random(Zp) r = mpc.prod([r, r]) # random square modulo p a, s, r = await mpc.gather(a, s, r) b = await mpc.prod([2 * a + 1, s[0], r]) b = await mpc.output(b) return s[0] * legendre_p(b)
async def bsgn_2(a): """Compute binary sign of a securely. Binary sign of a (1 if a>=0 else -1) is obtained by securely computing (t | p), with t = sum((2a+1+2i | p) for i=-2,-1,0,1,2). """ stype = type(a) await mpc.returnType(stype) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 6, signed=True) # 6 random signs r = mpc._randoms(Zp, 6) r = mpc.schur_prod(r, r) # 6 random squares modulo p a, s, r = await mpc.gather(a, s, r) y = [b + 2 * i for b in (2 * a + 1, ) for i in (-2, -1, 0, 1, 2)] y = await mpc.schur_prod(y, s[:-1]) y.append(s[-1]) y = await mpc.schur_prod(y, r) y = await mpc.output(y) t = sum(s[i] * legendre_p(y[i]) for i in range(5)) t = await mpc.output(t * y[-1]) return s[-1] * legendre_p(t)
noise1 = float(np.random.normal(0, sigma1)) # Convert them to secure type sec_noise0, sec_noise1 = secfxp(noise0), secfxp(noise1) else: # The server does not generate noise values sec_noise0, sec_noise1 = secfxp(None), secfxp(None) # Secret-share both noise values with every other party all_sec_noises0 = mpc.input(sec_noise0, senders=list(range(1, M))) all_sec_noises1 = mpc.input(sec_noise1, senders=list(range(1, M))) # Collectively (and securely) draw M-1 random bits (one for each client) # These will be use to select which noise to use (0 or 1), for all clients sec_selection_bits = mpc.random_bits(secfxp, M - 1) sec_chosen_noises = [] for client_id, selection_bit_for_client in enumerate(sec_selection_bits): # If b is 0, we select noise0 from this client # If b is 1, we select noise1 from this client sec_chosen_noise = mpc.if_else(selection_bit_for_client, all_sec_noises1[client_id], all_sec_noises0[client_id]) sec_chosen_noises.append(sec_chosen_noise) # Aggregate the secure noise values from all parties total_sec_noise = scalar_add_all(sec_chosen_noises) # Find the secure maximum in the aggregated array of votes sec_max = mpc.max(total_sec_votes)