

class PaddingBase:
    def __init__(self, block_size):
        self.block_size = block_size

    def encode(self, source: bytes) -> bytes:  # pragma: nocover
        raise NotImplementedError

    def decode(self, source: bytes) -> bytes:  # pragma: nocover
        raise NotImplementedError


class ZeroPadding(PaddingBase):
    """
    Specified for hashes and MACs as Padding Method 1 in ISO/IEC 10118-1 and ISO/IEC 9797-1.
    """

    def encode(self, source):
        pad_size = self.block_size - ((len(source) + self.block_size - 1) % self.block_size + 1)
        return source + b'\0' * pad_size

    def decode(self, source):
        assert len(source) % self.block_size == 0
        offset = len(source)
        if offset == 0:
            return b''
        end = offset - self.block_size + 1

        while offset > end:
            offset -= 1
            if source[offset]:
                return source[:offset + 1]

        return source[:end]


class Pkcs7Padding(PaddingBase):
    """
    Technique for padding a string as defined in RFC 2315, section 10.3,
    note #2
    """

    def encode(self, source):
        amount_to_pad = self.block_size - (len(source) % self.block_size)
        amount_to_pad = self.block_size if amount_to_pad == 0 else amount_to_pad
        pad = chr(amount_to_pad).encode()
        return source + pad * amount_to_pad

    def decode(self, source):
        return source[:-source[-1]]
