def send_hmac_C(salt, B): xH = SHA256_hex(salt + password) x = int(xH, 16) u = int(SHA256_hex(util.int_to_bytes(A_C) + util.int_to_bytes(B)), 16) S = pow(B, a + u * x, N) K = SHA256(util.int_to_bytes(S)) return HMAC.HMAC(SHA256, K, salt)
def search_for_pkcs_messages(i, c0, M, s): e, n = public_key if i == 1: # 2.a s_new = ceil_div(n, 3 * B) while True: c = compute_homomorphic_c(c0, s_new) if pkcs_oracle(util.int_to_bytes(c)): return s_new s_new += 1 elif len(M) > 1: # 2.b s_new = s + 1 while True: c = compute_homomorphic_c(c0, s_new) if pkcs_oracle(util.int_to_bytes(c)): return s_new s_new += 1 else: # 2.c (a, b) = M[0] r = 2 * ceil_div(b * s - 2 * B, n) while True: s_left = ceil_div(2 * B + r * n, b) s_right = ceil_div(3 * B + r * n, a) if s_left >= s_right: r += 1 continue for s_new in range(s_left, s_right): c = compute_homomorphic_c(c0, s_new) if pkcs_oracle(util.int_to_bytes(c)): return s_new r += 1 # in theory, we should never get here return None
def dictionary_attack(hmac, salt, A, B): global b u = int(SHA256_hex(util.int_to_bytes(A) + util.int_to_bytes(B)), 16) with open('/usr/share/dict/words', 'r') as f: for word in f: for d in range(10): guess = (word.strip() + str(d)).encode('utf-8') xH = SHA256_hex(salt + guess) x = int(xH, 16) v = pow(g, x, N) S = pow((A * pow(v, u, N)), b, N) K = SHA256(util.int_to_bytes(S)) if hmac == HMAC.HMAC(SHA256, K, salt): return guess
def forge_signature(message, key): h = SHA.new(message).hexdigest() asn_prefix = '3021300906052b0e03021a05000414' desired_message = '01ff00' + asn_prefix + h + 'ff' * 80 m = int(desired_message, 16) s = util.iroot(3, m) signature = util.int_to_bytes(s) return signature
def send_message_B(ciphertext, iv): global s_B, A_B, b, p s_B = pow(A_B, b, p) key = hashes.SHA1(util.int_to_bytes(s_B)) key = util.get_ith_block(key, 0, BLOCK_SIZE) plaintext = util.cbc_decrypt(ciphertext, key, iv) assert plaintext == MESSAGE iv = util.random_byte_string(BLOCK_SIZE) return (util.cbc_encrypt(plaintext, key, iv), iv)
def send_message_A(B): global s_A, a, p, B_A B_A = B s_A = pow(B_A, a, p) msg = MESSAGE key = hashes.SHA1(util.int_to_bytes(s_A)) key = util.get_ith_block(key, 0, BLOCK_SIZE) iv = util.random_byte_string(BLOCK_SIZE) return (util.cbc_encrypt(msg, key, iv), iv)
def md_hash(message, state_len = STATE_LEN, H = None): # initial state h = b''.join([util.int_to_bytes((37*i + 42) % 256) for i in range(state_len)]) if not H: H = h M = util.padding(message, AES_BLOCK_SIZE) for i in range(len(M)//AES_BLOCK_SIZE): Mi = util.get_ith_block(M, i, AES_BLOCK_SIZE) H = util.ecb_encrypt(Mi, util.padding(H, AES_BLOCK_SIZE))[0:state_len] return binascii.hexlify(H)
def generate_many_collisions(rounds): h = b''.join([util.int_to_bytes((37*i + 42) % 256) for i in range(STATE_LEN)]) colliding_messages = set() colliding_messages.add(b'') for i in range(rounds): new_set = set() m1, m2, h = find_block_collision(h) m1 = util.padding(m1, AES_BLOCK_SIZE) m2 = util.padding(m2, AES_BLOCK_SIZE) for m in colliding_messages: new_set.add(m + m1) new_set.add(m + m2) colliding_messages = new_set return colliding_messages
def recover_message(ciphertext): global public_key e, n = public_key c = int(binascii.hexlify(ciphertext), 16) x = pow(2, e, n) left = 0 right = n - 1 for i in range(1, KEY_SIZE + 1): y = pow(x, i, n) r = (c * y) % n b = return_parity(util.int_to_bytes(r)) l = (left * pow(2, i - 1, n)) % n z = (n - 2 * l - 1) // pow(2, i) mid = left + z if b == 0: right = mid else: left = mid + 1 m = left return util.int_to_bytes(m)
def broadcast_attack(c1, c2, c3, p1, p2, p3): _, n1 = p1 _, n2 = p2 _, n3 = p3 c1 = int(binascii.hexlify(c1), 16) c2 = int(binascii.hexlify(c2), 16) c3 = int(binascii.hexlify(c3), 16) x1 = c1 * n2 * n3 * util.modinv(n2 * n3, n1) x2 = c2 * n3 * n1 * util.modinv(n3 * n1, n2) x3 = c3 * n1 * n2 * util.modinv(n1 * n2, n3) m_cubed = (x1 + x2 + x3) % (n1 * n2 * n3) m = util.iroot(3, m_cubed) return util.int_to_bytes(m)
def faulty_verifier(message, signature, key): n = key.n e = 3 s = int(binascii.hexlify(signature), 16) m = util.int_to_bytes(pow(s, e, n)) r = re.match(br'01(ff)+00', binascii.hexlify(m)) if not r: return False asn = binascii.unhexlify(binascii.hexlify(m)[r.end():]) try: sequence = asn1.DerSequence() sequence.decode(asn) assert len(sequence) == 2 assert len(sequence[1]) == 20 + 2 found_hash = sequence[1][2:] except (ValueError, IndexError) as e: return False expected_hash = SHA.new(message).digest() return expected_hash == found_hash
def recover_message(ciphertext): # Setup global B B = pow(2, 8 * (k - 2)) # Step 1 # Our plaintext is already PKCS1.5 padded, so the blinding # is not necessary c = int(binascii.hexlify(ciphertext), 16) s = 1 M = [(2 * B, 3 * B - 1)] i = 1 while True: # Step 2 s = search_for_pkcs_messages(i, c, M, s) # Step 3 M = update_interval(M, s) # Step 4 if len(M) == 1 and M[0][0] == M[0][1]: plaintext = util.int_to_bytes(M[0][0]) return pkcs_unpad(plaintext) i += 1
from utilities import util import binascii # Challenge 54 STATE_LEN = 4 # 32 bits BLOCK_SIZE = 16 # 128 bits LEN_ENC_SIZE = 8 # 64 bits initial_state = b''.join( [util.int_to_bytes((37 * i + 42) % 256) for i in range(STATE_LEN)]) # Notes # - Hash functions are sometimes used as proof of a secret prediction. A # naive forgery would require a second pre-image attack. # - We (again) exploit the difference in difficulty between collisions # and second pre-images for this attack. We also exploit the ability # to precompute a lot of collisions. # - We create a funnel-like structure to hash many possible initial states # into one single final state # - The dummy hash function we use here has the following properties: # * 32 bit state # * 128 bit block # * 64 bit length encoding # Finding a second pre-image requires 2^32 operations (considered # infeasible in terms of programming competitions), but finding a # collision requires only 2^16 operations, which is comparatively trivial. # - If we have enough leaves in our funnel (say, 2^10 = 1024), finding # a collision takes only 2^22 time. # - We'll use the following list of spoilers below as our 'prediction'.
def encrypt(m, public_key): m_num = int(binascii.hexlify(m), 16) c_num = encrypt_num(m_num, public_key) return util.int_to_bytes(c_num)
def verify_hmac_S(hmac): u = int(SHA256_hex(util.int_to_bytes(A_S) + util.int_to_bytes(B)), 16) S = pow((A_S * pow(v, u, N)), b, N) K = SHA256(util.int_to_bytes(S)) return hmac == HMAC.HMAC(SHA256, K, salt)
def decrypt(c, private_key): c_num = int(binascii.hexlify(c), 16) m_num = decrypt_num(c_num, private_key) return util.int_to_bytes(m_num)
B_B = dh.send_params_B(p, g, A_A) (ciphertext, iv) = dh.send_message_A(B_B) (ciphertext, iv) = dh.send_message_B(ciphertext, iv) print('Successfully executed DH Protocol') # Simulate MITM Attack on DH Protocol # by messing with the 'g' parameter # g = 1 (p, g, A_A) = dh.send_params_A() B_B = dh.send_params_B(p, 1, 1) (ciphertext, iv) = dh.send_message_A(B_B) (_, _) = dh.send_message_B(ciphertext, iv) s = 1 key = hashes.SHA1(util.int_to_bytes(s)) key = util.get_ith_block(key, 0, dh.BLOCK_SIZE) plaintext = util.cbc_decrypt(ciphertext, key, iv) assert plaintext == dh.MESSAGE # g = p (p, g, A_A) = dh.send_params_A() B_B = dh.send_params_B(p, p, 0) (ciphertext, iv) = dh.send_message_A(B_B) (_, _) = dh.send_message_B(ciphertext, iv) s = 0 key = hashes.SHA1(util.int_to_bytes(s)) key = util.get_ith_block(key, 0, dh.BLOCK_SIZE) plaintext = util.cbc_decrypt(ciphertext, key, iv) assert plaintext == dh.MESSAGE
from utilities import util import binascii # Challenge 53 STATE_LEN = 4 # 32 bits BLOCK_SIZE = 16 # 128 bits LEN_ENC_SIZE = 8 # 64 bits initial_state = b''.join([util.int_to_bytes((37*i + 42) % 256) for i in range(STATE_LEN)]) # Notes # - Modern hash functions include the length of the message (modulo some huge # value) in the padding. This makes some conventional attacks a little # trickier. # - Collisions are much easier to find than second pre-images (birthday # paradox). Finding an expandable message requires many *collisions*. # Colliding with an intermediate state of a long message requires a # second pre-image of any of the intermediate states # - The dummy hash function we use here has the following properties: # * 32 bit state # * 128 bit block # * 64 bit length encoding # So, finding a second pre-image requires 2^32 operations (considered # infeasible in terms of programming competitions), but finding a # collision requires only 2^16 operations, which is comparatively trivial. # - If we have a long enough message, say 10000 blocks long, we can collide # with any of the intermediate blocks requiring only 2^32 / 10000 operations # which is feasible. # - We'll use the complete text of Arthur Conan Doyle's *The Hound of the # Baskervilles* as our long text. It's about 20000 blocks long.
def send_hmac_M(salt, B): K = srp.SHA256(util.int_to_bytes(0)) return HMAC.HMAC(srp.SHA256, K, salt)