cryptopals

https://cryptopals.com/
Log | Files | Refs

cbc_padding_oracle.py (2193B)


      1 from Crypto.Cipher import AES
      2 from Crypto import Random
      3 from Crypto.Random import random
      4 
      5 key = Random.new().read(AES.block_size)
      6 iv  = Random.new().read(AES.block_size)
      7 
      8 def encryption_oracle():
      9     with open('files/17.txt') as f:
     10         plaintext = f.read().splitlines()[random.randint(0, 9)]
     11 
     12     pad_len = AES.block_size - (len(plaintext) % AES.block_size)
     13     return iv + AES.new(key, AES.MODE_CBC, iv).encrypt(plaintext + ''.join([chr(pad_len) for i in range(pad_len)]))
     14 
     15 def pkcs7_padding_validation(msg, strip_mode):
     16     len_pad = ord(msg[len(msg) - 1])
     17 
     18     if len_pad == 0 or len_pad > AES.block_size:
     19         return False if not strip_mode else ""
     20 
     21     for c in msg[len(msg) - len_pad:]:
     22         if c != chr(len_pad):
     23             return False if not strip_mode else ""
     24 
     25     return True if not strip_mode else msg[:len(msg) - len_pad]
     26 
     27 def padding_oracle(ciphertext):
     28     return pkcs7_padding_validation(AES.new(key, AES.MODE_CBC, iv).decrypt(ciphertext), False)
     29 
     30 ciphertext = encryption_oracle()
     31 plaintext = ""
     32 
     33 for b_idx in reversed(range((len(ciphertext) / AES.block_size) - 1)):
     34     blocks = [ciphertext[i : i + AES.block_size] for i in range(0, len(ciphertext), AES.block_size)]
     35     block = list(blocks[b_idx])
     36     padding = []
     37 
     38     for i in range(AES.block_size):
     39         guessed_byte = block[AES.block_size - i - 1]
     40         found = False
     41 
     42         for c in range(256):
     43             if chr(c) != guessed_byte:
     44                 block[AES.block_size - i - 1] = chr(c)
     45                 blocks[b_idx] = ''.join(block)
     46 
     47                 if padding_oracle(''.join(blocks[:b_idx + 2])):
     48                     plaintext += chr(c ^ ord(guessed_byte) ^ (i + 1))
     49                     padding.append(c)
     50 
     51                     for p in range(i + 1):
     52                         block[AES.block_size - p - 1] = chr(padding[p] ^ (p + 1) ^ (i + 2))
     53 
     54                     found = True
     55                     break
     56 
     57         if not found:
     58             plaintext += chr(i + 1)
     59             padding.append(ord(guessed_byte))
     60 
     61             for p in range(i + 1):
     62                 block[AES.block_size - p - 1] = chr(padding[p] ^ (p + 1) ^ (i + 2))
     63 
     64 print pkcs7_padding_validation(plaintext[::-1], True).decode("base64")