# https://stackoverflow.com/questions/8356689/python-equivalent-of-phps-mcrypt-rijndael-256-cbc

class zeropad:

    def __init__(self, block_size):
        assert block_size > 0 and block_size < 256
        self.block_size = block_size

    def pad(self, pt):
        ptlen = len(pt)
        padsize = self.block_size - ((ptlen + self.block_size - 1) % self.block_size + 1)
        return pt + "\0" * padsize

    def unpad(self, ppt):
        assert len(ppt) % self.block_size == 0
        offset = len(ppt)
        if (offset == 0):
            return ''
        end = offset - self.block_size + 1
        while (offset > end):
            offset -= 1;
            if (ppt[offset] != "\0"):
                return ppt[:offset + 1]
        assert false

class cbc:

    def __init__(self, padding, cipher, iv):
        assert padding.block_size == cipher.block_size;
        assert len(iv) == cipher.block_size;
        self.padding = padding
        self.cipher = cipher
        self.iv = iv

    def encrypt(self, pt):
        ppt = self.padding.pad(pt)
        offset = 0
        ct = ''
        v = self.iv
        while (offset < len(ppt)):
            block = ppt[offset:offset + self.cipher.block_size]
            block = self.xorblock(block, v)
            block = self.cipher.encrypt(block)
            ct += block
            offset += self.cipher.block_size
            v = block
        return ct;

    def decrypt(self, ct):
        assert len(ct) % self.cipher.block_size == 0
        ppt = ''
        offset = 0
        v = self.iv
        while (offset < len(ct)):
            block = ct[offset:offset + self.cipher.block_size]
            decrypted = self.cipher.decrypt(block)
            ppt += self.xorblock(decrypted, v)
            offset += self.cipher.block_size
            v = block
        pt = self.padding.unpad(ppt)
        return pt;

    def xorblock(self, b1, b2):
        # sorry, not very Pythonesk
        i = 0
        r = '';
        while (i < self.cipher.block_size):
             r += chr(ord(b1[i]) ^ ord(b2[i]))
             i += 1
        return r

