@@ -160,29 +160,37 @@ def bust(self, block, block_size=8, **kwargs):
160160 :param int block_size: Cipher block size (in bytes).
161161 :returns: A bytearray containing the decrypted bytes
162162 '''
163- intermediate_bytes = bytearray ()
163+ intermediate_bytes = bytearray (block_size )
164164
165165 test_bytes = bytearray (block_size ) # '\x00\x00\x00\x00...'
166166 test_bytes .extend (block )
167167
168168 self .log .debug ('Processing block %r' , str (block ))
169169
170- # Work on one byte at a time, starting with the last byte
171- # and moving backwards
170+ retries = 0
171+ last_ok = 0
172+ while retries < self .max_retries :
172173
173- for byte_num in reversed (xrange (block_size )):
174- retries = 0
175- successful = False
174+ # Work on one byte at a time, starting with the last byte
175+ # and moving backwards
176176
177- # clear oracle history for each byte
177+ for byte_num in reversed ( xrange ( block_size )):
178178
179- self . history = []
179+ # clear oracle history for each byte
180180
181- # Break on first byte that returns an oracle, otherwise keep
182- # trying until we exceed the max retry attempts (default is 3)
181+ self .history = []
183182
184- while retries < self .max_retries and not successful :
185- for i in reversed (xrange (256 )):
183+ # Break on first value that returns an oracle, otherwise if we
184+ # don't find a good value it means we have a false positive
185+ # value for the last byte and we need to start all over again
186+ # from the last byte. We can resume where we left off for the
187+ # last byte though.
188+
189+ r = 256
190+ if byte_num == block_size - 1 and last_ok > 0 :
191+ r = last_ok
192+
193+ for i in reversed (xrange (r )):
186194
187195 # Fuzz the test byte
188196
@@ -195,6 +203,10 @@ def bust(self, block, block_size=8, **kwargs):
195203 try :
196204 self .attempts += 1
197205 self .oracle (test_bytes [:], ** kwargs )
206+
207+ if byte_num == block_size - 1 :
208+ last_ok = i
209+
198210 except BadPaddingException :
199211
200212 # TODO
@@ -215,13 +227,11 @@ def bust(self, block, block_size=8, **kwargs):
215227 intermediate_bytes , self .__dict__ )
216228 raise
217229
218- successful = True
219-
220230 current_pad_byte = block_size - byte_num
221231 next_pad_byte = block_size - byte_num + 1
222232 decrypted_byte = test_bytes [byte_num ] ^ current_pad_byte
223233
224- intermediate_bytes . insert ( 0 , decrypted_byte )
234+ intermediate_bytes [ byte_num ] = decrypted_byte
225235
226236 for k in xrange (byte_num , block_size ):
227237
@@ -237,15 +247,18 @@ def bust(self, block, block_size=8, **kwargs):
237247
238248 break
239249
240- if successful :
241- break
242250 else :
251+ self .log .debug ("byte %d not found, restarting" % byte_num )
243252 retries += 1
244253
254+ break
245255 else :
246- raise RuntimeError ('Could not decrypt byte %d in %r within '
247- 'maximum allotted retries (%d)' % (
248- byte_num , block , self .max_retries ))
256+ break
257+
258+ else :
259+ raise RuntimeError ('Could not decrypt byte %d in %r within '
260+ 'maximum allotted retries (%d)' % (
261+ byte_num , block , self .max_retries ))
249262
250263 return intermediate_bytes
251264
@@ -284,46 +297,49 @@ def oracle(self, data):
284297
285298 padbuster = PadBuster ()
286299
287- key = os .urandom (AES .block_size )
288- iv = bytearray (os .urandom (AES .block_size ))
300+ for _ in xrange (100 ):
301+ key = os .urandom (AES .block_size )
302+ iv = bytearray (os .urandom (AES .block_size ))
303+
304+ print "Testing padding oracle exploit in DECRYPT mode"
305+ cipher = AES .new (key , AES .MODE_CBC , str (iv ))
306+
307+ data = pkcs7_pad (teststring , blklen = AES .block_size )
308+ ctext = cipher .encrypt (data )
289309
290- print "Testing padding oracle exploit in DECRYPT mode"
291- cipher = AES .new (key , AES .MODE_CBC , str (iv ))
310+ print "Key: %r" % (key , )
311+ print "IV: %r" % (iv , )
312+ print "Plaintext: %r" % (data , )
313+ print "Ciphertext: %r" % (ctext , )
292314
293- data = pkcs7_pad (teststring , blklen = AES .block_size )
294- ctext = cipher .encrypt (data )
315+ decrypted = padbuster .decrypt (ctext , block_size = AES .block_size , iv = iv )
295316
296- decrypted = padbuster .decrypt (ctext , block_size = AES .block_size , iv = iv )
317+ print "Decrypted: %r" % (str (decrypted ), )
318+ print "\n Recovered in %d attempts\n " % (padbuster .attempts , )
297319
298- print "Key: %r" % (key , )
299- print "IV: %r" % (iv , )
300- print "Plaintext: %r" % (data , )
301- print "Ciphertext: %r" % (ctext , )
302- print "Decrypted: %r" % (str (decrypted ), )
303- print "\n Recovered in %d attempts\n " % (padbuster .attempts , )
320+ assert decrypted == data , \
321+ 'Decrypted data %r does not match original %r' % (
322+ decrypted , data )
304323
305- assert decrypted == data , \
306- 'Decrypted data %r does not match original %r' % (
307- decrypted , data )
324+ print "Testing padding oracle exploit in ENCRYPT mode"
325+ cipher2 = AES .new (key , AES .MODE_CBC , str (iv ))
308326
309- print "Testing padding oracle exploit in ENCRYPT mode"
310- cipher2 = AES .new (key , AES .MODE_CBC , str (iv ))
327+ encrypted = padbuster .encrypt (teststring , block_size = AES .block_size )
311328
312- encrypted = padbuster .encrypt (teststring , block_size = AES .block_size )
329+ print "Key: %r" % (key , )
330+ print "IV: %r" % (iv , )
331+ print "Plaintext: %r" % (teststring , )
332+ print "Ciphertext: %r" % (str (encrypted ), )
313333
314- decrypted = cipher2 .decrypt (str (encrypted ))[AES .block_size :]
315- decrypted = decrypted .rstrip (decrypted [- 1 ])
334+ decrypted = cipher2 .decrypt (str (encrypted ))[AES .block_size :]
335+ decrypted = decrypted .rstrip (decrypted [- 1 ])
316336
317- print "Key: %r" % (key , )
318- print "IV: %r" % (iv , )
319- print "Plaintext: %r" % (teststring , )
320- print "Ciphertext: %r" % (str (encrypted ), )
321- print "Decrypted: %r" % (str (decrypted ), )
322- print "\n Recovered in %d attempts" % (padbuster .attempts , )
337+ print "Decrypted: %r" % (str (decrypted ), )
338+ print "\n Recovered in %d attempts" % (padbuster .attempts , )
323339
324- assert decrypted == teststring , \
325- 'Encrypted data %r does not decrypt to %r, got %r' % (
326- encrypted , teststring , decrypted )
340+ assert decrypted == teststring , \
341+ 'Encrypted data %r does not decrypt to %r, got %r' % (
342+ encrypted , teststring , decrypted )
327343
328344
329345if __name__ == '__main__' :
0 commit comments