Advertisement
CSenshi

Cryptography - HW2.1 (decipher.py)

Jan 8th, 2020
502
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.55 KB | None | 0 0
  1. import time
  2. import sys
  3. import oracle as O
  4. import itertools
  5.  
  6. ORACLE_FALSE = 0
  7. ORACLE_TRUE = 1
  8.  
  9. LOG = True
  10.  
  11. ''' Save ranges for most common characters'''
  12. # list of ranges
  13. ords = [[ord(' ')],                                 # Space
  14.         range(ord('a'), ord('z') + 1),              # LowerCase Letters
  15.         range(ord('A'), ord('Z') + 1),              # UpperCase Letters
  16.         range(ord('0'), ord('9') + 1),              # Numbers
  17.         [44, 46, 33, 63, 40, 41, 39, 34],           # Punctuation Marks
  18.         range(58, 63), range(35, 39),               # other common ranges
  19.         [42, 43, 45, 47, 64],
  20.         range(91, 97), range(123, 127)]
  21.  
  22. # convert to one whole range
  23. common_chars_range = []
  24. for x in ords:
  25.     common_chars_range += x
  26.  
  27. ''' Range for last block [1..15] '''
  28. last_char_odds = range(1, 16)
  29.  
  30. ''' Ranges left with no occurances '''
  31. common_chars_set = set(common_chars_range)
  32. all_chars_set = set(list(range(256)))
  33. left_chars_set = all_chars_set - common_chars_set
  34. left_chars_range = list(left_chars_set)
  35.  
  36.  
  37. def _log(s):
  38.     # only log when LOG variable is set on True
  39.     if LOG:
  40.         print s
  41.  
  42.  
  43. def remove_pad(block):
  44.     # in the last byte is number of padded bytes
  45.     bytes_to_remove = block[-1]
  46.     # remove last n bytes
  47.     block_without_pad = block[: - bytes_to_remove]
  48.  
  49.     return block_without_pad
  50.  
  51.  
  52. def convert_to_string(result):
  53.     # remove padding
  54.     result[-1] = remove_pad(result[-1])
  55.  
  56.     plain_text = ''
  57.     for chunk in result:
  58.         # convert integers to characters
  59.         chunk_chars = list(map(chr, chunk))
  60.         # get current string
  61.         cur_str = ''.join(chunk_chars)
  62.         # append to result
  63.         plain_text += cur_str
  64.  
  65.     return plain_text
  66.  
  67.  
  68. def decrypt_byte(IV, block, byte_ind, chunk_len, range_to_iter, to_xor=0):
  69.     n = (chunk_len - byte_ind) ^ to_xor
  70.  
  71.     # function that takes range to iterate over
  72.     def _iter_over_range(current_range):
  73.         for curr_byte in current_range:
  74.             IV[byte_ind] = curr_byte ^ n
  75.  
  76.             # check if it is correct decoding
  77.             if O.Oracle_Send((IV+block), 2) == ORACLE_FALSE:
  78.                 continue
  79.  
  80.             # if not last byte return answer
  81.             if byte_ind != chunk_len - 1:
  82.                 return curr_byte
  83.  
  84.             # if it's last byte increase IV[-2] byte by 1 (for checking purposes)
  85.             IV_new = IV[:-2] + [(-IV[-2]+1) % 256] + [curr_byte ^ n]
  86.             # if with this IV its true than last byte guess was rigth
  87.             if O.Oracle_Send((IV_new + block), 2) == ORACLE_TRUE:
  88.                 return curr_byte
  89.         return None
  90.  
  91.     byte_found = _iter_over_range(range_to_iter)
  92.     # if we found result in common characters return result
  93.     if byte_found:
  94.         return byte_found
  95.     # at this point we need to iterate over uncommon characters
  96.     return _iter_over_range(left_chars_range)
  97.  
  98.  
  99. def decrypt_chunk(IV, cur, chunk_len, is_last):
  100.     # Initialize decrypted bytes with 0's
  101.     decrypted_bytes = [0] * chunk_len
  102.  
  103.     # EXTRA (to work faster)
  104.     # if it's last block then it has padding, so let's find only last block
  105.     # and we will know that last n bytes will be same
  106.     if is_last:
  107.         # decrypt current byte
  108.         cur_byte = decrypt_byte(
  109.             IV[:], cur, chunk_len-1, chunk_len, last_char_odds, IV[(chunk_len-1)])
  110.  
  111.         # update i'th byte of
  112.         n = decrypted_bytes[chunk_len-1] = cur_byte
  113.  
  114.         # assign values of n to last n elements of array
  115.         for i in range(n):
  116.             decrypted_bytes[-(i+1)] = n
  117.         _log("Last Block --> Change {} Bytes : {}".format(n, decrypted_bytes))
  118.  
  119.     # Iterate and evaluate for each byt
  120.     for i in reversed(range(chunk_len - decrypted_bytes[-1])):
  121.         # Change IV (xor in such way that last n byte contains integer "n" in them)
  122.         for j in range(i + 1, chunk_len):
  123.             IV[j] = IV[j] ^ decrypted_bytes[j] ^ (chunk_len - i)
  124.  
  125.         # decrypt current byte
  126.         cur_byte = decrypt_byte(
  127.             IV[:], cur, i, chunk_len, common_chars_range, IV[i])
  128.  
  129.         # update i'th byte of
  130.         decrypted_bytes[i] = cur_byte
  131.  
  132.         # change IV back to normal (as it was before)
  133.         for j in range(i + 1, chunk_len):
  134.             IV[j] = IV[j] ^ decrypted_bytes[j] ^ (chunk_len - i)
  135.  
  136.         _log('Change Byte #{:02d} : {}'.format(i, decrypted_bytes))
  137.     return decrypted_bytes
  138.  
  139.  
  140. def from_hex_to_integers(hex_cipher):
  141.     int_array = []
  142.     for i in range(0, len(hex_cipher), 2):
  143.         # get current pair
  144.         cur_hex = hex_cipher[i:i + 2]
  145.         # convert to integer
  146.         cur_int = int(cur_hex, 16)
  147.         # append to list
  148.         int_array += [cur_int]
  149.     return int_array
  150.  
  151.  
  152. def split_into_chunks(arr, n):
  153.     chunk_arr = []
  154.     for i in range(0, len(arr), n):
  155.         # get current chunk
  156.         chunk = arr[i:i + n]
  157.         # append to list
  158.         chunk_arr += [chunk]
  159.     return chunk_arr
  160.  
  161.  
  162. def break_block_cipher(ciphertext):
  163.     ''' Step 0: Initialize connection '''
  164.     O.Oracle_Connect()
  165.  
  166.     ''' Step 1: Convert pairs of hex numbers to array of integers'''
  167.     int_array = from_hex_to_integers(ciphertext)
  168.     _log('Integer Array : \n{}\n'.format(int_array))
  169.  
  170.     ''' Step 2: Split into chunks of 16 bytes '''
  171.     chunks_list = split_into_chunks(int_array, 16)
  172.     _log('Integer Chunks Array : \n{}\n'.format(chunks_list))
  173.  
  174.     ''' Step 3: Decrypt each chunk '''
  175.     decrypted_chunks = []
  176.     for i in range(len(chunks_list) - 1):
  177.         _log("Decrypting Chunk #{}".format(i))
  178.         # get current and next chunks
  179.         IV = chunks_list[i]
  180.         chunk = chunks_list[i + 1]
  181.         # decrypt current chunk using next
  182.         is_last = (i == len(chunks_list)-2)
  183.         decrypted_chunk = decrypt_chunk(IV, chunk, 16, is_last)
  184.         # append to result
  185.         decrypted_chunks += [decrypted_chunk]
  186.         _log('')
  187.  
  188.     ''' Step 4: Convert list of lists of ints to string to get plain text'''
  189.     plain_text = convert_to_string(decrypted_chunks)
  190.  
  191.     ''' Step 5: Disconnect '''
  192.     O.Oracle_Disconnect()
  193.  
  194.     return plain_text
  195.  
  196.  
  197. if __name__ == "__main__":
  198.     if len(sys.argv) != 2:
  199.         print('Should pass exactly 1 argument: python decipher.py <ciphertext_filename>')
  200.         exit()
  201.  
  202.     '''Input '''
  203.     # filename
  204.     ciphertext_filename = sys.argv[1]
  205.     # open and read file
  206.     with open(ciphertext_filename, 'r') as ciphertext_file:
  207.         ciphertext = ciphertext_file.read()
  208.  
  209.     ''' Break Code '''
  210.     plain_text = break_block_cipher(ciphertext)
  211.  
  212.     ''' Output '''
  213.     print(plain_text)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement