def flip_a5_bit(X, a5i, check=True): s0 = md4.INITIAL_STATE [s1, s2, s3, s4] = md4.do_round1(X, s0) a1, _, _, _ = s1 # a5i - 3 + 3 a1_new = flip_nth_bit(a1, a5i) s1_new = [a1_new, s1[1], s1[2], s1[3]] X_new = invert_round1(s0, [s1_new, s2, s3, s4]) if not check: return X_new for i in range(5, 16): assert_word_eq(X_new[i], X[i]) round1_states_new = md4.do_round1(X_new, s0) expected_round1_states = [s1_new, s2, s3, s4] for i in range(4): if round1_states_new[i] != expected_round1_states[i]: raise Exception('expected s[{}]={}, got {}'.format(i, dump_s(expected_round1_states[i]), dump_s(round1_states_new[i]))) round2_states_new = md4.do_round2(X_new, round1_states_new[-1]) [s5, s6, s7, s8] = md4.do_round2(X, s4) expected_round1_states = [round2_states_new[0], s6, s7, s8] a5_new, _, _, _ = round2_states_new[0] a5, _, _, _ = s5 if nth_bit(a5_new, a5i) == nth_bit(a5, a5i): raise Exception('a5 bit {} not flipped'.format(a5i)) return X_new
def flip_d5_bit(X, d5i, check=True): s0 = md4.INITIAL_STATE [s1, s2, s3, s4] = md4.do_round1(X, s0) a2, _, _, _ = s2 # d5i - 5 + 3 a2_new = flip_nth_bit(a2, d5i - 2) s2_new = [a2_new, s2[1], s2[2], s2[3]] X_new = invert_round1(s0, [s1, s2_new, s3, s4]) if not check: return X_new for i in list(range(0, 4)) + list(range(9, 16)): assert_word_eq(X_new[i], X[i]) round1_states_new = md4.do_round1(X_new, s0) expected_round1_states = [s1, s2_new, s3, s4] for i in range(4): if round1_states_new[i] != expected_round1_states[i]: raise Exception('expected s[{}]={}, got {}'.format(i, dump_s(expected_round1_states[i]), dump_s(round1_states_new[i]))) round2_states_new = md4.do_round2(X_new, round1_states_new[-1]) [s5, s6, s7, s8] = md4.do_round2(X, s4) expected_round1_states = [round2_states_new[0], s6, s7, s8] _, _, _, d5_new = round2_states_new[0] _, _, _, d5 = s5 if nth_bit(d5_new, d5i) == nth_bit(d5, d5i): raise Exception('d5 bit {} not flipped'.format(d5i)) return X_new
def do_multi_step_mod(words, check=True): round1_states = md4.do_round1(words) _, _, _, c4 = round1_states[-1] words = do_a5_mod(words, 18, nth_bit(c4, 18), check) words = do_a5_mod(words, 25, 1, check) words = do_a5_mod(words, 26, 0, check) words = do_a5_mod(words, 28, 1, check) words = do_a5_mod(words, 31, 1, check) if check: assert_collidable_round1(words, extra=False) assert_collidable_round2_a5(words) round1_states = md4.do_round1(words) _, b4, _, _ = round1_states[-1] round2_states = md4.do_round2(words, round1_states[-1]) a5, b5, c5, d5 = round2_states[0] words = do_d5_mod(words, 18, nth_bit(a5, 18), check) words = do_d5_mod(words, 25, nth_bit(b4, 25), check) words = do_d5_mod(words, 26, nth_bit(b4, 26), check) words = do_d5_mod(words, 28, nth_bit(b4, 28), check) if check: assert_collidable_round1(words, extra=False) assert_collidable_round2_a5(words) assert_collidable_round2_d5(words) round1_states = md4.do_round1(words) _, b4, _, _ = round1_states[-1] round2_states = md4.do_round2(words, round1_states[-1]) a5, b5, c5, d5 = round2_states[0] words = do_c5_mod(words, 25, nth_bit(d5, 25), check) words = do_c5_mod(words, 26, nth_bit(d5, 26), check) # skip 28 for now # words = do_c5_mod(words, 28, nth_bit(d5, 28), check) words = do_c5_mod(words, 31, nth_bit(d5, 31), check) if check: assert_collidable_round1(words, extra=False) assert_collidable_round2_a5(words) assert_collidable_round2_d5(words) assert_collidable_round2_c5(words) return words
def flip_c5_bit(X, c5i, check=True): s0 = md4.INITIAL_STATE [s1, s2, s3, s4] = md4.do_round1(X, s0) _, a2, _, d2 = s2 # ? d2_new = flip_nth_bit(d2, c5i - 9) if c5i == 28: a2_new = flip_nth_bit(a2, c5i - 9) else: a2_new = a2 s2_new = [s2[0], a2_new, s2[2], d2_new] X_new = invert_round1(s0, [s1, s2_new, s3, s4]) if not check: return X_new for i in list(range(0, 4)) + list(range(12, 16)): assert_word_eq(X_new[i], X[i]) if a2_new == a2: for i in [4, 10, 11]: assert_word_eq(X_new[i], X[i]) [s5, s6, s7, s8] = md4.do_round2(X, s4) round1_states_new = md4.do_round1(X_new, s0) expected_round1_states = [s1, s2_new, s3, s4] for i in range(4): if round1_states_new[i] != expected_round1_states[i]: raise Exception('expected s[{}]={}, got {}'.format( i, dump_s(expected_round1_states[i]), dump_s(round1_states_new[i]))) round2_states_new = md4.do_round2(X_new, round1_states_new[-1]) [s5, s6, s7, s8] = md4.do_round2(X, s4) expected_round1_states = [round2_states_new[0], s6, s7, s8] _, _, c5_new, _ = round2_states_new[0] _, _, c5, _ = s5 if nth_bit(c5_new, c5i) == nth_bit(c5, c5i): raise Exception('c5 bit {} not flipped'.format(c5i)) return X_new
def assert_collidable_round3(words): round1_states = md4.do_round1(words) round2_states = md4.do_round2(words, round1_states[-1]) round3_states = md4.do_round3(words, round2_states[-1]) _, b9, _, _ = round3_states[0] a10, _, _, _ = round3_states[1] assert_bit1(b9, 32, 1) assert_bit1(a10, 32, 1)
def do_a5_mod(words, a5i, b, check=True): if check: assert_collidable_round1(words, extra=False) round1_states = md4.do_round1(words) round2_states = md4.do_round2(words, round1_states[-1]) a5, b5, c5, d5 = round2_states[0] if nth_bit(a5, a5i) == b: return words words_new = flip_a5_bit(words, a5i, check) if check: assert_collidable_round1(words_new, extra=False) assert_collidable_round2_a5(words_new, a5i) return words_new
def assert_collidable_round2_d5(words, d5i=None): round1_states = md4.do_round1(words) a4, b4, c4, d4 = round1_states[-1] round2_states = md4.do_round2(words, round1_states[-1]) a5, b5, c5, d5 = round2_states[0] if d5i is None or d5i >= 18: assert_bit(d5, 18, nth_bit(a5, 18)) if d5i is None or d5i >= 25: assert_bit(d5, 25, nth_bit(b4, 25)) if d5i is None or d5i >= 26: assert_bit(d5, 26, nth_bit(b4, 26)) if d5i is None or d5i >= 28: assert_bit(d5, 28, nth_bit(b4, 28))
def assert_collidable_round2_c5(words, c5i=False): round1_states = md4.do_round1(words) a4, b4, c4, d4 = round1_states[-1] round2_states = md4.do_round2(words, round1_states[-1]) a5, b5, c5, d5 = round2_states[0] if c5i is None or c5i >= 25: assert_bit(c5, 25, nth_bit(d5, 25)) if c5i is None or c5i >= 26: assert_bit(c5, 26, nth_bit(d5, 26)) # skip 28 for now # if c5i is None or c5i >= 28: # assert_bit(c5, 28, nth_bit(d5, 28)) if c5i is None or c5i >= 31: assert_bit(c5, 31, nth_bit(d5, 31))
def assert_collidable_round2(words): round1_states = md4.do_round1(words) a4, b4, c4, d4 = round1_states[-1] round2_states = md4.do_round2(words, round1_states[-1]) a5, b5, c5, d5 = round2_states[0] a6, b6, c6, d6 = round2_states[1] assert_bit1(a5, 19, c4, 19) assert_bit1(a5, 26, 1) assert_bit1(a5, 27, 0) assert_bit1(a5, 29, 1) assert_bit1(a5, 32, 1) assert_bit1(d5, 19, a5, 19) assert_bit1(d5, 26, b4, 26) assert_bit1(d5, 27, b4, 27) assert_bit1(d5, 29, b4, 29) assert_bit1(d5, 32, b4, 32) assert_bit1(c5, 26, d5, 26) assert_bit1(c5, 27, d5, 27) assert_bit1(c5, 29, d5, 29) assert_bit1(c5, 30, d5, 30) assert_bit1(c5, 32, d5, 32) assert_bit1(b5, 29, c5, 29) assert_bit1(b5, 30, 1) assert_bit1(b5, 32, 0) assert_bit1(a6, 29, 1) assert_bit1(a6, 32, 1) # From https://eprint.iacr.org/2005/151.pdf assert_bit1(a6, 30, 0) assert_bit1(d6, 29, b5, 29) assert_bit1(c6, 29, d6, 29) assert_bit1(c6, 30, (1 + nth_bit(d6, 30 - 1)) % 2) assert_bit1(c6, 32, (1 + nth_bit(d6, 32 - 1)) % 2)
def assert_collidable_round2_a5(words, a5i=None): round1_states = md4.do_round1(words) a4, b4, c4, d4 = round1_states[-1] round2_states = md4.do_round2(words, round1_states[-1]) a5, b5, c5, d5 = round2_states[0] if a5i is None or a5i >= 18: assert_bit(a5, 18, nth_bit(c4, 18)) if a5i is None or a5i >= 25: assert_bit(a5, 25, 1) if a5i is None or a5i >= 26: assert_bit(a5, 26, 0) if a5i is None or a5i >= 28: assert_bit(a5, 28, 1) if a5i is None or a5i >= 31: assert_bit(a5, 31, 1)