I'm currently working on a simple encryption and decryption tool using pycryptodome
and its AES cipher. But there is a problem when padding the plaintext to the size of a block.
Usually people do it by adding zeros. But what happens when the last byte of the plaintext is a zero as well? I played around with some other pad
and unpad
functions I found online, but nothing worked for me.
def _pad(self, s):
rest = (AES.block_size - len(s)) % AES.block_size
if not s:
rest = AES.block_size
return s + (rest * bytes([rest]))
def _unpad(self, s):
return s[:-ord(s[len(s) - 1:])]
This was another function I saw quite a few times, but it does not work.
Encryption
def encrypt(self, plaintext, check_integrity=False):
if check_integrity is True:
plaintext += struct.pack('L', zlib.crc32(plaintext))
plaintext = self._pad(plaintext)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
ciphertext = iv + cipher.encrypt(plaintext)
return ciphertext
I'm using zlib.crc32()
to create a checksum in order to check integrity after decryption. I just append it at the the end of the plaintext. Maybe it has something to do with this, I'm not sure.
Decryption
def decrypt(self, ciphertext, check_integrity=False):
iv = ciphertext[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[AES.block_size:])
plaintext = self._unpad(plaintext)
if check_integrity is True:
crc, plaintext = (plaintext[-4:], plaintext[:-4])
if not crc == struct.pack('L', zlib.crc32(plaintext)):
return False
return plaintext
After the ciphertext
has been decrypted and unpadded the last 4 bytes should be equal to zlib.crc36(plaintext)
. But this is not the case. My guess is that the pad
and/or unpad
function is messing with some bytes, which causes the integrity check to fail.
Thank you for reading!