- Timestamp:
- 04/08/13 20:33:21 (12 years ago)
- Location:
- trunk/lib/bletchley
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/lib/bletchley/CBC/__init__.py
r38 r43 52 52 53 53 ## public (r/w ok) 54 retries = 2 54 55 decrypted = None 55 56 threads = None … … 57 58 58 59 def __init__(self, oracle, block_size, ciphertext, iv=None, 59 threads=1, decrypted= '', log_file=None):60 threads=1, decrypted=b'', log_file=None): 60 61 """Creates a new padding oracle attack (POA) object. 61 62 … … 110 111 self._ciphertext = ciphertext 111 112 if iv == None: 112 self._iv = '\x00'*self.block_size113 self._iv = b'\x00'*self.block_size 113 114 else: 114 115 self._iv = iv … … 141 142 if i == -1: 142 143 break 143 tweaked = struct.unpack("B", prior[i])[0] ^ 0xFF144 tweaked = prior[i] ^ 0xFF 144 145 tweaked = struct.pack("B", tweaked) 145 146 if not self._oracle(self._ciphertext+prior[:i]+tweaked+prior[i+1:]+final, self._iv): … … 149 150 self.log_message("Testing suspected pad length: %d" % pad_length) 150 151 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 # 151 156 # Verify suspected pad length by changing last pad byte to 1 152 157 # and making sure the padding succeeds 153 tweaked = struct.unpack("B", prior[-1])[0] ^ (pad_length^1)158 tweaked = prior[-1] ^ (pad_length^1) 154 159 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 155 170 if self._oracle(self._ciphertext+prior[:-1]+tweaked+final, self._iv): 156 171 ret_val = buffertools.pkcs7Pad(pad_length) … … 159 174 # Verify by changing pad byte to 2 and brute-force changing 160 175 # second-to-last byte to 2 as well 161 tweaked = struct.unpack("B", prior[-1])[0] ^ (2^1)176 tweaked = prior[-1] ^ (2^1) 162 177 tweaked = struct.pack("B", tweaked) 163 178 for j in range(1,256): 164 guess = struct.unpack("B", prior[-2])[0] ^ j179 guess = prior[-2] ^ j 165 180 guess = struct.pack("B", guess) 166 181 if self._oracle(self._ciphertext+prior[:-2]+guess+tweaked+final, self._iv): … … 177 192 # Stop if another thread found the result 178 193 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): 180 195 self._thread_result = b 181 196 break … … 201 216 202 217 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] 204 219 # Adjust known bytes to appear as a PKCS 7 pad 205 220 suffix = [0]*numKnownBytes 206 221 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) 208 223 suffix = struct.pack("B"*len(suffix),*suffix)+block 209 224 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 223 253 if self._thread_result == None: 224 254 self.log_message("Value of a byte could not be determined. Current plaintext suffix: "+ repr(self.decrypted)) 225 255 raise Exception #XXX: custom exception 226 256 227 257 decrypted = struct.pack("B",self._thread_result^base^(numKnownBytes+1)) 228 258 self.decrypted = decrypted + self.decrypted … … 231 261 232 262 233 def decrypt_block(self, prior, block, last_bytes= ''):263 def decrypt_block(self, prior, block, last_bytes=b''): 234 264 """Decrypts the block of ciphertext provided as a parameter. 235 265 … … 266 296 267 297 # number of blocks fully decrypted 268 finished_blocks = len(self.decrypted) / self.block_size298 finished_blocks = len(self.decrypted) // self.block_size 269 299 270 300 # contents of the partial block … … 281 311 for i in range(len(blocks)-1-finished_blocks, 0, -1): 282 312 decrypted = self.decrypt_block(blocks[i-1], blocks[i], partial) + decrypted 283 partial = ''313 partial = b'' 284 314 285 315 # Finally decrypt first block … … 303 333 raise InvalidBlockError(self.block_size,len(plaintext)) 304 334 305 ptext = self.decrypt_block( '\x00'*self.block_size, ciphertext)335 ptext = self.decrypt_block(b'\x00'*self.block_size, ciphertext) 306 336 prior = buffertools.xorBuffers(ptext, plaintext) 307 337 return prior,ciphertext … … 347 377 348 378 # prior as IV 349 return str(prior),str(ciphertext)379 return prior,ciphertext -
trunk/lib/bletchley/buffertools.py
r40 r43 104 104 Splits a buffer into evenly sized blocks. 105 105 ''' 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)] 107 107 108 108 def iterBuffer(buf, block_size): … … 110 110 Iterates through a buffer in evenly sized blocks. 111 111 ''' 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)) 113 113 114 114 def pkcs7PadBuffer(buf, block_size): … … 120 120 121 121 def pkcs7Pad(length): 122 return chr(length) * length122 return bytes([length]*length) 123 123 124 124 def stripPKCS7Pad(decrypted, block_size=16, log_file=None):
Note: See TracChangeset
for help on using the changeset viewer.