├── .gitignore ├── README.md ├── circuit.v ├── mpc.py ├── out.v ├── requirements.txt ├── sieve.py └── yosys-script.txt /.gitignore: -------------------------------------------------------------------------------- 1 | oss-cad-suite 2 | out.v 3 | primes.txt 4 | **.pyc 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Read the full blog post here 2 | 3 | https://www.zellic.io/blog/mpc-from-scratch/ 4 | 5 | # MPC from Scratch 6 | 7 | A toy, educational implementation of [Garbled Circuit protocol](https://en.wikipedia.org/wiki/Garbled_circuit) from scratch in Python 8 | 9 | Note: this uses TEXTBOOK cryptography and textbook RSA for illustrative purposes. Don't use it in production 10 | 11 | Circuit synthesis is done using Yosys. It synthesizes circuit.v (high level logic) into out.v which is only gate level logic. Then the MPC implementation uses a hacky Verilog parser to get the circuit 12 | -------------------------------------------------------------------------------- /circuit.v: -------------------------------------------------------------------------------- 1 | module test(x, y, out); 2 | input [31:0] x; 3 | input [31:0] y; 4 | // output [31:0] out; 5 | // input x; 6 | // input y; 7 | output out; 8 | 9 | reg [31:0] tmp; 10 | // wire tmp; 11 | initial begin 12 | // tmp = x*y; 13 | // out = tmp == 1; 14 | out = (x == 9001) && (y == 1337); 15 | end 16 | // assign out = tmp; 17 | endmodule 18 | -------------------------------------------------------------------------------- /mpc.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | from Crypto.Util.number import getRandomNBitInteger, getPrime, long_to_bytes, bytes_to_long 5 | from Crypto.Hash import SHA3_256 6 | from Crypto.Cipher import AES 7 | from Crypto.Util.Padding import pad, unpad 8 | 9 | # Primality testing 10 | def rabin_miller(n, k=40): 11 | if n == 2: 12 | return True 13 | if n % 2 == 0: 14 | return False 15 | r, s = 0, n - 1 16 | while s % 2 == 0: 17 | r += 1 18 | s //= 2 19 | for _ in range(k): 20 | a = random.randrange(2, n - 1) 21 | x = pow(a, s, n) 22 | if x == 1 or x == n - 1: 23 | continue 24 | for _ in range(r - 1): 25 | x = pow(x, 2, n) 26 | if x == n - 1: 27 | break 28 | else: 29 | return False 30 | return True 31 | 32 | SMALL_PRIMES = list(map(int, map(str.strip, open('primes.txt','r').readlines()))) # gen with sieve.py 33 | def rabin_miller_fast(n, k=40): 34 | for p in SMALL_PRIMES: 35 | if n % p == 0: 36 | return False 37 | return rabin_miller(n, k) 38 | 39 | 40 | def randbits(n): 41 | return getRandomNBitInteger(n) 42 | 43 | def gen_prime(n): 44 | while True: 45 | p = randbits(n) 46 | p |= 1 # we only want odd numbers 47 | if rabin_miller_fast(p): 48 | return p 49 | print('.', end='', flush=True) 50 | 51 | def gen_prime_fast(n): 52 | return getPrime(n, os.urandom) 53 | 54 | gen_prime = gen_prime_fast 55 | 56 | def egcd(aa, bb): 57 | lr, r = abs(aa), abs(bb) 58 | x, lx, y, ly = 0, 1, 1, 0 59 | while r: 60 | lr, (q, r) = r, divmod(lr, r) 61 | x, lx = lx - q*x, x 62 | y, ly = ly - q*y, y 63 | return lr, lx * (-1 if aa < 0 else 1), ly * (-1 if bb < 0 else 1) 64 | 65 | def modinv(a, m): 66 | g, x, y = egcd(a, m) 67 | if g != 1: 68 | raise ValueError 69 | return x % m 70 | 71 | def gen_rsa_params(n=2048): 72 | p, q = gen_prime(n//2), gen_prime(n//2) 73 | N = p * q 74 | e = 65537 75 | phi = (p-1)*(q-1) 76 | d = modinv(e, phi) 77 | return e,d,N 78 | 79 | # note: textbook rsa has issues, padding should be used 80 | 81 | def oblivious_transfer_alice(m0, m1, n=2048, e=None, d=None, N=None): 82 | # generate new rsa parameters if not specified, otherwise use provided 83 | if e is None or d is None or N is None: 84 | e, d, N = gen_rsa_params(n) 85 | 86 | if m0 >= N or m1 >= N: 87 | raise ValueError('N too low') 88 | yield (e, N) 89 | x0, x1 = randbits(n), randbits(n) 90 | v = yield (x0, x1) 91 | k0 = pow(v - x0, d, N) 92 | k1 = pow(v - x1, d, N) 93 | m0k = (m0 + k0) % N 94 | m1k = (m1 + k1) % N 95 | yield m0k, m1k 96 | 97 | def oblivious_transfer_bob(b, n=2048): 98 | if not b in (0, 1): 99 | raise ValueError('b must be 0 or 1') 100 | e, N = yield 101 | x0, x1 = yield 102 | k = randbits(n) 103 | v = ((x0, x1)[b] + pow(k, e, N)) % N 104 | m0k, m1k = yield v 105 | mb = ((m0k, m1k)[b] - k) % N 106 | yield mb 107 | 108 | # 1-2 oblivious transfer 109 | def oblivious_transfer(alice, bob): 110 | e, N = next(alice) 111 | next(bob) 112 | bob.send((e, N)) 113 | 114 | x0, x1 = next(alice) 115 | v = bob.send((x0, x1)) 116 | 117 | m0k, m1k = alice.send(v) 118 | 119 | mb = bob.send((m0k, m1k)) 120 | return mb 121 | 122 | 123 | # quick and dirty verilog parser 124 | def parse_verilog(filename): 125 | circuit = {} # map from wire name -> (gate, name of the wires that are inputs to the gate...) 126 | inputs = [] 127 | outputs = [] 128 | import re 129 | filecontents = open(filename, 'r').read() 130 | for l in filecontents.split(';'): 131 | if not l: continue 132 | l = re.sub(r"/\*.*?\*/", '', l, flags=re.DOTALL) # remove comments 133 | l = re.sub(r'//.*$', '', l, flags=re.MULTILINE) # remove comments 134 | l = l.strip() 135 | tokens = l.split(' ') 136 | if tokens[0] == 'module': continue 137 | if tokens[0] == 'endmodule': continue 138 | tokens[-1] = tokens[-1].rstrip(';') 139 | if tokens[0] in ('wire', 'output', 'input'): # declaration 140 | if len(tokens) != 2: 141 | raise ValueError('unsupported statement:', l) 142 | typ, name = tokens 143 | if typ == 'input': 144 | inputs.append(name) 145 | elif typ == 'output': 146 | outputs.append(name) 147 | circuit[name] = None 148 | elif tokens[0] == 'assign': # assignment 149 | if tokens[2] != '=': 150 | raise ValueError('unsupported statement:', l) 151 | lhs = tokens[1] 152 | if '[' in lhs or ':' in lhs: 153 | raise ValueError('unsupported statement:', l) 154 | rhs = [*filter(bool,re.split(r'\b',''.join(tokens[3:])))] 155 | match rhs: 156 | case ['~', var]: 157 | rhs = ('not', var) 158 | case [var1, '&', var2]: 159 | rhs = ('and', var1, var2) 160 | case [var1, '|', var2]: 161 | rhs = ('or', var1, var2) 162 | case [var1, '^', var2]: 163 | rhs = ('xor', var1, var2) 164 | case [var1, '|~(', var2, ')']: 165 | rhs = ('ornot', var1, var2) 166 | case [var1, '&~(', var2, ')']: 167 | rhs = ('andnot', var1, var2) 168 | case ['~(', var1, '&', var2, ')']: 169 | rhs = ('nand', var1, var2) 170 | case ['~(', var1, '|', var2, ')']: 171 | rhs = ('nor', var1, var2) 172 | case ['~(', var1, '^', var2, ')']: 173 | rhs = ('xnor', var1, var2) 174 | case ['1', "'", val]: 175 | if not re.match(r'h(0|1)', val): 176 | raise ValueError('unsupported statement:', l) 177 | rhs = ('const_' + val[1],) 178 | case _: 179 | raise ValueError('unsupported statement:', l) 180 | circuit[lhs] = rhs 181 | for var in rhs[1:]: 182 | if var not in circuit: 183 | raise ValueError('undefined variable:', var, 'in statement', l) 184 | else: 185 | raise ValueError('unsupported statement:', l) 186 | for wire, value in circuit.items(): 187 | if not value and wire not in inputs: 188 | raise ValueError('wire was never assigned:', wire) 189 | return circuit, inputs, outputs 190 | 191 | import itertools 192 | import functools 193 | import operator 194 | 195 | def label_truth_table(output_name, gate, input_names, labels, k=128): 196 | if gate == 'and': 197 | assert len(input_names) == 2 198 | logic_table = [[0, 0], [0, 1]] 199 | elif gate == 'or': 200 | assert len(input_names) == 2 201 | logic_table = [[0, 1], [1, 1]] 202 | elif gate == 'nand': 203 | assert len(input_names) == 2 204 | logic_table = [[1, 1], [1, 0]] 205 | elif gate == 'xnor': 206 | assert len(input_names) == 2 207 | logic_table = [[1, 0], [0, 1]] 208 | elif gate == 'xor': 209 | assert len(input_names) == 2 210 | logic_table = [[0, 1], [1, 0]] 211 | elif gate == 'ornot': 212 | assert len(input_names) == 2 213 | logic_table = [[1, 0], [1, 1]] 214 | elif gate == 'nor': 215 | assert len(input_names) == 2 216 | logic_table = [[1, 0], [0, 0]] 217 | elif gate == 'andnot': 218 | assert len(input_names) == 2 219 | logic_table = [[0, 0], [1, 0]] 220 | elif gate == 'not': 221 | assert len(input_names) == 1 222 | logic_table = [1, 0] 223 | elif gate == 'const_0': 224 | assert len(input_names) == 0 225 | logic_table = 0 226 | elif gate == 'const_1': 227 | assert len(input_names) == 0 228 | logic_table = 1 229 | else: 230 | raise ValueError('unsupported gate', gate) 231 | for var in (output_name, *input_names): 232 | if var not in labels: 233 | labels[var] = [randbits(k), randbits(k)] # 0 and 1 labels for each var 234 | labeled_table = [] 235 | for inp_values in itertools.product((0,1), repeat=len(input_names)): 236 | output_value = functools.reduce(operator.getitem, inp_values, logic_table) 237 | output_label = labels[output_name][output_value] 238 | input_labels = [labels[input_name][input_value] for input_name, input_value in zip(input_names, inp_values)] 239 | labeled_table.append((output_label, input_labels)) 240 | return labeled_table 241 | 242 | def combine_keys(keys, k=128): 243 | h = SHA3_256.new() 244 | for ki in keys: 245 | h.update(long_to_bytes(ki)) 246 | return h.digest() 247 | 248 | def symmetric_enc(key, x): 249 | cipher = AES.new(key, AES.MODE_GCM) 250 | ciphertext, tag = cipher.encrypt_and_digest(pad(long_to_bytes(x), 16)) 251 | nonce = cipher.nonce 252 | return ciphertext, tag, nonce 253 | 254 | def symmetric_dec(key, ciphertext, tag, nonce): 255 | cipher = AES.new(key, AES.MODE_GCM, nonce=nonce) 256 | x = bytes_to_long(unpad(cipher.decrypt_and_verify(ciphertext, tag), 16)) 257 | return x 258 | 259 | def garble_table(labeled_table): 260 | result = [] 261 | for row in labeled_table: 262 | output_label, input_labels = row 263 | key = combine_keys(input_labels) 264 | c, tag, nonce = symmetric_enc(key, output_label) 265 | result.append((c, tag, nonce)) 266 | random.shuffle(result) # this isn't a secure shuffle 267 | return result 268 | 269 | def topoorder(circuit, inputs, outputs): 270 | postorder = [] 271 | visited = set() 272 | def visit(wire_name): 273 | if wire_name in visited: 274 | return 275 | visited.add(wire_name) 276 | if wire_name not in inputs: 277 | gate, *input_wire_names = circuit[wire_name] 278 | for input_wire in input_wire_names: 279 | visit(input_wire) 280 | postorder.append(wire_name) 281 | for input_wire in outputs: 282 | visit(input_wire) 283 | return postorder # note: dont need to reverse for topo b.c nodes point to their dependencies 284 | 285 | def garble_circuit(circuit, inputs, outputs, k=128): 286 | labels = {} 287 | garbled_tables = [] 288 | 289 | # we topologically order all the wires. there is a valid topological ordering because circuits are acyclic. 290 | # by ordering the wires, we can use the indices as unique ids to refer to each wire 291 | wires = topoorder(circuit, inputs, outputs) 292 | wire_index = {wire: i for i, wire in enumerate(wires)} 293 | 294 | for wire_name in wires: 295 | if wire_name in inputs: 296 | print('input wire:', wire_name) 297 | garbled_tables.append((None, None)) # this is an input wire, just add a palceholder value 298 | continue 299 | gate, *input_wire_names = circuit[wire_name] 300 | print(wire_name, gate, input_wire_names) 301 | labeled_table = label_truth_table(wire_name, gate, input_wire_names, labels, k) 302 | garbled_table = garble_table(labeled_table) 303 | 304 | input_wire_indexes = [wire_index[input_wire] for input_wire in input_wire_names] 305 | assert all(i < len(garbled_tables) for i in input_wire_indexes) 306 | garbled_tables.append((garbled_table, input_wire_indexes)) 307 | 308 | assert len(garbled_tables) == len(wires) 309 | 310 | return garbled_tables, labels, wire_index 311 | 312 | def eval_garbled_circuit(garbled_tables, circuit_input_labels, output_wire_indexes): 313 | evaluated_gates = [] # holds an array of the output wire's decrypted label as we progressively evaluate the circuit 314 | 315 | for i, (garbled_table, input_wire_indexes) in enumerate(garbled_tables): 316 | if i in circuit_input_labels: # this is an input wire 317 | evaluated_gates.append(circuit_input_labels[i]) 318 | continue 319 | 320 | for row in garbled_table: 321 | c, tag, nonce = row 322 | gate_input_labels = [evaluated_gates[index] for index in input_wire_indexes] 323 | key = combine_keys(gate_input_labels) 324 | try: 325 | output_label = symmetric_dec(key, c, tag, nonce) 326 | except ValueError: # incorrect padding 327 | continue 328 | evaluated_gates.append(output_label) 329 | break 330 | else: 331 | raise ValueError('unable to decrypt garbled table', i) 332 | 333 | print('evaluated gate', i, '=', output_label) 334 | 335 | assert len(evaluated_gates) == len(garbled_tables) 336 | 337 | output_labels = [evaluated_gates[i] for i in output_wire_indexes] 338 | return output_labels 339 | 340 | def wire_values(wire_name, value, bitsize): 341 | bits = bin(value)[2:].zfill(32) 342 | return {f"{wire_name}_{i}": int(bit) for i, bit in enumerate(reversed(bits))} 343 | 344 | # X is alice's input 345 | # x = number of bits in the input wire 'x' in the circuit 346 | # y = number of bits in the input wire 'y' in the circuit 347 | # n = RSA security bits 348 | # k = garbled circuits security bits (label size) 349 | def garbled_circuit_alice(circuits, input_wires, output_wires, X, x_bits=32, y_bits=32, n=2048, k=128): 350 | garbled_tables, labels, wire_index = garble_circuit(circuit, input_wires, output_wires) 351 | output_indexes = [wire_index[wire] for wire in output_wires] 352 | 353 | # {wire_name: [label_0, label_1], ...} -> {label_0: wire_name=0, label_1: wire_name=1, ...} 354 | labels_to_names = dict((v, k + '=' + str(i)) for k, v01 in labels.items() for i, v in enumerate(v01)) 355 | for k, v in labels_to_names.items(): print(k, '\t', v) 356 | 357 | # setup Alice's input wires 358 | alice_input_values = {**wire_values('x', X, x_bits)} 359 | print('alice input values:', alice_input_values) 360 | 361 | # map of wire_index -> given label (for alice's wires) 362 | alice_input_labels = {wire_index[wire]: labels[wire][alice_input_values[wire]] for wire in input_wires if wire.startswith('x_')} 363 | 364 | # bob also needs to know which wires are his inputs 365 | bob_input_indexes = [wire_index[f'y_{i}'] for i in range(y_bits)] 366 | # setup the oblivious transfer for bob's input wires 367 | ot_alices = [] 368 | e, d, N = gen_rsa_params(n) 369 | for i in range(y_bits): 370 | m0, m1 = labels[f'y_{i}'] # get the 0 and 1 labels for bob's input wire 'y' 371 | ot_alices.append(oblivious_transfer_alice(m0, m1, n, e, d, N)) 372 | 373 | # send parameters to bob and do the oblivious transfer. Bob will reply back with his output labels 374 | output_labels = yield labels, garbled_tables, alice_input_labels, bob_input_indexes, output_indexes, ot_alices 375 | 376 | # convert the labels back to plain values 377 | output = [labels_to_names[label] for label in output_labels] 378 | yield output 379 | 380 | # Y is bob's input 381 | # input_bits = number of bits in the input wire 'y' in the circuit 382 | def garbled_circuit_bob(Y, y_bits=32, n=2048, k=128): 383 | bob_input_values = {**wire_values('y', Y, y_bits)} 384 | print('bob input values:', bob_input_values) 385 | 386 | # setup the oblivious transfer for bob's input wires 387 | ot_bobs = [oblivious_transfer_bob(bob_input_values[f'y_{i}'], n) for i in range(y_bits)] 388 | 389 | # do the oblivious transfer now. Also, receive the rest of alice's parameters 390 | garbled_tables, alice_input_labels, bob_input_indexes, output_indexes, bob_input_labels = yield ot_bobs 391 | assert len(bob_input_indexes) == y_bits and len(bob_input_labels) == y_bits 392 | 393 | # boilerplate, go from a list of label values to a dict from wire to label 394 | bob_input_labels = dict(zip(bob_input_indexes, bob_input_labels)) 395 | 396 | # now we have all the input labels 397 | input_labels = {**alice_input_labels, **bob_input_labels} 398 | print('input labels:', input_labels) 399 | 400 | output_labels = eval_garbled_circuit(garbled_tables, input_labels, output_indexes) 401 | yield output_labels 402 | 403 | if __name__ == '__main__': 404 | # build with ./oss-cad-suite/bin/yosys -s yosys-script.txt 405 | circuit, input_wires, output_wires = parse_verilog('out.v') 406 | 407 | X = 9001 408 | Y = 1337 409 | 410 | # setup 411 | gc_alice = garbled_circuit_alice(circuit, input_wires, output_wires, X, x_bits=32, y_bits=32) 412 | gc_bob = garbled_circuit_bob(Y, y_bits=32) 413 | 414 | # alice garbles the circuit and prepares for an oblivious transfer of bob's input labels 415 | labels, garbled_tables, alice_input_labels, bob_input_indexes, output_indexes, ot_alices = next(gc_alice) 416 | # bob prepares for an oblivious transfer of all his input labels 417 | ot_bobs = next(gc_bob) 418 | 419 | # do the oblivious transfer of all of bobs input wire bits 420 | bob_input_labels = [oblivious_transfer(alice, bob) for alice, bob in zip(ot_alices, ot_bobs)] 421 | print('bob input labels:', bob_input_labels) 422 | # Send bob all the other params from Alice too 423 | # then Bob will run the garbled circuit 424 | output_labels = gc_bob.send((garbled_tables, alice_input_labels, bob_input_indexes, output_indexes, bob_input_labels)) 425 | print('output labels:', output_labels) 426 | 427 | # give output labels to alice to get final output 428 | output = gc_alice.send(output_labels) 429 | for val in output: 430 | print(val) 431 | 432 | exit() 433 | 434 | -------------------------------------------------------------------------------- /out.v: -------------------------------------------------------------------------------- 1 | /* Generated by Yosys 0.36+61 (git sha1 df65634e0, clang 10.0.0-4ubuntu1 -fPIC -Os) */ 2 | 3 | module test(x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, x_9, x_10, x_11, x_12, x_13, x_14, x_15, x_16, x_17, x_18, x_19, x_20 4 | , x_21, x_22, x_23, x_24, x_25, x_26, x_27, x_28, x_29, x_30, x_31, y_0, y_1, y_2, y_3, y_4, y_5, y_6, y_7, y_8, y_9 5 | , y_10, y_11, y_12, y_13, y_14, y_15, y_16, y_17, y_18, y_19, y_20, y_21, y_22, y_23, y_24, y_25, y_26, y_27, y_28, y_29, y_30 6 | , y_31, out); 7 | wire _000_; 8 | wire _001_; 9 | wire _002_; 10 | wire _003_; 11 | wire _004_; 12 | wire _005_; 13 | wire _006_; 14 | wire _007_; 15 | wire _008_; 16 | wire _009_; 17 | wire _010_; 18 | wire _011_; 19 | wire _012_; 20 | wire _013_; 21 | wire _014_; 22 | wire _015_; 23 | wire _016_; 24 | wire _017_; 25 | wire _018_; 26 | wire _019_; 27 | wire _020_; 28 | wire _021_; 29 | wire _022_; 30 | wire _023_; 31 | wire _024_; 32 | wire _025_; 33 | wire _026_; 34 | wire _027_; 35 | wire _028_; 36 | wire _029_; 37 | wire _030_; 38 | wire _031_; 39 | wire _032_; 40 | wire _033_; 41 | wire _034_; 42 | wire _035_; 43 | wire _036_; 44 | wire _037_; 45 | wire _038_; 46 | wire _039_; 47 | wire _040_; 48 | wire _041_; 49 | wire _042_; 50 | wire _043_; 51 | wire _044_; 52 | wire _045_; 53 | wire _046_; 54 | wire _047_; 55 | wire _048_; 56 | wire _049_; 57 | wire _050_; 58 | wire _051_; 59 | wire _052_; 60 | wire _053_; 61 | wire _054_; 62 | wire _055_; 63 | wire _056_; 64 | wire _057_; 65 | wire _058_; 66 | wire _059_; 67 | wire _060_; 68 | wire _061_; 69 | output out; 70 | wire out; 71 | input x_0; 72 | wire x_0; 73 | input x_1; 74 | wire x_1; 75 | input x_10; 76 | wire x_10; 77 | input x_11; 78 | wire x_11; 79 | input x_12; 80 | wire x_12; 81 | input x_13; 82 | wire x_13; 83 | input x_14; 84 | wire x_14; 85 | input x_15; 86 | wire x_15; 87 | input x_16; 88 | wire x_16; 89 | input x_17; 90 | wire x_17; 91 | input x_18; 92 | wire x_18; 93 | input x_19; 94 | wire x_19; 95 | input x_2; 96 | wire x_2; 97 | input x_20; 98 | wire x_20; 99 | input x_21; 100 | wire x_21; 101 | input x_22; 102 | wire x_22; 103 | input x_23; 104 | wire x_23; 105 | input x_24; 106 | wire x_24; 107 | input x_25; 108 | wire x_25; 109 | input x_26; 110 | wire x_26; 111 | input x_27; 112 | wire x_27; 113 | input x_28; 114 | wire x_28; 115 | input x_29; 116 | wire x_29; 117 | input x_3; 118 | wire x_3; 119 | input x_30; 120 | wire x_30; 121 | input x_31; 122 | wire x_31; 123 | input x_4; 124 | wire x_4; 125 | input x_5; 126 | wire x_5; 127 | input x_6; 128 | wire x_6; 129 | input x_7; 130 | wire x_7; 131 | input x_8; 132 | wire x_8; 133 | input x_9; 134 | wire x_9; 135 | input y_0; 136 | wire y_0; 137 | input y_1; 138 | wire y_1; 139 | input y_10; 140 | wire y_10; 141 | input y_11; 142 | wire y_11; 143 | input y_12; 144 | wire y_12; 145 | input y_13; 146 | wire y_13; 147 | input y_14; 148 | wire y_14; 149 | input y_15; 150 | wire y_15; 151 | input y_16; 152 | wire y_16; 153 | input y_17; 154 | wire y_17; 155 | input y_18; 156 | wire y_18; 157 | input y_19; 158 | wire y_19; 159 | input y_2; 160 | wire y_2; 161 | input y_20; 162 | wire y_20; 163 | input y_21; 164 | wire y_21; 165 | input y_22; 166 | wire y_22; 167 | input y_23; 168 | wire y_23; 169 | input y_24; 170 | wire y_24; 171 | input y_25; 172 | wire y_25; 173 | input y_26; 174 | wire y_26; 175 | input y_27; 176 | wire y_27; 177 | input y_28; 178 | wire y_28; 179 | input y_29; 180 | wire y_29; 181 | input y_3; 182 | wire y_3; 183 | input y_30; 184 | wire y_30; 185 | input y_31; 186 | wire y_31; 187 | input y_4; 188 | wire y_4; 189 | input y_5; 190 | wire y_5; 191 | input y_6; 192 | wire y_6; 193 | input y_7; 194 | wire y_7; 195 | input y_8; 196 | wire y_8; 197 | input y_9; 198 | wire y_9; 199 | assign _028_ = ~(x_21 | x_20); 200 | assign _029_ = ~(x_23 | x_22); 201 | assign _030_ = _028_ & _029_; 202 | assign _031_ = ~(x_17 | x_16); 203 | assign _032_ = ~(x_19 | x_18); 204 | assign _033_ = _031_ & _032_; 205 | assign _034_ = _030_ & _033_; 206 | assign _035_ = ~(x_29 | x_28); 207 | assign _036_ = ~(x_31 | x_30); 208 | assign _037_ = _035_ & _036_; 209 | assign _038_ = ~(x_25 | x_24); 210 | assign _039_ = ~(x_27 | x_26); 211 | assign _040_ = _038_ & _039_; 212 | assign _041_ = _037_ & _040_; 213 | assign _042_ = _034_ & _041_; 214 | assign _043_ = x_5 & ~(x_4); 215 | assign _044_ = ~(x_7 | x_6); 216 | assign _045_ = _043_ & _044_; 217 | assign _046_ = x_0 & ~(x_1); 218 | assign _047_ = x_3 & ~(x_2); 219 | assign _048_ = _046_ & _047_; 220 | assign _049_ = _045_ & _048_; 221 | assign _050_ = x_13 & ~(x_12); 222 | assign _051_ = ~(x_15 | x_14); 223 | assign _052_ = _050_ & _051_; 224 | assign _053_ = x_9 & x_8; 225 | assign _054_ = ~(x_11 | x_10); 226 | assign _055_ = _053_ & _054_; 227 | assign _056_ = _052_ & _055_; 228 | assign _057_ = _049_ & _056_; 229 | assign _058_ = _042_ & _057_; 230 | assign _059_ = ~(y_21 | y_20); 231 | assign _060_ = ~(y_23 | y_22); 232 | assign _061_ = _059_ & _060_; 233 | assign _000_ = ~(y_17 | y_16); 234 | assign _001_ = ~(y_19 | y_18); 235 | assign _002_ = _000_ & _001_; 236 | assign _003_ = _061_ & _002_; 237 | assign _004_ = ~(y_29 | y_28); 238 | assign _005_ = ~(y_31 | y_30); 239 | assign _006_ = _004_ & _005_; 240 | assign _007_ = ~(y_25 | y_24); 241 | assign _008_ = ~(y_27 | y_26); 242 | assign _009_ = _007_ & _008_; 243 | assign _010_ = _006_ & _009_; 244 | assign _011_ = _003_ & _010_; 245 | assign _012_ = y_5 & y_4; 246 | assign _013_ = ~(y_7 | y_6); 247 | assign _014_ = _012_ & _013_; 248 | assign _015_ = y_0 & ~(y_1); 249 | assign _016_ = y_3 & ~(y_2); 250 | assign _017_ = _015_ & _016_; 251 | assign _018_ = _014_ & _017_; 252 | assign _019_ = ~(y_13 | y_12); 253 | assign _020_ = ~(y_15 | y_14); 254 | assign _021_ = _019_ & _020_; 255 | assign _022_ = y_8 & ~(y_9); 256 | assign _023_ = y_10 & ~(y_11); 257 | assign _024_ = _022_ & _023_; 258 | assign _025_ = _021_ & _024_; 259 | assign _026_ = _018_ & _025_; 260 | assign _027_ = _011_ & _026_; 261 | assign out = _058_ & _027_; 262 | endmodule 263 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome 2 | -------------------------------------------------------------------------------- /sieve.py: -------------------------------------------------------------------------------- 1 | import math 2 | wow = [2] 3 | for i in range(3,2000000): 4 | is_prime = True 5 | for p in wow: 6 | if p > int(math.sqrt(i)) + 1 or p > i: 7 | break 8 | 9 | if i % p == 0: 10 | is_prime = False 11 | break 12 | if is_prime: 13 | wow.append(i) 14 | print(i) 15 | -------------------------------------------------------------------------------- /yosys-script.txt: -------------------------------------------------------------------------------- 1 | read_verilog circuit.v 2 | synth 3 | abc -g gates 4 | splitnets -ports -format _ 5 | clean_zerowidth 6 | clean -purge 7 | write_verilog -simple-lhs -noattr out.v 8 | --------------------------------------------------------------------------------