def bytes_to_base64_original(data): b64 = '' for triplet in groups(data, 3): # 6 bits from triplet[0] r0 = triplet[0] >> 2 if len(triplet) == 3: r1 = triplet[0] & 0b11 r1 = r1 << 4 temp = triplet[1] >> 4 r1 = r1 | temp # 4 bits from triplet[1] and 2 bits from triplet[2] r2 = triplet[1] & 0b00001111 r2 = r2 << 2 temp = triplet[2] & 0b11000000 temp = temp >> 6 r2 |= temp # 6 bits from triplet[2] r3 = triplet[2] & 0b00111111 elif len(triplet) == 2: # 2 bits from triplet[0] and 4 bits from triplet[1] r1 = triplet[0] & 0b11 r1 = r1 << 4 temp = triplet[1] >> 4 r1 = r1 | temp # 4 bits from triplet[1] and 2 bits from triplet[2] r2 = triplet[1] & 0b00001111 r2 = r2 << 2 # triplet[2] is empty r3 = 64 elif len(triplet) == 1: print('triplet is length 1') print('before join', triplet) triplet = b''.join((triplet, b'\x00')) print('after join', triplet) # 2 bits from triplet[0] and 4 bits from triplet[1] r1 = triplet[0] & 0b11 r1 = r1 << 4 temp = triplet[1] >> 4 r1 = r1 | temp r2 = 64 r3 = 64 b64 += base64_table[r0] b64 += base64_table[r1] b64 += base64_table[r2] b64 += base64_table[r3] return b64
def detect_aes_ecb(data): ''' Detects AES ECB by looking for two identical blocks. :param data: Encrypted bytes. :return: Bool, true if CBC is detected. ''' for combo in itertools.combinations(util.groups(data[:-1], BLOCK_SIZE), 2): if combo[0] == combo[1]: return True return False
def base64_to_bytes(b64data): ''' Decode Base64 bytestring to bytestring. One base64 digit is six bits of data. :param b64data: :return: ''' if isinstance(b64data, str): b64data = b64data.encode() assert isinstance(b64data, bytes) decoded = bytearray() # b64data = b64data.replace(b'\n', b'') for quad in groups(b64data, 4): # print(quad, 'len(quad)', len(quad)) # 6 high bits (00111111), 2 low bits (00110000) triplet_1 = (base64_table.index(chr(quad[0])) << 2) + ( base64_table.index(chr(quad[1])) >> 4) decoded.append(triplet_1) # print('triplet_1:', triplet_1) if quad[2] == ord('='): # print('quad[2] is =') # 4 high bits (00001111), 0 low bits (00000000) triplet_2 = ((base64_table.index(chr(quad[1])) & 0b00001111) << 4) else: # 4 high bits (00001111), 4 low bits (00111100) triplet_2 = ((base64_table.index(chr(quad[1])) & 0b00001111) << 4) + (base64_table.index(chr(quad[2])) >> 2) if triplet_2 != 0: decoded.append(triplet_2) # print('triplet_2:', triplet_2) # 2 high bits (00000011), 6 low bits (00111111) if quad[3] == ord('='): # print('quad[3] is =') # triplet_3 = None pass else: triplet_3 = ((base64_table.index(chr(quad[2])) & 0b00000011) << 6) + base64_table.index(chr(quad[3])) decoded.append(triplet_3) # print('triplet_3:', triplet_3) # print(triplet_1, triplet_2, triplet_3) # print(chr(triplet_1), chr(triplet_2), chr(triplet_3)) # print('decoded:', decoded) return bytes(decoded)
def bytes_to_base64(data): ''' :param data: Bytestring :return: ''' # print('input', data) b64 = '' for triplet in groups(data, 3): len_triplet = len(triplet) # Store in case len(triplet) == 1. # 6 bits from triplet[0] r0 = triplet[0] >> 2 if len_triplet == 1: # Add a byte with zeros so that triplet[1] succeeds. # This changes the len(triplet), which is why we stored it earlier. triplet = b''.join((triplet, b'\x00')) r2 = 64 r3 = 64 # 2 bits from triplet[0] and 4 bits from triplet[1]. r1 = triplet[0] & 0b11 r1 = r1 << 4 temp = triplet[1] >> 4 r1 = r1 | temp if len_triplet > 1: # 4 bits from triplet[1] and 2 bits from triplet[2]. r2 = triplet[1] & 0b00001111 r2 = r2 << 2 if len_triplet == 3: temp = triplet[2] & 0b11000000 temp = temp >> 6 r2 |= temp # 6 bits from triplet[2] r3 = triplet[2] & 0b00111111 elif len_triplet == 2: # triplet[2] is empty r3 = 64 b64 += base64_table[r0] b64 += base64_table[r1] b64 += base64_table[r2] b64 += base64_table[r3] # print('output', b64) return b64
def test_detect_aes_ecb(): # Set 1, Challenge 8 # 16 byte blocks with open("8.txt") as f: lines = f.readlines() for index, line in enumerate(lines): for combo in itertools.combinations( util.groups(util.hexbytes_to_bytestr(line[:-1]), 16), 2): if combo[0] == combo[1]: print(index, line) print(lines) assert detect_aes_ecb(lines[132])
def aes_cbc_decrypt(data, key, IV): ''' Decrypt Cipher Block Chaining (CBC) with AES on each block. https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_(CBC) :param data: Encrypted bytes. :param key: Bytes to decrypt. :param IV: Initialization Vector. Must be block size (16 bytes default). :return: ''' if len(IV) is not BLOCK_SIZE: raise ValueError("IV must be " + str(BLOCK_SIZE) + " bytes.") # AES instance for decrypting aes_cipher = AES.new(key=key, mode=AES.MODE_ECB) # iterator for grabbing sequential blocks from data next_block = util.groups(data, BLOCK_SIZE) all_blocks = list(next_block) # 0th block with IV cipher_block = all_blocks[0] # decrypt first block decrypt_block = aes_cipher.decrypt(cipher_block) # xor with IV xor_block = xor.fixed_xor(decrypt_block, IV) # print(xor_block, len(xor_block), type(xor_block)) result = xor_block prev_cipher_block = cipher_block # while more data: for cipher_block in all_blocks[1:]: decrypt_block = aes_cipher.decrypt(cipher_block) xor_block = xor.fixed_xor(prev_cipher_block, decrypt_block) # print(xor_block, len(xor_block)) result += xor_block prev_cipher_block = cipher_block # decrypt next block # detect and remove PKCS7 padding if result[-1] < BLOCK_SIZE: # how many bytes to remove? BLOCK_SIZE - result[-1] result = result[:-result[-1]] return result
def aes_cbc_encrypt(data, key, IV): ''' Encrypt Cipher Block Chaining (CBC) with AES on each block. https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_(CBC) :param data: Plaintext bytes. :param key: Bytes to encrypt. :param IV: Initialization Vector. Must be block size (16 bytes default). :return: ''' if len(IV) is not BLOCK_SIZE: raise ValueError("IV must be " + str(BLOCK_SIZE) + " bytes.") # AES instance for decrypting aes_cipher = AES.new(key=key, mode=AES.MODE_ECB) # iterator for grabbing sequential blocks from data next_block = util.groups(data, BLOCK_SIZE) all_blocks = list(next_block) # first block plain_block = all_blocks[0] # xor with IV xor_block = xor.fixed_xor(plain_block, IV) # print(xor_block, len(xor_block), type(xor_block)) # encrypt encrypt_block = aes_cipher.encrypt(xor_block) result = encrypt_block prev_cipher_block = encrypt_block # while more data: for plain_block in all_blocks[1:]: if len(plain_block) < BLOCK_SIZE: plain_block = pkcs7pad(plain_block, BLOCK_SIZE) xor_block = xor.fixed_xor(prev_cipher_block, plain_block) decrypt_block = aes_cipher.encrypt(xor_block) # print(xor_block, len(xor_block)) result += decrypt_block prev_cipher_block = decrypt_block # encrypt next block return result
def __init__(self): self.headers = util.headers self.base_url = util.groups() print('Start to get friends list and save it for ./friends folder')
def test_solve_chall6(): ''' 1) Let KEYSIZE be the guessed length of the key; try values from 2 to (say) 40. 2) Write a function to compute the edit distance/Hamming distance between two strings. The Hamming distance is just the number of differing bits. The distance between: this is a test and wokka wokka!!! is 37. Make sure your code agrees before you proceed. 3) For each KEYSIZE, take the first KEYSIZE worth of bytes, and the second KEYSIZE worth of bytes, and find the edit distance between them. Normalize this result by dividing by KEYSIZE. 4) The KEYSIZE with the smallest normalized edit distance is probably the key. You could proceed perhaps with the smallest 2-3 KEYSIZE values. Or take 4 KEYSIZE blocks instead of 2 and average the distances. 5) Now that you probably know the KEYSIZE: break the ciphertext into blocks of KEYSIZE length. 6) Now transpose the blocks: make a block that is the first byte of every block, and a block that is the second byte of every block, and so on. 7) Solve each block as if it was single-character XOR. You already have code to do this. 8) For each block, the single-byte XOR key that produces the best looking histogram is the repeating-key XOR key byte for that block. Put them together and you have the key. ''' KEYSIZE_RANGE_MIN = 2 # Inclusive range of possible key sizes to search KEYSIZE_RANGE_MAX = 40 NUMBER_OF_BLOCKS = 4 # TODO 2, 4 also suggested. with open('6.txt') as f: # cipher_b64 = b''.join([x.rstrip('\n').encode() for x in f.readlines()]) cipher_b64_str = ''.join([x.rstrip('\n') for x in f.readlines()]) cipher_b64 = cipher_b64_str.encode() # print(cipher) # print() # TODO is this really one big string???? # Un-Base64 AFTER joining, not before # cipher = base_64.base64_to_bytes(cipher_b64) # TODO doesn't match base64 result cipher = base64.b64decode(cipher_b64_str) print() # print(cipher, len(cipher)) # print(cipher2, len(cipher2)) # for cipher in cipher: likely_xor_key_length = find_xor_key_length(cipher, KEYSIZE_RANGE_MIN, KEYSIZE_RANGE_MAX, NUMBER_OF_BLOCKS) print('likely_xor_key_length', likely_xor_key_length) transposed = [bytearray(b'') for i in range(likely_xor_key_length)] for block in util.groups(cipher, likely_xor_key_length): if len(block) < likely_xor_key_length: continue for position in range(likely_xor_key_length): transposed[position].append(block[position]) # pprint(transposed) # print(len(transposed)) xor_decoded = [] for index, transposed_block in enumerate(transposed): # print('decoding block:', transposed_block) # look at the decode results and scores decoded = decode_all_single_byte_xor(transposed_block) xor_decoded.append(decoded[0]) # print(index, 'decoding result:', xor_decoded[index]) # pprint(xor_decoded) xor_decoded_bytes = [sp.bytestr for sp in xor_decoded] key = [sp.key() for sp in xor_decoded] print(bytes(key)) # de-transpose result = bytearray() for byte in zip_longest(*xor_decoded_bytes): # print(byte, type(byte)) for b in byte: if b: # print(b) result.append(b) # print(result, len(result)) # print(bytes(result).decode(), len(result)) # also, instead of detransposing, xor original text with the key print(repeating_key_xor(cipher, key))
def say(line: str) -> str: """ Parameters: :param line: string of line that needs to be processed Return: str - text that was printed """ # Exporting it to the transpiler where the while loop works global transpile, tabnum listed = list(line) out = "" start = None quotes = ["'", '"'] quote_used = "" if len(util.groups(line, '"', "+")) > 1: groups = util.groups(line, '"', "+") out = "" tout = [] for i in groups: i = i.strip(" ") if i.startswith("say"): i = i.replace("say ", "") if i.startswith('"'): i = "".join(list(i)[1:]) i = i.rstrip('"') if transpile: if i in variables.keys(): tout.append([i, 0]) else: tout.append([i, 1]) continue print(i, end="") out += str(i) elif i.startswith('"'): i = i.strip('"') if transpile: tout.append([i, 1]) continue print(i, end="") out += str(i) else: try: if transpile: tout.append([i, 0]) continue print(variables[i], end="") out += str(variables[i]) except KeyError: raise Exception("Variable not found") if transpile: transpiler.add_line(" " * tabnum + transpiler.fill_print_text_var(tout)) return "__TRANSPILER.IGNORE.OUT__" print("") return out elif util.count("'", line) == 0 and util.count('"', line) == 0 and "," in line: line = line.rstrip("\n") line = line.lstrip("say") line = line.replace(" ", "") line = line.split(",") full_out = "" for i in line: try: print(variables[i], end=" ") full_out += str(variables[i]) + " " except KeyError: raise Exception("Variable not found") print("\n", end="") full_out += "\n" return full_out elif util.count("'", line) == 0 and util.count('"', line) == 0: line = line.rstrip("\n") line = line.lstrip("say") line = line.lstrip(" ") try: if not transpile: print(variables[line]) else: transpiler.add_line(" " * tabnum + transpiler.fill_print_plain_var(line)) except KeyError: raise Exception("Variable not found") return variables[line] else: to_say = list(re.findall(r"say[ ]*?['\"](.+)['\"]", line)) if len(to_say) > 0: if transpile: transpiler.add_line(" " * tabnum + transpiler.fill_print_plain(to_say[0])) else: print(to_say[0]) else: raise Exception( f"Error on line: {line}\nInvalid syntax for say statement. Did you add too many spaces or forget quotes?" ) return to_say[0]