def test_bsecfld(self): secfld = mpc.SecFld(char=2, min_order=2**8) a = secfld(57) b = secfld(67) self.assertEqual(int(mpc.run(mpc.output(mpc.input(a, 0)))), 57) self.assertEqual(int(mpc.run(mpc.output(+a - -a))), 0) self.assertEqual(int(mpc.run(mpc.output(a * b))), 137) self.assertEqual(int(mpc.run(mpc.output(a * b / a))), 67) self.assertEqual(int(mpc.run(mpc.output(a**254 * a))), 1) self.assertEqual(int(mpc.run(mpc.output(a & b))), 1) self.assertEqual(int(mpc.run(mpc.output(a | b))), 123) self.assertEqual(int(mpc.run(mpc.output(a ^ b))), 122) self.assertEqual(int(mpc.run(mpc.output(~a))), 198) c = mpc.run(mpc.output(mpc.to_bits(secfld(0)))) self.assertEqual(c, [0, 0, 0, 0, 0, 0, 0, 0]) c = mpc.run(mpc.output(mpc.to_bits(secfld(1)))) self.assertEqual(c, [1, 0, 0, 0, 0, 0, 0, 0]) c = mpc.run(mpc.output(mpc.to_bits(secfld(255)))) self.assertEqual(c, [1, 1, 1, 1, 1, 1, 1, 1]) c = mpc.run(mpc.output(mpc.to_bits(secfld(255), 1))) self.assertEqual(c, [1]) c = mpc.run(mpc.output(mpc.to_bits(secfld(255), 4))) self.assertEqual(c, [1, 1, 1, 1]) self.assertEqual(mpc.run(mpc.output(mpc.matrix_sub([[a]], [[a]])[0])), [0]) self.assertEqual( mpc.run(mpc.output(mpc.matrix_prod([c], [[a] * 4], True)[0])), [0]) self.assertEqual( mpc.run(mpc.output(mpc.matrix_prod([[a] * 4], [c], True)[0])), [0])
def sbox(x): """AES S-Box.""" y = mpc.to_bits(x**254) z = mpc.matrix_prod([y], A, True)[0] w = mpc.vector_add(z, B) v = mpc.from_bits(w) return v
def sbox1(v): """AES inverse S-Box.""" w = mpc.to_bits(v) z = mpc.vector_add(w, B) y = mpc.matrix_prod([z], A1, True)[0] x = mpc.from_bits(y)**254 return x
async def linear_solve(A, B): secfld = type(A[0][0]) d, e = len(A), len(B[0]) await mpc.returnType(secfld, d * e + 1) R, detR = random_matrix_determinant(secfld, d) RA = mpc.matrix_prod(R, A) RA = await mpc.output([a for row in RA for a in row]) RA = np.reshape(RA, (d, d)) RB = mpc.matrix_prod(R, B) RB = await mpc.gather(RB) # NB: RB is secret-shared invA_B, detRA = bareiss(secfld.field, np.concatenate((RA, RB), axis=1)) detA = detRA / detR adjA_B = [secfld(a) * detA for row in invA_B for a in row] return adjA_B + [detA]
async def main(): parser = argparse.ArgumentParser() parser.add_argument('-k', '--order', type=int, metavar='K', help='order K of hash chain, length n=2**K') parser.add_argument('--recursive', action='store_true', help='use recursive pebbler') parser.add_argument('--no-one-way', action='store_true', default=False, help='use dummy one-way function') parser.add_argument('--no-random-seed', action='store_true', default=False, help='use fixed seed') parser.set_defaults(order=1) args = parser.parse_args() await mpc.start() if args.recursive: Pebbler = P else: Pebbler = p IV = [[aes.secfld(3)] * 4] * 4 # IV as 4x4 array of GF(256) elements global f if args.no_one_way: D = aes.circulant_matrix([3, 0, 0, 0]) f = lambda x: mpc.matrix_prod(D, x) else: K = aes.key_expansion(IV) f = lambda x: mpc.matrix_add(aes.encrypt(K, x), x) if args.no_random_seed: x0 = IV else: x0 = [[mpc.random.getrandbits(aes.secfld, 8) for _ in range(4)] for _ in range(4)] k = args.order print(f'Hash chain of length {2**k}:') r = 1 for v in Pebbler(k, x0): if v is None: # initial stage print(f'{r:4}', '-') await mpc.throttler(0.1 ) # raise barrier roughly every 10 AES calls else: # output stage await aes.xprint(f'{r:4} x{2**(k+1) - 1 - r:<4} =', v) r += 1 await mpc.shutdown()
def decrypt(K, s): """AES decryption of s given key schedule K.""" Nr = len(K) - 1 # Nr is 10 or 14 for i in range(Nr, 0, -1): s = mpc.matrix_add(s, K[i]) if i < Nr: s = mpc.matrix_prod(C1, s) s = [s[j][-j:] + s[j][:-j] for j in range(4)] s = [[sbox1(x) for x in _] for _ in s] s = mpc.matrix_add(s, K[0]) return s
def encrypt(K, s): """AES encryption of s given key schedule K.""" Nr = len(K) - 1 # Nr is 10 or 14 s = mpc.matrix_add(s, K[0]) for i in range(1, Nr + 1): s = [[sbox(x) for x in _] for _ in s] s = [s[j][j:] + s[j][:j] for j in range(4)] if i < Nr: s = mpc.matrix_prod(C, s) s = mpc.matrix_add(s, K[i]) return s
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 schmidt_multilateration(locations, toas): """Schmidt's multilateration algorithm.""" # Transform sensor locations and ToA measurements # into linear system A w = b, using Schmidt's method: norm = [mpc.in_prod(p, p) for p in locations] A, b = [], [] for i, j, k in itertools.combinations(range(len(locations)), 3): Delta = [toas[j] - toas[k], toas[k] - toas[i], toas[i] - toas[j]] XYZN = [ locations[i] + [norm[i]], locations[j] + [norm[j]], locations[k] + [norm[k]] ] r_x, r_y, r_z, r_n = mpc.matrix_prod([Delta], XYZN)[0] A.append([2 * r_x, 2 * r_y, 2 * r_z]) b.append(mpc.prod(Delta) + r_n) # Compute least-squares solution w satisfying A^T A w = A^T b: AT, bT = list(map(list, zip(*A))), [b] # transpose of A and b ATA = mpc.matrix_prod(AT, AT, tr=True) # A^T (A^T)^T = A^T A ATb = mpc.matrix_prod(AT, bT, tr=True) # A^T (b^T)^T = A^T b w_det = linear_solve(ATA, ATb) x, y, z, det = await mpc.output(w_det) w = x / det, y / det, z / det return w
async def id3(T, R) -> asyncio.Future: sizes = [mpc.in_prod(T, v) for v in S[C]] i, mx = mpc.argmax(sizes) sizeT = mpc.sum(sizes) stop = (sizeT <= int(args.epsilon * len(T))) + (mx == sizeT) if not (R and await mpc.is_zero_public(stop)): i = await mpc.output(i) logging.info(f'Leaf node label {i}') tree = i else: T_R = [[mpc.schur_prod(T, v) for v in S[A]] for A in R] gains = [GI(mpc.matrix_prod(T_A, S[C], True)) for T_A in T_R] k = await mpc.output(mpc.argmax(gains, key=SecureFraction)[0]) T_Rk = T_R[k] del T_R, gains # release memory A = list(R)[k] logging.info(f'Attribute node {A}') if args.parallel_subtrees: subtrees = await mpc.gather( [id3(Tj, R.difference([A])) for Tj in T_Rk]) else: subtrees = [await id3(Tj, R.difference([A])) for Tj in T_Rk] tree = A, subtrees return tree
async def main(): parser = argparse.ArgumentParser() parser.add_argument( '-i', '--dataset', type=int, metavar='I', help=('dataset 0=uvlp (default), 1=wiki, 2=tb2x2, 3=woody, ' '4=LPExample_R20, 5=sc50b, 6=kb2, 7=LPExample')) parser.add_argument('-l', '--bit-length', type=int, metavar='L', help='override preset bit length for dataset') parser.set_defaults(dataset=0, bit_length=0) args = parser.parse_args() settings = [('uvlp', 24, 37 / 3), ('wiki', 24, 20), ('tb2x2', 18, 10.5), ('woody', 36, 540), ('LPExample_R20', 52, 3.441176), ('sc50b', 52, 70), ('kb2', 96, 1749.9204734889486), ('LPExample', 96, 1188806595)] name, bit_length, exact_max = settings[args.dataset] if args.bit_length: bit_length = args.bit_length with open(os.path.join('data', 'lp', name + '.csv')) as file: T = list(csv.reader(file)) m = len(T) - 1 n = len(T[0]) - 1 secfxp = mpc.SecFxp(bit_length) print( f'Using secure {bit_length}-bit fixed-point numbers: {secfxp.__name__}' ) print(f'dataset: {name} with {m} constraints and {n} variables') T[0][-1] = '0' # initialize optimal value for i in range(m + 1): for j in range(n + 1): T[i][j] = secfxp(float(T[i][j]), integral=False) c = T[0][:-1] # maximize c.x subject to A.x <= b, x >= 0 A = [T[i + 1][:-1] for i in range(m)] b = [T[i + 1][-1] for i in range(m)] await mpc.start() cobasis = [secfxp(j) for j in range(n)] basis = [secfxp(n + i) for i in range(m)] iteration = 0 while True: # find index of pivot column p_col_index, minimum = argmin_int(T[0][:-1]) if await mpc.output(minimum >= 0): break # maximum reached # find index of pivot row p_col = mpc.matrix_prod([p_col_index], T, True)[0] constraints = [[T[i][-1], p_col[i], p_col[i] > 0.0001] for i in range(1, m + 1)] p_row_index, (_, pivot, _) = argmin_rat(constraints) # reveal progress a bit iteration += 1 mx = await mpc.output(T[0][-1]) p = await mpc.output(pivot) logging.info(f'Iteration {iteration}: {mx} pivot={p}') # swap basis entries delta = mpc.in_prod(basis, p_row_index) - mpc.in_prod( cobasis, p_col_index) cobasis = mpc.vector_add(cobasis, mpc.scalar_mul(delta, p_col_index)) basis = mpc.vector_sub(basis, mpc.scalar_mul(delta, p_row_index)) # update tableau Tij = Tij - (Til - bool(i==k))/Tkl * (Tkj + bool(j==l)) p_col_index.append(secfxp(0)) p_row_index.insert(0, secfxp(0)) p_col = mpc.vector_sub(p_col, p_row_index) p_col = mpc.scalar_mul(1 / pivot, p_col) p_row = mpc.matrix_prod([p_row_index], T)[0] p_row = mpc.vector_add(p_row, p_col_index) T = mpc.gauss(T, secfxp(1), p_col, p_row) mx = await mpc.output(T[0][-1]) rel_error = (mx - exact_max) / exact_max print(f'max = {mx} (error {rel_error:.3%}) in {iteration} iterations') logging.info('Solution x') x = [secfxp(0) for _ in range(n)] for i in range(m): u = mpc.unit_vector(basis[i], m + n)[:n] v = mpc.scalar_mul(T[i + 1][-1], u) x = mpc.vector_add(x, v) cx = mpc.in_prod(c, x) Ax = mpc.matrix_prod([x], A, True)[0] approx = lambda a: 1.01 * a + 0.0001 Ax_bounded_by_b = mpc.all(Ax[i] <= approx(b[i]) for i in range(m)) x_nonnegative = mpc.all(x[j] >= 0 for j in range(n)) logging.info('Dual solution y') y = [secfxp(0) for _ in range(m)] for j in range(n): u = mpc.unit_vector(cobasis[j], m + n)[n:] v = mpc.scalar_mul(T[0][j], u) y = mpc.vector_sub(y, v) yb = mpc.in_prod(y, b) yA = mpc.matrix_prod([y], A)[0] approx = lambda a: mpc.if_else(a < 0, 1 / 1.01, 1.01) * a + 0.0001 yA_bounded_by_c = mpc.all(yA[j] <= approx(c[j]) for j in range(n)) y_nonpositive = mpc.all(y[i] <= 0 for i in range(m)) cx_eq_yb = abs(cx - yb) <= 0.01 * abs(cx) check = mpc.all([ cx_eq_yb, Ax_bounded_by_b, x_nonnegative, yA_bounded_by_c, y_nonpositive ]) check = bool(await mpc.output(check)) print( f'verification c.x == y.b, A.x <= b, x >= 0, y.A <= c, y <= 0: {check}' ) x = await mpc.output(x) print(f'solution = {x}') await mpc.shutdown()
def tensormatrix_prod(x, W, b): logging.info('- - - - - - - - fc - - - - - - -') W, b = W.tolist(), b.tolist() return [mpc.vector_add(mpc.matrix_prod([z.tolist()], W)[0], b) for z in x]
async def main(): parser = argparse.ArgumentParser() parser.add_argument('-d', '--data', help='filename for tableau') parser.add_argument('options', nargs='*') parser.set_defaults(data='default') args = parser.parse_args() if not args.options: certificate_filename = f'c{mpc.pid}.cert' logging.info('Setting certificate file to default = %s', certificate_filename) else: certificate_filename = args.options[0] T = load_tableau(args.data) l = mpc.options.bit_length m = len(T) - 1 n = len(T[0]) - 1 secint = mpc.SecInt(l, n=m + n) for i in range(m + 1): for j in range(n + 1): T[i][j] = secint(T[i][j]) Zp = secint.field N = Zp.nth w = Zp.root w_powers = [Zp(1)] for _ in range(N - 1): w_powers.append(w_powers[-1] * w) assert w_powers[-1] * w == 1 basis = [secint(w_powers[-(i + n)]) for i in range(m)] cobasis = [secint(w_powers[-j]) for j in range(n)] prev_pivot = secint(1) await mpc.start() iteration = 0 logging.info('%d Termination?...', iteration) p_col_index, minimum = argmin_int(T[-1][:-1]) while await mpc.output(minimum < 0): iteration += 1 logging.info('%d Determining pivot...', iteration) p_col = mpc.matrix_prod([p_col_index], T, True)[0] constraints = [(T[i][-1] + (p_col[i] <= 0), p_col[i]) for i in range(m)] p_row_index, (_, pivot) = argmin_rat(constraints) logging.info('%d Updating tableau...', iteration) # T[i,j] = T[i,j]*p/p' - (C[i]/p' - p_row_index[i])*(R[j] + p * p_col_index[j]) p_row = mpc.matrix_prod([p_row_index], T)[0] delta_row = mpc.scalar_mul(prev_pivot, p_col_index) delta_row.append(secint(0)) p_row = mpc.vector_add(p_row, delta_row) prev_p_inv = 1 / prev_pivot p_col = mpc.scalar_mul(prev_p_inv, p_col) p_col = mpc.vector_sub(p_col, p_row_index + [secint(0)]) T = mpc.gauss(T, pivot * prev_p_inv, p_col, p_row) prev_pivot = pivot # swap basis entries delta = mpc.in_prod(basis, p_row_index) - mpc.in_prod( cobasis, p_col_index) p_row_index = mpc.scalar_mul(delta, p_row_index) basis = mpc.vector_sub(basis, p_row_index) p_col_index = mpc.scalar_mul(delta, p_col_index) cobasis = mpc.vector_add(cobasis, p_col_index) logging.info('%d Termination?...', iteration) p_col_index, minimum = argmin_int(T[-1][:-1]) logging.info('Termination...') mx = await mpc.output(T[-1][-1]) cd = await mpc.output(prev_pivot) print(' max(f) = %d / %d = %f' % (mx.value, cd.value, float(mx.value) / cd.value)) logging.info('Computing solution...') sum_x_powers = [secint(0) for _ in range(N)] for i in range(m): x_powers = pow_list(T[i][-1] / N, basis[i], N) sum_x_powers = mpc.vector_add(sum_x_powers, x_powers) solution = [None] * n for j in range(n): coefs = [w_powers[(j * k) % N] for k in range(N)] solution[j] = mpc.lin_comb(coefs, sum_x_powers) solution = await mpc.output(solution) logging.info('Computing dual solution...') sum_x_powers = [secint(0) for _ in range(N)] for j in range(n): x_powers = pow_list(T[-1][j] / N, cobasis[j], N) sum_x_powers = mpc.vector_add(sum_x_powers, x_powers) dual_solution = [None] * m for i in range(m): coefs = [w_powers[((n + i) * k) % N] for k in range(N)] dual_solution[i] = mpc.lin_comb(coefs, sum_x_powers) dual_solution = await mpc.output(dual_solution) await mpc.shutdown() logging.info('Writing output to %s.', certificate_filename) with open(os.path.join('data', 'lp', certificate_filename), 'w') as f: f.write('# tableau = \n' + args.data + '\n') f.write('# bit-length = \n' + str(mpc.options.bit_length) + '\n') f.write('# security parameter = \n' + str(mpc.options.sec_param) + '\n') f.write('# threshold = \n' + str(mpc.threshold) + '\n') f.write('# common denominator = \n' + str(cd.value) + '\n') f.write('# solution = \n') f.write('\t'.join(str(x.value) for x in solution) + '\n') f.write('# dual solution = \n') f.write('\t'.join(str(x.value) for x in dual_solution) + '\n')
def test_secfxp(self): secfxp = mpc.SecFxp() c = mpc.to_bits(secfxp(0), 0) # mpc.output() only works for nonempty lists self.assertEqual(c, []) c = mpc.run(mpc.output(mpc.to_bits(secfxp(0)))) self.assertEqual(c, [0.0] * 32) c = mpc.run(mpc.output(mpc.to_bits(secfxp(1)))) self.assertEqual(c, [0.0] * 16 + [1.0] + [0.0] * 15) c = mpc.run(mpc.output(mpc.to_bits(secfxp(0.5)))) self.assertEqual(c, [0.0] * 15 + [1.0] + [0.0] * 16) c = mpc.run(mpc.output(mpc.to_bits(secfxp(8113)))) self.assertEqual(c, [0.0] * 16 + [ float(b) for b in [1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0] ]) c = mpc.run(mpc.output(mpc.to_bits(secfxp(2**15 - 1)))) self.assertEqual(c, [float(b) for b in [0] * 16 + [1] * 15 + [0]]) c = mpc.run(mpc.output(mpc.to_bits(secfxp(-1)))) self.assertEqual(c, [float(b) for b in [0] * 16 + [1] * 16]) c = mpc.run(mpc.output(mpc.to_bits(secfxp(-2**15)))) self.assertEqual(c, [float(b) for b in [0] * 31 + [1]]) for f in [8, 16, 32, 64]: secfxp = mpc.SecFxp(2 * f) c = mpc.run(mpc.output(secfxp(1) + secfxp(1))) self.assertEqual(c.frac_length, f) self.assertEqual(c, 2) c = mpc.run(mpc.output(secfxp(2**-f) + secfxp(1))) if f != 64: # NB: 1 + 2**-64 == 1 in Python self.assertEqual(c, 1 + 2**-f) self.assertEqual(mpc.run(mpc.output(secfxp(0.5) * secfxp(2.0))), 1) self.assertEqual(mpc.run(mpc.output(secfxp(2.0) * secfxp(0.5))), 1) s = [10.7, -3.4, 0.1, -0.11] self.assertEqual(mpc.run(mpc.output(list(map(secfxp, s)))), s) s = [10.5, -3.25, 0.125, -0.125] a, b, c, d = list(map(secfxp, s)) t = [v * v for v in s] self.assertEqual(mpc.run(mpc.output([a * a, b * b, c * c, d * d])), t) x = [a, b, c, d] self.assertEqual(mpc.run(mpc.output(mpc.schur_prod(x, x))), t) self.assertEqual(mpc.run(mpc.output(mpc.schur_prod(x, x[:]))), t) t = sum(t) self.assertEqual(mpc.run(mpc.output(mpc.in_prod(x, x))), t) self.assertEqual(mpc.run(mpc.output(mpc.in_prod(x, x[:]))), t) self.assertEqual( mpc.run(mpc.output(mpc.matrix_prod([x], [x], True)[0])), [t]) t = [_ for a, b, c, d in [s] for _ in [a + b, a * b, a - b]] self.assertEqual(mpc.run(mpc.output([a + b, a * b, a - b])), t) t = [ _ for a, b, c, d in [s] for _ in [(a + b)**2, (a + b)**2 + 3 * c] ] self.assertEqual( mpc.run(mpc.output([(a + b)**2, (a + b)**2 + 3 * c])), t) t = [int(_) for a, b, c, d in [s] for _ in [a < b, b < c, c < d]] self.assertEqual(mpc.run(mpc.output([a < b, b < c, c < d])), t) t = int(s[0] < s[1] and s[1] < s[2]) self.assertEqual(mpc.run(mpc.output((a < b) & (b < c))), t) t = int(s[0] < s[1] or s[1] < s[2]) self.assertEqual(mpc.run(mpc.output((a < b) | (b < c))), t) t = (int(s[0] < s[1]) ^ int(s[1] < s[2])) self.assertEqual(mpc.run(mpc.output((a < b) ^ (b < c))), t) t = (int(not s[0] < s[1]) ^ int(s[1] < s[2])) self.assertEqual(mpc.run(mpc.output(~(a < b) ^ b < c)), t) t = [int(s[0] > 1), int(10 * s[1] < 5), int(10 * s[0] == 5)] self.assertEqual( mpc.run(mpc.output([a > 1, 10 * b < 5, 10 * a == 5])), t) s[3] = -0.120 d = secfxp(s[3]) t = s[3] / 0.25 self.assertAlmostEqual(mpc.run(mpc.output(d / 0.25)).signed(), t, delta=1) t = s[3] / s[2] + s[0] self.assertAlmostEqual(mpc.run(mpc.output(d / c + a)).signed(), t, delta=1) t = round(t * (1 << f)) self.assertAlmostEqual(mpc.run(mpc.output(d / c + a)), t, delta=1) t = ((s[0] + s[1])**2 + 3 * s[2]) / s[2] self.assertAlmostEqual(mpc.run(mpc.output( ((a + b)**2 + 3 * c) / c)).signed(), t, delta=2) t = 1 / s[3] self.assertAlmostEqual((mpc.run(mpc.output(1 / d))).signed(), t, delta=1) t = s[2] / s[3] self.assertAlmostEqual(mpc.run(mpc.output(c / d)).signed(), t, delta=1) t = -s[3] / s[2] t = round(t * (1 << f)) self.assertAlmostEqual(mpc.run(mpc.output(-d / c)), t, delta=1) t = s[2] / s[3] t = round(t * (1 << f)) self.assertAlmostEqual(mpc.run(mpc.output(d / c)), t, delta=1) self.assertEqual(mpc.run(mpc.output(mpc.sgn(+a))), int(s[0] > 0)) self.assertEqual(mpc.run(mpc.output(mpc.sgn(-a))), -int(s[0] > 0)) self.assertEqual(mpc.run(mpc.output(mpc.sgn(secfxp(0)))), 0) self.assertEqual(mpc.run(mpc.output(abs(secfxp(-1.5)))), 1.5) self.assertEqual(mpc.run(mpc.output(mpc.min(a, b, c, d))), min(s)) self.assertEqual(mpc.run(mpc.output(mpc.min(a, 0))), min(s[0], 0)) self.assertEqual(mpc.run(mpc.output(mpc.min(0, b))), min(0, s[1])) self.assertEqual(mpc.run(mpc.output(mpc.max(a, b, c, d))), max(s)) self.assertEqual(mpc.run(mpc.output(mpc.max(a, 0))), max(s[0], 0)) self.assertEqual(mpc.run(mpc.output(mpc.max(0, b))), max(0, s[1])) self.assertEqual( mpc.run(mpc.output(list(mpc.min_max(a, b, c, d)))), [min(s), max(s)]) self.assertEqual(mpc.run(mpc.output(secfxp(5) % 2)), 1) self.assertEqual(mpc.run(mpc.output(secfxp(1) % 2**(1 - f))), 0) self.assertEqual(mpc.run(mpc.output(secfxp(2**-f) % 2**(1 - f))), 2**-f) self.assertEqual( mpc.run(mpc.output(secfxp(2 * 2**-f) % 2**(1 - f))), 0) self.assertEqual(mpc.run(mpc.output(secfxp(1) // 2**(1 - f))), 2**(f - 1)) self.assertEqual(mpc.run(mpc.output(secfxp(27.0) % 7.0)), 6.0) self.assertEqual(mpc.run(mpc.output(secfxp(-27.0) // 7.0)), -4.0) self.assertEqual( mpc.run(mpc.output(list(divmod(secfxp(27.0), 6.0)))), [4.0, 3.0]) self.assertEqual(mpc.run(mpc.output(secfxp(21.5) % 7.5)), 6.5) self.assertEqual(mpc.run(mpc.output(secfxp(-21.5) // 7.5)), -3.0) self.assertEqual( mpc.run(mpc.output(list(divmod(secfxp(21.5), 0.5)))), [43.0, 0.0])
async def main(): global secint parser = argparse.ArgumentParser() parser.add_argument('-b', '--batch-size', type=int, metavar='B', help='number of images to classify') parser.add_argument( '-o', '--offset', type=int, metavar='O', help='offset for batch (otherwise random in [0,10000-B])') parser.add_argument( '-d', '--d-k-star', type=int, metavar='D', help='k=D=0,1,2 for Legendre-based comparison using d_k^*') parser.add_argument('--no-legendre', action='store_true', help='disable Legendre-based comparison') parser.add_argument('--no-vectorization', action='store_true', help='disable vectorization of comparisons') parser.set_defaults(batch_size=1, offset=-1, d_k_star=1) args = parser.parse_args() batch_size = args.batch_size offset = args.offset if args.no_legendre: secint = mpc.SecInt(14) # using vectorized MPyC integer comparison else: if args.d_k_star == 0: secint = mpc.SecInt( 14, p=3546374752298322551) # Legendre-0 range [-134, 134] bsgn = bsgn_0 vector_bsgn = vector_bsgn_0 elif args.d_k_star == 1: secint = mpc.SecInt( 14, p=9409569905028393239) # Legendre-1 range [-383, 383] bsgn = bsgn_1 vector_bsgn = vector_bsgn_1 else: secint = mpc.SecInt( 14, p=15569949805843283171) # Legendre-2 range [-594, 594] bsgn = bsgn_2 vector_bsgn = vector_bsgn_2 one_by_one = args.no_vectorization await mpc.start() if offset < 0: offset = random.randrange(10001 - batch_size) if mpc.pid == 0 else None offset = await mpc.transfer(offset, senders=0) logging.info('--------------- INPUT -------------') print( f'Type = {secint.__name__}, range = ({offset}, {offset + batch_size})') # read batch_size labels and images at given offset df = gzip.open(os.path.join('data', 'cnn', 't10k-labels-idx1-ubyte.gz')) d = df.read()[8 + offset:8 + offset + batch_size] labels = list(map(int, d)) print('Labels:', labels) df = gzip.open(os.path.join('data', 'cnn', 't10k-images-idx3-ubyte.gz')) d = df.read()[16 + offset * 28**2:16 + (offset + batch_size) * 28**2] L = np.array(list(d)).reshape(batch_size, 28**2) if batch_size == 1: x = np.array(L[0]).reshape(28, 28) print( np.array2string(np.vectorize(lambda a: int(bool((a / 255))))(x), separator='')) L = np.vectorize(lambda a: secint(int(a)))(L).tolist() logging.info('--------------- LAYER 1 -------------') logging.info('- - - - - - - - fc - - - - - - -') L = mpc.matrix_prod(L, load_W('fc1')) L = mpc.matrix_add(L, [load_b('fc1')] * len(L)) logging.info('- - - - - - - - bsgn - - - - - - -') if one_by_one: L = np.vectorize(lambda a: (a >= 0) * 2 - 1)(L).tolist() else: L = [vector_sge(_) for _ in L] await mpc.barrier() logging.info('--------------- LAYER 2 -------------') logging.info('- - - - - - - - fc - - - - - - -') L = mpc.matrix_prod(L, load_W('fc2')) L = mpc.matrix_add(L, [load_b('fc2')] * len(L)) await mpc.barrier() logging.info('- - - - - - - - bsgn - - - - - - -') if args.no_legendre: secint.bit_length = 10 if one_by_one: activate = np.vectorize(lambda a: (a >= 0) * 2 - 1) L = activate(L).tolist() else: L = [vector_sge(_) for _ in L] else: if one_by_one: activate = np.vectorize(bsgn) L = activate(L).tolist() else: L = [vector_bsgn(_) for _ in L] await mpc.barrier() logging.info('--------------- LAYER 3 -------------') logging.info('- - - - - - - - fc - - - - - - -') L = mpc.matrix_prod(L, load_W('fc3')) L = mpc.matrix_add(L, [load_b('fc3')] * len(L)) await mpc.barrier() logging.info('- - - - - - - - bsgn - - - - - - -') if args.no_legendre: secint.bit_length = 10 if one_by_one: activate = np.vectorize(lambda a: (a >= 0) * 2 - 1) L = activate(L).tolist() else: L = [vector_sge(_) for _ in L] else: if one_by_one: activate = np.vectorize(bsgn) L = activate(L).tolist() else: L = [vector_bsgn(_) for _ in L] await mpc.barrier() logging.info('--------------- LAYER 4 -------------') logging.info('- - - - - - - - fc - - - - - - -') L = mpc.matrix_prod(L, load_W('fc4')) L = mpc.matrix_add(L, [load_b('fc4')] * len(L)) await mpc.barrier() logging.info('--------------- OUTPUT -------------') if args.no_legendre: secint.bit_length = 14 for i in range(batch_size): prediction = await mpc.output(mpc.argmax(L[i])[0]) error = '******* ERROR *******' if prediction != labels[i] else '' print( f'Image #{offset+i} with label {labels[i]}: {prediction} predicted. {error}' ) print(await mpc.output(L[i])) await mpc.shutdown()
def test_secfxp(self): secfxp = mpc.SecFxp() self.assertEqual( mpc.run(mpc.output(mpc.input(secfxp(7.75), senders=0))), 7.75) c = mpc.to_bits(secfxp(0), 0) # mpc.output() only works for nonempty lists self.assertEqual(c, []) c = mpc.run(mpc.output(mpc.to_bits(secfxp(0)))) self.assertEqual(c, [0.0] * 32) c = mpc.run(mpc.output(mpc.to_bits(secfxp(1)))) self.assertEqual(c, [0.0] * 16 + [1.0] + [0.0] * 15) c = mpc.run(mpc.output(mpc.to_bits(secfxp(0.5)))) self.assertEqual(c, [0.0] * 15 + [1.0] + [0.0] * 16) c = mpc.run(mpc.output(mpc.to_bits(secfxp(8113)))) self.assertEqual(c, [0.0] * 16 + [1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0]) c = mpc.run(mpc.output(mpc.to_bits(secfxp(2**15 - 1)))) self.assertEqual(c, [0] * 16 + [1] * 15 + [0]) c = mpc.run(mpc.output(mpc.to_bits(secfxp(-1)))) self.assertEqual(c, [0] * 16 + [1] * 16) c = mpc.run(mpc.output(mpc.to_bits(secfxp(-2**15)))) self.assertEqual(c, [0] * 31 + [1]) for f in [8, 16, 32, 64]: secfxp = mpc.SecFxp(2 * f) c = mpc.run(mpc.output(secfxp(1) + secfxp(1))) self.assertEqual(c, 2) c = mpc.run(mpc.output(secfxp(2**-f) + secfxp(1))) if f != 64: # NB: 1 + 2**-64 == 1 in Python self.assertEqual(c, 1 + 2**-f) self.assertEqual(mpc.run(mpc.output(secfxp(0.5) * secfxp(2.0))), 1) self.assertEqual(mpc.run(mpc.output(secfxp(2.0) * secfxp(0.5))), 1) c = mpc.run( mpc.output( secfxp(2**(f // 2 - 1) - 0.5) * secfxp(-2**(f // 2) + 0.5))) self.assertEqual(c, -2**(f - 1) + 1.5 * 2**(f // 2 - 1) - 0.25) s = [10.75, -3.375, 0.125, -0.125] self.assertEqual(mpc.run(mpc.output(list(map(secfxp, s)))), s) s = [10.5, -3.25, 0.125, -0.125] a, b, c, d = list(map(secfxp, s)) t = [v * v for v in s] self.assertEqual(mpc.run(mpc.output([a * a, b * b, c * c, d * d])), t) x = [a, b, c, d] self.assertEqual(mpc.run(mpc.output(mpc.schur_prod(x, x))), t) self.assertEqual(mpc.run(mpc.output(mpc.schur_prod(x, x[:]))), t) t = sum(t) self.assertEqual(mpc.run(mpc.output(mpc.in_prod(x, x))), t) self.assertEqual(mpc.run(mpc.output(mpc.in_prod(x, x[:]))), t) self.assertEqual( mpc.run(mpc.output(mpc.matrix_prod([x], [x], True)[0])), [t]) u = mpc.unit_vector(secfxp(3), 4) self.assertEqual( mpc.run(mpc.output(mpc.matrix_prod([x], [u], True)[0])), [s[3]]) self.assertEqual( mpc.run(mpc.output(mpc.matrix_prod([u], [x], True)[0])), [s[3]]) self.assertEqual( mpc.run(mpc.output(mpc.gauss([[a]], b, [a], [b])[0])), [0]) t = [_ for a, b, c, d in [s] for _ in [a + b, a * b, a - b]] self.assertEqual(mpc.run(mpc.output([a + b, a * b, a - b])), t) t = [ _ for a, b, c, d in [s] for _ in [(a + b)**2, (a + b)**2 + 3 * c] ] self.assertEqual( mpc.run(mpc.output([(a + b)**2, (a + b)**2 + 3 * c])), t) t = [_ for a, b, c, d in [s] for _ in [a < b, b < c, c < d]] self.assertEqual(mpc.run(mpc.output([a < b, b < c, c < d])), t) t = s[0] < s[1] and s[1] < s[2] self.assertEqual(mpc.run(mpc.output((a < b) & (b < c))), t) t = s[0] < s[1] or s[1] < s[2] self.assertEqual(mpc.run(mpc.output((a < b) | (b < c))), t) t = (int(s[0] < s[1]) ^ int(s[1] < s[2])) self.assertEqual(mpc.run(mpc.output((a < b) ^ (b < c))), t) t = (int(not s[0] < s[1]) ^ int(s[1] < s[2])) self.assertEqual(mpc.run(mpc.output(~(a < b) ^ b < c)), t) t = [s[0] > 1, 10 * s[1] < 5, 10 * s[0] == 5] self.assertEqual( mpc.run(mpc.output([a > 1, 10 * b < 5, 10 * a == 5])), t) s[3] = -0.120 d = secfxp(s[3]) t = s[3] / 0.25 self.assertAlmostEqual(mpc.run(mpc.output(d / 0.25)), t, delta=2**(1 - f)) t = round(s[3] / s[2] + s[0]) self.assertEqual(round(mpc.run(mpc.output(d / c + a))), t) t = ((s[0] + s[1])**2 + 3 * s[2]) / s[2] self.assertAlmostEqual(mpc.run(mpc.output( ((a + b)**2 + 3 * c) / c)), t, delta=2**(8 - f)) t = 1 / s[3] self.assertAlmostEqual(mpc.run(mpc.output(1 / d)), t, delta=2**(6 - f)) t = s[2] / s[3] self.assertAlmostEqual(mpc.run(mpc.output(c / d)), t, delta=2**(3 - f)) t = -s[3] / s[2] self.assertAlmostEqual(mpc.run(mpc.output(-d / c)), t, delta=2**(3 - f)) self.assertEqual(mpc.run(mpc.output(mpc.sgn(+a))), s[0] > 0) self.assertEqual(mpc.run(mpc.output(mpc.sgn(-a))), -(s[0] > 0)) self.assertEqual(mpc.run(mpc.output(mpc.sgn(secfxp(0)))), 0) self.assertEqual(mpc.run(mpc.output(abs(secfxp(-1.5)))), 1.5) self.assertEqual(mpc.run(mpc.output(mpc.min(a, b, c, d))), min(s)) self.assertEqual(mpc.run(mpc.output(mpc.min(a, 0))), min(s[0], 0)) self.assertEqual(mpc.run(mpc.output(mpc.min(0, b))), min(0, s[1])) self.assertEqual(mpc.run(mpc.output(mpc.max(a, b, c, d))), max(s)) self.assertEqual(mpc.run(mpc.output(mpc.max(a, 0))), max(s[0], 0)) self.assertEqual(mpc.run(mpc.output(mpc.max(0, b))), max(0, s[1])) self.assertEqual( mpc.run(mpc.output(list(mpc.min_max(a, b, c, d)))), [min(s), max(s)]) self.assertEqual(mpc.run(mpc.output(mpc.argmin([a, b, c, d])[0])), 1) self.assertEqual( mpc.run(mpc.output(mpc.argmin([a, b], key=operator.neg)[1])), max(s)) self.assertEqual(mpc.run(mpc.output(mpc.argmax([a, b, c, d])[0])), 0) self.assertEqual( mpc.run(mpc.output(mpc.argmax([a, b], key=operator.neg)[1])), min(s)) self.assertEqual(mpc.run(mpc.output(secfxp(5) % 2)), 1) self.assertEqual(mpc.run(mpc.output(secfxp(1) % 2**(1 - f))), 0) self.assertEqual(mpc.run(mpc.output(secfxp(2**-f) % 2**(1 - f))), 2**-f) self.assertEqual( mpc.run(mpc.output(secfxp(2 * 2**-f) % 2**(1 - f))), 0) self.assertEqual(mpc.run(mpc.output(secfxp(1) // 2**(1 - f))), 2**(f - 1)) self.assertEqual(mpc.run(mpc.output(secfxp(27.0) % 7.0)), 6.0) self.assertEqual(mpc.run(mpc.output(secfxp(-27.0) // 7.0)), -4.0) self.assertEqual( mpc.run(mpc.output(list(divmod(secfxp(27.0), 6.0)))), [4.0, 3.0]) self.assertEqual(mpc.run(mpc.output(secfxp(21.5) % 7.5)), 6.5) self.assertEqual(mpc.run(mpc.output(secfxp(-21.5) // 7.5)), -3.0) self.assertEqual( mpc.run(mpc.output(list(divmod(secfxp(21.5), 0.5)))), [43.0, 0.0])
async def main(): parser = argparse.ArgumentParser() parser.add_argument( '-i', '--dataset', type=int, metavar='I', help=('dataset 0=uvlp (default), 1=wiki, 2=tb2x2, 3=woody, ' '4=LPExample_R20, 5=sc50b, 6=kb2, 7=LPExample')) parser.add_argument('-l', '--bit-length', type=int, metavar='L', help='override preset bit length for dataset') parser.set_defaults(dataset=0, bit_length=0) args = parser.parse_args() settings = [('uvlp', 8, 1, 2), ('wiki', 6, 1, 2), ('tb2x2', 6, 1, 2), ('woody', 8, 1, 3), ('LPExample_R20', 70, 1, 5), ('sc50b', 104, 10, 55), ('kb2', 536, 100000, 106), ('LPExample', 110, 1, 178)] name, bit_length, scale, n_iter = settings[args.dataset] if args.bit_length: bit_length = args.bit_length with open(os.path.join('data', 'lp', name + '.csv')) as file: T = list(csv.reader(file)) m = len(T) - 1 n = len(T[0]) - 1 secint = mpc.SecInt(bit_length, n=m + n) # force existence of Nth root of unity, N>=m+n print(f'Using secure {bit_length}-bit integers: {secint.__name__}') print( f'dataset: {name} with {m} constraints and {n} variables (scale factor {scale})' ) T[0][-1] = '0' # initialize optimal value for i in range(m + 1): g = 0 for j in range(n + 1): T[i][j] = int(scale * float(T[i][j])) # scale to integer g = math.gcd(g, T[i][j]) g = max(g, 1) if i else 1 # skip cost row for j in range(n + 1): T[i][j] = secint(T[i][j] // g) c = T[0][:-1] # maximize c.x subject to A.x <= b, x >= 0 A = [T[i + 1][:-1] for i in range(m)] b = [T[i + 1][-1] for i in range(m)] Zp = secint.field N = Zp.nth w = Zp.root # w is an Nth root of unity in Zp, where N >= m + n w_powers = [Zp(1)] for _ in range(N - 1): w_powers.append(w_powers[-1] * w) assert w_powers[-1] * w == 1 await mpc.start() cobasis = [secint(w_powers[-j]) for j in range(n)] basis = [secint(w_powers[-(i + n)]) for i in range(m)] previous_pivot = secint(1) iteration = 0 while True: # find index of pivot column p_col_index, minimum = argmin_int(T[0][:-1]) if await mpc.output(minimum >= 0): break # maximum reached # find index of pivot row p_col = mpc.matrix_prod([p_col_index], T, True)[0] constraints = [[T[i][-1] + (p_col[i] <= 0), p_col[i]] for i in range(1, m + 1)] p_row_index, (_, pivot) = argmin_rat(constraints) # reveal progress a bit iteration += 1 mx = await mpc.output(T[0][-1]) cd = await mpc.output(previous_pivot) p = await mpc.output(pivot) # NB: no await in f-strings in Python 3.6 logging.info( f'Iteration {iteration}/{n_iter}: {mx / cd} pivot={p / cd}') # swap basis entries delta = mpc.in_prod(basis, p_row_index) - mpc.in_prod( cobasis, p_col_index) cobasis = mpc.vector_add(cobasis, mpc.scalar_mul(delta, p_col_index)) basis = mpc.vector_sub(basis, mpc.scalar_mul(delta, p_row_index)) # update tableau Tij = Tij*Tkl/Tkl' - (Til/Tkl' - bool(i==k)) * (Tkj + bool(j==l)*Tkl') p_col_index.append(secint(0)) p_row_index.insert(0, secint(0)) pp_inv = 1 / previous_pivot p_col = mpc.scalar_mul(pp_inv, p_col) p_col = mpc.vector_sub(p_col, p_row_index) p_row = mpc.matrix_prod([p_row_index], T)[0] p_row = mpc.vector_add(p_row, mpc.scalar_mul(previous_pivot, p_col_index)) T = mpc.gauss(T, pivot * pp_inv, p_col, p_row) previous_pivot = pivot mx = await mpc.output(T[0][-1]) cd = await mpc.output(previous_pivot ) # common denominator for all entries of T print( f'max = {mx} / {cd} / {scale} = {mx / cd / scale} in {iteration} iterations' ) logging.info('Solution x') sum_x_powers = [secint(0) for _ in range(N)] for i in range(m): x_powers = pow_list(T[i + 1][-1] / N, basis[i], N) sum_x_powers = mpc.vector_add(sum_x_powers, x_powers) x = [None] * n for j in range(n): coefs = [w_powers[(j * k) % N] for k in range(N)] x[j] = mpc.in_prod(coefs, sum_x_powers) cx = mpc.in_prod(c, x) Ax = mpc.matrix_prod([x], A, True)[0] Ax_bounded_by_b = mpc.all(Ax[i] <= b[i] * cd for i in range(m)) x_nonnegative = mpc.all(x[j] >= 0 for j in range(n)) logging.info('Dual solution y') sum_x_powers = [secint(0) for _ in range(N)] for j in range(n): x_powers = pow_list(T[0][j] / N, cobasis[j], N) sum_x_powers = mpc.vector_add(sum_x_powers, x_powers) y = [None] * m for i in range(m): coefs = [w_powers[((n + i) * k) % N] for k in range(N)] y[i] = mpc.in_prod(coefs, sum_x_powers) y[i] = -y[i] yb = mpc.in_prod(y, b) yA = mpc.matrix_prod([y], A)[0] yA_bounded_by_c = mpc.all(yA[j] <= c[j] * cd for j in range(n)) y_nonpositive = mpc.all(y[i] <= 0 for i in range(m)) cx_eq_yb = cx == yb check = mpc.all([ cx_eq_yb, Ax_bounded_by_b, x_nonnegative, yA_bounded_by_c, y_nonpositive ]) check = bool(await mpc.output(check)) print( f'verification c.x == y.b, A.x <= b, x >= 0, y.A <= c, y <= 0: {check}' ) x = await mpc.output(x) print(f'solution = {[a / cd for a in x]}') await mpc.shutdown()