Changeset 43 for trunk


Ignore:
Timestamp:
04/08/13 20:33:21 (11 years ago)
Author:
tmorgan
Message:

Fixed more Python3 conversion issues

Made several improvements to the probe and decryption routines to account for cases where corrupted blocks cause unintentional random failures due to insertion of critical delimiters or failure to decode from UTF-8.

Location:
trunk/lib/bletchley
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/lib/bletchley/CBC/__init__.py

    r38 r43  
    5252
    5353    ## public (r/w ok)
     54    retries = 2
    5455    decrypted = None
    5556    threads = None
     
    5758   
    5859    def __init__(self, oracle, block_size, ciphertext, iv=None,
    59                  threads=1, decrypted='', log_file=None):
     60                 threads=1, decrypted=b'', log_file=None):
    6061        """Creates a new padding oracle attack (POA) object.
    6162
     
    110111        self._ciphertext = ciphertext
    111112        if iv == None:
    112             self._iv = '\x00'*self.block_size
     113            self._iv = b'\x00'*self.block_size
    113114        else:
    114115            self._iv = iv
     
    141142            if i == -1:
    142143                break
    143             tweaked = struct.unpack("B", prior[i])[0] ^ 0xFF
     144            tweaked = prior[i] ^ 0xFF
    144145            tweaked = struct.pack("B", tweaked)
    145146            if not self._oracle(self._ciphertext+prior[:i]+tweaked+prior[i+1:]+final, self._iv):
     
    149150        self.log_message("Testing suspected pad length: %d" % pad_length)
    150151        if pad_length > 1:
     152            # XXX: If this test case fails, we should try instead
     153            # lengthing the pad by one byte with all 256 values (as is
     154            # done in the 1-byte pad case).
     155            #
    151156            # Verify suspected pad length by changing last pad byte to 1
    152157            # and making sure the padding succeeds
    153             tweaked = struct.unpack("B", prior[-1])[0] ^ (pad_length^1)
     158            tweaked = prior[-1] ^ (pad_length^1)
    154159            tweaked = struct.pack("B", tweaked)
     160
     161            #XXX: This replaces the pad bytes with spaces.  The hope is
     162            #     that any UTF-8 decoding errors that the pad bytes
     163            #     might generate are addressed this way.  It is not yet
     164            #     well tested.  An option should be added to allow other
     165            #     bytes to be used or to turn off the behavior.
     166            prior = bytearray(prior)
     167            for q in range(-16,-1):
     168                prior[q] = prior[q]^(pad_length^32) # space
     169
    155170            if self._oracle(self._ciphertext+prior[:-1]+tweaked+final, self._iv):
    156171                ret_val = buffertools.pkcs7Pad(pad_length)
     
    159174            # Verify by changing pad byte to 2 and brute-force changing
    160175            # second-to-last byte to 2 as well
    161             tweaked = struct.unpack("B", prior[-1])[0] ^ (2^1)
     176            tweaked = prior[-1] ^ (2^1)
    162177            tweaked = struct.pack("B", tweaked)
    163178            for j in range(1,256):
    164                 guess = struct.unpack("B", prior[-2])[0] ^ j
     179                guess = prior[-2] ^ j
    165180                guess = struct.pack("B", guess)
    166181                if self._oracle(self._ciphertext+prior[:-2]+guess+tweaked+final, self._iv):
     
    177192                # Stop if another thread found the result
    178193                break
    179             if self._oracle(str(prefix+struct.pack("B",b)+suffix), self._iv):
     194            if self._oracle(prefix+struct.pack("B",b)+suffix, self._iv):
    180195                self._thread_result = b
    181196                break
     
    201216       
    202217        prior_prefix = prior[0:self.block_size-numKnownBytes-1]
    203         base = ord(prior[self.block_size-numKnownBytes-1])
     218        base = prior[self.block_size-numKnownBytes-1]
    204219        # Adjust known bytes to appear as a PKCS 7 pad
    205220        suffix = [0]*numKnownBytes
    206221        for i in range(0,numKnownBytes):
    207             suffix[i] ^= ord(prior[0-numKnownBytes+i])^ord(known_bytes[i])^(numKnownBytes+1)
     222            suffix[i] ^= prior[0-numKnownBytes+i]^known_bytes[i]^(numKnownBytes+1)
    208223        suffix = struct.pack("B"*len(suffix),*suffix)+block
    209224
    210         # Each thread spawned searches a subset of the next byte's
    211         # 256 possible values
    212         self._thread_result = None
    213         threads = []
    214         for i in range(0,self.threads):
    215             t = threading.Thread(target=self._test_value_set,
    216                                  args=(self._ciphertext+prior_prefix, suffix, range(i,256,self.threads)))
    217             t.start()
    218             threads.append(t)
    219            
    220         for t in threads:
    221             t.join()
    222        
     225
     226        for x in range(0, 1+self.retries):
     227            # Each thread spawned searches a subset of the next byte's
     228            # 256 possible values
     229            self._thread_result = None
     230            threads = []
     231            for i in range(0,self.threads):
     232                t = threading.Thread(target=self._test_value_set,
     233                                     args=(self._ciphertext+prior_prefix, suffix, range(i,256,self.threads)))
     234                t.start()
     235                threads.append(t)
     236               
     237            for t in threads:
     238                t.join()
     239               
     240            # If a byte fails to decrypt, it could be because the prior
     241            # block's decrypted value violates UTF-8 decoding rules, or
     242            # because it randomly introduced a delimiter that causes
     243            # problems.  If retries are enabled, we insert an additional
     244            # random block before the prior block so that the decrypted
     245            # value can be changed.
     246            if self._thread_result == None:
     247                if x < self.retries:
     248                    self.log_message("Value of a byte could not be determined. Retrying...")
     249                    prior_prefix = bytes([random.getrandbits(8) for i in range(self.block_size)]) + prior_prefix
     250            else:
     251                break
     252
    223253        if self._thread_result == None:
    224254            self.log_message("Value of a byte could not be determined.  Current plaintext suffix: "+ repr(self.decrypted))
    225255            raise Exception #XXX: custom exception
    226 
     256       
    227257        decrypted = struct.pack("B",self._thread_result^base^(numKnownBytes+1))
    228258        self.decrypted = decrypted + self.decrypted
     
    231261   
    232262
    233     def decrypt_block(self, prior, block, last_bytes=''):
     263    def decrypt_block(self, prior, block, last_bytes=b''):
    234264        """Decrypts the block of ciphertext provided as a parameter.
    235265
     
    266296
    267297        # number of blocks fully decrypted
    268         finished_blocks = len(self.decrypted) / self.block_size
     298        finished_blocks = len(self.decrypted) // self.block_size
    269299
    270300        # contents of the partial block
     
    281311        for i in range(len(blocks)-1-finished_blocks, 0, -1):
    282312            decrypted = self.decrypt_block(blocks[i-1], blocks[i], partial) + decrypted
    283             partial = ''
     313            partial = b''
    284314               
    285315        # Finally decrypt first block
     
    303333            raise InvalidBlockError(self.block_size,len(plaintext))
    304334
    305         ptext = self.decrypt_block('\x00'*self.block_size, ciphertext)
     335        ptext = self.decrypt_block(b'\x00'*self.block_size, ciphertext)
    306336        prior = buffertools.xorBuffers(ptext, plaintext)
    307337        return prior,ciphertext
     
    347377       
    348378        # prior as IV
    349         return str(prior),str(ciphertext)
     379        return prior,ciphertext
  • trunk/lib/bletchley/buffertools.py

    r40 r43  
    104104        Splits a buffer into evenly sized blocks.
    105105        '''
    106         return [buf[i:i + block_size] for i in xrange(0, len(buf), block_size)]
     106        return [buf[i:i + block_size] for i in range(0, len(buf), block_size)]
    107107
    108108def iterBuffer(buf, block_size):
     
    110110        Iterates through a buffer in evenly sized blocks.
    111111        '''
    112         return (buf[i:i + block_size] for i in xrange(0, len(buf), block_size))
     112        return (buf[i:i + block_size] for i in range(0, len(buf), block_size))
    113113
    114114def pkcs7PadBuffer(buf, block_size):
     
    120120
    121121def pkcs7Pad(length):
    122         return chr(length) * length
     122        return bytes([length]*length)
    123123
    124124def stripPKCS7Pad(decrypted, block_size=16, log_file=None):
Note: See TracChangeset for help on using the changeset viewer.