Changeset 12
- Timestamp:
- 10/27/12 13:11:21 (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
lib/bletchley/PaddingOracle/DecryptionOracle.py
r8 r12 20 20 import random 21 21 import struct 22 import threading 23 from .. import buffertools 22 24 from .Exceptions import * 23 25 … … 29 31 ''' 30 32 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): 33 38 ''' 34 39 Creates a new DecryptionOracle object. Receives an oracle function which returns True … … 37 42 ''' 38 43 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 41 101 def decrypt_last_bytes(self,block): 42 102 ''' 43 103 Decrypts the last bytes of block using the oracle. 44 104 ''' 45 if(len(block)!=self.block Size):46 raise InvalidBlockError(self.block Size,len(block))105 if(len(block)!=self.block_size): 106 raise InvalidBlockError(self.block_size,len(block)) 47 107 48 108 #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)] 50 111 51 112 for b in range(256): 52 113 53 114 #XOR with current guess 54 rand[-1] = rand[-1]^b115 rand[-1] ^= b 55 116 #Generate padding string 56 117 randStr = "".join([ struct.pack("B",i) for i in rand ] ) … … 59 120 else: 60 121 #Remove current guess 61 rand[-1] =rand[-1]^b122 rand[-1] ^= b 62 123 63 124 #Now we have a correct padding, test how many bytes we got! 64 for i in range(self.block Size-1):125 for i in range(self.block_size-1): 65 126 #Modify currently tested byte 66 127 rand[i] = rand[i]^0x01 … … 68 129 if(not self.oracle(randStr+block)): 69 130 #We got a hit! Byte i is also part of the padding 70 paddingLen = self.block Size-i131 paddingLen = self.block_size-i 71 132 #Correct random i 72 133 rand[i] = rand[i]^0x01 … … 79 140 return "".join(struct.pack("B",rand[-1]^0x01)) 80 141 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 81 150 def decrypt_next_byte(self,block,known_bytes): 82 151 ''' 83 152 Given some known final bytes, decrypts the next byte using the padding oracle. 84 153 ''' 85 if(len(block)!=self.block Size):154 if(len(block)!=self.block_size): 86 155 raise InvalidBlockError 87 156 numKnownBytes = len(known_bytes) 88 157 89 if(numKnownBytes >= self.block Size):158 if(numKnownBytes >= self.block_size): 90 159 return known_bytes 91 160 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 109 184 # 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 112 189 def decrypt_block(self,block): 113 190 ''' … … 115 192 ''' 116 193 bytes = self.decrypt_last_bytes(block) 117 while(len(bytes)!=self.block Size):194 while(len(bytes)!=self.block_size): 118 195 bytes = self.decrypt_next_byte(block,bytes) 119 196 return bytes 197 120 198 121 199 def decrypt_message(self,ctext, iv = None): … … 124 202 ''' 125 203 #Recover first block 126 result = self.decrypt_block(ctext[0:self.block Size])204 result = self.decrypt_block(ctext[0:self.block_size]) 127 205 128 206 #XOR IV if provided, else we assume zero IV. … … 131 209 132 210 #Recover block by block, XORing with previous ctext block 133 for i in range(self.block Size,len(ctext),self.blockSize):134 prev = ctext[i-self.block Size:i]135 current = self.decrypt_block(ctext[i:i+self.block Size])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]) 136 214 result += self.xor_strings(prev,current) 137 215 return result 216 138 217 139 218 def xor_strings(self,s1,s2): … … 142 221 result += struct.pack("B",ord(s1[i])^ord(s2[i])) 143 222 return result 223 144 224 145 225 def hex_string(self,data):
Note: See TracChangeset
for help on using the changeset viewer.