def random_matrix_determinant(secfld, d): d_2 = d * (d - 1) // 2 L = np.diagflat([secfld(1)] * d) L[np.tril_indices(d, -1)] = mpc._randoms(secfld, d_2) L[np.triu_indices(d, 1)] = [secfld(0)] * d_2 diag = mpc._randoms(secfld, d) U = np.diagflat(diag) U[np.tril_indices(d, -1)] = [secfld(0)] * d_2 U[np.triu_indices(d, 1)] = mpc._randoms(secfld, d_2) R = mpc.matrix_prod(L.tolist(), U.tolist()) detR = mpc.prod(diag) # detR != 0 with overwhelming probability return R, detR
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_sge(x): """Compute binary signs securely for all elements of x in parallel. Vectorized version of MPyC's built-in secure comparison. Cf. mpc.sgn() with GE=True (and EQ=False). NB: mpc.prod() and mpc.is_zero_public() are not (yet) vectorized. """ stype = type(x[0]) n = len(x) await mpc.returnType(stype, n) Zp = stype.field l = stype.bit_length k = mpc.options.sec_param r_bits = await mpc.random_bits(Zp, (l + 1) * n) r_bits = [b.value for b in r_bits] r_modl = [0] * n for j in range(n): for i in range(l - 1, -1, -1): r_modl[j] <<= 1 r_modl[j] += r_bits[l * j + i] r_divl = mpc._randoms(Zp, n, 1 << k) x = await mpc.gather(x) x_r = [a + ((1 << l) + b) for a, b in zip(x, r_modl)] c = await mpc.output([a + (b.value << l) for a, b in zip(x_r, r_divl)]) c = [c.value % (1 << l) for c in c] e = [[None] * (l + 1) for _ in range(n)] for j in range(n): s_sign = (r_bits[l * n + j] << 1) - 1 sumXors = 0 for i in range(l - 1, -1, -1): c_i = (c[j] >> i) & 1 e[j][i] = Zp(s_sign + r_bits[l * j + i] - c_i + 3 * sumXors) sumXors += 1 - r_bits[l * j + i] if c_i else r_bits[l * j + i] e[j][l] = Zp(s_sign - 1 + 3 * sumXors) e = await mpc.gather([mpc.prod(_) for _ in e]) g = await mpc.gather([mpc.is_zero_public(stype(_)) for _ in e]) UF = [1 - b if g else b for b, g in zip(r_bits[-n:], g)] z = [(a - (c + (b << l))) / (1 << l - 1) - 1 for a, b, c in zip(x_r, UF, c)] return z
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_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)