def report(cand_single): most_probable = [] for i in range(nblock): top_cands = sorted(enumerate(cand_single[i]), key=lambda x: abs(x[1]))[::-1][:5] # color_str = '\033[01;33m' if i in sboxs_pats else '\033[01m' color_str = '\033[01;33m' if 0 else '\033[01m' print(f'{color_str}#{i:2d}\033[0m: ', end='') if not top_cands or top_cands[0][1] == 0: print('N/A') continue else: best_cand, _ = top_cands[0] most_probable.append(int2bits(best_cand, sbits)) for char, cnt in top_cands: print(f'[\033[01;34m{hex(char)[2:]}\033[0m {cnt:6d}] ', end='') print() if not is_revealing_last_round and len(most_probable) == nblock: result = ''.join(most_probable) result = cipher.permute(result, cipher.pbox) result = hex(int(result, 2))[2:] print(f'Most probable result: \033[01;32m{result}\033[01;0m')
def simulate(path_spec): counters_single = __utils.counters_single fins, fouts, bias = path_spec sboxs_o_pats = group_by_sboxs(fouts) output_sboxs = sboxs_o_pats.items() sboxs_a_pats = sboxs_o_pats affected_sboxs = output_sboxs if len(affected_sboxs) > 2: return print('Simulating', fins, '->', fouts, 'affecting sbox', ','.join(map(str, sboxs_a_pats.keys())), 'bias', bias) # itertools.product(*[range(16) for _ in affected_sboxs]) for target_subkeys in itertools.product( *[range(N) for _ in affected_sboxs]): cnt = 0 for xx, yy in data: xored = 0 # xor fan-ins for i in fins: xored ^= int(xx[i], 2) # construct masked key blocked_key = ['0' * sbits] * nblock for (blkid, _), subkey in zip(affected_sboxs, target_subkeys): blocked_key[blkid] = int2bits(subkey, sbits) key = ''.join(blocked_key) # if not is_revealing_last_round: # key = cipher.substitute(key, cipher.sbox) # partial decrypts affected sboxs yy_rev = yy # add round key yy_rev = cipher.add_round_key(yy_rev, key) # inverse S-box yy_rev = cipher.substitute(yy_rev, cipher.isbox) for (blkid, pat) in output_sboxs: blk_base = blkid * sbits blk_rev = yy_rev[blk_base:(blk_base + sbits)] # xor fan-outs for offs, bit in enumerate(int2bits(pat, sbits)): if int(bit, 2): xored ^= int(blk_rev[offs], 2) # (0, 1) -> (-1, 1) cnt += ((xored << 1) - 1) # makes biases add-able by converting them positive if len(affected_sboxs) == 1: ia, = sboxs_a_pats.keys() ka, = target_subkeys counters_single[ia][ka] += abs(cnt) # cand_single[ia].increment(ka, abs(cnt)) elif len(affected_sboxs) == 2: ia, ib = sboxs_a_pats.keys() ka, kb = target_subkeys counters_single[ia][ka] += abs(cnt) counters_single[ib][kb] += abs(cnt) # __utils.counters_double[(ia, ib)].increment((ka << 4) + kb, abs(cnt)) # cand_single[ia].increment(ka, abs(cnt)) # cand_single[ib].increment(kb, abs(cnt)) else: break
y = cipher.permute(y, cipher.ipbox) if i != nround - 1: y = cipher.permute(y, cipher.ipbox) # if nround == 0: # print('the key is', int(cipher.add_round_key(x, y), 2).to_bytes(nbits // 8, 'little')) return y nblock = cipher.nblock round_keys_inv = list( map( lambda x: int2bits(x, nbits), [ # placeholders... # 0x4dbf_cb9c_3d81_ff1b, # 0x0000_0000_0000_0000, # 0x1111_1111_1111_1111, # 0x2222_2222_2222_2222, # 0x3333_3333_3333_3333, ])) is_revealing_last_round = False nlevels = 3 assert (nlevels == nround - 1) == (is_revealing_last_round) assert len(round_keys_inv) + nlevels == nround - 1
def transform_input_hex(x, nbits): x = bytes.fromhex(x) x = int.from_bytes(x, 'little') x = int2bits(x, nbits) return x
def pat_to_pins(pat, base): l = int2bits(pat, 4) return [(base * 4 + i) for i, v in enumerate(l) if v == '1']
pa for pa in paths if (pa.bias == paths[0].bias and pa.bias != 0) ] best_paths.extend(round_best_paths) best_paths.sort(key=attrgetter('abs_bias'), reverse=True) for pa in best_paths: print(pa) exit() counts = np.load('round5_7_10.npy') counts -= 500 counts = np.abs(counts) counts /= 1000 max_idx = np.argmax(counts) print(counts[max_idx]) print(int2bits(max_idx, 12)) # counts = np.sort(counts) for i in range(len(counts)): if (counts[i] > 0.05): print(i) print(counts[i]) print(int2bits(i, 12)) exit() # -- Start Attack -- # # last round key candidate = best_paths[1] n = len(candidate.u) print(candidate) counts = np.zeros(1 << (sbits * n)) for k in range(1 << (sbits * n)): keys = [
# # counts = np.sort(counts) # for i in range(len(counts)): # if(counts[i] > 0.05): # print(i) # print(counts[i]) # print(int2bits(i, 12)) # exit() # -- Start Attack -- # # last round key candidate = best_paths[0] n = len(candidate.u) print(candidate) counts = np.zeros(1 << (sbits * n)) for k in range(1 << (sbits * n)): keys = [ int2bits(k, sbits * n)[i:i + sbits] for i in range(0, sbits * n, sbits) ] idx = list(candidate.u.keys()) test_key = key2int(keys, idx, sbits, nbits) # all known plaintext pairs for x, y in data: xx = int.from_bytes(x, "little") yy = int.from_bytes(y, "little") result = 0 # plaintext bits needed for p in candidate.p: result ^= int(int2bits(xx, nbits)[p], 2)